1// Copyright 2014 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Package testutil contains helper functions for writing tests. 16package testutil 17 18import ( 19 "context" 20 "errors" 21 "fmt" 22 "io/ioutil" 23 "log" 24 "os" 25 26 "golang.org/x/oauth2" 27 "golang.org/x/oauth2/google" 28 "golang.org/x/oauth2/jwt" 29) 30 31const ( 32 envProjID = "GCLOUD_TESTS_GOLANG_PROJECT_ID" 33 envPrivateKey = "GCLOUD_TESTS_GOLANG_KEY" 34) 35 36// ProjID returns the project ID to use in integration tests, or the empty 37// string if none is configured. 38func ProjID() string { 39 return os.Getenv(envProjID) 40} 41 42// Credentials returns the credentials to use in integration tests, or nil if 43// none is configured. It uses the standard environment variable for tests in 44// this repo. 45func Credentials(ctx context.Context, scopes ...string) *google.Credentials { 46 return CredentialsEnv(ctx, envPrivateKey, scopes...) 47} 48 49// CredentialsEnv returns the credentials to use in integration tests, or nil 50// if none is configured. If the environment variable is unset, CredentialsEnv 51// will try to find 'Application Default Credentials'. Else, CredentialsEnv 52// will return nil. CredentialsEnv will log.Fatal if the token source is 53// specified but missing or invalid. 54func CredentialsEnv(ctx context.Context, envVar string, scopes ...string) *google.Credentials { 55 key := os.Getenv(envVar) 56 if key == "" { // Try for application default credentials. 57 creds, err := google.FindDefaultCredentials(ctx, scopes...) 58 if err != nil { 59 log.Println("No 'Application Default Credentials' found.") 60 return nil 61 } 62 return creds 63 } 64 65 data, err := ioutil.ReadFile(key) 66 if err != nil { 67 log.Fatal(err) 68 } 69 70 creds, err := google.CredentialsFromJSON(ctx, data, scopes...) 71 if err != nil { 72 log.Fatal(err) 73 } 74 return creds 75} 76 77// TokenSource returns the OAuth2 token source to use in integration tests, 78// or nil if none is configured. It uses the standard environment variable 79// for tests in this repo. 80func TokenSource(ctx context.Context, scopes ...string) oauth2.TokenSource { 81 return TokenSourceEnv(ctx, envPrivateKey, scopes...) 82} 83 84// TokenSourceEnv returns the OAuth2 token source to use in integration tests. or nil 85// if none is configured. It tries to get credentials from the filename in the 86// environment variable envVar. If the environment variable is unset, TokenSourceEnv 87// will try to find 'Application Default Credentials'. Else, TokenSourceEnv will 88// return nil. TokenSourceEnv will log.Fatal if the token source is specified but 89// missing or invalid. 90func TokenSourceEnv(ctx context.Context, envVar string, scopes ...string) oauth2.TokenSource { 91 key := os.Getenv(envVar) 92 if key == "" { // Try for application default credentials. 93 ts, err := google.DefaultTokenSource(ctx, scopes...) 94 if err != nil { 95 log.Println("No 'Application Default Credentials' found.") 96 return nil 97 } 98 return ts 99 } 100 conf, err := jwtConfigFromFile(key, scopes) 101 if err != nil { 102 log.Fatal(err) 103 } 104 return conf.TokenSource(ctx) 105} 106 107// JWTConfig reads the JSON private key file whose name is in the default 108// environment variable, and returns the jwt.Config it contains. It ignores 109// scopes. 110// If the environment variable is empty, it returns (nil, nil). 111func JWTConfig() (*jwt.Config, error) { 112 return jwtConfigFromFile(os.Getenv(envPrivateKey), nil) 113} 114 115// jwtConfigFromFile reads the given JSON private key file, and returns the 116// jwt.Config it contains. 117// If the filename is empty, it returns (nil, nil). 118func jwtConfigFromFile(filename string, scopes []string) (*jwt.Config, error) { 119 if filename == "" { 120 return nil, nil 121 } 122 jsonKey, err := ioutil.ReadFile(filename) 123 if err != nil { 124 return nil, fmt.Errorf("cannot read the JSON key file, err: %v", err) 125 } 126 conf, err := google.JWTConfigFromJSON(jsonKey, scopes...) 127 if err != nil { 128 return nil, fmt.Errorf("google.JWTConfigFromJSON: %v", err) 129 } 130 return conf, nil 131} 132 133// CanReplay reports whether an integration test can be run in replay mode. 134// The replay file must exist, and the GCLOUD_TESTS_GOLANG_ENABLE_REPLAY 135// environment variable must be non-empty. 136func CanReplay(replayFilename string) bool { 137 if os.Getenv("GCLOUD_TESTS_GOLANG_ENABLE_REPLAY") == "" { 138 return false 139 } 140 _, err := os.Stat(replayFilename) 141 return err == nil 142} 143 144// ErroringTokenSource is a token source for testing purposes, 145// to always return a non-nil error to its caller. It is useful 146// when testing error responses with bad oauth2 credentials. 147type ErroringTokenSource struct{} 148 149// Token implements oauth2.TokenSource, returning a nil oauth2.Token and a non-nil error. 150func (fts ErroringTokenSource) Token() (*oauth2.Token, error) { 151 return nil, errors.New("intentional error") 152} 153