func runPush(dockerCli *command.DockerCli, remote string) error { ref, err := reference.ParseNamed(remote) if err != nil { return err } // Resolve the Repository name from fqn to RepositoryInfo repoInfo, err := registry.ParseRepositoryInfo(ref) if err != nil { return err } ctx := context.Background() // Resolve the Auth config relevant for this server authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "push") if command.IsTrusted() { return trustedPush(ctx, dockerCli, repoInfo, ref, authConfig, requestPrivilege) } responseBody, err := imagePushPrivileged(ctx, dockerCli, authConfig, ref.String(), requestPrivilege) if err != nil { return err } defer responseBody.Close() return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil) }
func runPull(dockerCli *command.DockerCli, opts pullOptions) error { distributionRef, err := reference.ParseNamed(opts.remote) if err != nil { return err } if opts.all && !reference.IsNameOnly(distributionRef) { return errors.New("tag can't be used with --all-tags/-a") } if !opts.all && reference.IsNameOnly(distributionRef) { distributionRef = reference.WithDefaultTag(distributionRef) fmt.Fprintf(dockerCli.Out(), "Using default tag: %s\n", reference.DefaultTag) } var tag string switch x := distributionRef.(type) { case reference.Canonical: tag = x.Digest().String() case reference.NamedTagged: tag = x.Tag() } registryRef := registry.ParseReference(tag) // Resolve the Repository name from fqn to RepositoryInfo repoInfo, err := registry.ParseRepositoryInfo(distributionRef) if err != nil { return err } ctx := context.Background() authConfig := dockerCli.ResolveAuthConfig(ctx, repoInfo.Index) requestPrivilege := dockerCli.RegistryAuthenticationPrivilegedFunc(repoInfo.Index, "pull") if command.IsTrusted() && !registryRef.HasDigest() { // Check if tag is digest err = trustedPull(ctx, dockerCli, repoInfo, registryRef, authConfig, requestPrivilege) } else { err = imagePullPrivileged(ctx, dockerCli, authConfig, distributionRef.String(), requestPrivilege, opts.all) } if err != nil { if strings.Contains(err.Error(), "target is a plugin") { return errors.New(err.Error() + " - Use `docker plugin install`") } return err } return nil }
func resolveServiceImageDigest(dockerCli *command.DockerCli, service *swarm.ServiceSpec) error { if !command.IsTrusted() { // Digests are resolved by the daemon when not using content // trust. return nil } image := service.TaskTemplate.ContainerSpec.Image // We only attempt to resolve the digest if the reference // could be parsed as a digest reference. Specifying an image ID // is valid but not resolvable. There is no warning message for // an image ID because it's valid to use one. if _, err := digest.ParseDigest(image); err == nil { return nil } ref, err := reference.ParseNamed(image) if err != nil { return fmt.Errorf("Could not parse image reference %s", service.TaskTemplate.ContainerSpec.Image) } if _, ok := ref.(reference.Canonical); !ok { ref = reference.WithDefaultTag(ref) taggedRef, ok := ref.(reference.NamedTagged) if !ok { // This should never happen because a reference either // has a digest, or WithDefaultTag would give it a tag. return errors.New("Failed to resolve image digest using content trust: reference is missing a tag") } resolvedImage, err := trustedResolveDigest(context.Background(), dockerCli, taggedRef) if err != nil { return fmt.Errorf("Failed to resolve image digest using content trust: %v", err) } logrus.Debugf("resolved image tag to %s using content trust", resolvedImage.String()) service.TaskTemplate.ContainerSpec.Image = resolvedImage.String() } return nil }
// rewriteDockerfileFrom rewrites the given Dockerfile by resolving images in // "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(ctx context.Context, dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) { scanner := bufio.NewScanner(dockerfile) buf := bytes.NewBuffer(nil) // Scan the lines of the Dockerfile, looking for a "FROM" line. for scanner.Scan() { line := scanner.Text() matches := dockerfileFromLinePattern.FindStringSubmatch(line) if matches != nil && matches[1] != api.NoBaseImageSpecifier { // Replace the line with a resolved "FROM repo@digest" ref, err := reference.ParseNamed(matches[1]) if err != nil { return nil, nil, err } ref = reference.WithDefaultTag(ref) if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() { trustedRef, err := translator(ctx, ref) if err != nil { return nil, nil, err } line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", trustedRef.String())) resolvedTags = append(resolvedTags, &resolvedTag{ digestRef: trustedRef, tagRef: ref, }) } } _, err := fmt.Fprintln(buf, line) if err != nil { return nil, nil, err } } return buf.Bytes(), resolvedTags, scanner.Err() }
func runPush(dockerCli *command.DockerCli, name string) error { named, err := reference.ParseNamed(name) // FIXME: validate if err != nil { return err } if reference.IsNameOnly(named) { named = reference.WithDefaultTag(named) } ref, ok := named.(reference.NamedTagged) if !ok { return fmt.Errorf("invalid name: %s", named.String()) } ctx := context.Background() repoInfo, err := registry.ParseRepositoryInfo(named) if err != nil { return err } authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) encodedAuth, err := command.EncodeAuthToBase64(authConfig) if err != nil { return err } responseBody, err := dockerCli.Client().PluginPush(ctx, ref.String(), encodedAuth) if err != nil { return err } defer responseBody.Close() if command.IsTrusted() { repoInfo.Class = "plugin" return image.PushTrustedReference(dockerCli, repoInfo, named, authConfig, responseBody) } return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil) }
func runBuild(dockerCli *command.DockerCli, options buildOptions) error { var ( buildCtx io.ReadCloser err error ) specifiedContext := options.context var ( contextDir string tempDir string relDockerfile string progBuff io.Writer buildBuff io.Writer ) progBuff = dockerCli.Out() buildBuff = dockerCli.Out() if options.quiet { progBuff = bytes.NewBuffer(nil) buildBuff = bytes.NewBuffer(nil) } switch { case specifiedContext == "-": buildCtx, relDockerfile, err = builder.GetContextFromReader(dockerCli.In(), options.dockerfileName) case urlutil.IsGitURL(specifiedContext): tempDir, relDockerfile, err = builder.GetContextFromGitURL(specifiedContext, options.dockerfileName) case urlutil.IsURL(specifiedContext): buildCtx, relDockerfile, err = builder.GetContextFromURL(progBuff, specifiedContext, options.dockerfileName) default: contextDir, relDockerfile, err = builder.GetContextFromLocalDir(specifiedContext, options.dockerfileName) } if err != nil { if options.quiet && urlutil.IsURL(specifiedContext) { fmt.Fprintln(dockerCli.Err(), progBuff) } return fmt.Errorf("unable to prepare context: %s", err) } if tempDir != "" { defer os.RemoveAll(tempDir) contextDir = tempDir } if buildCtx == nil { // And canonicalize dockerfile name to a platform-independent one relDockerfile, err = archive.CanonicalTarNameForPath(relDockerfile) if err != nil { return fmt.Errorf("cannot canonicalize dockerfile path %s: %v", relDockerfile, err) } f, err := os.Open(filepath.Join(contextDir, ".dockerignore")) if err != nil && !os.IsNotExist(err) { return err } defer f.Close() var excludes []string if err == nil { excludes, err = dockerignore.ReadAll(f) if err != nil { return err } } if err := builder.ValidateContextDirectory(contextDir, excludes); err != nil { return fmt.Errorf("Error checking context: '%s'.", err) } // If .dockerignore mentions .dockerignore or the Dockerfile // then make sure we send both files over to the daemon // because Dockerfile is, obviously, needed no matter what, and // .dockerignore is needed to know if either one needs to be // removed. The daemon will remove them for us, if needed, after it // parses the Dockerfile. Ignore errors here, as they will have been // caught by validateContextDirectory above. var includes = []string{"."} keepThem1, _ := fileutils.Matches(".dockerignore", excludes) keepThem2, _ := fileutils.Matches(relDockerfile, excludes) if keepThem1 || keepThem2 { includes = append(includes, ".dockerignore", relDockerfile) } buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{ Compression: archive.Uncompressed, ExcludePatterns: excludes, IncludeFiles: includes, }) if err != nil { return err } } ctx := context.Background() var resolvedTags []*resolvedTag if command.IsTrusted() { translator := func(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) { return TrustedReference(ctx, dockerCli, ref) } // Wrap the tar archive to replace the Dockerfile entry with the rewritten // Dockerfile which uses trusted pulls. buildCtx = replaceDockerfileTarWrapper(ctx, buildCtx, relDockerfile, translator, &resolvedTags) } // Setup an upload progress bar progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true) if !dockerCli.Out().IsTerminal() { progressOutput = &lastProgressOutput{output: progressOutput} } var body io.Reader = progress.NewProgressReader(buildCtx, progressOutput, 0, "", "Sending build context to Docker daemon") var memory int64 if options.memory != "" { parsedMemory, err := units.RAMInBytes(options.memory) if err != nil { return err } memory = parsedMemory } var memorySwap int64 if options.memorySwap != "" { if options.memorySwap == "-1" { memorySwap = -1 } else { parsedMemorySwap, err := units.RAMInBytes(options.memorySwap) if err != nil { return err } memorySwap = parsedMemorySwap } } var shmSize int64 if options.shmSize != "" { shmSize, err = units.RAMInBytes(options.shmSize) if err != nil { return err } } authConfig, _ := dockerCli.CredentialsStore().GetAll() buildOptions := types.ImageBuildOptions{ Memory: memory, MemorySwap: memorySwap, Tags: options.tags.GetAll(), SuppressOutput: options.quiet, NoCache: options.noCache, Remove: options.rm, ForceRemove: options.forceRm, PullParent: options.pull, Isolation: container.Isolation(options.isolation), CPUSetCPUs: options.cpuSetCpus, CPUSetMems: options.cpuSetMems, CPUShares: options.cpuShares, CPUQuota: options.cpuQuota, CPUPeriod: options.cpuPeriod, CgroupParent: options.cgroupParent, Dockerfile: relDockerfile, ShmSize: shmSize, Ulimits: options.ulimits.GetList(), BuildArgs: runconfigopts.ConvertKVStringsToMap(options.buildArgs.GetAll()), AuthConfigs: authConfig, Labels: runconfigopts.ConvertKVStringsToMap(options.labels.GetAll()), CacheFrom: options.cacheFrom, } response, err := dockerCli.Client().ImageBuild(ctx, body, buildOptions) if err != nil { if options.quiet { fmt.Fprintf(dockerCli.Err(), "%s", progBuff) } return err } defer response.Body.Close() err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, dockerCli.Out().FD(), dockerCli.Out().IsTerminal(), nil) if err != nil { if jerr, ok := err.(*jsonmessage.JSONError); ok { // If no error code is set, default to 1 if jerr.Code == 0 { jerr.Code = 1 } if options.quiet { fmt.Fprintf(dockerCli.Err(), "%s%s", progBuff, buildBuff) } return cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code} } } // Windows: show error message about modified file permissions if the // daemon isn't running Windows. if response.OSType != "windows" && runtime.GOOS == "windows" && !options.quiet { fmt.Fprintln(dockerCli.Err(), `SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`) } // Everything worked so if -q was provided the output from the daemon // should be just the image ID and we'll print that to stdout. if options.quiet { fmt.Fprintf(dockerCli.Out(), "%s", buildBuff) } if command.IsTrusted() { // 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 := TagTrusted(ctx, dockerCli, resolved.digestRef, resolved.tagRef); err != nil { return err } } } return nil }
func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) { stderr := dockerCli.Err() var containerIDFile *cidFile if cidfile != "" { var err error if containerIDFile, err = newCIDFile(cidfile); err != nil { return nil, err } defer containerIDFile.Close() } var trustedRef reference.Canonical _, ref, err := reference.ParseIDOrReference(config.Image) if err != nil { return nil, err } if ref != nil { ref = reference.WithDefaultTag(ref) if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() { var err error trustedRef, err = image.TrustedReference(ctx, dockerCli, ref) if err != nil { return nil, err } config.Image = trustedRef.String() } } //create the container response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name) //if image not found try to pull it if err != nil { if apiclient.IsErrImageNotFound(err) && ref != nil { fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", ref.String()) // we don't want to write to stdout anything apart from container.ID if err = pullImage(ctx, dockerCli, config.Image, stderr); err != nil { return nil, err } if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil { if err := image.TagTrusted(ctx, dockerCli, trustedRef, ref); err != nil { return nil, err } } // Retry var retryErr error response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name) if retryErr != nil { return nil, retryErr } } else { return nil, err } } for _, warning := range response.Warnings { fmt.Fprintf(stderr, "WARNING: %s\n", warning) } if containerIDFile != nil { if err = containerIDFile.Write(response.ID); err != nil { return nil, err } } return &response, nil }
func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error { // Parse name using distribution reference package to support name // containing both tag and digest. Names with both tag and digest // will be treated by the daemon as a pull by digest with // an alias for the tag (if no alias is provided). ref, err := distreference.ParseNamed(opts.name) if err != nil { return err } alias := "" if opts.alias != "" { aref, err := reference.ParseNamed(opts.alias) if err != nil { return err } aref = reference.WithDefaultTag(aref) if _, ok := aref.(reference.NamedTagged); !ok { return fmt.Errorf("invalid name: %s", opts.alias) } alias = aref.String() } ctx := context.Background() index, err := getRepoIndexFromUnnormalizedRef(ref) if err != nil { return err } remote := ref.String() _, isCanonical := ref.(distreference.Canonical) if command.IsTrusted() && !isCanonical { if alias == "" { alias = ref.String() } var nt reference.NamedTagged named, err := reference.ParseNamed(ref.Name()) if err != nil { return err } if tagged, ok := ref.(distreference.Tagged); ok { nt, err = reference.WithTag(named, tagged.Tag()) if err != nil { return err } } else { named = reference.WithDefaultTag(named) nt = named.(reference.NamedTagged) } trusted, err := image.TrustedReference(ctx, dockerCli, nt, newRegistryService()) if err != nil { return err } remote = trusted.String() } authConfig := command.ResolveAuthConfig(ctx, dockerCli, index) encodedAuth, err := command.EncodeAuthToBase64(authConfig) if err != nil { return err } registryAuthFunc := command.RegistryAuthenticationPrivilegedFunc(dockerCli, index, "plugin install") options := types.PluginInstallOptions{ RegistryAuth: encodedAuth, RemoteRef: remote, Disabled: opts.disable, AcceptAllPermissions: opts.grantPerms, AcceptPermissionsFunc: acceptPrivileges(dockerCli, opts.name), // TODO: Rename PrivilegeFunc, it has nothing to do with privileges PrivilegeFunc: registryAuthFunc, Args: opts.args, } responseBody, err := dockerCli.Client().PluginInstall(ctx, alias, options) if err != nil { if strings.Contains(err.Error(), "target is image") { return errors.New(err.Error() + " - Use `docker image pull`") } return err } defer responseBody.Close() if err := jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil); err != nil { return err } fmt.Fprintf(dockerCli.Out(), "Installed plugin %s\n", opts.name) // todo: return proper values from the API for this result return nil }