func Commit(d *daemon.Daemon, name string, c *daemon.ContainerCommitConfig) (string, error) { container, err := d.Get(name) if err != nil { return "", err } if c.Config == nil { c.Config = &runconfig.Config{} } newConfig, err := BuildFromConfig(d, c.Config, c.Changes) if err != nil { return "", err } if err := runconfig.Merge(newConfig, container.Config); err != nil { return "", err } img, err := d.Commit(container, c.Repo, c.Tag, c.Comment, c.Author, c.Pause, newConfig) if err != nil { return "", err } return img.ID, nil }
func (daemon *Daemon) ContainerCommit(job *engine.Job) engine.Status { if len(job.Args) != 1 { return job.Errorf("Not enough arguments. Usage: %s CONTAINER\n", job.Name) } name := job.Args[0] container, err := daemon.Get(name) if err != nil { return job.Error(err) } var ( config = container.Config newConfig runconfig.Config ) if err := job.GetenvJson("config", &newConfig); err != nil { return job.Error(err) } if err := runconfig.Merge(&newConfig, config); err != nil { return job.Error(err) } img, err := daemon.Commit(container, job.Getenv("repo"), job.Getenv("tag"), job.Getenv("comment"), job.Getenv("author"), job.GetenvBool("pause"), &newConfig) if err != nil { return job.Error(err) } job.Printf("%s\n", img.ID) return engine.StatusOK }
// Commit will create a new image from a container's changes func Commit(name string, d *daemon.Daemon, c *CommitConfig) (string, error) { container, err := d.Get(name) if err != nil { return "", err } if c.Config == nil { c.Config = &runconfig.Config{} } newConfig, err := BuildFromConfig(d, c.Config, c.Changes) if err != nil { return "", err } if err := runconfig.Merge(newConfig, container.Config); err != nil { return "", err } commitCfg := &daemon.ContainerCommitConfig{ Pause: c.Pause, Repo: c.Repo, Tag: c.Tag, Author: c.Author, Comment: c.Comment, Config: newConfig, } img, err := d.Commit(container, commitCfg) if err != nil { return "", err } return img.ID, nil }
// Commit will create a new image from a container's changes // TODO: remove daemon, make Commit a method on *Builder ? func Commit(container *daemon.Container, d *daemon.Daemon, c *CommitConfig) (string, error) { // It is not possible to commit a running container on Windows if runtime.GOOS == "windows" && container.IsRunning() { return "", fmt.Errorf("Windows does not support commit of a running container") } if c.Config == nil { c.Config = &runconfig.Config{} } newConfig, err := BuildFromConfig(c.Config, c.Changes) if err != nil { return "", err } if err := runconfig.Merge(newConfig, container.Config); err != nil { return "", err } commitCfg := &daemon.ContainerCommitConfig{ Pause: c.Pause, Repo: c.Repo, Tag: c.Tag, Author: c.Author, Comment: c.Comment, Config: newConfig, } img, err := d.Commit(container, commitCfg) if err != nil { return "", err } return img.ID, nil }
// RUN some command yo // // run a command and commit the image. Args are automatically prepended with // 'sh -c' in the event there is only one argument. The difference in // processing: // // RUN echo hi # sh -c echo hi // RUN [ "echo", "hi" ] # echo hi // func run(b *Builder, args []string, attributes map[string]bool, original string) error { if b.image == "" && !b.noBaseImage { return fmt.Errorf("Please provide a source image with `from` prior to run") } args = handleJsonArgs(args, attributes) if !attributes["json"] { args = append([]string{"/bin/sh", "-c"}, args...) } runCmd := flag.NewFlagSet("run", flag.ContinueOnError) runCmd.SetOutput(ioutil.Discard) runCmd.Usage = nil config, _, _, err := runconfig.Parse(runCmd, append([]string{b.image}, args...)) if err != nil { return err } cmd := b.Config.Cmd // set Cmd manually, this is special case only for Dockerfiles b.Config.Cmd = config.Cmd runconfig.Merge(b.Config, config) defer func(cmd []string) { b.Config.Cmd = cmd }(cmd) logrus.Debugf("[BUILDER] Command to be executed: %v", b.Config.Cmd) hit, err := b.probeCache() if err != nil { return err } if hit { return nil } c, err := b.create() if err != nil { return err } // Ensure that we keep the container mounted until the commit // to avoid unmounting and then mounting directly again c.Mount() defer c.Unmount() err = b.run(c) if err != nil { return err } if err := b.commit(c.ID, cmd, "run"); err != nil { return err } return nil }
// RUN some command yo // // run a command and commit the image. Args are automatically prepended with // 'sh -c' in the event there is only one argument. The difference in // processing: // // RUN echo hi # sh -c echo hi // RUN [ "echo", "hi" ] # echo hi // func run(b *Builder, args []string, attributes map[string]bool) error { if b.image == "" { return fmt.Errorf("Please provide a source image with `from` prior to run") } args = handleJsonArgs(args, attributes) if len(args) == 1 { args = append([]string{"/bin/sh", "-c"}, args[0]) } args = append([]string{b.image}, args...) config, _, _, err := runconfig.Parse(args, nil) if err != nil { return err } cmd := b.Config.Cmd // set Cmd manually, this is special case only for Dockerfiles b.Config.Cmd = config.Cmd runconfig.Merge(b.Config, config) defer func(cmd []string) { b.Config.Cmd = cmd }(cmd) log.Debugf("Command to be executed: %v", b.Config.Cmd) hit, err := b.probeCache() if err != nil { return err } if hit { return nil } c, err := b.create() if err != nil { return err } // Ensure that we keep the container mounted until the commit // to avoid unmounting and then mounting directly again c.Mount() defer c.Unmount() err = b.run(c) if err != nil { return err } if err := b.commit(c.ID, cmd, "run"); err != nil { return err } return nil }
func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) error { if img != nil && img.Config != nil { if err := runconfig.Merge(config, img.Config); err != nil { return err } } if config.Entrypoint.Len() == 0 && config.Cmd.Len() == 0 { return fmt.Errorf("No command specified") } return nil }
func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) ([]string, error) { warnings := []string{} if (img != nil && daemon.checkDeprecatedExpose(img.Config)) || daemon.checkDeprecatedExpose(config) { warnings = append(warnings, "The mapping to public ports on your host via Dockerfile EXPOSE (host:port:port) has been deprecated. Use -p to publish the ports.") } if img != nil && img.Config != nil { if err := runconfig.Merge(config, img.Config); err != nil { return nil, err } } if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { return nil, fmt.Errorf("No command specified") } return warnings, nil }
// Commit creates a new filesystem image from the current state of a container. // The image can optionally be tagged into a repository. func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (*image.Image, error) { container, err := daemon.Get(name) if err != nil { return nil, err } // It is not possible to commit a running container on Windows if runtime.GOOS == "windows" && container.IsRunning() { return nil, fmt.Errorf("Windows does not support commit of a running container") } if c.Pause && !container.isPaused() { daemon.containerPause(container) defer daemon.containerUnpause(container) } if c.MergeConfigs { if err := runconfig.Merge(c.Config, container.Config); err != nil { return nil, err } } rwTar, err := daemon.exportContainerRw(container) if err != nil { return nil, err } defer func() { if rwTar != nil { rwTar.Close() } }() // Create a new image from the container's base layers + a new layer from container changes img, err := daemon.graph.Create(rwTar, container.ID, container.ImageID, c.Comment, c.Author, container.Config, c.Config) if err != nil { return nil, err } // Register the image if needed if c.Repo != "" { if err := daemon.repositories.Tag(c.Repo, c.Tag, img.ID, true); err != nil { return img, err } } daemon.LogContainerEvent(container, "commit") return img, nil }
func (b *buildFile) CmdRun(args string) error { if b.image == "" { return fmt.Errorf("Please provide a source image with `from` prior to run") } config, _, _, err := runconfig.Parse(append([]string{b.image}, b.buildCmdFromJson(args)...), nil) if err != nil { return err } cmd := b.config.Cmd // set Cmd manually, this is special case only for Dockerfiles b.config.Cmd = config.Cmd runconfig.Merge(b.config, config) defer func(cmd []string) { b.config.Cmd = cmd }(cmd) log.Debugf("Command to be executed: %v", b.config.Cmd) hit, err := b.probeCache() if err != nil { return err } if hit { return nil } c, err := b.create() if err != nil { return err } // Ensure that we keep the container mounted until the commit // to avoid unmounting and then mounting directly again c.Mount() defer c.Unmount() err = b.run(c) if err != nil { return err } if err := b.commit(c.ID, cmd, "run"); err != nil { return err } return nil }
func (daemon *Daemon) ContainerCommit(job *engine.Job) error { if len(job.Args) != 1 { return fmt.Errorf("Not enough arguments. Usage: %s CONTAINER\n", job.Name) } name := job.Args[0] container, err := daemon.Get(name) if err != nil { return err } var ( config = container.Config stdoutBuffer = bytes.NewBuffer(nil) newConfig runconfig.Config ) buildConfigJob := daemon.eng.Job("build_config") buildConfigJob.Stdout.Add(stdoutBuffer) buildConfigJob.Setenv("changes", job.Getenv("changes")) // FIXME this should be remove when we remove deprecated config param buildConfigJob.Setenv("config", job.Getenv("config")) if err := buildConfigJob.Run(); err != nil { return err } if err := json.NewDecoder(stdoutBuffer).Decode(&newConfig); err != nil { return err } if err := runconfig.Merge(&newConfig, config); err != nil { return err } img, err := daemon.Commit(container, job.Getenv("repo"), job.Getenv("tag"), job.Getenv("comment"), job.Getenv("author"), job.GetenvBool("pause"), &newConfig) if err != nil { return err } job.Printf("%s\n", img.ID) return nil }
func (daemon *Daemon) ContainerCommit(name string, c *ContainerCommitConfig) (string, error) { container, err := daemon.Get(name) if err != nil { return "", err } var ( subenv engine.Env config = container.Config stdoutBuffer = bytes.NewBuffer(nil) newConfig runconfig.Config ) if err := subenv.Decode(c.Config); err != nil { logrus.Errorf("%s", err) } buildConfigJob := daemon.eng.Job("build_config") buildConfigJob.Stdout.Add(stdoutBuffer) buildConfigJob.SetenvList("changes", c.Changes) // FIXME this should be remove when we remove deprecated config param buildConfigJob.SetenvSubEnv("config", &subenv) if err := buildConfigJob.Run(); err != nil { return "", err } if err := json.NewDecoder(stdoutBuffer).Decode(&newConfig); err != nil { return "", err } if err := runconfig.Merge(&newConfig, config); err != nil { return "", err } img, err := daemon.Commit(container, c.Repo, c.Tag, c.Comment, c.Author, c.Pause, &newConfig) if err != nil { return "", err } return img.ID, nil }
// RUN some command yo // // run a command and commit the image. Args are automatically prepended with // 'sh -c' under linux or 'cmd /S /C' under Windows, in the event there is // only one argument. The difference in processing: // // RUN echo hi # sh -c echo hi (Linux) // RUN echo hi # cmd /S /C echo hi (Windows) // RUN [ "echo", "hi" ] # echo hi // func run(b *Builder, args []string, attributes map[string]bool, original string) error { if b.image == "" && !b.noBaseImage { return derr.ErrorCodeMissingFrom } if err := b.flags.Parse(); err != nil { return err } args = handleJSONArgs(args, attributes) if !attributes["json"] { if runtime.GOOS != "windows" { args = append([]string{"/bin/sh", "-c"}, args...) } else { args = append([]string{"cmd", "/S", "/C"}, args...) } } runCmd := flag.NewFlagSet("run", flag.ContinueOnError) runCmd.SetOutput(ioutil.Discard) runCmd.Usage = nil config, _, _, err := runconfig.Parse(runCmd, append([]string{b.image}, args...)) if err != nil { return err } // stash the cmd cmd := b.runConfig.Cmd runconfig.Merge(b.runConfig, config) // stash the config environment env := b.runConfig.Env defer func(cmd *stringutils.StrSlice) { b.runConfig.Cmd = cmd }(cmd) defer func(env []string) { b.runConfig.Env = env }(env) // derive the net build-time environment for this run. We let config // environment override the build time environment. // This means that we take the b.buildArgs list of env vars and remove // any of those variables that are defined as part of the container. In other // words, anything in b.Config.Env. What's left is the list of build-time env // vars that we need to add to each RUN command - note the list could be empty. // // We don't persist the build time environment with container's config // environment, but just sort and prepend it to the command string at time // of commit. // This helps with tracing back the image's actual environment at the time // of RUN, without leaking it to the final image. It also aids cache // lookup for same image built with same build time environment. cmdBuildEnv := []string{} configEnv := runconfig.ConvertKVStringsToMap(b.runConfig.Env) for key, val := range b.BuildArgs { if !b.isBuildArgAllowed(key) { // skip build-args that are not in allowed list, meaning they have // not been defined by an "ARG" Dockerfile command yet. // This is an error condition but only if there is no "ARG" in the entire // Dockerfile, so we'll generate any necessary errors after we parsed // the entire file (see 'leftoverArgs' processing in evaluator.go ) continue } if _, ok := configEnv[key]; !ok { cmdBuildEnv = append(cmdBuildEnv, fmt.Sprintf("%s=%s", key, val)) } } // derive the command to use for probeCache() and to commit in this container. // Note that we only do this if there are any build-time env vars. Also, we // use the special argument "|#" at the start of the args array. This will // avoid conflicts with any RUN command since commands can not // start with | (vertical bar). The "#" (number of build envs) is there to // help ensure proper cache matches. We don't want a RUN command // that starts with "foo=abc" to be considered part of a build-time env var. saveCmd := config.Cmd if len(cmdBuildEnv) > 0 { sort.Strings(cmdBuildEnv) tmpEnv := append([]string{fmt.Sprintf("|%d", len(cmdBuildEnv))}, cmdBuildEnv...) saveCmd = stringutils.NewStrSlice(append(tmpEnv, saveCmd.Slice()...)...) } b.runConfig.Cmd = saveCmd hit, err := b.probeCache() if err != nil { return err } if hit { return nil } // set Cmd manually, this is special case only for Dockerfiles b.runConfig.Cmd = config.Cmd // set build-time environment for 'run'. b.runConfig.Env = append(b.runConfig.Env, cmdBuildEnv...) // set config as already being escaped, this prevents double escaping on windows b.runConfig.ArgsEscaped = true logrus.Debugf("[BUILDER] Command to be executed: %v", b.runConfig.Cmd) c, err := b.create() if err != nil { return err } // Ensure that we keep the container mounted until the commit // to avoid unmounting and then mounting directly again b.docker.Mount(c) defer b.docker.Unmount(c) err = b.run(c) if err != nil { return err } // revert to original config environment and set the command string to // have the build-time env vars in it (if any) so that future cache look-ups // properly match it. b.runConfig.Env = env b.runConfig.Cmd = saveCmd if err := b.commit(c.ID, cmd, "run"); err != nil { return err } return nil }
func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error) { manifest := ®istry.ManifestData{ Name: remoteName, Tag: tag, SchemaVersion: 1, } localRepo, err := s.Get(localName) if err != nil { return nil, err } if localRepo == nil { return nil, fmt.Errorf("Repo does not exist: %s", localName) } // Get the top-most layer id which the tag points to layerId, exists := localRepo[tag] if !exists { return nil, fmt.Errorf("Tag does not exist for %s: %s", localName, tag) } layersSeen := make(map[string]bool) layer, err := s.graph.Get(layerId) if err != nil { return nil, err } manifest.Architecture = layer.Architecture manifest.FSLayers = make([]*registry.FSLayer, 0, 4) manifest.History = make([]*registry.ManifestHistory, 0, 4) var metadata runconfig.Config if layer.Config != nil { metadata = *layer.Config } for ; layer != nil; layer, err = s.graph.GetParent(layer) { if err != nil { return nil, err } if layersSeen[layer.ID] { break } if layer.Config != nil && metadata.Image != layer.ID { err = runconfig.Merge(&metadata, layer.Config) if err != nil { return nil, err } } dgst, err := s.graph.GetDigest(layer.ID) if err == ErrDigestNotSet { archive, err := s.graph.TarLayer(layer) if err != nil { return nil, err } defer archive.Close() dgst, err = digest.FromReader(archive) if err != nil { return nil, err } // Save checksum value if err := s.graph.SetDigest(layer.ID, dgst); err != nil { return nil, err } } else if err != nil { return nil, fmt.Errorf("Error getting image checksum: %s", err) } jsonData, err := s.graph.RawJSON(layer.ID) if err != nil { return nil, fmt.Errorf("Cannot retrieve the path for {%s}: %s", layer.ID, err) } manifest.FSLayers = append(manifest.FSLayers, ®istry.FSLayer{BlobSum: dgst.String()}) layersSeen[layer.ID] = true manifest.History = append(manifest.History, ®istry.ManifestHistory{V1Compatibility: string(jsonData)}) } manifestBytes, err := json.MarshalIndent(manifest, "", " ") if err != nil { return nil, err } return manifestBytes, nil }
func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error) { manifest := ®istry.ManifestData{ Name: remoteName, Tag: tag, SchemaVersion: 1, } localRepo, err := s.Get(localName) if err != nil { return nil, err } if localRepo == nil { return nil, fmt.Errorf("Repo does not exist: %s", localName) } // Get the top-most layer id which the tag points to layerId, exists := localRepo[tag] if !exists { return nil, fmt.Errorf("Tag does not exist for %s: %s", localName, tag) } layersSeen := make(map[string]bool) layer, err := s.graph.Get(layerId) if err != nil { return nil, err } manifest.Architecture = layer.Architecture manifest.FSLayers = make([]*registry.FSLayer, 0, 4) manifest.History = make([]*registry.ManifestHistory, 0, 4) var metadata runconfig.Config if layer.Config != nil { metadata = *layer.Config } for ; layer != nil; layer, err = layer.GetParent() { if err != nil { return nil, err } if layersSeen[layer.ID] { break } if layer.Config != nil && metadata.Image != layer.ID { err = runconfig.Merge(&metadata, layer.Config) if err != nil { return nil, err } } checksum, err := layer.GetCheckSum(s.graph.ImageRoot(layer.ID)) if err != nil { return nil, fmt.Errorf("Error getting image checksum: %s", err) } if tarsum.VersionLabelForChecksum(checksum) != tarsum.Version1.String() { archive, err := layer.TarLayer() if err != nil { return nil, err } tarSum, err := tarsum.NewTarSum(archive, true, tarsum.Version1) if err != nil { return nil, err } if _, err := io.Copy(ioutil.Discard, tarSum); err != nil { return nil, err } checksum = tarSum.Sum(nil) // Save checksum value if err := layer.SaveCheckSum(s.graph.ImageRoot(layer.ID), checksum); err != nil { return nil, err } } jsonData, err := layer.RawJson() if err != nil { return nil, fmt.Errorf("Cannot retrieve the path for {%s}: %s", layer.ID, err) } manifest.FSLayers = append(manifest.FSLayers, ®istry.FSLayer{BlobSum: checksum}) layersSeen[layer.ID] = true manifest.History = append(manifest.History, ®istry.ManifestHistory{V1Compatibility: string(jsonData)}) } manifestBytes, err := json.MarshalIndent(manifest, "", " ") if err != nil { return nil, err } return manifestBytes, nil }
// RUN some command yo // // run a command and commit the image. Args are automatically prepended with // 'sh -c' under linux or 'cmd /S /C' under Windows, in the event there is // only one argument. The difference in processing: // // RUN echo hi # sh -c echo hi (Linux) // RUN echo hi # cmd /S /C echo hi (Windows) // RUN [ "echo", "hi" ] # echo hi // func run(b *builder, args []string, attributes map[string]bool, original string) error { if b.image == "" && !b.noBaseImage { return derr.ErrorCodeMissingFrom } if err := b.BuilderFlags.Parse(); err != nil { return err } args = handleJSONArgs(args, attributes) if !attributes["json"] { if runtime.GOOS != "windows" { args = append([]string{"/bin/sh", "-c"}, args...) } else { args = append([]string{"cmd", "/S /C"}, args...) } } runCmd := flag.NewFlagSet("run", flag.ContinueOnError) runCmd.SetOutput(ioutil.Discard) runCmd.Usage = nil config, _, _, err := runconfig.Parse(runCmd, append([]string{b.image}, args...)) if err != nil { return err } cmd := b.Config.Cmd // set Cmd manually, this is special case only for Dockerfiles b.Config.Cmd = config.Cmd runconfig.Merge(b.Config, config) defer func(cmd *stringutils.StrSlice) { b.Config.Cmd = cmd }(cmd) logrus.Debugf("[BUILDER] Command to be executed: %v", b.Config.Cmd) hit, err := b.probeCache() if err != nil { return err } if hit { return nil } c, err := b.create() if err != nil { return err } // Ensure that we keep the container mounted until the commit // to avoid unmounting and then mounting directly again c.Mount() defer c.Unmount() err = b.run(c) if err != nil { return err } if err := b.commit(c.ID, cmd, "run"); err != nil { return err } return nil }
func (s *TagStore) newManifest(localName, remoteName, tag string) ([]byte, error) { manifest := ®istry.ManifestData{ Name: remoteName, Tag: tag, SchemaVersion: 1, } localRepo, err := s.Get(localName) if err != nil { return nil, err } if localRepo == nil { return nil, fmt.Errorf("Repo does not exist: %s", localName) } // Get the top-most layer id which the tag points to layerId, exists := localRepo[tag] if !exists { return nil, fmt.Errorf("Tag does not exist for %s: %s", localName, tag) } layersSeen := make(map[string]bool) layer, err := s.graph.Get(layerId) if err != nil { return nil, err } if layer.Config == nil { return nil, errors.New("Missing layer configuration") } manifest.Architecture = layer.Architecture manifest.FSLayers = make([]*registry.FSLayer, 0, 4) manifest.History = make([]*registry.ManifestHistory, 0, 4) var metadata runconfig.Config metadata = *layer.Config for ; layer != nil; layer, err = layer.GetParent() { if err != nil { return nil, err } if layersSeen[layer.ID] { break } if layer.Config != nil && metadata.Image != layer.ID { err = runconfig.Merge(&metadata, layer.Config) if err != nil { return nil, err } } archive, err := layer.TarLayer() if err != nil { return nil, err } tarSum, err := tarsum.NewTarSum(archive, true, tarsum.Version1) if err != nil { return nil, err } if _, err := io.Copy(ioutil.Discard, tarSum); err != nil { return nil, err } tarId := tarSum.Sum(nil) manifest.FSLayers = append(manifest.FSLayers, ®istry.FSLayer{BlobSum: tarId}) layersSeen[layer.ID] = true jsonData, err := ioutil.ReadFile(path.Join(s.graph.Root, layer.ID, "json")) if err != nil { return nil, fmt.Errorf("Cannot retrieve the path for {%s}: %s", layer.ID, err) } manifest.History = append(manifest.History, ®istry.ManifestHistory{V1Compatibility: string(jsonData)}) } manifestBytes, err := json.MarshalIndent(manifest, "", " ") if err != nil { return nil, err } return manifestBytes, nil }
func (p *v2Pusher) pushV2Tag(tag string) error { logrus.Debugf("Pushing repository: %s:%s", p.repo.Name(), tag) layerID, exists := p.localRepo[tag] if !exists { return fmt.Errorf("tag does not exist: %s", tag) } layersSeen := make(map[string]bool) layer, err := p.graph.Get(layerID) if err != nil { return err } m := &manifest.Manifest{ Versioned: manifest.Versioned{ SchemaVersion: 1, }, Name: p.repo.Name(), Tag: tag, Architecture: layer.Architecture, FSLayers: []manifest.FSLayer{}, History: []manifest.History{}, } var metadata runconfig.Config if layer != nil && layer.Config != nil { metadata = *layer.Config } out := p.config.OutStream for ; layer != nil; layer, err = p.graph.GetParent(layer) { if err != nil { return err } // break early if layer has already been seen in this image, // this prevents infinite loops on layers which loopback, this // cannot be prevented since layer IDs are not merkle hashes // TODO(dmcgowan): throw error if no valid use case is found if layersSeen[layer.ID] { break } logrus.Debugf("Pushing layer: %s", layer.ID) if layer.Config != nil && metadata.Image != layer.ID { if err := runconfig.Merge(&metadata, layer.Config); err != nil { return err } } var exists bool dgst, err := p.graph.GetLayerDigest(layer.ID) switch err { case nil: if p.layersPushed[dgst] { exists = true // break out of switch, it is already known that // the push is not needed and therefore doing a // stat is unnecessary break } _, err := p.repo.Blobs(context.Background()).Stat(context.Background(), dgst) switch err { case nil: exists = true out.Write(p.sf.FormatProgress(stringid.TruncateID(layer.ID), "Image already exists", nil)) case distribution.ErrBlobUnknown: // nop default: out.Write(p.sf.FormatProgress(stringid.TruncateID(layer.ID), "Image push failed", nil)) return err } case ErrDigestNotSet: // nop case digest.ErrDigestInvalidFormat, digest.ErrDigestUnsupported: return fmt.Errorf("error getting image checksum: %v", err) } // if digest was empty or not saved, or if blob does not exist on the remote repository, // then fetch it. if !exists { var pushDigest digest.Digest if pushDigest, err = p.pushV2Image(p.repo.Blobs(context.Background()), layer); err != nil { return err } if dgst == "" { // Cache new checksum if err := p.graph.SetLayerDigest(layer.ID, pushDigest); err != nil { return err } } dgst = pushDigest } // read v1Compatibility config, generate new if needed jsonData, err := p.graph.GenerateV1CompatibilityChain(layer.ID) if err != nil { return err } m.FSLayers = append(m.FSLayers, manifest.FSLayer{BlobSum: dgst}) m.History = append(m.History, manifest.History{V1Compatibility: string(jsonData)}) layersSeen[layer.ID] = true p.layersPushed[dgst] = true } // Fix parent chain if necessary if err = fixHistory(m); err != nil { return err } logrus.Infof("Signed manifest for %s:%s using daemon's key: %s", p.repo.Name(), tag, p.trustKey.KeyID()) signed, err := manifest.Sign(m, p.trustKey) if err != nil { return err } manifestDigest, manifestSize, err := digestFromManifest(signed, p.repo.Name()) if err != nil { return err } if manifestDigest != "" { out.Write(p.sf.FormatStatus("", "%s: digest: %s size: %d", tag, manifestDigest, manifestSize)) } manSvc, err := p.repo.Manifests(context.Background()) if err != nil { return err } return manSvc.Put(signed) }
// Commit creates a new filesystem image from the current state of a container. // The image can optionally be tagged into a repository. func (daemon *Daemon) Commit(name string, c *ContainerCommitConfig) (string, error) { container, err := daemon.Get(name) if err != nil { return "", err } // It is not possible to commit a running container on Windows if runtime.GOOS == "windows" && container.IsRunning() { return "", fmt.Errorf("Windows does not support commit of a running container") } if c.Pause && !container.isPaused() { daemon.containerPause(container) defer daemon.containerUnpause(container) } if c.MergeConfigs { if err := runconfig.Merge(c.Config, container.Config); err != nil { return "", err } } rwTar, err := daemon.exportContainerRw(container) if err != nil { return "", err } defer func() { if rwTar != nil { rwTar.Close() } }() var history []image.History rootFS := image.NewRootFS() if container.ImageID != "" { img, err := daemon.imageStore.Get(container.ImageID) if err != nil { return "", err } history = img.History rootFS = img.RootFS } l, err := daemon.layerStore.Register(rwTar, rootFS.ChainID()) if err != nil { return "", err } defer layer.ReleaseAndLog(daemon.layerStore, l) h := image.History{ Author: c.Author, Created: time.Now().UTC(), CreatedBy: strings.Join(container.Config.Cmd.Slice(), " "), Comment: c.Comment, EmptyLayer: true, } if diffID := l.DiffID(); layer.DigestSHA256EmptyTar != diffID { h.EmptyLayer = false rootFS.Append(diffID) } history = append(history, h) config, err := json.Marshal(&image.Image{ V1Image: image.V1Image{ DockerVersion: dockerversion.Version, Config: c.Config, Architecture: runtime.GOARCH, OS: runtime.GOOS, Container: container.ID, ContainerConfig: *container.Config, Author: c.Author, Created: h.Created, }, RootFS: rootFS, History: history, }) if err != nil { return "", err } id, err := daemon.imageStore.Create(config) if err != nil { return "", err } if container.ImageID != "" { if err := daemon.imageStore.SetParent(id, container.ImageID); err != nil { return "", err } } if c.Repo != "" { newTag, err := reference.WithName(c.Repo) // todo: should move this to API layer if err != nil { return "", err } if c.Tag != "" { if newTag, err = reference.WithTag(newTag, c.Tag); err != nil { return "", err } } if err := daemon.TagImage(newTag, id.String(), true); err != nil { return "", err } } daemon.LogContainerEvent(container, "commit") return id.String(), nil }
func (p *v2Pusher) pushV2Tag(tag string) error { logrus.Debugf("Pushing repository: %s:%s", p.repo.Name(), tag) layerID, exists := p.localRepo[tag] if !exists { return fmt.Errorf("tag does not exist: %s", tag) } layer, err := p.graph.Get(layerID) if err != nil { return err } m := &manifest.Manifest{ Versioned: manifest.Versioned{ SchemaVersion: 1, }, Name: p.repo.Name(), Tag: tag, Architecture: layer.Architecture, FSLayers: []manifest.FSLayer{}, History: []manifest.History{}, } var metadata runconfig.Config if layer != nil && layer.Config != nil { metadata = *layer.Config } out := p.config.OutStream for ; layer != nil; layer, err = p.graph.GetParent(layer) { if err != nil { return err } if p.layersSeen[layer.ID] { break } logrus.Debugf("Pushing layer: %s", layer.ID) if layer.Config != nil && metadata.Image != layer.ID { if err := runconfig.Merge(&metadata, layer.Config); err != nil { return err } } jsonData, err := p.graph.RawJSON(layer.ID) if err != nil { return fmt.Errorf("cannot retrieve the path for %s: %s", layer.ID, err) } var exists bool dgst, err := p.graph.GetDigest(layer.ID) switch err { case nil: _, err := p.repo.Blobs(nil).Stat(nil, dgst) switch err { case nil: exists = true out.Write(p.sf.FormatProgress(stringid.TruncateID(layer.ID), "Image already exists", nil)) case distribution.ErrBlobUnknown: // nop default: out.Write(p.sf.FormatProgress(stringid.TruncateID(layer.ID), "Image push failed", nil)) return err } case ErrDigestNotSet: // nop case digest.ErrDigestInvalidFormat, digest.ErrDigestUnsupported: return fmt.Errorf("error getting image checksum: %v", err) } // if digest was empty or not saved, or if blob does not exist on the remote repository, // then fetch it. if !exists { if pushDigest, err := p.pushV2Image(p.repo.Blobs(nil), layer); err != nil { return err } else if pushDigest != dgst { // Cache new checksum if err := p.graph.SetDigest(layer.ID, pushDigest); err != nil { return err } dgst = pushDigest } } m.FSLayers = append(m.FSLayers, manifest.FSLayer{BlobSum: dgst}) m.History = append(m.History, manifest.History{V1Compatibility: string(jsonData)}) p.layersSeen[layer.ID] = true } logrus.Infof("Signed manifest for %s:%s using daemon's key: %s", p.repo.Name(), tag, p.trustKey.KeyID()) signed, err := manifest.Sign(m, p.trustKey) if err != nil { return err } manifestDigest, manifestSize, err := digestFromManifest(signed, p.repo.Name()) if err != nil { return err } if manifestDigest != "" { out.Write(p.sf.FormatStatus("", "%s: digest: %s size: %d", tag, manifestDigest, manifestSize)) } manSvc, err := p.repo.Manifests(context.Background()) if err != nil { return err } return manSvc.Put(signed) }
func (s *TagStore) pushV2Repository(r *registry.Session, localRepo Repository, out io.Writer, repoInfo *registry.RepositoryInfo, tag string, sf *utils.StreamFormatter) error { endpoint, err := r.V2RegistryEndpoint(repoInfo.Index) if err != nil { if repoInfo.Index.Official { log.Debugf("Unable to push to V2 registry, falling back to v1: %s", err) return ErrV2RegistryUnavailable } return fmt.Errorf("error getting registry endpoint: %s", err) } tags, err := s.getImageTags(localRepo, tag) if err != nil { return err } if len(tags) == 0 { return fmt.Errorf("No tags to push for %s", repoInfo.LocalName) } auth, err := r.GetV2Authorization(endpoint, repoInfo.RemoteName, false) if err != nil { return fmt.Errorf("error getting authorization: %s", err) } for _, tag := range tags { log.Debugf("Pushing repository: %s:%s", repoInfo.CanonicalName, tag) layerId, exists := localRepo[tag] if !exists { return fmt.Errorf("tag does not exist: %s", tag) } layer, err := s.graph.Get(layerId) if err != nil { return err } m := ®istry.ManifestData{ SchemaVersion: 1, Name: repoInfo.RemoteName, Tag: tag, Architecture: layer.Architecture, } var metadata runconfig.Config if layer.Config != nil { metadata = *layer.Config } layersSeen := make(map[string]bool) layers := []*image.Image{layer} for ; layer != nil; layer, err = layer.GetParent() { if err != nil { return err } if layersSeen[layer.ID] { break } layers = append(layers, layer) layersSeen[layer.ID] = true } m.FSLayers = make([]*registry.FSLayer, len(layers)) m.History = make([]*registry.ManifestHistory, len(layers)) // Schema version 1 requires layer ordering from top to root for i, layer := range layers { log.Debugf("Pushing layer: %s", layer.ID) if layer.Config != nil && metadata.Image != layer.ID { err = runconfig.Merge(&metadata, layer.Config) if err != nil { return err } } jsonData, err := layer.RawJson() if err != nil { return fmt.Errorf("cannot retrieve the path for %s: %s", layer.ID, err) } checksum, err := layer.GetCheckSum(s.graph.ImageRoot(layer.ID)) if err != nil { return fmt.Errorf("error getting image checksum: %s", err) } var exists bool if len(checksum) > 0 { sumParts := strings.SplitN(checksum, ":", 2) if len(sumParts) < 2 { return fmt.Errorf("Invalid checksum: %s", checksum) } // Call mount blob exists, err = r.HeadV2ImageBlob(endpoint, repoInfo.RemoteName, sumParts[0], sumParts[1], auth) if err != nil { out.Write(sf.FormatProgress(common.TruncateID(layer.ID), "Image push failed", nil)) return err } } if !exists { if cs, err := s.pushV2Image(r, layer, endpoint, repoInfo.RemoteName, sf, out, auth); err != nil { return err } else if cs != checksum { // Cache new checksum if err := layer.SaveCheckSum(s.graph.ImageRoot(layer.ID), cs); err != nil { return err } checksum = cs } } else { out.Write(sf.FormatProgress(common.TruncateID(layer.ID), "Image already exists", nil)) } m.FSLayers[i] = ®istry.FSLayer{BlobSum: checksum} m.History[i] = ®istry.ManifestHistory{V1Compatibility: string(jsonData)} } if err := checkValidManifest(m); err != nil { return fmt.Errorf("invalid manifest: %s", err) } log.Debugf("Pushing %s:%s to v2 repository", repoInfo.LocalName, tag) mBytes, err := json.MarshalIndent(m, "", " ") if err != nil { return err } js, err := libtrust.NewJSONSignature(mBytes) if err != nil { return err } if err = js.Sign(s.trustKey); err != nil { return err } signedBody, err := js.PrettySignature("signatures") if err != nil { return err } log.Infof("Signed manifest for %s:%s using daemon's key: %s", repoInfo.LocalName, tag, s.trustKey.KeyID()) // push the manifest digest, err := r.PutV2ImageManifest(endpoint, repoInfo.RemoteName, tag, signedBody, mBytes, auth) if err != nil { return err } out.Write(sf.FormatStatus("", "Digest: %s", digest)) } return nil }