summaryrefslogtreecommitdiff
path: root/libpod/buildah/config.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/buildah/config.go')
-rw-r--r--libpod/buildah/config.go607
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
+}