...

Source file src/cloud.google.com/go/internal/testutil/context.go

Documentation: cloud.google.com/go/internal/testutil

     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.
    16  package testutil
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  	"log"
    23  	"os"
    24  
    25  	"golang.org/x/oauth2"
    26  	"golang.org/x/oauth2/google"
    27  	"golang.org/x/oauth2/jwt"
    28  	"google.golang.org/api/impersonate"
    29  )
    30  
    31  const (
    32  	envProjID      = "GCLOUD_TESTS_GOLANG_PROJECT_ID"
    33  	envPrivateKey  = "GCLOUD_TESTS_GOLANG_KEY"
    34  	envImpersonate = "GCLOUD_TESTS_IMPERSONATE_CREDENTIALS"
    35  )
    36  
    37  // ProjID returns the project ID to use in integration tests, or the empty
    38  // string if none is configured.
    39  func ProjID() string {
    40  	return os.Getenv(envProjID)
    41  }
    42  
    43  // Credentials returns the credentials to use in integration tests, or nil if
    44  // none is configured. It uses the standard environment variable for tests in
    45  // this repo.
    46  func Credentials(ctx context.Context, scopes ...string) *google.Credentials {
    47  	return CredentialsEnv(ctx, envPrivateKey, scopes...)
    48  }
    49  
    50  // CredentialsEnv returns the credentials to use in integration tests, or nil
    51  // if none is configured. If the environment variable is unset, CredentialsEnv
    52  // will try to find 'Application Default Credentials'. Else, CredentialsEnv
    53  // will return nil. CredentialsEnv will log.Fatal if the token source is
    54  // specified but missing or invalid.
    55  func CredentialsEnv(ctx context.Context, envVar string, scopes ...string) *google.Credentials {
    56  	if impKey := os.Getenv(envImpersonate); impKey == "true" {
    57  		return &google.Credentials{
    58  			TokenSource: impersonatedTokenSource(ctx, scopes),
    59  			ProjectID:   "dulcet-port-762",
    60  		}
    61  	}
    62  	key := os.Getenv(envVar)
    63  	if key == "" { // Try for application default credentials.
    64  		creds, err := google.FindDefaultCredentials(ctx, scopes...)
    65  		if err != nil {
    66  			log.Println("No 'Application Default Credentials' found.")
    67  			return nil
    68  		}
    69  		return creds
    70  	}
    71  
    72  	data, err := os.ReadFile(key)
    73  	if err != nil {
    74  		log.Fatal(err)
    75  	}
    76  
    77  	creds, err := google.CredentialsFromJSON(ctx, data, scopes...)
    78  	if err != nil {
    79  		log.Fatal(err)
    80  	}
    81  	return creds
    82  }
    83  
    84  // TokenSource returns the OAuth2 token source to use in integration tests,
    85  // or nil if none is configured. It uses the standard environment variable
    86  // for tests in this repo.
    87  func TokenSource(ctx context.Context, scopes ...string) oauth2.TokenSource {
    88  	return TokenSourceEnv(ctx, envPrivateKey, scopes...)
    89  }
    90  
    91  // TokenSourceEnv returns the OAuth2 token source to use in integration tests. or nil
    92  // if none is configured. It tries to get credentials from the filename in the
    93  // environment variable envVar. If the environment variable is unset, TokenSourceEnv
    94  // will try to find 'Application Default Credentials'. Else, TokenSourceEnv will
    95  // return nil. TokenSourceEnv will log.Fatal if the token source is specified but
    96  // missing or invalid.
    97  func TokenSourceEnv(ctx context.Context, envVar string, scopes ...string) oauth2.TokenSource {
    98  	if impKey := os.Getenv(envImpersonate); impKey == "true" {
    99  		return impersonatedTokenSource(ctx, scopes)
   100  	}
   101  	key := os.Getenv(envVar)
   102  	if key == "" { // Try for application default credentials.
   103  		ts, err := google.DefaultTokenSource(ctx, scopes...)
   104  		if err != nil {
   105  			log.Println("No 'Application Default Credentials' found.")
   106  			return nil
   107  		}
   108  		return ts
   109  	}
   110  	conf, err := jwtConfigFromFile(key, scopes)
   111  	if err != nil {
   112  		log.Fatal(err)
   113  	}
   114  	return conf.TokenSource(ctx)
   115  }
   116  
   117  func impersonatedTokenSource(ctx context.Context, scopes []string) oauth2.TokenSource {
   118  	ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{
   119  		TargetPrincipal: "kokoro@dulcet-port-762.iam.gserviceaccount.com",
   120  		Scopes:          scopes,
   121  	})
   122  	if err != nil {
   123  		log.Fatalf("Unable to impersonate credentials, exiting: %v", err)
   124  	}
   125  	return ts
   126  }
   127  
   128  // JWTConfig reads the JSON private key file whose name is in the default
   129  // environment variable, and returns the jwt.Config it contains. It ignores
   130  // scopes.
   131  // If the environment variable is empty, it returns (nil, nil).
   132  func JWTConfig() (*jwt.Config, error) {
   133  	return jwtConfigFromFile(os.Getenv(envPrivateKey), nil)
   134  }
   135  
   136  // jwtConfigFromFile reads the given JSON private key file, and returns the
   137  // jwt.Config it contains.
   138  // If the filename is empty, it returns (nil, nil).
   139  func jwtConfigFromFile(filename string, scopes []string) (*jwt.Config, error) {
   140  	if filename == "" {
   141  		return nil, nil
   142  	}
   143  	jsonKey, err := os.ReadFile(filename)
   144  	if err != nil {
   145  		return nil, fmt.Errorf("cannot read the JSON key file, err: %v", err)
   146  	}
   147  	conf, err := google.JWTConfigFromJSON(jsonKey, scopes...)
   148  	if err != nil {
   149  		return nil, fmt.Errorf("google.JWTConfigFromJSON: %v", err)
   150  	}
   151  	return conf, nil
   152  }
   153  
   154  // CanReplay reports whether an integration test can be run in replay mode.
   155  // The replay file must exist, and the GCLOUD_TESTS_GOLANG_ENABLE_REPLAY
   156  // environment variable must be non-empty.
   157  func CanReplay(replayFilename string) bool {
   158  	if os.Getenv("GCLOUD_TESTS_GOLANG_ENABLE_REPLAY") == "" {
   159  		return false
   160  	}
   161  	_, err := os.Stat(replayFilename)
   162  	return err == nil
   163  }
   164  
   165  // ErroringTokenSource is a token source for testing purposes,
   166  // to always return a non-nil error to its caller. It is useful
   167  // when testing error responses with bad oauth2 credentials.
   168  type ErroringTokenSource struct{}
   169  
   170  // Token implements oauth2.TokenSource, returning a nil oauth2.Token and a non-nil error.
   171  func (fts ErroringTokenSource) Token() (*oauth2.Token, error) {
   172  	return nil, errors.New("intentional error")
   173  }
   174  

View as plain text