diff options
author | baude <bbaude@redhat.com> | 2018-04-03 12:34:19 -0500 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2018-04-10 13:31:59 +0000 |
commit | 1700f2b2381d9665810ed4764d0fe357150c5978 (patch) | |
tree | a7338bdc31da8ef48d337555911932b097c06f84 /libpod/buildah/config.go | |
parent | 998fd2ece0480e581e013124d0969a1af6305110 (diff) | |
download | podman-1700f2b2381d9665810ed4764d0fe357150c5978.tar.gz podman-1700f2b2381d9665810ed4764d0fe357150c5978.tar.bz2 podman-1700f2b2381d9665810ed4764d0fe357150c5978.zip |
Use buildah commit for podman commit
Resolves: #586 and #520
Signed-off-by: baude <bbaude@redhat.com>
Closes: #592
Approved by: mheon
Diffstat (limited to 'libpod/buildah/config.go')
-rw-r--r-- | libpod/buildah/config.go | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/libpod/buildah/config.go b/libpod/buildah/config.go new file mode 100644 index 000000000..0759ca9da --- /dev/null +++ b/libpod/buildah/config.go @@ -0,0 +1,607 @@ +package buildah + +import ( + "encoding/json" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/opencontainers/go-digest" + ociv1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/projectatomic/libpod/cmd/podman/docker" +) + +// makeOCIv1Image builds the best OCIv1 image structure we can from the +// contents of the docker image structure. +func makeOCIv1Image(dimage *docker.V2Image) (ociv1.Image, error) { + config := dimage.Config + if config == nil { + config = &dimage.ContainerConfig + } + dcreated := dimage.Created.UTC() + image := ociv1.Image{ + Created: &dcreated, + Author: dimage.Author, + Architecture: dimage.Architecture, + OS: dimage.OS, + Config: ociv1.ImageConfig{ + User: config.User, + ExposedPorts: map[string]struct{}{}, + Env: config.Env, + Entrypoint: config.Entrypoint, + Cmd: config.Cmd, + Volumes: config.Volumes, + WorkingDir: config.WorkingDir, + Labels: config.Labels, + }, + RootFS: ociv1.RootFS{ + Type: "", + DiffIDs: []digest.Digest{}, + }, + History: []ociv1.History{}, + } + for port, what := range config.ExposedPorts { + image.Config.ExposedPorts[string(port)] = what + } + RootFS := docker.V2S2RootFS{} + if dimage.RootFS != nil { + RootFS = *dimage.RootFS + } + if RootFS.Type == docker.TypeLayers { + image.RootFS.Type = docker.TypeLayers + image.RootFS.DiffIDs = append(image.RootFS.DiffIDs, RootFS.DiffIDs...) + } + for _, history := range dimage.History { + hcreated := history.Created.UTC() + ohistory := ociv1.History{ + Created: &hcreated, + CreatedBy: history.CreatedBy, + Author: history.Author, + Comment: history.Comment, + EmptyLayer: history.EmptyLayer, + } + image.History = append(image.History, ohistory) + } + return image, nil +} + +// makeDockerV2S2Image builds the best docker image structure we can from the +// contents of the OCI image structure. +func makeDockerV2S2Image(oimage *ociv1.Image) (docker.V2Image, error) { + image := docker.V2Image{ + V1Image: docker.V1Image{Created: oimage.Created.UTC(), + Author: oimage.Author, + Architecture: oimage.Architecture, + OS: oimage.OS, + ContainerConfig: docker.Config{ + User: oimage.Config.User, + ExposedPorts: docker.PortSet{}, + Env: oimage.Config.Env, + Entrypoint: oimage.Config.Entrypoint, + Cmd: oimage.Config.Cmd, + Volumes: oimage.Config.Volumes, + WorkingDir: oimage.Config.WorkingDir, + Labels: oimage.Config.Labels, + }, + }, + RootFS: &docker.V2S2RootFS{ + Type: "", + DiffIDs: []digest.Digest{}, + }, + History: []docker.V2S2History{}, + } + for port, what := range oimage.Config.ExposedPorts { + image.ContainerConfig.ExposedPorts[docker.Port(port)] = what + } + if oimage.RootFS.Type == docker.TypeLayers { + image.RootFS.Type = docker.TypeLayers + image.RootFS.DiffIDs = append(image.RootFS.DiffIDs, oimage.RootFS.DiffIDs...) + } + for _, history := range oimage.History { + dhistory := docker.V2S2History{ + Created: history.Created.UTC(), + CreatedBy: history.CreatedBy, + Author: history.Author, + Comment: history.Comment, + EmptyLayer: history.EmptyLayer, + } + image.History = append(image.History, dhistory) + } + image.Config = &image.ContainerConfig + return image, nil +} + +// makeDockerV2S1Image builds the best docker image structure we can from the +// contents of the V2S1 image structure. +func makeDockerV2S1Image(manifest docker.V2S1Manifest) (docker.V2Image, error) { + // Treat the most recent (first) item in the history as a description of the image. + if len(manifest.History) == 0 { + return docker.V2Image{}, errors.Errorf("error parsing image configuration from manifest") + } + dimage := docker.V2Image{} + err := json.Unmarshal([]byte(manifest.History[0].V1Compatibility), &dimage) + if err != nil { + return docker.V2Image{}, err + } + if dimage.DockerVersion == "" { + return docker.V2Image{}, errors.Errorf("error parsing image configuration from history") + } + // The DiffID list is intended to contain the sums of _uncompressed_ blobs, and these are most + // likely compressed, so leave the list empty to avoid potential confusion later on. We can + // construct a list with the correct values when we prep layers for pushing, so we don't lose. + // information by leaving this part undone. + rootFS := &docker.V2S2RootFS{ + Type: docker.TypeLayers, + DiffIDs: []digest.Digest{}, + } + // Build a filesystem history. + history := []docker.V2S2History{} + lastID := "" + for i := range manifest.History { + // Decode the compatibility field. + dcompat := docker.V1Compatibility{} + if err = json.Unmarshal([]byte(manifest.History[i].V1Compatibility), &dcompat); err != nil { + return docker.V2Image{}, errors.Errorf("error parsing image compatibility data (%q) from history", manifest.History[i].V1Compatibility) + } + // Skip this history item if it shares the ID of the last one + // that we saw, since the image library will do the same. + if i > 0 && dcompat.ID == lastID { + continue + } + lastID = dcompat.ID + // Construct a new history item using the recovered information. + createdBy := "" + if len(dcompat.ContainerConfig.Cmd) > 0 { + createdBy = strings.Join(dcompat.ContainerConfig.Cmd, " ") + } + h := docker.V2S2History{ + Created: dcompat.Created.UTC(), + Author: dcompat.Author, + CreatedBy: createdBy, + Comment: dcompat.Comment, + EmptyLayer: dcompat.ThrowAway, + } + // Prepend this layer to the list, because a v2s1 format manifest's list is in reverse order + // compared to v2s2, which lists earlier layers before later ones. + history = append([]docker.V2S2History{h}, history...) + } + dimage.RootFS = rootFS + dimage.History = history + return dimage, nil +} + +func (b *Builder) initConfig() { + image := ociv1.Image{} + dimage := docker.V2Image{} + if len(b.Config) > 0 { + // Try to parse the image configuration. If we fail start over from scratch. + if err := json.Unmarshal(b.Config, &dimage); err == nil && dimage.DockerVersion != "" { + if image, err = makeOCIv1Image(&dimage); err != nil { + image = ociv1.Image{} + } + } else { + if err := json.Unmarshal(b.Config, &image); err != nil { + if dimage, err = makeDockerV2S2Image(&image); err != nil { + dimage = docker.V2Image{} + } + } + } + b.OCIv1 = image + b.Docker = dimage + } else { + // Try to dig out the image configuration from the manifest. + manifest := docker.V2S1Manifest{} + if err := json.Unmarshal(b.Manifest, &manifest); err == nil && manifest.SchemaVersion == 1 { + if dimage, err = makeDockerV2S1Image(manifest); err == nil { + if image, err = makeOCIv1Image(&dimage); err != nil { + image = ociv1.Image{} + } + } + } + b.OCIv1 = image + b.Docker = dimage + } + if len(b.Manifest) > 0 { + // Attempt to recover format-specific data from the manifest. + v1Manifest := ociv1.Manifest{} + if json.Unmarshal(b.Manifest, &v1Manifest) == nil { + b.ImageAnnotations = v1Manifest.Annotations + } + } + b.fixupConfig() +} + +func (b *Builder) fixupConfig() { + if b.Docker.Config != nil { + // Prefer image-level settings over those from the container it was built from. + b.Docker.ContainerConfig = *b.Docker.Config + } + b.Docker.Config = &b.Docker.ContainerConfig + b.Docker.DockerVersion = "" + now := time.Now().UTC() + if b.Docker.Created.IsZero() { + b.Docker.Created = now + } + if b.OCIv1.Created == nil || b.OCIv1.Created.IsZero() { + b.OCIv1.Created = &now + } + if b.OS() == "" { + b.SetOS(runtime.GOOS) + } + if b.Architecture() == "" { + b.SetArchitecture(runtime.GOARCH) + } + if b.WorkDir() == "" { + b.SetWorkDir(string(filepath.Separator)) + } +} + +// Annotations returns a set of key-value pairs from the image's manifest. +func (b *Builder) Annotations() map[string]string { + return copyStringStringMap(b.ImageAnnotations) +} + +// SetAnnotation adds or overwrites a key's value from the image's manifest. +// Note: this setting is not present in the Docker v2 image format, so it is +// discarded when writing images using Docker v2 formats. +func (b *Builder) SetAnnotation(key, value string) { + if b.ImageAnnotations == nil { + b.ImageAnnotations = map[string]string{} + } + b.ImageAnnotations[key] = value +} + +// UnsetAnnotation removes a key and its value from the image's manifest, if +// it's present. +func (b *Builder) UnsetAnnotation(key string) { + delete(b.ImageAnnotations, key) +} + +// ClearAnnotations removes all keys and their values from the image's +// manifest. +func (b *Builder) ClearAnnotations() { + b.ImageAnnotations = map[string]string{} +} + +// CreatedBy returns a description of how this image was built. +func (b *Builder) CreatedBy() string { + return b.ImageCreatedBy +} + +// SetCreatedBy sets the description of how this image was built. +func (b *Builder) SetCreatedBy(how string) { + b.ImageCreatedBy = how +} + +// OS returns a name of the OS on which the container, or a container built +// using an image built from this container, is intended to be run. +func (b *Builder) OS() string { + return b.OCIv1.OS +} + +// SetOS sets the name of the OS on which the container, or a container built +// using an image built from this container, is intended to be run. +func (b *Builder) SetOS(os string) { + b.OCIv1.OS = os + b.Docker.OS = os +} + +// Architecture returns a name of the architecture on which the container, or a +// container built using an image built from this container, is intended to be +// run. +func (b *Builder) Architecture() string { + return b.OCIv1.Architecture +} + +// SetArchitecture sets the name of the architecture on which the container, or +// a container built using an image built from this container, is intended to +// be run. +func (b *Builder) SetArchitecture(arch string) { + b.OCIv1.Architecture = arch + b.Docker.Architecture = arch +} + +// Maintainer returns contact information for the person who built the image. +func (b *Builder) Maintainer() string { + return b.OCIv1.Author +} + +// SetMaintainer sets contact information for the person who built the image. +func (b *Builder) SetMaintainer(who string) { + b.OCIv1.Author = who + b.Docker.Author = who +} + +// User returns information about the user as whom the container, or a +// container built using an image built from this container, should be run. +func (b *Builder) User() string { + return b.OCIv1.Config.User +} + +// SetUser sets information about the user as whom the container, or a +// container built using an image built from this container, should be run. +// Acceptable forms are a user name or ID, optionally followed by a colon and a +// group name or ID. +func (b *Builder) SetUser(spec string) { + b.OCIv1.Config.User = spec + b.Docker.Config.User = spec +} + +// WorkDir returns the default working directory for running commands in the +// container, or in a container built using an image built from this container. +func (b *Builder) WorkDir() string { + return b.OCIv1.Config.WorkingDir +} + +// SetWorkDir sets the location of the default working directory for running +// commands in the container, or in a container built using an image built from +// this container. +func (b *Builder) SetWorkDir(there string) { + b.OCIv1.Config.WorkingDir = there + b.Docker.Config.WorkingDir = there +} + +// Shell returns the default shell for running commands in the +// container, or in a container built using an image built from this container. +func (b *Builder) Shell() []string { + return b.Docker.Config.Shell +} + +// SetShell sets the default shell for running +// commands in the container, or in a container built using an image built from +// this container. +// Note: this setting is not present in the OCIv1 image format, so it is +// discarded when writing images using OCIv1 formats. +func (b *Builder) SetShell(shell []string) { + b.Docker.Config.Shell = shell +} + +// Env returns a list of key-value pairs to be set when running commands in the +// container, or in a container built using an image built from this container. +func (b *Builder) Env() []string { + return copyStringSlice(b.OCIv1.Config.Env) +} + +// SetEnv adds or overwrites a value to the set of environment strings which +// should be set when running commands in the container, or in a container +// built using an image built from this container. +func (b *Builder) SetEnv(k string, v string) { + reset := func(s *[]string) { + n := []string{} + for i := range *s { + if !strings.HasPrefix((*s)[i], k+"=") { + n = append(n, (*s)[i]) + } + } + n = append(n, k+"="+v) + *s = n + } + reset(&b.OCIv1.Config.Env) + reset(&b.Docker.Config.Env) +} + +// UnsetEnv removes a value from the set of environment strings which should be +// set when running commands in this container, or in a container built using +// an image built from this container. +func (b *Builder) UnsetEnv(k string) { + unset := func(s *[]string) { + n := []string{} + for i := range *s { + if !strings.HasPrefix((*s)[i], k+"=") { + n = append(n, (*s)[i]) + } + } + *s = n + } + unset(&b.OCIv1.Config.Env) + unset(&b.Docker.Config.Env) +} + +// ClearEnv removes all values from the set of environment strings which should +// be set when running commands in this container, or in a container built +// using an image built from this container. +func (b *Builder) ClearEnv() { + b.OCIv1.Config.Env = []string{} + b.Docker.Config.Env = []string{} +} + +// Cmd returns the default command, or command parameters if an Entrypoint is +// set, to use when running a container built from an image built from this +// container. +func (b *Builder) Cmd() []string { + return copyStringSlice(b.OCIv1.Config.Cmd) +} + +// SetCmd sets the default command, or command parameters if an Entrypoint is +// set, to use when running a container built from an image built from this +// container. +func (b *Builder) SetCmd(cmd []string) { + b.OCIv1.Config.Cmd = copyStringSlice(cmd) + b.Docker.Config.Cmd = copyStringSlice(cmd) +} + +// Entrypoint returns the command to be run for containers built from images +// built from this container. +func (b *Builder) Entrypoint() []string { + return copyStringSlice(b.OCIv1.Config.Entrypoint) +} + +// SetEntrypoint sets the command to be run for in containers built from images +// built from this container. +func (b *Builder) SetEntrypoint(ep []string) { + b.OCIv1.Config.Entrypoint = copyStringSlice(ep) + b.Docker.Config.Entrypoint = copyStringSlice(ep) +} + +// Labels returns a set of key-value pairs from the image's runtime +// configuration. +func (b *Builder) Labels() map[string]string { + return copyStringStringMap(b.OCIv1.Config.Labels) +} + +// SetLabel adds or overwrites a key's value from the image's runtime +// configuration. +func (b *Builder) SetLabel(k string, v string) { + if b.OCIv1.Config.Labels == nil { + b.OCIv1.Config.Labels = map[string]string{} + } + b.OCIv1.Config.Labels[k] = v + if b.Docker.Config.Labels == nil { + b.Docker.Config.Labels = map[string]string{} + } + b.Docker.Config.Labels[k] = v +} + +// UnsetLabel removes a key and its value from the image's runtime +// configuration, if it's present. +func (b *Builder) UnsetLabel(k string) { + delete(b.OCIv1.Config.Labels, k) + delete(b.Docker.Config.Labels, k) +} + +// ClearLabels removes all keys and their values from the image's runtime +// configuration. +func (b *Builder) ClearLabels() { + b.OCIv1.Config.Labels = map[string]string{} + b.Docker.Config.Labels = map[string]string{} +} + +// Ports returns the set of ports which should be exposed when a container +// based on an image built from this container is run. +func (b *Builder) Ports() []string { + p := []string{} + for k := range b.OCIv1.Config.ExposedPorts { + p = append(p, k) + } + return p +} + +// SetPort adds or overwrites an exported port in the set of ports which should +// be exposed when a container based on an image built from this container is +// run. +func (b *Builder) SetPort(p string) { + if b.OCIv1.Config.ExposedPorts == nil { + b.OCIv1.Config.ExposedPorts = map[string]struct{}{} + } + b.OCIv1.Config.ExposedPorts[p] = struct{}{} + if b.Docker.Config.ExposedPorts == nil { + b.Docker.Config.ExposedPorts = make(docker.PortSet) + } + b.Docker.Config.ExposedPorts[docker.Port(p)] = struct{}{} +} + +// UnsetPort removes an exposed port from the set of ports which should be +// exposed when a container based on an image built from this container is run. +func (b *Builder) UnsetPort(p string) { + delete(b.OCIv1.Config.ExposedPorts, p) + delete(b.Docker.Config.ExposedPorts, docker.Port(p)) +} + +// ClearPorts empties the set of ports which should be exposed when a container +// based on an image built from this container is run. +func (b *Builder) ClearPorts() { + b.OCIv1.Config.ExposedPorts = map[string]struct{}{} + b.Docker.Config.ExposedPorts = docker.PortSet{} +} + +// Volumes returns a list of filesystem locations which should be mounted from +// outside of the container when a container built from an image built from +// this container is run. +func (b *Builder) Volumes() []string { + v := []string{} + for k := range b.OCIv1.Config.Volumes { + v = append(v, k) + } + return v +} + +// AddVolume adds a location to the image's list of locations which should be +// mounted from outside of the container when a container based on an image +// built from this container is run. +func (b *Builder) AddVolume(v string) { + if b.OCIv1.Config.Volumes == nil { + b.OCIv1.Config.Volumes = map[string]struct{}{} + } + b.OCIv1.Config.Volumes[v] = struct{}{} + if b.Docker.Config.Volumes == nil { + b.Docker.Config.Volumes = map[string]struct{}{} + } + b.Docker.Config.Volumes[v] = struct{}{} +} + +// RemoveVolume removes a location from the list of locations which should be +// mounted from outside of the container when a container based on an image +// built from this container is run. +func (b *Builder) RemoveVolume(v string) { + delete(b.OCIv1.Config.Volumes, v) + delete(b.Docker.Config.Volumes, v) +} + +// ClearVolumes removes all locations from the image's list of locations which +// should be mounted from outside of the container when a container based on an +// image built from this container is run. +func (b *Builder) ClearVolumes() { + b.OCIv1.Config.Volumes = map[string]struct{}{} + b.Docker.Config.Volumes = map[string]struct{}{} +} + +// Hostname returns the hostname which will be set in the container and in +// containers built using images built from the container. +func (b *Builder) Hostname() string { + return b.Docker.Config.Hostname +} + +// SetHostname sets the hostname which will be set in the container and in +// containers built using images built from the container. +// Note: this setting is not present in the OCIv1 image format, so it is +// discarded when writing images using OCIv1 formats. +func (b *Builder) SetHostname(name string) { + b.Docker.Config.Hostname = name +} + +// Domainname returns the domainname which will be set in the container and in +// containers built using images built from the container. +func (b *Builder) Domainname() string { + return b.Docker.Config.Domainname +} + +// SetDomainname sets the domainname which will be set in the container and in +// containers built using images built from the container. +// Note: this setting is not present in the OCIv1 image format, so it is +// discarded when writing images using OCIv1 formats. +func (b *Builder) SetDomainname(name string) { + b.Docker.Config.Domainname = name +} + +// SetDefaultMountsFilePath sets the mounts file path for testing purposes +func (b *Builder) SetDefaultMountsFilePath(path string) { + b.DefaultMountsFilePath = path +} + +// Comment returns the comment which will be set in the container and in +//containers built using images buiilt from the container +func (b *Builder) Comment() string { + return b.Docker.Comment +} + +// SetComment sets the Comment which will be set in the container and in +// containers built using images built from the container. +func (b *Builder) SetComment(comment string) { + b.Docker.Comment = comment + b.OCIv1.History[0].Comment = comment +} + +// StopSignal returns the signal which will be set in the container and in +//containers built using images buiilt from the container +func (b *Builder) StopSignal() string { + return b.Docker.Config.StopSignal +} + +// SetStopSignal sets the signal which will be set in the container and in +// containers built using images built from the container. +func (b *Builder) SetStopSignal(stopSignal string) { + b.OCIv1.Config.StopSignal = stopSignal + b.Docker.Config.StopSignal = stopSignal +} |