github.com/GoogleCloudPlatform/compute-image-tools/cli_tools@v0.0.0-20240516224744-de2dabc4ed1b/common/utils/param/populator_test.go (about)

     1  //  Copyright 2020 Google Inc. All Rights Reserved.
     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 param
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"strings"
    21  	"testing"
    22  
    23  	"cloud.google.com/go/storage"
    24  	"github.com/golang/mock/gomock"
    25  	"github.com/stretchr/testify/assert"
    26  
    27  	daisy "github.com/GoogleCloudPlatform/compute-daisy"
    28  
    29  	"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/mocks"
    30  )
    31  
    32  func TestPopulator_PopulateMissingParametersReturnsErrorWhenZoneCantBeRetrieved(t *testing.T) {
    33  	project := "a_project"
    34  	scratchBucketGcsPath := "gs://scratchbucket/scratchpath"
    35  	zone := ""
    36  	region := ""
    37  	file := "gs://a_bucket/a_file"
    38  	storageLocation := "US"
    39  	network := "original-network"
    40  	subnet := "original-subnet"
    41  
    42  	mockCtrl := gomock.NewController(t)
    43  	defer mockCtrl.Finish()
    44  
    45  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
    46  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
    47  	mockScratchBucketCreator.EXPECT().IsBucketInProject(project, "scratchbucket").Return(true)
    48  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
    49  	mockResourceLocationRetriever.EXPECT().GetZone("us-west2", project).Return("",
    50  		daisy.Errf("zone not found")).Times(1)
    51  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
    52  	mockStorageClient.EXPECT().GetBucketAttrs("scratchbucket").Return(&storage.BucketAttrs{Location: "us-west2"}, nil).Times(1)
    53  	mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
    54  	err := NewPopulator(
    55  		mockNetworkResolver,
    56  		mockMetadataGce,
    57  		mockStorageClient,
    58  		mockResourceLocationRetriever,
    59  		mockScratchBucketCreator,
    60  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
    61  
    62  	assert.Contains(t, err.Error(), "zone not found")
    63  }
    64  
    65  func TestPopulator_PropagatesErrorFromNetworkResolver(t *testing.T) {
    66  	project := "a_project"
    67  	scratchBucketGcsPath := "gs://scratchbucket/scratchpath"
    68  	zone := "us-west2-a"
    69  	region := ""
    70  	file := "gs://a_bucket/a_file"
    71  	storageLocation := "US"
    72  	network := "original-network"
    73  	subnet := "original-subnet"
    74  
    75  	mockCtrl := gomock.NewController(t)
    76  	defer mockCtrl.Finish()
    77  
    78  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
    79  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
    80  	mockScratchBucketCreator.EXPECT().IsBucketInProject(project, "scratchbucket").Return(true)
    81  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
    82  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
    83  	mockStorageClient.EXPECT().GetBucketAttrs("scratchbucket").Return(&storage.BucketAttrs{Location: "us-west2"}, nil).Times(1)
    84  	mockNetworkResolver := mocks.NewMockNetworkResolver(mockCtrl)
    85  	mockNetworkResolver.EXPECT().ResolveAndValidateNetworkAndSubnet("original-network", "original-subnet", "us-west2", "a_project").Return("", "", daisy.Errf("network cannot be found"))
    86  	err := NewPopulator(
    87  		mockNetworkResolver,
    88  		mockMetadataGce,
    89  		mockStorageClient,
    90  		mockResourceLocationRetriever,
    91  		mockScratchBucketCreator,
    92  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
    93  
    94  	assert.Contains(t, err.Error(), "network cannot be found")
    95  }
    96  
    97  func TestPopulator_UsesReturnValuesFromNetworkResolver(t *testing.T) {
    98  	project := "a_project"
    99  	scratchBucketGcsPath := "gs://scratchbucket/scratchpath"
   100  	zone := "us-west2-a"
   101  	region := ""
   102  	file := "gs://a_bucket/a_file"
   103  	storageLocation := "US"
   104  	network := "original-network"
   105  	subnet := "original-subnet"
   106  
   107  	mockCtrl := gomock.NewController(t)
   108  	defer mockCtrl.Finish()
   109  
   110  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   111  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   112  	mockScratchBucketCreator.EXPECT().IsBucketInProject(project, "scratchbucket").Return(true)
   113  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   114  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   115  	mockStorageClient.EXPECT().GetBucketAttrs("scratchbucket").Return(&storage.BucketAttrs{Location: "us-west2"}, nil).Times(1)
   116  	mockNetworkResolver := mocks.NewMockNetworkResolver(mockCtrl)
   117  	mockNetworkResolver.EXPECT().ResolveAndValidateNetworkAndSubnet(
   118  		"original-network", "original-subnet", "us-west2", "a_project").Return("fixed-network", "fixed-subnet", nil)
   119  	err := NewPopulator(
   120  		mockNetworkResolver,
   121  		mockMetadataGce,
   122  		mockStorageClient,
   123  		mockResourceLocationRetriever,
   124  		mockScratchBucketCreator,
   125  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   126  	assert.NoError(t, err)
   127  	assert.Equal(t, "fixed-network", network)
   128  	assert.Equal(t, "fixed-subnet", subnet)
   129  }
   130  
   131  func TestPopulator_PopulateMissingParametersReturnsErrorWhenProjectNotProvidedAndNotRunningOnGCE(t *testing.T) {
   132  	project := ""
   133  	scratchBucketGcsPath := "gs://scratchbucket/scratchpath"
   134  	zone := ""
   135  	region := ""
   136  	file := "gs://a_bucket/a_file"
   137  	storageLocation := ""
   138  	network := "original-network"
   139  	subnet := "original-subnet"
   140  
   141  	mockCtrl := gomock.NewController(t)
   142  	defer mockCtrl.Finish()
   143  
   144  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   145  	mockMetadataGce.EXPECT().OnGCE().Return(false)
   146  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   147  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   148  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   149  	mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
   150  	err := NewPopulator(
   151  		mockNetworkResolver,
   152  		mockMetadataGce,
   153  		mockStorageClient,
   154  		mockResourceLocationRetriever,
   155  		mockScratchBucketCreator,
   156  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   157  
   158  	assert.Contains(t, err.Error(), "project cannot be determined because build is not running on GCE")
   159  }
   160  
   161  func TestPopulator_PopulateMissingParametersReturnsErrorWhenProjectNotProvidedAndGCEProjectIdEmpty(t *testing.T) {
   162  	project := ""
   163  	scratchBucketGcsPath := "gs://scratchbucket/scratchpath"
   164  	zone := ""
   165  	region := ""
   166  	file := "gs://a_bucket/a_file"
   167  	storageLocation := ""
   168  	network := "original-network"
   169  	subnet := "original-subnet"
   170  
   171  	mockCtrl := gomock.NewController(t)
   172  	defer mockCtrl.Finish()
   173  
   174  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   175  	mockMetadataGce.EXPECT().OnGCE().Return(true)
   176  	mockMetadataGce.EXPECT().ProjectID().Return("", nil)
   177  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   178  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   179  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   180  	mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
   181  	err := NewPopulator(
   182  		mockNetworkResolver,
   183  		mockMetadataGce,
   184  		mockStorageClient,
   185  		mockResourceLocationRetriever,
   186  		mockScratchBucketCreator,
   187  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   188  
   189  	assert.Contains(t, err.Error(), "project cannot be determined")
   190  }
   191  
   192  func TestPopulator_PopulateMissingParametersReturnsErrorWhenProjectNotProvidedAndMetadataReturnsError(t *testing.T) {
   193  	project := ""
   194  	scratchBucketGcsPath := "gs://scratchbucket/scratchpath"
   195  	zone := ""
   196  	region := ""
   197  	file := "gs://a_bucket/a_file"
   198  	storageLocation := ""
   199  	network := "original-network"
   200  	subnet := "original-subnet"
   201  
   202  	mockCtrl := gomock.NewController(t)
   203  	defer mockCtrl.Finish()
   204  
   205  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   206  	mockMetadataGce.EXPECT().OnGCE().Return(true)
   207  	mockMetadataGce.EXPECT().ProjectID().Return("pr", daisy.Errf("Err"))
   208  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   209  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   210  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   211  	mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
   212  	err := NewPopulator(
   213  		mockNetworkResolver,
   214  		mockMetadataGce,
   215  		mockStorageClient,
   216  		mockResourceLocationRetriever,
   217  		mockScratchBucketCreator,
   218  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   219  
   220  	assert.Contains(t, err.Error(), "project cannot be determined")
   221  }
   222  
   223  func TestPopulator_PopulateMissingParametersReturnsErrorWhenScratchBucketCreationError(t *testing.T) {
   224  	project := "a_project"
   225  	scratchBucketGcsPath := ""
   226  	zone := ""
   227  	region := ""
   228  	file := "gs://a_bucket/a_file"
   229  	storageLocation := ""
   230  	network := "original-network"
   231  	subnet := "original-subnet"
   232  
   233  	mockCtrl := gomock.NewController(t)
   234  	defer mockCtrl.Finish()
   235  
   236  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   237  	mockMetadataGce.EXPECT().OnGCE().Return(false)
   238  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   239  	mockScratchBucketCreator.EXPECT().CreateScratchBucket(file, project, zone).Return("", "", daisy.Errf("err"))
   240  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   241  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   242  	mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
   243  	err := NewPopulator(
   244  		mockNetworkResolver,
   245  		mockMetadataGce,
   246  		mockStorageClient,
   247  		mockResourceLocationRetriever,
   248  		mockScratchBucketCreator,
   249  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   250  
   251  	assert.Contains(t, err.Error(), "failed to create scratch bucket")
   252  }
   253  
   254  func TestPopulator_PopulateMissingParametersReturnsErrorWhenScratchBucketInvalidFormat(t *testing.T) {
   255  	project := "a_project"
   256  	scratchBucketGcsPath := "NOT_GCS_PATH"
   257  	zone := ""
   258  	region := ""
   259  	file := "gs://a_bucket/a_file"
   260  	storageLocation := ""
   261  	network := "original-network"
   262  	subnet := "original-subnet"
   263  
   264  	mockCtrl := gomock.NewController(t)
   265  	defer mockCtrl.Finish()
   266  
   267  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   268  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   269  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   270  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   271  	mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
   272  	err := NewPopulator(
   273  		mockNetworkResolver,
   274  		mockMetadataGce,
   275  		mockStorageClient,
   276  		mockResourceLocationRetriever,
   277  		mockScratchBucketCreator,
   278  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   279  
   280  	assert.Contains(t, err.Error(), "invalid scratch bucket")
   281  }
   282  
   283  func TestPopulator_PopulateMissingParametersReturnsErrorWhenPopulateRegionFails(t *testing.T) {
   284  	project := "a_project"
   285  	scratchBucketGcsPath := "gs://scratchbucket/scratchpath"
   286  	zone := "NOT_ZONE"
   287  	region := "NOT_REGION"
   288  	file := "gs://a_bucket/a_file"
   289  	storageLocation := "US"
   290  	network := "original-network"
   291  	subnet := "original-subnet"
   292  
   293  	mockCtrl := gomock.NewController(t)
   294  	defer mockCtrl.Finish()
   295  
   296  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   297  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   298  	mockScratchBucketCreator.EXPECT().IsBucketInProject(project, "scratchbucket").Return(true)
   299  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   300  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   301  	mockStorageClient.EXPECT().GetBucketAttrs("scratchbucket").Return(&storage.BucketAttrs{Location: region}, nil)
   302  	mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
   303  	err := NewPopulator(
   304  		mockNetworkResolver,
   305  		mockMetadataGce,
   306  		mockStorageClient,
   307  		mockResourceLocationRetriever,
   308  		mockScratchBucketCreator,
   309  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   310  
   311  	assert.Contains(t, err.Error(), "NOT_ZONE is not a valid zone")
   312  }
   313  
   314  func TestPopulator_PopulateMissingParametersDoesNotChangeProvidedScratchBucketAndUsesItsRegion(t *testing.T) {
   315  	project := "a_project"
   316  	zone := ""
   317  	region := ""
   318  	scratchBucketGcsPath := "gs://scratchbucket/scratchpath"
   319  	storageLocation := "US"
   320  	network := "original-network"
   321  	subnet := "original-subnet"
   322  
   323  	file := "gs://sourcebucket/sourcefile"
   324  	expectedBucketName := "scratchbucket"
   325  	expectedRegion := "europe-north1"
   326  	expectedZone := "europe-north1-b"
   327  
   328  	mockCtrl := gomock.NewController(t)
   329  	defer mockCtrl.Finish()
   330  
   331  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   332  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   333  	mockScratchBucketCreator.EXPECT().IsBucketInProject(project, "scratchbucket").Return(true)
   334  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   335  	mockResourceLocationRetriever.EXPECT().GetZone(expectedRegion, project).Return(expectedZone, nil).Times(1)
   336  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   337  	mockStorageClient.EXPECT().GetBucketAttrs(expectedBucketName).Return(&storage.BucketAttrs{Location: expectedRegion}, nil)
   338  	mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
   339  	err := NewPopulator(
   340  		mockNetworkResolver,
   341  		mockMetadataGce,
   342  		mockStorageClient,
   343  		mockResourceLocationRetriever,
   344  		mockScratchBucketCreator,
   345  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   346  
   347  	assert.Nil(t, err)
   348  	assert.Equal(t, "a_project", project)
   349  	assert.Equal(t, "europe-north1-b", zone)
   350  	assert.Equal(t, "europe-north1", region)
   351  	assert.Equal(t, "gs://scratchbucket/scratchpath", scratchBucketGcsPath)
   352  }
   353  
   354  func TestPopulator_DeleteResources_WhenScratchBucketInAnotherProject(t *testing.T) {
   355  	for _, tt := range []struct {
   356  		caseName                string
   357  		client                  string
   358  		deleteResult            error
   359  		deleteExpected          bool
   360  		expectedError           string
   361  		expectedAnonymizedError string
   362  		scratchBucketGCSPath    string
   363  		fileGCSPath             string
   364  	}{
   365  		{
   366  			caseName:       "In scratch - gcloud - Successful deletion",
   367  			client:         "gcloud",
   368  			deleteResult:   nil,
   369  			deleteExpected: true,
   370  			expectedError: "Scratch bucket \"scratchbucket\" is not in project \"a_project\". " +
   371  				"Deleted \"gs://scratchbucket/sourcefile\"",
   372  			expectedAnonymizedError: "Scratch bucket %q is not in project %q. Deleted %q",
   373  			scratchBucketGCSPath:    "gs://scratchbucket/scratchpath",
   374  			fileGCSPath:             "gs://scratchbucket/sourcefile",
   375  		},
   376  		{
   377  			caseName:       "In scratch - gcloud - Failed deletion",
   378  			client:         "gcloud",
   379  			deleteResult:   errors.New("Failed to delete path"),
   380  			deleteExpected: true,
   381  			expectedError: "Scratch bucket \"scratchbucket\" is not in project \"a_project\". Failed to delete " +
   382  				"\"gs://scratchbucket/sourcefile\": Failed to delete path. " +
   383  				"Check with the owner of gs://\"scratchbucket\" for more information",
   384  			expectedAnonymizedError: "Scratch bucket %q is not in project %q. Failed to delete %q: %v. " +
   385  				"Check with the owner of gs://%q for more information",
   386  			scratchBucketGCSPath: "gs://scratchbucket/scratchpath",
   387  			fileGCSPath:          "gs://scratchbucket/sourcefile",
   388  		},
   389  		{
   390  			caseName:                "In scratch - not gcloud - don't delete",
   391  			client:                  "api",
   392  			expectedError:           "Scratch bucket \"scratchbucket\" is not in project \"a_project\"",
   393  			expectedAnonymizedError: "Scratch bucket %q is not in project %q",
   394  			scratchBucketGCSPath:    "gs://scratchbucket/scratchpath",
   395  			fileGCSPath:             "gs://scratchbucket/sourcefile",
   396  		},
   397  		{
   398  			caseName:                "Not in scratch - Don't delete",
   399  			client:                  "gcloud",
   400  			expectedError:           "Scratch bucket \"scratchbucket\" is not in project \"a_project\"",
   401  			expectedAnonymizedError: "Scratch bucket %q is not in project %q",
   402  			scratchBucketGCSPath:    "gs://scratchbucket/scratchpath",
   403  			fileGCSPath:             "gs://source-images/sourcefile",
   404  		},
   405  		{
   406  			caseName:                "GCS Image - Don't delete",
   407  			client:                  "gcloud",
   408  			expectedError:           "Scratch bucket \"scratchbucket\" is not in project \"a_project\"",
   409  			expectedAnonymizedError: "Scratch bucket %q is not in project %q",
   410  			scratchBucketGCSPath:    "gs://scratchbucket/scratchpath",
   411  			fileGCSPath:             "",
   412  		},
   413  	} {
   414  		t.Run(tt.caseName, func(t *testing.T) {
   415  			project := "a_project"
   416  			zone := ""
   417  			region := ""
   418  			scratchBucketGcsPath := tt.scratchBucketGCSPath
   419  			storageLocation := "US"
   420  			file := tt.fileGCSPath
   421  			network := "original-network"
   422  			subnet := "original-subnet"
   423  
   424  			mockCtrl := gomock.NewController(t)
   425  			defer mockCtrl.Finish()
   426  
   427  			mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   428  			mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   429  			mockScratchBucketCreator.EXPECT().IsBucketInProject(project, "scratchbucket").Return(false)
   430  			mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   431  			mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   432  			if tt.deleteExpected {
   433  				mockStorageClient.EXPECT().DeleteObject(file).Return(tt.deleteResult)
   434  			}
   435  			mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
   436  			err := NewPopulator(
   437  				mockNetworkResolver,
   438  				mockMetadataGce,
   439  				mockStorageClient,
   440  				mockResourceLocationRetriever,
   441  				mockScratchBucketCreator,
   442  			).PopulateMissingParameters(&project, tt.client, &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   443  
   444  			realError := err.(daisy.DError)
   445  			assert.EqualError(t, realError, tt.expectedError)
   446  			assert.Equal(t, strings.Join(realError.AnonymizedErrs(), ""), tt.expectedAnonymizedError)
   447  		})
   448  	}
   449  }
   450  
   451  func TestPopulator_PopulateMissingParametersCreatesScratchBucketIfNotProvided(t *testing.T) {
   452  	project := "a_project"
   453  	zone := ""
   454  	region := ""
   455  	scratchBucketGcsPath := ""
   456  	storageLocation := "US"
   457  	network := "original-network"
   458  	subnet := "original-subnet"
   459  
   460  	file := "gs://sourcebucket/sourcefile"
   461  	expectedBucketName := "new_scratch_bucket"
   462  	expectedRegion := "europe-north1"
   463  	expectedZone := "europe-north1-c"
   464  
   465  	mockCtrl := gomock.NewController(t)
   466  	defer mockCtrl.Finish()
   467  
   468  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   469  	mockMetadataGce.EXPECT().OnGCE().Return(false)
   470  
   471  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   472  	mockScratchBucketCreator.EXPECT().
   473  		CreateScratchBucket(file, project, zone).
   474  		Return(expectedBucketName, expectedRegion, nil).
   475  		Times(1)
   476  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   477  	mockResourceLocationRetriever.EXPECT().GetZone(expectedRegion, project).Return(expectedZone, nil).Times(1)
   478  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   479  	mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
   480  	err := NewPopulator(
   481  		mockNetworkResolver,
   482  		mockMetadataGce,
   483  		mockStorageClient,
   484  		mockResourceLocationRetriever,
   485  		mockScratchBucketCreator,
   486  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   487  
   488  	assert.Nil(t, err)
   489  	assert.Equal(t, "a_project", project)
   490  	assert.Equal(t, expectedZone, zone)
   491  	assert.Equal(t, expectedRegion, region)
   492  	assert.Equal(t, fmt.Sprintf("gs://%v/", expectedBucketName), scratchBucketGcsPath)
   493  }
   494  
   495  func TestPopulator_PopulateMissingParametersCreatesScratchBucketIfNotProvidedOnGCE(t *testing.T) {
   496  	project := "a_project"
   497  	zone := ""
   498  	region := ""
   499  	scratchBucketGcsPath := ""
   500  	storageLocation := "US"
   501  	network := "original-network"
   502  	subnet := "original-subnet"
   503  
   504  	file := "gs://sourcebucket/sourcefile"
   505  	expectedBucketName := "new_scratch_bucket"
   506  	expectedRegion := "europe-north1"
   507  	expectedZone := "europe-north1-c"
   508  
   509  	mockCtrl := gomock.NewController(t)
   510  	defer mockCtrl.Finish()
   511  
   512  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   513  	mockMetadataGce.EXPECT().OnGCE().Return(true)
   514  	mockMetadataGce.EXPECT().Zone().Return(expectedZone, nil)
   515  
   516  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   517  	mockScratchBucketCreator.EXPECT().
   518  		CreateScratchBucket(file, project, expectedZone).
   519  		Return(expectedBucketName, expectedRegion, nil).
   520  		Times(1)
   521  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   522  	mockResourceLocationRetriever.EXPECT().GetZone(expectedRegion, project).Return(expectedZone, nil).Times(1)
   523  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   524  	mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
   525  	err := NewPopulator(
   526  		mockNetworkResolver,
   527  		mockMetadataGce,
   528  		mockStorageClient,
   529  		mockResourceLocationRetriever,
   530  		mockScratchBucketCreator,
   531  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   532  
   533  	assert.Nil(t, err)
   534  	assert.Equal(t, "a_project", project)
   535  	assert.Equal(t, expectedZone, zone)
   536  	assert.Equal(t, expectedRegion, region)
   537  	assert.Equal(t, fmt.Sprintf("gs://%v/", expectedBucketName), scratchBucketGcsPath)
   538  }
   539  
   540  func TestPopulator_PopulateMissingParametersPopulatesStorageLocationWithScratchBucketLocation(t *testing.T) {
   541  	project := "a_project"
   542  	scratchBucketGcsPath := "gs://scratchbucket/scratchpath"
   543  	zone := "us-central1-b"
   544  	region := "us-central1"
   545  	file := "gs://a_bucket/a_file"
   546  	storageLocation := ""
   547  	network := "original-network"
   548  	subnet := "original-subnet"
   549  
   550  	mockCtrl := gomock.NewController(t)
   551  	defer mockCtrl.Finish()
   552  
   553  	mockMetadataGce := mocks.NewMockMetadataGCEInterface(mockCtrl)
   554  	mockScratchBucketCreator := mocks.NewMockScratchBucketCreatorInterface(mockCtrl)
   555  	mockScratchBucketCreator.EXPECT().IsBucketInProject(project, "scratchbucket").Return(true)
   556  	mockResourceLocationRetriever := mocks.NewMockResourceLocationRetrieverInterface(mockCtrl)
   557  	mockStorageClient := mocks.NewMockStorageClientInterface(mockCtrl)
   558  	mockStorageClient.EXPECT().GetBucketAttrs("scratchbucket").Return(&storage.BucketAttrs{Location: region}, nil)
   559  	mockResourceLocationRetriever.EXPECT().GetLargestStorageLocation(region).Return("US")
   560  	mockNetworkResolver := newNoOpNetworkResolver(mockCtrl)
   561  	err := NewPopulator(
   562  		mockNetworkResolver,
   563  		mockMetadataGce,
   564  		mockStorageClient,
   565  		mockResourceLocationRetriever,
   566  		mockScratchBucketCreator,
   567  	).PopulateMissingParameters(&project, "gcloud", &zone, &region, &scratchBucketGcsPath, file, &storageLocation, &network, &subnet)
   568  
   569  	assert.Nil(t, err)
   570  	assert.Equal(t, "US", storageLocation)
   571  }
   572  
   573  func newNoOpNetworkResolver(ctrl *gomock.Controller) NetworkResolver {
   574  	m := mocks.NewMockNetworkResolver(ctrl)
   575  	m.EXPECT().ResolveAndValidateNetworkAndSubnet(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
   576  	return m
   577  }