From 38a1b2f16d210525eafcc845e7a9cce598207113 Mon Sep 17 00:00:00 2001 From: baude Date: Thu, 15 Mar 2018 10:06:49 -0500 Subject: Image library stage 4 - create and commit Migrate the podman create and commit subcommandis to leverage the images library. I also had to migrate the cmd/ portions of run and rmi. Signed-off-by: baude Closes: #498 Approved by: mheon --- libpod/image/image.go | 130 +++++++++++++++++++++++++++++++++++++++++---- libpod/image/image_test.go | 4 +- libpod/image/pull.go | 4 +- libpod/image/utils.go | 18 ++++--- 4 files changed, 134 insertions(+), 22 deletions(-) (limited to 'libpod/image') diff --git a/libpod/image/image.go b/libpod/image/image.go index b218c7d67..5e69a0a98 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -12,6 +12,7 @@ import ( cp "github.com/containers/image/copy" "github.com/containers/image/docker/reference" is "github.com/containers/image/storage" + "github.com/containers/image/tarball" "github.com/containers/image/transports/alltransports" "github.com/containers/image/types" "github.com/containers/storage" @@ -19,6 +20,8 @@ import ( "github.com/opencontainers/go-digest" ociv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" + "github.com/projectatomic/libpod/libpod/common" + "github.com/projectatomic/libpod/libpod/driver" "github.com/projectatomic/libpod/pkg/inspect" "github.com/projectatomic/libpod/pkg/util" ) @@ -36,12 +39,20 @@ type Image struct { // Runtime contains the store type Runtime struct { - store storage.Store + store storage.Store + SignaturePolicyPath string } -// NewImageRuntime creates an Image Runtime including the store given +// NewImageRuntimeFromStore creates an ImageRuntime based on a provided store +func NewImageRuntimeFromStore(store storage.Store) *Runtime { + return &Runtime{ + store: store, + } +} + +// NewImageRuntimeFromOptions creates an Image Runtime including the store given // store options -func NewImageRuntime(options storage.StoreOptions) (*Runtime, error) { +func NewImageRuntimeFromOptions(options storage.StoreOptions) (*Runtime, error) { if reexec.Init() { return nil, errors.Errorf("unable to reexec") } @@ -110,10 +121,12 @@ func (ir *Runtime) New(name, signaturePolicyPath, authfile string, writer io.Wri } // The image is not local - + if signaturePolicyPath == "" { + signaturePolicyPath = ir.SignaturePolicyPath + } imageName, err := newImage.pullImage(writer, authfile, signaturePolicyPath, signingoptions, dockeroptions) if err != nil { - return &newImage, errors.Errorf("unable to pull %s", name) + return nil, errors.Errorf("unable to pull %s", name) } newImage.InputName = imageName @@ -141,7 +154,7 @@ func (i *Image) reloadImage() error { // getLocalImage resolves an unknown input describing an image and // returns a storage.Image or an error. It is used by NewFromLocal. func (i *Image) getLocalImage() (*storage.Image, error) { - imageError := fmt.Sprintf("unable to find '%s' in local storage\n", i.InputName) + imageError := fmt.Sprintf("unable to find '%s' in local storage", i.InputName) if i.InputName == "" { return nil, errors.Errorf("input name is blank") } @@ -187,8 +200,7 @@ func (i *Image) getLocalImage() (*storage.Image, error) { if err == nil { return repoImage, nil } - - return nil, errors.Errorf("%s", imageError) + return nil, errors.Wrapf(err, imageError) } // hasRegistry returns a bool/err response if the image has a registry in its @@ -307,7 +319,11 @@ func (ir *Runtime) GetImages() ([]*Image, error) { return nil, err } for _, i := range images { - newImages = append(newImages, ir.newFromStorage(&i)) + // iterating over these, be careful to not iterate on the literal + // pointer. + image := i + img := ir.newFromStorage(&image) + newImages = append(newImages, img) } return newImages, nil } @@ -338,10 +354,24 @@ func (i *Image) TagImage(tag string) error { return i.imageruntime.store.SetNames(i.ID(), tags) } +// UntagImage removes a tag from the given image +func (i *Image) UntagImage(tag string) error { + var newTags []string + tags := i.Names() + if !util.StringInSlice(tag, tags) { + return nil + } + for _, t := range tags { + if tag != t { + newTags = append(newTags, t) + } + } + i.reloadImage() + return i.imageruntime.store.SetNames(i.ID(), newTags) +} + // PushImage pushes the given image to a location described by the given path func (i *Image) PushImage(destination, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions) error { - // PushImage pushes the src image to the destination - //func PushImage(source, destination string, options CopyOptions) error { if destination == "" { return errors.Wrapf(syscall.EINVAL, "destination image name must be specified") } @@ -395,6 +425,12 @@ func (i *Image) toStorageReference() (types.ImageReference, error) { return is.Transport.ParseStoreReference(i.imageruntime.store, i.ID()) } +// ToImageRef returns an image reference type from an image +// TODO: Hopefully we can remove this exported function for mheon +func (i *Image) ToImageRef() (types.Image, error) { + return i.toImageRef() +} + // toImageRef returns an Image Reference type from an image func (i *Image) toImageRef() (types.Image, error) { ref, err := is.Transport.ParseStoreReference(i.imageruntime.store, "@"+i.ID()) @@ -431,5 +467,77 @@ func (i *Image) Size() (*uint64, error) { } } return nil, errors.Errorf("unable to determine size") +} + +// DriverData gets the driver data from the store on a layer +func (i *Image) DriverData() (*inspect.Data, error) { + topLayer, err := i.Layer() + if err != nil { + return nil, err + } + return driver.GetDriverData(i.imageruntime.store, topLayer.ID) +} +// Layer returns the image's top layer +func (i *Image) Layer() (*storage.Layer, error) { + return i.imageruntime.store.Layer(i.image.TopLayer) +} + +// History gets the history of an image and information about its layers +func (i *Image) History() ([]ociv1.History, []types.BlobInfo, error) { + img, err := i.toImageRef() + if err != nil { + return nil, nil, err + } + oci, err := img.OCIConfig() + if err != nil { + return nil, nil, err + } + return oci.History, img.LayerInfos(), nil +} + +// Import imports and image into the store and returns an image +func Import(path, reference string, writer io.Writer, signingOptions SigningOptions, imageConfig ociv1.Image, runtime *Runtime) (*Image, error) { + file := TarballTransport + ":" + path + src, err := alltransports.ParseImageName(file) + if err != nil { + return nil, errors.Wrapf(err, "error parsing image name %q", path) + } + + updater, ok := src.(tarball.ConfigUpdater) + if !ok { + return nil, errors.Wrapf(err, "unexpected type, a tarball reference should implement tarball.ConfigUpdater") + } + + annotations := make(map[string]string) + + // config imgspecv1.Image + err = updater.ConfigUpdate(imageConfig, annotations) + if err != nil { + return nil, errors.Wrapf(err, "error updating image config") + } + + sc := common.GetSystemContext("", "", false) + + // if reference not given, get the image digest + if reference == "" { + reference, err = getImageDigest(src, sc) + if err != nil { + return nil, err + } + } + policyContext, err := getPolicyContext(sc) + if err != nil { + return nil, err + } + defer policyContext.Destroy() + copyOptions := getCopyOptions(writer, "", nil, nil, signingOptions, "", "", false) + dest, err := is.Transport.ParseStoreReference(runtime.store, reference) + if err != nil { + errors.Wrapf(err, "error getting image reference for %q", reference) + } + if err = cp.Image(policyContext, dest, src, copyOptions); err != nil { + return nil, err + } + return runtime.NewFromLocal(reference) } diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go index 0e2f0c241..d9e8987a6 100644 --- a/libpod/image/image_test.go +++ b/libpod/image/image_test.go @@ -79,7 +79,7 @@ func TestImage_NewFromLocal(t *testing.T) { writer = os.Stdout // Need images to be present for this test - ir, err := NewImageRuntime(so) + ir, err := NewImageRuntimeFromOptions(so) assert.NoError(t, err) bb, err := ir.New("docker.io/library/busybox:latest", "", "", writer, nil, SigningOptions{}) assert.NoError(t, err) @@ -115,7 +115,7 @@ func TestImage_New(t *testing.T) { RunRoot: workdir, GraphRoot: workdir, } - ir, err := NewImageRuntime(so) + ir, err := NewImageRuntimeFromOptions(so) assert.NoError(t, err) // Build the list of pull names names = append(names, bbNames...) diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 52ef175d4..8c43c6054 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -177,8 +177,8 @@ func (i *Image) pullImage(writer io.Writer, authfile, signaturePolicyPath string copyOptions := getCopyOptions(writer, signaturePolicyPath, dockerOptions, nil, signingOptions, authfile, "", false) for _, imageInfo := range pullStructs { // Print the following statement only when pulling from a docker or atomic registry - if writer != nil && (imageInfo.srcRef.Transport().Name() == DockerTransport || imageInfo.srcRef.Transport().Name() == AtomicTransport) { - io.WriteString(writer, fmt.Sprintf("Trying to pull %s...\n", imageInfo.image)) + if writer != nil && (strings.HasPrefix(DockerTransport, imageInfo.srcRef.Transport().Name()) || imageInfo.srcRef.Transport().Name() == AtomicTransport) { + io.WriteString(writer, fmt.Sprintf("Trying to pull %s...", imageInfo.image)) } if err = cp.Image(policyContext, imageInfo.dstRef, imageInfo.srcRef, copyOptions); err != nil { if writer != nil { diff --git a/libpod/image/utils.go b/libpod/image/utils.go index adc795e3a..76ec349f9 100644 --- a/libpod/image/utils.go +++ b/libpod/image/utils.go @@ -2,15 +2,14 @@ package image import ( "io" + "strings" cp "github.com/containers/image/copy" "github.com/containers/image/docker/reference" - "github.com/containers/storage" - "github.com/pkg/errors" - "github.com/containers/image/signature" "github.com/containers/image/types" - "strings" + "github.com/containers/storage" + "github.com/pkg/errors" ) func getTags(nameInput string) (reference.NamedTagged, bool, error) { @@ -36,14 +35,19 @@ func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, er } if d.name == search.name && d.tag == search.tag { results = append(results, image.image) - break + continue + } + // account for registry:/somedir/image + if strings.HasSuffix(d.name, search.name) && d.tag == search.tag { + results = append(results, image.image) + continue } } } if len(results) == 0 { - return &storage.Image{}, errors.Errorf("unable to find a name and tag match for %s in repotags", search) + return &storage.Image{}, errors.Errorf("unable to find a name and tag match for %s in repotags", search.name) } else if len(results) > 1 { - return &storage.Image{}, errors.Errorf("found multiple name and tag matches for %s in repotags", search) + return &storage.Image{}, errors.Errorf("found multiple name and tag matches for %s in repotags", search.name) } return results[0], nil } -- cgit v1.2.3-54-g00ecf