restore push/pull and util API
Restore the push and pull API that commit dcd2a92e56
removed.
These changes would break vendoring into openshift/builder due
to build errors.
For the same reason, restore `util.FindImage` and `util.AddImageNames`
but deprecate the `findRegistry` argument.
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
parent
eb52311c23
commit
fb331c1861
|
@ -235,7 +235,7 @@ func manifestCreateCmd(c *cobra.Command, args []string, opts manifestCreateOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
|
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
|
||||||
// check if the local image exists
|
// check if the local image exists
|
||||||
if ref, _, err = util.FindImage(store, systemContext, imageSpec); err != nil {
|
if ref, _, err = util.FindImage(store, "", systemContext, imageSpec); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,7 +285,7 @@ func manifestAddCmd(c *cobra.Command, args []string, opts manifestAddOpts) error
|
||||||
return errors.Wrapf(err, "error building system context")
|
return errors.Wrapf(err, "error building system context")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, listImage, err := util.FindImage(store, systemContext, listImageSpec)
|
_, listImage, err := util.FindImage(store, "", systemContext, listImageSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -294,7 +294,7 @@ func manifestAddCmd(c *cobra.Command, args []string, opts manifestAddOpts) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
|
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
|
||||||
// check if the local image exists
|
// check if the local image exists
|
||||||
if ref, _, err = util.FindImage(store, systemContext, imageSpec); err != nil {
|
if ref, _, err = util.FindImage(store, "", systemContext, imageSpec); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -309,7 +309,7 @@ func manifestAddCmd(c *cobra.Command, args []string, opts manifestAddOpts) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var storeErr error
|
var storeErr error
|
||||||
// check if the local image exists
|
// check if the local image exists
|
||||||
if ref, _, storeErr = util.FindImage(store, systemContext, imageSpec); storeErr != nil {
|
if ref, _, storeErr = util.FindImage(store, "", systemContext, imageSpec); storeErr != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
digest, storeErr = list.Add(getContext(), systemContext, ref, opts.all)
|
digest, storeErr = list.Add(getContext(), systemContext, ref, opts.all)
|
||||||
|
@ -404,7 +404,7 @@ func manifestRemoveCmd(c *cobra.Command, args []string, opts manifestRemoveOpts)
|
||||||
return errors.Wrapf(err, "error building system context")
|
return errors.Wrapf(err, "error building system context")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, listImage, err := util.FindImage(store, systemContext, listImageSpec)
|
_, listImage, err := util.FindImage(store, "", systemContext, listImageSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -461,7 +461,7 @@ func manifestAnnotateCmd(c *cobra.Command, args []string, opts manifestAnnotateO
|
||||||
return errors.Wrapf(err, "error building system context")
|
return errors.Wrapf(err, "error building system context")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, listImage, err := util.FindImage(store, systemContext, listImageSpec)
|
_, listImage, err := util.FindImage(store, "", systemContext, listImageSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -474,7 +474,7 @@ func manifestAnnotateCmd(c *cobra.Command, args []string, opts manifestAnnotateO
|
||||||
digest, err := digest.Parse(imageSpec)
|
digest, err := digest.Parse(imageSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx := getContext()
|
ctx := getContext()
|
||||||
ref, _, err := util.FindImage(store, systemContext, imageSpec)
|
ref, _, err := util.FindImage(store, "", systemContext, imageSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -581,7 +581,7 @@ func manifestInspect(ctx context.Context, store storage.Store, systemContext *ty
|
||||||
logrus.Debugf("error parsing reference to image %q: %v", imageSpec, err)
|
logrus.Debugf("error parsing reference to image %q: %v", imageSpec, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ref, _, err := util.FindImage(store, systemContext, imageSpec); err == nil {
|
if ref, _, err := util.FindImage(store, "", systemContext, imageSpec); err == nil {
|
||||||
refs = append(refs, ref)
|
refs = append(refs, ref)
|
||||||
} else if ref, err := alltransports.ParseImageName(imageSpec); err == nil {
|
} else if ref, err := alltransports.ParseImageName(imageSpec); err == nil {
|
||||||
refs = append(refs, ref)
|
refs = append(refs, ref)
|
||||||
|
@ -679,7 +679,7 @@ func manifestPushCmd(c *cobra.Command, args []string, opts pushOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func manifestPush(systemContext *types.SystemContext, store storage.Store, listImageSpec, destSpec string, opts pushOptions) error {
|
func manifestPush(systemContext *types.SystemContext, store storage.Store, listImageSpec, destSpec string, opts pushOptions) error {
|
||||||
_, listImage, err := util.FindImage(store, systemContext, listImageSpec)
|
_, listImage, err := util.FindImage(store, "", systemContext, listImageSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,31 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/containers/buildah/pkg/blobcache"
|
"github.com/containers/buildah"
|
||||||
|
"github.com/containers/buildah/define"
|
||||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||||
"github.com/containers/buildah/pkg/parse"
|
"github.com/containers/buildah/pkg/parse"
|
||||||
"github.com/containers/common/libimage"
|
|
||||||
libimageTypes "github.com/containers/common/libimage/types"
|
|
||||||
"github.com/containers/common/pkg/auth"
|
"github.com/containers/common/pkg/auth"
|
||||||
"github.com/containers/image/v5/types"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pullOptions struct {
|
type pullOptions struct {
|
||||||
// We can feed many flags directly to the options of libmage.
|
allTags bool
|
||||||
libimage.PullOptions
|
authfile string
|
||||||
|
blobCache string
|
||||||
// Other flags need some massaging and validation.
|
certDir string
|
||||||
blobCache string
|
creds string
|
||||||
pullPolicy string
|
signaturePolicy string
|
||||||
decryptionKeys []string
|
quiet bool
|
||||||
tlsVerify bool
|
removeSignatures bool
|
||||||
quiet bool
|
tlsVerify bool
|
||||||
|
decryptionKeys []string
|
||||||
|
pullPolicy string
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -43,7 +42,7 @@ func init() {
|
||||||
Short: "Pull an image from the specified location",
|
Short: "Pull an image from the specified location",
|
||||||
Long: pullDescription,
|
Long: pullDescription,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return pullCmd(cmd, args, &opts)
|
return pullCmd(cmd, args, opts)
|
||||||
},
|
},
|
||||||
Example: `buildah pull imagename
|
Example: `buildah pull imagename
|
||||||
buildah pull docker-daemon:imagename:imagetag
|
buildah pull docker-daemon:imagename:imagetag
|
||||||
|
@ -53,22 +52,22 @@ func init() {
|
||||||
|
|
||||||
flags := pullCommand.Flags()
|
flags := pullCommand.Flags()
|
||||||
flags.SetInterspersed(false)
|
flags.SetInterspersed(false)
|
||||||
flags.BoolVarP(&opts.AllTags, "all-tags", "a", false, "download all tagged images in the repository")
|
flags.BoolVarP(&opts.allTags, "all-tags", "a", false, "download all tagged images in the repository")
|
||||||
flags.StringVar(&opts.AuthFilePath, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
|
flags.StringVar(&opts.authfile, "authfile", auth.GetDefaultAuthFile(), "path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
|
||||||
flags.StringVar(&opts.blobCache, "blob-cache", "", "store copies of pulled image blobs in the specified directory")
|
flags.StringVar(&opts.blobCache, "blob-cache", "", "store copies of pulled image blobs in the specified directory")
|
||||||
flags.StringVar(&opts.CertDirPath, "cert-dir", "", "use certificates at the specified path to access the registry")
|
flags.StringVar(&opts.certDir, "cert-dir", "", "use certificates at the specified path to access the registry")
|
||||||
flags.StringVar(&opts.Credentials, "creds", "", "use `[username[:password]]` for accessing the registry")
|
flags.StringVar(&opts.creds, "creds", "", "use `[username[:password]]` for accessing the registry")
|
||||||
flags.StringVar(&opts.pullPolicy, "policy", "missing", "missing, always, or never.")
|
flags.StringVar(&opts.pullPolicy, "policy", "missing", "missing, always, or never.")
|
||||||
flags.BoolVarP(&opts.RemoveSignatures, "remove-signatures", "", false, "don't copy signatures when pulling image")
|
flags.BoolVarP(&opts.removeSignatures, "remove-signatures", "", false, "don't copy signatures when pulling image")
|
||||||
flags.StringVar(&opts.SignaturePolicyPath, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
|
flags.StringVar(&opts.signaturePolicy, "signature-policy", "", "`pathname` of signature policy file (not usually used)")
|
||||||
flags.StringSliceVar(&opts.decryptionKeys, "decryption-key", nil, "key needed to decrypt the image")
|
flags.StringSliceVar(&opts.decryptionKeys, "decryption-key", nil, "key needed to decrypt the image")
|
||||||
if err := flags.MarkHidden("signature-policy"); err != nil {
|
if err := flags.MarkHidden("signature-policy"); err != nil {
|
||||||
panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err))
|
panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err))
|
||||||
}
|
}
|
||||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "don't output progress information when pulling images")
|
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "don't output progress information when pulling images")
|
||||||
flags.StringVar(&opts.OS, "os", runtime.GOOS, "prefer `OS` instead of the running OS for choosing images")
|
flags.String("os", runtime.GOOS, "prefer `OS` instead of the running OS for choosing images")
|
||||||
flags.StringVar(&opts.Architecture, "arch", runtime.GOARCH, "prefer `ARCH` instead of the architecture of the machine for choosing images")
|
flags.String("arch", runtime.GOARCH, "prefer `ARCH` instead of the architecture of the machine for choosing images")
|
||||||
flags.StringVar(&opts.Variant, "variant", "", "override the `variant` of the specified image")
|
flags.String("variant", "", "override the `variant` of the specified image")
|
||||||
flags.BoolVar(&opts.tlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
flags.BoolVar(&opts.tlsVerify, "tls-verify", true, "require HTTPS and verify certificates when accessing the registry. TLS verification cannot be used when talking to an insecure registry.")
|
||||||
if err := flags.MarkHidden("blob-cache"); err != nil {
|
if err := flags.MarkHidden("blob-cache"); err != nil {
|
||||||
panic(fmt.Sprintf("error marking blob-cache as hidden: %v", err))
|
panic(fmt.Sprintf("error marking blob-cache as hidden: %v", err))
|
||||||
|
@ -77,8 +76,7 @@ func init() {
|
||||||
rootCmd.AddCommand(pullCommand)
|
rootCmd.AddCommand(pullCommand)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pullCmd(c *cobra.Command, args []string, options *pullOptions) error {
|
func pullCmd(c *cobra.Command, args []string, iopts pullOptions) error {
|
||||||
var err error
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return errors.Errorf("an image name must be specified")
|
return errors.Errorf("an image name must be specified")
|
||||||
}
|
}
|
||||||
|
@ -88,27 +86,7 @@ func pullCmd(c *cobra.Command, args []string, options *pullOptions) error {
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
return errors.Errorf("too many arguments specified")
|
return errors.Errorf("too many arguments specified")
|
||||||
}
|
}
|
||||||
if err := auth.CheckAuthFile(options.AuthFilePath); err != nil {
|
if err := auth.CheckAuthFile(iopts.authfile); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
options.OciDecryptConfig, err = getDecryptConfig(options.decryptionKeys)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to obtain decrypt config")
|
|
||||||
}
|
|
||||||
|
|
||||||
options.Writer = os.Stderr
|
|
||||||
if options.quiet {
|
|
||||||
options.Writer = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.blobCache != "" {
|
|
||||||
// options.SourceLookupReferenceFunc = blobcache.CacheLookupReferenceFunc(options.blobCache, types.PreserveOriginal)
|
|
||||||
options.DestinationLookupReferenceFunc = blobcache.CacheLookupReferenceFunc(options.blobCache, types.PreserveOriginal)
|
|
||||||
}
|
|
||||||
|
|
||||||
pullPolicy, err := libimageTypes.ParsePullPolicy(options.pullPolicy)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,19 +100,37 @@ func pullCmd(c *cobra.Command, args []string, options *pullOptions) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
decConfig, err := getDecryptConfig(iopts.decryptionKeys)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "unable to obtain decrypt config")
|
||||||
|
}
|
||||||
|
|
||||||
|
policy, ok := define.PolicyMap[iopts.pullPolicy]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unsupported pull policy %q", iopts.pullPolicy)
|
||||||
|
}
|
||||||
|
options := buildah.PullOptions{
|
||||||
|
SignaturePolicyPath: iopts.signaturePolicy,
|
||||||
|
Store: store,
|
||||||
|
SystemContext: systemContext,
|
||||||
|
BlobDirectory: iopts.blobCache,
|
||||||
|
AllTags: iopts.allTags,
|
||||||
|
ReportWriter: os.Stderr,
|
||||||
|
RemoveSignatures: iopts.removeSignatures,
|
||||||
|
MaxRetries: maxPullPushRetries,
|
||||||
|
RetryDelay: pullPushRetryDelay,
|
||||||
|
OciDecryptConfig: decConfig,
|
||||||
|
PullPolicy: policy,
|
||||||
|
}
|
||||||
|
|
||||||
|
if iopts.quiet {
|
||||||
|
options.ReportWriter = nil // Turns off logging output
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := buildah.Pull(getContext(), args[0], options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
fmt.Printf("%s\n", id)
|
||||||
pulledImages, err := runtime.Pull(context.Background(), args[0], pullPolicy, &options.PullOptions)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, pulledImage := range pulledImages {
|
|
||||||
fmt.Printf("%s\n", pulledImage.ID())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/buildah/pkg/blobcache"
|
"github.com/containers/buildah"
|
||||||
|
"github.com/containers/buildah/define"
|
||||||
buildahcli "github.com/containers/buildah/pkg/cli"
|
buildahcli "github.com/containers/buildah/pkg/cli"
|
||||||
"github.com/containers/buildah/pkg/parse"
|
"github.com/containers/buildah/pkg/parse"
|
||||||
"github.com/containers/common/libimage"
|
"github.com/containers/buildah/util"
|
||||||
"github.com/containers/common/pkg/auth"
|
"github.com/containers/common/pkg/auth"
|
||||||
"github.com/containers/image/v5/manifest"
|
"github.com/containers/image/v5/manifest"
|
||||||
"github.com/containers/image/v5/transports"
|
"github.com/containers/image/v5/transports"
|
||||||
"github.com/containers/image/v5/types"
|
"github.com/containers/image/v5/transports/alltransports"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -41,55 +41,6 @@ type pushOptions struct {
|
||||||
encryptLayers []int
|
encryptLayers []int
|
||||||
}
|
}
|
||||||
|
|
||||||
// translates the pushOptions into libimage.PushOptions.
|
|
||||||
func (iopts *pushOptions) toLibimagePushOptions() (*libimage.PushOptions, error) {
|
|
||||||
pushOptions := &libimage.PushOptions{}
|
|
||||||
pushOptions.PolicyAllowStorage = true
|
|
||||||
pushOptions.AuthFilePath = iopts.authfile
|
|
||||||
pushOptions.CertDirPath = iopts.certDir
|
|
||||||
pushOptions.Credentials = iopts.creds
|
|
||||||
pushOptions.RemoveSignatures = iopts.removeSignatures
|
|
||||||
pushOptions.SignaturePolicyPath = iopts.signaturePolicy
|
|
||||||
pushOptions.SignBy = iopts.signBy
|
|
||||||
|
|
||||||
if iopts.blobCache != "" {
|
|
||||||
compress := types.Compress
|
|
||||||
if iopts.disableCompression {
|
|
||||||
compress = types.PreserveOriginal
|
|
||||||
}
|
|
||||||
pushOptions.SourceLookupReferenceFunc = blobcache.CacheLookupReferenceFunc(iopts.blobCache, compress)
|
|
||||||
}
|
|
||||||
|
|
||||||
var manifestType string
|
|
||||||
if iopts.format != "" {
|
|
||||||
switch iopts.format {
|
|
||||||
case "oci":
|
|
||||||
manifestType = imgspecv1.MediaTypeImageManifest
|
|
||||||
case "v2s1":
|
|
||||||
manifestType = manifest.DockerV2Schema1SignedMediaType
|
|
||||||
case "v2s2", "docker":
|
|
||||||
manifestType = manifest.DockerV2Schema2MediaType
|
|
||||||
default:
|
|
||||||
return nil, errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", iopts.format)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pushOptions.ManifestMIMEType = manifestType
|
|
||||||
|
|
||||||
encConfig, encLayers, err := getEncryptConfig(iopts.encryptionKeys, iopts.encryptLayers)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "unable to obtain encryption config")
|
|
||||||
}
|
|
||||||
pushOptions.OciEncryptConfig = encConfig
|
|
||||||
pushOptions.OciEncryptLayers = encLayers
|
|
||||||
pushOptions.InsecureSkipTLSVerify = types.NewOptionalBool(!iopts.tlsVerify)
|
|
||||||
|
|
||||||
if !iopts.quiet {
|
|
||||||
pushOptions.Writer = os.Stderr
|
|
||||||
}
|
|
||||||
|
|
||||||
return pushOptions, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var (
|
var (
|
||||||
opts pushOptions
|
opts pushOptions
|
||||||
|
@ -174,52 +125,108 @@ func pushCmd(c *cobra.Command, args []string, iopts pushOptions) error {
|
||||||
return errors.New("Only two arguments are necessary to push: source and destination")
|
return errors.New("Only two arguments are necessary to push: source and destination")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compress := define.Gzip
|
||||||
|
if iopts.disableCompression {
|
||||||
|
compress = define.Uncompressed
|
||||||
|
}
|
||||||
|
|
||||||
store, err := getStore(c)
|
store, err := getStore(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dest, err := alltransports.ParseImageName(destSpec)
|
||||||
|
// add the docker:// transport to see if they neglected it.
|
||||||
|
if err != nil {
|
||||||
|
destTransport := strings.Split(destSpec, ":")[0]
|
||||||
|
if t := transports.Get(destTransport); t != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(destSpec, "://") {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
destSpec = "docker://" + destSpec
|
||||||
|
dest2, err2 := alltransports.ParseImageName(destSpec)
|
||||||
|
if err2 != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dest = dest2
|
||||||
|
logrus.Debugf("Assuming docker:// as the transport method for DESTINATION: %s", destSpec)
|
||||||
|
}
|
||||||
|
|
||||||
systemContext, err := parse.SystemContextFromOptions(c)
|
systemContext, err := parse.SystemContextFromOptions(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error building system context")
|
return errors.Wrapf(err, "error building system context")
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
var manifestType string
|
||||||
if err != nil {
|
if iopts.format != "" {
|
||||||
return err
|
switch iopts.format {
|
||||||
|
case "oci":
|
||||||
|
manifestType = imgspecv1.MediaTypeImageManifest
|
||||||
|
case "v2s1":
|
||||||
|
manifestType = manifest.DockerV2Schema1SignedMediaType
|
||||||
|
case "v2s2", "docker":
|
||||||
|
manifestType = manifest.DockerV2Schema2MediaType
|
||||||
|
default:
|
||||||
|
return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci', 'v2s1', or 'v2s2'", iopts.format)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pushOptions, err := iopts.toLibimagePushOptions()
|
encConfig, encLayers, err := getEncryptConfig(iopts.encryptionKeys, iopts.encryptLayers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrapf(err, "unable to obtain encryption config")
|
||||||
}
|
}
|
||||||
|
|
||||||
pushedManifestBytes, pushError := runtime.Push(context.Background(), src, destSpec, pushOptions)
|
options := buildah.PushOptions{
|
||||||
if pushError != nil {
|
Compression: compress,
|
||||||
// TODO: maybe we find a way to handle that transparently in libimage?
|
ManifestType: manifestType,
|
||||||
if errors.Cause(pushError) != storage.ErrImageUnknown {
|
SignaturePolicyPath: iopts.signaturePolicy,
|
||||||
|
Store: store,
|
||||||
|
SystemContext: systemContext,
|
||||||
|
BlobDirectory: iopts.blobCache,
|
||||||
|
RemoveSignatures: iopts.removeSignatures,
|
||||||
|
SignBy: iopts.signBy,
|
||||||
|
MaxRetries: maxPullPushRetries,
|
||||||
|
RetryDelay: pullPushRetryDelay,
|
||||||
|
OciEncryptConfig: encConfig,
|
||||||
|
OciEncryptLayers: encLayers,
|
||||||
|
}
|
||||||
|
if !iopts.quiet {
|
||||||
|
options.ReportWriter = os.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
ref, digest, err := buildah.Push(getContext(), src, dest, options)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Cause(err) != storage.ErrImageUnknown {
|
||||||
// Image might be a manifest so attempt a manifest push
|
// Image might be a manifest so attempt a manifest push
|
||||||
if manifestsErr := manifestPush(systemContext, store, src, destSpec, iopts); manifestsErr == nil {
|
if manifestsErr := manifestPush(systemContext, store, src, destSpec, iopts); manifestsErr == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pushError
|
return util.GetFailureCause(err, errors.Wrapf(err, "error pushing image %q to %q", src, destSpec))
|
||||||
|
}
|
||||||
|
if ref != nil {
|
||||||
|
logrus.Debugf("pushed image %q with digest %s", ref, digest.String())
|
||||||
|
} else {
|
||||||
|
logrus.Debugf("pushed image with digest %s", digest.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if iopts.digestfile != "" {
|
logrus.Debugf("Successfully pushed %s with digest %s", transports.ImageName(dest), digest.String())
|
||||||
manifestDigest, err := manifest.Digest(pushedManifestBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(iopts.digestfile, []byte(manifestDigest.String()), 0644); err != nil {
|
if iopts.digestfile != "" {
|
||||||
return err
|
if err = ioutil.WriteFile(iopts.digestfile, []byte(digest.String()), 0644); err != nil {
|
||||||
|
return util.GetFailureCause(err, errors.Wrapf(err, "failed to write digest to file %q", iopts.digestfile))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getListOfTransports gets the transports supported from the image library
|
||||||
|
// and strips of the "tarball" transport from the string of transports returned
|
||||||
func getListOfTransports() string {
|
func getListOfTransports() string {
|
||||||
allTransports := strings.Join(transports.ListNames(), ",")
|
allTransports := strings.Join(transports.ListNames(), ",")
|
||||||
return strings.Replace(allTransports, ",tarball", "", 1)
|
return strings.Replace(allTransports, ",tarball", "", 1)
|
||||||
|
|
|
@ -174,7 +174,7 @@ func (b *Builder) addManifest(ctx context.Context, manifestName string, imageSpe
|
||||||
var create bool
|
var create bool
|
||||||
systemContext := &types.SystemContext{}
|
systemContext := &types.SystemContext{}
|
||||||
var list manifests.List
|
var list manifests.List
|
||||||
_, listImage, err := util.FindImage(b.store, systemContext, manifestName)
|
_, listImage, err := util.FindImage(b.store, "", systemContext, manifestName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
create = true
|
create = true
|
||||||
list = manifests.Create()
|
list = manifests.Create()
|
||||||
|
@ -194,7 +194,7 @@ func (b *Builder) addManifest(ctx context.Context, manifestName string, imageSpe
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
|
if ref, err = alltransports.ParseImageName(util.DefaultTransport + imageSpec); err != nil {
|
||||||
// check if the local image exists
|
// check if the local image exists
|
||||||
if ref, _, err = util.FindImage(b.store, systemContext, imageSpec); err != nil {
|
if ref, _, err = util.FindImage(b.store, "", systemContext, imageSpec); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,7 +355,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return imgID, nil, "", errors.Wrapf(err, "error locating just-written image %q", transports.ImageName(dest))
|
return imgID, nil, "", errors.Wrapf(err, "error locating just-written image %q", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
if err = util.TagImage(b.store, systemContext, img, options.AdditionalTags); err != nil {
|
if err = util.AddImageNames(b.store, "", systemContext, img, options.AdditionalTags); err != nil {
|
||||||
return imgID, nil, "", errors.Wrapf(err, "error setting image names to %v", append(img.Names, options.AdditionalTags...))
|
return imgID, nil, "", errors.Wrapf(err, "error setting image names to %v", append(img.Names, options.AdditionalTags...))
|
||||||
}
|
}
|
||||||
logrus.Debugf("assigned names %v to image %q", img.Names, img.ID)
|
logrus.Debugf("assigned names %v to image %q", img.Names, img.ID)
|
||||||
|
|
|
@ -684,7 +684,7 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
|
||||||
return imageID, ref, errors.Wrapf(err, "error locating just-written image %q", transports.ImageName(dest))
|
return imageID, ref, errors.Wrapf(err, "error locating just-written image %q", transports.ImageName(dest))
|
||||||
}
|
}
|
||||||
if len(b.additionalTags) > 0 {
|
if len(b.additionalTags) > 0 {
|
||||||
if err = util.TagImage(b.store, b.systemContext, img, b.additionalTags); err != nil {
|
if err = util.AddImageNames(b.store, "", b.systemContext, img, b.additionalTags); err != nil {
|
||||||
return imageID, ref, errors.Wrapf(err, "error setting image names to %v", append(img.Names, b.additionalTags...))
|
return imageID, ref, errors.Wrapf(err, "error setting image names to %v", append(img.Names, b.additionalTags...))
|
||||||
}
|
}
|
||||||
logrus.Debugf("assigned names %v to image %q", img.Names, img.ID)
|
logrus.Debugf("assigned names %v to image %q", img.Names, img.ID)
|
||||||
|
|
|
@ -153,7 +153,7 @@ func importBuilderFromImage(ctx context.Context, store storage.Store, options Im
|
||||||
|
|
||||||
systemContext := getSystemContext(store, options.SystemContext, options.SignaturePolicyPath)
|
systemContext := getSystemContext(store, options.SystemContext, options.SignaturePolicyPath)
|
||||||
|
|
||||||
_, img, err := util.FindImage(store, systemContext, options.Image)
|
_, img, err := util.FindImage(store, "", systemContext, options.Image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "importing settings")
|
return nil, errors.Wrapf(err, "importing settings")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package buildah
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/buildah/define"
|
||||||
|
"github.com/containers/buildah/pkg/blobcache"
|
||||||
|
"github.com/containers/common/libimage"
|
||||||
|
libimageTypes "github.com/containers/common/libimage/types"
|
||||||
|
"github.com/containers/image/v5/types"
|
||||||
|
encconfig "github.com/containers/ocicrypt/config"
|
||||||
|
"github.com/containers/storage"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PullOptions can be used to alter how an image is copied in from somewhere.
|
||||||
|
type PullOptions struct {
|
||||||
|
// SignaturePolicyPath specifies an override location for the signature
|
||||||
|
// policy which should be used for verifying the new image as it is
|
||||||
|
// being written. Except in specific circumstances, no value should be
|
||||||
|
// specified, indicating that the shared, system-wide default policy
|
||||||
|
// should be used.
|
||||||
|
SignaturePolicyPath string
|
||||||
|
// ReportWriter is an io.Writer which will be used to log the writing
|
||||||
|
// of the new image.
|
||||||
|
ReportWriter io.Writer
|
||||||
|
// Store is the local storage store which holds the source image.
|
||||||
|
Store storage.Store
|
||||||
|
// github.com/containers/image/types SystemContext to hold credentials
|
||||||
|
// and other authentication/authorization information.
|
||||||
|
SystemContext *types.SystemContext
|
||||||
|
// BlobDirectory is the name of a directory in which we'll attempt to
|
||||||
|
// store copies of layer blobs that we pull down, if any. It should
|
||||||
|
// already exist.
|
||||||
|
BlobDirectory string
|
||||||
|
// AllTags is a boolean value that determines if all tagged images
|
||||||
|
// will be downloaded from the repository. The default is false.
|
||||||
|
AllTags bool
|
||||||
|
// RemoveSignatures causes any existing signatures for the image to be
|
||||||
|
// discarded when pulling it.
|
||||||
|
RemoveSignatures bool
|
||||||
|
// MaxRetries is the maximum number of attempts we'll make to pull any
|
||||||
|
// one image from the external registry if the first attempt fails.
|
||||||
|
MaxRetries int
|
||||||
|
// RetryDelay is how long to wait before retrying a pull attempt.
|
||||||
|
RetryDelay time.Duration
|
||||||
|
// OciDecryptConfig contains the config that can be used to decrypt an image if it is
|
||||||
|
// encrypted if non-nil. If nil, it does not attempt to decrypt an image.
|
||||||
|
OciDecryptConfig *encconfig.DecryptConfig
|
||||||
|
// PullPolicy takes the value PullIfMissing, PullAlways, PullIfNewer, or PullNever.
|
||||||
|
PullPolicy define.PullPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull copies the contents of the image from somewhere else to local storage. Returns the
|
||||||
|
// ID of the local image or an error.
|
||||||
|
func Pull(ctx context.Context, imageName string, options PullOptions) (imageID string, err error) {
|
||||||
|
libimageOptions := &libimage.PullOptions{}
|
||||||
|
libimageOptions.SignaturePolicyPath = options.SignaturePolicyPath
|
||||||
|
libimageOptions.Writer = options.ReportWriter
|
||||||
|
libimageOptions.RemoveSignatures = options.RemoveSignatures
|
||||||
|
libimageOptions.OciDecryptConfig = options.OciDecryptConfig
|
||||||
|
libimageOptions.AllTags = options.AllTags
|
||||||
|
libimageOptions.RetryDelay = &options.RetryDelay
|
||||||
|
|
||||||
|
if options.MaxRetries > 0 {
|
||||||
|
retries := uint(options.MaxRetries)
|
||||||
|
libimageOptions.MaxRetries = &retries
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.BlobDirectory != "" {
|
||||||
|
libimageOptions.DestinationLookupReferenceFunc = blobcache.CacheLookupReferenceFunc(options.BlobDirectory, types.PreserveOriginal)
|
||||||
|
}
|
||||||
|
|
||||||
|
pullPolicy, err := libimageTypes.ParsePullPolicy(options.PullPolicy.String())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime, err := libimage.RuntimeFromStore(options.Store, &libimage.RuntimeOptions{SystemContext: options.SystemContext})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
pulledImages, err := runtime.Pull(context.Background(), imageName, pullPolicy, libimageOptions)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pulledImages) == 0 {
|
||||||
|
return "", errors.Errorf("internal error pulling %s: no image pulled and no error", imageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pulledImages[0].ID(), nil
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
package buildah
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/buildah/pkg/blobcache"
|
||||||
|
"github.com/containers/common/libimage"
|
||||||
|
"github.com/containers/image/v5/docker/reference"
|
||||||
|
"github.com/containers/image/v5/manifest"
|
||||||
|
"github.com/containers/image/v5/transports"
|
||||||
|
"github.com/containers/image/v5/types"
|
||||||
|
encconfig "github.com/containers/ocicrypt/config"
|
||||||
|
"github.com/containers/storage"
|
||||||
|
"github.com/containers/storage/pkg/archive"
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PushOptions can be used to alter how an image is copied somewhere.
|
||||||
|
type PushOptions struct {
|
||||||
|
// Compression specifies the type of compression which is applied to
|
||||||
|
// layer blobs. The default is to not use compression, but
|
||||||
|
// archive.Gzip is recommended.
|
||||||
|
Compression archive.Compression
|
||||||
|
// SignaturePolicyPath specifies an override location for the signature
|
||||||
|
// policy which should be used for verifying the new image as it is
|
||||||
|
// being written. Except in specific circumstances, no value should be
|
||||||
|
// specified, indicating that the shared, system-wide default policy
|
||||||
|
// should be used.
|
||||||
|
SignaturePolicyPath string
|
||||||
|
// ReportWriter is an io.Writer which will be used to log the writing
|
||||||
|
// of the new image.
|
||||||
|
ReportWriter io.Writer
|
||||||
|
// Store is the local storage store which holds the source image.
|
||||||
|
Store storage.Store
|
||||||
|
// github.com/containers/image/types SystemContext to hold credentials
|
||||||
|
// and other authentication/authorization information.
|
||||||
|
SystemContext *types.SystemContext
|
||||||
|
// ManifestType is the format to use when saving the image using the 'dir' transport
|
||||||
|
// possible options are oci, v2s1, and v2s2
|
||||||
|
ManifestType string
|
||||||
|
// BlobDirectory is the name of a directory in which we'll look for
|
||||||
|
// prebuilt copies of layer blobs that we might otherwise need to
|
||||||
|
// regenerate from on-disk layers, substituting them in the list of
|
||||||
|
// blobs to copy whenever possible.
|
||||||
|
BlobDirectory string
|
||||||
|
// Quiet is a boolean value that determines if minimal output to
|
||||||
|
// the user will be displayed, this is best used for logging.
|
||||||
|
// The default is false.
|
||||||
|
Quiet bool
|
||||||
|
// SignBy is the fingerprint of a GPG key to use for signing the image.
|
||||||
|
SignBy string
|
||||||
|
// RemoveSignatures causes any existing signatures for the image to be
|
||||||
|
// discarded for the pushed copy.
|
||||||
|
RemoveSignatures bool
|
||||||
|
// MaxRetries is the maximum number of attempts we'll make to push any
|
||||||
|
// one image to the external registry if the first attempt fails.
|
||||||
|
MaxRetries int
|
||||||
|
// RetryDelay is how long to wait before retrying a push attempt.
|
||||||
|
RetryDelay time.Duration
|
||||||
|
// OciEncryptConfig when non-nil indicates that an image should be encrypted.
|
||||||
|
// The encryption options is derived from the construction of EncryptConfig object.
|
||||||
|
OciEncryptConfig *encconfig.EncryptConfig
|
||||||
|
// OciEncryptLayers represents the list of layers to encrypt.
|
||||||
|
// If nil, don't encrypt any layers.
|
||||||
|
// If non-nil and len==0, denotes encrypt all layers.
|
||||||
|
// integers in the slice represent 0-indexed layer indices, with support for negativ
|
||||||
|
// indexing. i.e. 0 is the first layer, -1 is the last (top-most) layer.
|
||||||
|
OciEncryptLayers *[]int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push copies the contents of the image to a new location.
|
||||||
|
func Push(ctx context.Context, image string, dest types.ImageReference, options PushOptions) (reference.Canonical, digest.Digest, error) {
|
||||||
|
libimageOptions := &libimage.PushOptions{}
|
||||||
|
libimageOptions.SignaturePolicyPath = options.SignaturePolicyPath
|
||||||
|
libimageOptions.Writer = options.ReportWriter
|
||||||
|
libimageOptions.ManifestMIMEType = options.ManifestType
|
||||||
|
libimageOptions.SignBy = options.SignBy
|
||||||
|
libimageOptions.RemoveSignatures = options.RemoveSignatures
|
||||||
|
libimageOptions.RetryDelay = &options.RetryDelay
|
||||||
|
libimageOptions.OciEncryptConfig = options.OciEncryptConfig
|
||||||
|
libimageOptions.OciEncryptLayers = options.OciEncryptLayers
|
||||||
|
libimageOptions.PolicyAllowStorage = true
|
||||||
|
|
||||||
|
if options.Quiet {
|
||||||
|
libimageOptions.Writer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.BlobDirectory != "" {
|
||||||
|
compress := types.PreserveOriginal
|
||||||
|
if options.Compression == archive.Gzip {
|
||||||
|
compress = types.Compress
|
||||||
|
}
|
||||||
|
libimageOptions.SourceLookupReferenceFunc = blobcache.CacheLookupReferenceFunc(options.BlobDirectory, compress)
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime, err := libimage.RuntimeFromStore(options.Store, &libimage.RuntimeOptions{SystemContext: options.SystemContext})
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
destString := fmt.Sprintf("%s:%s", dest.Transport().Name(), dest.StringWithinTransport())
|
||||||
|
manifestBytes, err := runtime.Push(ctx, image, destString, libimageOptions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestDigest, err := manifest.Digest(manifestBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", errors.Wrapf(err, "error computing digest of manifest of new image %q", transports.ImageName(dest))
|
||||||
|
}
|
||||||
|
|
||||||
|
var ref reference.Canonical
|
||||||
|
if name := dest.DockerReference(); name != nil {
|
||||||
|
ref, err = reference.WithDigest(name, manifestDigest)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warnf("error generating canonical reference with name %q and digest %s: %v", name, manifestDigest.String(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref, manifestDigest, nil
|
||||||
|
}
|
|
@ -105,7 +105,7 @@ func main() {
|
||||||
manifestType := ""
|
manifestType := ""
|
||||||
configType := ""
|
configType := ""
|
||||||
|
|
||||||
ref, _, err := util.FindImage(store, systemContext, image)
|
ref, _, err := util.FindImage(store, "", systemContext, image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ref2, err2 := alltransports.ParseImageName(image)
|
ref2, err2 := alltransports.ParseImageName(image)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
|
|
10
util/util.go
10
util/util.go
|
@ -161,7 +161,9 @@ func ExpandNames(names []string, systemContext *types.SystemContext, store stora
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindImage locates the locally-stored image which corresponds to a given name.
|
// FindImage locates the locally-stored image which corresponds to a given name.
|
||||||
func FindImage(store storage.Store, systemContext *types.SystemContext, image string) (types.ImageReference, *storage.Image, error) {
|
// Please note that the `firstRegistry` argument has been deprecated and has no
|
||||||
|
// effect anymore.
|
||||||
|
func FindImage(store storage.Store, firstRegistry string, systemContext *types.SystemContext, image string) (types.ImageReference, *storage.Image, error) {
|
||||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -212,8 +214,10 @@ func ResolveNameToReferences(
|
||||||
return refs, nil
|
return refs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagImage adds the specified names to the specified image.
|
// AddImageNames adds the specified names to the specified image. Please note
|
||||||
func TagImage(store storage.Store, systemContext *types.SystemContext, image *storage.Image, addNames []string) error {
|
// that the `firstRegistry` argument has been deprecated and has no effect
|
||||||
|
// anymore.
|
||||||
|
func AddImageNames(store storage.Store, firstRegistry string, systemContext *types.SystemContext, image *storage.Image, addNames []string) error {
|
||||||
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
runtime, err := libimage.RuntimeFromStore(store, &libimage.RuntimeOptions{SystemContext: systemContext})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in New Issue