diff --git a/README.md b/README.md index 0e1cde0..5c8c87a 100644 --- a/README.md +++ b/README.md @@ -390,10 +390,6 @@ Genval offers comprehensive management capabilities for the configuration files To bolster supply chain security workflows, Genval enables users to sign the artifacts after storing them in the registry. Similarly, when pulling any artifact, Genval provides functionality to verify the signatures of the artifacts. This feature leverages **Sigstore's Cosign keyless mode** of signing and verifying artifacts. However, users can also utilize their own private and public keys for signing and verifying the artifacts respectively. -To facilitate authentication with container registries, Genval initially looks for the `~/.docker/config.json` file in the user's -`$HOME` directory. If this file is found, Genval utilizes it for authentication with the registry. However, if the file is not present, -users must set the `ARTIFACT_REGISTRY_USERNAME` and `ARTIFACT_REGISTRY_PASSWORD` environment variables to authenticate with the container registry. - #### Building, pushing, and signing generated and/or verified config files and OCI artifacts The following command demonstrates building and pushing the OCI artifact (genval:test) to GitHub Container Registry (ghcr.io) while signing the artifact with Cosign in Keyless mode: diff --git a/cmd/artifact_pull.go b/cmd/artifact_pull.go index 86120aa..fc2d20a 100644 --- a/cmd/artifact_pull.go +++ b/cmd/artifact_pull.go @@ -106,8 +106,8 @@ func runPullArtifactCmd(cmd *cobra.Command, args []string) error { defer spin.Stop() if err := oci.PullArtifact(context.Background(), pullArgs.creds, pullArgs.dest, pullArgs.path); err != nil { - color.Red("Error pulling artifact from remote : %v", err) - return err + fe := color.RedString("Error pulling artifact from remote: %v", err) + return fmt.Errorf(fe) } spin.Stop() color.Green("Artifact from %s pulled and stored in :%s", pullArgs.dest, pullArgs.path) diff --git a/cmd/artifact_push.go b/cmd/artifact_push.go index 87d5941..3d5336a 100644 --- a/cmd/artifact_push.go +++ b/cmd/artifact_push.go @@ -1,11 +1,13 @@ package cmd import ( + "context" "fmt" "os" "path/filepath" "time" + "github.com/fatih/color" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/compression" "github.com/google/go-containerregistry/pkg/crane" @@ -155,21 +157,31 @@ func runPushCmd(cmd *cobra.Command, args []string) error { if err != nil { log.Errorf("appending content to artifact failed: %v", err) } + spin := utils.StartSpinner("pushing artifact") defer spin.Stop() + + var opts []crane.Option auth, err := oci.GetCreds(pushArgs.creds) if err != nil { - return fmt.Errorf("error getting credentials: %v", err) + return err } - opts, err := oci.GenerateCraneOptions(ref, auth, []string{ref.Context().Scope(transport.PushScope)}) - if err != nil { - log.Errorf("Error creating options required for push: %v", err) + + if pushArgs.creds == "" || auth == nil { + auth, err = authn.DefaultKeychain.Resolve(ref.Context()) + if err != nil { + return err + } } + opts = append(opts, crane.WithAuth(auth)) - if pushArgs.creds == "" { - opts = append(opts, crane.WithAuthFromKeychain(authn.DefaultKeychain)) + + topts, err := oci.GenerateCraneOptions(context.Background(), ref.Context().Registry, auth, []string{ref.Context().Scope(transport.PushScope)}) + if err != nil { + return fmt.Errorf("error creating transport for push operation: %v", err) } + opts = append(opts, topts) if err := crane.Push(img, ref.String(), opts...); err != nil { log.Fatalf("Error pushing artifact: %v", err) } @@ -188,7 +200,8 @@ func runPushCmd(cmd *cobra.Command, args []string) error { } } - log.Infof("✔ Artifact pushed successfully to: %v\n,Artifact Digest: %v\n", source, digest) - log.Infof("Digest URL: %v\n", digestURL) + log.Infof(color.GreenString("✔ Artifact pushed successfully to: %v", pushArgs.dest)) + log.Infof(color.GreenString("✔ Digest: %v", digest)) + log.Infof(color.GreenString("✔ Digest URL: %v\n", digestURL)) return nil } diff --git a/pkg/oci/ociClient.go b/pkg/oci/ociClient.go index ecdb096..1767298 100644 --- a/pkg/oci/ociClient.go +++ b/pkg/oci/ociClient.go @@ -52,18 +52,26 @@ func CheckTagAndPullArchive(url, tool, creds string, archivePath *os.File) error ociref := parts[0] desiredTag := parts[1] + var opts []crane.Option auth, err := GetCreds(creds) if err != nil { - return fmt.Errorf("error getting credentials: %v", err) + return fmt.Errorf("error fetching credentials: %v", err) } - opts, err := GenerateCraneOptions(ref, auth, []string{ref.Context().Scope(transport.PullScope)}) - if err != nil { - log.Errorf("Error reading credentials: %v", err) + + if creds == "" || auth == nil { + auth, err = authn.DefaultKeychain.Resolve(ref.Context()) + if err != nil { + return fmt.Errorf("error fetching default keychain: %v", err) + } } + opts = append(opts, crane.WithAuth(auth)) - if creds == "" { - opts = append(opts, crane.WithAuthFromKeychain(authn.DefaultKeychain)) + + topts, err := GenerateCraneOptions(context.Background(), ref.Context().Registry, auth, []string{ref.Context().Scope(transport.PullScope)}) + if err != nil { + return fmt.Errorf("error creating transport for pull: %v", err) } + opts = append(opts, topts) tags, err := crane.ListTags(ociref, opts...) if err != nil { @@ -204,18 +212,26 @@ func PullArtifact(ctx context.Context, creds, dest, path string) error { url := parts[0] desiredTag := parts[1] + var opts []crane.Option auth, err := GetCreds(creds) if err != nil { - return fmt.Errorf("error getting credentials: %v", err) + return fmt.Errorf("error fetching credentials: %v", err) } - opts, err := GenerateCraneOptions(ref, auth, []string{ref.Context().Scope(transport.PullScope)}) - if err != nil { - return fmt.Errorf("error getting credentials: %v", err) + + if creds == "" || auth == nil { + auth, err = authn.DefaultKeychain.Resolve(ref.Context()) + if err != nil { + return fmt.Errorf("error fetching default keychain: %v", err) + } } + opts = append(opts, crane.WithAuth(auth)) - if creds == "" { - opts = append(opts, crane.WithAuthFromKeychain(authn.DefaultKeychain)) + + topts, err := GenerateCraneOptions(context.Background(), ref.Context().Registry, auth, []string{ref.Context().Scope(transport.PullScope)}) + if err != nil { + return fmt.Errorf("error creating transport for pull:%v", err) } + opts = append(opts, topts) tags, err := crane.ListTags(url, opts...) if err != nil { @@ -297,11 +313,11 @@ func GetCreds(creds string) (authn.Authenticator, error) { } // Most parts of GenerateCraneOptions and its related funcs are copied from https://github.com/google/go-containerregistry/blob/1b4e4078a545f2b6f96766a064b45ee77cdbefdd/pkg/v1/remote/options.go#L128 -func GenerateCraneOptions(ref name.Reference, auth authn.Authenticator, scopes []string) ([]crane.Option, error) { - opts := []crane.Option{} +// GenerateCraneOptions generates an crane options object to perform remote operations +func GenerateCraneOptions(ctx context.Context, ref name.Registry, auth authn.Authenticator, scopes []string) (crane.Option, error) { var retryTransport http.RoundTripper - userAgent := fmt.Sprintf("intelops/genval/%s (%s; %s)", version.GetVersionInfo().GitVersion, runtime.GOOS, runtime.GOARCH) + userAgent := fmt.Sprintf("genval/%s (%s; %s)", version.GetVersionInfo().GitVersion, runtime.GOOS, runtime.GOARCH) retryTransport = remote.DefaultTransport.(*http.Transport).Clone() if logs.Enabled(logs.Debug) { retryTransport = transport.NewLogger(retryTransport) @@ -319,13 +335,12 @@ func GenerateCraneOptions(ref name.Reference, auth authn.Authenticator, scopes [ })) retryTransport = transport.NewUserAgent(retryTransport, userAgent) - // t, err := transport.NewWithContext(ref.Context().Registry, auth, retryTransport, scopes) - // if err != nil { - // return nil, err - // } - opts = append(opts, crane.WithTransport(retryTransport)) + t, err := transport.NewWithContext(ctx, ref, auth, retryTransport, scopes) + if err != nil { + return nil, err + } - return opts, nil + return crane.WithTransport(t), nil } var defaultRetryPredicate = func(err error) bool {