summaryrefslogtreecommitdiff
path: root/libpod/images/image_data.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/images/image_data.go')
-rw-r--r--libpod/images/image_data.go203
1 files changed, 203 insertions, 0 deletions
diff --git a/libpod/images/image_data.go b/libpod/images/image_data.go
new file mode 100644
index 000000000..0295578c5
--- /dev/null
+++ b/libpod/images/image_data.go
@@ -0,0 +1,203 @@
+package images
+
+import (
+ "encoding/json"
+ "time"
+
+ "github.com/containers/image/docker/reference"
+ is "github.com/containers/image/storage"
+ "github.com/containers/image/transports"
+ "github.com/containers/image/types"
+ "github.com/containers/storage"
+ "github.com/projectatomic/libpod/libpod/driver"
+ digest "github.com/opencontainers/go-digest"
+ ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/pkg/errors"
+)
+
+// Data handles the data used when inspecting a container
+// nolint
+type Data struct {
+ ID string
+ Tags []string
+ Digests []string
+ Digest digest.Digest
+ Comment string
+ Created *time.Time
+ Container string
+ Author string
+ Config ociv1.ImageConfig
+ Architecture string
+ OS string
+ Annotations map[string]string
+ CreatedBy string
+ Size uint
+ VirtualSize uint
+ GraphDriver driver.Data
+ RootFS ociv1.RootFS
+}
+
+// ParseImageNames parses the names we've stored with an image into a list of
+// tagged references and a list of references which contain digests.
+func ParseImageNames(names []string) (tags, digests []string, err error) {
+ for _, name := range names {
+ if named, err := reference.ParseNamed(name); err == nil {
+ if digested, ok := named.(reference.Digested); ok {
+ canonical, err := reference.WithDigest(named, digested.Digest())
+ if err == nil {
+ digests = append(digests, canonical.String())
+ }
+ } else {
+ if reference.IsNameOnly(named) {
+ named = reference.TagNameOnly(named)
+ }
+ if tagged, ok := named.(reference.Tagged); ok {
+ namedTagged, err := reference.WithTag(named, tagged.Tag())
+ if err == nil {
+ tags = append(tags, namedTagged.String())
+ }
+ }
+ }
+ }
+ }
+ return tags, digests, nil
+}
+
+func annotations(manifest []byte, manifestType string) map[string]string {
+ annotations := make(map[string]string)
+ switch manifestType {
+ case ociv1.MediaTypeImageManifest:
+ var m ociv1.Manifest
+ if err := json.Unmarshal(manifest, &m); err == nil {
+ for k, v := range m.Annotations {
+ annotations[k] = v
+ }
+ }
+ }
+ return annotations
+}
+
+// GetData gets the Data for a container with the given name in the given store.
+func GetData(store storage.Store, name string) (*Data, error) {
+ img, err := FindImage(store, name)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading image %q", name)
+ }
+
+ imgRef, err := FindImageRef(store, "@"+img.ID)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading image %q", img.ID)
+ }
+ defer imgRef.Close()
+
+ tags, digests, err := ParseImageNames(img.Names)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing image names for %q", name)
+ }
+
+ driverName, err := driver.GetDriverName(store)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading name of storage driver")
+ }
+
+ topLayerID := img.TopLayer
+
+ driverMetadata, err := driver.GetDriverMetadata(store, topLayerID)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error asking storage driver %q for metadata", driverName)
+ }
+
+ layer, err := store.Layer(topLayerID)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading information about layer %q", topLayerID)
+ }
+ size, err := store.DiffSize(layer.Parent, layer.ID)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error determining size of layer %q", layer.ID)
+ }
+
+ imgSize, err := imgRef.Size()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error determining size of image %q", transports.ImageName(imgRef.Reference()))
+ }
+
+ manifest, manifestType, err := imgRef.Manifest()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading manifest for image %q", img.ID)
+ }
+ manifestDigest := digest.Digest("")
+ if len(manifest) > 0 {
+ manifestDigest = digest.Canonical.FromBytes(manifest)
+ }
+ annotations := annotations(manifest, manifestType)
+
+ config, err := imgRef.OCIConfig()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading image configuration for %q", img.ID)
+ }
+ historyComment := ""
+ historyCreatedBy := ""
+ if len(config.History) > 0 {
+ historyComment = config.History[len(config.History)-1].Comment
+ historyCreatedBy = config.History[len(config.History)-1].CreatedBy
+ }
+
+ return &Data{
+ ID: img.ID,
+ Tags: tags,
+ Digests: digests,
+ Digest: manifestDigest,
+ Comment: historyComment,
+ Created: config.Created,
+ Author: config.Author,
+ Config: config.Config,
+ Architecture: config.Architecture,
+ OS: config.OS,
+ Annotations: annotations,
+ CreatedBy: historyCreatedBy,
+ Size: uint(size),
+ VirtualSize: uint(size + imgSize),
+ GraphDriver: driver.Data{
+ Name: driverName,
+ Data: driverMetadata,
+ },
+ RootFS: config.RootFS,
+ }, nil
+}
+
+// FindImage searches for a *storage.Image with a matching the given name or ID in the given store.
+func FindImage(store storage.Store, image string) (*storage.Image, error) {
+ var img *storage.Image
+ ref, err := is.Transport.ParseStoreReference(store, image)
+ if err == nil {
+ img, err = is.Transport.GetStoreImage(store, ref)
+ }
+ if err != nil {
+ img2, err2 := store.Image(image)
+ if err2 != nil {
+ if ref == nil {
+ return nil, errors.Wrapf(err, "error parsing reference to image %q", image)
+ }
+ return nil, errors.Wrapf(err, "unable to locate image %q", image)
+ }
+ img = img2
+ }
+ return img, nil
+}
+
+// FindImageRef searches for and returns a new types.Image matching the given name or ID in the given store.
+func FindImageRef(store storage.Store, image string) (types.Image, error) {
+ img, err := FindImage(store, image)
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to locate image %q", image)
+ }
+ ref, err := is.Transport.ParseStoreReference(store, "@"+img.ID)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing reference to image %q", img.ID)
+ }
+ imgRef, err := ref.NewImage(nil)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading image %q", img.ID)
+ }
+ return imgRef, nil
+}