summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/buildah/buildah.go233
-rw-r--r--libpod/buildah/commit.go148
-rw-r--r--libpod/buildah/common.go28
-rw-r--r--libpod/buildah/config.go607
-rw-r--r--libpod/buildah/image.go529
-rw-r--r--libpod/buildah/util.go67
-rw-r--r--libpod/container_commit.go48
-rw-r--r--libpod/image/image.go41
-rw-r--r--libpod/runtime_img.go7
9 files changed, 89 insertions, 1619 deletions
diff --git a/libpod/buildah/buildah.go b/libpod/buildah/buildah.go
deleted file mode 100644
index 8f4b95ac8..000000000
--- a/libpod/buildah/buildah.go
+++ /dev/null
@@ -1,233 +0,0 @@
-package buildah
-
-import (
- "context"
- "encoding/json"
- "path/filepath"
-
- is "github.com/containers/image/storage"
- "github.com/containers/image/types"
- "github.com/containers/storage"
- "github.com/containers/storage/pkg/ioutils"
- "github.com/opencontainers/go-digest"
- "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/pkg/errors"
- "github.com/projectatomic/libpod/cmd/podman/docker"
-)
-
-const (
- // Package is the name of this package, used in help output and to
- // identify working containers.
- Package = "buildah"
- // Version for the Package. Bump version in contrib/rpm/buildah.spec
- // too.
- Version = "0.15"
- // The value we use to identify what type of information, currently a
- // serialized Builder structure, we are using as per-container state.
- // This should only be changed when we make incompatible changes to
- // that data structure, as it's used to distinguish containers which
- // are "ours" from ones that aren't.
- containerType = Package + " 0.0.1"
- // The file in the per-container directory which we use to store our
- // per-container state. If it isn't there, then the container isn't
- // one of our build containers.
- stateFile = Package + ".json"
-)
-
-// Builder objects are used to represent containers which are being used to
-// build images. They also carry potential updates which will be applied to
-// the image's configuration when the container's contents are used to build an
-// image.
-type Builder struct {
- store storage.Store
-
- // Type is used to help identify a build container's metadata. It
- // should not be modified.
- Type string `json:"type"`
- // FromImage is the name of the source image which was used to create
- // the container, if one was used. It should not be modified.
- FromImage string `json:"image,omitempty"`
- // FromImageID is the ID of the source image which was used to create
- // the container, if one was used. It should not be modified.
- FromImageID string `json:"image-id"`
- // Config is the source image's configuration. It should not be
- // modified.
- Config []byte `json:"config,omitempty"`
- // Manifest is the source image's manifest. It should not be modified.
- Manifest []byte `json:"manifest,omitempty"`
-
- // Container is the name of the build container. It should not be modified.
- Container string `json:"container-name,omitempty"`
- // ContainerID is the ID of the build container. It should not be modified.
- ContainerID string `json:"container-id,omitempty"`
- // MountPoint is the last location where the container's root
- // filesystem was mounted. It should not be modified.
- MountPoint string `json:"mountpoint,omitempty"`
- // ProcessLabel is the SELinux process label associated with the container
- ProcessLabel string `json:"process-label,omitempty"`
- // MountLabel is the SELinux mount label associated with the container
- MountLabel string `json:"mount-label,omitempty"`
-
- // ImageAnnotations is a set of key-value pairs which is stored in the
- // image's manifest.
- ImageAnnotations map[string]string `json:"annotations,omitempty"`
- // ImageCreatedBy is a description of how this container was built.
- ImageCreatedBy string `json:"created-by,omitempty"`
-
- // Image metadata and runtime settings, in multiple formats.
- OCIv1 v1.Image `json:"ociv1,omitempty"`
- Docker docker.V2Image `json:"docker,omitempty"`
- // DefaultMountsFilePath is the file path holding the mounts to be mounted in "host-path:container-path" format
- DefaultMountsFilePath string `json:"defaultMountsFilePath,omitempty"`
- CommonBuildOpts *CommonBuildOptions
-}
-
-// CommonBuildOptions are reseources that can be defined by flags for both buildah from and bud
-type CommonBuildOptions struct {
- // AddHost is the list of hostnames to add to the resolv.conf
- AddHost []string
- //CgroupParent it the path to cgroups under which the cgroup for the container will be created.
- CgroupParent string
- //CPUPeriod limits the CPU CFS (Completely Fair Scheduler) period
- CPUPeriod uint64
- //CPUQuota limits the CPU CFS (Completely Fair Scheduler) quota
- CPUQuota int64
- //CPUShares (relative weight
- CPUShares uint64
- //CPUSetCPUs in which to allow execution (0-3, 0,1)
- CPUSetCPUs string
- //CPUSetMems memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.
- CPUSetMems string
- //Memory limit
- Memory int64
- //MemorySwap limit value equal to memory plus swap.
- MemorySwap int64
- //SecruityOpts modify the way container security is running
- LabelOpts []string
- SeccompProfilePath string
- ApparmorProfile string
- //ShmSize is the shared memory size
- ShmSize string
- //Ulimit options
- Ulimit []string
- //Volumes to bind mount into the container
- Volumes []string
-}
-
-// ImportOptions are used to initialize a Builder from an existing container
-// which was created elsewhere.
-type ImportOptions struct {
- // Container is the name of the build container.
- Container string
- // SignaturePolicyPath specifies an override location for the signature
- // policy which should be used for verifying the new image as it is
- // being written. Except in specific circumstances, no value should be
- // specified, indicating that the shared, system-wide default policy
- // should be used.
- SignaturePolicyPath string
-}
-
-// ImportBuilder creates a new build configuration using an already-present
-// container.
-func ImportBuilder(ctx context.Context, store storage.Store, options ImportOptions) (*Builder, error) {
- return importBuilder(ctx, store, options)
-}
-
-func importBuilder(ctx context.Context, store storage.Store, options ImportOptions) (*Builder, error) {
- if options.Container == "" {
- return nil, errors.Errorf("container name must be specified")
- }
-
- c, err := store.Container(options.Container)
- if err != nil {
- return nil, err
- }
-
- systemContext := getSystemContext(&types.SystemContext{}, options.SignaturePolicyPath)
-
- builder, err := importBuilderDataFromImage(ctx, store, systemContext, c.ImageID, options.Container, c.ID)
- if err != nil {
- return nil, err
- }
-
- if builder.FromImageID != "" {
- if d, err2 := digest.Parse(builder.FromImageID); err2 == nil {
- builder.Docker.Parent = docker.ID(d)
- } else {
- builder.Docker.Parent = docker.ID(digest.NewDigestFromHex(digest.Canonical.String(), builder.FromImageID))
- }
- }
- if builder.FromImage != "" {
- builder.Docker.ContainerConfig.Image = builder.FromImage
- }
-
- err = builder.Save()
- if err != nil {
- return nil, errors.Wrapf(err, "error saving builder state")
- }
-
- return builder, nil
-}
-
-func importBuilderDataFromImage(ctx context.Context, store storage.Store, systemContext *types.SystemContext, imageID, containerName, containerID string) (*Builder, error) {
- manifest := []byte{}
- config := []byte{}
- imageName := ""
-
- if imageID != "" {
- ref, err := is.Transport.ParseStoreReference(store, imageID)
- if err != nil {
- return nil, errors.Wrapf(err, "no such image %q", imageID)
- }
- src, err2 := ref.NewImage(ctx, systemContext)
- if err2 != nil {
- return nil, errors.Wrapf(err2, "error instantiating image")
- }
- defer src.Close()
- config, err = src.ConfigBlob(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading image configuration")
- }
- manifest, _, err = src.Manifest(ctx)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading image manifest")
- }
- if img, err3 := store.Image(imageID); err3 == nil {
- if len(img.Names) > 0 {
- imageName = img.Names[0]
- }
- }
- }
-
- builder := &Builder{
- store: store,
- Type: containerType,
- FromImage: imageName,
- FromImageID: imageID,
- Config: config,
- Manifest: manifest,
- Container: containerName,
- ContainerID: containerID,
- ImageAnnotations: map[string]string{},
- ImageCreatedBy: "",
- }
-
- builder.initConfig()
-
- return builder, nil
-}
-
-// Save saves the builder's current state to the build container's metadata.
-// This should not need to be called directly, as other methods of the Builder
-// object take care of saving their state.
-func (b *Builder) Save() error {
- buildstate, err := json.Marshal(b)
- if err != nil {
- return err
- }
- cdir, err := b.store.ContainerDirectory(b.ContainerID)
- if err != nil {
- return err
- }
- return ioutils.AtomicWriteFile(filepath.Join(cdir, stateFile), buildstate, 0600)
-}
diff --git a/libpod/buildah/commit.go b/libpod/buildah/commit.go
deleted file mode 100644
index 537f9edbf..000000000
--- a/libpod/buildah/commit.go
+++ /dev/null
@@ -1,148 +0,0 @@
-package buildah
-
-import (
- "context"
- "fmt"
- "io"
- "time"
-
- cp "github.com/containers/image/copy"
- "github.com/containers/image/signature"
- is "github.com/containers/image/storage"
- "github.com/containers/image/transports"
- "github.com/containers/image/types"
- "github.com/containers/storage"
- "github.com/containers/storage/pkg/archive"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// CommitOptions can be used to alter how an image is committed.
-type CommitOptions struct {
- // PreferredManifestType is the preferred type of image manifest. The
- // image configuration format will be of a compatible type.
- PreferredManifestType string
- // Compression specifies the type of compression which is applied to
- // layer blobs. The default is to not use compression, but
- // archive.Gzip is recommended.
- Compression archive.Compression
- // SignaturePolicyPath specifies an override location for the signature
- // policy which should be used for verifying the new image as it is
- // being written. Except in specific circumstances, no value should be
- // specified, indicating that the shared, system-wide default policy
- // should be used.
- SignaturePolicyPath string
- // AdditionalTags is a list of additional names to add to the image, if
- // the transport to which we're writing the image gives us a way to add
- // them.
- AdditionalTags []string
- // ReportWriter is an io.Writer which will be used to log the writing
- // of the new image.
- ReportWriter io.Writer
- // HistoryTimestamp is the timestamp used when creating new items in the
- // image's history. If unset, the current time will be used.
- HistoryTimestamp *time.Time
- // github.com/containers/image/types SystemContext to hold credentials
- // and other authentication/authorization information.
- SystemContext *types.SystemContext
-}
-
-// PushOptions can be used to alter how an image is copied somewhere.
-type PushOptions struct {
- // Compression specifies the type of compression which is applied to
- // layer blobs. The default is to not use compression, but
- // archive.Gzip is recommended.
- Compression archive.Compression
- // SignaturePolicyPath specifies an override location for the signature
- // policy which should be used for verifying the new image as it is
- // being written. Except in specific circumstances, no value should be
- // specified, indicating that the shared, system-wide default policy
- // should be used.
- SignaturePolicyPath string
- // ReportWriter is an io.Writer which will be used to log the writing
- // of the new image.
- ReportWriter io.Writer
- // Store is the local storage store which holds the source image.
- Store storage.Store
- // github.com/containers/image/types SystemContext to hold credentials
- // and other authentication/authorization information.
- SystemContext *types.SystemContext
- // ManifestType is the format to use when saving the imge using the 'dir' transport
- // possible options are oci, v2s1, and v2s2
- ManifestType string
-}
-
-// Commit writes the contents of the container, along with its updated
-// configuration, to a new image in the specified location, and if we know how,
-// add any additional tags that were specified.
-func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options CommitOptions) error {
- policy, err := signature.DefaultPolicy(getSystemContext(options.SystemContext, options.SignaturePolicyPath))
- if err != nil {
- return errors.Wrapf(err, "error obtaining default signature policy")
- }
- policyContext, err := signature.NewPolicyContext(policy)
- if err != nil {
- return errors.Wrapf(err, "error creating new signature policy context")
- }
- defer func() {
- if err2 := policyContext.Destroy(); err2 != nil {
- logrus.Debugf("error destroying signature policy context: %v", err2)
- }
- }()
- // Check if we're keeping everything in local storage. If so, we can take certain shortcuts.
- _, destIsStorage := dest.Transport().(is.StoreTransport)
- exporting := !destIsStorage
- src, err := b.makeImageRef(options.PreferredManifestType, exporting, options.Compression, options.HistoryTimestamp)
- if err != nil {
- return errors.Wrapf(err, "error computing layer digests and building metadata")
- }
- // "Copy" our image to where it needs to be.
- err = cp.Image(ctx, policyContext, dest, src, getCopyOptions(options.ReportWriter, nil, options.SystemContext, ""))
- if err != nil {
- return errors.Wrapf(err, "error copying layers and metadata")
- }
- if len(options.AdditionalTags) > 0 {
- switch dest.Transport().Name() {
- case is.Transport.Name():
- img, err := is.Transport.GetStoreImage(b.store, dest)
- if err != nil {
- return errors.Wrapf(err, "error locating just-written image %q", transports.ImageName(dest))
- }
- err = AddImageNames(b.store, img, options.AdditionalTags)
- if err != nil {
- return errors.Wrapf(err, "error setting image names to %v", append(img.Names, options.AdditionalTags...))
- }
- logrus.Debugf("assigned names %v to image %q", img.Names, img.ID)
- default:
- logrus.Warnf("don't know how to add tags to images stored in %q transport", dest.Transport().Name())
- }
- }
- return nil
-}
-
-// Push copies the contents of the image to a new location.
-func Push(ctx context.Context, image string, dest types.ImageReference, options PushOptions) error {
- systemContext := getSystemContext(options.SystemContext, options.SignaturePolicyPath)
- policy, err := signature.DefaultPolicy(systemContext)
- if err != nil {
- return errors.Wrapf(err, "error obtaining default signature policy")
- }
- policyContext, err := signature.NewPolicyContext(policy)
- if err != nil {
- return errors.Wrapf(err, "error creating new signature policy context")
- }
- // Look up the image.
- src, err := is.Transport.ParseStoreReference(options.Store, image)
- if err != nil {
- return errors.Wrapf(err, "error parsing reference to image %q", image)
- }
- // Copy everything.
- err = cp.Image(ctx, policyContext, dest, src, getCopyOptions(options.ReportWriter, nil, options.SystemContext, options.ManifestType))
- if err != nil {
- return errors.Wrapf(err, "error copying layers and metadata")
- }
- if options.ReportWriter != nil {
- fmt.Fprintf(options.ReportWriter, "\n")
- }
- return nil
-}
diff --git a/libpod/buildah/common.go b/libpod/buildah/common.go
deleted file mode 100644
index 18c960003..000000000
--- a/libpod/buildah/common.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package buildah
-
-import (
- "io"
-
- cp "github.com/containers/image/copy"
- "github.com/containers/image/types"
-)
-
-func getCopyOptions(reportWriter io.Writer, sourceSystemContext *types.SystemContext, destinationSystemContext *types.SystemContext, manifestType string) *cp.Options {
- return &cp.Options{
- ReportWriter: reportWriter,
- SourceCtx: sourceSystemContext,
- DestinationCtx: destinationSystemContext,
- ForceManifestMIMEType: manifestType,
- }
-}
-
-func getSystemContext(defaults *types.SystemContext, signaturePolicyPath string) *types.SystemContext {
- sc := &types.SystemContext{}
- if defaults != nil {
- *sc = *defaults
- }
- if signaturePolicyPath != "" {
- sc.SignaturePolicyPath = signaturePolicyPath
- }
- return sc
-}
diff --git a/libpod/buildah/config.go b/libpod/buildah/config.go
deleted file mode 100644
index 0759ca9da..000000000
--- a/libpod/buildah/config.go
+++ /dev/null
@@ -1,607 +0,0 @@
-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
-}
diff --git a/libpod/buildah/image.go b/libpod/buildah/image.go
deleted file mode 100644
index 7232d53ad..000000000
--- a/libpod/buildah/image.go
+++ /dev/null
@@ -1,529 +0,0 @@
-package buildah
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "time"
-
- "github.com/containers/image/docker/reference"
- "github.com/containers/image/image"
- is "github.com/containers/image/storage"
- "github.com/containers/image/types"
- "github.com/containers/storage"
- "github.com/containers/storage/pkg/archive"
- "github.com/containers/storage/pkg/ioutils"
- digest "github.com/opencontainers/go-digest"
- specs "github.com/opencontainers/image-spec/specs-go"
- "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/pkg/errors"
- "github.com/projectatomic/libpod/cmd/podman/docker"
- "github.com/sirupsen/logrus"
-)
-
-const (
- // OCIv1ImageManifest is the MIME type of an OCIv1 image manifest,
- // suitable for specifying as a value of the PreferredManifestType
- // member of a CommitOptions structure. It is also the default.
- OCIv1ImageManifest = v1.MediaTypeImageManifest
- // Dockerv2ImageManifest is the MIME type of a Docker v2s2 image
- // manifest, suitable for specifying as a value of the
- // PreferredManifestType member of a CommitOptions structure.
- Dockerv2ImageManifest = docker.V2S2MediaTypeManifest
-)
-
-type containerImageRef struct {
- store storage.Store
- compression archive.Compression
- name reference.Named
- names []string
- layerID string
- oconfig []byte
- dconfig []byte
- created time.Time
- createdBy string
- annotations map[string]string
- preferredManifestType string
- exporting bool
-}
-
-type containerImageSource struct {
- path string
- ref *containerImageRef
- store storage.Store
- layerID string
- names []string
- compression archive.Compression
- config []byte
- configDigest digest.Digest
- manifest []byte
- manifestType string
- exporting bool
-}
-
-func (i *containerImageRef) NewImage(ctx context.Context, sc *types.SystemContext) (types.ImageCloser, error) {
- src, err := i.NewImageSource(ctx, sc)
- if err != nil {
- return nil, err
- }
- return image.FromSource(ctx, sc, src)
-}
-
-func expectedOCIDiffIDs(image v1.Image) int {
- expected := 0
- for _, history := range image.History {
- if !history.EmptyLayer {
- expected = expected + 1
- }
- }
- return expected
-}
-
-func expectedDockerDiffIDs(image docker.V2Image) int {
- expected := 0
- for _, history := range image.History {
- if !history.EmptyLayer {
- expected = expected + 1
- }
- }
- return expected
-}
-
-func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.SystemContext) (src types.ImageSource, err error) {
- // Decide which type of manifest and configuration output we're going to provide.
- manifestType := i.preferredManifestType
- // If it's not a format we support, return an error.
- if manifestType != v1.MediaTypeImageManifest && manifestType != docker.V2S2MediaTypeManifest {
- return nil, errors.Errorf("no supported manifest types (attempted to use %q, only know %q and %q)",
- manifestType, v1.MediaTypeImageManifest, docker.V2S2MediaTypeManifest)
- }
- // Start building the list of layers using the read-write layer.
- layers := []string{}
- layerID := i.layerID
- layer, err := i.store.Layer(layerID)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to read layer %q", layerID)
- }
- // Walk the list of parent layers, prepending each as we go.
- for layer != nil {
- layers = append(append([]string{}, layerID), layers...)
- layerID = layer.Parent
- if layerID == "" {
- err = nil
- break
- }
- layer, err = i.store.Layer(layerID)
- if err != nil {
- return nil, errors.Wrapf(err, "unable to read layer %q", layerID)
- }
- }
- logrus.Debugf("layer list: %q", layers)
-
- // Make a temporary directory to hold blobs.
- path, err := ioutil.TempDir(os.TempDir(), Package)
- if err != nil {
- return nil, err
- }
- logrus.Debugf("using %q to hold temporary data", path)
- defer func() {
- if src == nil {
- err2 := os.RemoveAll(path)
- if err2 != nil {
- logrus.Errorf("error removing %q: %v", path, err)
- }
- }
- }()
-
- // Build fresh copies of the configurations so that we don't mess with the values in the Builder
- // object itself.
- oimage := v1.Image{}
- err = json.Unmarshal(i.oconfig, &oimage)
- if err != nil {
- return nil, err
- }
- created := i.created
- oimage.Created = &created
- dimage := docker.V2Image{}
- err = json.Unmarshal(i.dconfig, &dimage)
- if err != nil {
- return nil, err
- }
- dimage.Created = created
-
- // Start building manifests.
- omanifest := v1.Manifest{
- Versioned: specs.Versioned{
- SchemaVersion: 2,
- },
- Config: v1.Descriptor{
- MediaType: v1.MediaTypeImageConfig,
- },
- Layers: []v1.Descriptor{},
- Annotations: i.annotations,
- }
- dmanifest := docker.V2S2Manifest{
- V2Versioned: docker.V2Versioned{
- SchemaVersion: 2,
- MediaType: docker.V2S2MediaTypeManifest,
- },
- Config: docker.V2S2Descriptor{
- MediaType: docker.V2S2MediaTypeImageConfig,
- },
- Layers: []docker.V2S2Descriptor{},
- }
-
- oimage.RootFS.Type = docker.TypeLayers
- oimage.RootFS.DiffIDs = []digest.Digest{}
- dimage.RootFS = &docker.V2S2RootFS{}
- dimage.RootFS.Type = docker.TypeLayers
- dimage.RootFS.DiffIDs = []digest.Digest{}
-
- // Extract each layer and compute its digests, both compressed (if requested) and uncompressed.
- for _, layerID := range layers {
- // The default layer media type assumes no compression.
- omediaType := v1.MediaTypeImageLayer
- dmediaType := docker.V2S2MediaTypeUncompressedLayer
- // If we're not re-exporting the data, reuse the blobsum and diff IDs.
- if !i.exporting && layerID != i.layerID {
- layer, err2 := i.store.Layer(layerID)
- if err2 != nil {
- return nil, errors.Wrapf(err, "unable to locate layer %q", layerID)
- }
- if layer.UncompressedDigest == "" {
- return nil, errors.Errorf("unable to look up size of layer %q", layerID)
- }
- layerBlobSum := layer.UncompressedDigest
- layerBlobSize := layer.UncompressedSize
- // Note this layer in the manifest, using the uncompressed blobsum.
- olayerDescriptor := v1.Descriptor{
- MediaType: omediaType,
- Digest: layerBlobSum,
- Size: layerBlobSize,
- }
- omanifest.Layers = append(omanifest.Layers, olayerDescriptor)
- dlayerDescriptor := docker.V2S2Descriptor{
- MediaType: dmediaType,
- Digest: layerBlobSum,
- Size: layerBlobSize,
- }
- dmanifest.Layers = append(dmanifest.Layers, dlayerDescriptor)
- // Note this layer in the list of diffIDs, again using the uncompressed blobsum.
- oimage.RootFS.DiffIDs = append(oimage.RootFS.DiffIDs, layerBlobSum)
- dimage.RootFS.DiffIDs = append(dimage.RootFS.DiffIDs, layerBlobSum)
- continue
- }
- // Figure out if we need to change the media type, in case we're using compression.
- if i.compression != archive.Uncompressed {
- switch i.compression {
- case archive.Gzip:
- omediaType = v1.MediaTypeImageLayerGzip
- dmediaType = docker.V2S2MediaTypeLayer
- logrus.Debugf("compressing layer %q with gzip", layerID)
- case archive.Bzip2:
- // Until the image specs define a media type for bzip2-compressed layers, even if we know
- // how to decompress them, we can't try to compress layers with bzip2.
- return nil, errors.New("media type for bzip2-compressed layers is not defined")
- case archive.Xz:
- // Until the image specs define a media type for xz-compressed layers, even if we know
- // how to decompress them, we can't try to compress layers with xz.
- return nil, errors.New("media type for xz-compressed layers is not defined")
- default:
- logrus.Debugf("compressing layer %q with unknown compressor(?)", layerID)
- }
- }
- // Start reading the layer.
- noCompression := archive.Uncompressed
- diffOptions := &storage.DiffOptions{
- Compression: &noCompression,
- }
- rc, err := i.store.Diff("", layerID, diffOptions)
- if err != nil {
- return nil, errors.Wrapf(err, "error extracting layer %q", layerID)
- }
- defer rc.Close()
- srcHasher := digest.Canonical.Digester()
- reader := io.TeeReader(rc, srcHasher.Hash())
- // Set up to write the possibly-recompressed blob.
- layerFile, err := os.OpenFile(filepath.Join(path, "layer"), os.O_CREATE|os.O_WRONLY, 0600)
- if err != nil {
- return nil, errors.Wrapf(err, "error opening file for layer %q", layerID)
- }
- destHasher := digest.Canonical.Digester()
- counter := ioutils.NewWriteCounter(layerFile)
- multiWriter := io.MultiWriter(counter, destHasher.Hash())
- // Compress the layer, if we're recompressing it.
- writer, err := archive.CompressStream(multiWriter, i.compression)
- if err != nil {
- return nil, errors.Wrapf(err, "error compressing layer %q", layerID)
- }
- size, err := io.Copy(writer, reader)
- if err != nil {
- return nil, errors.Wrapf(err, "error storing layer %q to file", layerID)
- }
- writer.Close()
- layerFile.Close()
- if i.compression == archive.Uncompressed {
- if size != counter.Count {
- return nil, errors.Errorf("error storing layer %q to file: inconsistent layer size (copied %d, wrote %d)", layerID, size, counter.Count)
- }
- } else {
- size = counter.Count
- }
- logrus.Debugf("layer %q size is %d bytes", layerID, size)
- // Rename the layer so that we can more easily find it by digest later.
- err = os.Rename(filepath.Join(path, "layer"), filepath.Join(path, destHasher.Digest().String()))
- if err != nil {
- return nil, errors.Wrapf(err, "error storing layer %q to file", layerID)
- }
- // Add a note in the manifest about the layer. The blobs are identified by their possibly-
- // compressed blob digests.
- olayerDescriptor := v1.Descriptor{
- MediaType: omediaType,
- Digest: destHasher.Digest(),
- Size: size,
- }
- omanifest.Layers = append(omanifest.Layers, olayerDescriptor)
- dlayerDescriptor := docker.V2S2Descriptor{
- MediaType: dmediaType,
- Digest: destHasher.Digest(),
- Size: size,
- }
- dmanifest.Layers = append(dmanifest.Layers, dlayerDescriptor)
- // Add a note about the diffID, which is always the layer's uncompressed digest.
- oimage.RootFS.DiffIDs = append(oimage.RootFS.DiffIDs, srcHasher.Digest())
- dimage.RootFS.DiffIDs = append(dimage.RootFS.DiffIDs, srcHasher.Digest())
- }
-
- // Build history notes in the image configurations.
- onews := v1.History{
- Created: &i.created,
- CreatedBy: i.createdBy,
- Author: oimage.Author,
- EmptyLayer: false,
- }
- oimage.History = append(oimage.History, onews)
- dnews := docker.V2S2History{
- Created: i.created,
- CreatedBy: i.createdBy,
- Author: dimage.Author,
- EmptyLayer: false,
- }
- dimage.History = append(dimage.History, dnews)
-
- // Sanity check that we didn't just create a mismatch between non-empty layers in the
- // history and the number of diffIDs.
- expectedDiffIDs := expectedOCIDiffIDs(oimage)
- if len(oimage.RootFS.DiffIDs) != expectedDiffIDs {
- return nil, errors.Errorf("internal error: history lists %d non-empty layers, but we have %d layers on disk", expectedDiffIDs, len(oimage.RootFS.DiffIDs))
- }
- expectedDiffIDs = expectedDockerDiffIDs(dimage)
- if len(dimage.RootFS.DiffIDs) != expectedDiffIDs {
- return nil, errors.Errorf("internal error: history lists %d non-empty layers, but we have %d layers on disk", expectedDiffIDs, len(dimage.RootFS.DiffIDs))
- }
-
- // Encode the image configuration blob.
- oconfig, err := json.Marshal(&oimage)
- if err != nil {
- return nil, err
- }
- logrus.Debugf("OCIv1 config = %s", oconfig)
-
- // Add the configuration blob to the manifest.
- omanifest.Config.Digest = digest.Canonical.FromBytes(oconfig)
- omanifest.Config.Size = int64(len(oconfig))
- omanifest.Config.MediaType = v1.MediaTypeImageConfig
-
- // Encode the manifest.
- omanifestbytes, err := json.Marshal(&omanifest)
- if err != nil {
- return nil, err
- }
- logrus.Debugf("OCIv1 manifest = %s", omanifestbytes)
-
- // Encode the image configuration blob.
- dconfig, err := json.Marshal(&dimage)
- if err != nil {
- return nil, err
- }
- logrus.Debugf("Docker v2s2 config = %s", dconfig)
-
- // Add the configuration blob to the manifest.
- dmanifest.Config.Digest = digest.Canonical.FromBytes(dconfig)
- dmanifest.Config.Size = int64(len(dconfig))
- dmanifest.Config.MediaType = docker.V2S2MediaTypeImageConfig
-
- // Encode the manifest.
- dmanifestbytes, err := json.Marshal(&dmanifest)
- if err != nil {
- return nil, err
- }
- logrus.Debugf("Docker v2s2 manifest = %s", dmanifestbytes)
-
- // Decide which manifest and configuration blobs we'll actually output.
- var config []byte
- var manifest []byte
- switch manifestType {
- case v1.MediaTypeImageManifest:
- manifest = omanifestbytes
- config = oconfig
- case docker.V2S2MediaTypeManifest:
- manifest = dmanifestbytes
- config = dconfig
- default:
- panic("unreachable code: unsupported manifest type")
- }
- src = &containerImageSource{
- path: path,
- ref: i,
- store: i.store,
- layerID: i.layerID,
- names: i.names,
- compression: i.compression,
- config: config,
- configDigest: digest.Canonical.FromBytes(config),
- manifest: manifest,
- manifestType: manifestType,
- exporting: i.exporting,
- }
- return src, nil
-}
-
-func (i *containerImageRef) NewImageDestination(ctx context.Context, sc *types.SystemContext) (types.ImageDestination, error) {
- return nil, errors.Errorf("can't write to a container")
-}
-
-func (i *containerImageRef) DockerReference() reference.Named {
- return i.name
-}
-
-func (i *containerImageRef) StringWithinTransport() string {
- if len(i.names) > 0 {
- return i.names[0]
- }
- return ""
-}
-
-func (i *containerImageRef) DeleteImage(context.Context, *types.SystemContext) error {
- // we were never here
- return nil
-}
-
-func (i *containerImageRef) PolicyConfigurationIdentity() string {
- return ""
-}
-
-func (i *containerImageRef) PolicyConfigurationNamespaces() []string {
- return nil
-}
-
-func (i *containerImageRef) Transport() types.ImageTransport {
- return is.Transport
-}
-
-func (i *containerImageSource) Close() error {
- err := os.RemoveAll(i.path)
- if err != nil {
- logrus.Errorf("error removing %q: %v", i.path, err)
- }
- return err
-}
-
-func (i *containerImageSource) Reference() types.ImageReference {
- return i.ref
-}
-
-func (i *containerImageSource) GetSignatures(ctx context.Context, instanceDigest *digest.Digest) ([][]byte, error) {
- if instanceDigest != nil && *instanceDigest != digest.FromBytes(i.manifest) {
- return nil, errors.Errorf("TODO")
- }
- return nil, nil
-}
-
-func (i *containerImageSource) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) {
- if instanceDigest != nil && *instanceDigest != digest.FromBytes(i.manifest) {
- return nil, "", errors.Errorf("TODO")
- }
- return i.manifest, i.manifestType, nil
-}
-
-func (i *containerImageSource) LayerInfosForCopy(context.Context) ([]types.BlobInfo, error) {
- return nil, nil
-}
-
-func (i *containerImageSource) GetBlob(ctx context.Context, blob types.BlobInfo) (reader io.ReadCloser, size int64, err error) {
- if blob.Digest == i.configDigest {
- logrus.Debugf("start reading config")
- reader := bytes.NewReader(i.config)
- closer := func() error {
- logrus.Debugf("finished reading config")
- return nil
- }
- return ioutils.NewReadCloserWrapper(reader, closer), reader.Size(), nil
- }
- layerFile, err := os.OpenFile(filepath.Join(i.path, blob.Digest.String()), os.O_RDONLY, 0600)
- if err != nil {
- logrus.Debugf("error reading layer %q: %v", blob.Digest.String(), err)
- return nil, -1, err
- }
- size = -1
- st, err := layerFile.Stat()
- if err != nil {
- logrus.Warnf("error reading size of layer %q: %v", blob.Digest.String(), err)
- } else {
- size = st.Size()
- }
- logrus.Debugf("reading layer %q", blob.Digest.String())
- closer := func() error {
- layerFile.Close()
- logrus.Debugf("finished reading layer %q", blob.Digest.String())
- return nil
- }
- return ioutils.NewReadCloserWrapper(layerFile, closer), size, nil
-}
-
-func (b *Builder) makeImageRef(manifestType string, exporting bool, compress archive.Compression, historyTimestamp *time.Time) (types.ImageReference, error) {
- var name reference.Named
- container, err := b.store.Container(b.ContainerID)
- if err != nil {
- return nil, errors.Wrapf(err, "error locating container %q", b.ContainerID)
- }
- if len(container.Names) > 0 {
- if parsed, err2 := reference.ParseNamed(container.Names[0]); err2 == nil {
- name = parsed
- }
- }
- if manifestType == "" {
- manifestType = OCIv1ImageManifest
- }
- oconfig, err := json.Marshal(&b.OCIv1)
- if err != nil {
- return nil, errors.Wrapf(err, "error encoding OCI-format image configuration")
- }
- dconfig, err := json.Marshal(&b.Docker)
- if err != nil {
- return nil, errors.Wrapf(err, "error encoding docker-format image configuration")
- }
- created := time.Now().UTC()
- if historyTimestamp != nil {
- created = historyTimestamp.UTC()
- }
- ref := &containerImageRef{
- store: b.store,
- compression: compress,
- name: name,
- names: container.Names,
- layerID: container.LayerID,
- oconfig: oconfig,
- dconfig: dconfig,
- created: created,
- createdBy: b.CreatedBy(),
- annotations: b.Annotations(),
- preferredManifestType: manifestType,
- exporting: exporting,
- }
- return ref, nil
-}
diff --git a/libpod/buildah/util.go b/libpod/buildah/util.go
deleted file mode 100644
index 96f9ebf86..000000000
--- a/libpod/buildah/util.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package buildah
-
-import (
- "github.com/containers/image/docker/reference"
- "github.com/containers/storage"
- "github.com/containers/storage/pkg/reexec"
- "github.com/pkg/errors"
-)
-
-// InitReexec is a wrapper for reexec.Init(). It should be called at
-// the start of main(), and if it returns true, main() should return
-// immediately.
-func InitReexec() bool {
- return reexec.Init()
-}
-
-func copyStringStringMap(m map[string]string) map[string]string {
- n := map[string]string{}
- for k, v := range m {
- n[k] = v
- }
- return n
-}
-
-func copyStringSlice(s []string) []string {
- t := make([]string, len(s))
- copy(t, s)
- return t
-}
-
-// AddImageNames adds the specified names to the specified image.
-func AddImageNames(store storage.Store, image *storage.Image, addNames []string) error {
- names, err := ExpandNames(addNames)
- if err != nil {
- return err
- }
- err = store.SetNames(image.ID, append(image.Names, names...))
- if err != nil {
- return errors.Wrapf(err, "error adding names (%v) to image %q", names, image.ID)
- }
- return nil
-}
-
-// ExpandNames takes unqualified names, parses them as image names, and returns
-// the fully expanded result, including a tag. Names which don't include a registry
-// name will be marked for the most-preferred registry (i.e., the first one in our
-// configuration).
-func ExpandNames(names []string) ([]string, error) {
- expanded := make([]string, 0, len(names))
- for _, n := range names {
- name, err := reference.ParseNormalizedNamed(n)
- if err != nil {
- return nil, errors.Wrapf(err, "error parsing name %q", n)
- }
- name = reference.TagNameOnly(name)
- tag := ""
- digest := ""
- if tagged, ok := name.(reference.NamedTagged); ok {
- tag = ":" + tagged.Tag()
- }
- if digested, ok := name.(reference.Digested); ok {
- digest = "@" + digested.Digest().String()
- }
- expanded = append(expanded, name.Name()+tag+digest)
- }
- return expanded, nil
-}
diff --git a/libpod/container_commit.go b/libpod/container_commit.go
index e136f96a4..a227e0987 100644
--- a/libpod/container_commit.go
+++ b/libpod/container_commit.go
@@ -2,11 +2,12 @@ package libpod
import (
"context"
+ "fmt"
"strings"
is "github.com/containers/image/storage"
"github.com/pkg/errors"
- "github.com/projectatomic/libpod/libpod/buildah"
+ "github.com/projectatomic/buildah"
"github.com/projectatomic/libpod/libpod/image"
"github.com/sirupsen/logrus"
)
@@ -52,9 +53,10 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai
SignaturePolicyPath: options.SignaturePolicyPath,
}
commitOptions := buildah.CommitOptions{
- SignaturePolicyPath: options.SignaturePolicyPath,
- ReportWriter: options.ReportWriter,
- SystemContext: sc,
+ SignaturePolicyPath: options.SignaturePolicyPath,
+ ReportWriter: options.ReportWriter,
+ SystemContext: sc,
+ PreferredManifestType: options.PreferredManifestType,
}
importBuilder, err := buildah.ImportBuilder(ctx, c.runtime.store, builderOptions)
if err != nil {
@@ -68,6 +70,38 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai
importBuilder.SetComment(options.Message)
}
+ // We need to take meta we find in the current container and
+ // add it to the resulting image.
+
+ // Entrypoint - always set this first or cmd will get wiped out
+ importBuilder.SetEntrypoint(c.Spec().Process.Args)
+ // Cmd
+ // We cannot differentiate between cmd and entrypoint here
+ // so we assign args to both
+ importBuilder.SetCmd(c.Spec().Process.Args)
+ // Env
+ for _, e := range c.config.Spec.Process.Env {
+ splitEnv := strings.Split(e, "=")
+ importBuilder.SetEnv(splitEnv[0], splitEnv[1])
+ }
+ // Expose ports
+ for _, p := range c.config.PortMappings {
+ importBuilder.SetPort(string(p.ContainerPort))
+ }
+ // Labels
+ for k, v := range c.Labels() {
+ importBuilder.SetLabel(k, v)
+ }
+ // No stop signal
+ // User
+ importBuilder.SetUser(c.User())
+ // Volumes
+ for _, v := range c.config.Spec.Mounts {
+ importBuilder.AddVolume(v.Source)
+ }
+ // Workdir
+ importBuilder.SetWorkDir(c.Spec().Process.Cwd)
+
// Process user changes
for _, change := range options.Changes {
splitChange := strings.Split(change, "=")
@@ -77,16 +111,20 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai
case "ENTRYPOINT":
importBuilder.SetEntrypoint(splitChange[1:])
case "ENV":
+ importBuilder.ClearEnv()
importBuilder.SetEnv(splitChange[1], splitChange[2])
case "EXPOSE":
+ importBuilder.ClearPorts()
importBuilder.SetPort(splitChange[1])
case "LABEL":
+ importBuilder.ClearLabels()
importBuilder.SetLabel(splitChange[1], splitChange[2])
case "STOPSIGNAL":
// No Set StopSignal
case "USER":
importBuilder.SetUser(splitChange[1])
case "VOLUME":
+ importBuilder.ClearVolumes()
importBuilder.AddVolume(splitChange[1])
case "WORKDIR":
importBuilder.SetWorkDir(splitChange[1])
@@ -96,9 +134,9 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai
if err != nil {
return nil, err
}
-
if err = importBuilder.Commit(ctx, imageRef, commitOptions); err != nil {
return nil, err
}
+ fmt.Println(importBuilder.Comment())
return c.runtime.imageRuntime.NewFromLocal(imageRef.DockerReference().String())
}
diff --git a/libpod/image/image.go b/libpod/image/image.go
index b2dd22b82..db0fdab90 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -12,6 +12,7 @@ import (
types2 "github.com/containernetworking/cni/pkg/types"
cp "github.com/containers/image/copy"
"github.com/containers/image/docker/reference"
+ "github.com/containers/image/manifest"
is "github.com/containers/image/storage"
"github.com/containers/image/tarball"
"github.com/containers/image/transports/alltransports"
@@ -21,6 +22,7 @@ import (
"github.com/opencontainers/go-digest"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
+ "github.com/projectatomic/buildah"
"github.com/projectatomic/libpod/libpod/common"
"github.com/projectatomic/libpod/libpod/driver"
"github.com/projectatomic/libpod/pkg/inspect"
@@ -608,6 +610,7 @@ func (i *Image) ociv1Image(ctx context.Context) (*ociv1.Image, error) {
if err != nil {
return nil, err
}
+
return imgRef.OCIConfig(ctx)
}
@@ -660,11 +663,20 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) {
return nil, err
}
+ _, manifestType, err := i.Manifest(ctx)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to determine manifest type")
+ }
+ comment, err := i.Comment(ctx, manifestType)
+ if err != nil {
+ return nil, err
+ }
+
data := &inspect.ImageData{
ID: i.ID(),
RepoTags: i.Names(),
RepoDigests: repoDigests,
- Comment: ociv1Img.History[0].Comment,
+ Comment: comment,
Created: ociv1Img.Created,
Author: ociv1Img.Author,
Architecture: ociv1Img.Architecture,
@@ -680,7 +692,8 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) {
Type: ociv1Img.RootFS.Type,
Layers: ociv1Img.RootFS.DiffIDs,
},
- GraphDriver: driver,
+ GraphDriver: driver,
+ ManifestType: manifestType,
}
return data, nil
}
@@ -802,3 +815,27 @@ func (i *Image) Containers() ([]string, error) {
}
return imageContainers, err
}
+
+// Comment returns the Comment for an image depending on its ManifestType
+func (i *Image) Comment(ctx context.Context, manifestType string) (string, error) {
+ if manifestType == buildah.Dockerv2ImageManifest {
+ imgRef, err := i.toImageRef(ctx)
+ if err != nil {
+ return "", errors.Wrapf(err, "unable to create image reference from image")
+ }
+ blob, err := imgRef.ConfigBlob(ctx)
+ if err != nil {
+ return "", errors.Wrapf(err, "unable to get config blob from image")
+ }
+ b := manifest.Schema2Image{}
+ if err := json.Unmarshal(blob, &b); err != nil {
+ return "", err
+ }
+ return b.Comment, nil
+ }
+ ociv1Img, err := i.ociv1Image(ctx)
+ if err != nil {
+ return "", err
+ }
+ return ociv1Img.History[0].Comment, nil
+}
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 041de0cc2..2392c41d4 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -1,6 +1,7 @@
package libpod
import (
+ "context"
"fmt"
"io"
@@ -13,6 +14,7 @@ import (
"github.com/containers/storage/pkg/archive"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
+ "github.com/projectatomic/buildah/imagebuildah"
"github.com/projectatomic/libpod/libpod/common"
"github.com/projectatomic/libpod/libpod/image"
)
@@ -177,3 +179,8 @@ func removeStorageContainers(ctrIDs []string, store storage.Store) error {
}
return nil
}
+
+// Build adds the runtime to the imagebuildah call
+func (r *Runtime) Build(ctx context.Context, options imagebuildah.BuildOptions, dockerfiles ...string) error {
+ return imagebuildah.BuildDockerfiles(ctx, r.store, options, dockerfiles...)
+}