summaryrefslogtreecommitdiffstats
path: root/pkg/v1/google/keychain.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/v1/google/keychain.go')
-rw-r--r--pkg/v1/google/keychain.go92
1 files changed, 92 insertions, 0 deletions
diff --git a/pkg/v1/google/keychain.go b/pkg/v1/google/keychain.go
new file mode 100644
index 0000000..6dc7a50
--- /dev/null
+++ b/pkg/v1/google/keychain.go
@@ -0,0 +1,92 @@
+// Copyright 2018 Google LLC All Rights Reserved.
+//
+// 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 google
+
+import (
+ "strings"
+ "sync"
+
+ "github.com/google/go-containerregistry/pkg/authn"
+ "github.com/google/go-containerregistry/pkg/logs"
+)
+
+// Keychain exports an instance of the google Keychain.
+var Keychain authn.Keychain = &googleKeychain{}
+
+type googleKeychain struct {
+ once sync.Once
+ auth authn.Authenticator
+}
+
+// Resolve implements authn.Keychain a la docker-credential-gcr.
+//
+// This behaves similarly to the GCR credential helper, but reuses tokens until
+// they expire.
+//
+// We can't easily add this behavior to our credential helper implementation
+// of authn.Authenticator because the credential helper protocol doesn't include
+// expiration information, see here:
+// https://godoc.org/github.com/docker/docker-credential-helpers/credentials#Credentials
+//
+// In addition to being a performance optimization, the reuse of these access
+// tokens works around a bug in gcloud. It appears that attempting to invoke
+// `gcloud config config-helper` multiple times too quickly will fail:
+// https://github.com/GoogleCloudPlatform/docker-credential-gcr/issues/54
+//
+// We could upstream this behavior into docker-credential-gcr by parsing
+// gcloud's output and persisting its tokens across invocations, but then
+// we have to deal with invalidating caches across multiple runs (no fun).
+//
+// In general, we don't worry about that here because we expect to use the same
+// gcloud configuration in the scope of this one process.
+func (gk *googleKeychain) Resolve(target authn.Resource) (authn.Authenticator, error) {
+ // Only authenticate GCR and AR so it works with authn.NewMultiKeychain to fallback.
+ host := target.RegistryStr()
+ if host != "gcr.io" &&
+ !strings.HasSuffix(host, ".gcr.io") &&
+ !strings.HasSuffix(host, ".pkg.dev") &&
+ !strings.HasSuffix(host, ".google.com") {
+ return authn.Anonymous, nil
+ }
+
+ gk.once.Do(func() {
+ gk.auth = resolve()
+ })
+
+ return gk.auth, nil
+}
+
+func resolve() authn.Authenticator {
+ auth, envErr := NewEnvAuthenticator()
+ if envErr == nil && auth != authn.Anonymous {
+ logs.Debug.Println("google.Keychain: using Application Default Credentials")
+ return auth
+ }
+
+ auth, gErr := NewGcloudAuthenticator()
+ if gErr == nil && auth != authn.Anonymous {
+ logs.Debug.Println("google.Keychain: using gcloud fallback")
+ return auth
+ }
+
+ logs.Debug.Println("Failed to get any Google credentials, falling back to Anonymous")
+ if envErr != nil {
+ logs.Debug.Printf("Google env error: %v", envErr)
+ }
+ if gErr != nil {
+ logs.Debug.Printf("gcloud error: %v", gErr)
+ }
+ return authn.Anonymous
+}