diff --git a/infra/blueprint-test/pkg/cai/cai.go b/infra/blueprint-test/pkg/cai/cai.go new file mode 100644 index 000000000000..953c79783608 --- /dev/null +++ b/infra/blueprint-test/pkg/cai/cai.go @@ -0,0 +1,82 @@ +/** + * Copyright 2024 Google LLC + * + * 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 cai provides a set of helpers to interact with Cloud Asset Inventory +package cai + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud" + "github.com/tidwall/gjson" +) + +type CmdCfg struct { + sleep int // minutes to sleep prior to CAI retreval. default: 2 + assetType string // asset type to retrieve. default: all + args []string // arguments to pass to call +} + +type cmdOption func(*CmdCfg) + +// newCmdConfig sets defaults and options +func newCmdConfig(opts ...cmdOption) (*CmdCfg) { + caiOpts := &CmdCfg{ + sleep: 2, + assetType: "", + args: nil, + } + + for _, opt := range opts { + opt(caiOpts) + } + + if caiOpts.assetType != "" { + caiOpts.args = []string{"--asset-types", caiOpts.assetType} + } + caiOpts.args = append(caiOpts.args, "--content-type", "resource") + + return caiOpts +} + +// Set custom sleep minutes +func WithSleep(sleep int) cmdOption { + return func(f *CmdCfg) { + f.sleep = sleep + } +} + +// Set asset type +func WithAssetType(assetType string) cmdOption { + return func(f *CmdCfg) { + f.assetType = assetType + } +} + +// GetProjectResources returns the cloud asset inventory resources for a project as a gjson.Result +func GetProjectResources(t testing.TB, project string, opts ...cmdOption) gjson.Result { + caiOpts := newCmdConfig(opts...) + + // Cloud Asset Inventory offers best-effort data freshness. + t.Logf("Sleeping for %d minutes before retrieving Cloud Asset Inventory...", caiOpts.sleep) + time.Sleep(time.Duration(caiOpts.sleep) * time.Minute) + + cmd := fmt.Sprintf("asset list --project %s", project) + return gcloud.Runf(t, strings.Join(append([]string{cmd}, caiOpts.args...), " ")) +} diff --git a/infra/blueprint-test/test/cai_test.go b/infra/blueprint-test/test/cai_test.go new file mode 100644 index 000000000000..da22124c7c18 --- /dev/null +++ b/infra/blueprint-test/test/cai_test.go @@ -0,0 +1,65 @@ +/** + * Copyright 2024 Google LLC + * + * 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 test + +import ( + "encoding/base64" + "fmt" + "testing" + + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/cai" + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud" + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" + "github.com/stretchr/testify/assert" +) + +func TestGetProjectResources(t *testing.T) { + tests := []struct { + name string + assetType string + wantKeyPath string + wantVal string + }{ + {name: "all", assetType: "", wantKeyPath: "resource.data.nodeConfig.imageType", wantVal: "COS_CONTAINERD"}, + {name: "cluster", assetType: "container.googleapis.com/Cluster", wantKeyPath: "resource.data.nodeConfig.imageType", wantVal: "COS_CONTAINERD"}, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + assert := assert.New(t) + + tfBlueprint := tft.NewTFBlueprintTest(t, + tft.WithTFDir("setup"), + ) + + clusterResourceName := fmt.Sprintf("//container.googleapis.com/projects/%s/locations/%s/clusters/%s", + tfBlueprint.GetStringOutput("project_id"), + tfBlueprint.GetStringOutput("cluster_region"), + tfBlueprint.GetStringOutput("cluster_name"), + ) + + // Use the test SA for cai call + credDec, _ := base64.StdEncoding.DecodeString(tfBlueprint.GetStringOutput("sa_key")) + gcloud.ActivateCredsAndEnvVars(t, string(credDec)) + + cai := cai.GetProjectResources(t, tfBlueprint.GetStringOutput("project_id"), cai.WithAssetType(tt.assetType)) + assert.Equal(tt.wantVal, cai.Get("#(name=\"" + clusterResourceName + "\")." + tt.wantKeyPath).String()) + }) + } +} diff --git a/infra/blueprint-test/test/setup/main.tf b/infra/blueprint-test/test/setup/main.tf index ebeccc0eba60..27dcdff20e7a 100644 --- a/infra/blueprint-test/test/setup/main.tf +++ b/infra/blueprint-test/test/setup/main.tf @@ -21,13 +21,15 @@ locals { "roles/iam.serviceAccountUser", "roles/vpcaccess.admin", "roles/serviceusage.serviceUsageAdmin", - "roles/container.admin" + "roles/container.admin", + "roles/cloudasset.viewer", + "roles/serviceusage.serviceUsageConsumer" ] } module "project" { source = "terraform-google-modules/project-factory/google" - version = "~> 14.0" + version = "~> 17.0" name = "ci-bptest" random_project_id = "true" @@ -35,12 +37,16 @@ module "project" { folder_id = var.folder_id billing_account = var.billing_account + default_service_account = "DEPRIVILEGE" + deletion_policy = "DELETE" + activate_apis = [ "cloudresourcemanager.googleapis.com", "compute.googleapis.com", "serviceusage.googleapis.com", "vpcaccess.googleapis.com", - "container.googleapis.com" + "container.googleapis.com", + "cloudasset.googleapis.com" ] } @@ -64,6 +70,6 @@ resource "google_service_account_key" "key" { module "kubernetes-engine_example_simple_autopilot_public" { source = "terraform-google-modules/kubernetes-engine/google//examples/simple_autopilot_public" - version = "~> 30.0" + version = "~> 34.0" project_id = module.project.project_id }