diff options
-rw-r--r-- | cmd/podman/commit.go | 20 | ||||
-rw-r--r-- | cmd/podman/create.go | 61 | ||||
-rw-r--r-- | cmd/podman/history.go | 11 | ||||
-rw-r--r-- | cmd/podman/import.go | 22 | ||||
-rw-r--r-- | cmd/podman/inspect.go | 16 | ||||
-rw-r--r-- | cmd/podman/rmi.go | 28 | ||||
-rw-r--r-- | cmd/podman/run.go | 9 | ||||
-rw-r--r-- | libpod/container_api.go | 8 | ||||
-rw-r--r-- | libpod/container_internal.go | 6 | ||||
-rw-r--r-- | libpod/image/image.go | 130 | ||||
-rw-r--r-- | libpod/image/image_test.go | 4 | ||||
-rw-r--r-- | libpod/image/pull.go | 4 | ||||
-rw-r--r-- | libpod/image/utils.go | 18 | ||||
-rw-r--r-- | libpod/image_inspect.go | 26 | ||||
-rw-r--r-- | libpod/runtime.go | 21 | ||||
-rw-r--r-- | libpod/runtime_img.go | 202 | ||||
-rw-r--r-- | test/e2e/pull_test.go | 2 |
17 files changed, 303 insertions, 285 deletions
diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go index 59ced293f..34a086004 100644 --- a/cmd/podman/commit.go +++ b/cmd/podman/commit.go @@ -2,11 +2,12 @@ package main import ( "fmt" + "io" "os" "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" - "github.com/projectatomic/libpod/libpod" + "github.com/projectatomic/libpod/libpod/image" "github.com/urfave/cli" ) @@ -58,8 +59,11 @@ func commitCmd(c *cli.Context) error { } defer runtime.Shutdown(false) - var opts libpod.CopyOptions - var container string + var ( + container string + reference string + writer io.Writer + ) args := c.Args() switch len(args) { case 0: @@ -68,7 +72,7 @@ func commitCmd(c *cli.Context) error { container = args[0] case 2: container = args[0] - opts.Reference = args[1] + reference = args[1] default: return errors.Errorf("too many arguments. Usage CONTAINER [REFERENCE]") } @@ -90,20 +94,18 @@ func commitCmd(c *cli.Context) error { History: history, Author: c.String("author"), } - opts.ImageConfig = config - opts.Writer = nil if !c.Bool("quiet") { - opts.Writer = os.Stderr + writer = os.Stderr } ctr, err := runtime.LookupContainer(container) if err != nil { return errors.Wrapf(err, "error looking up container %q", container) } - img, err := ctr.Commit(c.BoolT("pause"), opts) + newImage, err := ctr.Commit(c.BoolT("pause"), reference, writer, image.SigningOptions{}, config) if err == nil { - fmt.Println(img.ID) + fmt.Println(newImage.ID()) } return nil } diff --git a/cmd/podman/create.go b/cmd/podman/create.go index efd7458ea..947f6d2a6 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -3,13 +3,6 @@ package main import ( "encoding/json" "fmt" - "io" - "net" - "os" - "strconv" - "strings" - "syscall" - "github.com/docker/docker/api/types/container" "github.com/docker/docker/pkg/signal" "github.com/docker/go-connections/nat" @@ -17,10 +10,16 @@ import ( "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/projectatomic/libpod/libpod" + "github.com/projectatomic/libpod/libpod/image" "github.com/projectatomic/libpod/pkg/inspect" "github.com/projectatomic/libpod/pkg/util" "github.com/sirupsen/logrus" "github.com/urfave/cli" + "net" + "os" + "strconv" + "strings" + "syscall" ) type mountType string @@ -154,6 +153,7 @@ var createCommand = cli.Command{ func createCmd(c *cli.Context) error { // TODO should allow user to create based off a directory on the host not just image // Need CLI support for this + if err := validateFlags(c, createFlags); err != nil { return err } @@ -174,11 +174,14 @@ func createCmd(c *cli.Context) error { } defer runtime.Shutdown(false) - imageName, _, data, err := imageData(c, runtime, c.Args()[0]) + rtc := runtime.GetConfig() + + newImage, err := runtime.ImageRuntime().New(c.Args()[0], rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}) if err != nil { return err } - createConfig, err := parseCreateOpts(c, runtime, imageName, data) + data, err := libpod.GetImageData(newImage) + createConfig, err := parseCreateOpts(c, runtime, newImage.Names()[0], data) if err != nil { return err } @@ -379,46 +382,6 @@ func exposedPorts(c *cli.Context, imageExposedPorts map[string]struct{}) (map[na return portBindings, nil } -// imageData pulls down the image if not stored locally and extracts the -// default container runtime data out of it. imageData returns the data -// to the caller. Example Data: Entrypoint, Env, WorkingDir, Labels ... -func imageData(c *cli.Context, runtime *libpod.Runtime, image string) (string, string, *inspect.ImageData, error) { - var ( - err error - imageName, imageID string - ) - // Deal with the image after all the args have been checked - createImage := runtime.NewImage(image) - imageName, imageID, _ = createImage.GetLocalImageName() - if createImage.LocalName == "" { - // The image wasnt found by the user input'd name or its fqname - // Pull the image - var writer io.Writer - if !c.Bool("quiet") { - writer = os.Stderr - } - createImage.Pull(writer) - } - - createImage.LocalName = imageName - if imageName == "" { - imageName, err = createImage.GetFQName() - _, imageID, _ = createImage.GetLocalImageName() - } - if err != nil { - return "", "", nil, err - } - storageImage, err := runtime.GetImage(imageName) - if err != nil { - return "", "", nil, errors.Wrapf(err, "error getting storage image %q", image) - } - data, err := runtime.GetImageInspectInfo(*storageImage) - if err != nil { - return "", "", nil, errors.Wrapf(err, "error parsing image data %q", image) - } - return imageName, imageID, data, err -} - // Parses CLI options related to container creation into a config which can be // parsed into an OCI runtime spec func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime, imageName string, data *inspect.ImageData) (*createConfig, error) { diff --git a/cmd/podman/history.go b/cmd/podman/history.go index 1164555d3..0f9698f4f 100644 --- a/cmd/podman/history.go +++ b/cmd/podman/history.go @@ -98,8 +98,11 @@ func historyCmd(c *cli.Context) error { if len(args) > 1 { return errors.Errorf("podman history takes at most 1 argument") } - imgName := args[0] + image, err := runtime.ImageRuntime().NewFromLocal(args[0]) + if err != nil { + return err + } opts := historyOptions{ human: c.BoolT("human"), noTrunc: c.Bool("no-trunc"), @@ -107,12 +110,12 @@ func historyCmd(c *cli.Context) error { format: format, } - history, layers, imageID, err := runtime.GetHistory(imgName) + history, layers, err := image.History() if err != nil { - return errors.Wrapf(err, "error getting history of image %q", imgName) + return errors.Wrapf(err, "error getting history of image %q", image.InputName) } - return generateHistoryOutput(history, layers, imageID, opts) + return generateHistoryOutput(history, layers, image.ID(), opts) } func genHistoryFormat(format string, quiet bool) string { diff --git a/cmd/podman/import.go b/cmd/podman/import.go index d3c497d9d..6f7565b4b 100644 --- a/cmd/podman/import.go +++ b/cmd/podman/import.go @@ -11,7 +11,7 @@ import ( "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" - "github.com/projectatomic/libpod/libpod" + "github.com/projectatomic/libpod/libpod/image" "github.com/urfave/cli" ) @@ -55,8 +55,12 @@ func importCmd(c *cli.Context) error { } defer runtime.Shutdown(false) - var opts libpod.CopyOptions - var source string + var ( + source string + reference string + writer io.Writer + ) + args := c.Args() switch len(args) { case 0: @@ -65,7 +69,7 @@ func importCmd(c *cli.Context) error { source = args[0] case 2: source = args[0] - opts.Reference = args[1] + reference = args[1] default: return errors.Errorf("too many arguments. Usage TARBALL [REFERENCE]") } @@ -87,11 +91,9 @@ func importCmd(c *cli.Context) error { History: history, } - opts.ImageConfig = config - opts.Writer = nil - + writer = nil if !c.Bool("quiet") { - opts.Writer = os.Stderr + writer = os.Stderr } // if source is a url, download it and save to a temp file @@ -105,9 +107,9 @@ func importCmd(c *cli.Context) error { source = file } - img, err := runtime.ImportImage(source, opts) + newImage, err := runtime.Import(source, reference, writer, image.SigningOptions{}, config) if err == nil { - fmt.Println(img.ID) + fmt.Println(newImage.ID()) } return err } diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index cfd257af4..b691b7a12 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -128,31 +128,27 @@ func iterateInput(c *cli.Context, args []string, runtime *libpod.Runtime, inspec break } case inspectTypeImage: - newImage := runtime.NewImage(input) - newImage.GetLocalImageName() - image, err := runtime.GetImage(newImage.LocalName) + image, err := runtime.ImageRuntime().NewFromLocal(input) if err != nil { inspectError = errors.Wrapf(err, "error getting image %q", input) break } - data, err = runtime.GetImageInspectInfo(*image) + data, err = libpod.GetImageData(image) if err != nil { - inspectError = errors.Wrapf(err, "error parsing image data %q", image.ID) + inspectError = errors.Wrapf(err, "error parsing image data %q", image.ID()) break } case inspectAll: ctr, err := runtime.LookupContainer(input) if err != nil { - newImage := runtime.NewImage(input) - newImage.GetLocalImageName() - image, err := runtime.GetImage(newImage.LocalName) + image, err := runtime.ImageRuntime().NewFromLocal(input) if err != nil { inspectError = errors.Wrapf(err, "error getting image %q", input) break } - data, err = runtime.GetImageInspectInfo(*image) + data, err = libpod.GetImageData(image) if err != nil { - inspectError = errors.Wrapf(err, "error parsing image data %q", image.ID) + inspectError = errors.Wrapf(err, "error parsing image data %q", image.ID()) break } } else { diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go index f5938ffb9..b38686a87 100644 --- a/cmd/podman/rmi.go +++ b/cmd/podman/rmi.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" "github.com/projectatomic/libpod/libpod" + "github.com/projectatomic/libpod/libpod/image" "github.com/urfave/cli" ) @@ -51,28 +52,37 @@ func rmiCmd(c *cli.Context) error { return errors.Errorf("when using the --all switch, you may not pass any images names or IDs") } - imagesToDelete := args[:] + images := args[:] var lastError error + var imagesToDelete []*image.Image if removeAll { - localImages, err := runtime.GetImages(&libpod.ImageFilterParams{}) + imagesToDelete, err = runtime.GetImages(&libpod.ImageFilterParams{}) if err != nil { return errors.Wrapf(err, "unable to query local images") } - for _, image := range localImages { - imagesToDelete = append(imagesToDelete, image.ID) + } else { + // Create image.image objects for deletion from user input + for _, i := range images { + newImage, err := runtime.ImageRuntime().NewFromLocal(i) + if err != nil { + fmt.Fprintln(os.Stderr, err) + continue + } + imagesToDelete = append(imagesToDelete, newImage) } } - - for _, arg := range imagesToDelete { - image := runtime.NewImage(arg) - iid, err := image.Remove(c.Bool("force")) + if len(imagesToDelete) == 0 { + return errors.Errorf("no valid images to delete") + } + for _, img := range imagesToDelete { + err := runtime.RemoveImage(img, c.Bool("force")) if err != nil { if lastError != nil { fmt.Fprintln(os.Stderr, lastError) } lastError = err } else { - fmt.Println(iid) + fmt.Println(img.ID()) } } return lastError diff --git a/cmd/podman/run.go b/cmd/podman/run.go index 7b840a387..f1e11f839 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -7,8 +7,10 @@ import ( "github.com/pkg/errors" "github.com/projectatomic/libpod/libpod" + "github.com/projectatomic/libpod/libpod/image" "github.com/sirupsen/logrus" "github.com/urfave/cli" + "os" ) var runDescription = "Runs a command in a new container from the given image" @@ -49,12 +51,15 @@ func runCmd(c *cli.Context) error { return errors.Errorf("image name or ID is required") } - imageName, _, data, err := imageData(c, runtime, c.Args()[0]) + rtc := runtime.GetConfig() + newImage, err := runtime.ImageRuntime().New(c.Args()[0], rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}) + + data, err := libpod.GetImageData(newImage) if err != nil { return err } - createConfig, err := parseCreateOpts(c, runtime, imageName, data) + createConfig, err := parseCreateOpts(c, runtime, newImage.Names()[0], data) if err != nil { return err } diff --git a/libpod/container_api.go b/libpod/container_api.go index ef94aa4c9..27c7994f4 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -1,16 +1,18 @@ package libpod import ( + "io" "io/ioutil" "os" "strconv" "time" - "github.com/containers/storage" "github.com/docker/docker/daemon/caps" "github.com/docker/docker/pkg/stringid" + ociv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/projectatomic/libpod/libpod/driver" + "github.com/projectatomic/libpod/libpod/image" "github.com/projectatomic/libpod/pkg/inspect" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/util/wait" @@ -532,7 +534,7 @@ func (c *Container) Inspect(size bool) (*inspect.ContainerInspectData, error) { // Commit commits the changes between a container and its image, creating a new // image -func (c *Container) Commit(pause bool, options CopyOptions) (*storage.Image, error) { +func (c *Container) Commit(pause bool, reference string, writer io.Writer, signingOptions image.SigningOptions, imageConfig ociv1.Image) (*image.Image, error) { if !c.locked { c.lock.Lock() defer c.lock.Unlock() @@ -563,7 +565,7 @@ func (c *Container) Commit(pause bool, options CopyOptions) (*storage.Image, err if err := c.export(tempFile.Name()); err != nil { return nil, err } - return c.runtime.ImportImage(tempFile.Name(), options) + return image.Import(tempFile.Name(), reference, writer, signingOptions, imageConfig, c.runtime.imageRuntime) } // Wait blocks on a container to exit and returns its exit code diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 2ee2174db..c3cae8509 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -926,16 +926,14 @@ func (c *Container) addImageVolumes(g *generate.Generator) error { if !c.state.Mounted { return errors.Wrapf(ErrInternal, "container is not mounted") } - - imageStorage, err := c.runtime.getImage(c.config.RootfsImageID) + newImage, err := c.runtime.imageRuntime.NewFromLocal(c.config.RootfsImageID) if err != nil { return err } - imageData, err := c.runtime.getImageInspectInfo(*imageStorage) + imageData, err := GetImageData(newImage) if err != nil { return err } - for k := range imageData.ContainerConfig.Volumes { mount := spec.Mount{ Destination: k, 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 } diff --git a/libpod/image_inspect.go b/libpod/image_inspect.go index df2d3640e..bedad3668 100644 --- a/libpod/image_inspect.go +++ b/libpod/image_inspect.go @@ -4,18 +4,22 @@ import ( "encoding/json" "strings" - "github.com/containers/image/types" - "github.com/containers/storage" digest "github.com/opencontainers/go-digest" ociv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" + "github.com/projectatomic/libpod/libpod/image" "github.com/projectatomic/libpod/pkg/inspect" ) -func getImageData(img storage.Image, imgRef types.Image, size int64, driver *inspect.Data) (*inspect.ImageData, error) { - imgSize, err := imgRef.Size() +// GetImageData returns an image's inspect data +func GetImageData(img *image.Image) (*inspect.ImageData, error) { + imgRef, err := img.ToImageRef() if err != nil { - return nil, errors.Wrapf(err, "error reading size of image %q", img.ID) + return nil, err + } + size, err := imgRef.Size() + if err != nil { + return nil, err } manifest, manifestType, err := imgRef.Manifest() if err != nil { @@ -37,13 +41,17 @@ func getImageData(img storage.Image, imgRef types.Image, size int64, driver *ins } var repoDigests []string - for _, name := range img.Names { + for _, name := range img.Names() { repoDigests = append(repoDigests, strings.SplitN(name, ":", 2)[0]+"@"+imgDigest.String()) } + driver, err := img.DriverData() + if err != nil { + return nil, err + } data := &inspect.ImageData{ - ID: img.ID, - RepoTags: img.Names, + ID: img.ID(), + RepoTags: img.Names(), RepoDigests: repoDigests, Comment: ociv1Img.History[0].Comment, Created: ociv1Img.Created, @@ -53,7 +61,7 @@ func getImageData(img storage.Image, imgRef types.Image, size int64, driver *ins ContainerConfig: &ociv1Img.Config, Version: info.DockerVersion, Size: size, - VirtualSize: size + imgSize, + VirtualSize: size, Annotations: annotations, Digest: imgDigest, Labels: info.Labels, diff --git a/libpod/runtime.go b/libpod/runtime.go index cd519b3ab..5c3705ee9 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -13,6 +13,7 @@ import ( "github.com/containers/storage" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" + "github.com/projectatomic/libpod/libpod/image" "github.com/sirupsen/logrus" "github.com/ulule/deepcopier" ) @@ -69,6 +70,7 @@ type Runtime struct { conmonPath string valid bool lock sync.RWMutex + imageRuntime *image.Runtime } // RuntimeConfig contains configuration options used to set up the runtime @@ -194,11 +196,9 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { return nil, errors.Wrapf(err, "error configuring runtime") } } - if err := makeRuntime(runtime); err != nil { return nil, err } - return runtime, nil } @@ -293,8 +293,20 @@ func makeRuntime(runtime *Runtime) error { if err != nil { return err } + runtime.store = store is.Transport.SetStore(store) + + // Set up image runtime and store in runtime + ir := image.NewImageRuntimeFromStore(runtime.store) + if err != nil { + return err + } + + runtime.imageRuntime = ir + + // Setting signaturepolicypath + ir.SignaturePolicyPath = runtime.config.SignaturePolicyPath defer func() { if err != nil { // Don't forcibly shut down @@ -576,3 +588,8 @@ func SaveDefaultConfig(path string) error { return ioutil.WriteFile(path, w.Bytes(), 0644) } + +// ImageRuntime returns the imageruntime for image resolution +func (r *Runtime) ImageRuntime() *image.Runtime { + return r.imageRuntime +} diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index fe9864dca..a58626855 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -27,7 +27,7 @@ import ( 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/libpod/image" "github.com/projectatomic/libpod/pkg/inspect" "github.com/projectatomic/libpod/pkg/util" ) @@ -152,7 +152,7 @@ func (r *Runtime) IsImageID(input string) (bool, error) { return false, errors.Wrapf(err, "unable to get images") } for _, image := range images { - if strings.HasPrefix(image.ID, input) { + if strings.HasPrefix(image.ID(), input) { return true, nil } } @@ -167,8 +167,8 @@ func (k *Image) GetNameByID() (string, error) { return "", errors.Wrapf(err, "unable to get images") } for _, image := range images { - if strings.HasPrefix(image.ID, k.Name) { - return image.Names[0], nil + if strings.HasPrefix(image.ID(), k.Name) { + return image.Names()[0], nil } } return "", errors.Errorf("unable to determine image for %s", k.Name) @@ -196,13 +196,13 @@ func (k *Image) GetImageID() (string, error) { } for _, image := range images { // Check if we have an ID match - if strings.HasPrefix(image.ID, k.Name) { - return image.ID, nil + if strings.HasPrefix(image.ID(), k.Name) { + return image.ID(), nil } // Check if we have a name match, perhaps a tagged name - for _, name := range image.Names { + for _, name := range image.Names() { if k.Name == name { - return image.ID, nil + return image.ID(), nil } } } @@ -364,7 +364,7 @@ func (k *Image) GetLocalImageName() (string, string, error) { return "", "", err } for _, image := range localImages { - for _, name := range image.Names { + for _, name := range image.Names() { imgRef, err := reference.Parse(name) if err != nil { continue @@ -383,13 +383,13 @@ func (k *Image) GetLocalImageName() (string, string, error) { if imageName == k.Name { k.LocalName = name - return name, image.ID, nil + return name, image.ID(), nil } imageSplit := strings.Split(imageName, "/") baseName := imageSplit[len(imageSplit)-1] if baseName == k.Name { k.LocalName = name - return name, image.ID, nil + return name, image.ID(), nil } } } @@ -431,26 +431,6 @@ func (k *Image) Pull(writer io.Writer) error { return nil } -// Remove calls into container storage and deletes the image -func (k *Image) Remove(force bool) (string, error) { - if k.LocalName == "" { - // This populates the images local name - _, _, err := k.GetLocalImageName() - if err != nil { - return "", errors.Wrapf(err, "unable to find %s locally", k.Name) - } - } - iid, err := k.GetImageID() - if err != nil { - return "", errors.Wrapf(err, "unable to get image id") - } - image, err := k.runtime.GetImage(iid) - if err != nil { - return "", errors.Wrapf(err, "unable to remove %s", iid) - } - return k.runtime.RemoveImage(image, force) -} - // GetRegistries gets the searchable registries from the global registration file. func GetRegistries() ([]string, error) { registryConfigPath := "" @@ -497,7 +477,7 @@ func getRegistries() ([]string, error) { // ImageFilter is a function to determine whether an image is included in // command output. Images to be outputted are tested using the function. A true // return will include the image, a false return will exclude it. -type ImageFilter func(*storage.Image, *inspect.ImageData) bool +type ImageFilter func(*image.Image, *inspect.ImageData) bool func (ips imageDecomposeStruct) returnFQName() string { return fmt.Sprintf("%s%s/%s:%s", ips.transport, ips.registry, ips.imageName, ips.tag) @@ -854,50 +834,52 @@ func (r *Runtime) UntagImage(image *storage.Image, tag string) (string, error) { // RemoveImage deletes an image from local storage // Images being used by running containers can only be removed if force=true -func (r *Runtime) RemoveImage(image *storage.Image, force bool) (string, error) { +func (r *Runtime) RemoveImage(image *image.Image, force bool) error { r.lock.Lock() defer r.lock.Unlock() if !r.valid { - return "", ErrRuntimeStopped + return ErrRuntimeStopped } // Get all containers, filter to only those using the image, and remove those containers ctrs, err := r.state.AllContainers() if err != nil { - return "", err + return err } imageCtrs := []*Container{} for _, ctr := range ctrs { - if ctr.config.RootfsImageID == image.ID { + if ctr.config.RootfsImageID == image.ID() { imageCtrs = append(imageCtrs, ctr) } } - if len(imageCtrs) > 0 && len(image.Names) <= 1 { + if len(imageCtrs) > 0 && len(image.Names()) <= 1 { if force { for _, ctr := range imageCtrs { if err := r.removeContainer(ctr, true); err != nil { - return "", errors.Wrapf(err, "error removing image %s: container %s using image could not be removed", image.ID, ctr.ID()) + return errors.Wrapf(err, "error removing image %s: container %s using image could not be removed", image.ID, ctr.ID()) } } } else { - return "", fmt.Errorf("could not remove image %s as it is being used by %d containers", image.ID, len(imageCtrs)) + return fmt.Errorf("could not remove image %s as it is being used by %d containers", image.ID, len(imageCtrs)) } } - if len(image.Names) > 1 && !force { - return "", fmt.Errorf("unable to delete %s (must force) - image is referred to in multiple tags", image.ID) + if len(image.Names()) > 1 && !force { + return fmt.Errorf("unable to delete %s (must force) - image is referred to in multiple tags", image.ID) } - // If it is forced, we have to untag the image so that it can be deleted - if err = r.store.SetNames(image.ID, image.Names[:0]); err != nil { - return "", err + if len(image.Names()) > 1 && force { + // If it is forced, we have to untag the image so that it can be deleted + if err = r.store.SetNames(image.ID(), image.Names()[:0]); err != nil { + return err + } } - _, err = r.store.DeleteImage(image.ID, true) - if err != nil { - return "", err + + if err = image.Remove(force); err != nil { + return err } - return image.ID, nil + return nil } // GetImage retrieves an image matching the given name or hash from system @@ -964,7 +946,7 @@ func (r *Runtime) getImageRef(image string) (types.Image, error) { // Filters can be provided which will determine which images are included in the // output. Multiple filters are handled by ANDing their output, so only images // matching all filters are included -func (r *Runtime) GetImages(params *ImageFilterParams, filters ...ImageFilter) ([]*storage.Image, error) { +func (r *Runtime) GetImages(params *ImageFilterParams, filters ...ImageFilter) ([]*image.Image, error) { r.lock.RLock() defer r.lock.RUnlock() @@ -972,21 +954,21 @@ func (r *Runtime) GetImages(params *ImageFilterParams, filters ...ImageFilter) ( return nil, ErrRuntimeStopped } - images, err := r.store.Images() + images, err := r.imageRuntime.GetImages() if err != nil { return nil, err } - - var imagesFiltered []*storage.Image + var imagesFiltered []*image.Image for _, img := range images { - info, err := r.getImageInspectInfo(img) + info, err := GetImageData(img) + if err != nil { return nil, err } var names []string - if len(img.Names) > 0 { - names = img.Names + if len(img.Names()) > 0 { + names = img.Names() } else { names = append(names, "<none>") } @@ -996,13 +978,14 @@ func (r *Runtime) GetImages(params *ImageFilterParams, filters ...ImageFilter) ( params.ImageName = name } for _, filter := range filters { - include = include && filter(&img, info) + include = include && filter(img, info) } if include { newImage := img - newImage.Names = []string{name} - imagesFiltered = append(imagesFiltered, &newImage) + // TODO I dont think this is needed. Will verify along the way + //newImage.Names = []string{name} + imagesFiltered = append(imagesFiltered, newImage) } } } @@ -1037,92 +1020,9 @@ func (r *Runtime) GetHistory(image string) ([]ociv1.History, []types.BlobInfo, s return oci.History, src.LayerInfos(), img.ID, nil } -// ImportImage imports an OCI format image archive into storage as an image -func (r *Runtime) ImportImage(path string, options CopyOptions) (*storage.Image, error) { - r.lock.RLock() - defer r.lock.RUnlock() - - if !r.valid { - return nil, ErrRuntimeStopped - } - - 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) - - err = updater.ConfigUpdate(options.ImageConfig, annotations) - if err != nil { - return nil, errors.Wrapf(err, "error updating image config") - } - - var reference = options.Reference - 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 := common.GetCopyOptions(options.Writer, "", nil, nil, common.SigningOptions{}, "", "", false) - - dest, err := is.Transport.ParseStoreReference(r.store, reference) - if err != nil { - errors.Wrapf(err, "error getting image reference for %q", options.Reference) - } - if err = cp.Image(policyContext, dest, src, copyOptions); err != nil { - return nil, err - } - // Use no lock version of GetImage - return r.getImage(reference) -} - -// GetImageInspectInfo returns the inspect information of an image -func (r *Runtime) GetImageInspectInfo(image storage.Image) (*inspect.ImageData, error) { - r.lock.RLock() - defer r.lock.RUnlock() - - if !r.valid { - return nil, ErrRuntimeStopped - } - return r.getImageInspectInfo(image) -} - -func (r *Runtime) getImageInspectInfo(image storage.Image) (*inspect.ImageData, error) { - imgRef, err := r.getImageRef("@" + image.ID) - if err != nil { - return nil, errors.Wrapf(err, "error reading image %q", image.ID) - } - - layer, err := r.store.Layer(image.TopLayer) - if err != nil { - return nil, errors.Wrapf(err, "error reading information about layer %q", image.TopLayer) - } - size, err := r.store.DiffSize(layer.Parent, layer.ID) - if err != nil { - return nil, errors.Wrapf(err, "error determining size of layer %q", layer.ID) - } - driverData, err := driver.GetDriverData(r.store, layer.ID) - if err != nil { - return nil, errors.Wrapf(err, "error getting graph driver info %q", image.ID) - } - return getImageData(image, imgRef, size, driverData) +//Import imports an oci format image archive into storage as an image +func (r *Runtime) Import(path, reference string, writer io.Writer, signingOptions image.SigningOptions, imageConfig ociv1.Image) (*image.Image, error) { + return image.Import(path, reference, writer, signingOptions, imageConfig, r.imageRuntime) } // ParseImageFilter takes a set of images and a filter string as input, and returns the libpod.ImageFilterParams struct @@ -1145,7 +1045,7 @@ func (r *Runtime) ParseImageFilter(imageInput, filter string) (*ImageFilterParam return ¶ms, nil } - images, err := r.store.Images() + images, err := r.imageRuntime.GetImages() if err != nil { return nil, err } @@ -1164,7 +1064,7 @@ func (r *Runtime) ParseImageFilter(imageInput, filter string) (*ImageFilterParam params.Label = pair[1] case "before": if img, err := findImageInSlice(images, pair[1]); err == nil { - info, err := r.GetImageInspectInfo(img) + info, err := GetImageData(img) if err != nil { return nil, err } @@ -1174,7 +1074,7 @@ func (r *Runtime) ParseImageFilter(imageInput, filter string) (*ImageFilterParam } case "since": if img, err := findImageInSlice(images, pair[1]); err == nil { - info, err := r.GetImageInspectInfo(img) + info, err := GetImageData(img) if err != nil { return nil, err } @@ -1240,18 +1140,18 @@ func ParseImageNames(names []string) (tags, digests []string, err error) { return tags, digests, nil } -func findImageInSlice(images []storage.Image, ref string) (storage.Image, error) { +func findImageInSlice(images []*image.Image, ref string) (*image.Image, error) { for _, image := range images { - if MatchesID(image.ID, ref) { + if MatchesID(image.ID(), ref) { return image, nil } - for _, name := range image.Names { + for _, name := range image.Names() { if MatchesReference(name, ref) { return image, nil } } } - return storage.Image{}, errors.New("could not find image") + return nil, errors.New("could not find image") } // getImageDigest creates an image object and uses the hex value of the digest as the image ID diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index 25e33240e..bb2d0c3a0 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -140,7 +140,7 @@ var _ = Describe("Podman pull", func() { clean.WaitWithDefaultTimeout() }) - It("podman pull from local directory", func() { + It("podman pull check quiet", func() { podmanTest.RestoreArtifact(ALPINE) setup := podmanTest.Podman([]string{"images", ALPINE, "-q", "--no-trunc"}) setup.WaitWithDefaultTimeout() |