mirror of https://github.com/docker/docs.git
				
				
				
			Merge branch 'master' into b
This commit is contained in:
		
						commit
						59bb86a964
					
				|  | @ -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 | ||||
|  |  | |||
|  | @ -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" | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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) | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,5 +38,4 @@ func (cli *DockerCli) CmdExport(args ...string) error { | |||
| 	} | ||||
| 
 | ||||
| 	return copyToFile(*outfile, responseBody) | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
|  |  | |||
|  | @ -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) | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
|  | @ -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 | ||||
| 		} | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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) | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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()) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										55
									
								
								cli/cli.go
								
								
								
								
							
							
						
						
									
										55
									
								
								cli/cli.go
								
								
								
								
							|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 ( | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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) \ | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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) | ||||
| } | ||||
|  | @ -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"` | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -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 | ||||
|  |  | |||
							
								
								
									
										392
									
								
								daemon/daemon.go
								
								
								
								
							
							
						
						
									
										392
									
								
								daemon/daemon.go
								
								
								
								
							|  | @ -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 ®istrytypes.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 { | ||||
|  |  | |||
|  | @ -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{} | ||||
| } | ||||
|  | @ -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") | ||||
| 	} | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // +build !linux,!freebsd,!windows
 | ||||
| // +build !linux,!freebsd,!windows,!solaris
 | ||||
| 
 | ||||
| package daemon | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
| 		} | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // +build !linux,!darwin,!freebsd,!windows
 | ||||
| // +build !linux,!darwin,!freebsd,!windows,!solaris
 | ||||
| 
 | ||||
| package daemon | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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) | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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) | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -1,4 +1,4 @@ | |||
| // +build !linux,!windows,!freebsd
 | ||||
| // +build !linux,!windows,!freebsd,!solaris
 | ||||
| 
 | ||||
| package graphdriver | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,8 +4,6 @@ var ( | |||
| 	// Slice of drivers that should be used in order
 | ||||
| 	priority = []string{ | ||||
| 		"windowsfilter", | ||||
| 		"windowsdiff", | ||||
| 		"vfs", | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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") | ||||
| } | ||||
|  | @ -1,4 +1,4 @@ | |||
| // +build !linux,!freebsd
 | ||||
| // +build !linux,!freebsd,!solaris
 | ||||
| 
 | ||||
| package zfs | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -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) | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
|  | @ -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, | ||||
| 	} | ||||
| } | ||||
|  | @ -1,4 +1,4 @@ | |||
| // +build !windows
 | ||||
| // +build !windows,!solaris
 | ||||
| 
 | ||||
| package daemon | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| // +build linux freebsd
 | ||||
| // +build linux freebsd solaris
 | ||||
| 
 | ||||
| package daemon | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
|  | @ -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 | ||||
| 		} | ||||
|  |  | |||
|  | @ -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{}) { | ||||
| } | ||||
|  | @ -1,4 +1,4 @@ | |||
| // +build !windows
 | ||||
| // +build !windows,!solaris
 | ||||
| 
 | ||||
| package daemon | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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** | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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: | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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). | ||||
| 
 | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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
		Loading…
	
		Reference in New Issue