diff --git a/.github/workflows/ecr-auth.yaml b/.github/workflows/ecr-auth.yaml index 3daaa3a32..3d5e8d557 100644 --- a/.github/workflows/ecr-auth.yaml +++ b/.github/workflows/ecr-auth.yaml @@ -39,6 +39,18 @@ jobs: # List the tags krane ls ${{ env.AWS_ACCOUNT }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com/go-containerregistry-test + - name: Test krane auth get + ECR + shell: bash + run: | + CRED1=$(krane auth get ${{ env.AWS_ACCOUNT }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com) + CRED2=$(krane auth get ${{ env.AWS_ACCOUNT }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com) + if [[ "$CRED1" == "" ]] ; then + exit 1 + fi + if [[ "$CRED1" == "$CRED2" ]] ; then + echo "credentials are cached by infrastructure" + fi + crane-ecr-login: runs-on: ubuntu-latest env: diff --git a/.github/workflows/ghcr-auth.yaml b/.github/workflows/ghcr-auth.yaml index caaf7186d..166ea41cc 100644 --- a/.github/workflows/ghcr-auth.yaml +++ b/.github/workflows/ghcr-auth.yaml @@ -30,3 +30,18 @@ jobs: run: | # List the tags krane ls ghcr.io/${{ github.repository }}/testimage + + - name: Test krane auth get + GHCR + env: + GITHUB_TOKEN: ${{ github.token }} + shell: bash + run: | + CRED1=$(krane auth get ghcr.io) + CRED2=$(krane auth get ghcr.io) + if [[ "$CRED1" == "" ]] ; then + exit 1 + fi + if [[ "$CRED1" == "$CRED2" ]] ; then + echo "credentials are cached by infrastructure" + fi + \ No newline at end of file diff --git a/cmd/crane/cmd/auth.go b/cmd/crane/cmd/auth.go index 4914aeaee..e2351ae7d 100644 --- a/cmd/crane/cmd/auth.go +++ b/cmd/crane/cmd/auth.go @@ -26,19 +26,20 @@ import ( "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/types" "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/crane" "github.com/google/go-containerregistry/pkg/name" "github.com/spf13/cobra" ) // NewCmdAuth creates a new cobra.Command for the auth subcommand. -func NewCmdAuth(argv ...string) *cobra.Command { +func NewCmdAuth(options []crane.Option, argv ...string) *cobra.Command { cmd := &cobra.Command{ Use: "auth", Short: "Log in or access credentials", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { return cmd.Usage() }, } - cmd.AddCommand(NewCmdAuthGet(argv...), NewCmdAuthLogin(argv...)) + cmd.AddCommand(NewCmdAuthGet(options, argv...), NewCmdAuthLogin(argv...)) return cmd } @@ -62,30 +63,41 @@ func toCreds(config *authn.AuthConfig) credentials { } // NewCmdAuthGet creates a new `crane auth get` command. -func NewCmdAuthGet(argv ...string) *cobra.Command { +func NewCmdAuthGet(options []crane.Option, argv ...string) *cobra.Command { if len(argv) == 0 { argv = []string{os.Args[0]} } + baseCmd := strings.Join(argv, " ") eg := fmt.Sprintf(` # Read configured credentials for reg.example.com - echo "reg.example.com" | %s get - {"username":"AzureDiamond","password":"hunter2"}`, strings.Join(argv, " ")) + $ echo "reg.example.com" | %s get + {"username":"AzureDiamond","password":"hunter2"} + # or + $ %s get reg.example.com + {"username":"AzureDiamond","password":"hunter2"}`, baseCmd, baseCmd) return &cobra.Command{ - Use: "get", + Use: "get [REGISTRY_ADDR]", Short: "Implements a credential helper", Example: eg, - Args: cobra.NoArgs, + Args: cobra.MaximumNArgs(1), RunE: func(_ *cobra.Command, args []string) error { - b, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return err + registryAddr := "" + if len(args) == 1 { + registryAddr = args[0] + } else { + b, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return err + } + registryAddr = strings.TrimSpace(string(b)) } - reg, err := name.NewRegistry(strings.TrimSpace(string(b))) + + reg, err := name.NewRegistry(registryAddr) if err != nil { return err } - authorizer, err := authn.DefaultKeychain.Resolve(reg) + authorizer, err := crane.GetOptions(options...).Keychain.Resolve(reg) if err != nil { return err } diff --git a/cmd/crane/cmd/root.go b/cmd/crane/cmd/root.go index d06fe0190..7950ae7b6 100644 --- a/cmd/crane/cmd/root.go +++ b/cmd/crane/cmd/root.go @@ -93,7 +93,7 @@ func New(use, short string, options []crane.Option) *cobra.Command { commands := []*cobra.Command{ NewCmdAppend(&options), - NewCmdAuth("crane", "auth"), + NewCmdAuth(options, "crane", "auth"), NewCmdBlob(&options), NewCmdCatalog(&options), NewCmdConfig(&options), diff --git a/cmd/crane/doc/crane_auth_get.md b/cmd/crane/doc/crane_auth_get.md index 2265b84f0..6ff89c1c8 100644 --- a/cmd/crane/doc/crane_auth_get.md +++ b/cmd/crane/doc/crane_auth_get.md @@ -3,14 +3,17 @@ Implements a credential helper ``` -crane auth get [flags] +crane auth get [REGISTRY_ADDR] [flags] ``` ### Examples ``` # Read configured credentials for reg.example.com - echo "reg.example.com" | crane auth get + $ echo "reg.example.com" | crane auth get + {"username":"AzureDiamond","password":"hunter2"} + # or + $ crane auth get reg.example.com {"username":"AzureDiamond","password":"hunter2"} ``` diff --git a/cmd/gcrane/main.go b/cmd/gcrane/main.go index e3672a36d..a77168420 100644 --- a/cmd/gcrane/main.go +++ b/cmd/gcrane/main.go @@ -38,11 +38,12 @@ const ( ) func main() { + options := []crane.Option{crane.WithAuthFromKeychain(gcrane.Keychain)} // Same as crane, but override usage and keychain. - root := cmd.New(use, short, []crane.Option{crane.WithAuthFromKeychain(gcrane.Keychain)}) + root := cmd.New(use, short, options) // Add or override commands. - gcraneCmds := []*cobra.Command{gcmd.NewCmdList(), gcmd.NewCmdGc(), gcmd.NewCmdCopy(), cmd.NewCmdAuth("gcrane", "auth")} + gcraneCmds := []*cobra.Command{gcmd.NewCmdList(), gcmd.NewCmdGc(), gcmd.NewCmdCopy(), cmd.NewCmdAuth(options, "gcrane", "auth")} // Maintain a map of google-specific commands that we "override". used := make(map[string]bool) diff --git a/pkg/crane/options.go b/pkg/crane/options.go index 2f95e0556..2e992789b 100644 --- a/pkg/crane/options.go +++ b/pkg/crane/options.go @@ -29,6 +29,7 @@ type Options struct { Name []name.Option Remote []remote.Option Platform *v1.Platform + Keychain authn.Keychain } // GetOptions exposes the underlying []remote.Option, []name.Option, and @@ -44,6 +45,7 @@ func makeOptions(opts ...Option) Options { Remote: []remote.Option{ remote.WithAuthFromKeychain(authn.DefaultKeychain), }, + Keychain: authn.DefaultKeychain, } for _, o := range opts { o(&opt) @@ -86,6 +88,7 @@ func WithAuthFromKeychain(keys authn.Keychain) Option { return func(o *Options) { // Replace the default keychain at position 0. o.Remote[0] = remote.WithAuthFromKeychain(keys) + o.Keychain = keys } }