@@ -12,6 +12,9 @@ import (
12
12
"testing"
13
13
"time"
14
14
// For beta tests only
15
+ "github.com/hashicorp/terraform-provider-google-beta/google-beta/services/kms"
16
+ tpgservicusage "github.com/hashicorp/terraform-provider-google-beta/google-beta/services/serviceusage"
17
+ resourceManagerV3 "google.golang.org/api/cloudresourcemanager/v3"
15
18
"net/http"
16
19
17
20
"github.com/hashicorp/terraform-provider-google-beta/google-beta/envvar"
@@ -35,6 +38,11 @@ import (
35
38
)
36
39
37
40
var SharedKeyRing = "tftest-shared-keyring-1"
41
+
42
+ var DefaultKeyHandleName = "eed58b7b-20ad-4da8-ad85-ba78a0d5ab87"
43
+ var DefaultKeyHandleResourceType = "compute.googleapis.com/Disk"
44
+ var CloudKmsSrviceName = "cloudkms.googleapis.com"
45
+
38
46
var SharedCryptoKey = map [string ]string {
39
47
"ENCRYPT_DECRYPT" : "tftest-shared-key-1" ,
40
48
"ASYMMETRIC_SIGN" : "tftest-shared-sign-key-1" ,
@@ -76,6 +84,237 @@ func BootstrapKMSKeyWithPurposeInLocation(t *testing.T, purpose, locationID stri
76
84
return BootstrapKMSKeyWithPurposeInLocationAndName (t , purpose , locationID , SharedCryptoKey [purpose ])
77
85
}
78
86
87
+ type BootstrappedKMSAutokey struct {
88
+ * cloudkms.KeyHandle
89
+ }
90
+
91
+ func BootstrapKMSAutokeyKeyHandle (t * testing.T ) BootstrappedKMSAutokey {
92
+ return BootstrapKMSAutokeyKeyHandleWithLocation (t , "global" )
93
+ }
94
+
95
+ func BootstrapKMSAutokeyKeyHandleWithLocation (t * testing.T , locationID string ) BootstrappedKMSAutokey {
96
+ config := BootstrapConfig (t )
97
+ if config == nil {
98
+ return BootstrappedKMSAutokey {
99
+ & cloudkms.KeyHandle {},
100
+ }
101
+ }
102
+
103
+ autokeyFolder , kmsProject , resourceProject := setupAutokeyTestResources (t , config )
104
+
105
+ // Enable autokey on autokey test folder
106
+ kmsClient := config .NewKmsClient (config .UserAgent )
107
+ autokeyConfigID := fmt .Sprintf ("%s/autokeyConfig" , autokeyFolder .Name )
108
+ _ , err := kmsClient .Folders .UpdateAutokeyConfig (autokeyConfigID , & cloudkms.AutokeyConfig {
109
+ KeyProject : fmt .Sprintf ("projects/%s" , kmsProject .ProjectId ),
110
+ }).UpdateMask ("keyProject" ).Do ()
111
+ if err != nil {
112
+ t .Errorf ("unable to bootstrap KMS keyHandle. Cannot enable autokey on autokey test folder: %s" , err )
113
+ }
114
+
115
+ keyHandleParent := fmt .Sprintf ("projects/%s/locations/%s" , resourceProject .ProjectId , locationID )
116
+ keyHandleName := fmt .Sprintf ("%s/keyHandles/%s" , keyHandleParent , DefaultKeyHandleName )
117
+
118
+ // Get or Create the hard coded keyHandle for testing
119
+ keyHandle , err := kmsClient .Projects .Locations .KeyHandles .Get (keyHandleName ).Do ()
120
+
121
+ if err != nil {
122
+ if transport_tpg .IsGoogleApiErrorWithCode (err , 404 ) {
123
+ newKeyHandle := cloudkms.KeyHandle {
124
+ ResourceTypeSelector : DefaultKeyHandleResourceType ,
125
+ }
126
+
127
+ keyHandleOp , err := kmsClient .Projects .Locations .KeyHandles .Create (keyHandleParent , & newKeyHandle ).KeyHandleId (DefaultKeyHandleName ).Do ()
128
+ if err != nil {
129
+ t .Errorf ("unable to bootstrap KMS keyHandle. Cannot create new KeyHandle: %s" , err )
130
+ }
131
+
132
+ opAsMap , err := tpgresource .ConvertToMap (keyHandleOp )
133
+ if err != nil {
134
+ t .Errorf ("unable to bootstrap KMS keyHandle. Cannot get operation map: %s" , err )
135
+ }
136
+
137
+ var response map [string ]interface {}
138
+ err = kms .KMSOperationWaitTimeWithResponse (config , opAsMap , & response , resourceProject .ProjectId , "creating keyHandle" , config .UserAgent , time .Duration (5 )* time .Minute )
139
+ if err != nil {
140
+ t .Errorf ("unable to bootstrap KMS keyHandle. Cannot wait for create keyhandle operation: %s" , err )
141
+ }
142
+ keyHandle = & cloudkms.KeyHandle {
143
+ Name : response ["name" ].(string ),
144
+ KmsKey : response ["kmsKey" ].(string ),
145
+ ResourceTypeSelector : response ["resourceTypeSelector" ].(string ),
146
+ }
147
+ } else {
148
+ t .Errorf ("unable to bootstrap KMS keyHandle. Cannot call KeyHandle service: %s" , err )
149
+ }
150
+ }
151
+
152
+ if keyHandle == nil {
153
+ t .Fatalf ("unable to bootstrap KMS keyHandle. KeyHandle is nil!" )
154
+ }
155
+
156
+ return BootstrappedKMSAutokey {
157
+ keyHandle ,
158
+ }
159
+ }
160
+
161
+ func setupAutokeyTestResources (t * testing.T , config * transport_tpg.Config ) (* resourceManagerV3.Folder , * cloudresourcemanager.Project , * cloudresourcemanager.Project ) {
162
+ projectIDSuffix := strings .Replace (envvar .GetTestProjectFromEnv (), "ci-test-project-" , "" , 1 )
163
+ defaultAutokeyTestFolderName := fmt .Sprintf ("autokeytest-%s-fd" , projectIDSuffix )
164
+ defaultAutokeyTestKmsProject := fmt .Sprintf ("test-kms-%s-prj" , projectIDSuffix )
165
+ defaultAutokeyTestResourceProject := fmt .Sprintf ("test-res-%s-prj" , projectIDSuffix )
166
+
167
+ curUserEmail , err := transport_tpg .GetCurrentUserEmail (config , config .UserAgent )
168
+ if err != nil {
169
+ t .Errorf ("unable to bootstrap KMS keyHandle. Cannot get current usr: %s" , err )
170
+ }
171
+ // create a folder to configure autokey config and resource folder
172
+ autokeyFolder := BootstrapFolder (t , defaultAutokeyTestFolderName )
173
+ parent := & cloudresourcemanager.ResourceId {
174
+ Type : "folder" ,
175
+ Id : strings .Split (autokeyFolder .Name , "/" )[1 ],
176
+ }
177
+ // create and setup kms project for hosting keyring and keys for autokey
178
+ kmsProject := BootstrapProjectWithParent (t , defaultAutokeyTestKmsProject , envvar .GetTestBillingAccountFromEnv (t ), parent , []string {CloudKmsSrviceName })
179
+ kmsProjectID := fmt .Sprintf ("projects/%s" , kmsProject .ProjectId )
180
+ kmsSAEmail , err := GenerateCloudKmsServiceIdentity (config , fmt .Sprintf ("%v" , kmsProject .ProjectNumber ))
181
+ if err != nil {
182
+ t .Errorf ("unable to bootstrap KMS keyHandle. Cannot create cloudkms service identity: %s" , err )
183
+ }
184
+ err = addFolderBinding2 (config .NewResourceManagerV3Client (config .UserAgent ), autokeyFolder .Name , fmt .Sprintf ("user:%s" , curUserEmail ), []string {"roles/cloudkms.admin" })
185
+ if err != nil {
186
+ t .Errorf ("unable to bootstrap KMS keyHandle. Cannot assign cloudkms.admin role to current user on autokey test folder: %s" , err )
187
+ }
188
+ err = addProjectBinding (config .NewResourceManagerV3Client (config .UserAgent ), kmsProjectID , fmt .Sprintf ("user:%s" , curUserEmail ), []string {"roles/resourcemanager.projectIamAdmin" , "roles/cloudkms.admin" })
189
+ if err != nil {
190
+ t .Errorf ("unable to bootstrap KMS keyHandle. Cannot assign cloudkms.admin and projectIamAdmin role to current user on kms project: %s" , err )
191
+ }
192
+ err = addProjectBinding (config .NewResourceManagerV3Client (config .UserAgent ), kmsProjectID , fmt .Sprintf ("serviceAccount:%s" , kmsSAEmail ), []string {"roles/cloudkms.admin" })
193
+ if err != nil {
194
+ t .Errorf ("unable to bootstrap KMS keyHandle. Cannot assign cloudkms.admin role to cloudkms service identity on kms project: %s" , err )
195
+ }
196
+
197
+ // create and setup resource folder to host keyhandle
198
+ resourceProject := BootstrapProjectWithParent (t , defaultAutokeyTestResourceProject , envvar .GetTestBillingAccountFromEnv (t ), parent , []string {})
199
+ return autokeyFolder , kmsProject , resourceProject
200
+ }
201
+
202
+ // GenerateCloudKmsServiceIdentity generates cloud kms service identity within a project
203
+ func GenerateCloudKmsServiceIdentity (config * transport_tpg.Config , projectNum string ) (string , error ) {
204
+ url := fmt .Sprintf ("https://serviceusage.googleapis.com/v1beta1/projects/%s/services/%s:generateServiceIdentity" , projectNum , CloudKmsSrviceName )
205
+
206
+ res , err := transport_tpg .SendRequest (transport_tpg.SendRequestOptions {
207
+ Config : config ,
208
+ Method : "POST" ,
209
+ Project : projectNum ,
210
+ RawURL : url ,
211
+ UserAgent : config .UserAgent ,
212
+ Timeout : time .Minute * 4 ,
213
+ })
214
+ if err != nil {
215
+ return "" , fmt .Errorf ("error creating cloudkms service identity: %s" , err )
216
+ }
217
+
218
+ var opRes map [string ]interface {}
219
+ err = tpgservicusage .ServiceUsageOperationWaitTimeWithResponse (
220
+ config , res , & opRes , projectNum , "Creating cloudkms service identity" , config .UserAgent ,
221
+ time .Minute * 4 )
222
+ if err != nil {
223
+ return "" , err
224
+ }
225
+ return fmt .
Sprintf (
"service-%[email protected] " ,
projectNum ),
nil
226
+ }
227
+
228
+ func addProjectBinding (crmService * resourceManagerV3.Service , projectID string , member string , roles []string ) error {
229
+ return addBinding (crmService , "project" , projectID , member , roles )
230
+ }
231
+
232
+ func addFolderBinding2 (crmService * resourceManagerV3.Service , folderID string , member string , roles []string ) error {
233
+ return addBinding (crmService , "folder" , folderID , member , roles )
234
+ }
235
+
236
+ // addBinding adds the member to the project's IAM policy
237
+ func addBinding (crmService * resourceManagerV3.Service , resourceType string , resourceID string , member string , roles []string ) error {
238
+
239
+ policy , err := getPolicy (crmService , resourceType , resourceID )
240
+ if err != nil {
241
+ return err
242
+ }
243
+
244
+ // Find the policy binding for role. Only one binding can have the role.
245
+ var binding * resourceManagerV3.Binding
246
+ for _ , role := range roles {
247
+ for _ , b := range policy .Bindings {
248
+ if b .Role == role {
249
+ binding = b
250
+ break
251
+ }
252
+ }
253
+
254
+ if binding != nil {
255
+ // If the binding exists, adds the member to the binding
256
+ binding .Members = append (binding .Members , member )
257
+ } else {
258
+ // If the binding does not exist, adds a new binding to the policy
259
+ binding = & resourceManagerV3.Binding {
260
+ Role : role ,
261
+ Members : []string {member },
262
+ }
263
+ policy .Bindings = append (policy .Bindings , binding )
264
+ }
265
+ }
266
+ setPolicy (crmService , resourceType , resourceID , policy )
267
+ return nil
268
+ }
269
+
270
+ // getPolicy gets the IAM policy on input resourceID
271
+ // resourceType can be "project" or "folder"
272
+ func getPolicy (crmService * resourceManagerV3.Service , resourceType string , resourceID string ) (* resourceManagerV3.Policy , error ) {
273
+
274
+ ctx := context .Background ()
275
+
276
+ ctx , cancel := context .WithTimeout (ctx , time .Second * 10 )
277
+ defer cancel ()
278
+ request := new (resourceManagerV3.GetIamPolicyRequest )
279
+ var policy * resourceManagerV3.Policy
280
+ var err error
281
+ if resourceType == "project" {
282
+ policy , err = crmService .Projects .GetIamPolicy (resourceID , request ).Do ()
283
+ } else if resourceType == "folder" {
284
+ policy , err = crmService .Folders .GetIamPolicy (resourceID , request ).Do ()
285
+ } else {
286
+ return nil , fmt .Errorf ("invalid resourceType, supported values: project or folder" )
287
+ }
288
+ if err != nil {
289
+ return nil , fmt .Errorf ("error getting iam policy: %s" , err )
290
+ }
291
+ return policy , nil
292
+ }
293
+
294
+ // setPolicy sets the IAM policy on input resourceID
295
+ // resourceType can be "project" or "folder"
296
+ func setPolicy (crmService * resourceManagerV3.Service , resourceType string , resourceID string , policy * resourceManagerV3.Policy ) error {
297
+
298
+ ctx := context .Background ()
299
+
300
+ ctx , cancel := context .WithTimeout (ctx , time .Second * 10 )
301
+ defer cancel ()
302
+ request := new (resourceManagerV3.SetIamPolicyRequest )
303
+ request .Policy = policy
304
+ var err error
305
+ if resourceType == "project" {
306
+ _ , err = crmService .Projects .SetIamPolicy (resourceID , request ).Do ()
307
+ } else if resourceType == "folder" {
308
+ _ , err = crmService .Folders .SetIamPolicy (resourceID , request ).Do ()
309
+ } else {
310
+ return fmt .Errorf ("invalid resourceType, supported values: project or folder" )
311
+ }
312
+ if err != nil {
313
+ return fmt .Errorf ("error setting iam policy: %s" , err )
314
+ }
315
+ return nil
316
+ }
317
+
79
318
func BootstrapKMSKeyWithPurposeInLocationAndName (t * testing.T , purpose , locationID , keyShortName string ) BootstrappedKMS {
80
319
config := BootstrapConfig (t )
81
320
if config == nil {
@@ -630,37 +869,89 @@ func BootstrapServicePerimeterProjects(t *testing.T, desiredProjects int) []*clo
630
869
return projects
631
870
}
632
871
872
+ // BootstrapFolder creates or get a folder having a input folderDisplayName within a TestOrgEnv
873
+ func BootstrapFolder (t * testing.T , folderDisplayName string ) * resourceManagerV3.Folder {
874
+ config := BootstrapConfig (t )
875
+ if config == nil {
876
+ return nil
877
+ }
878
+
879
+ crmClient := config .NewResourceManagerV3Client (config .UserAgent )
880
+ searchQuery := fmt .Sprintf ("displayName=%s" , folderDisplayName )
881
+ folderSearchResp , err := crmClient .Folders .Search ().Query (searchQuery ).Do ()
882
+ if err != nil {
883
+ t .Fatalf ("error searching for folder with displayName: %s" , folderDisplayName )
884
+ }
885
+ var folder * resourceManagerV3.Folder
886
+ if len (folderSearchResp .Folders ) == 0 {
887
+ op , err := crmClient .Folders .Create (& resourceManagerV3.Folder {
888
+ DisplayName : folderDisplayName ,
889
+ Parent : fmt .Sprintf ("organizations/%s" , envvar .GetTestOrgFromEnv (t )),
890
+ }).Do ()
891
+ if err != nil {
892
+ t .Fatalf ("error bootstrapping test folder: %s" , err )
893
+ }
894
+
895
+ opAsMap , err := tpgresource .ConvertToMap (op )
896
+ if err != nil {
897
+ t .Fatalf ("error converting folder operation map: %s" , err )
898
+ }
899
+ var responseMap map [string ]interface {}
900
+ err = resourcemanager .ResourceManagerOperationWaitTimeWithResponse (config , opAsMap , & responseMap , "creating folder" , config .UserAgent , 4 * time .Minute )
901
+ if err != nil {
902
+ t .Fatalf ("error waiting for create folder operation: %s" , err )
903
+ }
904
+ folder , err = crmClient .Folders .Get (responseMap ["name" ].(string )).Do ()
905
+ if err != nil {
906
+ t .Fatalf ("error getting folder: %s" , err )
907
+ }
908
+ } else {
909
+ folder = folderSearchResp .Folders [0 ]
910
+ }
911
+
912
+ if folder .State == "DELETE_REQUESTED" {
913
+ _ , err := crmClient .Folders .Undelete (folder .Name , & resourceManagerV3.UndeleteFolderRequest {}).Do ()
914
+ if err != nil {
915
+ t .Fatalf ("error undeleting folder: %s" , err )
916
+ }
917
+ }
918
+ return folder
919
+ }
920
+
633
921
// BootstrapProject will create or get a project named
634
922
// "<projectIDPrefix><projectIDSuffix>" that will persist across test runs,
635
923
// where projectIDSuffix is based off of getTestProjectFromEnv(). The reason
636
924
// for the naming is to isolate bootstrapped projects by test environment.
637
925
// Given the existing projects being used by our team, the prefix provided to
638
926
// this function can be no longer than 18 characters.
639
927
func BootstrapProject (t * testing.T , projectIDPrefix , billingAccount string , services []string ) * cloudresourcemanager.Project {
640
- config := BootstrapConfig (t )
641
- if config == nil {
642
- return nil
928
+ org := envvar .GetTestOrgFromEnv (t )
929
+ parent := & cloudresourcemanager.ResourceId {
930
+ Type : "organization" ,
931
+ Id : org ,
643
932
}
644
-
645
933
projectIDSuffix := strings .Replace (envvar .GetTestProjectFromEnv (), "ci-test-project-" , "" , 1 )
646
934
projectID := projectIDPrefix + projectIDSuffix
647
935
936
+ return BootstrapProjectWithParent (t , projectID , billingAccount , parent , services )
937
+ }
938
+
939
+ func BootstrapProjectWithParent (t * testing.T , projectID string , billingAccount string , parent * cloudresourcemanager.ResourceId , services []string ) * cloudresourcemanager.Project {
940
+ config := BootstrapConfig (t )
941
+ if config == nil {
942
+ return nil
943
+ }
648
944
crmClient := config .NewResourceManagerClient (config .UserAgent )
649
945
650
946
project , err := crmClient .Projects .Get (projectID ).Do ()
651
947
if err != nil {
652
948
if ! transport_tpg .IsGoogleApiErrorWithCode (err , 403 ) {
653
949
t .Fatalf ("Error getting bootstrapped project: %s" , err )
654
950
}
655
- org := envvar .GetTestOrgFromEnv (t )
656
-
657
951
op , err := crmClient .Projects .Create (& cloudresourcemanager.Project {
658
952
ProjectId : projectID ,
659
953
Name : "Bootstrapped Test Project" ,
660
- Parent : & cloudresourcemanager.ResourceId {
661
- Type : "organization" ,
662
- Id : org ,
663
- },
954
+ Parent : parent ,
664
955
}).Do ()
665
956
if err != nil {
666
957
t .Fatalf ("Error creating bootstrapped test project: %s" , err )
0 commit comments