Skip to content

Commit

Permalink
Use active gcloud credentials for executing cloudbuild when available
Browse files Browse the repository at this point in the history
  • Loading branch information
nkubala committed Aug 28, 2019
1 parent fb2107f commit 9b14768
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 34 deletions.
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
Expand Down
7 changes: 6 additions & 1 deletion pkg/skaffold/build/cluster/sources/gcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ func (g *GCSBucket) Setup(ctx context.Context, out io.Writer, artifact *latest.A
color.Default.Fprintln(out, "Uploading sources to", bucket, "GCS bucket")

g.tarName = fmt.Sprintf("context-%s.tar.gz", initialTag)
if err := sources.UploadToGCS(ctx, artifact, bucket, g.tarName, dependencies); err != nil {
c, err := gcp.CloudStorageClient()
if err != nil {
return "", errors.Wrap(err, "getting cloud storage client")
}
defer c.Close()
if err := sources.UploadToGCS(ctx, c, artifact, bucket, g.tarName, dependencies); err != nil {
return "", errors.Wrap(err, "uploading sources to GCS")
}

Expand Down
36 changes: 10 additions & 26 deletions pkg/skaffold/build/gcb/cloud_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import (
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/sources"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/version"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
cloudbuild "google.golang.org/api/cloudbuild/v1"
Expand All @@ -50,13 +49,12 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, tags tag.ImageTags,
}

func (b *Builder) buildArtifactWithCloudBuild(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) {
cbclient, err := cloudbuild.NewService(ctx)
cbclient, err := gcp.CloudBuildClient()
if err != nil {
return "", errors.Wrap(err, "getting cloudbuild client")
}
cbclient.UserAgent = version.UserAgent()

c, err := cstorage.NewClient(ctx)
c, err := gcp.CloudStorageClient()
if err != nil {
return "", errors.Wrap(err, "getting cloud storage client")
}
Expand All @@ -75,10 +73,10 @@ func (b *Builder) buildArtifactWithCloudBuild(ctx context.Context, out io.Writer
cbBucket := fmt.Sprintf("%s%s", projectID, constants.GCSBucketSuffix)
buildObject := fmt.Sprintf("source/%s-%s.tar.gz", projectID, util.RandomID())

if err := b.createBucketIfNotExists(ctx, projectID, cbBucket); err != nil {
if err := b.createBucketIfNotExists(ctx, c, projectID, cbBucket); err != nil {
return "", errors.Wrap(err, "creating bucket if not exists")
}
if err := b.checkBucketProjectCorrect(ctx, projectID, cbBucket); err != nil {
if err := b.checkBucketProjectCorrect(ctx, c, projectID, cbBucket); err != nil {
return "", errors.Wrap(err, "checking bucket is in correct project")
}

Expand All @@ -88,7 +86,7 @@ func (b *Builder) buildArtifactWithCloudBuild(ctx context.Context, out io.Writer
}

color.Default.Fprintf(out, "Pushing code to gs://%s/%s\n", cbBucket, buildObject)
if err := sources.UploadToGCS(ctx, artifact, cbBucket, buildObject, dependencies); err != nil {
if err := sources.UploadToGCS(ctx, c, artifact, cbBucket, buildObject, dependencies); err != nil {
return "", errors.Wrap(err, "uploading source tarball")
}

Expand Down Expand Up @@ -136,7 +134,7 @@ watch:
return "", errors.Wrap(err, "getting build status")
}

r, err := b.getLogs(ctx, offset, cbBucket, logsObject)
r, err := b.getLogs(ctx, c, offset, cbBucket, logsObject)
if err != nil {
return "", errors.Wrap(err, "getting logs")
}
Expand Down Expand Up @@ -198,13 +196,7 @@ func getDigest(b *cloudbuild.Build, defaultToTag string) (string, error) {
return docker.RemoteDigest(defaultToTag, nil)
}

func (b *Builder) getLogs(ctx context.Context, offset int64, bucket, objectName string) (io.ReadCloser, error) {
c, err := cstorage.NewClient(ctx)
if err != nil {
return nil, errors.Wrap(err, "getting storage client")
}
defer c.Close()

func (b *Builder) getLogs(ctx context.Context, c *cstorage.Client, offset int64, bucket, objectName string) (io.ReadCloser, error) {
r, err := c.Bucket(bucket).Object(objectName).NewRangeReader(ctx, offset, -1)
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok {
Expand All @@ -224,11 +216,7 @@ func (b *Builder) getLogs(ctx context.Context, offset int64, bucket, objectName
return r, nil
}

func (b *Builder) checkBucketProjectCorrect(ctx context.Context, projectID, bucket string) error {
c, err := cstorage.NewClient(ctx)
if err != nil {
return errors.Wrap(err, "getting storage client")
}
func (b *Builder) checkBucketProjectCorrect(ctx context.Context, c *cstorage.Client, projectID, bucket string) error {
it := c.Buckets(ctx, projectID)
// Set the prefix to the bucket we're looking for to only return that bucket and buckets with that prefix
// that we'll filter further later on
Expand All @@ -248,12 +236,8 @@ func (b *Builder) checkBucketProjectCorrect(ctx context.Context, projectID, buck
}
}

func (b *Builder) createBucketIfNotExists(ctx context.Context, projectID, bucket string) error {
c, err := cstorage.NewClient(ctx)
if err != nil {
return errors.Wrap(err, "getting storage client")
}
defer c.Close()
func (b *Builder) createBucketIfNotExists(ctx context.Context, c *cstorage.Client, projectID, bucket string) error {
var err error

_, err = c.Bucket(bucket).Attrs(ctx)

Expand Down
43 changes: 43 additions & 0 deletions pkg/skaffold/gcp/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,23 @@ limitations under the License.
package gcp

import (
"context"
"encoding/json"
"os/exec"
"strings"
"sync"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
"github.com/docker/cli/cli/config/configfile"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/oauth2/google"
)

var (
creds *google.Credentials
credsOnce sync.Once
credsErr error
)

// AutoConfigureGCRCredentialHelper automatically adds the `gcloud` credential helper
Expand Down Expand Up @@ -50,3 +62,34 @@ func AutoConfigureGCRCredentialHelper(cf *configfile.ConfigFile, registry string
}
cf.CredentialHelpers[registry] = "gcloud"
}

func activeUserCredentials() (*google.Credentials, error) {
credsOnce.Do(func() {
cmd := exec.Command("gcloud", "auth", "print-access-token", "--format=json")
body, err := util.RunCmdOut(cmd)
if err != nil {
credsErr = errors.Wrap(err, "retrieving gcloud access token")
return
}
jsonCreds := make(map[string]interface{})
json.Unmarshal(body, &jsonCreds)
jsonCreds["type"] = "authorized_user"
body, _ = json.Marshal(jsonCreds)

c, err := google.CredentialsFromJSON(context.Background(), body)
if err != nil {
logrus.Infof("unable to retrieve google creds: %v", err)
logrus.Infof("falling back to application default credentials")
return
}
_, err = c.TokenSource.Token()
if err != nil {
logrus.Infof("unable to retrieve token: %v", err)
logrus.Infof("falling back to application default credentials")
return
}
creds = c
})

return creds, credsErr
}
76 changes: 76 additions & 0 deletions pkg/skaffold/gcp/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Copyright 2019 The Skaffold 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 gcp

import (
"context"
"sync"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/version"

cstorage "cloud.google.com/go/storage"
"github.com/pkg/errors"
cloudbuild "google.golang.org/api/cloudbuild/v1"
"google.golang.org/api/option"
)

var (
cbclient *cloudbuild.Service
cbOnce sync.Once
cbErr error
)

// CloudBuildClient returns an authenticated client for interacting with
// the Google Cloud Build API. This client is created once and cached
// for repeated use in Skaffold.
func CloudBuildClient() (*cloudbuild.Service, error) {
cbOnce.Do(func() {
var options []option.ClientOption
var err error
creds, cErr := activeUserCredentials()
if cErr == nil && creds != nil {
options = append(options, option.WithCredentials(creds))
}

c, err := cloudbuild.NewService(context.Background(), options...)
if err != nil {
cbErr = err
return
}
c.UserAgent = version.UserAgent()
cbclient = c
})

return cbclient, cbErr
}

// CloudStorageClient returns an authenticated client for interacting with
// the Google Cloud Storage API. This client is not cached by Skaffold,
// because it needs to be closed each time it is done being used by the caller.
func CloudStorageClient() (*cstorage.Client, error) {
var options []option.ClientOption
var err error
creds, cErr := activeUserCredentials()
if cErr == nil && creds != nil {
options = append(options, option.WithCredentials(creds))
}
c, err := cstorage.NewClient(context.Background(), options...)
if err != nil {
return nil, errors.Wrap(err, "getting cloud storage client")
}
return c, nil
}
8 changes: 1 addition & 7 deletions pkg/skaffold/sources/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,7 @@ func TarGz(ctx context.Context, w io.Writer, a *latest.Artifact, dependencies []
}

// UploadToGCS uploads the artifact's sources to a GCS bucket.
func UploadToGCS(ctx context.Context, a *latest.Artifact, bucket, objectName string, dependencies []string) error {
c, err := cstorage.NewClient(ctx)
if err != nil {
return errors.Wrap(err, "creating GCS client")
}
defer c.Close()

func UploadToGCS(ctx context.Context, c *cstorage.Client, a *latest.Artifact, bucket, objectName string, dependencies []string) error {
w := c.Bucket(bucket).Object(objectName).NewWriter(ctx)
if err := TarGz(ctx, w, a, dependencies); err != nil {
return errors.Wrap(err, "uploading targz to google storage")
Expand Down

0 comments on commit 9b14768

Please sign in to comment.