1
0
mirror of synced 2026-02-06 19:05:55 +00:00

Retry oci push operations

Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
This commit is contained in:
Somtochi Onyekwere
2023-05-25 12:12:43 +01:00
parent 96ed1e1372
commit 020a494f3f
3 changed files with 85 additions and 26 deletions

View File

@@ -22,15 +22,24 @@ import (
"fmt"
"os"
"strings"
"time"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/logs"
"github.com/google/go-containerregistry/pkg/name"
reg "github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
"github.com/spf13/cobra"
"sigs.k8s.io/yaml"
"github.com/fluxcd/flux2/v2/internal/flags"
"github.com/fluxcd/pkg/oci"
"github.com/fluxcd/pkg/oci/auth/login"
client "github.com/fluxcd/pkg/oci/client"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
oci "github.com/fluxcd/pkg/oci/client"
"github.com/fluxcd/flux2/v2/internal/flags"
)
var pushArtifactCmd = &cobra.Command{
@@ -105,6 +114,7 @@ type pushArtifactFlags struct {
ignorePaths []string
annotations []string
output string
debug bool
}
var pushArtifactArgs = newPushArtifactFlags()
@@ -125,6 +135,7 @@ func init() {
pushArtifactCmd.Flags().StringArrayVarP(&pushArtifactArgs.annotations, "annotations", "a", nil, "Set custom OCI annotations in the format '<key>=<value>'")
pushArtifactCmd.Flags().StringVarP(&pushArtifactArgs.output, "output", "o", "",
"the format in which the artifact digest should be printed, can be 'json' or 'yaml'")
pushArtifactCmd.Flags().BoolVarP(&pushArtifactArgs.debug, "debug", "", false, "display logs from underlying library")
pushCmd.AddCommand(pushArtifactCmd)
}
@@ -147,7 +158,12 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("invalid path %q", pushArtifactArgs.path)
}
url, err := oci.ParseArtifactURL(ociURL)
url, err := client.ParseArtifactURL(ociURL)
if err != nil {
return err
}
ref, err := name.ParseReference(url)
if err != nil {
return err
}
@@ -175,7 +191,13 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
annotations[kv[0]] = kv[1]
}
meta := oci.Metadata{
if pushArtifactArgs.debug {
// direct logs from crane library to stderr
// this can be useful to figure out things happening underneath e.g when the library is retrying a request
logs.Warn.SetOutput(os.Stderr)
}
meta := client.Metadata{
Source: pushArtifactArgs.source,
Revision: pushArtifactArgs.revision,
Annotations: annotations,
@@ -184,13 +206,15 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
ociClient := oci.NewClient(oci.DefaultOptions())
var auth authn.Authenticator
opts := client.DefaultOptions()
if pushArtifactArgs.provider.String() == sourcev1.GenericOCIProvider && pushArtifactArgs.creds != "" {
logger.Actionf("logging in to registry with credentials")
if err := ociClient.LoginWithCredentials(pushArtifactArgs.creds); err != nil {
auth, err = client.GetAuthFromCredentials(pushArtifactArgs.creds)
if err != nil {
return fmt.Errorf("could not login with credentials: %w", err)
}
opts = append(opts, crane.WithAuth(auth))
}
if pushArtifactArgs.provider.String() != sourcev1.GenericOCIProvider {
@@ -200,15 +224,43 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("provider not supported: %w", err)
}
if err := ociClient.LoginWithProvider(ctx, url, ociProvider); err != nil {
auth, err = login.NewManager().Login(ctx, url, ref, getProviderLoginOption(ociProvider))
if err != nil {
return fmt.Errorf("error during login with provider: %w", err)
}
opts = append(opts, crane.WithAuth(auth))
}
if rootArgs.timeout != 0 {
backoff := remote.Backoff{
Duration: 1.0 * time.Second,
Factor: 3,
Jitter: 0.1,
// timeout happens when the cap is exceeded or number of steps is reached
// 10 steps is big enough that most reasonable cap(under 30min) will be exceeded before
// the number of steps are completed.
Steps: 10,
Cap: rootArgs.timeout,
}
if auth == nil {
auth, err = authn.DefaultKeychain.Resolve(ref.Context())
if err != nil {
return err
}
}
transportOpts, err := client.WithRetryTransport(ctx, ref, auth, backoff, []string{ref.Context().Scope(transport.PushScope)})
if err != nil {
return fmt.Errorf("error setting up transport: %w", err)
}
opts = append(opts, transportOpts, client.WithRetryBackOff(backoff))
}
if pushArtifactArgs.output == "" {
logger.Actionf("pushing artifact to %s", url)
}
ociClient := client.NewClient(opts)
digestURL, err := ociClient.Push(ctx, url, path, meta, pushArtifactArgs.ignorePaths)
if err != nil {
return fmt.Errorf("pushing artifact failed: %w", err)
@@ -256,3 +308,16 @@ func pushArtifactCmdRun(cmd *cobra.Command, args []string) error {
return nil
}
func getProviderLoginOption(provider oci.Provider) login.ProviderOptions {
var opts login.ProviderOptions
switch provider {
case oci.ProviderAzure:
opts.AzureAutoLogin = true
case oci.ProviderAWS:
opts.AwsAutoLogin = true
case oci.ProviderGCP:
opts.GcpAutoLogin = true
}
return opts
}