mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
Stage3 Image Library
This represents the stage3 implementation for the image library. At this point, we are moving the image-centric functions to pkg/image including migration of args and object-oriented references. This is a not a one-for-one migration of funcs and some funcs will need to continue to reside in runtime_img as they are overly specific to libpod and probably not useful to others. Signed-off-by: baude <bbaude@redhat.com> Closes: #484 Approved by: baude
This commit is contained in:
@ -18,6 +18,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
"github.com/projectatomic/libpod/pkg/inspect"
|
||||
"github.com/projectatomic/libpod/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@ -299,7 +300,7 @@ func isPortInPortBindings(pb map[nat.Port][]nat.PortBinding, port nat.Port) bool
|
||||
for _, i := range pb {
|
||||
hostPorts = append(hostPorts, i[0].HostPort)
|
||||
}
|
||||
return libpod.StringInSlice(port.Port(), hostPorts)
|
||||
return util.StringInSlice(port.Port(), hostPorts)
|
||||
}
|
||||
|
||||
// isPortInImagePorts determines if an exposed host port was given to us by metadata
|
||||
@ -625,7 +626,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime, imageName string,
|
||||
}
|
||||
|
||||
// Check for . and dns-search domains
|
||||
if libpod.StringInSlice(".", c.StringSlice("dns-search")) && len(c.StringSlice("dns-search")) > 1 {
|
||||
if util.StringInSlice(".", c.StringSlice("dns-search")) && len(c.StringSlice("dns-search")) > 1 {
|
||||
return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'")
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
"github.com/projectatomic/libpod/pkg/util"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
@ -89,7 +90,7 @@ func execCmd(c *cli.Context) error {
|
||||
// key and value to the environment variables. this is needed to set
|
||||
// PATH for example.
|
||||
for k, v := range defaultEnvVariables {
|
||||
if !libpod.StringInSlice(k, userEnvKeys) {
|
||||
if !util.StringInSlice(k, userEnvKeys) {
|
||||
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/projectatomic/libpod/cmd/podman/formats"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
"github.com/projectatomic/libpod/pkg/inspect"
|
||||
"github.com/projectatomic/libpod/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@ -68,7 +69,7 @@ func inspectCmd(c *cli.Context) error {
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
if !libpod.StringInSlice(inspectType, []string{inspectTypeContainer, inspectTypeImage, inspectAll}) {
|
||||
if !util.StringInSlice(inspectType, []string{inspectTypeContainer, inspectTypeImage, inspectAll}) {
|
||||
return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll)
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/cmd/podman/formats"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
"github.com/projectatomic/libpod/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
@ -275,7 +276,7 @@ func generateContainerFilterFuncs(filter, filterValue string, runtime *libpod.Ru
|
||||
return false
|
||||
}, nil
|
||||
case "status":
|
||||
if !libpod.StringInSlice(filterValue, []string{"created", "restarting", "running", "paused", "exited", "unknown"}) {
|
||||
if !util.StringInSlice(filterValue, []string{"created", "restarting", "running", "paused", "exited", "unknown"}) {
|
||||
return nil, errors.Errorf("%s is not a valid status", filterValue)
|
||||
}
|
||||
return func(c *libpod.Container) bool {
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
crioAnnotations "github.com/projectatomic/libpod/pkg/annotations"
|
||||
"github.com/projectatomic/libpod/pkg/chrootuser"
|
||||
"github.com/projectatomic/libpod/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/ulule/deepcopier"
|
||||
"golang.org/x/sys/unix"
|
||||
@ -642,7 +643,7 @@ func (c *Container) generateResolvConf() (string, error) {
|
||||
if len(c.config.DNSSearch) > 0 {
|
||||
resolv.searchDomains = nil
|
||||
// The . character means the user doesnt want any search domains in the container
|
||||
if !StringInSlice(".", c.config.DNSSearch) {
|
||||
if !util.StringInSlice(".", c.config.DNSSearch) {
|
||||
resolv.searchDomains = append(resolv.searchDomains, c.Config().DNSSearch...)
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/pkg/util"
|
||||
"github.com/projectatomic/libpod/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -91,7 +92,7 @@ func filterPids(psOutput string, pids []string) ([]string, error) {
|
||||
}
|
||||
cols := fieldsASCII(l)
|
||||
pid := cols[pidIndex]
|
||||
if StringInSlice(pid, pids) {
|
||||
if util.StringInSlice(pid, pids) {
|
||||
output = append(output, l)
|
||||
}
|
||||
}
|
||||
|
46
libpod/image/docker_registry_options.go
Normal file
46
libpod/image/docker_registry_options.go
Normal file
@ -0,0 +1,46 @@
|
||||
package image
|
||||
|
||||
import "github.com/containers/image/types"
|
||||
|
||||
// DockerRegistryOptions encapsulates settings that affect how we connect or
|
||||
// authenticate to a remote registry.
|
||||
type DockerRegistryOptions struct {
|
||||
// DockerRegistryCreds is the user name and password to supply in case
|
||||
// we need to pull an image from a registry, and it requires us to
|
||||
// authenticate.
|
||||
DockerRegistryCreds *types.DockerAuthConfig
|
||||
// DockerCertPath is the location of a directory containing CA
|
||||
// certificates which will be used to verify the registry's certificate
|
||||
// (all files with names ending in ".crt"), and possibly client
|
||||
// certificates and private keys (pairs of files with the same name,
|
||||
// except for ".cert" and ".key" suffixes).
|
||||
DockerCertPath string
|
||||
// DockerInsecureSkipTLSVerify turns off verification of TLS
|
||||
// certificates and allows connecting to registries without encryption.
|
||||
DockerInsecureSkipTLSVerify bool
|
||||
}
|
||||
|
||||
// GetSystemContext constructs a new system context from the given signaturePolicy path and the
|
||||
// values in the DockerRegistryOptions
|
||||
func (o DockerRegistryOptions) GetSystemContext(signaturePolicyPath, authFile string, forceCompress bool) *types.SystemContext {
|
||||
sc := &types.SystemContext{
|
||||
SignaturePolicyPath: signaturePolicyPath,
|
||||
DockerAuthConfig: o.DockerRegistryCreds,
|
||||
DockerCertPath: o.DockerCertPath,
|
||||
DockerInsecureSkipTLSVerify: o.DockerInsecureSkipTLSVerify,
|
||||
AuthFilePath: authFile,
|
||||
DirForceCompress: forceCompress,
|
||||
}
|
||||
return sc
|
||||
}
|
||||
|
||||
// GetSystemContext Constructs a new containers/image/types.SystemContext{} struct from the given signaturePolicy path
|
||||
func GetSystemContext(signaturePolicyPath, authFilePath string, forceCompress bool) *types.SystemContext {
|
||||
sc := &types.SystemContext{}
|
||||
if signaturePolicyPath != "" {
|
||||
sc.SignaturePolicyPath = signaturePolicyPath
|
||||
}
|
||||
sc.AuthFilePath = authFilePath
|
||||
sc.DirForceCompress = forceCompress
|
||||
return sc
|
||||
}
|
@ -1,15 +1,26 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
types2 "github.com/containernetworking/cni/pkg/types"
|
||||
cp "github.com/containers/image/copy"
|
||||
"github.com/containers/image/docker/reference"
|
||||
is "github.com/containers/image/storage"
|
||||
"github.com/containers/image/transports/alltransports"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/storage"
|
||||
"github.com/containers/storage/pkg/reexec"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
"github.com/projectatomic/libpod/pkg/inspect"
|
||||
"github.com/projectatomic/libpod/pkg/util"
|
||||
)
|
||||
|
||||
// Image is the primary struct for dealing with images
|
||||
@ -18,63 +29,113 @@ type Image struct {
|
||||
inspect.ImageData
|
||||
InputName string
|
||||
Local bool
|
||||
runtime *libpod.Runtime
|
||||
//runtime *libpod.Runtime
|
||||
image *storage.Image
|
||||
imageruntime *Runtime
|
||||
}
|
||||
|
||||
// Runtime contains the store
|
||||
type Runtime struct {
|
||||
store storage.Store
|
||||
}
|
||||
|
||||
// NewImageRuntime creates an Image Runtime including the store given
|
||||
// store options
|
||||
func NewImageRuntime(options storage.StoreOptions) (*Runtime, error) {
|
||||
if reexec.Init() {
|
||||
return nil, errors.Errorf("unable to reexec")
|
||||
}
|
||||
store, err := setStore(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Runtime{
|
||||
store: store,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func setStore(options storage.StoreOptions) (storage.Store, error) {
|
||||
store, err := storage.GetStore(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
is.Transport.SetStore(store)
|
||||
return store, nil
|
||||
}
|
||||
|
||||
// newFromStorage creates a new image object from a storage.Image
|
||||
func (ir *Runtime) newFromStorage(img *storage.Image) *Image {
|
||||
image := Image{
|
||||
InputName: img.ID,
|
||||
Local: true,
|
||||
imageruntime: ir,
|
||||
image: img,
|
||||
}
|
||||
return &image
|
||||
}
|
||||
|
||||
// NewFromLocal creates a new image object that is intended
|
||||
// to only deal with local images already in the store (or
|
||||
// its aliases)
|
||||
func NewFromLocal(name string, runtime *libpod.Runtime) (Image, error) {
|
||||
func (ir *Runtime) NewFromLocal(name string) (*Image, error) {
|
||||
|
||||
image := Image{
|
||||
InputName: name,
|
||||
Local: true,
|
||||
runtime: runtime,
|
||||
imageruntime: ir,
|
||||
}
|
||||
localImage, err := image.getLocalImage()
|
||||
if err != nil {
|
||||
return Image{}, err
|
||||
return nil, err
|
||||
}
|
||||
image.image = localImage
|
||||
return image, nil
|
||||
return &image, nil
|
||||
}
|
||||
|
||||
// New creates a new image object where the image could be local
|
||||
// or remote
|
||||
func New(name string, runtime *libpod.Runtime) (Image, error) {
|
||||
func (ir *Runtime) New(name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *DockerRegistryOptions, signingoptions SigningOptions) (*Image, error) {
|
||||
// We don't know if the image is local or not ... check local first
|
||||
newImage := Image{
|
||||
InputName: name,
|
||||
Local: false,
|
||||
runtime: runtime,
|
||||
imageruntime: ir,
|
||||
}
|
||||
localImage, err := newImage.getLocalImage()
|
||||
if err == nil {
|
||||
newImage.Local = true
|
||||
newImage.image = localImage
|
||||
return newImage, nil
|
||||
return &newImage, nil
|
||||
}
|
||||
|
||||
// The image is not local
|
||||
pullNames, err := newImage.createNamesToPull()
|
||||
|
||||
imageName, err := newImage.pullImage(writer, authfile, signaturePolicyPath, signingoptions, dockeroptions)
|
||||
if err != nil {
|
||||
return newImage, err
|
||||
return &newImage, errors.Errorf("unable to pull %s", name)
|
||||
}
|
||||
if len(pullNames) == 0 {
|
||||
return newImage, errors.Errorf("unable to pull %s", newImage.InputName)
|
||||
}
|
||||
var writer io.Writer
|
||||
writer = os.Stderr
|
||||
for _, p := range pullNames {
|
||||
_, err := newImage.pull(p, writer, runtime)
|
||||
if err == nil {
|
||||
newImage.InputName = p
|
||||
|
||||
newImage.InputName = imageName
|
||||
img, err := newImage.getLocalImage()
|
||||
newImage.image = img
|
||||
return newImage, err
|
||||
return &newImage, nil
|
||||
}
|
||||
|
||||
// Shutdown closes down the storage and require a bool arg as to
|
||||
// whether it should do so forcibly.
|
||||
func (ir *Runtime) Shutdown(force bool) error {
|
||||
_, err := ir.store.Shutdown(force)
|
||||
return err
|
||||
}
|
||||
return newImage, errors.Errorf("unable to find %s", name)
|
||||
|
||||
func (i *Image) reloadImage() error {
|
||||
newImage, err := i.imageruntime.getImage(i.ID())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to reload image")
|
||||
}
|
||||
i.image = newImage.image
|
||||
return nil
|
||||
}
|
||||
|
||||
// getLocalImage resolves an unknown input describing an image and
|
||||
@ -85,9 +146,9 @@ func (i *Image) getLocalImage() (*storage.Image, error) {
|
||||
return nil, errors.Errorf("input name is blank")
|
||||
}
|
||||
var taggedName string
|
||||
img, err := i.runtime.GetImage(i.InputName)
|
||||
img, err := i.imageruntime.getImage(i.InputName)
|
||||
if err == nil {
|
||||
return img, err
|
||||
return img.image, err
|
||||
}
|
||||
|
||||
// container-storage wasn't able to find it in its current form
|
||||
@ -100,9 +161,9 @@ func (i *Image) getLocalImage() (*storage.Image, error) {
|
||||
// the inputname isn't tagged, so we assume latest and try again
|
||||
if !decomposedImage.isTagged {
|
||||
taggedName = fmt.Sprintf("%s:latest", i.InputName)
|
||||
img, err = i.runtime.GetImage(taggedName)
|
||||
img, err = i.imageruntime.getImage(taggedName)
|
||||
if err == nil {
|
||||
return img, nil
|
||||
return img.image, nil
|
||||
}
|
||||
}
|
||||
hasReg, err := i.hasRegistry()
|
||||
@ -116,7 +177,7 @@ func (i *Image) getLocalImage() (*storage.Image, error) {
|
||||
}
|
||||
|
||||
// grab all the local images
|
||||
images, err := i.runtime.GetImages(&libpod.ImageFilterParams{})
|
||||
images, err := i.imageruntime.GetImages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -149,43 +210,226 @@ func (i *Image) ID() string {
|
||||
return i.image.ID
|
||||
}
|
||||
|
||||
// createNamesToPull looks at a decomposed image and determines the possible
|
||||
// images names to try pulling in combination with the registries.conf file as well
|
||||
func (i *Image) createNamesToPull() ([]string, error) {
|
||||
var pullNames []string
|
||||
decomposedImage, err := decompose(i.InputName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Digest returns the image's Manifest
|
||||
func (i *Image) Digest() digest.Digest {
|
||||
return i.image.Digest
|
||||
}
|
||||
|
||||
if decomposedImage.hasRegistry {
|
||||
pullNames = append(pullNames, i.InputName)
|
||||
} else {
|
||||
registries, err := libpod.GetRegistries()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, registry := range registries {
|
||||
decomposedImage.registry = registry
|
||||
pullNames = append(pullNames, decomposedImage.assemble())
|
||||
}
|
||||
}
|
||||
return pullNames, nil
|
||||
// Names returns a string array of names associated with the image
|
||||
func (i *Image) Names() []string {
|
||||
return i.image.Names
|
||||
}
|
||||
|
||||
// pull is a temporary function for stage1 to be able to pull images during the image
|
||||
// resolution tests. it will be replaced in stage2 with a more robust function.
|
||||
func (i *Image) pull(name string, writer io.Writer, r *libpod.Runtime) (string, error) {
|
||||
options := libpod.CopyOptions{
|
||||
Writer: writer,
|
||||
SignaturePolicyPath: r.GetConfig().SignaturePolicyPath,
|
||||
}
|
||||
return i.runtime.PullImage(name, options)
|
||||
// Created returns the time the image was created
|
||||
func (i *Image) Created() time.Time {
|
||||
return i.image.Created
|
||||
}
|
||||
|
||||
// Remove an image
|
||||
// This function is only complete enough for the stage 1 tests.
|
||||
// TopLayer returns the top layer id as a string
|
||||
func (i *Image) TopLayer() string {
|
||||
return i.image.TopLayer
|
||||
}
|
||||
|
||||
// Remove an image; container removal for the image must be done
|
||||
// outside the context of images
|
||||
func (i *Image) Remove(force bool) error {
|
||||
_, err := i.runtime.RemoveImage(i.image, force)
|
||||
_, err := i.imageruntime.store.DeleteImage(i.ID(), true)
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Decompose an Image
|
||||
func (i *Image) Decompose() error {
|
||||
return types2.NotImplementedError
|
||||
}
|
||||
|
||||
// TODO: Rework this method to not require an assembly of the fq name with transport
|
||||
/*
|
||||
// GetManifest tries to GET an images manifest, returns nil on success and err on failure
|
||||
func (i *Image) GetManifest() error {
|
||||
pullRef, err := alltransports.ParseImageName(i.assembleFqNameTransport())
|
||||
if err != nil {
|
||||
return errors.Errorf("unable to parse '%s'", i.Names()[0])
|
||||
}
|
||||
imageSource, err := pullRef.NewImageSource(nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to create new image source")
|
||||
}
|
||||
_, _, err = imageSource.GetManifest(nil)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
*/
|
||||
|
||||
// getImage retrieves an image matching the given name or hash from system
|
||||
// storage
|
||||
// If no matching image can be found, an error is returned
|
||||
func (ir *Runtime) getImage(image string) (*Image, error) {
|
||||
var img *storage.Image
|
||||
ref, err := is.Transport.ParseStoreReference(ir.store, image)
|
||||
if err == nil {
|
||||
img, err = is.Transport.GetStoreImage(ir.store, ref)
|
||||
}
|
||||
if err != nil {
|
||||
img2, err2 := ir.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
|
||||
}
|
||||
newImage := ir.newFromStorage(img)
|
||||
return newImage, nil
|
||||
}
|
||||
|
||||
// GetImages retrieves all images present in storage
|
||||
func (ir *Runtime) GetImages() ([]*Image, error) {
|
||||
var newImages []*Image
|
||||
images, err := ir.store.Images()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, i := range images {
|
||||
newImages = append(newImages, ir.newFromStorage(&i))
|
||||
}
|
||||
return newImages, nil
|
||||
}
|
||||
|
||||
// getImageDigest creates an image object and uses the hex value of the digest as the image ID
|
||||
// for parsing the store reference
|
||||
func getImageDigest(src types.ImageReference, ctx *types.SystemContext) (string, error) {
|
||||
newImg, err := src.NewImage(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer newImg.Close()
|
||||
digest := newImg.ConfigInfo().Digest
|
||||
if err = digest.Validate(); err != nil {
|
||||
return "", errors.Wrapf(err, "error getting config info")
|
||||
}
|
||||
return "@" + digest.Hex(), nil
|
||||
}
|
||||
|
||||
// TagImage adds a tag to the given image
|
||||
func (i *Image) TagImage(tag string) error {
|
||||
tags := i.Names()
|
||||
if util.StringInSlice(tag, tags) {
|
||||
return nil
|
||||
}
|
||||
tags = append(tags, tag)
|
||||
i.reloadImage()
|
||||
return i.imageruntime.store.SetNames(i.ID(), tags)
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
|
||||
// Get the destination Image Reference
|
||||
dest, err := alltransports.ParseImageName(destination)
|
||||
if err != nil {
|
||||
if hasTransport(destination) {
|
||||
return errors.Wrapf(err, "error getting destination imageReference for %q", destination)
|
||||
}
|
||||
// Try adding the images default transport
|
||||
destination2 := DefaultTransport + destination
|
||||
dest, err = alltransports.ParseImageName(destination2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sc := GetSystemContext(signaturePolicyPath, authFile, forceCompress)
|
||||
|
||||
policyContext, err := getPolicyContext(sc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer policyContext.Destroy()
|
||||
|
||||
// Look up the source image, expecting it to be in local storage
|
||||
src, err := is.Transport.ParseStoreReference(i.imageruntime.store, i.ID())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error getting source imageReference for %q", i.InputName)
|
||||
}
|
||||
|
||||
copyOptions := getCopyOptions(writer, signaturePolicyPath, nil, dockerRegistryOptions, signingOptions, authFile, manifestMIMEType, forceCompress)
|
||||
|
||||
// Copy the image to the remote destination
|
||||
err = cp.Image(policyContext, dest, src, copyOptions)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Error copying image to the remote destination")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MatchesID returns a bool based on if the input id
|
||||
// matches the image's id
|
||||
func (i *Image) MatchesID(id string) bool {
|
||||
return strings.HasPrefix(i.ID(), id)
|
||||
}
|
||||
|
||||
// toStorageReference returns a *storageReference from an Image
|
||||
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
|
||||
func (i *Image) toImageRef() (types.Image, error) {
|
||||
ref, err := is.Transport.ParseStoreReference(i.imageruntime.store, "@"+i.ID())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error parsing reference to image %q", i.ID())
|
||||
}
|
||||
imgRef, err := ref.NewImage(nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error reading image %q", i.ID())
|
||||
}
|
||||
return imgRef, nil
|
||||
}
|
||||
|
||||
// sizer knows its size.
|
||||
type sizer interface {
|
||||
Size() (int64, error)
|
||||
}
|
||||
|
||||
//Size returns the size of the image
|
||||
func (i *Image) Size() (*uint64, error) {
|
||||
storeRef, err := is.Transport.ParseStoreReference(i.imageruntime.store, i.ID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
systemContext := &types.SystemContext{}
|
||||
img, err := storeRef.NewImageSource(systemContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s, ok := img.(sizer); ok {
|
||||
if sum, err := s.Size(); err == nil {
|
||||
usum := uint64(sum)
|
||||
return &usum, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Errorf("unable to determine size")
|
||||
|
||||
}
|
||||
|
@ -2,15 +2,12 @@ package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/storage"
|
||||
"github.com/containers/storage/pkg/reexec"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -20,43 +17,9 @@ var (
|
||||
fedoraNames = []string{"registry.fedoraproject.org/fedora-minimal:latest", "registry.fedoraproject.org/fedora-minimal", "fedora-minimal:latest", "fedora-minimal"}
|
||||
)
|
||||
|
||||
// setup a runtime for the tests in an alternative location on the filesystem
|
||||
func setupRuntime(workdir string) (*libpod.Runtime, error) {
|
||||
if reexec.Init() {
|
||||
return nil, errors.Errorf("dude")
|
||||
}
|
||||
sc := libpod.WithStorageConfig(storage.StoreOptions{
|
||||
GraphRoot: workdir,
|
||||
RunRoot: workdir,
|
||||
})
|
||||
sd := libpod.WithStaticDir(path.Join(workdir, "libpod_tmp"))
|
||||
td := libpod.WithTmpDir(path.Join(workdir, "tmpdir"))
|
||||
|
||||
options := []libpod.RuntimeOption{sc, sd, td}
|
||||
return libpod.NewRuntime(options...)
|
||||
}
|
||||
|
||||
// getImage is only used to build a test matrix for testing local images
|
||||
func getImage(r *libpod.Runtime, fqImageName string) (*storage.Image, error) {
|
||||
img, err := NewFromLocal(fqImageName, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img.image, nil
|
||||
}
|
||||
|
||||
func tagImage(r *libpod.Runtime, fqImageName, tagName string) error {
|
||||
img, err := NewFromLocal(fqImageName, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.TagImage(img.image, tagName)
|
||||
return nil
|
||||
}
|
||||
|
||||
type localImageTest struct {
|
||||
fqname, taggedName string
|
||||
img *storage.Image
|
||||
img *Image
|
||||
names []string
|
||||
}
|
||||
|
||||
@ -66,8 +29,11 @@ func mkWorkDir() (string, error) {
|
||||
}
|
||||
|
||||
// shutdown the runtime and clean behind it
|
||||
func cleanup(r *libpod.Runtime, workdir string) {
|
||||
r.Shutdown(true)
|
||||
func cleanup(workdir string, ir *Runtime) {
|
||||
if err := ir.Shutdown(false); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err := os.RemoveAll(workdir)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
@ -75,46 +41,27 @@ func cleanup(r *libpod.Runtime, workdir string) {
|
||||
}
|
||||
}
|
||||
|
||||
func makeLocalMatrix(r *libpod.Runtime) ([]localImageTest, error) {
|
||||
func makeLocalMatrix(b, bg *Image) ([]localImageTest, error) {
|
||||
var l []localImageTest
|
||||
// busybox
|
||||
busybox := localImageTest{
|
||||
fqname: "docker.io/library/busybox:latest",
|
||||
taggedName: "bb:latest",
|
||||
}
|
||||
b, err := getImage(r, busybox.fqname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
busybox.img = b
|
||||
busybox.names = bbNames
|
||||
busybox.names = append(busybox.names, []string{"bb:latest", "bb", b.ID, b.ID[0:7], fmt.Sprintf("busybox@%s", b.Digest.String())}...)
|
||||
|
||||
//fedora
|
||||
fedora := localImageTest{
|
||||
fqname: "registry.fedoraproject.org/fedora-minimal:latest",
|
||||
taggedName: "f27:latest",
|
||||
}
|
||||
f, err := getImage(r, fedora.fqname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fedora.img = f
|
||||
fedora.names = fedoraNames
|
||||
busybox.names = b.Names()
|
||||
busybox.names = append(busybox.names, []string{"bb:latest", "bb", b.ID(), b.ID()[0:7], fmt.Sprintf("busybox@%s", b.Digest())}...)
|
||||
|
||||
// busybox-glibc
|
||||
busyboxGlibc := localImageTest{
|
||||
fqname: "docker.io/library/busybox:glibc",
|
||||
taggedName: "bb:glibc",
|
||||
}
|
||||
bg, err := getImage(r, busyboxGlibc.fqname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
busyboxGlibc.img = bg
|
||||
busyboxGlibc.names = bbGlibcNames
|
||||
|
||||
l = append(l, busybox, fedora)
|
||||
l = append(l, busybox, busyboxGlibc)
|
||||
return l, nil
|
||||
|
||||
}
|
||||
@ -124,32 +71,37 @@ func makeLocalMatrix(r *libpod.Runtime) ([]localImageTest, error) {
|
||||
func TestImage_NewFromLocal(t *testing.T) {
|
||||
workdir, err := mkWorkDir()
|
||||
assert.NoError(t, err)
|
||||
runtime, err := setupRuntime(workdir)
|
||||
assert.NoError(t, err)
|
||||
so := storage.StoreOptions{
|
||||
RunRoot: workdir,
|
||||
GraphRoot: workdir,
|
||||
}
|
||||
var writer io.Writer
|
||||
writer = os.Stdout
|
||||
|
||||
// Need images to be present for this test
|
||||
_, err = runtime.PullImage("docker.io/library/busybox:latest", libpod.CopyOptions{})
|
||||
ir, err := NewImageRuntime(so)
|
||||
assert.NoError(t, err)
|
||||
_, err = runtime.PullImage("docker.io/library/busybox:glibc", libpod.CopyOptions{})
|
||||
bb, err := ir.New("docker.io/library/busybox:latest", "", "", writer, nil, SigningOptions{})
|
||||
assert.NoError(t, err)
|
||||
_, err = runtime.PullImage("registry.fedoraproject.org/fedora-minimal:latest", libpod.CopyOptions{})
|
||||
bbglibc, err := ir.New("docker.io/library/busybox:glibc", "", "", writer, nil, SigningOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
tm, err := makeLocalMatrix(runtime)
|
||||
tm, err := makeLocalMatrix(bb, bbglibc)
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, image := range tm {
|
||||
// tag our images
|
||||
err = tagImage(runtime, image.fqname, image.taggedName)
|
||||
image.img.TagImage(image.taggedName)
|
||||
assert.NoError(t, err)
|
||||
for _, name := range image.names {
|
||||
newImage, err := NewFromLocal(name, runtime)
|
||||
newImage, err := ir.NewFromLocal(name)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, newImage.ID(), image.img.ID)
|
||||
assert.Equal(t, newImage.ID(), image.img.ID())
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown the runtime and remove the temporary storage
|
||||
cleanup(runtime, workdir)
|
||||
cleanup(workdir, ir)
|
||||
}
|
||||
|
||||
// TestImage_New tests pulling the image by various names, tags, and from
|
||||
@ -158,21 +110,23 @@ func TestImage_New(t *testing.T) {
|
||||
var names []string
|
||||
workdir, err := mkWorkDir()
|
||||
assert.NoError(t, err)
|
||||
runtime, err := setupRuntime(workdir)
|
||||
assert.NoError(t, err)
|
||||
|
||||
so := storage.StoreOptions{
|
||||
RunRoot: workdir,
|
||||
GraphRoot: workdir,
|
||||
}
|
||||
ir, err := NewImageRuntime(so)
|
||||
assert.NoError(t, err)
|
||||
// Build the list of pull names
|
||||
names = append(names, bbNames...)
|
||||
names = append(names, fedoraNames...)
|
||||
var writer io.Writer
|
||||
writer = os.Stdout
|
||||
|
||||
// Iterate over the names and delete the image
|
||||
// after the pull
|
||||
for _, img := range names {
|
||||
_, err := runtime.GetImage(img)
|
||||
if err == nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
newImage, err := New(img, runtime)
|
||||
newImage, err := ir.New(img, "", "", writer, nil, SigningOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, newImage.ID(), "")
|
||||
err = newImage.Remove(false)
|
||||
@ -180,5 +134,5 @@ func TestImage_New(t *testing.T) {
|
||||
}
|
||||
|
||||
// Shutdown the runtime and remove the temporary storage
|
||||
cleanup(runtime, workdir)
|
||||
cleanup(workdir, ir)
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ func decompose(input string) (imageParts, error) {
|
||||
name: imageName,
|
||||
tag: tag,
|
||||
isTagged: isTagged,
|
||||
transport: DefaultTransport,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -54,3 +55,8 @@ func decompose(input string) (imageParts, error) {
|
||||
func (ip *imageParts) assemble() string {
|
||||
return fmt.Sprintf("%s/%s:%s", ip.registry, ip.name, ip.tag)
|
||||
}
|
||||
|
||||
// assemble concatenates an image's parts with transport into a string
|
||||
func (ip *imageParts) assembleWithTransport() string {
|
||||
return fmt.Sprintf("%s%s/%s:%s", ip.transport, ip.registry, ip.name, ip.tag)
|
||||
}
|
||||
|
246
libpod/image/pull.go
Normal file
246
libpod/image/pull.go
Normal file
@ -0,0 +1,246 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
cp "github.com/containers/image/copy"
|
||||
"github.com/containers/image/directory"
|
||||
"github.com/containers/image/docker"
|
||||
dockerarchive "github.com/containers/image/docker/archive"
|
||||
"github.com/containers/image/docker/tarfile"
|
||||
ociarchive "github.com/containers/image/oci/archive"
|
||||
"github.com/containers/image/pkg/sysregistries"
|
||||
is "github.com/containers/image/storage"
|
||||
"github.com/containers/image/tarball"
|
||||
"github.com/containers/image/transports/alltransports"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// DockerArchive is the transport we prepend to an image name
|
||||
// when saving to docker-archive
|
||||
DockerArchive = dockerarchive.Transport.Name()
|
||||
// OCIArchive is the transport we prepend to an image name
|
||||
// when saving to oci-archive
|
||||
OCIArchive = ociarchive.Transport.Name()
|
||||
// DirTransport is the transport for pushing and pulling
|
||||
// images to and from a directory
|
||||
DirTransport = directory.Transport.Name()
|
||||
// TransportNames are the supported transports in string form
|
||||
TransportNames = [...]string{DefaultTransport, DockerArchive, OCIArchive, "ostree:", "dir:"}
|
||||
// TarballTransport is the transport for importing a tar archive
|
||||
// and creating a filesystem image
|
||||
TarballTransport = tarball.Transport.Name()
|
||||
// DockerTransport is the transport for docker registries
|
||||
DockerTransport = docker.Transport.Name() + "://"
|
||||
// AtomicTransport is the transport for atomic registries
|
||||
AtomicTransport = "atomic"
|
||||
// DefaultTransport is a prefix that we apply to an image name
|
||||
DefaultTransport = DockerTransport
|
||||
)
|
||||
|
||||
type pullStruct struct {
|
||||
image string
|
||||
srcRef types.ImageReference
|
||||
dstRef types.ImageReference
|
||||
}
|
||||
|
||||
func (ir *Runtime) getPullStruct(srcRef types.ImageReference, destName string) (*pullStruct, error) {
|
||||
reference := destName
|
||||
if srcRef.DockerReference() != nil {
|
||||
reference = srcRef.DockerReference().String()
|
||||
}
|
||||
destRef, err := is.Transport.ParseStoreReference(ir.store, reference)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("error parsing dest reference name: %v", err)
|
||||
}
|
||||
return &pullStruct{
|
||||
image: destName,
|
||||
srcRef: srcRef,
|
||||
dstRef: destRef,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// returns a list of pullStruct with the srcRef and DstRef based on the transport being used
|
||||
func (ir *Runtime) getPullListFromRef(srcRef types.ImageReference, imgName string, sc *types.SystemContext) ([]*pullStruct, error) {
|
||||
var pullStructs []*pullStruct
|
||||
splitArr := strings.Split(imgName, ":")
|
||||
archFile := splitArr[len(splitArr)-1]
|
||||
|
||||
// supports pulling from docker-archive, oci, and registries
|
||||
if srcRef.Transport().Name() == DockerArchive {
|
||||
tarSource, err := tarfile.NewSourceFromFile(archFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
manifest, err := tarSource.LoadTarManifest()
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("error retrieving manifest.json: %v", err)
|
||||
}
|
||||
// to pull the first image stored in the tar file
|
||||
if len(manifest) == 0 {
|
||||
// use the hex of the digest if no manifest is found
|
||||
reference, err := getImageDigest(srcRef, sc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pullInfo, err := ir.getPullStruct(srcRef, reference)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pullStructs = append(pullStructs, pullInfo)
|
||||
} else {
|
||||
var dest string
|
||||
if len(manifest[0].RepoTags) > 0 {
|
||||
dest = manifest[0].RepoTags[0]
|
||||
} else {
|
||||
// If the input image has no repotags, we need to feed it a dest anyways
|
||||
dest, err = getImageDigest(srcRef, sc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
pullInfo, err := ir.getPullStruct(srcRef, dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pullStructs = append(pullStructs, pullInfo)
|
||||
}
|
||||
} else if srcRef.Transport().Name() == OCIArchive {
|
||||
// retrieve the manifest from index.json to access the image name
|
||||
manifest, err := ociarchive.LoadManifestDescriptor(srcRef)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error loading manifest for %q", srcRef)
|
||||
}
|
||||
|
||||
if manifest.Annotations == nil || manifest.Annotations["org.opencontainers.image.ref.name"] == "" {
|
||||
return nil, errors.Errorf("error, archive doesn't have a name annotation. Cannot store image with no name")
|
||||
}
|
||||
pullInfo, err := ir.getPullStruct(srcRef, manifest.Annotations["org.opencontainers.image.ref.name"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pullStructs = append(pullStructs, pullInfo)
|
||||
} else if srcRef.Transport().Name() == DirTransport {
|
||||
// supports pull from a directory
|
||||
image := splitArr[1]
|
||||
// remove leading "/"
|
||||
if image[:1] == "/" {
|
||||
image = image[1:]
|
||||
}
|
||||
pullInfo, err := ir.getPullStruct(srcRef, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pullStructs = append(pullStructs, pullInfo)
|
||||
} else {
|
||||
pullInfo, err := ir.getPullStruct(srcRef, imgName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pullStructs = append(pullStructs, pullInfo)
|
||||
}
|
||||
return pullStructs, nil
|
||||
}
|
||||
|
||||
// pullImage pulls an image from configured registries
|
||||
// By default, only the latest tag (or a specific tag if requested) will be
|
||||
// pulled.
|
||||
func (i *Image) pullImage(writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions) (string, error) {
|
||||
// pullImage copies the image from the source to the destination
|
||||
var pullStructs []*pullStruct
|
||||
sc := GetSystemContext(signaturePolicyPath, authfile, false)
|
||||
srcRef, err := alltransports.ParseImageName(i.InputName)
|
||||
if err != nil {
|
||||
// could be trying to pull from registry with short name
|
||||
pullStructs, err = i.createNamesToPull()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "error getting default registries to try")
|
||||
}
|
||||
} else {
|
||||
pullStructs, err = i.imageruntime.getPullListFromRef(srcRef, i.InputName, sc)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error getting pullStruct info to pull image %q", i.InputName)
|
||||
}
|
||||
}
|
||||
policyContext, err := getPolicyContext(sc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer policyContext.Destroy()
|
||||
|
||||
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 err = cp.Image(policyContext, imageInfo.dstRef, imageInfo.srcRef, copyOptions); err != nil {
|
||||
if writer != nil {
|
||||
io.WriteString(writer, "Failed\n")
|
||||
}
|
||||
} else {
|
||||
return imageInfo.image, nil
|
||||
}
|
||||
}
|
||||
return "", errors.Wrapf(err, "error pulling image from")
|
||||
}
|
||||
|
||||
// createNamesToPull looks at a decomposed image and determines the possible
|
||||
// images names to try pulling in combination with the registries.conf file as well
|
||||
func (i *Image) createNamesToPull() ([]*pullStruct, error) {
|
||||
var pullNames []*pullStruct
|
||||
decomposedImage, err := decompose(i.InputName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if decomposedImage.hasRegistry {
|
||||
srcRef, err := alltransports.ParseImageName(decomposedImage.assembleWithTransport())
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("unable to parse '%s'", i.InputName)
|
||||
}
|
||||
ps := pullStruct{
|
||||
image: i.InputName,
|
||||
srcRef: srcRef,
|
||||
}
|
||||
pullNames = append(pullNames, &ps)
|
||||
|
||||
} else {
|
||||
registryConfigPath := ""
|
||||
envOverride := os.Getenv("REGISTRIES_CONFIG_PATH")
|
||||
if len(envOverride) > 0 {
|
||||
registryConfigPath = envOverride
|
||||
}
|
||||
searchRegistries, err := sysregistries.GetRegistries(&types.SystemContext{SystemRegistriesConfPath: registryConfigPath})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, registry := range searchRegistries {
|
||||
decomposedImage.registry = registry
|
||||
srcRef, err := alltransports.ParseImageName(decomposedImage.assembleWithTransport())
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("unable to parse '%s'", i.InputName)
|
||||
}
|
||||
ps := pullStruct{
|
||||
image: decomposedImage.assemble(),
|
||||
srcRef: srcRef,
|
||||
}
|
||||
pullNames = append(pullNames, &ps)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pStruct := range pullNames {
|
||||
destRef, err := is.Transport.ParseStoreReference(i.imageruntime.store, pStruct.image)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("error parsing dest reference name: %v", err)
|
||||
}
|
||||
pStruct.dstRef = destRef
|
||||
}
|
||||
|
||||
return pullNames, nil
|
||||
}
|
10
libpod/image/signing_options.go
Normal file
10
libpod/image/signing_options.go
Normal file
@ -0,0 +1,10 @@
|
||||
package image
|
||||
|
||||
// SigningOptions encapsulates settings that control whether or not we strip or
|
||||
// add signatures to images when writing them.
|
||||
type SigningOptions struct {
|
||||
// RemoveSignatures directs us to remove any signatures which are already present.
|
||||
RemoveSignatures bool
|
||||
// SignBy is a key identifier of some kind, indicating that a signature should be generated using the specified private key and stored with the image.
|
||||
SignBy string
|
||||
}
|
@ -1,9 +1,16 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
func getTags(nameInput string) (reference.NamedTagged, bool, error) {
|
||||
@ -18,17 +25,17 @@ func getTags(nameInput string) (reference.NamedTagged, bool, error) {
|
||||
|
||||
// findImageInRepotags takes an imageParts struct and searches images' repotags for
|
||||
// a match on name:tag
|
||||
func findImageInRepotags(search imageParts, images []*storage.Image) (*storage.Image, error) {
|
||||
func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, error) {
|
||||
var results []*storage.Image
|
||||
for _, image := range images {
|
||||
for _, name := range image.Names {
|
||||
for _, name := range image.Names() {
|
||||
d, err := decompose(name)
|
||||
// if we get an error, ignore and keep going
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if d.name == search.name && d.tag == search.tag {
|
||||
results = append(results, image)
|
||||
results = append(results, image.image)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -40,3 +47,42 @@ func findImageInRepotags(search imageParts, images []*storage.Image) (*storage.I
|
||||
}
|
||||
return results[0], nil
|
||||
}
|
||||
|
||||
// getCopyOptions constructs a new containers/image/copy.Options{} struct from the given parameters
|
||||
func getCopyOptions(reportWriter io.Writer, signaturePolicyPath string, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions, authFile, manifestType string, forceCompress bool) *cp.Options {
|
||||
if srcDockerRegistry == nil {
|
||||
srcDockerRegistry = &DockerRegistryOptions{}
|
||||
}
|
||||
if destDockerRegistry == nil {
|
||||
destDockerRegistry = &DockerRegistryOptions{}
|
||||
}
|
||||
srcContext := srcDockerRegistry.GetSystemContext(signaturePolicyPath, authFile, forceCompress)
|
||||
destContext := destDockerRegistry.GetSystemContext(signaturePolicyPath, authFile, forceCompress)
|
||||
return &cp.Options{
|
||||
RemoveSignatures: signing.RemoveSignatures,
|
||||
SignBy: signing.SignBy,
|
||||
ReportWriter: reportWriter,
|
||||
SourceCtx: srcContext,
|
||||
DestinationCtx: destContext,
|
||||
ForceManifestMIMEType: manifestType,
|
||||
}
|
||||
}
|
||||
|
||||
// getPolicyContext sets up, intializes and returns a new context for the specified policy
|
||||
func getPolicyContext(ctx *types.SystemContext) (*signature.PolicyContext, error) {
|
||||
policy, err := signature.DefaultPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
policyContext, err := signature.NewPolicyContext(policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return policyContext, nil
|
||||
}
|
||||
|
||||
// hasTransport determines if the image string contains '://', returns bool
|
||||
func hasTransport(image string) bool {
|
||||
return strings.Contains(image, "://")
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
"github.com/projectatomic/libpod/libpod/common"
|
||||
"github.com/projectatomic/libpod/libpod/driver"
|
||||
"github.com/projectatomic/libpod/pkg/inspect"
|
||||
"github.com/projectatomic/libpod/pkg/util"
|
||||
)
|
||||
|
||||
// Runtime API
|
||||
@ -312,7 +313,7 @@ func (k *Image) Decompose() error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if StringInSlice(k.Registry, registries) {
|
||||
if util.StringInSlice(k.Registry, registries) {
|
||||
return nil
|
||||
}
|
||||
// We need to check if the registry name is legit
|
||||
|
@ -39,16 +39,6 @@ func WriteFile(content string, path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// StringInSlice determines if a string is in a string slice, returns bool
|
||||
func StringInSlice(s string, sl []string) bool {
|
||||
for _, i := range sl {
|
||||
if i == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FuncTimer helps measure the execution time of a function
|
||||
// For debug purposes, do not leave in code
|
||||
// used like defer FuncTimer("foo")
|
||||
|
@ -5,19 +5,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
sliceData = []string{"one", "two", "three", "four"}
|
||||
)
|
||||
|
||||
func TestStringInSlice(t *testing.T) {
|
||||
// string is in the slice
|
||||
assert.True(t, StringInSlice("one", sliceData))
|
||||
// string is not in the slice
|
||||
assert.False(t, StringInSlice("five", sliceData))
|
||||
// string is not in empty slice
|
||||
assert.False(t, StringInSlice("one", []string{}))
|
||||
}
|
||||
|
||||
func TestRemoveScientificNotationFromFloat(t *testing.T) {
|
||||
numbers := []float64{0.0, .5, 1.99999932, 1.04e+10}
|
||||
results := []float64{0.0, .5, 1.99999932, 1.04}
|
||||
|
@ -44,3 +44,13 @@ func ParseRegistryCreds(creds string) (*types.DockerAuthConfig, error) {
|
||||
Password: password,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// StringInSlice determines if a string is in a string slice, returns bool
|
||||
func StringInSlice(s string, sl []string) bool {
|
||||
for _, i := range sl {
|
||||
if i == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
19
pkg/util/utils_test.go
Normal file
19
pkg/util/utils_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
sliceData = []string{"one", "two", "three", "four"}
|
||||
)
|
||||
|
||||
func TestStringInSlice(t *testing.T) {
|
||||
// string is in the slice
|
||||
assert.True(t, StringInSlice("one", sliceData))
|
||||
// string is not in the slice
|
||||
assert.False(t, StringInSlice("five", sliceData))
|
||||
// string is not in empty slice
|
||||
assert.False(t, StringInSlice("one", []string{}))
|
||||
}
|
Reference in New Issue
Block a user