Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for google_vertex_ai_tensorboard resource #6759

Merged
84 changes: 84 additions & 0 deletions mmv1/products/vertexai/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions mmv1/products/vertexai/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@

--- !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"
skip_import_test: true
primary_resource_id: "tensorboard"
vars:
display_name: "terraform"
- !ruby/object:Provider::Terraform::Examples
name: "vertex_ai_tensorboard_full"
skip_import_test: true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do these tests use skip_import_test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because we need to use ImportStateVerifyIgnore: []string{"region", "project"} instead of ImportStateVerifyIgnore: []string{"region"}. There is a difference in the name field (projects/{project}/locations/{location}/tensorboards/{tensorboard}) of imported resources where instead of project_id we are getting project_number. Hence, I've used skip_import_test

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that should be fine, since name is an output-only field.

You can use ignore_read_extra here & above instead of skip_import_test to just ignore the one additional field:

Suggested change
skip_import_test: true
ignore_read_extra:
- project

If the API returns project numbers - will users get a permadiff if they try to set the project field using a project id?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, User won't face state drift if the project field is set to project id. It's just that import is forming the name field using the replaceVars function which takes the project_id and this gets changed when the resource is successfully imported.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated examples to include ignore_read_extra for the tests.

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'
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}}'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
config := meta.(*Config)
if err := parseImportId([]string{
"projects/(?P<project>[^/]+)/locations/(?P<region>[^/]+)/tensorboards/(?P<name>[^/]+)",
"(?P<project>[^/]+)/(?P<region>[^/]+)/(?P<name>[^/]+)",
"(?P<region>[^/]+)/(?P<name>[^/]+)",
"(?P<name>[^/]+)",
}, 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
Original file line number Diff line number Diff line change
@@ -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"
}
Original file line number Diff line number Diff line change
@@ -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" {}
Original file line number Diff line number Diff line change
@@ -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)
}