cri-o/pkg/storage/image.go
Nalin Dahyabhai 4ae8606edf Add storage utility functions
Add an intermediate API layer that uses containers/storage, and a
containers/image that has been patched to use it, to manage images and
containers, storing the data that we need to know about containers and
pods in the metadata fields provided by containers/storage.

While ocid manages pods and containers as different types of items, with
disjoint sets of IDs and names, it remains true that every pod includes
at least one container.  When a container's only purpose is to serve as
a home for namespaces that are shared with the other containers in the
pod, it is referred to as the pod's infrastructure container.

At the storage level, a pod is stored as its set of containers.  We keep
track of both pod IDs and container IDs in the metadata field of
Container objects that the storage library manages for us.  Containers
which bear the same pod ID are members of the pod which has that ID.
Other information about the pod, which ocid needs to remember in order
to answer requests for information about the pod, is also kept in the
metadata field of its member containers.

The container's runtime configuration should be stored in the
container's ContainerDirectory, and used as a template.  Each time the
container is about to be started, its layer should be mounted, that
configuration template should be read, the template's rootfs location
should be replaced with the mountpoint for the container's layer, and
the result should be saved to the container's ContainerRunDirectory,
for use as the configuration for the container.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2016-12-19 11:44:34 -05:00

195 lines
5.3 KiB
Go

package storage
import (
"github.com/containers/image/copy"
"github.com/containers/image/signature"
istorage "github.com/containers/image/storage"
"github.com/containers/image/transports"
"github.com/containers/image/types"
"github.com/containers/storage/storage"
)
// ImageResult wraps a subset of information about an image: its ID, its names,
// and the size, if known, or nil if it isn't.
type ImageResult struct {
ID string
Names []string
Size *uint64
}
type imageService struct {
store storage.Store
defaultTransport string
}
// ImageServer wraps up various CRI-related activities into a reusable
// implementation.
type ImageServer interface {
// ListImages returns list of all images which match the filter.
ListImages(filter string) ([]ImageResult, error)
// ImageStatus returns status of an image which matches the filter.
ImageStatus(systemContext *types.SystemContext, filter string) (*ImageResult, error)
// PullImage imports an image from the specified location.
PullImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.ImageReference, error)
// RemoveImage deletes the specified image.
RemoveImage(systemContext *types.SystemContext, imageName string) error
// GetStore returns the reference to the storage library Store which
// the image server uses to hold images, and is the destination used
// when it's asked to pull an image.
GetStore() storage.Store
}
func (svc *imageService) ListImages(filter string) ([]ImageResult, error) {
results := []ImageResult{}
if filter != "" {
if image, err := svc.store.GetImage(filter); err == nil {
results = append(results, ImageResult{
ID: image.ID,
Names: image.Names,
})
}
} else {
images, err := svc.store.Images()
if err != nil {
return nil, err
}
for _, image := range images {
results = append(results, ImageResult{
ID: image.ID,
Names: image.Names,
})
}
}
return results, nil
}
func (svc *imageService) ImageStatus(systemContext *types.SystemContext, nameOrID string) (*ImageResult, error) {
ref, err := transports.ParseImageName(nameOrID)
if err != nil {
ref2, err2 := istorage.Transport.ParseStoreReference(svc.store, "@"+nameOrID)
if err2 != nil {
ref3, err3 := istorage.Transport.ParseStoreReference(svc.store, nameOrID)
if err3 != nil {
return nil, err
}
ref2 = ref3
}
ref = ref2
}
image, err := istorage.Transport.GetStoreImage(svc.store, ref)
if err != nil {
return nil, err
}
img, err := ref.NewImage(systemContext)
if err != nil {
return nil, err
}
size := imageSize(img)
img.Close()
return &ImageResult{
ID: image.ID,
Names: image.Names,
Size: size,
}, nil
}
func imageSize(img types.Image) *uint64 {
if sum, err := img.Size(); err == nil {
usum := uint64(sum)
return &usum
}
return nil
}
func (svc *imageService) PullImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.ImageReference, error) {
policy, err := signature.DefaultPolicy(systemContext)
if err != nil {
return nil, err
}
policyContext, err := signature.NewPolicyContext(policy)
if err != nil {
return nil, err
}
if imageName == "" {
return nil, storage.ErrNotAnImage
}
if options == nil {
options = &copy.Options{}
}
srcRef, err := transports.ParseImageName(imageName)
if err != nil {
if svc.defaultTransport == "" {
return nil, err
}
srcRef2, err2 := transports.ParseImageName(svc.defaultTransport + imageName)
if err2 != nil {
return nil, err
}
srcRef = srcRef2
}
dest := imageName
if srcRef.DockerReference() != nil {
dest = srcRef.DockerReference().FullName()
}
destRef, err := istorage.Transport.ParseStoreReference(svc.store, dest)
if err != nil {
return nil, err
}
err = copy.Image(policyContext, destRef, srcRef, options)
if err != nil {
return nil, err
}
// Go find the image, and attach the requested name to it, so that we
// can more easily find it later, even if the destination reference
// looks different.
destImage, err := istorage.Transport.GetStoreImage(svc.store, destRef)
if err != nil {
return nil, err
}
names := append(destImage.Names, imageName, dest)
err = svc.store.SetNames(destImage.ID, names)
if err != nil {
return nil, err
}
return destRef, nil
}
func (svc *imageService) RemoveImage(systemContext *types.SystemContext, nameOrID string) error {
ref, err := transports.ParseImageName(nameOrID)
if err != nil {
ref2, err2 := istorage.Transport.ParseStoreReference(svc.store, "@"+nameOrID)
if err2 != nil {
ref3, err3 := istorage.Transport.ParseStoreReference(svc.store, nameOrID)
if err3 != nil {
return err
}
ref2 = ref3
}
ref = ref2
}
return ref.DeleteImage(systemContext)
}
func (svc *imageService) GetStore() storage.Store {
return svc.store
}
// GetImageService returns an ImageServer that uses the passed-in store, and
// which will prepend the passed-in defaultTransport value to an image name if
// a name that's passed to its PullImage() method can't be resolved to an image
// in the store and can't be resolved to a source on its own.
func GetImageService(store storage.Store, defaultTransport string) (ImageServer, error) {
if store == nil {
var err error
store, err = storage.GetStore(storage.DefaultStoreOptions)
if err != nil {
return nil, err
}
}
return &imageService{
store: store,
defaultTransport: defaultTransport,
}, nil
}