Skip to content

Commit

Permalink
Add support for multiple keychains (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
sethvargo authored Aug 21, 2022
1 parent 99120be commit a5b09b0
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 34 deletions.
21 changes: 7 additions & 14 deletions cmd/gcr-cleaner-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"syscall"
"time"

"github.com/GoogleCloudPlatform/gcr-cleaner/internal/bearerkeychain"
"github.com/GoogleCloudPlatform/gcr-cleaner/pkg/gcrcleaner"
gcrauthn "github.com/google/go-containerregistry/pkg/authn"
gcrgoogle "github.com/google/go-containerregistry/pkg/v1/google"
Expand Down Expand Up @@ -115,22 +116,14 @@ func realMain(ctx context.Context, logger *gcrcleaner.Logger) error {
return fmt.Errorf("failed to parse tag filter: %w", err)
}

// Try to find the "best" authentication.
var auther gcrauthn.Authenticator
if *tokenPtr != "" {
logger.Debug("using token from flag for authentication")
auther = &gcrauthn.Bearer{Token: *tokenPtr}
} else {
logger.Debug("using default token resolution for authentication")
var err error
auther, err = gcrgoogle.NewEnvAuthenticator()
if err != nil {
return fmt.Errorf("failed to setup auther: %w", err)
}
}
keychain := gcrauthn.NewMultiKeychain(
bearerkeychain.New(*tokenPtr),
gcrauthn.DefaultKeychain,
gcrgoogle.Keychain,
)

concurrency := runtime.NumCPU()
cleaner, err := gcrcleaner.NewCleaner(auther, logger, concurrency)
cleaner, err := gcrcleaner.NewCleaner(keychain, logger, concurrency)
if err != nil {
return fmt.Errorf("failed to create cleaner: %w", err)
}
Expand Down
20 changes: 7 additions & 13 deletions cmd/gcr-cleaner-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"syscall"
"time"

"github.com/GoogleCloudPlatform/gcr-cleaner/internal/bearerkeychain"
"github.com/GoogleCloudPlatform/gcr-cleaner/pkg/gcrcleaner"
gcrauthn "github.com/google/go-containerregistry/pkg/authn"
gcrgoogle "github.com/google/go-containerregistry/pkg/v1/google"
Expand Down Expand Up @@ -60,21 +61,14 @@ func realMain(ctx context.Context, logger *gcrcleaner.Logger) error {
}
addr := ":" + port

var auther gcrauthn.Authenticator
if token := os.Getenv("GCRCLEANER_TOKEN"); token != "" {
logger.Debug("using token from GCRCLEANER_TOKEN for authentication")
auther = &gcrauthn.Bearer{Token: token}
} else {
logger.Debug("using default token resolution for authentication")
var err error
auther, err = gcrgoogle.NewEnvAuthenticator()
if err != nil {
return fmt.Errorf("failed to setup auther: %w", err)
}
}
keychain := gcrauthn.NewMultiKeychain(
bearerkeychain.New(os.Getenv("GCRCLEANER_TOKEN")),
gcrauthn.DefaultKeychain,
gcrgoogle.Keychain,
)

concurrency := runtime.NumCPU()
cleaner, err := gcrcleaner.NewCleaner(auther, logger, concurrency)
cleaner, err := gcrcleaner.NewCleaner(keychain, logger, concurrency)
if err != nil {
return fmt.Errorf("failed to create cleaner: %w", err)
}
Expand Down
40 changes: 40 additions & 0 deletions internal/bearerkeychain/keychain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2022 The GCR Cleaner Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bearerkeychain

import (
gcrauthn "github.com/google/go-containerregistry/pkg/authn"
)

// Keychain represents a Bearer Token keychain entry.
type Keychain struct {
token string
}

// New creates a new Bearer Token keychain. If the provided token is empty, this
// will always resolve to anonymous auth. Otherwise it returns the bearer auth.
func New(token string) *Keychain {
return &Keychain{
token: token,
}
}

// Resolve implements Resolver for the given keychain.
func (k *Keychain) Resolve(_ gcrauthn.Resource) (gcrauthn.Authenticator, error) {
if k.token == "" {
return gcrauthn.Anonymous, nil
}
return &gcrauthn.Bearer{Token: k.token}, nil
}
13 changes: 6 additions & 7 deletions pkg/gcrcleaner/cleaner.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,25 @@ import (
// to a very old value[2] and thus sorting by creation date fails.
//
// [1]: https://en.wikipedia.org/wiki/Docker_(software)
//
// [2]: https://buildpacks.io/docs/features/reproducibility/
var dockerExistence = time.Date(2013, time.March, 20, 0, 0, 0, 0, time.UTC)

// Cleaner is a gcr cleaner.
type Cleaner struct {
auther gcrauthn.Authenticator
keychain gcrauthn.Keychain
logger *Logger
concurrency int
}

// NewCleaner creates a new GCR cleaner with the given token provider and
// concurrency.
func NewCleaner(auther gcrauthn.Authenticator, logger *Logger, c int) (*Cleaner, error) {
func NewCleaner(keychain gcrauthn.Keychain, logger *Logger, c int) (*Cleaner, error) {
if c < 1 {
return nil, fmt.Errorf("concurrency must be at least 1, got %d", c)
}

return &Cleaner{
auther: auther,
keychain: keychain,
concurrency: c,
logger: logger,
}, nil
Expand All @@ -72,7 +71,7 @@ func (c *Cleaner) Clean(ctx context.Context, repo string, since time.Time, keep

tags, err := gcrgoogle.List(gcrrepo,
gcrgoogle.WithContext(ctx),
gcrgoogle.WithAuth(c.auther))
gcrgoogle.WithAuthFromKeychain(c.keychain))
if err != nil {
return nil, fmt.Errorf("failed to list tags for repo %s: %w", repo, err)
}
Expand Down Expand Up @@ -222,7 +221,7 @@ type manifest struct {
// deleteOne deletes a single repo ref using the supplied auth.
func (c *Cleaner) deleteOne(ctx context.Context, ref gcrname.Reference) error {
if err := gcrremote.Delete(ref,
gcrremote.WithAuth(c.auther),
gcrremote.WithAuthFromKeychain(c.keychain),
gcrremote.WithContext(ctx)); err != nil {
return fmt.Errorf("failed to delete %s: %w", ref, err)
}
Expand Down Expand Up @@ -329,7 +328,7 @@ func (c *Cleaner) ListChildRepositories(ctx context.Context, roots []string) ([]

// List all repos in the registry.
allRepos, err := gcrremote.Catalog(ctx, *registry,
gcrremote.WithAuth(c.auther),
gcrremote.WithAuthFromKeychain(c.keychain),
gcrremote.WithContext(ctx))
if err != nil {
return nil, fmt.Errorf("failed to fetch catalog for registry %q: %w", registry.Name(), err)
Expand Down

0 comments on commit a5b09b0

Please sign in to comment.