Merge branch 'master' into b
This commit is contained in:
commit
59bb86a964
183 changed files with 4555 additions and 1563 deletions
|
@ -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 {
|
||||
|
|
55
builder/dockerfile/internals_test.go
Normal file
55
builder/dockerfile/internals_test.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package dockerfile
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
func TestEmptyDockerfile(t *testing.T) {
|
||||
contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
|
||||
defer cleanup()
|
||||
|
||||
createTestTempFile(t, contextDir, builder.DefaultDockerfileName, "", 0777)
|
||||
|
||||
tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error when creating tar stream: %s", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err = tarStream.Close(); err != nil {
|
||||
t.Fatalf("Error when closing tar stream: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
context, err := builder.MakeTarSumContext(tarStream)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error when creating tar context: %s", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err = context.Close(); err != nil {
|
||||
t.Fatalf("Error when closing tar context: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
options := &types.ImageBuildOptions{}
|
||||
|
||||
b := &Builder{options: options, context: context}
|
||||
|
||||
err = b.readDockerfile()
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("No error when executing test for empty Dockerfile")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), "The Dockerfile (Dockerfile) cannot be empty") {
|
||||
t.Fatalf("Wrong error message. Should be \"%s\". Got \"%s\"", "The Dockerfile (Dockerfile) cannot be empty", err.Error())
|
||||
}
|
||||
}
|
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 {
|
||||
|
|
74
cmd/dockerd/daemon_solaris.go
Normal file
74
cmd/dockerd/daemon_solaris.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
// +build solaris
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/docker/libcontainerd"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
)
|
||||
|
||||
const defaultDaemonConfigFile = ""
|
||||
|
||||
// currentUserIsOwner checks whether the current user is the owner of the given
|
||||
// file.
|
||||
func currentUserIsOwner(f string) bool {
|
||||
if fileInfo, err := system.Stat(f); err == nil && fileInfo != nil {
|
||||
if int(fileInfo.UID()) == os.Getuid() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// setDefaultUmask sets the umask to 0022 to avoid problems
|
||||
// caused by custom umask
|
||||
func setDefaultUmask() error {
|
||||
desiredUmask := 0022
|
||||
syscall.Umask(desiredUmask)
|
||||
if umask := syscall.Umask(desiredUmask); umask != desiredUmask {
|
||||
return fmt.Errorf("failed to set umask: expected %#o, got %#o", desiredUmask, umask)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDaemonConfDir() string {
|
||||
return "/etc/docker"
|
||||
}
|
||||
|
||||
// setupConfigReloadTrap configures the USR2 signal to reload the configuration.
|
||||
func (cli *DaemonCli) setupConfigReloadTrap() {
|
||||
}
|
||||
|
||||
// notifySystem sends a message to the host when the server is ready to be used
|
||||
func notifySystem() {
|
||||
}
|
||||
|
||||
func (cli *DaemonCli) getPlatformRemoteOptions() []libcontainerd.RemoteOption {
|
||||
opts := []libcontainerd.RemoteOption{}
|
||||
return opts
|
||||
}
|
||||
|
||||
// getLibcontainerdRoot gets the root directory for libcontainerd/containerd to
|
||||
// store their state.
|
||||
func (cli *DaemonCli) getLibcontainerdRoot() string {
|
||||
return filepath.Join(cli.Config.ExecRoot, "libcontainerd")
|
||||
}
|
||||
|
||||
func allocateDaemonPort(addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// notifyShutdown is called after the daemon shuts down but before the process exits.
|
||||
func notifyShutdown(err error) {
|
||||
}
|
||||
|
||||
func wrapListeners(proto string, ls []net.Listener) []net.Listener {
|
||||
return ls
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
116
cmd/dockerd/hack/malformed_host_override.go
Normal file
116
cmd/dockerd/hack/malformed_host_override.go
Normal file
|
@ -0,0 +1,116 @@
|
|||
// +build !windows
|
||||
|
||||
package hack
|
||||
|
||||
import "net"
|
||||
|
||||
// MalformedHostHeaderOverride is a wrapper to be able
|
||||
// to overcome the 400 Bad request coming from old docker
|
||||
// clients that send an invalid Host header.
|
||||
type MalformedHostHeaderOverride struct {
|
||||
net.Listener
|
||||
}
|
||||
|
||||
// MalformedHostHeaderOverrideConn wraps the underlying unix
|
||||
// connection and keeps track of the first read from http.Server
|
||||
// which just reads the headers.
|
||||
type MalformedHostHeaderOverrideConn struct {
|
||||
net.Conn
|
||||
first bool
|
||||
}
|
||||
|
||||
var closeConnHeader = []byte("\r\nConnection: close\r")
|
||||
|
||||
// Read reads the first *read* request from http.Server to inspect
|
||||
// the Host header. If the Host starts with / then we're talking to
|
||||
// an old docker client which send an invalid Host header. To not
|
||||
// error out in http.Server we rewrite the first bytes of the request
|
||||
// to sanitize the Host header itself.
|
||||
// In case we're not dealing with old docker clients the data is just passed
|
||||
// to the server w/o modification.
|
||||
func (l *MalformedHostHeaderOverrideConn) Read(b []byte) (n int, err error) {
|
||||
// http.Server uses a 4k buffer
|
||||
if l.first && len(b) == 4096 {
|
||||
// This keeps track of the first read from http.Server which just reads
|
||||
// the headers
|
||||
l.first = false
|
||||
// The first read of the connection by http.Server is done limited to
|
||||
// DefaultMaxHeaderBytes (usually 1 << 20) + 4096.
|
||||
// Here we do the first read which gets us all the http headers to
|
||||
// be inspected and modified below.
|
||||
c, err := l.Conn.Read(b)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
var (
|
||||
start, end int
|
||||
firstLineFeed = -1
|
||||
buf []byte
|
||||
)
|
||||
for i, bb := range b[:c] {
|
||||
if bb == '\n' && firstLineFeed == -1 {
|
||||
firstLineFeed = i
|
||||
}
|
||||
if bb != '\n' {
|
||||
continue
|
||||
}
|
||||
if b[i+1] != 'H' {
|
||||
continue
|
||||
}
|
||||
if b[i+2] != 'o' {
|
||||
continue
|
||||
}
|
||||
if b[i+3] != 's' {
|
||||
continue
|
||||
}
|
||||
if b[i+4] != 't' {
|
||||
continue
|
||||
}
|
||||
if b[i+5] != ':' {
|
||||
continue
|
||||
}
|
||||
if b[i+6] != ' ' {
|
||||
continue
|
||||
}
|
||||
if b[i+7] != '/' {
|
||||
continue
|
||||
}
|
||||
// ensure clients other than the docker clients do not get this hack
|
||||
if i != firstLineFeed {
|
||||
return c, nil
|
||||
}
|
||||
start = i + 7
|
||||
// now find where the value ends
|
||||
for ii, bbb := range b[start:c] {
|
||||
if bbb == '\n' {
|
||||
end = start + ii
|
||||
break
|
||||
}
|
||||
}
|
||||
buf = make([]byte, 0, c+len(closeConnHeader)-(end-start))
|
||||
// strip the value of the host header and
|
||||
// inject `Connection: close` to ensure we don't reuse this connection
|
||||
buf = append(buf, b[:start]...)
|
||||
buf = append(buf, closeConnHeader...)
|
||||
buf = append(buf, b[end:c]...)
|
||||
copy(b, buf)
|
||||
break
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
return c, nil
|
||||
}
|
||||
return len(buf), nil
|
||||
}
|
||||
return l.Conn.Read(b)
|
||||
}
|
||||
|
||||
// Accept makes the listener accepts connections and wraps the connection
|
||||
// in a MalformedHostHeaderOverrideConn initilizing first to true.
|
||||
func (l *MalformedHostHeaderOverride) Accept() (net.Conn, error) {
|
||||
c, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
return &MalformedHostHeaderOverrideConn{c, true}, nil
|
||||
}
|
115
cmd/dockerd/hack/malformed_host_override_test.go
Normal file
115
cmd/dockerd/hack/malformed_host_override_test.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
// +build !windows
|
||||
|
||||
package hack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHeaderOverrideHack(t *testing.T) {
|
||||
client, srv := net.Pipe()
|
||||
tests := [][2][]byte{
|
||||
{
|
||||
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
|
||||
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\n"),
|
||||
},
|
||||
{
|
||||
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\nFoo: Bar\r\n"),
|
||||
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\nFoo: Bar\r\n"),
|
||||
},
|
||||
{
|
||||
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\ntest something!"),
|
||||
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\ntest something!"),
|
||||
},
|
||||
{
|
||||
[]byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\ntest something! " + strings.Repeat("test", 15000)),
|
||||
[]byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\ntest something! " + strings.Repeat("test", 15000)),
|
||||
},
|
||||
{
|
||||
[]byte("GET /foo\nFoo: Bar\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
|
||||
[]byte("GET /foo\nFoo: Bar\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"),
|
||||
},
|
||||
}
|
||||
l := MalformedHostHeaderOverrideConn{client, true}
|
||||
read := make([]byte, 4096)
|
||||
|
||||
for _, pair := range tests {
|
||||
go func(x []byte) {
|
||||
srv.Write(x)
|
||||
}(pair[0])
|
||||
n, err := l.Read(read)
|
||||
if err != nil && err != io.EOF {
|
||||
t.Fatalf("read: %d - %d, err: %v\n%s", n, len(pair[0]), err, string(read[:n]))
|
||||
}
|
||||
if !bytes.Equal(read[:n], pair[1][:n]) {
|
||||
t.Fatalf("\n%s\n%s\n", read[:n], pair[1][:n])
|
||||
}
|
||||
l.first = true
|
||||
// clean out the slice
|
||||
read = read[:0]
|
||||
}
|
||||
srv.Close()
|
||||
l.Close()
|
||||
}
|
||||
|
||||
func BenchmarkWithHack(b *testing.B) {
|
||||
client, srv := net.Pipe()
|
||||
done := make(chan struct{})
|
||||
req := []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\n")
|
||||
read := make([]byte, 4096)
|
||||
b.SetBytes(int64(len(req) * 30))
|
||||
|
||||
l := MalformedHostHeaderOverrideConn{client, true}
|
||||
go func() {
|
||||
for {
|
||||
if _, err := srv.Write(req); err != nil {
|
||||
srv.Close()
|
||||
break
|
||||
}
|
||||
l.first = true // make sure each subsequent run uses the hack parsing
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for i := 0; i < 30; i++ {
|
||||
if n, err := l.Read(read); err != nil && err != io.EOF {
|
||||
b.Fatalf("read: %d - %d, err: %v\n%s", n, len(req), err, string(read[:n]))
|
||||
}
|
||||
}
|
||||
}
|
||||
l.Close()
|
||||
<-done
|
||||
}
|
||||
|
||||
func BenchmarkNoHack(b *testing.B) {
|
||||
client, srv := net.Pipe()
|
||||
done := make(chan struct{})
|
||||
req := []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\n")
|
||||
read := make([]byte, 4096)
|
||||
b.SetBytes(int64(len(req) * 30))
|
||||
|
||||
go func() {
|
||||
for {
|
||||
if _, err := srv.Write(req); err != nil {
|
||||
srv.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for i := 0; i < 30; i++ {
|
||||
if _, err := client.Read(read); err != nil && err != io.EOF {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
client.Close()
|
||||
<-done
|
||||
}
|
95
container/container_solaris.go
Normal file
95
container/container_solaris.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
// +build solaris
|
||||
|
||||
package container
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/volume"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
)
|
||||
|
||||
// Container holds fields specific to the Solaris implementation. See
|
||||
// CommonContainer for standard fields common to all containers.
|
||||
type Container struct {
|
||||
CommonContainer
|
||||
|
||||
// fields below here are platform specific.
|
||||
HostnamePath string
|
||||
HostsPath string
|
||||
ResolvConfPath string
|
||||
}
|
||||
|
||||
// ExitStatus provides exit reasons for a container.
|
||||
type ExitStatus struct {
|
||||
// The exit code with which the container exited.
|
||||
ExitCode int
|
||||
}
|
||||
|
||||
// CreateDaemonEnvironment creates a new environment variable slice for this container.
|
||||
func (container *Container) CreateDaemonEnvironment(linkedEnv []string) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendNetworkMounts(container *Container, volumeMounts []volume.MountPoint) ([]volume.MountPoint, error) {
|
||||
return volumeMounts, nil
|
||||
}
|
||||
|
||||
// TrySetNetworkMount attempts to set the network mounts given a provided destination and
|
||||
// the path to use for it; return true if the given destination was a network mount file
|
||||
func (container *Container) TrySetNetworkMount(destination string, path string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// NetworkMounts returns the list of network mounts.
|
||||
func (container *Container) NetworkMounts() []Mount {
|
||||
var mount []Mount
|
||||
return mount
|
||||
}
|
||||
|
||||
// CopyImagePathContent copies files in destination to the volume.
|
||||
func (container *Container) CopyImagePathContent(v volume.Volume, destination string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmountIpcMounts unmount Ipc related mounts.
|
||||
func (container *Container) UnmountIpcMounts(unmount func(pth string) error) {
|
||||
}
|
||||
|
||||
// IpcMounts returns the list of Ipc related mounts.
|
||||
func (container *Container) IpcMounts() []Mount {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateContainer updates configuration of a container
|
||||
func (container *Container) UpdateContainer(hostConfig *container.HostConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmountVolumes explicitly unmounts volumes from the container.
|
||||
func (container *Container) UnmountVolumes(forceSyscall bool, volumeEventLog func(name, action string, attributes map[string]string)) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TmpfsMounts returns the list of tmpfs mounts
|
||||
func (container *Container) TmpfsMounts() []Mount {
|
||||
var mounts []Mount
|
||||
return mounts
|
||||
}
|
||||
|
||||
// cleanResourcePath cleans a resource path and prepares to combine with mnt path
|
||||
func cleanResourcePath(path string) string {
|
||||
return filepath.Join(string(os.PathSeparator), path)
|
||||
}
|
||||
|
||||
// BuildHostnameFile writes the container's hostname file.
|
||||
func (container *Container) BuildHostnameFile() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// canMountFS determines if the file system for the container
|
||||
// can be mounted locally. A no-op on non-Windows platforms
|
||||
func (container *Container) canMountFS() bool {
|
||||
return true
|
||||
}
|
7
container/state_solaris.go
Normal file
7
container/state_solaris.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package container
|
||||
|
||||
// setFromExitStatus is a platform specific helper function to set the state
|
||||
// based on the ExitStatus structure.
|
||||
func (s *State) setFromExitStatus(exitStatus *ExitStatus) {
|
||||
s.ExitCode = exitStatus.ExitCode
|
||||
}
|
|
@ -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
|
||||
|
|
39
daemon/config_solaris.go
Normal file
39
daemon/config_solaris.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
flag "github.com/docker/docker/pkg/mflag"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultPidFile = "/var/run/docker.pid"
|
||||
defaultGraph = "/var/lib/docker"
|
||||
defaultExec = "zones"
|
||||
)
|
||||
|
||||
// Config defines the configuration of a docker daemon.
|
||||
// These are the configuration settings that you pass
|
||||
// to the docker daemon when you launch it with say: `docker -d -e lxc`
|
||||
type Config struct {
|
||||
CommonConfig
|
||||
|
||||
// Fields below here are platform specific.
|
||||
ExecRoot string `json:"exec-root,omitempty"`
|
||||
}
|
||||
|
||||
// bridgeConfig stores all the bridge driver specific
|
||||
// configuration.
|
||||
type bridgeConfig struct {
|
||||
commonBridgeConfig
|
||||
}
|
||||
|
||||
// InstallFlags adds command-line options to the top-level flag parser for
|
||||
// the current process.
|
||||
// Subsequent calls to `flag.Parse` will populate config with values parsed
|
||||
// from the command-line.
|
||||
func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
|
||||
// First handle install flags which are consistent cross-platform
|
||||
config.InstallCommonFlags(cmd, usageFn)
|
||||
|
||||
// Then platform-specific install flags
|
||||
config.attachExperimentalFlags(cmd, usageFn)
|
||||
}
|
|
@ -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"`
|
||||
|
|
50
daemon/container_operations_solaris.go
Normal file
50
daemon/container_operations_solaris.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
// +build solaris
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
"github.com/docker/libnetwork"
|
||||
)
|
||||
|
||||
func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ConnectToNetwork connects a container to a network
|
||||
func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
|
||||
return fmt.Errorf("Solaris does not support connecting a running container to a network")
|
||||
}
|
||||
|
||||
// getSize returns real size & virtual size
|
||||
func (daemon *Daemon) getSize(container *container.Container) (int64, int64) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// DisconnectFromNetwork disconnects a container from the network
|
||||
func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n libnetwork.Network, force bool) error {
|
||||
return fmt.Errorf("Solaris does not support disconnecting a running container from a network")
|
||||
}
|
||||
|
||||
func (daemon *Daemon) setupIpcDirs(container *container.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) mountVolumes(container *container.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func killProcessDirectly(container *container.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func detachMounted(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func isLinkable(child *container.Container) bool {
|
||||
return false
|
||||
}
|
|
@ -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 {
|
||||
|
|
159
daemon/daemon_solaris.go
Normal file
159
daemon/daemon_solaris.go
Normal file
|
@ -0,0 +1,159 @@
|
|||
// +build solaris,cgo
|
||||
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/engine-api/types"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
"github.com/docker/libnetwork"
|
||||
nwconfig "github.com/docker/libnetwork/config"
|
||||
)
|
||||
|
||||
//#include <zone.h>
|
||||
import "C"
|
||||
|
||||
const (
|
||||
defaultVirtualSwitch = "Virtual Switch"
|
||||
platformSupported = true
|
||||
solarisMinCPUShares = 1
|
||||
solarisMaxCPUShares = 65535
|
||||
)
|
||||
|
||||
func (daemon *Daemon) cleanupMountsByID(id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseSecurityOpt(container *container.Container, config *containertypes.HostConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupRemappedRoot(config *Config) ([]idtools.IDMap, []idtools.IDMap, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func setupDaemonRoot(config *Config, rootDir string, rootUID, rootGID int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupInitLayer populates a directory with mountpoints suitable
|
||||
// for bind-mounting dockerinit into the container. The mountpoint is simply an
|
||||
// empty file at /.dockerinit
|
||||
//
|
||||
// This extra layer is used by all containers as the top-most ro layer. It protects
|
||||
// the container from unwanted side-effects on the rw layer.
|
||||
func setupInitLayer(initLayer string, rootUID, rootGID int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkKernel() error {
|
||||
// solaris can rely upon checkSystem() below, we don't skew kernel versions
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) getCgroupDriver() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConfig, adjustCPUShares bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyPlatformContainerSettings performs platform-specific validation of the
|
||||
// hostconfig and config structures.
|
||||
func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) {
|
||||
warnings := []string{}
|
||||
return warnings, nil
|
||||
}
|
||||
|
||||
// verifyDaemonSettings performs validation of daemon config struct
|
||||
func verifyDaemonSettings(config *Config) error {
|
||||
// checkSystem validates platform-specific requirements
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSystem() error {
|
||||
// check OS version for compatibility, ensure running in global zone
|
||||
var err error
|
||||
var id C.zoneid_t
|
||||
|
||||
if id, err = C.getzoneid(); err != nil {
|
||||
return fmt.Errorf("Exiting. Error getting zone id: %+v", err)
|
||||
}
|
||||
if int(id) != 0 {
|
||||
return fmt.Errorf("Exiting because the Docker daemon is not running in the global zone")
|
||||
}
|
||||
|
||||
v, err := kernel.GetKernelVersion()
|
||||
if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 5, Major: 12, Minor: 0}) < 0 {
|
||||
return fmt.Errorf("Your Solaris kernel version: %s doesn't support Docker. Please upgrade to 5.12.0", v.String())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// configureMaxThreads sets the Go runtime max threads threshold
|
||||
// which is 90% of the kernel setting from /proc/sys/kernel/threads-max
|
||||
func configureMaxThreads(config *Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// configureKernelSecuritySupport configures and validate security support for the kernel
|
||||
func configureKernelSecuritySupport(config *Config, driverName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkController, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// registerLinks sets up links between containers and writes the
|
||||
// configuration out for persistence.
|
||||
func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *containertypes.HostConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) cleanupMounts() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// conditionalMountOnStart is a platform specific helper function during the
|
||||
// container start to call mount.
|
||||
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// conditionalUnmountOnCleanup is a platform specific helper function called
|
||||
// during the cleanup of a container to unmount.
|
||||
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
|
||||
return daemon.Unmount(container)
|
||||
}
|
||||
|
||||
func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) error {
|
||||
// Solaris has no custom images to register
|
||||
return nil
|
||||
}
|
||||
|
||||
func driverOptions(config *Config) []nwconfig.Option {
|
||||
return []nwconfig.Option{}
|
||||
}
|
||||
|
||||
func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// setDefaultIsolation determine the default isolation mode for the
|
||||
// daemon to run in. This is only applicable on Windows
|
||||
func (daemon *Daemon) setDefaultIsolation() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func rootFSToAPIType(rootfs *image.RootFS) types.RootFS {
|
||||
return types.RootFS{}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
11
daemon/exec_solaris.go
Normal file
11
daemon/exec_solaris.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/exec"
|
||||
"github.com/docker/docker/libcontainerd"
|
||||
)
|
||||
|
||||
func execSetPlatformOpt(c *container.Container, ec *exec.Config, p *libcontainerd.Process) error {
|
||||
return nil
|
||||
}
|
|
@ -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
|
||||
|
|
65
daemon/graphdriver/driver_solaris.go
Normal file
65
daemon/graphdriver/driver_solaris.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
// +build solaris,cgo
|
||||
|
||||
package graphdriver
|
||||
|
||||
/*
|
||||
#include <sys/statvfs.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline struct statvfs *getstatfs(char *s) {
|
||||
struct statvfs *buf;
|
||||
int err;
|
||||
buf = (struct statvfs *)malloc(sizeof(struct statvfs));
|
||||
err = statvfs(s, buf);
|
||||
return buf;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"path/filepath"
|
||||
"unsafe"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
// FsMagicZfs filesystem id for Zfs
|
||||
FsMagicZfs = FsMagic(0x2fc12fc1)
|
||||
)
|
||||
|
||||
var (
|
||||
// Slice of drivers that should be used in an order
|
||||
priority = []string{
|
||||
"zfs",
|
||||
}
|
||||
|
||||
// FsNames maps filesystem id to name of the filesystem.
|
||||
FsNames = map[FsMagic]string{
|
||||
FsMagicZfs: "zfs",
|
||||
}
|
||||
)
|
||||
|
||||
// GetFSMagic returns the filesystem id given the path.
|
||||
func GetFSMagic(rootpath string) (FsMagic, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Mounted checks if the given path is mounted as the fs type
|
||||
//Solaris supports only ZFS for now
|
||||
func Mounted(fsType FsMagic, mountPath string) (bool, error) {
|
||||
|
||||
cs := C.CString(filepath.Dir(mountPath))
|
||||
buf := C.getstatfs(cs)
|
||||
|
||||
// on Solaris buf.f_basetype contains ['z', 'f', 's', 0 ... ]
|
||||
if (buf.f_basetype[0] != 122) || (buf.f_basetype[1] != 102) || (buf.f_basetype[2] != 115) ||
|
||||
(buf.f_basetype[3] != 0) {
|
||||
log.Debugf("[zfs] no zfs dataset found for rootdir '%s'", mountPath)
|
||||
C.free(unsafe.Pointer(buf))
|
||||
return false, ErrPrerequisites
|
||||
}
|
||||
|
||||
C.free(unsafe.Pointer(buf))
|
||||
C.free(unsafe.Pointer(cs))
|
||||
return true, nil
|
||||
}
|
|
@ -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
|
||||
|
|
59
daemon/graphdriver/zfs/zfs_solaris.go
Normal file
59
daemon/graphdriver/zfs/zfs_solaris.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
// +build solaris,cgo
|
||||
|
||||
package zfs
|
||||
|
||||
/*
|
||||
#include <sys/statvfs.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline struct statvfs *getstatfs(char *s) {
|
||||
struct statvfs *buf;
|
||||
int err;
|
||||
buf = (struct statvfs *)malloc(sizeof(struct statvfs));
|
||||
err = statvfs(s, buf);
|
||||
return buf;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/graphdriver"
|
||||
)
|
||||
|
||||
func checkRootdirFs(rootdir string) error {
|
||||
|
||||
cs := C.CString(filepath.Dir(rootdir))
|
||||
buf := C.getstatfs(cs)
|
||||
|
||||
// on Solaris buf.f_basetype contains ['z', 'f', 's', 0 ... ]
|
||||
if (buf.f_basetype[0] != 122) || (buf.f_basetype[1] != 102) || (buf.f_basetype[2] != 115) ||
|
||||
(buf.f_basetype[3] != 0) {
|
||||
log.Debugf("[zfs] no zfs dataset found for rootdir '%s'", rootdir)
|
||||
C.free(unsafe.Pointer(buf))
|
||||
return graphdriver.ErrPrerequisites
|
||||
}
|
||||
|
||||
C.free(unsafe.Pointer(buf))
|
||||
C.free(unsafe.Pointer(cs))
|
||||
return nil
|
||||
}
|
||||
|
||||
/* rootfs is introduced to comply with the OCI spec
|
||||
which states that root filesystem must be mounted at <CID>/rootfs/ instead of <CID>/
|
||||
*/
|
||||
func getMountpoint(id string) string {
|
||||
maxlen := 12
|
||||
|
||||
// we need to preserve filesystem suffix
|
||||
suffix := strings.SplitN(id, "-", 2)
|
||||
|
||||
if len(suffix) > 1 {
|
||||
return filepath.Join(id[:maxlen]+"-"+suffix[1], "rootfs", "root")
|
||||
}
|
||||
|
||||
return filepath.Join(id[:maxlen], "rootfs", "root")
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// +build !linux,!freebsd
|
||||
// +build !linux,!freebsd,!solaris
|
||||
|
||||
package zfs
|
||||
|
||||
|
|
124
daemon/image.go
Normal file
124
daemon/image.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/docker/runconfig"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
)
|
||||
|
||||
// ErrImageDoesNotExist is error returned when no image can be found for a reference.
|
||||
type ErrImageDoesNotExist struct {
|
||||
RefOrID string
|
||||
}
|
||||
|
||||
func (e ErrImageDoesNotExist) Error() string {
|
||||
return fmt.Sprintf("no such id: %s", e.RefOrID)
|
||||
}
|
||||
|
||||
// GetImageID returns an image ID corresponding to the image referred to by
|
||||
// refOrID.
|
||||
func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) {
|
||||
id, ref, err := reference.ParseIDOrReference(refOrID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if id != "" {
|
||||
if _, err := daemon.imageStore.Get(image.ID(id)); err != nil {
|
||||
return "", ErrImageDoesNotExist{refOrID}
|
||||
}
|
||||
return image.ID(id), nil
|
||||
}
|
||||
|
||||
if id, err := daemon.referenceStore.Get(ref); err == nil {
|
||||
return id, nil
|
||||
}
|
||||
if tagged, ok := ref.(reference.NamedTagged); ok {
|
||||
if id, err := daemon.imageStore.Search(tagged.Tag()); err == nil {
|
||||
for _, namedRef := range daemon.referenceStore.References(id) {
|
||||
if namedRef.Name() == ref.Name() {
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search based on ID
|
||||
if id, err := daemon.imageStore.Search(refOrID); err == nil {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
return "", ErrImageDoesNotExist{refOrID}
|
||||
}
|
||||
|
||||
// GetImage returns an image corresponding to the image referred to by refOrID.
|
||||
func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
|
||||
imgID, err := daemon.GetImageID(refOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return daemon.imageStore.Get(imgID)
|
||||
}
|
||||
|
||||
// GetImageOnBuild looks up a Docker image referenced by `name`.
|
||||
func (daemon *Daemon) GetImageOnBuild(name string) (builder.Image, error) {
|
||||
img, err := daemon.GetImage(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// GetCachedImage returns the most recent created image that is a child
|
||||
// of the image with imgID, that had the same config when it was
|
||||
// created. nil is returned if a child cannot be found. An error is
|
||||
// returned if the parent image cannot be found.
|
||||
func (daemon *Daemon) GetCachedImage(imgID image.ID, config *containertypes.Config) (*image.Image, error) {
|
||||
// Loop on the children of the given image and check the config
|
||||
getMatch := func(siblings []image.ID) (*image.Image, error) {
|
||||
var match *image.Image
|
||||
for _, id := range siblings {
|
||||
img, err := daemon.imageStore.Get(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to find image %q", id)
|
||||
}
|
||||
|
||||
if runconfig.Compare(&img.ContainerConfig, config) {
|
||||
// check for the most up to date match
|
||||
if match == nil || match.Created.Before(img.Created) {
|
||||
match = img
|
||||
}
|
||||
}
|
||||
}
|
||||
return match, nil
|
||||
}
|
||||
|
||||
// In this case, this is `FROM scratch`, which isn't an actual image.
|
||||
if imgID == "" {
|
||||
images := daemon.imageStore.Map()
|
||||
var siblings []image.ID
|
||||
for id, img := range images {
|
||||
if img.Parent == imgID {
|
||||
siblings = append(siblings, id)
|
||||
}
|
||||
}
|
||||
return getMatch(siblings)
|
||||
}
|
||||
|
||||
// find match from child images
|
||||
siblings := daemon.imageStore.Children(imgID)
|
||||
return getMatch(siblings)
|
||||
}
|
||||
|
||||
// GetCachedImageOnBuild returns a reference to a cached image whose parent equals `parent`
|
||||
// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
|
||||
func (daemon *Daemon) GetCachedImageOnBuild(imgID string, cfg *containertypes.Config) (string, error) {
|
||||
cache, err := daemon.GetCachedImage(image.ID(imgID), cfg)
|
||||
if cache == nil || err != nil {
|
||||
return "", err
|
||||
}
|
||||
return cache.ID().String(), nil
|
||||
}
|
25
daemon/image_exporter.go
Normal file
25
daemon/image_exporter.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/image/tarexport"
|
||||
)
|
||||
|
||||
// ExportImage exports a list of images to the given output stream. The
|
||||
// exported images are archived into a tar when written to the output
|
||||
// stream. All images with the given tag and all versions containing
|
||||
// the same tag are exported. names is the set of tags to export, and
|
||||
// outStream is the writer which the images are written to.
|
||||
func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
|
||||
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
|
||||
return imageExporter.Save(names, outStream)
|
||||
}
|
||||
|
||||
// LoadImage uploads a set of images into the repository. This is the
|
||||
// complement of ImageExport. The input stream is an uncompressed tar
|
||||
// ball containing images and metadata.
|
||||
func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
|
||||
imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore, daemon)
|
||||
return imageExporter.Load(inTar, outStream, quiet)
|
||||
}
|
82
daemon/image_history.go
Normal file
82
daemon/image_history.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// ImageHistory returns a slice of ImageHistory structures for the specified image
|
||||
// name by walking the image lineage.
|
||||
func (daemon *Daemon) ImageHistory(name string) ([]*types.ImageHistory, error) {
|
||||
img, err := daemon.GetImage(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
history := []*types.ImageHistory{}
|
||||
|
||||
layerCounter := 0
|
||||
rootFS := *img.RootFS
|
||||
rootFS.DiffIDs = nil
|
||||
|
||||
for _, h := range img.History {
|
||||
var layerSize int64
|
||||
|
||||
if !h.EmptyLayer {
|
||||
if len(img.RootFS.DiffIDs) <= layerCounter {
|
||||
return nil, fmt.Errorf("too many non-empty layers in History section")
|
||||
}
|
||||
|
||||
rootFS.Append(img.RootFS.DiffIDs[layerCounter])
|
||||
l, err := daemon.layerStore.Get(rootFS.ChainID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
layerSize, err = l.DiffSize()
|
||||
layer.ReleaseAndLog(daemon.layerStore, l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
layerCounter++
|
||||
}
|
||||
|
||||
history = append([]*types.ImageHistory{{
|
||||
ID: "<missing>",
|
||||
Created: h.Created.Unix(),
|
||||
CreatedBy: h.CreatedBy,
|
||||
Comment: h.Comment,
|
||||
Size: layerSize,
|
||||
}}, history...)
|
||||
}
|
||||
|
||||
// Fill in image IDs and tags
|
||||
histImg := img
|
||||
id := img.ID()
|
||||
for _, h := range history {
|
||||
h.ID = id.String()
|
||||
|
||||
var tags []string
|
||||
for _, r := range daemon.referenceStore.References(id) {
|
||||
if _, ok := r.(reference.NamedTagged); ok {
|
||||
tags = append(tags, r.String())
|
||||
}
|
||||
}
|
||||
|
||||
h.Tags = tags
|
||||
|
||||
id = histImg.Parent
|
||||
if id == "" {
|
||||
break
|
||||
}
|
||||
histImg, err = daemon.GetImage(id.String())
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return history, nil
|
||||
}
|
81
daemon/image_inspect.go
Normal file
81
daemon/image_inspect.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/layer"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// LookupImage looks up an image by name and returns it as an ImageInspect
|
||||
// structure.
|
||||
func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
|
||||
img, err := daemon.GetImage(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("No such image: %s", name)
|
||||
}
|
||||
|
||||
refs := daemon.referenceStore.References(img.ID())
|
||||
repoTags := []string{}
|
||||
repoDigests := []string{}
|
||||
for _, ref := range refs {
|
||||
switch ref.(type) {
|
||||
case reference.NamedTagged:
|
||||
repoTags = append(repoTags, ref.String())
|
||||
case reference.Canonical:
|
||||
repoDigests = append(repoDigests, ref.String())
|
||||
}
|
||||
}
|
||||
|
||||
var size int64
|
||||
var layerMetadata map[string]string
|
||||
layerID := img.RootFS.ChainID()
|
||||
if layerID != "" {
|
||||
l, err := daemon.layerStore.Get(layerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer layer.ReleaseAndLog(daemon.layerStore, l)
|
||||
size, err = l.Size()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
layerMetadata, err = l.Metadata()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
comment := img.Comment
|
||||
if len(comment) == 0 && len(img.History) > 0 {
|
||||
comment = img.History[len(img.History)-1].Comment
|
||||
}
|
||||
|
||||
imageInspect := &types.ImageInspect{
|
||||
ID: img.ID().String(),
|
||||
RepoTags: repoTags,
|
||||
RepoDigests: repoDigests,
|
||||
Parent: img.Parent.String(),
|
||||
Comment: comment,
|
||||
Created: img.Created.Format(time.RFC3339Nano),
|
||||
Container: img.Container,
|
||||
ContainerConfig: &img.ContainerConfig,
|
||||
DockerVersion: img.DockerVersion,
|
||||
Author: img.Author,
|
||||
Config: img.Config,
|
||||
Architecture: img.Architecture,
|
||||
Os: img.OS,
|
||||
Size: size,
|
||||
VirtualSize: size, // TODO: field unused, deprecate
|
||||
RootFS: rootFSToAPIType(img.RootFS),
|
||||
}
|
||||
|
||||
imageInspect.GraphDriver.Name = daemon.GraphDriverName()
|
||||
|
||||
imageInspect.GraphDriver.Data = layerMetadata
|
||||
|
||||
return imageInspect, nil
|
||||
}
|
40
daemon/inspect_solaris.go
Normal file
40
daemon/inspect_solaris.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/daemon/exec"
|
||||
"github.com/docker/engine-api/types"
|
||||
)
|
||||
|
||||
// This sets platform-specific fields
|
||||
func setPlatformSpecificContainerFields(container *container.Container, contJSONBase *types.ContainerJSONBase) *types.ContainerJSONBase {
|
||||
return contJSONBase
|
||||
}
|
||||
|
||||
// containerInspectPre120 get containers for pre 1.20 APIs.
|
||||
func (daemon *Daemon) containerInspectPre120(name string) (*types.ContainerJSON, error) {
|
||||
return daemon.containerInspectCurrent(name, false)
|
||||
}
|
||||
|
||||
func addMountPoints(container *container.Container) []types.MountPoint {
|
||||
mountPoints := make([]types.MountPoint, 0, len(container.MountPoints))
|
||||
for _, m := range container.MountPoints {
|
||||
mountPoints = append(mountPoints, types.MountPoint{
|
||||
Name: m.Name,
|
||||
Source: m.Path(),
|
||||
Destination: m.Destination,
|
||||
Driver: m.Driver,
|
||||
RW: m.RW,
|
||||
})
|
||||
}
|
||||
return mountPoints
|
||||
}
|
||||
|
||||
func inspectExecProcessConfig(e *exec.Config) *backend.ExecProcessConfig {
|
||||
return &backend.ExecProcessConfig{
|
||||
Tty: e.Tty,
|
||||
Entrypoint: e.Entrypoint,
|
||||
Arguments: e.Args,
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// +build !windows
|
||||
// +build !windows,!solaris
|
||||
|
||||
package daemon
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build linux freebsd
|
||||
// +build linux freebsd solaris
|
||||
|
||||
package daemon
|
||||
|
||||
|
|
18
daemon/monitor_solaris.go
Normal file
18
daemon/monitor_solaris.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/libcontainerd"
|
||||
)
|
||||
|
||||
// platformConstructExitStatus returns a platform specific exit status structure
|
||||
func platformConstructExitStatus(e libcontainerd.StateInfo) *container.ExitStatus {
|
||||
return &container.ExitStatus{
|
||||
ExitCode: int(e.ExitCode),
|
||||
}
|
||||
}
|
||||
|
||||
// postRunProcessing perfoms any processing needed on the container after it has stopped.
|
||||
func (daemon *Daemon) postRunProcessing(container *container.Container, e libcontainerd.StateInfo) error {
|
||||
return nil
|
||||
}
|
12
daemon/oci_solaris.go
Normal file
12
daemon/oci_solaris.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/libcontainerd"
|
||||
"github.com/docker/docker/oci"
|
||||
)
|
||||
|
||||
func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, error) {
|
||||
s := oci.DefaultSpec()
|
||||
return (*libcontainerd.Spec)(&s), nil
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
34
daemon/stats_collector_solaris.go
Normal file
34
daemon/stats_collector_solaris.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/container"
|
||||
"time"
|
||||
)
|
||||
|
||||
// newStatsCollector returns a new statsCollector for collection stats
|
||||
// for a registered container at the specified interval. The collector allows
|
||||
// non-running containers to be added and will start processing stats when
|
||||
// they are started.
|
||||
func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector {
|
||||
return &statsCollector{}
|
||||
}
|
||||
|
||||
// statsCollector manages and provides container resource stats
|
||||
type statsCollector struct {
|
||||
}
|
||||
|
||||
// collect registers the container with the collector and adds it to
|
||||
// the event loop for collection on the specified interval returning
|
||||
// a channel for the subscriber to receive on.
|
||||
func (s *statsCollector) collect(c *container.Container) chan interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// stopCollection closes the channels for all subscribers and removes
|
||||
// the container from metrics collection.
|
||||
func (s *statsCollector) stopCollection(c *container.Container) {
|
||||
}
|
||||
|
||||
// unsubscribe removes a specific subscriber from receiving updates for a container's stats.
|
||||
func (s *statsCollector) unsubscribe(c *container.Container, ch chan interface{}) {
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// +build !windows
|
||||
// +build !windows,!solaris
|
||||
|
||||
package daemon
|
||||
|
||||
|
|
11
daemon/update_solaris.go
Normal file
11
daemon/update_solaris.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/libcontainerd"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
)
|
||||
|
||||
func toContainerdResources(resources container.Resources) libcontainerd.Resources {
|
||||
var r libcontainerd.Resources
|
||||
return r
|
||||
}
|
|
@ -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…
Add table
Reference in a new issue