mirror of https://github.com/containers/podman.git
Remove unused pkg/storage code
This code was refactored into libpod, so no need to keep the old version around Signed-off-by: Matthew Heon <matthew.heon@gmail.com>
This commit is contained in:
parent
dd0d35deb0
commit
0e52d43962
|
@ -1,5 +0,0 @@
|
|||
// Package storage provides helper functions for creating and managing CRI pod
|
||||
// sandboxes and containers and metadata associated with them in the format
|
||||
// that crio understands. The API it provides should be considered to be
|
||||
// unstable.
|
||||
package storage
|
|
@ -1,551 +0,0 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/copy"
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/image"
|
||||
"github.com/containers/image/signature"
|
||||
istorage "github.com/containers/image/storage"
|
||||
"github.com/containers/image/transports/alltransports"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/storage"
|
||||
distreference "github.com/docker/distribution/reference"
|
||||
)
|
||||
|
||||
// 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
|
||||
Digests []string
|
||||
Size *uint64
|
||||
ImageRef string
|
||||
}
|
||||
|
||||
type indexInfo struct {
|
||||
name string
|
||||
secure bool
|
||||
}
|
||||
|
||||
type imageService struct {
|
||||
store storage.Store
|
||||
defaultTransport string
|
||||
insecureRegistryCIDRs []*net.IPNet
|
||||
indexConfigs map[string]*indexInfo
|
||||
registries []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(systemContext *types.SystemContext, 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)
|
||||
// UntagImage removes a name from the specified image, and if it was
|
||||
// the only name the image had, removes the image.
|
||||
UntagImage(systemContext *types.SystemContext, imageName string) 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
|
||||
// CanPull preliminary checks whether we're allowed to pull an image
|
||||
CanPull(imageName string, options *copy.Options) (bool, error)
|
||||
// ResolveNames takes an image reference and if it's unqualified (w/o hostname),
|
||||
// it uses crio's default registries to qualify it.
|
||||
ResolveNames(imageName string) ([]string, error)
|
||||
}
|
||||
|
||||
func (svc *imageService) getRef(name string) (types.ImageReference, error) {
|
||||
ref, err := alltransports.ParseImageName(name)
|
||||
if err != nil {
|
||||
ref2, err2 := istorage.Transport.ParseStoreReference(svc.store, "@"+name)
|
||||
if err2 != nil {
|
||||
ref3, err3 := istorage.Transport.ParseStoreReference(svc.store, name)
|
||||
if err3 != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref2 = ref3
|
||||
}
|
||||
ref = ref2
|
||||
}
|
||||
return ref, nil
|
||||
}
|
||||
|
||||
func (svc *imageService) ListImages(systemContext *types.SystemContext, filter string) ([]ImageResult, error) {
|
||||
results := []ImageResult{}
|
||||
if filter != "" {
|
||||
ref, err := svc.getRef(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if image, err := istorage.Transport.GetStoreImage(svc.store, ref); err == nil {
|
||||
img, err := ref.NewImage(systemContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size := imageSize(img)
|
||||
img.Close()
|
||||
results = append(results, ImageResult{
|
||||
ID: image.ID,
|
||||
Names: image.Names,
|
||||
Size: size,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
images, err := svc.store.Images()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, image := range images {
|
||||
ref, err := istorage.Transport.ParseStoreReference(svc.store, "@"+image.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img, err := ref.NewImage(systemContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size := imageSize(img)
|
||||
img.Close()
|
||||
results = append(results, ImageResult{
|
||||
ID: image.ID,
|
||||
Names: image.Names,
|
||||
Size: size,
|
||||
})
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (svc *imageService) ImageStatus(systemContext *types.SystemContext, nameOrID string) (*ImageResult, error) {
|
||||
ref, err := alltransports.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()
|
||||
|
||||
result := ImageResult{
|
||||
ID: image.ID,
|
||||
Names: image.Names,
|
||||
Size: size,
|
||||
}
|
||||
if len(image.Names) > 0 {
|
||||
result.ImageRef = image.Names[0]
|
||||
if ref2, err2 := istorage.Transport.ParseStoreReference(svc.store, image.Names[0]); err2 == nil {
|
||||
if dref := ref2.DockerReference(); dref != nil {
|
||||
result.ImageRef = reference.FamiliarString(dref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func imageSize(img types.Image) *uint64 {
|
||||
if sum, err := img.Size(); err == nil {
|
||||
usum := uint64(sum)
|
||||
return &usum
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *imageService) CanPull(imageName string, options *copy.Options) (bool, error) {
|
||||
srcRef, err := svc.prepareImage(imageName, options)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
rawSource, err := srcRef.NewImageSource(options.SourceCtx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
src, err := image.FromSource(options.SourceCtx, rawSource)
|
||||
if err != nil {
|
||||
rawSource.Close()
|
||||
return false, err
|
||||
}
|
||||
src.Close()
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// prepareImage creates an image reference from an image string and set options
|
||||
// for the source context
|
||||
func (svc *imageService) prepareImage(imageName string, options *copy.Options) (types.ImageReference, error) {
|
||||
if imageName == "" {
|
||||
return nil, storage.ErrNotAnImage
|
||||
}
|
||||
|
||||
srcRef, err := alltransports.ParseImageName(imageName)
|
||||
if err != nil {
|
||||
if svc.defaultTransport == "" {
|
||||
return nil, err
|
||||
}
|
||||
srcRef2, err2 := alltransports.ParseImageName(svc.defaultTransport + imageName)
|
||||
if err2 != nil {
|
||||
return nil, err
|
||||
}
|
||||
srcRef = srcRef2
|
||||
}
|
||||
|
||||
if options.SourceCtx == nil {
|
||||
options.SourceCtx = &types.SystemContext{}
|
||||
}
|
||||
|
||||
hostname := reference.Domain(srcRef.DockerReference())
|
||||
if secure := svc.isSecureIndex(hostname); !secure {
|
||||
options.SourceCtx.DockerInsecureSkipTLSVerify = !secure
|
||||
}
|
||||
return srcRef, 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 options == nil {
|
||||
options = ©.Options{}
|
||||
}
|
||||
|
||||
srcRef, err := svc.prepareImage(imageName, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dest := imageName
|
||||
if srcRef.DockerReference() != nil {
|
||||
dest = srcRef.DockerReference().Name()
|
||||
if tagged, ok := srcRef.DockerReference().(reference.NamedTagged); ok {
|
||||
dest = dest + ":" + tagged.Tag()
|
||||
}
|
||||
if canonical, ok := srcRef.DockerReference().(reference.Canonical); ok {
|
||||
dest = dest + "@" + canonical.Digest().String()
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
return destRef, nil
|
||||
}
|
||||
|
||||
func (svc *imageService) UntagImage(systemContext *types.SystemContext, nameOrID string) error {
|
||||
ref, err := alltransports.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
|
||||
}
|
||||
|
||||
img, err := istorage.Transport.GetStoreImage(svc.store, ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nameOrID != img.ID {
|
||||
namedRef, err := svc.prepareImage(nameOrID, ©.Options{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := nameOrID
|
||||
if namedRef.DockerReference() != nil {
|
||||
name = namedRef.DockerReference().Name()
|
||||
if tagged, ok := namedRef.DockerReference().(reference.NamedTagged); ok {
|
||||
name = name + ":" + tagged.Tag()
|
||||
}
|
||||
if canonical, ok := namedRef.DockerReference().(reference.Canonical); ok {
|
||||
name = name + "@" + canonical.Digest().String()
|
||||
}
|
||||
}
|
||||
|
||||
prunedNames := make([]string, 0, len(img.Names))
|
||||
for _, imgName := range img.Names {
|
||||
if imgName != name && imgName != nameOrID {
|
||||
prunedNames = append(prunedNames, imgName)
|
||||
}
|
||||
}
|
||||
|
||||
if len(prunedNames) > 0 {
|
||||
return svc.store.SetNames(img.ID, prunedNames)
|
||||
}
|
||||
}
|
||||
|
||||
return ref.DeleteImage(systemContext)
|
||||
}
|
||||
|
||||
func (svc *imageService) RemoveImage(systemContext *types.SystemContext, nameOrID string) error {
|
||||
ref, err := alltransports.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
|
||||
}
|
||||
|
||||
func (svc *imageService) isSecureIndex(indexName string) bool {
|
||||
if index, ok := svc.indexConfigs[indexName]; ok {
|
||||
return index.secure
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(indexName)
|
||||
if err != nil {
|
||||
// assume indexName is of the form `host` without the port and go on.
|
||||
host = indexName
|
||||
}
|
||||
|
||||
addrs, err := net.LookupIP(host)
|
||||
if err != nil {
|
||||
ip := net.ParseIP(host)
|
||||
if ip != nil {
|
||||
addrs = []net.IP{ip}
|
||||
}
|
||||
|
||||
// if ip == nil, then `host` is neither an IP nor it could be looked up,
|
||||
// either because the index is unreachable, or because the index is behind an HTTP proxy.
|
||||
// So, len(addrs) == 0 and we're not aborting.
|
||||
}
|
||||
|
||||
// Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined.
|
||||
for _, addr := range addrs {
|
||||
for _, ipnet := range svc.insecureRegistryCIDRs {
|
||||
// check if the addr falls in the subnet
|
||||
if ipnet.Contains(addr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isValidHostname(hostname string) bool {
|
||||
return hostname != "" && !strings.Contains(hostname, "/") &&
|
||||
(strings.Contains(hostname, ".") ||
|
||||
strings.Contains(hostname, ":") || hostname == "localhost")
|
||||
}
|
||||
|
||||
func isReferenceFullyQualified(reposName reference.Named) bool {
|
||||
indexName, _, _ := splitReposName(reposName)
|
||||
return indexName != ""
|
||||
}
|
||||
|
||||
const (
|
||||
// defaultHostname is the default built-in hostname
|
||||
defaultHostname = "docker.io"
|
||||
// legacyDefaultHostname is automatically converted to DefaultHostname
|
||||
legacyDefaultHostname = "index.docker.io"
|
||||
// defaultRepoPrefix is the prefix used for default repositories in default host
|
||||
defaultRepoPrefix = "library/"
|
||||
)
|
||||
|
||||
// splitReposName breaks a reposName into an index name and remote name
|
||||
func splitReposName(reposName reference.Named) (indexName string, remoteName reference.Named, err error) {
|
||||
var remoteNameStr string
|
||||
indexName, remoteNameStr = distreference.SplitHostname(reposName)
|
||||
if !isValidHostname(indexName) {
|
||||
// This is a Docker Index repos (ex: samalba/hipache or ubuntu)
|
||||
// 'docker.io'
|
||||
indexName = ""
|
||||
remoteName = reposName
|
||||
} else {
|
||||
remoteName, err = withName(remoteNameStr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateName(name string) error {
|
||||
if err := validateID(strings.TrimPrefix(name, defaultHostname+"/")); err == nil {
|
||||
return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
|
||||
|
||||
// validateID checks whether an ID string is a valid image ID.
|
||||
func validateID(id string) error {
|
||||
if ok := validHex.MatchString(id); !ok {
|
||||
return fmt.Errorf("image ID %q is invalid", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// withName returns a named object representing the given string. If the input
|
||||
// is invalid ErrReferenceInvalidFormat will be returned.
|
||||
func withName(name string) (reference.Named, error) {
|
||||
name, err := normalize(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validateName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := distreference.WithName(name)
|
||||
return r, err
|
||||
}
|
||||
|
||||
// splitHostname splits a repository name to hostname and remotename string.
|
||||
// If no valid hostname is found, empty string will be returned as a resulting
|
||||
// hostname. Repository name needs to be already validated before.
|
||||
func splitHostname(name string) (hostname, remoteName string) {
|
||||
i := strings.IndexRune(name, '/')
|
||||
if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
|
||||
hostname, remoteName = "", name
|
||||
} else {
|
||||
hostname, remoteName = name[:i], name[i+1:]
|
||||
}
|
||||
if hostname == legacyDefaultHostname {
|
||||
hostname = defaultHostname
|
||||
}
|
||||
if hostname == defaultHostname && !strings.ContainsRune(remoteName, '/') {
|
||||
remoteName = defaultRepoPrefix + remoteName
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// normalize returns a repository name in its normalized form, meaning it
|
||||
// will contain library/ prefix for official images.
|
||||
func normalize(name string) (string, error) {
|
||||
host, remoteName := splitHostname(name)
|
||||
if strings.ToLower(remoteName) != remoteName {
|
||||
return "", errors.New("invalid reference format: repository name must be lowercase")
|
||||
}
|
||||
if host == defaultHostname {
|
||||
if strings.HasPrefix(remoteName, defaultRepoPrefix) {
|
||||
remoteName = strings.TrimPrefix(remoteName, defaultRepoPrefix)
|
||||
}
|
||||
return host + "/" + remoteName, nil
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (svc *imageService) ResolveNames(imageName string) ([]string, error) {
|
||||
r, err := reference.ParseNormalizedNamed(imageName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isReferenceFullyQualified(r) {
|
||||
// this means the image is already fully qualified
|
||||
return []string{imageName}, nil
|
||||
}
|
||||
// we got an unqualified image here, we can't go ahead w/o registries configured
|
||||
// properly.
|
||||
if len(svc.registries) == 0 {
|
||||
return nil, errors.New("no registries configured while trying to pull an unqualified image")
|
||||
}
|
||||
// this means we got an image in the form of "busybox"
|
||||
// we need to use additional registries...
|
||||
// normalize the unqualified image to be domain/repo/image...
|
||||
_, rest := splitDomain(r.Name())
|
||||
images := []string{}
|
||||
for _, r := range svc.registries {
|
||||
images = append(images, path.Join(r, rest))
|
||||
}
|
||||
return images, nil
|
||||
}
|
||||
|
||||
// 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, insecureRegistries []string, registries []string) (ImageServer, error) {
|
||||
if store == nil {
|
||||
var err error
|
||||
store, err = storage.GetStore(storage.DefaultStoreOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
seenRegistries := make(map[string]bool, len(registries))
|
||||
cleanRegistries := []string{}
|
||||
for _, r := range registries {
|
||||
if seenRegistries[r] {
|
||||
continue
|
||||
}
|
||||
cleanRegistries = append(cleanRegistries, r)
|
||||
seenRegistries[r] = true
|
||||
}
|
||||
|
||||
is := &imageService{
|
||||
store: store,
|
||||
defaultTransport: defaultTransport,
|
||||
indexConfigs: make(map[string]*indexInfo),
|
||||
insecureRegistryCIDRs: make([]*net.IPNet, 0),
|
||||
registries: cleanRegistries,
|
||||
}
|
||||
|
||||
insecureRegistries = append(insecureRegistries, "127.0.0.0/8")
|
||||
// Split --insecure-registry into CIDR and registry-specific settings.
|
||||
for _, r := range insecureRegistries {
|
||||
// Check if CIDR was passed to --insecure-registry
|
||||
_, ipnet, err := net.ParseCIDR(r)
|
||||
if err == nil {
|
||||
// Valid CIDR.
|
||||
is.insecureRegistryCIDRs = append(is.insecureRegistryCIDRs, ipnet)
|
||||
} else {
|
||||
// Assume `host:port` if not CIDR.
|
||||
is.indexConfigs[r] = &indexInfo{
|
||||
name: r,
|
||||
secure: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return is, nil
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
package storage
|
||||
|
||||
// This is a fork of docker/distribution code to be used when manipulating image
|
||||
// references.
|
||||
// DO NOT EDIT THIS FILE.
|
||||
|
||||
import "regexp"
|
||||
|
||||
var (
|
||||
// alphaNumericRegexp defines the alpha numeric atom, typically a
|
||||
// component of names. This only allows lower case characters and digits.
|
||||
alphaNumericRegexp = match(`[a-z0-9]+`)
|
||||
|
||||
// separatorRegexp defines the separators allowed to be embedded in name
|
||||
// components. This allow one period, one or two underscore and multiple
|
||||
// dashes.
|
||||
separatorRegexp = match(`(?:[._]|__|[-]*)`)
|
||||
|
||||
// nameComponentRegexp restricts registry path component names to start
|
||||
// with at least one letter or number, with following parts able to be
|
||||
// separated by one period, one or two underscore and multiple dashes.
|
||||
nameComponentRegexp = expression(
|
||||
alphaNumericRegexp,
|
||||
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
||||
|
||||
// domainComponentRegexp restricts the registry domain component of a
|
||||
// repository name to start with a component as defined by domainRegexp
|
||||
// and followed by an optional port.
|
||||
domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
||||
|
||||
// domainRegexp defines the structure of potential domain components
|
||||
// that may be part of image names. This is purposely a subset of what is
|
||||
// allowed by DNS to ensure backwards compatibility with Docker image
|
||||
// names.
|
||||
domainRegexp = expression(
|
||||
domainComponentRegexp,
|
||||
optional(repeated(literal(`.`), domainComponentRegexp)),
|
||||
optional(literal(`:`), match(`[0-9]+`)))
|
||||
|
||||
// NameRegexp is the format for the name component of references. The
|
||||
// regexp has capturing groups for the domain and name part omitting
|
||||
// the separating forward slash from either.
|
||||
NameRegexp = expression(
|
||||
optional(domainRegexp, literal(`/`)),
|
||||
nameComponentRegexp,
|
||||
optional(repeated(literal(`/`), nameComponentRegexp)))
|
||||
|
||||
// anchoredNameRegexp is used to parse a name value, capturing the
|
||||
// domain and trailing components.
|
||||
anchoredNameRegexp = anchored(
|
||||
optional(capture(domainRegexp), literal(`/`)),
|
||||
capture(nameComponentRegexp,
|
||||
optional(repeated(literal(`/`), nameComponentRegexp))))
|
||||
|
||||
// IdentifierRegexp is the format for string identifier used as a
|
||||
// content addressable identifier using sha256. These identifiers
|
||||
// are like digests without the algorithm, since sha256 is used.
|
||||
IdentifierRegexp = match(`([a-f0-9]{64})`)
|
||||
|
||||
// ShortIdentifierRegexp is the format used to represent a prefix
|
||||
// of an identifier. A prefix may be used to match a sha256 identifier
|
||||
// within a list of trusted identifiers.
|
||||
ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`)
|
||||
)
|
||||
|
||||
// match compiles the string to a regular expression.
|
||||
var match = regexp.MustCompile
|
||||
|
||||
// literal compiles s into a literal regular expression, escaping any regexp
|
||||
// reserved characters.
|
||||
func literal(s string) *regexp.Regexp {
|
||||
re := match(regexp.QuoteMeta(s))
|
||||
|
||||
if _, complete := re.LiteralPrefix(); !complete {
|
||||
panic("must be a literal")
|
||||
}
|
||||
|
||||
return re
|
||||
}
|
||||
|
||||
func splitDomain(name string) (string, string) {
|
||||
match := anchoredNameRegexp.FindStringSubmatch(name)
|
||||
if len(match) != 3 {
|
||||
return "", name
|
||||
}
|
||||
return match[1], match[2]
|
||||
}
|
||||
|
||||
// expression defines a full expression, where each regular expression must
|
||||
// follow the previous.
|
||||
func expression(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
var s string
|
||||
for _, re := range res {
|
||||
s += re.String()
|
||||
}
|
||||
|
||||
return match(s)
|
||||
}
|
||||
|
||||
// optional wraps the expression in a non-capturing group and makes the
|
||||
// production optional.
|
||||
func optional(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(group(expression(res...)).String() + `?`)
|
||||
}
|
||||
|
||||
// repeated wraps the regexp in a non-capturing group to get one or more
|
||||
// matches.
|
||||
func repeated(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(group(expression(res...)).String() + `+`)
|
||||
}
|
||||
|
||||
// group wraps the regexp in a non-capturing group.
|
||||
func group(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(`(?:` + expression(res...).String() + `)`)
|
||||
}
|
||||
|
||||
// capture wraps the expression in a capturing group.
|
||||
func capture(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(`(` + expression(res...).String() + `)`)
|
||||
}
|
||||
|
||||
// anchored anchors the regular expression by adding start and end delimiters.
|
||||
func anchored(res ...*regexp.Regexp) *regexp.Regexp {
|
||||
return match(`^` + expression(res...).String() + `$`)
|
||||
}
|
|
@ -1,452 +0,0 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/copy"
|
||||
istorage "github.com/containers/image/storage"
|
||||
"github.com/containers/image/transports/alltransports"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidPodName is returned when a pod name specified to a
|
||||
// function call is found to be invalid (most often, because it's
|
||||
// empty).
|
||||
ErrInvalidPodName = errors.New("invalid pod name")
|
||||
// ErrInvalidImageName is returned when an image name specified to a
|
||||
// function call is found to be invalid (most often, because it's
|
||||
// empty).
|
||||
ErrInvalidImageName = errors.New("invalid image name")
|
||||
// ErrInvalidContainerName is returned when a container name specified
|
||||
// to a function call is found to be invalid (most often, because it's
|
||||
// empty).
|
||||
ErrInvalidContainerName = errors.New("invalid container name")
|
||||
// ErrInvalidSandboxID is returned when a sandbox ID specified to a
|
||||
// function call is found to be invalid (because it's either
|
||||
// empty or doesn't match a valid sandbox).
|
||||
ErrInvalidSandboxID = errors.New("invalid sandbox ID")
|
||||
// ErrInvalidContainerID is returned when a container ID specified to a
|
||||
// function call is found to be invalid (because it's either
|
||||
// empty or doesn't match a valid container).
|
||||
ErrInvalidContainerID = errors.New("invalid container ID")
|
||||
)
|
||||
|
||||
type runtimeService struct {
|
||||
storageImageServer ImageServer
|
||||
pauseImage string
|
||||
}
|
||||
|
||||
// ContainerInfo wraps a subset of information about a container: its ID and
|
||||
// the locations of its nonvolatile and volatile per-container directories,
|
||||
// along with a copy of the configuration blob from the image that was used to
|
||||
// create the container, if the image had a configuration.
|
||||
type ContainerInfo struct {
|
||||
ID string
|
||||
Dir string
|
||||
RunDir string
|
||||
Config *v1.Image
|
||||
}
|
||||
|
||||
// RuntimeServer wraps up various CRI-related activities into a reusable
|
||||
// implementation.
|
||||
type RuntimeServer interface {
|
||||
// CreatePodSandbox creates a pod infrastructure container, using the
|
||||
// specified PodID for the infrastructure container's ID. In the CRI
|
||||
// view of things, a sandbox is distinct from its containers, including
|
||||
// its infrastructure container, but at this level the sandbox is
|
||||
// essentially the same as its infrastructure container, with a
|
||||
// container's membership in a pod being signified by it listing the
|
||||
// same pod ID in its metadata that the pod's other members do, and
|
||||
// with the pod's infrastructure container having the same value for
|
||||
// both its pod's ID and its container ID.
|
||||
// Pointer arguments can be nil. Either the image name or ID can be
|
||||
// omitted, but not both. All other arguments are required.
|
||||
CreatePodSandbox(systemContext *types.SystemContext, podName, podID, imageName, imageID, containerName, metadataName, uid, namespace string, attempt uint32, copyOptions *copy.Options) (ContainerInfo, error)
|
||||
// RemovePodSandbox deletes a pod sandbox's infrastructure container.
|
||||
// The CRI expects that a sandbox can't be removed unless its only
|
||||
// container is its infrastructure container, but we don't enforce that
|
||||
// here, since we're just keeping track of it for higher level APIs.
|
||||
RemovePodSandbox(idOrName string) error
|
||||
|
||||
// GetContainerMetadata returns the metadata we've stored for a container.
|
||||
GetContainerMetadata(idOrName string) (RuntimeContainerMetadata, error)
|
||||
// SetContainerMetadata updates the metadata we've stored for a container.
|
||||
SetContainerMetadata(idOrName string, metadata RuntimeContainerMetadata) error
|
||||
|
||||
// CreateContainer creates a container with the specified ID.
|
||||
// Pointer arguments can be nil. Either the image name or ID can be
|
||||
// omitted, but not both. All other arguments are required.
|
||||
CreateContainer(systemContext *types.SystemContext, podName, podID, imageName, imageID, containerName, containerID, metadataName string, attempt uint32, mountLabel string, copyOptions *copy.Options) (ContainerInfo, error)
|
||||
// DeleteContainer deletes a container, unmounting it first if need be.
|
||||
DeleteContainer(idOrName string) error
|
||||
|
||||
// StartContainer makes sure a container's filesystem is mounted, and
|
||||
// returns the location of its root filesystem, which is not guaranteed
|
||||
// by lower-level drivers to never change.
|
||||
StartContainer(idOrName string) (string, error)
|
||||
// StopContainer attempts to unmount a container's root filesystem,
|
||||
// freeing up any kernel resources which may be limited.
|
||||
StopContainer(idOrName string) error
|
||||
|
||||
// GetWorkDir returns the path of a nonvolatile directory on the
|
||||
// filesystem (somewhere under the Store's Root directory) which can be
|
||||
// used to store arbitrary data that is specific to the container. It
|
||||
// will be removed automatically when the container is deleted.
|
||||
GetWorkDir(id string) (string, error)
|
||||
// GetRunDir returns the path of a volatile directory (does not survive
|
||||
// the host rebooting, somewhere under the Store's RunRoot directory)
|
||||
// on the filesystem which can be used to store arbitrary data that is
|
||||
// specific to the container. It will be removed automatically when
|
||||
// the container is deleted.
|
||||
GetRunDir(id string) (string, error)
|
||||
}
|
||||
|
||||
// RuntimeContainerMetadata is the structure that we encode as JSON and store
|
||||
// in the metadata field of storage.Container objects. It is used for
|
||||
// specifying attributes of pod sandboxes and containers when they are being
|
||||
// created, and allows a container's MountLabel, and possibly other values, to
|
||||
// be modified in one read/write cycle via calls to
|
||||
// RuntimeServer.ContainerMetadata, RuntimeContainerMetadata.SetMountLabel,
|
||||
// and RuntimeServer.SetContainerMetadata.
|
||||
type RuntimeContainerMetadata struct {
|
||||
// Pod is true if this is the pod's infrastructure container.
|
||||
Pod bool `json:"pod,omitempty"` // Applicable to both PodSandboxes and Containers
|
||||
// The pod's name and ID, kept for use by upper layers in determining
|
||||
// which containers belong to which pods.
|
||||
PodName string `json:"pod-name"` // Applicable to both PodSandboxes and Containers, mandatory
|
||||
PodID string `json:"pod-id"` // Applicable to both PodSandboxes and Containers, mandatory
|
||||
// The provided name and the ID of the image that was used to
|
||||
// instantiate the container.
|
||||
ImageName string `json:"image-name"` // Applicable to both PodSandboxes and Containers
|
||||
ImageID string `json:"image-id"` // Applicable to both PodSandboxes and Containers
|
||||
// The container's name, which for an infrastructure container is usually PodName + "-infra".
|
||||
ContainerName string `json:"name"` // Applicable to both PodSandboxes and Containers, mandatory
|
||||
// The name as originally specified in PodSandbox or Container CRI metadata.
|
||||
MetadataName string `json:"metadata-name"` // Applicable to both PodSandboxes and Containers, mandatory
|
||||
UID string `json:"uid,omitempty"` // Only applicable to pods
|
||||
Namespace string `json:"namespace,omitempty"` // Only applicable to pods
|
||||
Attempt uint32 `json:"attempt,omitempty"` // Applicable to both PodSandboxes and Containers
|
||||
CreatedAt int64 `json:"created-at"` // Applicable to both PodSandboxes and Containers
|
||||
MountLabel string `json:"mountlabel,omitempty"` // Applicable to both PodSandboxes and Containers
|
||||
}
|
||||
|
||||
// SetMountLabel updates the mount label held by a RuntimeContainerMetadata
|
||||
// object.
|
||||
func (metadata *RuntimeContainerMetadata) SetMountLabel(mountLabel string) {
|
||||
metadata.MountLabel = mountLabel
|
||||
}
|
||||
|
||||
func (r *runtimeService) createContainerOrPodSandbox(systemContext *types.SystemContext, podName, podID, imageName, imageID, containerName, containerID, metadataName, uid, namespace string, attempt uint32, mountLabel string, options *copy.Options) (ContainerInfo, error) {
|
||||
var ref types.ImageReference
|
||||
if podName == "" || podID == "" {
|
||||
return ContainerInfo{}, ErrInvalidPodName
|
||||
}
|
||||
if imageName == "" && imageID == "" {
|
||||
return ContainerInfo{}, ErrInvalidImageName
|
||||
}
|
||||
if containerName == "" {
|
||||
return ContainerInfo{}, ErrInvalidContainerName
|
||||
}
|
||||
if metadataName == "" {
|
||||
metadataName = containerName
|
||||
}
|
||||
|
||||
// Check if we have the specified image.
|
||||
ref, err := istorage.Transport.ParseStoreReference(r.storageImageServer.GetStore(), imageName)
|
||||
if err != nil {
|
||||
// Maybe it's some other transport's copy of the image?
|
||||
otherRef, err2 := alltransports.ParseImageName(imageName)
|
||||
if err2 == nil && otherRef.DockerReference() != nil {
|
||||
ref, err = istorage.Transport.ParseStoreReference(r.storageImageServer.GetStore(), otherRef.DockerReference().Name())
|
||||
}
|
||||
if err != nil {
|
||||
// Maybe the image ID is sufficient?
|
||||
ref, err = istorage.Transport.ParseStoreReference(r.storageImageServer.GetStore(), "@"+imageID)
|
||||
if err != nil {
|
||||
return ContainerInfo{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
img, err := istorage.Transport.GetStoreImage(r.storageImageServer.GetStore(), ref)
|
||||
if img == nil && errors.Cause(err) == storage.ErrImageUnknown && imageName == r.pauseImage {
|
||||
image := imageID
|
||||
if imageName != "" {
|
||||
image = imageName
|
||||
}
|
||||
if image == "" {
|
||||
return ContainerInfo{}, ErrInvalidImageName
|
||||
}
|
||||
logrus.Debugf("couldn't find image %q, retrieving it", image)
|
||||
ref, err = r.storageImageServer.PullImage(systemContext, image, options)
|
||||
if err != nil {
|
||||
return ContainerInfo{}, err
|
||||
}
|
||||
img, err = istorage.Transport.GetStoreImage(r.storageImageServer.GetStore(), ref)
|
||||
if err != nil {
|
||||
return ContainerInfo{}, err
|
||||
}
|
||||
logrus.Debugf("successfully pulled image %q", image)
|
||||
}
|
||||
if img == nil && errors.Cause(err) == storage.ErrImageUnknown {
|
||||
if imageID == "" {
|
||||
return ContainerInfo{}, fmt.Errorf("image %q not present in image store", imageName)
|
||||
}
|
||||
if imageName == "" {
|
||||
return ContainerInfo{}, fmt.Errorf("image with ID %q not present in image store", imageID)
|
||||
}
|
||||
return ContainerInfo{}, fmt.Errorf("image %q with ID %q not present in image store", imageName, imageID)
|
||||
}
|
||||
|
||||
// Pull out a copy of the image's configuration.
|
||||
image, err := ref.NewImage(systemContext)
|
||||
if err != nil {
|
||||
return ContainerInfo{}, err
|
||||
}
|
||||
defer image.Close()
|
||||
|
||||
imageConfig, err := image.OCIConfig()
|
||||
if err != nil {
|
||||
return ContainerInfo{}, err
|
||||
}
|
||||
|
||||
// Update the image name and ID.
|
||||
if imageName == "" && len(img.Names) > 0 {
|
||||
imageName = img.Names[0]
|
||||
}
|
||||
imageID = img.ID
|
||||
|
||||
// Build metadata to store with the container.
|
||||
metadata := RuntimeContainerMetadata{
|
||||
Pod: containerID == podID,
|
||||
PodName: podName,
|
||||
PodID: podID,
|
||||
ImageName: imageName,
|
||||
ImageID: imageID,
|
||||
ContainerName: containerName,
|
||||
MetadataName: metadataName,
|
||||
UID: uid,
|
||||
Namespace: namespace,
|
||||
Attempt: attempt,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
MountLabel: mountLabel,
|
||||
}
|
||||
mdata, err := json.Marshal(&metadata)
|
||||
if err != nil {
|
||||
return ContainerInfo{}, err
|
||||
}
|
||||
|
||||
// Build the container.
|
||||
names := []string{metadata.ContainerName}
|
||||
if metadata.Pod {
|
||||
names = append(names, metadata.PodName)
|
||||
}
|
||||
container, err := r.storageImageServer.GetStore().CreateContainer(containerID, names, img.ID, "", string(mdata), nil)
|
||||
if err != nil {
|
||||
if metadata.Pod {
|
||||
logrus.Debugf("failed to create pod sandbox %s(%s): %v", metadata.PodName, metadata.PodID, err)
|
||||
} else {
|
||||
logrus.Debugf("failed to create container %s(%s): %v", metadata.ContainerName, containerID, err)
|
||||
}
|
||||
return ContainerInfo{}, err
|
||||
}
|
||||
if metadata.Pod {
|
||||
logrus.Debugf("created pod sandbox %q", container.ID)
|
||||
} else {
|
||||
logrus.Debugf("created container %q", container.ID)
|
||||
}
|
||||
|
||||
// If anything fails after this point, we need to delete the incomplete
|
||||
// container before returning.
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if err2 := r.storageImageServer.GetStore().DeleteContainer(container.ID); err2 != nil {
|
||||
if metadata.Pod {
|
||||
logrus.Infof("%v deleting partially-created pod sandbox %q", err2, container.ID)
|
||||
} else {
|
||||
logrus.Infof("%v deleting partially-created container %q", err2, container.ID)
|
||||
}
|
||||
return
|
||||
}
|
||||
logrus.Infof("deleted partially-created container %q", container.ID)
|
||||
}
|
||||
}()
|
||||
|
||||
// Add a name to the container's layer so that it's easier to follow
|
||||
// what's going on if we're just looking at the storage-eye view of things.
|
||||
layerName := metadata.ContainerName + "-layer"
|
||||
names, err = r.storageImageServer.GetStore().Names(container.LayerID)
|
||||
if err != nil {
|
||||
return ContainerInfo{}, err
|
||||
}
|
||||
names = append(names, layerName)
|
||||
err = r.storageImageServer.GetStore().SetNames(container.LayerID, names)
|
||||
if err != nil {
|
||||
return ContainerInfo{}, err
|
||||
}
|
||||
|
||||
// Find out where the container work directories are, so that we can return them.
|
||||
containerDir, err := r.storageImageServer.GetStore().ContainerDirectory(container.ID)
|
||||
if err != nil {
|
||||
return ContainerInfo{}, err
|
||||
}
|
||||
if metadata.Pod {
|
||||
logrus.Debugf("pod sandbox %q has work directory %q", container.ID, containerDir)
|
||||
} else {
|
||||
logrus.Debugf("container %q has work directory %q", container.ID, containerDir)
|
||||
}
|
||||
|
||||
containerRunDir, err := r.storageImageServer.GetStore().ContainerRunDirectory(container.ID)
|
||||
if err != nil {
|
||||
return ContainerInfo{}, err
|
||||
}
|
||||
if metadata.Pod {
|
||||
logrus.Debugf("pod sandbox %q has run directory %q", container.ID, containerRunDir)
|
||||
} else {
|
||||
logrus.Debugf("container %q has run directory %q", container.ID, containerRunDir)
|
||||
}
|
||||
|
||||
return ContainerInfo{
|
||||
ID: container.ID,
|
||||
Dir: containerDir,
|
||||
RunDir: containerRunDir,
|
||||
Config: imageConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *runtimeService) CreatePodSandbox(systemContext *types.SystemContext, podName, podID, imageName, imageID, containerName, metadataName, uid, namespace string, attempt uint32, copyOptions *copy.Options) (ContainerInfo, error) {
|
||||
return r.createContainerOrPodSandbox(systemContext, podName, podID, imageName, imageID, containerName, podID, metadataName, uid, namespace, attempt, "", copyOptions)
|
||||
}
|
||||
|
||||
func (r *runtimeService) CreateContainer(systemContext *types.SystemContext, podName, podID, imageName, imageID, containerName, containerID, metadataName string, attempt uint32, mountLabel string, copyOptions *copy.Options) (ContainerInfo, error) {
|
||||
return r.createContainerOrPodSandbox(systemContext, podName, podID, imageName, imageID, containerName, containerID, metadataName, "", "", attempt, mountLabel, copyOptions)
|
||||
}
|
||||
|
||||
func (r *runtimeService) RemovePodSandbox(idOrName string) error {
|
||||
container, err := r.storageImageServer.GetStore().Container(idOrName)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == storage.ErrContainerUnknown {
|
||||
return ErrInvalidSandboxID
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = r.storageImageServer.GetStore().DeleteContainer(container.ID)
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to delete pod sandbox %q: %v", container.ID, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *runtimeService) DeleteContainer(idOrName string) error {
|
||||
if idOrName == "" {
|
||||
return ErrInvalidContainerID
|
||||
}
|
||||
container, err := r.storageImageServer.GetStore().Container(idOrName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.storageImageServer.GetStore().DeleteContainer(container.ID)
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to delete container %q: %v", container.ID, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *runtimeService) SetContainerMetadata(idOrName string, metadata RuntimeContainerMetadata) error {
|
||||
mdata, err := json.Marshal(&metadata)
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to encode metadata for %q: %v", idOrName, err)
|
||||
return err
|
||||
}
|
||||
return r.storageImageServer.GetStore().SetMetadata(idOrName, string(mdata))
|
||||
}
|
||||
|
||||
func (r *runtimeService) GetContainerMetadata(idOrName string) (RuntimeContainerMetadata, error) {
|
||||
metadata := RuntimeContainerMetadata{}
|
||||
mdata, err := r.storageImageServer.GetStore().Metadata(idOrName)
|
||||
if err != nil {
|
||||
return metadata, err
|
||||
}
|
||||
if err = json.Unmarshal([]byte(mdata), &metadata); err != nil {
|
||||
return metadata, err
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func (r *runtimeService) StartContainer(idOrName string) (string, error) {
|
||||
container, err := r.storageImageServer.GetStore().Container(idOrName)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == storage.ErrContainerUnknown {
|
||||
return "", ErrInvalidContainerID
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
metadata := RuntimeContainerMetadata{}
|
||||
if err = json.Unmarshal([]byte(container.Metadata), &metadata); err != nil {
|
||||
return "", err
|
||||
}
|
||||
mountPoint, err := r.storageImageServer.GetStore().Mount(container.ID, metadata.MountLabel)
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to mount container %q: %v", container.ID, err)
|
||||
return "", err
|
||||
}
|
||||
logrus.Debugf("mounted container %q at %q", container.ID, mountPoint)
|
||||
return mountPoint, nil
|
||||
}
|
||||
|
||||
func (r *runtimeService) StopContainer(idOrName string) error {
|
||||
if idOrName == "" {
|
||||
return ErrInvalidContainerID
|
||||
}
|
||||
container, err := r.storageImageServer.GetStore().Container(idOrName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.storageImageServer.GetStore().Unmount(container.ID)
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to unmount container %q: %v", container.ID, err)
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("unmounted container %q", container.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *runtimeService) GetWorkDir(id string) (string, error) {
|
||||
container, err := r.storageImageServer.GetStore().Container(id)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == storage.ErrContainerUnknown {
|
||||
return "", ErrInvalidContainerID
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return r.storageImageServer.GetStore().ContainerDirectory(container.ID)
|
||||
}
|
||||
|
||||
func (r *runtimeService) GetRunDir(id string) (string, error) {
|
||||
container, err := r.storageImageServer.GetStore().Container(id)
|
||||
if err != nil {
|
||||
if errors.Cause(err) == storage.ErrContainerUnknown {
|
||||
return "", ErrInvalidContainerID
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return r.storageImageServer.GetStore().ContainerRunDirectory(container.ID)
|
||||
}
|
||||
|
||||
// GetRuntimeService returns a RuntimeServer that uses the passed-in image
|
||||
// service to pull and manage images, and its store to manage containers based
|
||||
// on those images.
|
||||
func GetRuntimeService(storageImageServer ImageServer, pauseImage string) RuntimeServer {
|
||||
return &runtimeService{
|
||||
storageImageServer: storageImageServer,
|
||||
pauseImage: pauseImage,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue