Merge branch 'master' into b

This commit is contained in:
Nirmal Mehta 2016-05-24 21:43:45 -04:00
commit 59bb86a964
183 changed files with 4555 additions and 1563 deletions

View file

@ -168,7 +168,7 @@ RUN useradd --create-home --gid docker unprivilegeduser
VOLUME /var/lib/docker
WORKDIR /go/src/github.com/docker/docker
ENV DOCKER_BUILDTAGS apparmor pkcs11 selinux
ENV DOCKER_BUILDTAGS apparmor pkcs11 seccomp selinux
# Let us use a .bashrc file
RUN ln -sfv $PWD/.bashrc ~/.bashrc

View file

@ -35,6 +35,7 @@
"estesp",
"icecrime",
"jhowardmsft",
"justincormack",
"lk4d4",
"mavenugo",
"mhbauer",
@ -204,6 +205,11 @@
Email = "jess@linux.com"
GitHub = "jfrazelle"
[people.justincormack]
Name = "Justin Cormack"
Email = "justin.cormack@docker.com"
GitHub = "justincormack"
[people.lk4d4]
Name = "Alexander Morozov"
Email = "lk4d4@docker.com"

View file

@ -27,7 +27,9 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
cmd.ParseFlags(args, true)
c, err := cli.client.ContainerInspect(context.Background(), cmd.Arg(0))
ctx := context.Background()
c, err := cli.client.ContainerInspect(ctx, cmd.Arg(0))
if err != nil {
return err
}
@ -64,11 +66,11 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
}
if *proxy && !c.Config.Tty {
sigc := cli.forwardAllSignals(container)
sigc := cli.forwardAllSignals(ctx, container)
defer signal.StopCatch(sigc)
}
resp, errAttach := cli.client.ContainerAttach(context.Background(), container, options)
resp, errAttach := cli.client.ContainerAttach(ctx, container, options)
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
// ContainerAttach returns an ErrPersistEOF (connection closed)
// means server met an error and put it in Hijacked connection
@ -83,15 +85,15 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
// resize it, then go back to normal. Without this, every attach after the first will
// require the user to manually resize or hit enter.
cli.resizeTtyTo(cmd.Arg(0), height+1, width+1, false)
cli.resizeTtyTo(ctx, cmd.Arg(0), height+1, width+1, false)
// After the above resizing occurs, the call to monitorTtySize below will handle resetting back
// to the actual size.
if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
if err := cli.monitorTtySize(ctx, cmd.Arg(0), false); err != nil {
logrus.Debugf("Error monitoring TTY size: %s", err)
}
}
if err := cli.holdHijackedConnection(context.Background(), c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
if err := cli.holdHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp); err != nil {
return err
}
@ -99,7 +101,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
return errAttach
}
_, status, err := getExitCode(cli, container)
_, status, err := cli.getExitCode(ctx, container)
if err != nil {
return err
}

View file

@ -32,7 +32,7 @@ import (
"github.com/docker/go-units"
)
type translatorFunc func(reference.NamedTagged) (reference.Canonical, error)
type translatorFunc func(context.Context, reference.NamedTagged) (reference.Canonical, error)
// CmdBuild builds a new image from the source code at a given path.
//
@ -77,8 +77,8 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
cmd.ParseFlags(args, true)
var (
ctx io.ReadCloser
err error
buildCtx io.ReadCloser
err error
)
specifiedContext := cmd.Arg(0)
@ -100,11 +100,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
switch {
case specifiedContext == "-":
ctx, relDockerfile, err = builder.GetContextFromReader(cli.in, *dockerfileName)
buildCtx, relDockerfile, err = builder.GetContextFromReader(cli.in, *dockerfileName)
case urlutil.IsGitURL(specifiedContext):
tempDir, relDockerfile, err = builder.GetContextFromGitURL(specifiedContext, *dockerfileName)
case urlutil.IsURL(specifiedContext):
ctx, relDockerfile, err = builder.GetContextFromURL(progBuff, specifiedContext, *dockerfileName)
buildCtx, relDockerfile, err = builder.GetContextFromURL(progBuff, specifiedContext, *dockerfileName)
default:
contextDir, relDockerfile, err = builder.GetContextFromLocalDir(specifiedContext, *dockerfileName)
}
@ -121,7 +121,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
contextDir = tempDir
}
if ctx == nil {
if buildCtx == nil {
// And canonicalize dockerfile name to a platform-independent one
relDockerfile, err = archive.CanonicalTarNameForPath(relDockerfile)
if err != nil {
@ -159,7 +159,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
includes = append(includes, ".dockerignore", relDockerfile)
}
ctx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
Compression: archive.Uncompressed,
ExcludePatterns: excludes,
IncludeFiles: includes,
@ -169,17 +169,19 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
}
}
ctx := context.Background()
var resolvedTags []*resolvedTag
if isTrusted() {
// Wrap the tar archive to replace the Dockerfile entry with the rewritten
// Dockerfile which uses trusted pulls.
ctx = replaceDockerfileTarWrapper(ctx, relDockerfile, cli.trustedReference, &resolvedTags)
buildCtx = replaceDockerfileTarWrapper(ctx, buildCtx, relDockerfile, cli.trustedReference, &resolvedTags)
}
// Setup an upload progress bar
progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true)
var body io.Reader = progress.NewProgressReader(ctx, progressOutput, 0, "", "Sending build context to Docker daemon")
var body io.Reader = progress.NewProgressReader(buildCtx, progressOutput, 0, "", "Sending build context to Docker daemon")
var memory int64
if *flMemoryString != "" {
@ -235,7 +237,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
Labels: runconfigopts.ConvertKVStringsToMap(flLabels.GetAll()),
}
response, err := cli.client.ImageBuild(context.Background(), body, options)
response, err := cli.client.ImageBuild(ctx, body, options)
if err != nil {
return err
}
@ -271,7 +273,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
// Since the build was successful, now we must tag any of the resolved
// images from the above Dockerfile rewrite.
for _, resolved := range resolvedTags {
if err := cli.tagTrusted(resolved.digestRef, resolved.tagRef); err != nil {
if err := cli.tagTrusted(ctx, resolved.digestRef, resolved.tagRef); err != nil {
return err
}
}
@ -303,7 +305,7 @@ type resolvedTag struct {
// "FROM <image>" instructions to a digest reference. `translator` is a
// function that takes a repository name and tag reference and returns a
// trusted digest reference.
func rewriteDockerfileFrom(dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) {
func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) {
scanner := bufio.NewScanner(dockerfile)
buf := bytes.NewBuffer(nil)
@ -320,7 +322,7 @@ func rewriteDockerfileFrom(dockerfile io.Reader, translator translatorFunc) (new
}
ref = reference.WithDefaultTag(ref)
if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() {
trustedRef, err := translator(ref)
trustedRef, err := translator(ctx, ref)
if err != nil {
return nil, nil, err
}
@ -346,7 +348,7 @@ func rewriteDockerfileFrom(dockerfile io.Reader, translator translatorFunc) (new
// replaces the entry with the given Dockerfile name with the contents of the
// new Dockerfile. Returns a new tar archive stream with the replaced
// Dockerfile.
func replaceDockerfileTarWrapper(inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser {
func replaceDockerfileTarWrapper(ctx context.Context, inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser {
pipeReader, pipeWriter := io.Pipe()
go func() {
tarReader := tar.NewReader(inputTarStream)
@ -373,7 +375,7 @@ func replaceDockerfileTarWrapper(inputTarStream io.ReadCloser, dockerfileName st
// generated from a directory on the local filesystem, the
// Dockerfile will only appear once in the archive.
var newDockerfile []byte
newDockerfile, *resolvedTags, err = rewriteDockerfileFrom(content, translator)
newDockerfile, *resolvedTags, err = rewriteDockerfileFrom(ctx, content, translator)
if err != nil {
pipeWriter.CloseWithError(err)
return

View file

@ -81,11 +81,13 @@ func (cli *DockerCli) CmdCp(args ...string) error {
followLink: *followLink,
}
ctx := context.Background()
switch direction {
case fromContainer:
return cli.copyFromContainer(srcContainer, srcPath, dstPath, cpParam)
return cli.copyFromContainer(ctx, srcContainer, srcPath, dstPath, cpParam)
case toContainer:
return cli.copyToContainer(srcPath, dstContainer, dstPath, cpParam)
return cli.copyToContainer(ctx, srcPath, dstContainer, dstPath, cpParam)
case acrossContainers:
// Copying between containers isn't supported.
return fmt.Errorf("copying between containers is not supported")
@ -126,8 +128,8 @@ func splitCpArg(arg string) (container, path string) {
return parts[0], parts[1]
}
func (cli *DockerCli) statContainerPath(containerName, path string) (types.ContainerPathStat, error) {
return cli.client.ContainerStatPath(context.Background(), containerName, path)
func (cli *DockerCli) statContainerPath(ctx context.Context, containerName, path string) (types.ContainerPathStat, error) {
return cli.client.ContainerStatPath(ctx, containerName, path)
}
func resolveLocalPath(localPath string) (absPath string, err error) {
@ -138,7 +140,7 @@ func resolveLocalPath(localPath string) (absPath string, err error) {
return archive.PreserveTrailingDotOrSeparator(absPath, localPath), nil
}
func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
func (cli *DockerCli) copyFromContainer(ctx context.Context, srcContainer, srcPath, dstPath string, cpParam *cpConfig) (err error) {
if dstPath != "-" {
// Get an absolute destination path.
dstPath, err = resolveLocalPath(dstPath)
@ -150,7 +152,7 @@ func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, c
// if client requests to follow symbol link, then must decide target file to be copied
var rebaseName string
if cpParam.followLink {
srcStat, err := cli.statContainerPath(srcContainer, srcPath)
srcStat, err := cli.statContainerPath(ctx, srcContainer, srcPath)
// If the destination is a symbolic link, we should follow it.
if err == nil && srcStat.Mode&os.ModeSymlink != 0 {
@ -167,7 +169,7 @@ func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, c
}
content, stat, err := cli.client.CopyFromContainer(context.Background(), srcContainer, srcPath)
content, stat, err := cli.client.CopyFromContainer(ctx, srcContainer, srcPath)
if err != nil {
return err
}
@ -199,7 +201,7 @@ func (cli *DockerCli) copyFromContainer(srcContainer, srcPath, dstPath string, c
return archive.CopyTo(preArchive, srcInfo, dstPath)
}
func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpParam *cpConfig) (err error) {
func (cli *DockerCli) copyToContainer(ctx context.Context, srcPath, dstContainer, dstPath string, cpParam *cpConfig) (err error) {
if srcPath != "-" {
// Get an absolute source path.
srcPath, err = resolveLocalPath(srcPath)
@ -215,7 +217,7 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpP
// Prepare destination copy info by stat-ing the container path.
dstInfo := archive.CopyInfo{Path: dstPath}
dstStat, err := cli.statContainerPath(dstContainer, dstPath)
dstStat, err := cli.statContainerPath(ctx, dstContainer, dstPath)
// If the destination is a symbolic link, we should evaluate it.
if err == nil && dstStat.Mode&os.ModeSymlink != 0 {
@ -227,7 +229,7 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpP
}
dstInfo.Path = linkTarget
dstStat, err = cli.statContainerPath(dstContainer, linkTarget)
dstStat, err = cli.statContainerPath(ctx, dstContainer, linkTarget)
}
// Ignore any error and assume that the parent directory of the destination
@ -291,5 +293,5 @@ func (cli *DockerCli) copyToContainer(srcPath, dstContainer, dstPath string, cpP
AllowOverwriteDirWithFile: false,
}
return cli.client.CopyToContainer(context.Background(), dstContainer, resolvedDstPath, content, options)
return cli.client.CopyToContainer(ctx, dstContainer, resolvedDstPath, content, options)
}

View file

@ -19,7 +19,7 @@ import (
networktypes "github.com/docker/engine-api/types/network"
)
func (cli *DockerCli) pullImage(image string, out io.Writer) error {
func (cli *DockerCli) pullImage(ctx context.Context, image string, out io.Writer) error {
ref, err := reference.ParseNamed(image)
if err != nil {
return err
@ -31,7 +31,7 @@ func (cli *DockerCli) pullImage(image string, out io.Writer) error {
return err
}
authConfig := cli.resolveAuthConfig(repoInfo.Index)
authConfig := cli.resolveAuthConfig(ctx, repoInfo.Index)
encodedAuth, err := encodeAuthToBase64(authConfig)
if err != nil {
return err
@ -41,7 +41,7 @@ func (cli *DockerCli) pullImage(image string, out io.Writer) error {
RegistryAuth: encodedAuth,
}
responseBody, err := cli.client.ImageCreate(context.Background(), image, options)
responseBody, err := cli.client.ImageCreate(ctx, image, options)
if err != nil {
return err
}
@ -69,7 +69,7 @@ func newCIDFile(path string) (*cidFile, error) {
return &cidFile{path: path, file: f}, nil
}
func (cli *DockerCli) createContainer(config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
func (cli *DockerCli) createContainer(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
var containerIDFile *cidFile
if cidfile != "" {
var err error
@ -89,7 +89,7 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
if ref, ok := ref.(reference.NamedTagged); ok && isTrusted() {
var err error
trustedRef, err = cli.trustedReference(ref)
trustedRef, err = cli.trustedReference(ctx, ref)
if err != nil {
return nil, err
}
@ -98,7 +98,7 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
}
//create the container
response, err := cli.client.ContainerCreate(context.Background(), config, hostConfig, networkingConfig, name)
response, err := cli.client.ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
//if image not found try to pull it
if err != nil {
@ -106,17 +106,17 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", ref.String())
// we don't want to write to stdout anything apart from container.ID
if err = cli.pullImage(config.Image, cli.err); err != nil {
if err = cli.pullImage(ctx, config.Image, cli.err); err != nil {
return nil, err
}
if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil {
if err := cli.tagTrusted(trustedRef, ref); err != nil {
if err := cli.tagTrusted(ctx, trustedRef, ref); err != nil {
return nil, err
}
}
// Retry
var retryErr error
response, retryErr = cli.client.ContainerCreate(context.Background(), config, hostConfig, networkingConfig, name)
response, retryErr = cli.client.ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
if retryErr != nil {
return nil, retryErr
}
@ -158,7 +158,7 @@ func (cli *DockerCli) CmdCreate(args ...string) error {
cmd.Usage()
return nil
}
response, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
response, err := cli.createContainer(context.Background(), config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
if err != nil {
return err
}

View file

@ -34,7 +34,9 @@ func (cli *DockerCli) CmdExec(args ...string) error {
// Send client escape keys
execConfig.DetachKeys = cli.configFile.DetachKeys
response, err := cli.client.ContainerExecCreate(context.Background(), container, *execConfig)
ctx := context.Background()
response, err := cli.client.ContainerExecCreate(ctx, container, *execConfig)
if err != nil {
return err
}
@ -56,7 +58,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
Tty: execConfig.Tty,
}
if err := cli.client.ContainerExecStart(context.Background(), execID, execStartCheck); err != nil {
if err := cli.client.ContainerExecStart(ctx, execID, execStartCheck); err != nil {
return err
}
// For now don't print this - wait for when we support exec wait()
@ -85,17 +87,17 @@ func (cli *DockerCli) CmdExec(args ...string) error {
}
}
resp, err := cli.client.ContainerExecAttach(context.Background(), execID, *execConfig)
resp, err := cli.client.ContainerExecAttach(ctx, execID, *execConfig)
if err != nil {
return err
}
defer resp.Close()
errCh = promise.Go(func() error {
return cli.holdHijackedConnection(context.Background(), execConfig.Tty, in, out, stderr, resp)
return cli.holdHijackedConnection(ctx, execConfig.Tty, in, out, stderr, resp)
})
if execConfig.Tty && cli.isTerminalIn {
if err := cli.monitorTtySize(execID, true); err != nil {
if err := cli.monitorTtySize(ctx, execID, true); err != nil {
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
}
}
@ -106,7 +108,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
}
var status int
if _, status, err = getExecExitCode(cli, execID); err != nil {
if _, status, err = cli.getExecExitCode(ctx, execID); err != nil {
return err
}

View file

@ -38,5 +38,4 @@ func (cli *DockerCli) CmdExport(args ...string) error {
}
return copyToFile(*outfile, responseBody)
}

View file

@ -28,38 +28,40 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
return fmt.Errorf("%q is not a valid value for --type", *inspectType)
}
ctx := context.Background()
var elementSearcher inspectSearcher
switch *inspectType {
case "container":
elementSearcher = cli.inspectContainers(*size)
elementSearcher = cli.inspectContainers(ctx, *size)
case "image":
elementSearcher = cli.inspectImages(*size)
elementSearcher = cli.inspectImages(ctx, *size)
default:
elementSearcher = cli.inspectAll(*size)
elementSearcher = cli.inspectAll(ctx, *size)
}
return cli.inspectElements(*tmplStr, cmd.Args(), elementSearcher)
}
func (cli *DockerCli) inspectContainers(getSize bool) inspectSearcher {
func (cli *DockerCli) inspectContainers(ctx context.Context, getSize bool) inspectSearcher {
return func(ref string) (interface{}, []byte, error) {
return cli.client.ContainerInspectWithRaw(context.Background(), ref, getSize)
return cli.client.ContainerInspectWithRaw(ctx, ref, getSize)
}
}
func (cli *DockerCli) inspectImages(getSize bool) inspectSearcher {
func (cli *DockerCli) inspectImages(ctx context.Context, getSize bool) inspectSearcher {
return func(ref string) (interface{}, []byte, error) {
return cli.client.ImageInspectWithRaw(context.Background(), ref, getSize)
return cli.client.ImageInspectWithRaw(ctx, ref, getSize)
}
}
func (cli *DockerCli) inspectAll(getSize bool) inspectSearcher {
func (cli *DockerCli) inspectAll(ctx context.Context, getSize bool) inspectSearcher {
return func(ref string) (interface{}, []byte, error) {
c, rawContainer, err := cli.client.ContainerInspectWithRaw(context.Background(), ref, getSize)
c, rawContainer, err := cli.client.ContainerInspectWithRaw(ctx, ref, getSize)
if err != nil {
// Search for image with that id if a container doesn't exist.
if client.IsErrContainerNotFound(err) {
i, rawImage, err := cli.client.ImageInspectWithRaw(context.Background(), ref, getSize)
i, rawImage, err := cli.client.ImageInspectWithRaw(ctx, ref, getSize)
if err != nil {
if client.IsErrImageNotFound(err) {
return nil, nil, fmt.Errorf("Error: No such image or container: %s", ref)

View file

@ -40,12 +40,14 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
cli.in = os.Stdin
}
ctx := context.Background()
var serverAddress string
var isDefaultRegistry bool
if len(cmd.Args()) > 0 {
serverAddress = cmd.Arg(0)
} else {
serverAddress = cli.electAuthServer()
serverAddress = cli.electAuthServer(ctx)
isDefaultRegistry = true
}
@ -54,7 +56,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
return err
}
response, err := cli.client.RegistryLogin(context.Background(), authConfig)
response, err := cli.client.RegistryLogin(ctx, authConfig)
if err != nil {
return err
}

View file

@ -3,6 +3,8 @@ package client
import (
"fmt"
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
flag "github.com/docker/docker/pkg/mflag"
)
@ -22,7 +24,7 @@ func (cli *DockerCli) CmdLogout(args ...string) error {
if len(cmd.Args()) > 0 {
serverAddress = cmd.Arg(0)
} else {
serverAddress = cli.electAuthServer()
serverAddress = cli.electAuthServer(context.Background())
}
// check if we're logged in based on the records in the config file

View file

@ -33,7 +33,9 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
name := cmd.Arg(0)
c, err := cli.client.ContainerInspect(context.Background(), name)
ctx := context.Background()
c, err := cli.client.ContainerInspect(ctx, name)
if err != nil {
return err
}
@ -51,7 +53,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
Tail: *tail,
Details: *details,
}
responseBody, err := cli.client.ContainerLogs(context.Background(), name, options)
responseBody, err := cli.client.ContainerLogs(ctx, name, options)
if err != nil {
return err
}

View file

@ -104,9 +104,11 @@ func (cli *DockerCli) CmdNetworkRm(args ...string) error {
return err
}
ctx := context.Background()
status := 0
for _, net := range cmd.Args() {
if err := cli.client.NetworkRemove(context.Background(), net); err != nil {
if err := cli.client.NetworkRemove(ctx, net); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
status = 1
continue
@ -239,8 +241,10 @@ func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
return err
}
ctx := context.Background()
inspectSearcher := func(name string) (interface{}, []byte, error) {
i, err := cli.client.NetworkInspect(context.Background(), name)
i, err := cli.client.NetworkInspect(ctx, name)
return i, nil, err
}

View file

@ -19,9 +19,11 @@ func (cli *DockerCli) CmdPause(args ...string) error {
cmd.ParseFlags(args, true)
ctx := context.Background()
var errs []string
for _, name := range cmd.Args() {
if err := cli.client.ContainerPause(context.Background(), name); err != nil {
if err := cli.client.ContainerPause(ctx, name); err != nil {
errs = append(errs, err.Error())
} else {
fmt.Fprintf(cli.out, "%s\n", name)

View file

@ -55,18 +55,20 @@ func (cli *DockerCli) CmdPull(args ...string) error {
return err
}
authConfig := cli.resolveAuthConfig(repoInfo.Index)
ctx := context.Background()
authConfig := cli.resolveAuthConfig(ctx, repoInfo.Index)
requestPrivilege := cli.registryAuthenticationPrivilegedFunc(repoInfo.Index, "pull")
if isTrusted() && !registryRef.HasDigest() {
// Check if tag is digest
return cli.trustedPull(repoInfo, registryRef, authConfig, requestPrivilege)
return cli.trustedPull(ctx, repoInfo, registryRef, authConfig, requestPrivilege)
}
return cli.imagePullPrivileged(authConfig, distributionRef.String(), requestPrivilege, *allTags)
return cli.imagePullPrivileged(ctx, authConfig, distributionRef.String(), requestPrivilege, *allTags)
}
func (cli *DockerCli) imagePullPrivileged(authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc, all bool) error {
func (cli *DockerCli) imagePullPrivileged(ctx context.Context, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc, all bool) error {
encodedAuth, err := encodeAuthToBase64(authConfig)
if err != nil {
@ -78,7 +80,7 @@ func (cli *DockerCli) imagePullPrivileged(authConfig types.AuthConfig, ref strin
All: all,
}
responseBody, err := cli.client.ImagePull(context.Background(), ref, options)
responseBody, err := cli.client.ImagePull(ctx, ref, options)
if err != nil {
return err
}

View file

@ -33,15 +33,18 @@ func (cli *DockerCli) CmdPush(args ...string) error {
if err != nil {
return err
}
// Resolve the Auth config relevant for this server
authConfig := cli.resolveAuthConfig(repoInfo.Index)
ctx := context.Background()
// Resolve the Auth config relevant for this server
authConfig := cli.resolveAuthConfig(ctx, repoInfo.Index)
requestPrivilege := cli.registryAuthenticationPrivilegedFunc(repoInfo.Index, "push")
if isTrusted() {
return cli.trustedPush(repoInfo, ref, authConfig, requestPrivilege)
return cli.trustedPush(ctx, repoInfo, ref, authConfig, requestPrivilege)
}
responseBody, err := cli.imagePushPrivileged(authConfig, ref.String(), requestPrivilege)
responseBody, err := cli.imagePushPrivileged(ctx, authConfig, ref.String(), requestPrivilege)
if err != nil {
return err
}
@ -51,7 +54,7 @@ func (cli *DockerCli) CmdPush(args ...string) error {
return jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, nil)
}
func (cli *DockerCli) imagePushPrivileged(authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
func (cli *DockerCli) imagePushPrivileged(ctx context.Context, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
encodedAuth, err := encodeAuthToBase64(authConfig)
if err != nil {
return nil, err
@ -61,5 +64,5 @@ func (cli *DockerCli) imagePushPrivileged(authConfig types.AuthConfig, ref strin
PrivilegeFunc: requestPrivilege,
}
return cli.client.ImagePush(context.Background(), ref, options)
return cli.client.ImagePush(ctx, ref, options)
}

View file

@ -23,6 +23,8 @@ func (cli *DockerCli) CmdRm(args ...string) error {
cmd.ParseFlags(args, true)
ctx := context.Background()
var errs []string
for _, name := range cmd.Args() {
if name == "" {
@ -30,7 +32,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
}
name = strings.Trim(name, "/")
if err := cli.removeContainer(name, *v, *link, *force); err != nil {
if err := cli.removeContainer(ctx, name, *v, *link, *force); err != nil {
errs = append(errs, err.Error())
} else {
fmt.Fprintf(cli.out, "%s\n", name)
@ -42,13 +44,13 @@ func (cli *DockerCli) CmdRm(args ...string) error {
return nil
}
func (cli *DockerCli) removeContainer(container string, removeVolumes, removeLinks, force bool) error {
func (cli *DockerCli) removeContainer(ctx context.Context, container string, removeVolumes, removeLinks, force bool) error {
options := types.ContainerRemoveOptions{
RemoveVolumes: removeVolumes,
RemoveLinks: removeLinks,
Force: force,
}
if err := cli.client.ContainerRemove(context.Background(), container, options); err != nil {
if err := cli.client.ContainerRemove(ctx, container, options); err != nil {
return err
}
return nil

View file

@ -31,6 +31,8 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
v.Set("noprune", "1")
}
ctx := context.Background()
var errs []string
for _, image := range cmd.Args() {
options := types.ImageRemoveOptions{
@ -38,7 +40,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
PruneChildren: !*noprune,
}
dels, err := cli.client.ImageRemove(context.Background(), image, options)
dels, err := cli.client.ImageRemove(ctx, image, options)
if err != nil {
errs = append(errs, err.Error())
} else {

View file

@ -147,20 +147,20 @@ func (cli *DockerCli) CmdRun(args ...string) error {
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = cli.getTtySize()
}
createResponse, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
ctx, cancelFun := context.WithCancel(context.Background())
createResponse, err := cli.createContainer(ctx, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
if err != nil {
cmd.ReportError(err.Error(), true)
return runStartContainerErr(err)
}
if sigProxy {
sigc := cli.forwardAllSignals(createResponse.ID)
sigc := cli.forwardAllSignals(ctx, createResponse.ID)
defer signal.StopCatch(sigc)
}
var (
waitDisplayID chan struct{}
errCh chan error
cancelFun context.CancelFunc
ctx context.Context
)
if !config.AttachStdout && !config.AttachStderr {
// Make this asynchronous to allow the client to write to stdin before having to read the ID
@ -205,7 +205,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
DetachKeys: cli.configFile.DetachKeys,
}
resp, errAttach := cli.client.ContainerAttach(context.Background(), createResponse.ID, options)
resp, errAttach := cli.client.ContainerAttach(ctx, createResponse.ID, options)
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
// ContainerAttach returns an ErrPersistEOF (connection closed)
// means server met an error and put it in Hijacked connection
@ -214,7 +214,6 @@ func (cli *DockerCli) CmdRun(args ...string) error {
}
defer resp.Close()
ctx, cancelFun = context.WithCancel(context.Background())
errCh = promise.Go(func() error {
errHijack := cli.holdHijackedConnection(ctx, config.Tty, in, out, stderr, resp)
if errHijack == nil {
@ -226,14 +225,16 @@ func (cli *DockerCli) CmdRun(args ...string) error {
if *flAutoRemove {
defer func() {
if err := cli.removeContainer(createResponse.ID, true, false, true); err != nil {
// Explicitly not sharing the context as it could be "Done" (by calling cancelFun)
// and thus the container would not be removed.
if err := cli.removeContainer(context.Background(), createResponse.ID, true, false, true); err != nil {
fmt.Fprintf(cli.err, "%v\n", err)
}
}()
}
//start the container
if err := cli.client.ContainerStart(context.Background(), createResponse.ID); err != nil {
if err := cli.client.ContainerStart(ctx, createResponse.ID); err != nil {
// If we have holdHijackedConnection, we should notify
// holdHijackedConnection we are going to exit and wait
// to avoid the terminal are not restored.
@ -247,7 +248,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
}
if (config.AttachStdin || config.AttachStdout || config.AttachStderr) && config.Tty && cli.isTerminalOut {
if err := cli.monitorTtySize(createResponse.ID, false); err != nil {
if err := cli.monitorTtySize(ctx, createResponse.ID, false); err != nil {
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
}
}
@ -272,23 +273,23 @@ func (cli *DockerCli) CmdRun(args ...string) error {
if *flAutoRemove {
// Autoremove: wait for the container to finish, retrieve
// the exit code and remove the container
if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
if status, err = cli.client.ContainerWait(ctx, createResponse.ID); err != nil {
return runStartContainerErr(err)
}
if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
if _, status, err = cli.getExitCode(ctx, createResponse.ID); err != nil {
return err
}
} else {
// No Autoremove: Simply retrieve the exit code
if !config.Tty {
// In non-TTY mode, we can't detach, so we must wait for container exit
if status, err = cli.client.ContainerWait(context.Background(), createResponse.ID); err != nil {
if status, err = cli.client.ContainerWait(ctx, createResponse.ID); err != nil {
return err
}
} else {
// In TTY mode, there is a race: if the process dies too slowly, the state could
// be updated after the getExitCode call and result in the wrong exit code being reported
if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
if _, status, err = cli.getExitCode(ctx, createResponse.ID); err != nil {
return err
}
}

View file

@ -10,10 +10,12 @@ import (
"golang.org/x/net/context"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/opts"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/stringutils"
"github.com/docker/docker/registry"
"github.com/docker/engine-api/types"
"github.com/docker/engine-api/types/filters"
registrytypes "github.com/docker/engine-api/types/registry"
)
@ -21,14 +23,32 @@ import (
//
// Usage: docker search [OPTIONS] TERM
func (cli *DockerCli) CmdSearch(args ...string) error {
var (
err error
filterArgs = filters.NewArgs()
flFilter = opts.NewListOpts(nil)
)
cmd := Cli.Subcmd("search", []string{"TERM"}, Cli.DockerCommands["search"].Description, true)
noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Don't truncate output")
automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")
stars := cmd.Uint([]string{"s", "-stars"}, 0, "Only displays with at least x stars")
cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
// Deprecated since Docker 1.12 in favor of "--filter"
automated := cmd.Bool([]string{"#-automated"}, false, "Only show automated builds - DEPRECATED")
stars := cmd.Uint([]string{"s", "#-stars"}, 0, "Only displays with at least x stars - DEPRECATED")
cmd.Require(flag.Exact, 1)
cmd.ParseFlags(args, true)
for _, f := range flFilter.GetAll() {
if filterArgs, err = filters.ParseFlag(f, filterArgs); err != nil {
return err
}
}
name := cmd.Arg(0)
v := url.Values{}
v.Set("term", name)
@ -38,7 +58,9 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
return err
}
authConfig := cli.resolveAuthConfig(indexInfo)
ctx := context.Background()
authConfig := cli.resolveAuthConfig(ctx, indexInfo)
requestPrivilege := cli.registryAuthenticationPrivilegedFunc(indexInfo, "search")
encodedAuth, err := encodeAuthToBase64(authConfig)
@ -49,9 +71,10 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
options := types.ImageSearchOptions{
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,
Filters: filterArgs,
}
unorderedResults, err := cli.client.ImageSearch(context.Background(), name, options)
unorderedResults, err := cli.client.ImageSearch(ctx, name, options)
if err != nil {
return err
}
@ -62,6 +85,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n")
for _, res := range results {
// --automated and -s, --stars are deprecated since Docker 1.12
if (*automated && !res.IsAutomated) || (int(*stars) > res.StarCount) {
continue
}

View file

@ -17,7 +17,7 @@ import (
"github.com/docker/engine-api/types"
)
func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
func (cli *DockerCli) forwardAllSignals(ctx context.Context, cid string) chan os.Signal {
sigc := make(chan os.Signal, 128)
signal.CatchAll(sigc)
go func() {
@ -37,7 +37,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
continue
}
if err := cli.client.ContainerKill(context.Background(), cid, sig); err != nil {
if err := cli.client.ContainerKill(ctx, cid, sig); err != nil {
logrus.Debugf("Error sending signal: %s", err)
}
}
@ -57,6 +57,8 @@ func (cli *DockerCli) CmdStart(args ...string) error {
cmd.ParseFlags(args, true)
ctx, cancelFun := context.WithCancel(context.Background())
if *attach || *openStdin {
// We're going to attach to a container.
// 1. Ensure we only have one container.
@ -66,13 +68,13 @@ func (cli *DockerCli) CmdStart(args ...string) error {
// 2. Attach to the container.
container := cmd.Arg(0)
c, err := cli.client.ContainerInspect(context.Background(), container)
c, err := cli.client.ContainerInspect(ctx, container)
if err != nil {
return err
}
if !c.Config.Tty {
sigc := cli.forwardAllSignals(container)
sigc := cli.forwardAllSignals(ctx, container)
defer signal.StopCatch(sigc)
}
@ -94,7 +96,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
in = cli.in
}
resp, errAttach := cli.client.ContainerAttach(context.Background(), container, options)
resp, errAttach := cli.client.ContainerAttach(ctx, container, options)
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
// ContainerAttach return an ErrPersistEOF (connection closed)
// means server met an error and put it in Hijacked connection
@ -102,7 +104,6 @@ func (cli *DockerCli) CmdStart(args ...string) error {
return errAttach
}
defer resp.Close()
ctx, cancelFun := context.WithCancel(context.Background())
cErr := promise.Go(func() error {
errHijack := cli.holdHijackedConnection(ctx, c.Config.Tty, in, cli.out, cli.err, resp)
if errHijack == nil {
@ -112,7 +113,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
})
// 3. Start the container.
if err := cli.client.ContainerStart(context.Background(), container); err != nil {
if err := cli.client.ContainerStart(ctx, container); err != nil {
cancelFun()
<-cErr
return err
@ -120,14 +121,14 @@ func (cli *DockerCli) CmdStart(args ...string) error {
// 4. Wait for attachment to break.
if c.Config.Tty && cli.isTerminalOut {
if err := cli.monitorTtySize(container, false); err != nil {
if err := cli.monitorTtySize(ctx, container, false); err != nil {
fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
}
}
if attchErr := <-cErr; attchErr != nil {
return attchErr
}
_, status, err := getExitCode(cli, container)
_, status, err := cli.getExitCode(ctx, container)
if err != nil {
return err
}
@ -137,16 +138,16 @@ func (cli *DockerCli) CmdStart(args ...string) error {
} else {
// We're not going to attach to anything.
// Start as many containers as we want.
return cli.startContainersWithoutAttachments(cmd.Args())
return cli.startContainersWithoutAttachments(ctx, cmd.Args())
}
return nil
}
func (cli *DockerCli) startContainersWithoutAttachments(containers []string) error {
func (cli *DockerCli) startContainersWithoutAttachments(ctx context.Context, containers []string) error {
var failedContainers []string
for _, container := range containers {
if err := cli.client.ContainerStart(context.Background(), container); err != nil {
if err := cli.client.ContainerStart(ctx, container); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
failedContainers = append(failedContainers, container)
} else {

View file

@ -33,6 +33,8 @@ func (cli *DockerCli) CmdStats(args ...string) error {
showAll := len(names) == 0
closeChan := make(chan error)
ctx := context.Background()
// monitorContainerEvents watches for container creation and removal (only
// used when calling `docker stats` without arguments).
monitorContainerEvents := func(started chan<- struct{}, c chan events.Message) {
@ -41,7 +43,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
options := types.EventsOptions{
Filters: f,
}
resBody, err := cli.client.Events(context.Background(), options)
resBody, err := cli.client.Events(ctx, options)
// Whether we successfully subscribed to events or not, we can now
// unblock the main goroutine.
close(started)
@ -71,7 +73,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
options := types.ContainerListOptions{
All: *all,
}
cs, err := cli.client.ContainerList(context.Background(), options)
cs, err := cli.client.ContainerList(ctx, options)
if err != nil {
closeChan <- err
}
@ -79,7 +81,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
s := &containerStats{Name: container.ID[:12]}
if cStats.add(s) {
waitFirst.Add(1)
go s.Collect(cli.client, !*noStream, waitFirst)
go s.Collect(ctx, cli.client, !*noStream, waitFirst)
}
}
}
@ -96,7 +98,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
s := &containerStats{Name: e.ID[:12]}
if cStats.add(s) {
waitFirst.Add(1)
go s.Collect(cli.client, !*noStream, waitFirst)
go s.Collect(ctx, cli.client, !*noStream, waitFirst)
}
}
})
@ -105,7 +107,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
s := &containerStats{Name: e.ID[:12]}
if cStats.add(s) {
waitFirst.Add(1)
go s.Collect(cli.client, !*noStream, waitFirst)
go s.Collect(ctx, cli.client, !*noStream, waitFirst)
}
})
@ -131,7 +133,7 @@ func (cli *DockerCli) CmdStats(args ...string) error {
s := &containerStats{Name: name}
if cStats.add(s) {
waitFirst.Add(1)
go s.Collect(cli.client, !*noStream, waitFirst)
go s.Collect(ctx, cli.client, !*noStream, waitFirst)
}
}

View file

@ -63,7 +63,7 @@ func (s *stats) isKnownContainer(cid string) (int, bool) {
return -1, false
}
func (s *containerStats) Collect(cli client.APIClient, streamStats bool, waitFirst *sync.WaitGroup) {
func (s *containerStats) Collect(ctx context.Context, cli client.APIClient, streamStats bool, waitFirst *sync.WaitGroup) {
logrus.Debugf("collecting stats for %s", s.Name)
var (
getFirst bool
@ -80,7 +80,7 @@ func (s *containerStats) Collect(cli client.APIClient, streamStats bool, waitFir
}
}()
responseBody, err := cli.ContainerStats(context.Background(), s.Name, streamStats)
responseBody, err := cli.ContainerStats(ctx, s.Name, streamStats)
if err != nil {
s.mu.Lock()
s.err = err

View file

@ -22,9 +22,11 @@ func (cli *DockerCli) CmdStop(args ...string) error {
cmd.ParseFlags(args, true)
ctx := context.Background()
var errs []string
for _, name := range cmd.Args() {
if err := cli.client.ContainerStop(context.Background(), name, *nSeconds); err != nil {
if err := cli.client.ContainerStop(ctx, name, *nSeconds); err != nil {
errs = append(errs, err.Error())
} else {
fmt.Fprintf(cli.out, "%s\n", name)

View file

@ -229,14 +229,14 @@ func (cli *DockerCli) getPassphraseRetriever() passphrase.Retriever {
}
}
func (cli *DockerCli) trustedReference(ref reference.NamedTagged) (reference.Canonical, error) {
func (cli *DockerCli) trustedReference(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) {
repoInfo, err := registry.ParseRepositoryInfo(ref)
if err != nil {
return nil, err
}
// Resolve the Auth config relevant for this server
authConfig := cli.resolveAuthConfig(repoInfo.Index)
authConfig := cli.resolveAuthConfig(ctx, repoInfo.Index)
notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig, "pull")
if err != nil {
@ -262,14 +262,14 @@ func (cli *DockerCli) trustedReference(ref reference.NamedTagged) (reference.Can
return reference.WithDigest(ref, r.digest)
}
func (cli *DockerCli) tagTrusted(trustedRef reference.Canonical, ref reference.NamedTagged) error {
func (cli *DockerCli) tagTrusted(ctx context.Context, trustedRef reference.Canonical, ref reference.NamedTagged) error {
fmt.Fprintf(cli.out, "Tagging %s as %s\n", trustedRef.String(), ref.String())
options := types.ImageTagOptions{
Force: true,
}
return cli.client.ImageTag(context.Background(), trustedRef.String(), ref.String(), options)
return cli.client.ImageTag(ctx, trustedRef.String(), ref.String(), options)
}
func notaryError(repoName string, err error) error {
@ -302,7 +302,7 @@ func notaryError(repoName string, err error) error {
return err
}
func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registry.Reference, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
func (cli *DockerCli) trustedPull(ctx context.Context, repoInfo *registry.RepositoryInfo, ref registry.Reference, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
var refs []target
notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig, "pull")
@ -364,7 +364,7 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
if err != nil {
return err
}
if err := cli.imagePullPrivileged(authConfig, ref.String(), requestPrivilege, false); err != nil {
if err := cli.imagePullPrivileged(ctx, authConfig, ref.String(), requestPrivilege, false); err != nil {
return err
}
@ -378,7 +378,7 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
if err != nil {
return err
}
if err := cli.tagTrusted(trustedRef, tagged); err != nil {
if err := cli.tagTrusted(ctx, trustedRef, tagged); err != nil {
return err
}
}
@ -386,8 +386,8 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr
return nil
}
func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
responseBody, err := cli.imagePushPrivileged(authConfig, ref.String(), requestPrivilege)
func (cli *DockerCli) trustedPush(ctx context.Context, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
responseBody, err := cli.imagePushPrivileged(ctx, authConfig, ref.String(), requestPrivilege)
if err != nil {
return err
}

View file

@ -19,9 +19,11 @@ func (cli *DockerCli) CmdUnpause(args ...string) error {
cmd.ParseFlags(args, true)
ctx := context.Background()
var errs []string
for _, name := range cmd.Args() {
if err := cli.client.ContainerUnpause(context.Background(), name); err != nil {
if err := cli.client.ContainerUnpause(ctx, name); err != nil {
errs = append(errs, err.Error())
} else {
fmt.Fprintf(cli.out, "%s\n", name)

View file

@ -99,10 +99,13 @@ func (cli *DockerCli) CmdUpdate(args ...string) error {
RestartPolicy: restartPolicy,
}
ctx := context.Background()
names := cmd.Args()
var errs []string
for _, name := range names {
if err := cli.client.ContainerUpdate(context.Background(), name, updateConfig); err != nil {
if err := cli.client.ContainerUpdate(ctx, name, updateConfig); err != nil {
errs = append(errs, err.Error())
} else {
fmt.Fprintf(cli.out, "%s\n", name)

View file

@ -23,13 +23,13 @@ import (
registrytypes "github.com/docker/engine-api/types/registry"
)
func (cli *DockerCli) electAuthServer() string {
func (cli *DockerCli) electAuthServer(ctx context.Context) string {
// The daemon `/info` endpoint informs us of the default registry being
// used. This is essential in cross-platforms environment, where for
// example a Linux client might be interacting with a Windows daemon, hence
// the default registry URL might be Windows specific.
serverAddress := registry.IndexServer
if info, err := cli.client.Info(context.Background()); err != nil {
if info, err := cli.client.Info(ctx); err != nil {
fmt.Fprintf(cli.out, "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress)
} else {
serverAddress = info.IndexServerAddress
@ -58,12 +58,12 @@ func (cli *DockerCli) registryAuthenticationPrivilegedFunc(index *registrytypes.
}
}
func (cli *DockerCli) resizeTty(id string, isExec bool) {
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
height, width := cli.getTtySize()
cli.resizeTtyTo(id, height, width, isExec)
cli.resizeTtyTo(ctx, id, height, width, isExec)
}
func (cli *DockerCli) resizeTtyTo(id string, height, width int, isExec bool) {
func (cli *DockerCli) resizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
if height == 0 && width == 0 {
return
}
@ -75,9 +75,9 @@ func (cli *DockerCli) resizeTtyTo(id string, height, width int, isExec bool) {
var err error
if isExec {
err = cli.client.ContainerExecResize(context.Background(), id, options)
err = cli.client.ContainerExecResize(ctx, id, options)
} else {
err = cli.client.ContainerResize(context.Background(), id, options)
err = cli.client.ContainerResize(ctx, id, options)
}
if err != nil {
@ -87,8 +87,8 @@ func (cli *DockerCli) resizeTtyTo(id string, height, width int, isExec bool) {
// getExitCode perform an inspect on the container. It returns
// the running state and the exit code.
func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
c, err := cli.client.ContainerInspect(context.Background(), containerID)
func (cli *DockerCli) getExitCode(ctx context.Context, containerID string) (bool, int, error) {
c, err := cli.client.ContainerInspect(ctx, containerID)
if err != nil {
// If we can't connect, then the daemon probably died.
if err != client.ErrConnectionFailed {
@ -102,8 +102,8 @@ func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
// getExecExitCode perform an inspect on the exec command. It returns
// the running state and the exit code.
func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
resp, err := cli.client.ContainerExecInspect(context.Background(), execID)
func (cli *DockerCli) getExecExitCode(ctx context.Context, execID string) (bool, int, error) {
resp, err := cli.client.ContainerExecInspect(ctx, execID)
if err != nil {
// If we can't connect, then the daemon probably died.
if err != client.ErrConnectionFailed {
@ -115,8 +115,8 @@ func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
return resp.Running, resp.ExitCode, nil
}
func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
cli.resizeTty(id, isExec)
func (cli *DockerCli) monitorTtySize(ctx context.Context, id string, isExec bool) error {
cli.resizeTty(ctx, id, isExec)
if runtime.GOOS == "windows" {
go func() {
@ -126,7 +126,7 @@ func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
h, w := cli.getTtySize()
if prevW != w || prevH != h {
cli.resizeTty(id, isExec)
cli.resizeTty(ctx, id, isExec)
}
prevH = h
prevW = w
@ -137,7 +137,7 @@ func (cli *DockerCli) monitorTtySize(id string, isExec bool) error {
gosignal.Notify(sigchan, signal.SIGWINCH)
go func() {
for range sigchan {
cli.resizeTty(id, isExec)
cli.resizeTty(ctx, id, isExec)
}
}()
}
@ -185,10 +185,10 @@ func copyToFile(outfile string, r io.Reader) error {
// resolveAuthConfig is like registry.ResolveAuthConfig, but if using the
// default index, it uses the default index name for the daemon's platform,
// not the client's platform.
func (cli *DockerCli) resolveAuthConfig(index *registrytypes.IndexInfo) types.AuthConfig {
func (cli *DockerCli) resolveAuthConfig(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig {
configKey := index.Name
if index.Official {
configKey = cli.electAuthServer()
configKey = cli.electAuthServer(ctx)
}
a, _ := getCredentials(cli.configFile, configKey)

View file

@ -110,8 +110,10 @@ func (cli *DockerCli) CmdVolumeInspect(args ...string) error {
return nil
}
ctx := context.Background()
inspectSearcher := func(name string) (interface{}, []byte, error) {
i, err := cli.client.VolumeInspect(context.Background(), name)
i, err := cli.client.VolumeInspect(ctx, name)
return i, nil, err
}
@ -161,8 +163,10 @@ func (cli *DockerCli) CmdVolumeRm(args ...string) error {
var status = 0
ctx := context.Background()
for _, name := range cmd.Args() {
if err := cli.client.VolumeRemove(context.Background(), name); err != nil {
if err := cli.client.VolumeRemove(ctx, name); err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
status = 1
continue

View file

@ -21,9 +21,11 @@ func (cli *DockerCli) CmdWait(args ...string) error {
cmd.ParseFlags(args, true)
ctx := context.Background()
var errs []string
for _, name := range cmd.Args() {
status, err := cli.client.ContainerWait(context.Background(), name)
status, err := cli.client.ContainerWait(ctx, name)
if err != nil {
errs = append(errs, err.Error())
} else {

View file

@ -39,5 +39,5 @@ type importExportBackend interface {
type registryBackend interface {
PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
SearchRegistryForImages(ctx context.Context, term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
}

View file

@ -301,7 +301,7 @@ func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter
headers[k] = v
}
}
query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("term"), config, headers)
query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), config, headers)
if err != nil {
return err
}

View file

@ -266,7 +266,7 @@ func workdir(b *Builder, args []string, attributes map[string]bool, original str
return err
}
return b.commit("", b.runConfig.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
return b.commit("", b.runConfig.Cmd, fmt.Sprintf("WORKDIR %v", b.runConfig.WorkingDir))
}
// RUN some command yo

View file

@ -3,6 +3,7 @@ package dockerfile
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
@ -16,6 +17,7 @@ import (
type dispatchTestCase struct {
name, dockerfile, expectedError string
files map[string]string
}
func init() {
@ -34,21 +36,97 @@ func initDispatchTestCases() []dispatchTestCase {
name: "ONBUILD forbidden FROM",
dockerfile: "ONBUILD FROM scratch",
expectedError: "FROM isn't allowed as an ONBUILD trigger",
files: nil,
},
{
name: "ONBUILD forbidden MAINTAINER",
dockerfile: "ONBUILD MAINTAINER docker.io",
expectedError: "MAINTAINER isn't allowed as an ONBUILD trigger",
files: nil,
},
{
name: "ARG two arguments",
dockerfile: "ARG foo bar",
expectedError: "ARG requires exactly one argument definition",
files: nil,
},
{
name: "MAINTAINER unknown flag",
dockerfile: "MAINTAINER --boo joe@example.com",
expectedError: "Unknown flag: boo",
files: nil,
},
{
name: "ADD multiple files to file",
dockerfile: "ADD file1.txt file2.txt test",
expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
},
{
name: "JSON ADD multiple files to file",
dockerfile: `ADD ["file1.txt", "file2.txt", "test"]`,
expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
},
{
name: "Wildcard ADD multiple files to file",
dockerfile: "ADD file*.txt test",
expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
},
{
name: "Wildcard JSON ADD multiple files to file",
dockerfile: `ADD ["file*.txt", "test"]`,
expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
},
{
name: "COPY multiple files to file",
dockerfile: "COPY file1.txt file2.txt test",
expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /",
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
},
{
name: "JSON COPY multiple files to file",
dockerfile: `COPY ["file1.txt", "file2.txt", "test"]`,
expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /",
files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"},
},
{
name: "ADD multiple files to file with whitespace",
dockerfile: `ADD [ "test file1.txt", "test file2.txt", "test" ]`,
expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /",
files: map[string]string{"test file1.txt": "test1", "test file2.txt": "test2"},
},
{
name: "COPY multiple files to file with whitespace",
dockerfile: `COPY [ "test file1.txt", "test file2.txt", "test" ]`,
expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /",
files: map[string]string{"test file1.txt": "test1", "test file2.txt": "test2"},
},
{
name: "COPY wildcard no files",
dockerfile: `COPY file*.txt /tmp/`,
expectedError: "No source files were specified",
files: nil,
},
{
name: "COPY url",
dockerfile: `COPY https://index.docker.io/robots.txt /`,
expectedError: "Source can't be a URL for COPY",
files: nil,
},
{
name: "Chaining ONBUILD",
dockerfile: `ONBUILD ONBUILD RUN touch foobar`,
expectedError: "Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed",
files: nil,
},
{
name: "Invalid instruction",
dockerfile: `foo bar`,
expectedError: "Unknown instruction: FOO",
files: nil,
}}
return dispatchTestCases
@ -66,6 +144,10 @@ func executeTestCase(t *testing.T, testCase dispatchTestCase) {
contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
defer cleanup()
for filename, content := range testCase.files {
createTestTempFile(t, contextDir, filename, content, 0777)
}
tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
if err != nil {
@ -132,3 +214,16 @@ func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) {
}
}
}
// createTestTempFile creates a temporary file within dir with specific contents and permissions.
// When an error occurs, it terminates the test
func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.FileMode) string {
filePath := filepath.Join(dir, filename)
err := ioutil.WriteFile(filePath, []byte(contents), perm)
if err != nil {
t.Fatalf("Error when creating %s file: %s", filename, err)
}
return filePath
}

View file

@ -618,25 +618,8 @@ func (b *Builder) readDockerfile() error {
}
}
f, err := b.context.Open(b.options.Dockerfile)
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("Cannot locate specified Dockerfile: %s", b.options.Dockerfile)
}
return err
}
if f, ok := f.(*os.File); ok {
// ignoring error because Open already succeeded
fi, err := f.Stat()
if err != nil {
return fmt.Errorf("Unexpected error reading Dockerfile: %v", err)
}
if fi.Size() == 0 {
return fmt.Errorf("The Dockerfile (%s) cannot be empty", b.options.Dockerfile)
}
}
b.dockerfile, err = parser.Parse(f)
f.Close()
err := b.parseDockerfile()
if err != nil {
return err
}
@ -655,6 +638,33 @@ func (b *Builder) readDockerfile() error {
return nil
}
func (b *Builder) parseDockerfile() error {
f, err := b.context.Open(b.options.Dockerfile)
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("Cannot locate specified Dockerfile: %s", b.options.Dockerfile)
}
return err
}
defer f.Close()
if f, ok := f.(*os.File); ok {
// ignoring error because Open already succeeded
fi, err := f.Stat()
if err != nil {
return fmt.Errorf("Unexpected error reading Dockerfile: %v", err)
}
if fi.Size() == 0 {
return fmt.Errorf("The Dockerfile (%s) cannot be empty", b.options.Dockerfile)
}
}
b.dockerfile, err = parser.Parse(f)
if err != nil {
return err
}
return nil
}
// determine if build arg is part of built-in args or user
// defined args in Dockerfile at any point in time.
func (b *Builder) isBuildArgAllowed(arg string) bool {

View file

@ -0,0 +1,55 @@
package dockerfile
import (
"strings"
"testing"
"github.com/docker/docker/builder"
"github.com/docker/docker/pkg/archive"
"github.com/docker/engine-api/types"
)
func TestEmptyDockerfile(t *testing.T) {
contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
defer cleanup()
createTestTempFile(t, contextDir, builder.DefaultDockerfileName, "", 0777)
tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
if err != nil {
t.Fatalf("Error when creating tar stream: %s", err)
}
defer func() {
if err = tarStream.Close(); err != nil {
t.Fatalf("Error when closing tar stream: %s", err)
}
}()
context, err := builder.MakeTarSumContext(tarStream)
if err != nil {
t.Fatalf("Error when creating tar context: %s", err)
}
defer func() {
if err = context.Close(); err != nil {
t.Fatalf("Error when closing tar context: %s", err)
}
}()
options := &types.ImageBuildOptions{}
b := &Builder{options: options, context: context}
err = b.readDockerfile()
if err == nil {
t.Fatalf("No error when executing test for empty Dockerfile")
}
if !strings.Contains(err.Error(), "The Dockerfile (Dockerfile) cannot be empty") {
t.Fatalf("Wrong error message. Should be \"%s\". Got \"%s\"", "The Dockerfile (Dockerfile) cannot be empty", err.Error())
}
}

View file

@ -39,12 +39,7 @@ func New(handlers ...Handler) *Cli {
return cli
}
// initErr is an error returned upon initialization of a handler implementing Initializer.
type initErr struct{ error }
func (err initErr) Error() string {
return err.Error()
}
var errCommandNotFound = errors.New("command not found")
func (cli *Cli) command(args ...string) (func(...string) error, error) {
for _, c := range cli.handlers {
@ -54,35 +49,36 @@ func (cli *Cli) command(args ...string) (func(...string) error, error) {
if cmd := c.Command(strings.Join(args, " ")); cmd != nil {
if ci, ok := c.(Initializer); ok {
if err := ci.Initialize(); err != nil {
return nil, initErr{err}
return nil, err
}
}
return cmd, nil
}
}
return nil, errors.New("command not found")
return nil, errCommandNotFound
}
// Run executes the specified command.
func (cli *Cli) Run(args ...string) error {
if len(args) > 1 {
command, err := cli.command(args[:2]...)
switch err := err.(type) {
case nil:
if err == nil {
return command(args[2:]...)
case initErr:
return err.error
}
if err != errCommandNotFound {
return err
}
}
if len(args) > 0 {
command, err := cli.command(args[0])
switch err := err.(type) {
case nil:
return command(args[1:]...)
case initErr:
return err.error
if err != nil {
if err == errCommandNotFound {
cli.noSuchCommand(args[0])
return nil
}
return err
}
cli.noSuchCommand(args[0])
return command(args[1:]...)
}
return cli.CmdHelp()
}
@ -110,24 +106,25 @@ func (cli *Cli) Command(name string) func(...string) error {
func (cli *Cli) CmdHelp(args ...string) error {
if len(args) > 1 {
command, err := cli.command(args[:2]...)
switch err := err.(type) {
case nil:
if err == nil {
command("--help")
return nil
case initErr:
return err.error
}
if err != errCommandNotFound {
return err
}
}
if len(args) > 0 {
command, err := cli.command(args[0])
switch err := err.(type) {
case nil:
command("--help")
return nil
case initErr:
return err.error
if err != nil {
if err == errCommandNotFound {
cli.noSuchCommand(args[0])
return nil
}
return err
}
cli.noSuchCommand(args[0])
command("--help")
return nil
}
if cli.Usage == nil {

View file

@ -16,11 +16,6 @@ const (
ConfigFileName = "config.json"
configFileDir = ".docker"
oldConfigfile = ".dockercfg"
// This constant is only used for really old config files when the
// URL wasn't saved as part of the config file and it was just
// assumed to be this value.
defaultIndexserver = "https://index.docker.io/v1/"
)
var (

View file

@ -228,10 +228,11 @@ func (cli *DaemonCli) start() (err error) {
if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) {
logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]")
}
l, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig)
ls, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig)
if err != nil {
return err
}
ls = wrapListeners(proto, ls)
// If we're binding to a TCP port, make sure that a container doesn't try to use it.
if proto == "tcp" {
if err := allocateDaemonPort(addr); err != nil {
@ -239,7 +240,7 @@ func (cli *DaemonCli) start() (err error) {
}
}
logrus.Debugf("Listener created for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
api.Accept(protoAddrParts[1], l...)
api.Accept(protoAddrParts[1], ls...)
}
if err := migrateKey(); err != nil {

View file

@ -0,0 +1,74 @@
// +build solaris
package main
import (
"fmt"
"net"
"os"
"path/filepath"
"syscall"
"github.com/docker/docker/libcontainerd"
"github.com/docker/docker/pkg/system"
)
const defaultDaemonConfigFile = ""
// currentUserIsOwner checks whether the current user is the owner of the given
// file.
func currentUserIsOwner(f string) bool {
if fileInfo, err := system.Stat(f); err == nil && fileInfo != nil {
if int(fileInfo.UID()) == os.Getuid() {
return true
}
}
return false
}
// setDefaultUmask sets the umask to 0022 to avoid problems
// caused by custom umask
func setDefaultUmask() error {
desiredUmask := 0022
syscall.Umask(desiredUmask)
if umask := syscall.Umask(desiredUmask); umask != desiredUmask {
return fmt.Errorf("failed to set umask: expected %#o, got %#o", desiredUmask, umask)
}
return nil
}
func getDaemonConfDir() string {
return "/etc/docker"
}
// setupConfigReloadTrap configures the USR2 signal to reload the configuration.
func (cli *DaemonCli) setupConfigReloadTrap() {
}
// notifySystem sends a message to the host when the server is ready to be used
func notifySystem() {
}
func (cli *DaemonCli) getPlatformRemoteOptions() []libcontainerd.RemoteOption {
opts := []libcontainerd.RemoteOption{}
return opts
}
// getLibcontainerdRoot gets the root directory for libcontainerd/containerd to
// store their state.
func (cli *DaemonCli) getLibcontainerdRoot() string {
return filepath.Join(cli.Config.ExecRoot, "libcontainerd")
}
func allocateDaemonPort(addr string) error {
return nil
}
// notifyShutdown is called after the daemon shuts down but before the process exits.
func notifyShutdown(err error) {
}
func wrapListeners(proto string, ls []net.Listener) []net.Listener {
return ls
}

View file

@ -1,4 +1,4 @@
// +build !windows
// +build !windows,!solaris
package main
@ -11,6 +11,7 @@ import (
"strconv"
"syscall"
"github.com/docker/docker/cmd/dockerd/hack"
"github.com/docker/docker/daemon"
"github.com/docker/docker/libcontainerd"
"github.com/docker/docker/pkg/system"
@ -111,3 +112,17 @@ func allocateDaemonPort(addr string) error {
// notifyShutdown is called after the daemon shuts down but before the process exits.
func notifyShutdown(err error) {
}
func wrapListeners(proto string, ls []net.Listener) []net.Listener {
if os.Getenv("DOCKER_HTTP_HOST_COMPAT") != "" {
switch proto {
case "unix":
ls[0] = &hack.MalformedHostHeaderOverride{ls[0]}
case "fd":
for i := range ls {
ls[i] = &hack.MalformedHostHeaderOverride{ls[i]}
}
}
}
return ls
}

View file

@ -2,6 +2,7 @@ package main
import (
"fmt"
"net"
"os"
"syscall"
@ -75,3 +76,7 @@ func (cli *DaemonCli) getLibcontainerdRoot() string {
func allocateDaemonPort(addr string) error {
return nil
}
func wrapListeners(proto string, ls []net.Listener) []net.Listener {
return ls
}

View file

@ -0,0 +1,116 @@
// +build !windows
package hack
import "net"
// MalformedHostHeaderOverride is a wrapper to be able
// to overcome the 400 Bad request coming from old docker
// clients that send an invalid Host header.
type MalformedHostHeaderOverride struct {
net.Listener
}
// MalformedHostHeaderOverrideConn wraps the underlying unix
// connection and keeps track of the first read from http.Server
// which just reads the headers.
type MalformedHostHeaderOverrideConn struct {
net.Conn
first bool
}
var closeConnHeader = []byte("\r\nConnection: close\r")
// Read reads the first *read* request from http.Server to inspect
// the Host header. If the Host starts with / then we're talking to
// an old docker client which send an invalid Host header. To not
// error out in http.Server we rewrite the first bytes of the request
// to sanitize the Host header itself.
// In case we're not dealing with old docker clients the data is just passed
// to the server w/o modification.
func (l *MalformedHostHeaderOverrideConn) Read(b []byte) (n int, err error) {
// http.Server uses a 4k buffer
if l.first && len(b) == 4096 {
// This keeps track of the first read from http.Server which just reads
// the headers
l.first = false
// The first read of the connection by http.Server is done limited to
// DefaultMaxHeaderBytes (usually 1 << 20) + 4096.
// Here we do the first read which gets us all the http headers to
// be inspected and modified below.
c, err := l.Conn.Read(b)
if err != nil {
return c, err
}
var (
start, end int
firstLineFeed = -1
buf []byte
)
for i, bb := range b[:c] {
if bb == '\n' && firstLineFeed == -1 {
firstLineFeed = i
}
if bb != '\n' {
continue
}
if b[i+1] != 'H' {
continue
}
if b[i+2] != 'o' {
continue
}
if b[i+3] != 's' {
continue
}
if b[i+4] != 't' {
continue
}
if b[i+5] != ':' {
continue
}
if b[i+6] != ' ' {
continue
}
if b[i+7] != '/' {
continue
}
// ensure clients other than the docker clients do not get this hack
if i != firstLineFeed {
return c, nil
}
start = i + 7
// now find where the value ends
for ii, bbb := range b[start:c] {
if bbb == '\n' {
end = start + ii
break
}
}
buf = make([]byte, 0, c+len(closeConnHeader)-(end-start))
// strip the value of the host header and
// inject `Connection: close` to ensure we don't reuse this connection
buf = append(buf, b[:start]...)
buf = append(buf, closeConnHeader...)
buf = append(buf, b[end:c]...)
copy(b, buf)
break
}
if len(buf) == 0 {
return c, nil
}
return len(buf), nil
}
return l.Conn.Read(b)
}
// Accept makes the listener accepts connections and wraps the connection
// in a MalformedHostHeaderOverrideConn initilizing first to true.
func (l *MalformedHostHeaderOverride) Accept() (net.Conn, error) {
c, err := l.Listener.Accept()
if err != nil {
return c, err
}
return &MalformedHostHeaderOverrideConn{c, true}, nil
}

View file

@ -0,0 +1,115 @@
// +build !windows
package hack
import (
"bytes"
"io"
"net"
"strings"
"testing"
)
func TestHeaderOverrideHack(t *testing.T) {
client, srv := net.Pipe()
tests := [][2][]byte{
{
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\n"),
},
{
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\nFoo: Bar\r\n"),
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\nFoo: Bar\r\n"),
},
{
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\ntest something!"),
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\ntest something!"),
},
{
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\ntest something! " + strings.Repeat("test", 15000)),
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\ntest something! " + strings.Repeat("test", 15000)),
},
{
[]byte("GET /foo\nFoo: Bar\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
[]byte("GET /foo\nFoo: Bar\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
},
}
l := MalformedHostHeaderOverrideConn{client, true}
read := make([]byte, 4096)
for _, pair := range tests {
go func(x []byte) {
srv.Write(x)
}(pair[0])
n, err := l.Read(read)
if err != nil && err != io.EOF {
t.Fatalf("read: %d - %d, err: %v\n%s", n, len(pair[0]), err, string(read[:n]))
}
if !bytes.Equal(read[:n], pair[1][:n]) {
t.Fatalf("\n%s\n%s\n", read[:n], pair[1][:n])
}
l.first = true
// clean out the slice
read = read[:0]
}
srv.Close()
l.Close()
}
func BenchmarkWithHack(b *testing.B) {
client, srv := net.Pipe()
done := make(chan struct{})
req := []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\n")
read := make([]byte, 4096)
b.SetBytes(int64(len(req) * 30))
l := MalformedHostHeaderOverrideConn{client, true}
go func() {
for {
if _, err := srv.Write(req); err != nil {
srv.Close()
break
}
l.first = true // make sure each subsequent run uses the hack parsing
}
close(done)
}()
for i := 0; i < b.N; i++ {
for i := 0; i < 30; i++ {
if n, err := l.Read(read); err != nil && err != io.EOF {
b.Fatalf("read: %d - %d, err: %v\n%s", n, len(req), err, string(read[:n]))
}
}
}
l.Close()
<-done
}
func BenchmarkNoHack(b *testing.B) {
client, srv := net.Pipe()
done := make(chan struct{})
req := []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\n")
read := make([]byte, 4096)
b.SetBytes(int64(len(req) * 30))
go func() {
for {
if _, err := srv.Write(req); err != nil {
srv.Close()
break
}
}
close(done)
}()
for i := 0; i < b.N; i++ {
for i := 0; i < 30; i++ {
if _, err := client.Read(read); err != nil && err != io.EOF {
b.Fatal(err)
}
}
}
client.Close()
<-done
}

View file

@ -0,0 +1,95 @@
// +build solaris
package container
import (
"os"
"path/filepath"
"github.com/docker/docker/volume"
"github.com/docker/engine-api/types/container"
)
// Container holds fields specific to the Solaris implementation. See
// CommonContainer for standard fields common to all containers.
type Container struct {
CommonContainer
// fields below here are platform specific.
HostnamePath string
HostsPath string
ResolvConfPath string
}
// ExitStatus provides exit reasons for a container.
type ExitStatus struct {
// The exit code with which the container exited.
ExitCode int
}
// CreateDaemonEnvironment creates a new environment variable slice for this container.
func (container *Container) CreateDaemonEnvironment(linkedEnv []string) []string {
return nil
}
func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) {
return volumeMounts, nil
}
// TrySetNetworkMount attempts to set the network mounts given a provided destination and
// the path to use for it; return true if the given destination was a network mount file
func (container *Container) TrySetNetworkMount(destination string, path string) bool {
return true
}
// NetworkMounts returns the list of network mounts.
func (container *Container) NetworkMounts() []Mount {
var mount []Mount
return mount
}
// CopyImagePathContent copies files in destination to the volume.
func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error {
return nil
}
// UnmountIpcMounts unmount Ipc related mounts.
func (container *Container) UnmountIpcMounts(unmount func(pth string) error) {
}
// IpcMounts returns the list of Ipc related mounts.
func (container *Container) IpcMounts() []Mount {
return nil
}
// UpdateContainer updates configuration of a container
func (container *Container) UpdateContainer(hostConfig *container.HostConfig) error {
return nil
}
// UnmountVolumes explicitly unmounts volumes from the container.
func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error {
return nil
}
// TmpfsMounts returns the list of tmpfs mounts
func (container *Container) TmpfsMounts() []Mount {
var mounts []Mount
return mounts
}
// cleanResourcePath cleans a resource path and prepares to combine with mnt path
func cleanResourcePath(path string) string {
return filepath.Join(string(os.PathSeparator), path)
}
// BuildHostnameFile writes the container's hostname file.
func (container *Container) BuildHostnameFile() error {
return nil
}
// canMountFS determines if the file system for the container
// can be mounted locally. A no-op on non-Windows platforms
func (container *Container) canMountFS() bool {
return true
}

View file

@ -0,0 +1,7 @@
package container
// setFromExitStatus is a platform specific helper function to set the state
// based on the ExitStatus structure.
func (s *State) setFromExitStatus(exitStatus *ExitStatus) {
s.ExitCode = exitStatus.ExitCode
}

View file

@ -966,6 +966,11 @@ _docker_events() {
__docker_complete_containers_all
return
;;
daemon)
local name=$(__docker_q info | sed -n 's/^\(ID\|Name\): //p')
COMPREPLY=( $( compgen -W "$name" -- "${cur##*=}" ) )
return
;;
event)
COMPREPLY=( $( compgen -W "
attach
@ -987,6 +992,7 @@ _docker_events() {
pause
pull
push
reload
rename
resize
restart
@ -1012,7 +1018,7 @@ _docker_events() {
return
;;
type)
COMPREPLY=( $( compgen -W "container image network volume" -- "${cur##*=}" ) )
COMPREPLY=( $( compgen -W "container daemon image network volume" -- "${cur##*=}" ) )
return
;;
volume)
@ -1024,7 +1030,7 @@ _docker_events() {
case "$prev" in
--filter|-f)
COMPREPLY=( $( compgen -S = -W "container event image label network type volume" -- "$cur" ) )
COMPREPLY=( $( compgen -S = -W "container daemon event image label network type volume" -- "$cur" ) )
__docker_nospace
return
;;
@ -1907,15 +1913,29 @@ _docker_save() {
}
_docker_search() {
local key=$(__docker_map_key_of_current_option '--filter|-f')
case "$key" in
is-automated)
COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) )
return
;;
is-official)
COMPREPLY=( $( compgen -W "false true" -- "${cur##*=}" ) )
return
;;
esac
case "$prev" in
--stars|-s)
--filter|-f)
COMPREPLY=( $( compgen -S = -W "is-automated is-official stars" -- "$cur" ) )
__docker_nospace
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--automated --help --no-trunc --stars -s" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--filter --help --no-trunc" -- "$cur" ) )
;;
esac
}

View file

@ -311,6 +311,54 @@ __docker_complete_ps_filters() {
return ret
}
__docker_complete_search_filters() {
[[ $PREFIX = -* ]] && return 1
integer ret=1
declare -a boolean_opts opts
boolean_opts=('true' 'false')
opts=('is-automated' 'is-official' 'stars')
if compset -P '*='; then
case "${${words[-1]%=*}#*=}" in
(is-automated|is-official)
_describe -t boolean-filter-opts "filter options" boolean_opts && ret=0
;;
*)
_message 'value' && ret=0
;;
esac
else
_describe -t filter-opts "filter options" opts -qS "=" && ret=0
fi
return ret
}
__docker_complete_images_filters() {
[[ $PREFIX = -* ]] && return 1
integer ret=1
declare -a boolean_opts opts
boolean_opts=('true' 'false')
opts=('dangling' 'label')
if compset -P '*='; then
case "${${words[-1]%=*}#*=}" in
(dangling)
_describe -t boolean-filter-opts "filter options" boolean_opts && ret=0
;;
*)
_message 'value' && ret=0
;;
esac
else
_describe -t filter-opts "Filter Options" opts -qS "=" && ret=0
fi
return ret
}
__docker_network_complete_ls_filters() {
[[ $PREFIX = -* ]] && return 1
integer ret=1
@ -929,11 +977,17 @@ __docker_subcommand() {
$opts_help \
"($help -a --all)"{-a,--all}"[Show all images]" \
"($help)--digests[Show digests]" \
"($help)*"{-f=,--filter=}"[Filter values]:filter: " \
"($help)*"{-f=,--filter=}"[Filter values]:filter:->filter-options" \
"($help)--format[Pretty-print containers using a Go template]:format: " \
"($help)--no-trunc[Do not truncate output]" \
"($help -q --quiet)"{-q,--quiet}"[Only show numeric IDs]" \
"($help -): :__docker_repositories" && ret=0
case $state in
(filter-options)
__docker_complete_images_filters && ret=0
;;
esac
;;
(import)
_arguments $(__docker_arguments) \
@ -1126,10 +1180,15 @@ __docker_subcommand() {
(search)
_arguments $(__docker_arguments) \
$opts_help \
"($help)--automated[Only show automated builds]" \
"($help)*"{-f=,--filter=}"[Filter values]:filter:->filter-options" \
"($help)--no-trunc[Do not truncate output]" \
"($help -s --stars)"{-s=,--stars=}"[Only display with at least X stars]:stars:(0 10 100 1000)" \
"($help -):term: " && ret=0
case $state in
(filter-options)
__docker_complete_search_filters && ret=0
;;
esac
;;
(start)
_arguments $(__docker_arguments) \

View file

@ -88,7 +88,7 @@ type CommonConfig struct {
Root string `json:"graph,omitempty"`
SocketGroup string `json:"group,omitempty"`
TrustKeyPath string `json:"-"`
CorsHeaders string `json:"api-cors-headers,omitempty"`
CorsHeaders string `json:"api-cors-header,omitempty"`
EnableCors bool `json:"api-enable-cors,omitempty"`
// ClusterStore is the storage backend used for the cluster information. It is used by both

39
daemon/config_solaris.go Normal file
View file

@ -0,0 +1,39 @@
package daemon
import (
flag "github.com/docker/docker/pkg/mflag"
)
var (
defaultPidFile = "/var/run/docker.pid"
defaultGraph = "/var/lib/docker"
defaultExec = "zones"
)
// Config defines the configuration of a docker daemon.
// These are the configuration settings that you pass
// to the docker daemon when you launch it with say: `docker -d -e lxc`
type Config struct {
CommonConfig
// Fields below here are platform specific.
ExecRoot string `json:"exec-root,omitempty"`
}
// bridgeConfig stores all the bridge driver specific
// configuration.
type bridgeConfig struct {
commonBridgeConfig
}
// InstallFlags adds command-line options to the top-level flag parser for
// the current process.
// Subsequent calls to `flag.Parse` will populate config with values parsed
// from the command-line.
func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
// First handle install flags which are consistent cross-platform
config.InstallCommonFlags(cmd, usageFn)
// Then platform-specific install flags
config.attachExperimentalFlags(cmd, usageFn)
}

View file

@ -41,7 +41,7 @@ type bridgeConfig struct {
EnableIPv6 bool `json:"ipv6,omitempty"`
EnableIPTables bool `json:"iptables,omitempty"`
EnableIPForward bool `json:"ip-forward,omitempty"`
EnableIPMasq bool `json:"ip-mask,omitempty"`
EnableIPMasq bool `json:"ip-masq,omitempty"`
EnableUserlandProxy bool `json:"userland-proxy,omitempty"`
DefaultIP net.IP `json:"ip,omitempty"`
IP string `json:"bip,omitempty"`

View file

@ -0,0 +1,50 @@
// +build solaris
package daemon
import (
"fmt"
"github.com/docker/docker/container"
networktypes "github.com/docker/engine-api/types/network"
"github.com/docker/libnetwork"
)
func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
return nil, nil
}
// ConnectToNetwork connects a container to a network
func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
return fmt.Errorf("Solaris does not support connecting a running container to a network")
}
// getSize returns real size & virtual size
func (daemon *Daemon) getSize(container *container.Container) (int64, int64) {
return 0, 0
}
// DisconnectFromNetwork disconnects a container from the network
func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n libnetwork.Network, force bool) error {
return fmt.Errorf("Solaris does not support disconnecting a running container from a network")
}
func (daemon *Daemon) setupIpcDirs(container *container.Container) error {
return nil
}
func (daemon *Daemon) mountVolumes(container *container.Container) error {
return nil
}
func killProcessDirectly(container *container.Container) error {
return nil
}
func detachMounted(path string) error {
return nil
}
func isLinkable(child *container.Container) bool {
return false
}

View file

@ -9,6 +9,7 @@ import (
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/runconfig"
volumestore "github.com/docker/docker/volume/store"
"github.com/docker/engine-api/types"
containertypes "github.com/docker/engine-api/types/container"
@ -122,6 +123,9 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig) (retC *containe
if params.NetworkingConfig != nil {
endpointsConfigs = params.NetworkingConfig.EndpointsConfig
}
// Make sure NetworkMode has an acceptable value. We do this to ensure
// backwards API compatibility.
container.HostConfig = runconfig.SetDefaultNetModeIfBlank(container.HostConfig)
if err := daemon.updateContainerNetworkSettings(container, endpointsConfigs); err != nil {
return nil, err

View file

@ -6,6 +6,7 @@
package daemon
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
@ -15,6 +16,7 @@ import (
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
"syscall"
@ -23,7 +25,6 @@ import (
"github.com/Sirupsen/logrus"
containerd "github.com/docker/containerd/api/grpc/types"
"github.com/docker/docker/api"
"github.com/docker/docker/builder"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/events"
"github.com/docker/docker/daemon/exec"
@ -40,7 +41,6 @@ import (
"github.com/docker/docker/distribution/xfer"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/image"
"github.com/docker/docker/image/tarexport"
"github.com/docker/docker/layer"
"github.com/docker/docker/libcontainerd"
"github.com/docker/docker/migrate/v1"
@ -64,6 +64,7 @@ import (
volumedrivers "github.com/docker/docker/volume/drivers"
"github.com/docker/docker/volume/local"
"github.com/docker/docker/volume/store"
"github.com/docker/engine-api/types/filters"
"github.com/docker/go-connections/nat"
"github.com/docker/libnetwork"
nwconfig "github.com/docker/libnetwork/config"
@ -78,15 +79,6 @@ var (
errSystemNotSupported = fmt.Errorf("The Docker daemon is not supported on this platform.")
)
// ErrImageDoesNotExist is error returned when no image can be found for a reference.
type ErrImageDoesNotExist struct {
RefOrID string
}
func (e ErrImageDoesNotExist) Error() string {
return fmt.Sprintf("no such id: %s", e.RefOrID)
}
// Daemon holds information about the Docker daemon.
type Daemon struct {
ID string
@ -286,11 +278,6 @@ func (daemon *Daemon) restore() error {
defer wg.Done()
rm := c.RestartManager(false)
if c.IsRunning() || c.IsPaused() {
// Fix activityCount such that graph mounts can be unmounted later
if err := daemon.layerStore.ReinitRWLayer(c.RWLayer); err != nil {
logrus.Errorf("Failed to ReinitRWLayer for %s due to %s", c.ID, err)
return
}
if err := daemon.containerd.Restore(c.ID, libcontainerd.WithRestartManager(rm)); err != nil {
logrus.Errorf("Failed to restore with containerd: %q", err)
return
@ -808,7 +795,7 @@ func NewDaemon(config *Config, registryService *registry.Service, containerdRemo
sysInfo := sysinfo.New(false)
// Check if Devices cgroup is mounted, it is hard requirement for container security,
// on Linux/FreeBSD.
if runtime.GOOS != "windows" && !sysInfo.CgroupDevicesEnabled {
if runtime.GOOS != "windows" && runtime.GOOS != "solaris" && !sysInfo.CgroupDevicesEnabled {
return nil, fmt.Errorf("Devices cgroup isn't mounted")
}
@ -1006,221 +993,6 @@ func isBrokenPipe(e error) bool {
return e == syscall.EPIPE
}
// ExportImage exports a list of images to the given output stream. The
// exported images are archived into a tar when written to the output
// stream. All images with the given tag and all versions containing
// the same tag are exported. names is the set of tags to export, and
// outStream is the writer which the images are written to.
func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
return imageExporter.Save(names, outStream)
}
// LookupImage looks up an image by name and returns it as an ImageInspect
// structure.
func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
img, err := daemon.GetImage(name)
if err != nil {
return nil, fmt.Errorf("No such image: %s", name)
}
refs := daemon.referenceStore.References(img.ID())
repoTags := []string{}
repoDigests := []string{}
for _, ref := range refs {
switch ref.(type) {
case reference.NamedTagged:
repoTags = append(repoTags, ref.String())
case reference.Canonical:
repoDigests = append(repoDigests, ref.String())
}
}
var size int64
var layerMetadata map[string]string
layerID := img.RootFS.ChainID()
if layerID != "" {
l, err := daemon.layerStore.Get(layerID)
if err != nil {
return nil, err
}
defer layer.ReleaseAndLog(daemon.layerStore, l)
size, err = l.Size()
if err != nil {
return nil, err
}
layerMetadata, err = l.Metadata()
if err != nil {
return nil, err
}
}
comment := img.Comment
if len(comment) == 0 && len(img.History) > 0 {
comment = img.History[len(img.History)-1].Comment
}
imageInspect := &types.ImageInspect{
ID: img.ID().String(),
RepoTags: repoTags,
RepoDigests: repoDigests,
Parent: img.Parent.String(),
Comment: comment,
Created: img.Created.Format(time.RFC3339Nano),
Container: img.Container,
ContainerConfig: &img.ContainerConfig,
DockerVersion: img.DockerVersion,
Author: img.Author,
Config: img.Config,
Architecture: img.Architecture,
Os: img.OS,
Size: size,
VirtualSize: size, // TODO: field unused, deprecate
RootFS: rootFSToAPIType(img.RootFS),
}
imageInspect.GraphDriver.Name = daemon.GraphDriverName()
imageInspect.GraphDriver.Data = layerMetadata
return imageInspect, nil
}
// LoadImage uploads a set of images into the repository. This is the
// complement of ImageExport. The input stream is an uncompressed tar
// ball containing images and metadata.
func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
return imageExporter.Load(inTar, outStream, quiet)
}
// ImageHistory returns a slice of ImageHistory structures for the specified image
// name by walking the image lineage.
func (daemon *Daemon) ImageHistory(name string) ([]*types.ImageHistory, error) {
img, err := daemon.GetImage(name)
if err != nil {
return nil, err
}
history := []*types.ImageHistory{}
layerCounter := 0
rootFS := *img.RootFS
rootFS.DiffIDs = nil
for _, h := range img.History {
var layerSize int64
if !h.EmptyLayer {
if len(img.RootFS.DiffIDs) <= layerCounter {
return nil, fmt.Errorf("too many non-empty layers in History section")
}
rootFS.Append(img.RootFS.DiffIDs[layerCounter])
l, err := daemon.layerStore.Get(rootFS.ChainID())
if err != nil {
return nil, err
}
layerSize, err = l.DiffSize()
layer.ReleaseAndLog(daemon.layerStore, l)
if err != nil {
return nil, err
}
layerCounter++
}
history = append([]*types.ImageHistory{{
ID: "<missing>",
Created: h.Created.Unix(),
CreatedBy: h.CreatedBy,
Comment: h.Comment,
Size: layerSize,
}}, history...)
}
// Fill in image IDs and tags
histImg := img
id := img.ID()
for _, h := range history {
h.ID = id.String()
var tags []string
for _, r := range daemon.referenceStore.References(id) {
if _, ok := r.(reference.NamedTagged); ok {
tags = append(tags, r.String())
}
}
h.Tags = tags
id = histImg.Parent
if id == "" {
break
}
histImg, err = daemon.GetImage(id.String())
if err != nil {
break
}
}
return history, nil
}
// GetImageID returns an image ID corresponding to the image referred to by
// refOrID.
func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
id, ref, err := reference.ParseIDOrReference(refOrID)
if err != nil {
return "", err
}
if id != "" {
if _, err := daemon.imageStore.Get(image.ID(id)); err != nil {
return "", ErrImageDoesNotExist{refOrID}
}
return image.ID(id), nil
}
if id, err := daemon.referenceStore.Get(ref); err == nil {
return id, nil
}
if tagged, ok := ref.(reference.NamedTagged); ok {
if id, err := daemon.imageStore.Search(tagged.Tag()); err == nil {
for _, namedRef := range daemon.referenceStore.References(id) {
if namedRef.Name() == ref.Name() {
return id, nil
}
}
}
}
// Search based on ID
if id, err := daemon.imageStore.Search(refOrID); err == nil {
return id, nil
}
return "", ErrImageDoesNotExist{refOrID}
}
// GetImage returns an image corresponding to the image referred to by refOrID.
func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
imgID, err := daemon.GetImageID(refOrID)
if err != nil {
return nil, err
}
return daemon.imageStore.Get(imgID)
}
// GetImageOnBuild looks up a Docker image referenced by `name`.
func (daemon *Daemon) GetImageOnBuild(name string) (builder.Image, error) {
img, err := daemon.GetImage(name)
if err != nil {
return nil, err
}
return img, nil
}
// GraphDriverName returns the name of the graph driver used by the layer.Store
func (daemon *Daemon) GraphDriverName() string {
return daemon.layerStore.DriverName()
@ -1241,57 +1013,6 @@ func (daemon *Daemon) GetRemappedUIDGID() (int, int) {
return uid, gid
}
// GetCachedImage returns the most recent created image that is a child
// of the image with imgID, that had the same config when it was
// created. nil is returned if a child cannot be found. An error is
// returned if the parent image cannot be found.
func (daemon *Daemon) GetCachedImage(imgID image.ID, config *containertypes.Config) (*image.Image, error) {
// Loop on the children of the given image and check the config
getMatch := func(siblings []image.ID) (*image.Image, error) {
var match *image.Image
for _, id := range siblings {
img, err := daemon.imageStore.Get(id)
if err != nil {
return nil, fmt.Errorf("unable to find image %q", id)
}
if runconfig.Compare(&img.ContainerConfig, config) {
// check for the most up to date match
if match == nil || match.Created.Before(img.Created) {
match = img
}
}
}
return match, nil
}
// In this case, this is `FROM scratch`, which isn't an actual image.
if imgID == "" {
images := daemon.imageStore.Map()
var siblings []image.ID
for id, img := range images {
if img.Parent == imgID {
siblings = append(siblings, id)
}
}
return getMatch(siblings)
}
// find match from child images
siblings := daemon.imageStore.Children(imgID)
return getMatch(siblings)
}
// GetCachedImageOnBuild returns a reference to a cached image whose parent equals `parent`
// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
func (daemon *Daemon) GetCachedImageOnBuild(imgID string, cfg *containertypes.Config) (string, error) {
cache, err := daemon.GetCachedImage(image.ID(imgID), cfg)
if cache == nil || err != nil {
return "", err
}
return cache.ID().String(), nil
}
// tempDir returns the default directory to use for temporary files.
func tempDir(rootDir string, rootUID, rootGID int) (string, error) {
var tmpDir string
@ -1427,12 +1148,85 @@ func (daemon *Daemon) AuthenticateToRegistry(ctx context.Context, authConfig *ty
return daemon.RegistryService.Auth(authConfig, dockerversion.DockerUserAgent(ctx))
}
var acceptedSearchFilterTags = map[string]bool{
"is-automated": true,
"is-official": true,
"stars": true,
}
// SearchRegistryForImages queries the registry for images matching
// term. authConfig is used to login.
func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, term string,
func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, filtersArgs string, term string,
authConfig *types.AuthConfig,
headers map[string][]string) (*registrytypes.SearchResults, error) {
return daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(ctx), headers)
searchFilters, err := filters.FromParam(filtersArgs)
if err != nil {
return nil, err
}
if err := searchFilters.Validate(acceptedSearchFilterTags); err != nil {
return nil, err
}
unfilteredResult, err := daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(ctx), headers)
if err != nil {
return nil, err
}
var isAutomated, isOfficial bool
var hasStarFilter = 0
if searchFilters.Include("is-automated") {
if searchFilters.ExactMatch("is-automated", "true") {
isAutomated = true
} else if !searchFilters.ExactMatch("is-automated", "false") {
return nil, fmt.Errorf("Invalid filter 'is-automated=%s'", searchFilters.Get("is-automated"))
}
}
if searchFilters.Include("is-official") {
if searchFilters.ExactMatch("is-official", "true") {
isOfficial = true
} else if !searchFilters.ExactMatch("is-official", "false") {
return nil, fmt.Errorf("Invalid filter 'is-official=%s'", searchFilters.Get("is-official"))
}
}
if searchFilters.Include("stars") {
hasStars := searchFilters.Get("stars")
for _, hasStar := range hasStars {
iHasStar, err := strconv.Atoi(hasStar)
if err != nil {
return nil, fmt.Errorf("Invalid filter 'stars=%s'", hasStar)
}
if iHasStar > hasStarFilter {
hasStarFilter = iHasStar
}
}
}
filteredResults := []registrytypes.SearchResult{}
for _, result := range unfilteredResult.Results {
if searchFilters.Include("is-automated") {
if isAutomated != result.IsAutomated {
continue
}
}
if searchFilters.Include("is-official") {
if isOfficial != result.IsOfficial {
continue
}
}
if searchFilters.Include("stars") {
if result.StarCount < hasStarFilter {
continue
}
}
filteredResults = append(filteredResults, result)
}
return &registrytypes.SearchResults{
Query: unfilteredResult.Query,
NumResults: len(filteredResults),
Results: filteredResults,
}, nil
}
// IsShuttingDown tells whether the daemon is shutting down or not
@ -1539,6 +1333,11 @@ func (daemon *Daemon) initDiscovery(config *Config) error {
func (daemon *Daemon) Reload(config *Config) error {
daemon.configStore.reloadLock.Lock()
defer daemon.configStore.reloadLock.Unlock()
if err := daemon.reloadClusterDiscovery(config); err != nil {
return err
}
if config.IsValueSet("labels") {
daemon.configStore.Labels = config.Labels
}
@ -1572,7 +1371,28 @@ func (daemon *Daemon) Reload(config *Config) error {
daemon.uploadManager.SetConcurrency(*daemon.configStore.MaxConcurrentUploads)
}
return daemon.reloadClusterDiscovery(config)
// We emit daemon reload event here with updatable configurations
attributes := map[string]string{}
attributes["debug"] = fmt.Sprintf("%t", daemon.configStore.Debug)
attributes["cluster-store"] = daemon.configStore.ClusterStore
if daemon.configStore.ClusterOpts != nil {
opts, _ := json.Marshal(daemon.configStore.ClusterOpts)
attributes["cluster-store-opts"] = string(opts)
} else {
attributes["cluster-store-opts"] = "{}"
}
attributes["cluster-advertise"] = daemon.configStore.ClusterAdvertise
if daemon.configStore.Labels != nil {
labels, _ := json.Marshal(daemon.configStore.Labels)
attributes["labels"] = string(labels)
} else {
attributes["labels"] = "[]"
}
attributes["max-concurrent-downloads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentDownloads)
attributes["max-concurrent-uploads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentUploads)
daemon.LogDaemonEventWithAttributes("reload", attributes)
return nil
}
func (daemon *Daemon) reloadClusterDiscovery(config *Config) error {

159
daemon/daemon_solaris.go Normal file
View file

@ -0,0 +1,159 @@
// +build solaris,cgo
package daemon
import (
"fmt"
"github.com/docker/docker/container"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/parsers/kernel"
"github.com/docker/docker/reference"
"github.com/docker/engine-api/types"
containertypes "github.com/docker/engine-api/types/container"
"github.com/docker/libnetwork"
nwconfig "github.com/docker/libnetwork/config"
)
//#include <zone.h>
import "C"
const (
defaultVirtualSwitch = "Virtual Switch"
platformSupported = true
solarisMinCPUShares = 1
solarisMaxCPUShares = 65535
)
func (daemon *Daemon) cleanupMountsByID(id string) error {
return nil
}
func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
return nil
}
func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) {
return nil, nil, nil
}
func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error {
return nil
}
// setupInitLayer populates a directory with mountpoints suitable
// for bind-mounting dockerinit into the container. The mountpoint is simply an
// empty file at /.dockerinit
//
// This extra layer is used by all containers as the top-most ro layer. It protects
// the container from unwanted side-effects on the rw layer.
func setupInitLayer(initLayer string, rootUID, rootGID int) error {
return nil
}
func checkKernel() error {
// solaris can rely upon checkSystem() below, we don't skew kernel versions
return nil
}
func (daemon *Daemon) getCgroupDriver() string {
return ""
}
func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
return nil
}
// verifyPlatformContainerSettings performs platform-specific validation of the
// hostconfig and config structures.
func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
warnings := []string{}
return warnings, nil
}
// verifyDaemonSettings performs validation of daemon config struct
func verifyDaemonSettings(config *Config) error {
// checkSystem validates platform-specific requirements
return nil
}
func checkSystem() error {
// check OS version for compatibility, ensure running in global zone
var err error
var id C.zoneid_t
if id, err = C.getzoneid(); err != nil {
return fmt.Errorf("Exiting. Error getting zone id: %+v", err)
}
if int(id) != 0 {
return fmt.Errorf("Exiting because the Docker daemon is not running in the global zone")
}
v, err := kernel.GetKernelVersion()
if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 5, Major: 12, Minor: 0}) < 0 {
return fmt.Errorf("Your Solaris kernel version: %s doesn't support Docker. Please upgrade to 5.12.0", v.String())
}
return err
}
// configureMaxThreads sets the Go runtime max threads threshold
// which is 90% of the kernel setting from /proc/sys/kernel/threads-max
func configureMaxThreads(config *Config) error {
return nil
}
// configureKernelSecuritySupport configures and validate security support for the kernel
func configureKernelSecuritySupport(config *Config, driverName string) error {
return nil
}
func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkController, error) {
return nil, nil
}
// registerLinks sets up links between containers and writes the
// configuration out for persistence.
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
return nil
}
func (daemon *Daemon) cleanupMounts() error {
return nil
}
// conditionalMountOnStart is a platform specific helper function during the
// container start to call mount.
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
return nil
}
// conditionalUnmountOnCleanup is a platform specific helper function called
// during the cleanup of a container to unmount.
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
return daemon.Unmount(container)
}
func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error {
// Solaris has no custom images to register
return nil
}
func driverOptions(config *Config) []nwconfig.Option {
return []nwconfig.Option{}
}
func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
return nil, nil
}
// setDefaultIsolation determine the default isolation mode for the
// daemon to run in. This is only applicable on Windows
func (daemon *Daemon) setDefaultIsolation() error {
return nil
}
func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
return types.RootFS{}
}

View file

@ -488,7 +488,9 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
if hostConfig.OomScoreAdj < -1000 || hostConfig.OomScoreAdj > 1000 {
return warnings, fmt.Errorf("Invalid value %d, range for oom score adj is [-1000, 1000]", hostConfig.OomScoreAdj)
}
if sysInfo.IPv4ForwardingDisabled {
// ip-forwarding does not affect container with '--net=host' (or '--net=none')
if sysInfo.IPv4ForwardingDisabled && !(hostConfig.NetworkMode.IsHost() || hostConfig.NetworkMode.IsNone()) {
warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.")
logrus.Warnf("IPv4 forwarding is disabled. Networking will not work")
}

View file

@ -1,4 +1,4 @@
// +build !linux,!freebsd,!windows
// +build !linux,!freebsd,!windows,!solaris
package daemon

View file

@ -470,6 +470,10 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
// daemon to run in. This is only applicable on Windows
func (daemon *Daemon) setDefaultIsolation() error {
daemon.defaultIsolation = containertypes.Isolation("process")
// On client SKUs, default to Hyper-V
if system.IsWindowsClient() {
daemon.defaultIsolation = containertypes.Isolation("hyperv")
}
for _, option := range daemon.configStore.ExecOptions {
key, val, err := parsers.ParseKeyValueOpt(option)
if err != nil {
@ -485,6 +489,12 @@ func (daemon *Daemon) setDefaultIsolation() error {
if containertypes.Isolation(val).IsHyperV() {
daemon.defaultIsolation = containertypes.Isolation("hyperv")
}
if containertypes.Isolation(val).IsProcess() {
if system.IsWindowsClient() {
return fmt.Errorf("Windows client operating systems only support Hyper-V containers")
}
daemon.defaultIsolation = containertypes.Isolation("process")
}
default:
return fmt.Errorf("Unrecognised exec-opt '%s'\n", key)
}

View file

@ -1,4 +1,4 @@
// +build !linux,!darwin,!freebsd,!windows
// +build !linux,!darwin,!freebsd,!windows,!solaris
package daemon

View file

@ -80,6 +80,20 @@ func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, actio
daemon.EventsService.Log(action, events.NetworkEventType, actor)
}
// LogDaemonEventWithAttributes generates an event related to the daemon itself with specific given attributes.
func (daemon *Daemon) LogDaemonEventWithAttributes(action string, attributes map[string]string) {
if daemon.EventsService != nil {
if info, err := daemon.SystemInfo(); err == nil && info.Name != "" {
attributes["name"] = info.Name
}
actor := events.Actor{
ID: daemon.ID,
Attributes: attributes,
}
daemon.EventsService.Log(action, events.DaemonEventType, actor)
}
}
// SubscribeToEvents returns the currently record of events, a channel to stream new events from, and a function to cancel the stream of events.
func (daemon *Daemon) SubscribeToEvents(since, until time.Time, filter filters.Args) ([]events.Message, chan interface{}) {
ef := daemonevents.NewFilter(filter)

View file

@ -20,6 +20,7 @@ func NewFilter(filter filters.Args) *Filter {
func (ef *Filter) Include(ev events.Message) bool {
return ef.filter.ExactMatch("event", ev.Action) &&
ef.filter.ExactMatch("type", ev.Type) &&
ef.matchDaemon(ev) &&
ef.matchContainer(ev) &&
ef.matchVolume(ev) &&
ef.matchNetwork(ev) &&
@ -34,6 +35,10 @@ func (ef *Filter) matchLabels(attributes map[string]string) bool {
return ef.filter.MatchKVList("label", attributes)
}
func (ef *Filter) matchDaemon(ev events.Message) bool {
return ef.fuzzyMatchName(ev, events.DaemonEventType)
}
func (ef *Filter) matchContainer(ev events.Message) bool {
return ef.fuzzyMatchName(ev, events.ContainerEventType)
}

11
daemon/exec_solaris.go Normal file
View file

@ -0,0 +1,11 @@
package daemon
import (
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/exec"
"github.com/docker/docker/libcontainerd"
)
func execSetPlatformOpt(c *container.Container, ec *exec.Config, p *libcontainerd.Process) error {
return nil
}

View file

@ -70,6 +70,7 @@ type Driver struct {
root string
uidMaps []idtools.IDMap
gidMaps []idtools.IDMap
ctr *graphdriver.RefCounter
pathCacheLock sync.Mutex
pathCache map[string]string
}
@ -108,6 +109,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
uidMaps: uidMaps,
gidMaps: gidMaps,
pathCache: make(map[string]string),
ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicAufs)),
}
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
@ -320,6 +322,9 @@ func (a *Driver) Get(id, mountLabel string) (string, error) {
m = a.getMountpoint(id)
}
}
if count := a.ctr.Increment(m); count > 1 {
return m, nil
}
// If a dir does not have a parent ( no layers )do not try to mount
// just return the diff path to the data
@ -344,6 +349,9 @@ func (a *Driver) Put(id string) error {
a.pathCache[id] = m
}
a.pathCacheLock.Unlock()
if count := a.ctr.Decrement(m); count > 0 {
return nil
}
err := a.unmount(m)
if err != nil {

View file

@ -2,31 +2,66 @@ package graphdriver
import "sync"
type minfo struct {
check bool
count int
}
// RefCounter is a generic counter for use by graphdriver Get/Put calls
type RefCounter struct {
counts map[string]int
mu sync.Mutex
counts map[string]*minfo
mu sync.Mutex
checker Checker
}
// NewRefCounter returns a new RefCounter
func NewRefCounter() *RefCounter {
return &RefCounter{counts: make(map[string]int)}
func NewRefCounter(c Checker) *RefCounter {
return &RefCounter{
checker: c,
counts: make(map[string]*minfo),
}
}
// Increment increaes the ref count for the given id and returns the current count
func (c *RefCounter) Increment(id string) int {
func (c *RefCounter) Increment(path string) int {
c.mu.Lock()
c.counts[id]++
count := c.counts[id]
m := c.counts[path]
if m == nil {
m = &minfo{}
c.counts[path] = m
}
// if we are checking this path for the first time check to make sure
// if it was already mounted on the system and make sure we have a correct ref
// count if it is mounted as it is in use.
if !m.check {
m.check = true
if c.checker.IsMounted(path) {
m.count++
}
}
m.count++
c.mu.Unlock()
return count
return m.count
}
// Decrement decreases the ref count for the given id and returns the current count
func (c *RefCounter) Decrement(id string) int {
func (c *RefCounter) Decrement(path string) int {
c.mu.Lock()
c.counts[id]--
count := c.counts[id]
m := c.counts[path]
if m == nil {
m = &minfo{}
c.counts[path] = m
}
// if we are checking this path for the first time check to make sure
// if it was already mounted on the system and make sure we have a correct ref
// count if it is mounted as it is in use.
if !m.check {
m.check = true
if c.checker.IsMounted(path) {
m.count++
}
}
m.count--
c.mu.Unlock()
return count
return m.count
}

View file

@ -47,7 +47,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
home: home,
uidMaps: uidMaps,
gidMaps: gidMaps,
ctr: graphdriver.NewRefCounter(),
ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()),
}
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
@ -160,35 +160,35 @@ func (d *Driver) Remove(id string) error {
// Get mounts a device with given id into the root filesystem
func (d *Driver) Get(id, mountLabel string) (string, error) {
mp := path.Join(d.home, "mnt", id)
if count := d.ctr.Increment(id); count > 1 {
if count := d.ctr.Increment(mp); count > 1 {
return mp, nil
}
uid, gid, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
if err != nil {
d.ctr.Decrement(id)
d.ctr.Decrement(mp)
return "", err
}
// Create the target directories if they don't exist
if err := idtools.MkdirAllAs(path.Join(d.home, "mnt"), 0755, uid, gid); err != nil && !os.IsExist(err) {
d.ctr.Decrement(id)
d.ctr.Decrement(mp)
return "", err
}
if err := idtools.MkdirAs(mp, 0755, uid, gid); err != nil && !os.IsExist(err) {
d.ctr.Decrement(id)
d.ctr.Decrement(mp)
return "", err
}
// Mount the device
if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil {
d.ctr.Decrement(id)
d.ctr.Decrement(mp)
return "", err
}
rootFs := path.Join(mp, "rootfs")
if err := idtools.MkdirAllAs(rootFs, 0755, uid, gid); err != nil && !os.IsExist(err) {
d.ctr.Decrement(id)
d.ctr.Decrement(mp)
d.DeviceSet.UnmountDevice(id, mp)
return "", err
}
@ -198,7 +198,7 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
// Create an "id" file with the container/image id in it to help reconstruct this in case
// of later problems
if err := ioutil.WriteFile(idFile, []byte(id), 0600); err != nil {
d.ctr.Decrement(id)
d.ctr.Decrement(mp)
d.DeviceSet.UnmountDevice(id, mp)
return "", err
}
@ -209,10 +209,10 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
// Put unmounts a device and removes it.
func (d *Driver) Put(id string) error {
if count := d.ctr.Decrement(id); count > 0 {
mp := path.Join(d.home, "mnt", id)
if count := d.ctr.Decrement(mp); count > 0 {
return nil
}
mp := path.Join(d.home, "mnt", id)
err := d.DeviceSet.UnmountDevice(id, mp)
if err != nil {
logrus.Errorf("devmapper: Error unmounting device %s: %s", id, err)

View file

@ -113,6 +113,12 @@ type FileGetCloser interface {
Close() error
}
// Checker makes checks on specified filesystems.
type Checker interface {
// IsMounted returns true if the provided path is mounted for the specific checker
IsMounted(path string) bool
}
func init() {
drivers = make(map[string]InitFunc)
}

View file

@ -5,6 +5,8 @@ package graphdriver
import (
"path/filepath"
"syscall"
"github.com/docker/docker/pkg/mount"
)
const (
@ -89,6 +91,36 @@ func GetFSMagic(rootpath string) (FsMagic, error) {
return FsMagic(buf.Type), nil
}
// NewFsChecker returns a checker configured for the provied FsMagic
func NewFsChecker(t FsMagic) Checker {
return &fsChecker{
t: t,
}
}
type fsChecker struct {
t FsMagic
}
func (c *fsChecker) IsMounted(path string) bool {
m, _ := Mounted(c.t, path)
return m
}
// NewDefaultChecker returns a check that parses /proc/mountinfo to check
// if the specified path is mounted.
func NewDefaultChecker() Checker {
return &defaultChecker{}
}
type defaultChecker struct {
}
func (c *defaultChecker) IsMounted(path string) bool {
m, _ := mount.Mounted(path)
return m
}
// Mounted checks if the given path is mounted as the fs type
func Mounted(fsType FsMagic, mountPath string) (bool, error) {
var buf syscall.Statfs_t

View file

@ -0,0 +1,65 @@
// +build solaris,cgo
package graphdriver
/*
#include <sys/statvfs.h>
#include <stdlib.h>
static inline struct statvfs *getstatfs(char *s) {
struct statvfs *buf;
int err;
buf = (struct statvfs *)malloc(sizeof(struct statvfs));
err = statvfs(s, buf);
return buf;
}
*/
import "C"
import (
"path/filepath"
"unsafe"
log "github.com/Sirupsen/logrus"
)
const (
// FsMagicZfs filesystem id for Zfs
FsMagicZfs = FsMagic(0x2fc12fc1)
)
var (
// Slice of drivers that should be used in an order
priority = []string{
"zfs",
}
// FsNames maps filesystem id to name of the filesystem.
FsNames = map[FsMagic]string{
FsMagicZfs: "zfs",
}
)
// GetFSMagic returns the filesystem id given the path.
func GetFSMagic(rootpath string) (FsMagic, error) {
return 0, nil
}
// Mounted checks if the given path is mounted as the fs type
//Solaris supports only ZFS for now
func Mounted(fsType FsMagic, mountPath string) (bool, error) {
cs := C.CString(filepath.Dir(mountPath))
buf := C.getstatfs(cs)
// on Solaris buf.f_basetype contains ['z', 'f', 's', 0 ... ]
if (buf.f_basetype[0] != 122) || (buf.f_basetype[1] != 102) || (buf.f_basetype[2] != 115) ||
(buf.f_basetype[3] != 0) {
log.Debugf("[zfs] no zfs dataset found for rootdir '%s'", mountPath)
C.free(unsafe.Pointer(buf))
return false, ErrPrerequisites
}
C.free(unsafe.Pointer(buf))
C.free(unsafe.Pointer(cs))
return true, nil
}

View file

@ -1,4 +1,4 @@
// +build !linux,!windows,!freebsd
// +build !linux,!windows,!freebsd,!solaris
package graphdriver

View file

@ -4,8 +4,6 @@ var (
// Slice of drivers that should be used in order
priority = []string{
"windowsfilter",
"windowsdiff",
"vfs",
}
)

View file

@ -9,7 +9,6 @@ import (
"os"
"os/exec"
"path"
"sync"
"syscall"
"github.com/Sirupsen/logrus"
@ -92,12 +91,10 @@ func (d *naiveDiffDriverWithApply) ApplyDiff(id, parent string, diff archive.Rea
// Driver contains information about the home directory and the list of active mounts that are created using this driver.
type Driver struct {
home string
pathCacheLock sync.Mutex
pathCache map[string]string
uidMaps []idtools.IDMap
gidMaps []idtools.IDMap
ctr *graphdriver.RefCounter
home string
uidMaps []idtools.IDMap
gidMaps []idtools.IDMap
ctr *graphdriver.RefCounter
}
func init() {
@ -141,11 +138,10 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
}
d := &Driver{
home: home,
pathCache: make(map[string]string),
uidMaps: uidMaps,
gidMaps: gidMaps,
ctr: graphdriver.NewRefCounter(),
home: home,
uidMaps: uidMaps,
gidMaps: gidMaps,
ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
}
return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
@ -328,110 +324,64 @@ func (d *Driver) Remove(id string) error {
if err := os.RemoveAll(d.dir(id)); err != nil && !os.IsNotExist(err) {
return err
}
d.pathCacheLock.Lock()
delete(d.pathCache, id)
d.pathCacheLock.Unlock()
return nil
}
// Get creates and mounts the required file system for the given id and returns the mount path.
func (d *Driver) Get(id string, mountLabel string) (string, error) {
func (d *Driver) Get(id string, mountLabel string) (s string, err error) {
dir := d.dir(id)
if _, err := os.Stat(dir); err != nil {
return "", err
}
// If id has a root, just return it
rootDir := path.Join(dir, "root")
if _, err := os.Stat(rootDir); err == nil {
d.pathCacheLock.Lock()
d.pathCache[id] = rootDir
d.pathCacheLock.Unlock()
return rootDir, nil
}
mergedDir := path.Join(dir, "merged")
if count := d.ctr.Increment(mergedDir); count > 1 {
return mergedDir, nil
}
defer func() {
if err != nil {
if c := d.ctr.Decrement(mergedDir); c <= 0 {
syscall.Unmount(mergedDir, 0)
}
}
}()
lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id"))
if err != nil {
return "", err
}
lowerDir := path.Join(d.dir(string(lowerID)), "root")
upperDir := path.Join(dir, "upper")
workDir := path.Join(dir, "work")
mergedDir := path.Join(dir, "merged")
if count := d.ctr.Increment(id); count > 1 {
return mergedDir, nil
}
opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir)
// if it's mounted already, just return
mounted, err := d.mounted(mergedDir)
if err != nil {
d.ctr.Decrement(id)
return "", err
}
if mounted {
d.ctr.Decrement(id)
return mergedDir, nil
}
var (
lowerDir = path.Join(d.dir(string(lowerID)), "root")
upperDir = path.Join(dir, "upper")
workDir = path.Join(dir, "work")
opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir)
)
if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil {
d.ctr.Decrement(id)
return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
}
// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
// user namespace requires this to move a directory from lower to upper.
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
if err != nil {
d.ctr.Decrement(id)
syscall.Unmount(mergedDir, 0)
return "", err
}
if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
d.ctr.Decrement(id)
syscall.Unmount(mergedDir, 0)
return "", err
}
d.pathCacheLock.Lock()
d.pathCache[id] = mergedDir
d.pathCacheLock.Unlock()
return mergedDir, nil
}
func (d *Driver) mounted(dir string) (bool, error) {
return graphdriver.Mounted(graphdriver.FsMagicOverlay, dir)
}
// Put unmounts the mount path created for the give id.
func (d *Driver) Put(id string) error {
if count := d.ctr.Decrement(id); count > 0 {
mountpoint := path.Join(d.dir(id), "merged")
if count := d.ctr.Decrement(mountpoint); count > 0 {
return nil
}
d.pathCacheLock.Lock()
mountpoint, exists := d.pathCache[id]
d.pathCacheLock.Unlock()
if !exists {
logrus.Debugf("Put on a non-mounted device %s", id)
// but it might be still here
if d.Exists(id) {
mountpoint = path.Join(d.dir(id), "merged")
}
d.pathCacheLock.Lock()
d.pathCache[id] = mountpoint
d.pathCacheLock.Unlock()
}
if mounted, err := d.mounted(mountpoint); mounted || err != nil {
if err = syscall.Unmount(mountpoint, 0); err != nil {
logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
}
return err
if err := syscall.Unmount(mountpoint, 0); err != nil {
logrus.Debugf("Failed to unmount %s overlay: %v", id, err)
}
return nil
}

View file

@ -1,4 +1,4 @@
// +build !exclude_graphdriver_zfs,linux !exclude_graphdriver_zfs,freebsd
// +build !exclude_graphdriver_zfs,linux !exclude_graphdriver_zfs,freebsd, solaris
package register

View file

@ -15,6 +15,7 @@ import (
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"
"unsafe"
@ -26,7 +27,6 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/chrootarchive"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/longpath"
@ -35,28 +35,33 @@ import (
"github.com/vbatts/tar-split/tar/storage"
)
// filterDriver is an HCSShim driver type for the Windows Filter driver.
const filterDriver = 1
// init registers the windows graph drivers to the register.
func init() {
graphdriver.Register("windowsfilter", InitFilter)
graphdriver.Register("windowsdiff", InitDiff)
reexec.Register("docker-windows-write-layer", writeLayer)
}
const (
// diffDriver is an hcsshim driver type
diffDriver = iota
// filterDriver is an hcsshim driver type
filterDriver
)
type checker struct {
}
func (c *checker) IsMounted(path string) bool {
return false
}
// Driver represents a windows graph driver.
type Driver struct {
// info stores the shim driver information
info hcsshim.DriverInfo
ctr *graphdriver.RefCounter
// it is safe for windows to use a cache here because it does not support
// restoring containers when the daemon dies.
cacheMu sync.Mutex
cache map[string]string
}
var _ graphdriver.DiffGetterDriver = &Driver{}
func isTP5OrOlder() bool {
return system.GetOSVersion().Build <= 14300
}
@ -69,32 +74,15 @@ func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap)
HomeDir: home,
Flavour: filterDriver,
},
}
return d, nil
}
// InitDiff returns a new Windows differencing disk driver.
func InitDiff(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
logrus.Debugf("WindowsGraphDriver InitDiff at %s", home)
d := &Driver{
info: hcsshim.DriverInfo{
HomeDir: home,
Flavour: diffDriver,
},
cache: make(map[string]string),
ctr: graphdriver.NewRefCounter(&checker{}),
}
return d, nil
}
// String returns the string representation of a driver.
func (d *Driver) String() string {
switch d.info.Flavour {
case diffDriver:
return "windowsdiff"
case filterDriver:
return "windowsfilter"
default:
return "Unknown driver flavour"
}
return "Windows filter storage driver"
}
// Status returns the status of the driver.
@ -238,17 +226,23 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
if err != nil {
return "", err
}
if count := d.ctr.Increment(rID); count > 1 {
return d.cache[rID], nil
}
// Getting the layer paths must be done outside of the lock.
layerChain, err := d.getLayerChain(rID)
if err != nil {
d.ctr.Decrement(rID)
return "", err
}
if err := hcsshim.ActivateLayer(d.info, rID); err != nil {
d.ctr.Decrement(rID)
return "", err
}
if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
d.ctr.Decrement(rID)
if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
logrus.Warnf("Failed to Deactivate %s: %s", id, err)
}
@ -257,11 +251,15 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
mountPath, err := hcsshim.GetLayerMountPath(d.info, rID)
if err != nil {
d.ctr.Decrement(rID)
if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
logrus.Warnf("Failed to Deactivate %s: %s", id, err)
}
return "", err
}
d.cacheMu.Lock()
d.cache[rID] = mountPath
d.cacheMu.Unlock()
// If the layer has a mount path, use that. Otherwise, use the
// folder path.
@ -282,6 +280,12 @@ func (d *Driver) Put(id string) error {
if err != nil {
return err
}
if count := d.ctr.Decrement(rID); count > 0 {
return nil
}
d.cacheMu.Lock()
delete(d.cache, rID)
d.cacheMu.Unlock()
if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
return err
@ -390,20 +394,6 @@ func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
// new layer in bytes.
// The layer should not be mounted when calling this function
func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (int64, error) {
if d.info.Flavour == diffDriver {
start := time.Now().UTC()
logrus.Debugf("WindowsGraphDriver ApplyDiff: Start untar layer")
destination := d.dir(id)
destination = filepath.Dir(destination)
size, err := chrootarchive.ApplyUncompressedLayer(destination, diff, nil)
if err != nil {
return 0, err
}
logrus.Debugf("WindowsGraphDriver ApplyDiff: Untar time: %vs", time.Now().UTC().Sub(start).Seconds())
return size, nil
}
var layerChain []string
if parent != "" {
rPId, err := d.resolveID(parent)

View file

@ -1,4 +1,4 @@
// +build linux freebsd
// +build linux freebsd solaris
package zfs
@ -105,7 +105,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
filesystemsCache: filesystemsCache,
uidMaps: uidMaps,
gidMaps: gidMaps,
ctr: graphdriver.NewRefCounter(),
ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()),
}
return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
}
@ -307,7 +307,7 @@ func (d *Driver) Remove(id string) error {
// Get returns the mountpoint for the given id after creating the target directories if necessary.
func (d *Driver) Get(id, mountLabel string) (string, error) {
mountpoint := d.mountPath(id)
if count := d.ctr.Increment(id); count > 1 {
if count := d.ctr.Increment(mountpoint); count > 1 {
return mountpoint, nil
}
@ -317,17 +317,17 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
if err != nil {
d.ctr.Decrement(id)
d.ctr.Decrement(mountpoint)
return "", err
}
// Create the target directories if they don't exist
if err := idtools.MkdirAllAs(mountpoint, 0755, rootUID, rootGID); err != nil {
d.ctr.Decrement(id)
d.ctr.Decrement(mountpoint)
return "", err
}
if err := mount.Mount(filesystem, mountpoint, "zfs", options); err != nil {
d.ctr.Decrement(id)
d.ctr.Decrement(mountpoint)
return "", fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err)
}
@ -335,7 +335,7 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
// permissions instead of the remapped root uid:gid (if user namespaces are enabled):
if err := os.Chown(mountpoint, rootUID, rootGID); err != nil {
mount.Unmount(mountpoint)
d.ctr.Decrement(id)
d.ctr.Decrement(mountpoint)
return "", fmt.Errorf("error modifying zfs mountpoint (%s) directory ownership: %v", mountpoint, err)
}
@ -344,10 +344,10 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
// Put removes the existing mountpoint for the given id if it exists.
func (d *Driver) Put(id string) error {
if count := d.ctr.Decrement(id); count > 0 {
mountpoint := d.mountPath(id)
if count := d.ctr.Decrement(mountpoint); count > 0 {
return nil
}
mountpoint := d.mountPath(id)
mounted, err := graphdriver.Mounted(graphdriver.FsMagicZfs, mountpoint)
if err != nil || !mounted {
return err

View file

@ -0,0 +1,59 @@
// +build solaris,cgo
package zfs
/*
#include <sys/statvfs.h>
#include <stdlib.h>
static inline struct statvfs *getstatfs(char *s) {
struct statvfs *buf;
int err;
buf = (struct statvfs *)malloc(sizeof(struct statvfs));
err = statvfs(s, buf);
return buf;
}
*/
import "C"
import (
"path/filepath"
"strings"
"unsafe"
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/graphdriver"
)
func checkRootdirFs(rootdir string) error {
cs := C.CString(filepath.Dir(rootdir))
buf := C.getstatfs(cs)
// on Solaris buf.f_basetype contains ['z', 'f', 's', 0 ... ]
if (buf.f_basetype[0] != 122) || (buf.f_basetype[1] != 102) || (buf.f_basetype[2] != 115) ||
(buf.f_basetype[3] != 0) {
log.Debugf("[zfs] no zfs dataset found for rootdir '%s'", rootdir)
C.free(unsafe.Pointer(buf))
return graphdriver.ErrPrerequisites
}
C.free(unsafe.Pointer(buf))
C.free(unsafe.Pointer(cs))
return nil
}
/* rootfs is introduced to comply with the OCI spec
which states that root filesystem must be mounted at <CID>/rootfs/ instead of <CID>/
*/
func getMountpoint(id string) string {
maxlen := 12
// we need to preserve filesystem suffix
suffix := strings.SplitN(id, "-", 2)
if len(suffix) > 1 {
return filepath.Join(id[:maxlen]+"-"+suffix[1], "rootfs", "root")
}
return filepath.Join(id[:maxlen], "rootfs", "root")
}

View file

@ -1,4 +1,4 @@
// +build !linux,!freebsd
// +build !linux,!freebsd,!solaris
package zfs

124
daemon/image.go Normal file
View file

@ -0,0 +1,124 @@
package daemon
import (
"fmt"
"github.com/docker/docker/builder"
"github.com/docker/docker/image"
"github.com/docker/docker/reference"
"github.com/docker/docker/runconfig"
containertypes "github.com/docker/engine-api/types/container"
)
// ErrImageDoesNotExist is error returned when no image can be found for a reference.
type ErrImageDoesNotExist struct {
RefOrID string
}
func (e ErrImageDoesNotExist) Error() string {
return fmt.Sprintf("no such id: %s", e.RefOrID)
}
// GetImageID returns an image ID corresponding to the image referred to by
// refOrID.
func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
id, ref, err := reference.ParseIDOrReference(refOrID)
if err != nil {
return "", err
}
if id != "" {
if _, err := daemon.imageStore.Get(image.ID(id)); err != nil {
return "", ErrImageDoesNotExist{refOrID}
}
return image.ID(id), nil
}
if id, err := daemon.referenceStore.Get(ref); err == nil {
return id, nil
}
if tagged, ok := ref.(reference.NamedTagged); ok {
if id, err := daemon.imageStore.Search(tagged.Tag()); err == nil {
for _, namedRef := range daemon.referenceStore.References(id) {
if namedRef.Name() == ref.Name() {
return id, nil
}
}
}
}
// Search based on ID
if id, err := daemon.imageStore.Search(refOrID); err == nil {
return id, nil
}
return "", ErrImageDoesNotExist{refOrID}
}
// GetImage returns an image corresponding to the image referred to by refOrID.
func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
imgID, err := daemon.GetImageID(refOrID)
if err != nil {
return nil, err
}
return daemon.imageStore.Get(imgID)
}
// GetImageOnBuild looks up a Docker image referenced by `name`.
func (daemon *Daemon) GetImageOnBuild(name string) (builder.Image, error) {
img, err := daemon.GetImage(name)
if err != nil {
return nil, err
}
return img, nil
}
// GetCachedImage returns the most recent created image that is a child
// of the image with imgID, that had the same config when it was
// created. nil is returned if a child cannot be found. An error is
// returned if the parent image cannot be found.
func (daemon *Daemon) GetCachedImage(imgID image.ID, config *containertypes.Config) (*image.Image, error) {
// Loop on the children of the given image and check the config
getMatch := func(siblings []image.ID) (*image.Image, error) {
var match *image.Image
for _, id := range siblings {
img, err := daemon.imageStore.Get(id)
if err != nil {
return nil, fmt.Errorf("unable to find image %q", id)
}
if runconfig.Compare(&img.ContainerConfig, config) {
// check for the most up to date match
if match == nil || match.Created.Before(img.Created) {
match = img
}
}
}
return match, nil
}
// In this case, this is `FROM scratch`, which isn't an actual image.
if imgID == "" {
images := daemon.imageStore.Map()
var siblings []image.ID
for id, img := range images {
if img.Parent == imgID {
siblings = append(siblings, id)
}
}
return getMatch(siblings)
}
// find match from child images
siblings := daemon.imageStore.Children(imgID)
return getMatch(siblings)
}
// GetCachedImageOnBuild returns a reference to a cached image whose parent equals `parent`
// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
func (daemon *Daemon) GetCachedImageOnBuild(imgID string, cfg *containertypes.Config) (string, error) {
cache, err := daemon.GetCachedImage(image.ID(imgID), cfg)
if cache == nil || err != nil {
return "", err
}
return cache.ID().String(), nil
}

25
daemon/image_exporter.go Normal file
View file

@ -0,0 +1,25 @@
package daemon
import (
"io"
"github.com/docker/docker/image/tarexport"
)
// ExportImage exports a list of images to the given output stream. The
// exported images are archived into a tar when written to the output
// stream. All images with the given tag and all versions containing
// the same tag are exported. names is the set of tags to export, and
// outStream is the writer which the images are written to.
func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
return imageExporter.Save(names, outStream)
}
// LoadImage uploads a set of images into the repository. This is the
// complement of ImageExport. The input stream is an uncompressed tar
// ball containing images and metadata.
func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
return imageExporter.Load(inTar, outStream, quiet)
}

82
daemon/image_history.go Normal file
View file

@ -0,0 +1,82 @@
package daemon
import (
"fmt"
"github.com/docker/docker/layer"
"github.com/docker/docker/reference"
"github.com/docker/engine-api/types"
)
// ImageHistory returns a slice of ImageHistory structures for the specified image
// name by walking the image lineage.
func (daemon *Daemon) ImageHistory(name string) ([]*types.ImageHistory, error) {
img, err := daemon.GetImage(name)
if err != nil {
return nil, err
}
history := []*types.ImageHistory{}
layerCounter := 0
rootFS := *img.RootFS
rootFS.DiffIDs = nil
for _, h := range img.History {
var layerSize int64
if !h.EmptyLayer {
if len(img.RootFS.DiffIDs) <= layerCounter {
return nil, fmt.Errorf("too many non-empty layers in History section")
}
rootFS.Append(img.RootFS.DiffIDs[layerCounter])
l, err := daemon.layerStore.Get(rootFS.ChainID())
if err != nil {
return nil, err
}
layerSize, err = l.DiffSize()
layer.ReleaseAndLog(daemon.layerStore, l)
if err != nil {
return nil, err
}
layerCounter++
}
history = append([]*types.ImageHistory{{
ID: "<missing>",
Created: h.Created.Unix(),
CreatedBy: h.CreatedBy,
Comment: h.Comment,
Size: layerSize,
}}, history...)
}
// Fill in image IDs and tags
histImg := img
id := img.ID()
for _, h := range history {
h.ID = id.String()
var tags []string
for _, r := range daemon.referenceStore.References(id) {
if _, ok := r.(reference.NamedTagged); ok {
tags = append(tags, r.String())
}
}
h.Tags = tags
id = histImg.Parent
if id == "" {
break
}
histImg, err = daemon.GetImage(id.String())
if err != nil {
break
}
}
return history, nil
}

81
daemon/image_inspect.go Normal file
View file

@ -0,0 +1,81 @@
package daemon
import (
"fmt"
"time"
"github.com/docker/docker/layer"
"github.com/docker/docker/reference"
"github.com/docker/engine-api/types"
)
// LookupImage looks up an image by name and returns it as an ImageInspect
// structure.
func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
img, err := daemon.GetImage(name)
if err != nil {
return nil, fmt.Errorf("No such image: %s", name)
}
refs := daemon.referenceStore.References(img.ID())
repoTags := []string{}
repoDigests := []string{}
for _, ref := range refs {
switch ref.(type) {
case reference.NamedTagged:
repoTags = append(repoTags, ref.String())
case reference.Canonical:
repoDigests = append(repoDigests, ref.String())
}
}
var size int64
var layerMetadata map[string]string
layerID := img.RootFS.ChainID()
if layerID != "" {
l, err := daemon.layerStore.Get(layerID)
if err != nil {
return nil, err
}
defer layer.ReleaseAndLog(daemon.layerStore, l)
size, err = l.Size()
if err != nil {
return nil, err
}
layerMetadata, err = l.Metadata()
if err != nil {
return nil, err
}
}
comment := img.Comment
if len(comment) == 0 && len(img.History) > 0 {
comment = img.History[len(img.History)-1].Comment
}
imageInspect := &types.ImageInspect{
ID: img.ID().String(),
RepoTags: repoTags,
RepoDigests: repoDigests,
Parent: img.Parent.String(),
Comment: comment,
Created: img.Created.Format(time.RFC3339Nano),
Container: img.Container,
ContainerConfig: &img.ContainerConfig,
DockerVersion: img.DockerVersion,
Author: img.Author,
Config: img.Config,
Architecture: img.Architecture,
Os: img.OS,
Size: size,
VirtualSize: size, // TODO: field unused, deprecate
RootFS: rootFSToAPIType(img.RootFS),
}
imageInspect.GraphDriver.Name = daemon.GraphDriverName()
imageInspect.GraphDriver.Data = layerMetadata
return imageInspect, nil
}

40
daemon/inspect_solaris.go Normal file
View file

@ -0,0 +1,40 @@
package daemon
import (
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/exec"
"github.com/docker/engine-api/types"
)
// This sets platform-specific fields
func setPlatformSpecificContainerFields(container *container.Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase {
return contJSONBase
}
// containerInspectPre120 get containers for pre 1.20 APIs.
func (daemon *Daemon) containerInspectPre120(name string) (*types.ContainerJSON, error) {
return daemon.containerInspectCurrent(name, false)
}
func addMountPoints(container *container.Container) []types.MountPoint {
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
for _, m := range container.MountPoints {
mountPoints = append(mountPoints, types.MountPoint{
Name: m.Name,
Source: m.Path(),
Destination: m.Destination,
Driver: m.Driver,
RW: m.RW,
})
}
return mountPoints
}
func inspectExecProcessConfig(e *exec.Config) *backend.ExecProcessConfig {
return &backend.ExecProcessConfig{
Tty: e.Tty,
Entrypoint: e.Entrypoint,
Arguments: e.Args,
}
}

View file

@ -1,4 +1,4 @@
// +build !windows
// +build !windows,!solaris
package daemon

View file

@ -1,4 +1,4 @@
// +build linux freebsd
// +build linux freebsd solaris
package daemon

18
daemon/monitor_solaris.go Normal file
View file

@ -0,0 +1,18 @@
package daemon
import (
"github.com/docker/docker/container"
"github.com/docker/docker/libcontainerd"
)
// platformConstructExitStatus returns a platform specific exit status structure
func platformConstructExitStatus(e libcontainerd.StateInfo) *container.ExitStatus {
return &container.ExitStatus{
ExitCode: int(e.ExitCode),
}
}
// postRunProcessing perfoms any processing needed on the container after it has stopped.
func (daemon *Daemon) postRunProcessing(container *container.Container, e libcontainerd.StateInfo) error {
return nil
}

12
daemon/oci_solaris.go Normal file
View file

@ -0,0 +1,12 @@
package daemon
import (
"github.com/docker/docker/container"
"github.com/docker/docker/libcontainerd"
"github.com/docker/docker/oci"
)
func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, error) {
s := oci.DefaultSpec()
return (*libcontainerd.Spec)(&s), nil
}

View file

@ -35,7 +35,7 @@ func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error {
return err
}
} else {
profile, err = seccomp.GetDefaultProfile()
profile, err = seccomp.GetDefaultProfile(rs)
if err != nil {
return err
}

View file

@ -0,0 +1,34 @@
package daemon
import (
"github.com/docker/docker/container"
"time"
)
// newStatsCollector returns a new statsCollector for collection stats
// for a registered container at the specified interval. The collector allows
// non-running containers to be added and will start processing stats when
// they are started.
func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector {
return &statsCollector{}
}
// statsCollector manages and provides container resource stats
type statsCollector struct {
}
// collect registers the container with the collector and adds it to
// the event loop for collection on the specified interval returning
// a channel for the subscriber to receive on.
func (s *statsCollector) collect(c *container.Container) chan interface{} {
return nil
}
// stopCollection closes the channels for all subscribers and removes
// the container from metrics collection.
func (s *statsCollector) stopCollection(c *container.Container) {
}
// unsubscribe removes a specific subscriber from receiving updates for a container's stats.
func (s *statsCollector) unsubscribe(c *container.Container, ch chan interface{}) {
}

View file

@ -1,4 +1,4 @@
// +build !windows
// +build !windows,!solaris
package daemon

11
daemon/update_solaris.go Normal file
View file

@ -0,0 +1,11 @@
package daemon
import (
"github.com/docker/docker/libcontainerd"
"github.com/docker/engine-api/types/container"
)
func toContainerdResources(resources container.Resources) libcontainerd.Resources {
var r libcontainerd.Resources
return r
}

View file

@ -121,10 +121,6 @@ func (ls *mockLayerStore) GetMountID(string) (string, error) {
return "", errors.New("not implemented")
}
func (ls *mockLayerStore) ReinitRWLayer(layer.RWLayer) error {
return errors.New("not implemented")
}
func (ls *mockLayerStore) Cleanup() error {
return nil
}

View file

@ -23,9 +23,8 @@ HUGO_BASE_URL=$(shell test -z "$(DOCKER_IP)" && echo localhost || echo "$(DOCKER
HUGO_BIND_IP=0.0.0.0
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
DOCKER_IMAGE := docker$(if $(GIT_BRANCH),:$(GIT_BRANCH))
DOCKER_DOCS_IMAGE := docs-base$(if $(GIT_BRANCH),:$(GIT_BRANCH))
GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g")
DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH_CLEAN),:$(GIT_BRANCH_CLEAN))
DOCKER_RUN_DOCS := docker run --rm -it $(DOCS_MOUNT) -e AWS_S3_BUCKET -e NOCACHE

View file

@ -22,6 +22,13 @@ Unfortunately, Docker is a fast moving project, and newly introduced features
may sometime introduce breaking changes and/or incompatibilities. This page
documents these by Engine version.
# Engine 1.12
Docker clients <= 1.9.2 used an invalid Host header when making request to the
daemon. Docker 1.12 is built using golang 1.6 which is now checking the validity
of the Host header and as such clients <= 1.9.2 can't talk anymore to the daemon.
[An environment variable was added to overcome this issue.](reference/commandline/dockerd.md#miscellaneous-options)
# Engine 1.10
There were two breaking changes in the 1.10 release.

View file

@ -58,6 +58,15 @@ defining it at container creation (`POST /containers/create`).
The `docker ps --before` and `docker ps --since` options are deprecated.
Use `docker ps --filter=before=...` and `docker ps --filter=since=...` instead.
### Docker search 'automated' and 'stars' options
**Deprecated in Release: [v1.12.0](https://github.com/docker/docker/releases/tag/v1.12.0)**
**Removed In Release: v1.14**
The `docker search --automated` and `docker search --stars` options are deprecated.
Use `docker search --filter=is-automated=...` and `docker search --filter=stars=...` instead.
### Command line short variant options
**Deprecated In Release: v1.9**

View file

@ -118,6 +118,9 @@ This section lists each version from latest to oldest. Each listing includes a
* `POST /containers/create` now takes `MaximumIOps` and `MaximumIOBps` fields. Windows daemon only.
* `POST /containers/create` now returns a HTTP 400 "bad parameter" message
if no command is specified (instead of a HTTP 500 "server error")
* `GET /images/search` now takes a `filters` query parameter.
* `GET /events` now supports a `reload` event that is emitted when the daemon configuration is reloaded.
* `GET /events` now supports filtering by daemon name or ID.
### v1.23 API changes

View file

@ -2133,6 +2133,10 @@ Search for an image on [Docker Hub](https://hub.docker.com).
Query Parameters:
- **term** term to search
- **filters** a JSON encoded value of the filters (a map[string][]string) to process on the images list. Available filters:
- `stars=<number>`
- `is-automated=(true|false)`
- `is-official=(true|false)`
Status Codes:
@ -2416,6 +2420,10 @@ Docker networks report the following events:
create, connect, disconnect, destroy
Docker daemon report the following event:
reload
**Example request**:
GET /events?since=1374067924
@ -2585,9 +2593,10 @@ Query Parameters:
- `event=<string>`; -- event to filter
- `image=<string>`; -- image to filter
- `label=<string>`; -- image and container label to filter
- `type=<string>`; -- either `container` or `image` or `volume` or `network`
- `type=<string>`; -- either `container` or `image` or `volume` or `network` or `daemon`
- `volume=<string>`; -- volume to filter
- `network=<string>`; -- network to filter
- `daemon=<string>`; -- daemon name or id to filter
Status Codes:

View file

@ -362,6 +362,15 @@ RUN /bin/bash -c 'source $HOME/.bashrc ; echo $HOME'
> `RUN [ "echo", "$HOME" ]` will not do variable substitution on `$HOME`.
> If you want shell processing then either use the *shell* form or execute
> a shell directly, for example: `RUN [ "sh", "-c", "echo $HOME" ]`.
>
> **Note**:
> In the *JSON* form, it is necessary to escape backslashes. This is
> particularly relevant on Windows where the backslash is the path seperator.
> The following line would otherwise be treated as *shell* form due to not
> being valid JSON, and fail in an unexpected way:
> `RUN ["c:\windows\system32\tasklist.exe"]`
> The correct syntax for this example is:
> `RUN ["c:\\windows\\system32\\tasklist.exe"]`
The cache for `RUN` instructions isn't invalidated automatically during
the next build. The cache for an instruction like

View file

@ -225,7 +225,8 @@ uploaded context. The builder reference contains detailed information on
$ docker build -t vieux/apache:2.0 .
This will build like the previous example, but it will then tag the resulting
image. The repository name will be `vieux/apache` and the tag will be `2.0`
image. The repository name will be `vieux/apache` and the tag will be `2.0`.
[Read more about valid tags](tag.md).
You can apply multiple tags to an image. For example, you can apply the `latest`
tag to a newly built image and add another tag that references a specific
@ -298,6 +299,9 @@ accessed like regular environment variables in the `RUN` instruction of the
Dockerfile. Also, these values don't persist in the intermediate or final images
like `ENV` values do.
Using this flag will not alter the output you see when the `ARG` lines from the
Dockerfile are echoed during the build process.
For detailed information on using `ARG` and `ENV` instructions, see the
[Dockerfile reference](../builder.md).

View file

@ -24,6 +24,7 @@ It can be useful to commit a container's file changes or settings into a new
image. This allows you debug a container by running an interactive shell, or to
export a working dataset to another server. Generally, it is better to use
Dockerfiles to manage your images in a documented and maintainable way.
[Read more about valid image names and tags](tag.md).
The commit operation will not include any data contained in
volumes mounted inside the container.

View file

@ -81,7 +81,17 @@ you must be explicit with a relative or absolute path, for example:
`/path/to/file:name.txt` or `./file:name.txt`
It is not possible to copy certain system files such as resources under
`/proc`, `/sys`, `/dev`, and mounts created by the user in the container.
`/proc`, `/sys`, `/dev`, [tmpfs](run.md#mount-tmpfs-tmpfs), and mounts created by
the user in the container. However, you can still copy such files by manually
running `tar` in `docker exec`. For example (consider `SRC_PATH` and `DEST_PATH`
are directories):
$ docker exec foo tar Ccf $(dirname SRC_PATH) - $(basename SRC_PATH) | tar Cxf DEST_PATH -
or
$ tar Ccf $(dirname SRC_PATH) - $(basename SRC_PATH) | docker exec -i foo tar Cxf DEST_PATH -
Using `-` as the `SRC_PATH` streams the contents of `STDIN` as a tar archive.
The command extracts the content of the tar to the `DEST_PATH` in container's

View file

@ -164,7 +164,8 @@ Linux namespaces. On Microsoft Windows, you can specify these values:
| Value | Description |
|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `default` | Use the value specified by the Docker daemon's `--exec-opt` . If the `daemon` does not specify an isolation technology, Microsoft Windows uses `process` as its default value. |
| `default` | Use the value specified by the Docker daemon's `--exec-opt` . If the `daemon` does not specify an isolation technology, Microsoft Windows uses `process` as its default value if the
daemon is running on Windows server, or `hyperv` if running on Windows client. |
| `process` | Namespace isolation only. |
| `hyperv` | Hyper-V hypervisor partition-based isolation. |

Some files were not shown because too many files have changed in this diff Show more