2017-07-23 23:01:37 +00:00
|
|
|
package image
|
2017-06-29 19:16:06 +00:00
|
|
|
|
|
|
|
import (
|
2017-07-27 16:49:44 +00:00
|
|
|
"encoding/json"
|
2017-06-29 19:16:06 +00:00
|
|
|
"time"
|
|
|
|
|
2017-07-25 19:39:16 +00:00
|
|
|
"github.com/containers/image/docker/reference"
|
2017-07-27 16:49:44 +00:00
|
|
|
"github.com/containers/image/transports"
|
2017-06-29 19:16:06 +00:00
|
|
|
"github.com/containers/storage"
|
2017-07-21 20:43:30 +00:00
|
|
|
"github.com/kubernetes-incubator/cri-o/libkpod/driver"
|
2017-07-25 19:33:41 +00:00
|
|
|
digest "github.com/opencontainers/go-digest"
|
2017-06-29 19:16:06 +00:00
|
|
|
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
2017-07-23 23:01:37 +00:00
|
|
|
// ImageData handles the data used when inspecting a container
|
2017-07-24 13:21:50 +00:00
|
|
|
// nolint
|
2017-07-23 23:01:37 +00:00
|
|
|
type ImageData struct {
|
2017-07-27 16:49:44 +00:00
|
|
|
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
|
2017-06-29 19:16:06 +00:00
|
|
|
}
|
|
|
|
|
2017-07-25 19:39:16 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2017-07-27 16:49:44 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-07-23 23:01:37 +00:00
|
|
|
// GetImageData gets the ImageData for a container with the given name in the given store.
|
|
|
|
func GetImageData(store storage.Store, name string) (*ImageData, error) {
|
|
|
|
img, err := FindImage(store, name)
|
2017-06-29 19:16:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "error reading image %q", name)
|
|
|
|
}
|
|
|
|
|
2017-07-27 16:49:44 +00:00
|
|
|
imgRef, err := FindImageRef(store, "@"+img.ID)
|
2017-06-29 19:16:06 +00:00
|
|
|
if err != nil {
|
2017-07-27 16:49:44 +00:00
|
|
|
return nil, errors.Wrapf(err, "error reading image %q", img.ID)
|
2017-06-29 19:16:06 +00:00
|
|
|
}
|
2017-07-27 16:49:44 +00:00
|
|
|
defer imgRef.Close()
|
2017-06-29 19:16:06 +00:00
|
|
|
|
2017-07-25 19:39:16 +00:00
|
|
|
tags, digests, err := ParseImageNames(img.Names)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "error parsing image names for %q", name)
|
|
|
|
}
|
|
|
|
|
2017-07-21 20:43:30 +00:00
|
|
|
driverName, err := driver.GetDriverName(store)
|
2017-06-29 19:16:06 +00:00
|
|
|
if err != nil {
|
2017-07-27 16:49:44 +00:00
|
|
|
return nil, errors.Wrapf(err, "error reading name of storage driver")
|
2017-06-29 19:16:06 +00:00
|
|
|
}
|
|
|
|
|
2017-07-25 19:33:41 +00:00
|
|
|
topLayerID := img.TopLayer
|
2017-07-25 19:39:16 +00:00
|
|
|
|
2017-07-21 20:43:30 +00:00
|
|
|
driverMetadata, err := driver.GetDriverMetadata(store, topLayerID)
|
2017-06-29 19:16:06 +00:00
|
|
|
if err != nil {
|
2017-07-27 16:49:44 +00:00
|
|
|
return nil, errors.Wrapf(err, "error asking storage driver %q for metadata", driverName)
|
2017-06-29 19:16:06 +00:00
|
|
|
}
|
|
|
|
|
2017-07-24 15:14:18 +00:00
|
|
|
layer, err := store.Layer(topLayerID)
|
2017-06-29 19:16:06 +00:00
|
|
|
if err != nil {
|
2017-07-27 16:49:44 +00:00
|
|
|
return nil, errors.Wrapf(err, "error reading information about layer %q", topLayerID)
|
2017-06-29 19:16:06 +00:00
|
|
|
}
|
2017-07-24 15:14:18 +00:00
|
|
|
size, err := store.DiffSize(layer.Parent, layer.ID)
|
2017-06-29 19:16:06 +00:00
|
|
|
if err != nil {
|
2017-07-27 16:49:44 +00:00
|
|
|
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()))
|
2017-06-29 19:16:06 +00:00
|
|
|
}
|
|
|
|
|
2017-07-27 16:49:44 +00:00
|
|
|
manifest, manifestType, err := imgRef.Manifest()
|
2017-06-29 19:16:06 +00:00
|
|
|
if err != nil {
|
2017-07-27 16:49:44 +00:00
|
|
|
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
|
2017-06-29 19:16:06 +00:00
|
|
|
}
|
|
|
|
|
2017-07-23 23:01:37 +00:00
|
|
|
return &ImageData{
|
2017-07-27 16:49:44 +00:00
|
|
|
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),
|
2017-07-23 23:01:37 +00:00
|
|
|
GraphDriver: driver.Data{
|
2017-06-29 19:16:06 +00:00
|
|
|
Name: driverName,
|
|
|
|
Data: driverMetadata,
|
|
|
|
},
|
2017-07-27 16:49:44 +00:00
|
|
|
RootFS: config.RootFS,
|
2017-06-29 19:16:06 +00:00
|
|
|
}, nil
|
|
|
|
}
|