diff --git a/mmv1/products/vertexai/api.yaml b/mmv1/products/vertexai/api.yaml index 740147d8755a..3dcdf9786d5e 100644 --- a/mmv1/products/vertexai/api.yaml +++ b/mmv1/products/vertexai/api.yaml @@ -24,6 +24,90 @@ versions: scopes: - https://www.googleapis.com/auth/cloud-platform objects: +# Vertex AI Tensorboards + - !ruby/object:Api::Resource + name: Tensorboard + base_url: projects/{{project}}/locations/{{region}}/tensorboards + self_link: '{{name}}' + update_verb: :PATCH + update_mask: true + references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Official Documentation': + 'https://cloud.google.com/vertex-ai/docs' + api: 'https://cloud.google.com/vertex-ai/docs/reference/rest/v1/projects.locations.tensorboards' + async: !ruby/object:Api::OpAsync + operation: !ruby/object:Api::OpAsync::Operation + path: 'name' + base_url: '{{op_id}}' + wait_ms: 1000 + result: !ruby/object:Api::OpAsync::Result + path: 'response' + resource_inside_response: true + status: !ruby/object:Api::OpAsync::Status + path: 'done' + complete: True + allowed: + - True + - False + error: !ruby/object:Api::OpAsync::Error + path: 'error' + message: 'message' + description: |- + Tensorboard is a physical database that stores users' training metrics. A default Tensorboard is provided in each region of a GCP project. If needed users can also create extra Tensorboards in their projects. + parameters: + - !ruby/object:Api::Type::String + name: region + description: The region of the tensorboard. eg us-central1 + url_param_only: true + input: true + properties: + - !ruby/object:Api::Type::String + name: 'name' + description: Name of the Tensorboard. + output: true + - !ruby/object:Api::Type::String + name: 'displayName' + required: true + description: User provided name of this Tensorboard. + - !ruby/object:Api::Type::String + name: 'description' + description: Description of this Tensorboard. + - !ruby/object:Api::Type::NestedObject + name: 'encryptionSpec' + input: true + description: | + Customer-managed encryption key spec for a Tensorboard. If set, this Tensorboard and all sub-resources of this Tensorboard will be secured by this key. + properties: + - !ruby/object:Api::Type::String + name: 'kmsKeyName' + required: true + description: | + The Cloud KMS resource identifier of the customer managed encryption key used to protect a resource. + Has the form: projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key. The key needs to be in the same region as where the resource is created. + input: true + - !ruby/object:Api::Type::String + name: 'blobStoragePathPrefix' + description: Consumer project Cloud Storage path prefix used to store blob data, which can either be a bucket or directory. Does not end with a '/'. + output: true + - !ruby/object:Api::Type::String + name: 'runCount' + description: The number of Runs stored in this Tensorboard. + output: true + - !ruby/object:Api::Type::String + name: 'createTime' + output: true + description: | + The timestamp of when the Tensorboard was created in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits. + - !ruby/object:Api::Type::String + name: 'updateTime' + output: true + description: | + The timestamp of when the Tensorboard was last updated in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits. + - !ruby/object:Api::Type::KeyValuePairs + name: 'labels' + description: | + The labels with user-defined metadata to organize your Tensorboards. # Vertex AI Datasets - !ruby/object:Api::Resource name: Dataset diff --git a/mmv1/products/vertexai/terraform.yaml b/mmv1/products/vertexai/terraform.yaml index 0460a85568a0..87628b71435b 100644 --- a/mmv1/products/vertexai/terraform.yaml +++ b/mmv1/products/vertexai/terraform.yaml @@ -13,6 +13,31 @@ --- !ruby/object:Provider::Terraform::Config overrides: !ruby/object:Overrides::ResourceOverrides + Tensorboard: !ruby/object:Overrides::Terraform::ResourceOverride + skip_sweeper: true + examples: + - !ruby/object:Provider::Terraform::Examples + name: "vertex_ai_tensorboard" + primary_resource_id: "tensorboard" + vars: + display_name: "terraform" + ignore_read_extra: + - "project" + - !ruby/object:Provider::Terraform::Examples + name: "vertex_ai_tensorboard_full" + primary_resource_id: "tensorboard" + vars: + display_name: "terraform" + kms_key_name: "kms-name" + test_vars_overrides: + kms_key_name: 'BootstrapKMSKeyInLocation(t, "us-central1").CryptoKey.Name' + ignore_read_extra: + - "project" + properties: + region: !ruby/object:Overrides::Terraform::PropertyOverride + default_from_api: true + custom_code: !ruby/object:Provider::Terraform::CustomCode + custom_import: templates/terraform/custom_import/vertex_ai_tensorboard_import.go.erb Dataset: !ruby/object:Overrides::Terraform::ResourceOverride autogen_async: false id_format: '{{name}}' diff --git a/mmv1/templates/terraform/custom_import/vertex_ai_tensorboard_import.go.erb b/mmv1/templates/terraform/custom_import/vertex_ai_tensorboard_import.go.erb new file mode 100644 index 000000000000..22caf4d61589 --- /dev/null +++ b/mmv1/templates/terraform/custom_import/vertex_ai_tensorboard_import.go.erb @@ -0,0 +1,22 @@ + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/locations/(?P[^/]+)/tensorboards/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVars(d, config, "projects/{{project}}/locations/{{region}}/tensorboards/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + if err := d.Set("name", id); err != nil { + return nil, fmt.Errorf("Error setting name for import: %s", err) + } + + return []*schema.ResourceData{d}, nil \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/vertex_ai_tensorboard.tf.erb b/mmv1/templates/terraform/examples/vertex_ai_tensorboard.tf.erb new file mode 100644 index 000000000000..1552516b3d6b --- /dev/null +++ b/mmv1/templates/terraform/examples/vertex_ai_tensorboard.tf.erb @@ -0,0 +1,9 @@ +resource "google_vertex_ai_tensorboard" "<%= ctx[:primary_resource_id] %>" { + display_name = "<%= ctx[:vars]['display_name'] %>" + description = "sample description" + labels = { + "key1" : "value1", + "key2" : "value2" + } + region = "us-central1" +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/vertex_ai_tensorboard_full.tf.erb b/mmv1/templates/terraform/examples/vertex_ai_tensorboard_full.tf.erb new file mode 100644 index 000000000000..ab59cee9ed7c --- /dev/null +++ b/mmv1/templates/terraform/examples/vertex_ai_tensorboard_full.tf.erb @@ -0,0 +1,21 @@ +resource "google_vertex_ai_tensorboard" "<%= ctx[:primary_resource_id] %>" { + display_name = "<%= ctx[:vars]['display_name'] %>" + description = "sample description" + labels = { + "key1" : "value1", + "key2" : "value2" + } + region = "us-central1" + encryption_spec { + kms_key_name = "<%= ctx[:vars]['kms_key_name'] %>" + } + depends_on = [google_kms_crypto_key_iam_member.crypto_key] +} + +resource "google_kms_crypto_key_iam_member" "crypto_key" { + crypto_key_id = "<%= ctx[:vars]['kms_key_name'] %>" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-aiplatform.iam.gserviceaccount.com" +} + +data "google_project" "project" {} diff --git a/mmv1/third_party/terraform/tests/resource_vertex_ai_tensorboard_test.go b/mmv1/third_party/terraform/tests/resource_vertex_ai_tensorboard_test.go new file mode 100644 index 000000000000..fd294810aea7 --- /dev/null +++ b/mmv1/third_party/terraform/tests/resource_vertex_ai_tensorboard_test.go @@ -0,0 +1,89 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccVertexAITensorboard_Update(t *testing.T) { + t.Parallel() + + random_suffix := "tf-test-" + randString(t, 10) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVertexAITensorboardDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccVertexAITensorboard_Update(random_suffix, random_suffix, random_suffix, random_suffix), + }, + { + ResourceName: "google_vertex_ai_tensorboard.tensorboard", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region", "project"}, + }, + { + Config: testAccVertexAITensorboard_Update(random_suffix+"new", random_suffix, random_suffix, random_suffix), + }, + { + ResourceName: "google_vertex_ai_tensorboard.tensorboard", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region", "project"}, + }, + { + Config: testAccVertexAITensorboard_Update(random_suffix+"new", random_suffix+"new", random_suffix, random_suffix), + }, + { + ResourceName: "google_vertex_ai_tensorboard.tensorboard", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region", "project"}, + }, + { + Config: testAccVertexAITensorboard_Update(random_suffix+"new", random_suffix+"new", random_suffix+"new", random_suffix), + }, + { + ResourceName: "google_vertex_ai_tensorboard.tensorboard", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region", "project"}, + }, + { + Config: testAccVertexAITensorboard_Update(random_suffix+"new", random_suffix+"new", random_suffix+"new", random_suffix+"new"), + }, + { + ResourceName: "google_vertex_ai_tensorboard.tensorboard", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region", "project"}, + }, + { + Config: testAccVertexAITensorboard_Update(random_suffix, random_suffix, random_suffix, random_suffix), + }, + { + ResourceName: "google_vertex_ai_tensorboard.tensorboard", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region", "project"}, + }, + }, + }) +} + +func testAccVertexAITensorboard_Update(displayName, description, labelKey, labelVal string) string { + return fmt.Sprintf(` +resource "google_vertex_ai_tensorboard" "tensorboard" { + display_name = "%s" + description = "%s" + labels = { + "%s" : "%s", + } + region = "us-central1" +} +`, displayName, description, labelKey, labelVal) +}