Merge pull request #977 from QiWang19/commands-retry

Retry on skopeo subcommands
This commit is contained in:
Miloslav Trmač 2020-07-18 21:55:52 +02:00 committed by GitHub
commit 494d237789
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 99 additions and 48 deletions

View File

@ -22,6 +22,7 @@ type copyOptions struct {
global *globalOptions global *globalOptions
srcImage *imageOptions srcImage *imageOptions
destImage *imageDestOptions destImage *imageDestOptions
retryOpts *retryOptions
additionalTags []string // For docker-archive: destinations, in addition to the name:tag specified as destination, also add these additionalTags []string // For docker-archive: destinations, in addition to the name:tag specified as destination, also add these
removeSignatures bool // Do not copy signatures from the source image removeSignatures bool // Do not copy signatures from the source image
signByFingerprint string // Sign the image using a GPG key with the specified fingerprint signByFingerprint string // Sign the image using a GPG key with the specified fingerprint
@ -37,9 +38,11 @@ func copyCmd(global *globalOptions) *cobra.Command {
sharedFlags, sharedOpts := sharedImageFlags() sharedFlags, sharedOpts := sharedImageFlags()
srcFlags, srcOpts := imageFlags(global, sharedOpts, "src-", "screds") srcFlags, srcOpts := imageFlags(global, sharedOpts, "src-", "screds")
destFlags, destOpts := imageDestFlags(global, sharedOpts, "dest-", "dcreds") destFlags, destOpts := imageDestFlags(global, sharedOpts, "dest-", "dcreds")
retryFlags, retryOpts := retryFlags()
opts := copyOptions{global: global, opts := copyOptions{global: global,
srcImage: srcOpts, srcImage: srcOpts,
destImage: destOpts, destImage: destOpts,
retryOpts: retryOpts,
} }
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "copy [command options] SOURCE-IMAGE DESTINATION-IMAGE", Use: "copy [command options] SOURCE-IMAGE DESTINATION-IMAGE",
@ -59,6 +62,7 @@ See skopeo(1) section "IMAGE NAMES" for the expected format
flags.AddFlagSet(&sharedFlags) flags.AddFlagSet(&sharedFlags)
flags.AddFlagSet(&srcFlags) flags.AddFlagSet(&srcFlags)
flags.AddFlagSet(&destFlags) flags.AddFlagSet(&destFlags)
flags.AddFlagSet(&retryFlags)
flags.StringSliceVar(&opts.additionalTags, "additional-tag", []string{}, "additional tags (supports docker-archive)") flags.StringSliceVar(&opts.additionalTags, "additional-tag", []string{}, "additional tags (supports docker-archive)")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress output information when copying images") flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress output information when copying images")
flags.BoolVarP(&opts.all, "all", "a", false, "Copy all images if SOURCE-IMAGE is a list") flags.BoolVarP(&opts.all, "all", "a", false, "Copy all images if SOURCE-IMAGE is a list")
@ -178,17 +182,19 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) error {
decConfig = cc.DecryptConfig decConfig = cc.DecryptConfig
} }
_, err = copy.Image(ctx, policyContext, destRef, srcRef, &copy.Options{ return retryIfNecessary(ctx, func() error {
RemoveSignatures: opts.removeSignatures, _, err = copy.Image(ctx, policyContext, destRef, srcRef, &copy.Options{
SignBy: opts.signByFingerprint, RemoveSignatures: opts.removeSignatures,
ReportWriter: stdout, SignBy: opts.signByFingerprint,
SourceCtx: sourceCtx, ReportWriter: stdout,
DestinationCtx: destinationCtx, SourceCtx: sourceCtx,
ForceManifestMIMEType: manifestType, DestinationCtx: destinationCtx,
ImageListSelection: imageListSelection, ForceManifestMIMEType: manifestType,
OciDecryptConfig: decConfig, ImageListSelection: imageListSelection,
OciEncryptLayers: encLayers, OciDecryptConfig: decConfig,
OciEncryptConfig: encConfig, OciEncryptLayers: encLayers,
}) OciEncryptConfig: encConfig,
return err })
return err
}, opts.retryOpts)
} }

View File

@ -12,16 +12,19 @@ import (
) )
type deleteOptions struct { type deleteOptions struct {
global *globalOptions global *globalOptions
image *imageOptions image *imageOptions
retryOpts *retryOptions
} }
func deleteCmd(global *globalOptions) *cobra.Command { func deleteCmd(global *globalOptions) *cobra.Command {
sharedFlags, sharedOpts := sharedImageFlags() sharedFlags, sharedOpts := sharedImageFlags()
imageFlags, imageOpts := imageFlags(global, sharedOpts, "", "") imageFlags, imageOpts := imageFlags(global, sharedOpts, "", "")
retryFlags, retryOpts := retryFlags()
opts := deleteOptions{ opts := deleteOptions{
global: global, global: global,
image: imageOpts, image: imageOpts,
retryOpts: retryOpts,
} }
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "delete [command options] IMAGE-NAME", Use: "delete [command options] IMAGE-NAME",
@ -38,6 +41,7 @@ See skopeo(1) section "IMAGE NAMES" for the expected format
flags := cmd.Flags() flags := cmd.Flags()
flags.AddFlagSet(&sharedFlags) flags.AddFlagSet(&sharedFlags)
flags.AddFlagSet(&imageFlags) flags.AddFlagSet(&imageFlags)
flags.AddFlagSet(&retryFlags)
return cmd return cmd
} }
@ -63,5 +67,8 @@ func (opts *deleteOptions) run(args []string, stdout io.Writer) error {
ctx, cancel := opts.global.commandTimeoutContext() ctx, cancel := opts.global.commandTimeoutContext()
defer cancel() defer cancel()
return ref.DeleteImage(ctx, sys)
return retryIfNecessary(ctx, func() error {
return ref.DeleteImage(ctx, sys)
}, opts.retryOpts)
} }

View File

@ -17,16 +17,19 @@ import (
) )
type layersOptions struct { type layersOptions struct {
global *globalOptions global *globalOptions
image *imageOptions image *imageOptions
retryOpts *retryOptions
} }
func layersCmd(global *globalOptions) *cobra.Command { func layersCmd(global *globalOptions) *cobra.Command {
sharedFlags, sharedOpts := sharedImageFlags() sharedFlags, sharedOpts := sharedImageFlags()
imageFlags, imageOpts := imageFlags(global, sharedOpts, "", "") imageFlags, imageOpts := imageFlags(global, sharedOpts, "", "")
retryFlags, retryOpts := retryFlags()
opts := layersOptions{ opts := layersOptions{
global: global, global: global,
image: imageOpts, image: imageOpts,
retryOpts: retryOpts,
} }
cmd := &cobra.Command{ cmd := &cobra.Command{
Hidden: true, Hidden: true,
@ -38,6 +41,7 @@ func layersCmd(global *globalOptions) *cobra.Command {
flags := cmd.Flags() flags := cmd.Flags()
flags.AddFlagSet(&sharedFlags) flags.AddFlagSet(&sharedFlags)
flags.AddFlagSet(&imageFlags) flags.AddFlagSet(&imageFlags)
flags.AddFlagSet(&retryFlags)
return cmd return cmd
} }
@ -60,12 +64,20 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
return err return err
} }
cache := blobinfocache.DefaultCache(sys) cache := blobinfocache.DefaultCache(sys)
rawSource, err := parseImageSource(ctx, opts.image, imageName) var (
if err != nil { rawSource types.ImageSource
src types.ImageCloser
)
if err = retryIfNecessary(ctx, func() error {
rawSource, err = parseImageSource(ctx, opts.image, imageName)
return err
}, opts.retryOpts); err != nil {
return err return err
} }
src, err := image.FromSource(ctx, sys, rawSource) if err = retryIfNecessary(ctx, func() error {
if err != nil { src, err = image.FromSource(ctx, sys, rawSource)
return err
}, opts.retryOpts); err != nil {
if closeErr := rawSource.Close(); closeErr != nil { if closeErr := rawSource.Close(); closeErr != nil {
return errors.Wrapf(err, " (close error: %v)", closeErr) return errors.Wrapf(err, " (close error: %v)", closeErr)
} }
@ -129,8 +141,14 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
}() }()
for _, bd := range blobDigests { for _, bd := range blobDigests {
r, blobSize, err := rawSource.GetBlob(ctx, types.BlobInfo{Digest: bd.digest, Size: -1}, cache) var (
if err != nil { r io.ReadCloser
blobSize int64
)
if err = retryIfNecessary(ctx, func() error {
r, blobSize, err = rawSource.GetBlob(ctx, types.BlobInfo{Digest: bd.digest, Size: -1}, cache)
return err
}, opts.retryOpts); err != nil {
return err return err
} }
if _, err := dest.PutBlob(ctx, r, types.BlobInfo{Digest: bd.digest, Size: blobSize}, cache, bd.isConfig); err != nil { if _, err := dest.PutBlob(ctx, r, types.BlobInfo{Digest: bd.digest, Size: blobSize}, cache, bd.isConfig); err != nil {
@ -141,8 +159,11 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
} }
} }
manifest, _, err := src.Manifest(ctx) var manifest []byte
if err != nil { if err = retryIfNecessary(ctx, func() error {
manifest, _, err = src.Manifest(ctx)
return err
}, opts.retryOpts); err != nil {
return err return err
} }
if err := dest.PutManifest(ctx, manifest, nil); err != nil { if err := dest.PutManifest(ctx, manifest, nil); err != nil {

View File

@ -22,17 +22,20 @@ type tagListOutput struct {
} }
type tagsOptions struct { type tagsOptions struct {
global *globalOptions global *globalOptions
image *imageOptions image *imageOptions
retryOpts *retryOptions
} }
func tagsCmd(global *globalOptions) *cobra.Command { func tagsCmd(global *globalOptions) *cobra.Command {
sharedFlags, sharedOpts := sharedImageFlags() sharedFlags, sharedOpts := sharedImageFlags()
imageFlags, imageOpts := dockerImageFlags(global, sharedOpts, "", "") imageFlags, imageOpts := dockerImageFlags(global, sharedOpts, "", "")
retryFlags, retryOpts := retryFlags()
opts := tagsOptions{ opts := tagsOptions{
global: global, global: global,
image: imageOpts, image: imageOpts,
retryOpts: retryOpts,
} }
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "list-tags [command options] REPOSITORY-NAME", Use: "list-tags [command options] REPOSITORY-NAME",
@ -51,6 +54,7 @@ See skopeo-list-tags(1) section "REPOSITORY NAMES" for the expected format
flags := cmd.Flags() flags := cmd.Flags()
flags.AddFlagSet(&sharedFlags) flags.AddFlagSet(&sharedFlags)
flags.AddFlagSet(&imageFlags) flags.AddFlagSet(&imageFlags)
flags.AddFlagSet(&retryFlags)
return cmd return cmd
} }
@ -118,8 +122,12 @@ func (opts *tagsOptions) run(args []string, stdout io.Writer) (retErr error) {
return err return err
} }
repositoryName, tagListing, err := listDockerTags(ctx, sys, imgRef) var repositoryName string
if err != nil { var tagListing []string
if err = retryIfNecessary(ctx, func() error {
repositoryName, tagListing, err = listDockerTags(ctx, sys, imgRef)
return err
}, opts.retryOpts); err != nil {
return err return err
} }

View File

@ -28,11 +28,12 @@ type syncOptions struct {
global *globalOptions // Global (not command dependant) skopeo options global *globalOptions // Global (not command dependant) skopeo options
srcImage *imageOptions // Source image options srcImage *imageOptions // Source image options
destImage *imageDestOptions // Destination image options destImage *imageDestOptions // Destination image options
removeSignatures bool // Do not copy signatures from the source image retryOpts *retryOptions
signByFingerprint string // Sign the image using a GPG key with the specified fingerprint removeSignatures bool // Do not copy signatures from the source image
source string // Source repository name signByFingerprint string // Sign the image using a GPG key with the specified fingerprint
destination string // Destination registry name source string // Source repository name
scoped bool // When true, namespace copied images at destination using the source repository name destination string // Destination registry name
scoped bool // When true, namespace copied images at destination using the source repository name
} }
// repoDescriptor contains information of a single repository used as a sync source. // repoDescriptor contains information of a single repository used as a sync source.
@ -65,11 +66,13 @@ func syncCmd(global *globalOptions) *cobra.Command {
sharedFlags, sharedOpts := sharedImageFlags() sharedFlags, sharedOpts := sharedImageFlags()
srcFlags, srcOpts := dockerImageFlags(global, sharedOpts, "src-", "screds") srcFlags, srcOpts := dockerImageFlags(global, sharedOpts, "src-", "screds")
destFlags, destOpts := dockerImageFlags(global, sharedOpts, "dest-", "dcreds") destFlags, destOpts := dockerImageFlags(global, sharedOpts, "dest-", "dcreds")
retryFlags, retryOpts := retryFlags()
opts := syncOptions{ opts := syncOptions{
global: global, global: global,
srcImage: srcOpts, srcImage: srcOpts,
destImage: &imageDestOptions{imageOptions: destOpts}, destImage: &imageDestOptions{imageOptions: destOpts},
retryOpts: retryOpts,
} }
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -95,6 +98,7 @@ See skopeo-sync(1) for details.
flags.AddFlagSet(&sharedFlags) flags.AddFlagSet(&sharedFlags)
flags.AddFlagSet(&srcFlags) flags.AddFlagSet(&srcFlags)
flags.AddFlagSet(&destFlags) flags.AddFlagSet(&destFlags)
flags.AddFlagSet(&retryFlags)
return cmd return cmd
} }
@ -509,9 +513,15 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error {
return err return err
} }
ctx, cancel := opts.global.commandTimeoutContext()
defer cancel()
sourceArg := args[0] sourceArg := args[0]
srcRepoList, err := imagesToCopy(sourceArg, opts.source, sourceCtx) var srcRepoList []repoDescriptor
if err != nil { if err = retryIfNecessary(ctx, func() error {
srcRepoList, err = imagesToCopy(sourceArg, opts.source, sourceCtx)
return err
}, opts.retryOpts); err != nil {
return err return err
} }
@ -521,9 +531,6 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error {
return err return err
} }
ctx, cancel := opts.global.commandTimeoutContext()
defer cancel()
imagesNumber := 0 imagesNumber := 0
options := copy.Options{ options := copy.Options{
RemoveSignatures: opts.removeSignatures, RemoveSignatures: opts.removeSignatures,
@ -563,8 +570,10 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error {
"to": transports.ImageName(destRef), "to": transports.ImageName(destRef),
}).Infof("Copying image tag %d/%d", counter+1, len(srcRepo.TaggedImages)) }).Infof("Copying image tag %d/%d", counter+1, len(srcRepo.TaggedImages))
_, err = copy.Image(ctx, policyContext, destRef, ref, &options) if err = retryIfNecessary(ctx, func() error {
if err != nil { _, err = copy.Image(ctx, policyContext, destRef, ref, &options)
return err
}, opts.retryOpts); err != nil {
return errors.Wrapf(err, "Error copying tag %q", transports.ImageName(ref)) return errors.Wrapf(err, "Error copying tag %q", transports.ImageName(ref))
} }
imagesNumber++ imagesNumber++