diff --git a/mmv1/api/async.rb b/mmv1/api/async.rb index f68f1291f4af..7c018f5d78e5 100644 --- a/mmv1/api/async.rb +++ b/mmv1/api/async.rb @@ -66,6 +66,9 @@ class OpAsync < Async attr_reader :result attr_reader :status attr_reader :error + # If true, include project as an argument to OperationWaitTime. + # It is intended for resources that calculate project/region from a selflink field + attr_reader :include_project # The list of methods where operations are used. attr_reader :actions @@ -78,6 +81,7 @@ def validate check :status, type: Status, required: true check :error, type: Error, required: true check :actions, default: %w[create delete update], type: ::Array, item_type: ::String + check :include_project, type: :boolean, default: false end # The main implementation of Operation, diff --git a/mmv1/products/vertexai/api.yaml b/mmv1/products/vertexai/api.yaml index 55ba55db5db9..5126d83f0b79 100644 --- a/mmv1/products/vertexai/api.yaml +++ b/mmv1/products/vertexai/api.yaml @@ -75,12 +75,12 @@ objects: name: 'createTime' output: true description: | - The timestamp of when the workflow was created in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits. + The timestamp of when the dataset 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 workflow was last updated in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits. + The timestamp of when the dataset 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: | @@ -102,4 +102,169 @@ objects: required: true input: true description: | - Points to a YAML file stored on Google Cloud Storage describing additional information about the Dataset. The schema is defined as an OpenAPI 3.0.2 Schema Object. The schema files that can be used here are found in gs://google-cloud-aiplatform/schema/dataset/metadata/. \ No newline at end of file + Points to a YAML file stored on Google Cloud Storage describing additional information about the Dataset. The schema is defined as an OpenAPI 3.0.2 Schema Object. The schema files that can be used here are found in gs://google-cloud-aiplatform/schema/dataset/metadata/. +# Vertex AI Featurestores + - !ruby/object:Api::Resource + name: Featurestore + base_url: projects/{{project}}/locations/{{region}}/featurestores + create_url: projects/{{project}}/locations/{{region}}/featurestores?featurestoreId={{name}} + self_link: 'projects/{{project}}/locations/{{region}}/featurestores/{{name}}' + min_version: beta + 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/v1beta1/projects.locations.featurestores' + 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: |- + A collection of DataItems and Annotations on them. + parameters: + - !ruby/object:Api::Type::String + name: region + description: The region of the dataset. eg us-central1 + url_param_only: true + input: true + properties: + - !ruby/object:Api::Type::String + name: 'name' + description: The name of the Featurestore. This value may be up to 60 characters, and valid characters are [a-z0-9_]. The first character cannot be a number. + input: true + url_param_only: true + pattern: projects/{{project}}/locations/{{region}}/featurestores/{{name}} + - !ruby/object:Api::Type::String + name: 'etag' + description: Used to perform consistent read-modify-write updates. + output: true + - !ruby/object:Api::Type::String + name: 'createTime' + output: true + description: | + The timestamp of when the featurestore 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 featurestore 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: | + A set of key/value label pairs to assign to this Featurestore. + - !ruby/object:Api::Type::NestedObject + name: 'onlineServingConfig' + description: | + Config for online serving resources. + properties: + - !ruby/object:Api::Type::Integer + name: 'fixedNodeCount' + required: true + description: | + The number of nodes for each cluster. The number of nodes will not scale automatically but can be scaled manually by providing different values when updating. +# Vertex AI Featurestore Entity Type + - !ruby/object:Api::Resource + name: FeaturestoreEntitytype + base_url: '{{featurestore}}/entityTypes' + create_url: '{{featurestore}}/entityTypes?entityTypeId={{name}}' + self_link: '{{featurestore}}/entityTypes/{{name}}' + min_version: beta + 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/v1beta1/projects.locations.featurestores.entityTypes' + 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' + include_project: true + description: |- + An entity type is a type of object in a system that needs to be modeled and have stored information about. For example, driver is an entity type, and driver0 is an instance of an entity type driver. + parameters: + - !ruby/object:Api::Type::String + name: featurestore + description: The name of the Featurestore to use, in the format projects/{project}/locations/{location}/featurestores/{featurestore}. + url_param_only: true + input: true + required: true + properties: + - !ruby/object:Api::Type::String + name: 'name' + description: The name of the EntityType. This value may be up to 60 characters, and valid characters are [a-z0-9_]. The first character cannot be a number. + input: true + url_param_only: true + pattern: '{featurestore}}/entityTypes/{{name}}' + - !ruby/object:Api::Type::String + name: 'etag' + description: Used to perform consistent read-modify-write updates. + output: true + - !ruby/object:Api::Type::String + name: 'createTime' + output: true + description: | + The timestamp of when the featurestore 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 featurestore 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: | + A set of key/value label pairs to assign to this EntityType. + - !ruby/object:Api::Type::NestedObject + name: 'monitoringConfig' + description: | + The default monitoring configuration for all Features under this EntityType. + + If this is populated with [FeaturestoreMonitoringConfig.monitoring_interval] specified, snapshot analysis monitoring is enabled. Otherwise, snapshot analysis monitoring is disabled. + properties: + - !ruby/object:Api::Type::NestedObject + name: 'snapshotAnalysis' + description: | + Configuration of how features in Featurestore are monitored. + properties: + - !ruby/object:Api::Type::Boolean + name: 'disabled' + default_value: false + description: | + The monitoring schedule for snapshot analysis. For EntityType-level config: unset / disabled = true indicates disabled by default for Features under it; otherwise by default enable snapshot analysis monitoring with monitoringInterval for Features under it. + - !ruby/object:Api::Type::String + name: 'monitoringInterval' + description: | + Configuration of the snapshot analysis based monitoring pipeline running interval. The value is rolled up to full day. + + A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s". + + diff --git a/mmv1/products/vertexai/terraform.yaml b/mmv1/products/vertexai/terraform.yaml index 751410b8207d..744c42a277e8 100644 --- a/mmv1/products/vertexai/terraform.yaml +++ b/mmv1/products/vertexai/terraform.yaml @@ -34,7 +34,56 @@ overrides: !ruby/object:Overrides::ResourceOverrides default_from_api: true region: !ruby/object:Overrides::Terraform::PropertyOverride default_from_api: true - + Featurestore: !ruby/object:Overrides::Terraform::ResourceOverride + autogen_async: false + skip_sweeper: true + timeouts: !ruby/object:Api::Timeouts + insert_minutes: 6 + update_minutes: 6 + delete_minutes: 10 + examples: + - !ruby/object:Provider::Terraform::Examples + name: "vertex_ai_featurestore" + primary_resource_id: "dataset" + vars: + name: "terraform" + properties: + region: !ruby/object:Overrides::Terraform::PropertyOverride + default_from_api: true + etag: !ruby/object:Overrides::Terraform::PropertyOverride + ignore_read: true + name: !ruby/object:Overrides::Terraform::PropertyOverride + custom_flatten: templates/terraform/custom_flatten/name_from_self_link.erb + custom_code: !ruby/object:Provider::Terraform::CustomCode + pre_delete: templates/terraform/pre_delete/vertex_ai_force_delete.go.erb + virtual_fields: + - !ruby/object:Api::Type::Boolean + name: 'force_destroy' + description: 'If set to true, any EntityTypes and Features for this Featurestore will also be deleted' + default_value: false + FeaturestoreEntitytype: !ruby/object:Overrides::Terraform::ResourceOverride + import_format: ["{{featurestore}}/entityTypes/{{name}}"] + autogen_async: false + skip_sweeper: true + timeouts: !ruby/object:Api::Timeouts + insert_minutes: 6 + update_minutes: 6 + delete_minutes: 10 + examples: + - !ruby/object:Provider::Terraform::Examples + name: "vertex_ai_featurestore_entitytype" + primary_resource_id: "entity" + vars: + name: "terraform" + properties: + etag: !ruby/object:Overrides::Terraform::PropertyOverride + ignore_read: true + name: !ruby/object:Overrides::Terraform::PropertyOverride + custom_flatten: templates/terraform/custom_flatten/name_from_self_link.erb + custom_code: !ruby/object:Provider::Terraform::CustomCode + pre_create: templates/terraform/constants/vertex_ai_featurestore_entitytype.go.erb + pre_update: templates/terraform/constants/vertex_ai_featurestore_entitytype.go.erb + pre_delete: templates/terraform/constants/vertex_ai_featurestore_entitytype.go.erb # This is for copying files over files: !ruby/object:Provider::Config::Files # These files have templating (ERB) code that will be run. diff --git a/mmv1/templates/terraform/constants/vertex_ai_featurestore_entitytype.go.erb b/mmv1/templates/terraform/constants/vertex_ai_featurestore_entitytype.go.erb new file mode 100644 index 000000000000..bc77cf0eba2e --- /dev/null +++ b/mmv1/templates/terraform/constants/vertex_ai_featurestore_entitytype.go.erb @@ -0,0 +1,9 @@ +if v, ok := d.GetOk("featurestore"); ok { + re := regexp.MustCompile("projects/([a-zA-Z0-9-]*)/(?:locations|regions)/([a-zA-Z0-9-]*)") + switch { + case re.MatchString(v.(string)): + if res := re.FindStringSubmatch(v.(string)); len(res) == 3 && res[1] != "" { + project = res[1] + } + } +} diff --git a/mmv1/templates/terraform/examples/vertex_ai_featurestore.tf.erb b/mmv1/templates/terraform/examples/vertex_ai_featurestore.tf.erb new file mode 100644 index 000000000000..60a1fdad2051 --- /dev/null +++ b/mmv1/templates/terraform/examples/vertex_ai_featurestore.tf.erb @@ -0,0 +1,11 @@ +resource "google_vertex_ai_featurestore" "featurestore" { + provider = google-beta + name = "<%= ctx[:vars]['name'] %>" + labels = { + foo = "bar" + } + region = "us-central1" + online_serving_config { + fixed_node_count = 2 + } +} diff --git a/mmv1/templates/terraform/examples/vertex_ai_featurestore_entitytype.tf.erb b/mmv1/templates/terraform/examples/vertex_ai_featurestore_entitytype.tf.erb new file mode 100644 index 000000000000..5f8510d65db0 --- /dev/null +++ b/mmv1/templates/terraform/examples/vertex_ai_featurestore_entitytype.tf.erb @@ -0,0 +1,26 @@ +resource "google_vertex_ai_featurestore" "featurestore" { + provider = google-beta + name = "<%= ctx[:vars]['name'] %>" + labels = { + foo = "bar" + } + region = "us-central1" + online_serving_config { + fixed_node_count = 2 + } +} + +resource "google_vertex_ai_featurestore_entitytype" "featurestore" { + provider = google-beta + name = "<%= ctx[:vars]['name'] %>" + labels = { + foo = "bar" + } + featurestore = google_vertex_ai_featurestore.featurestore.id + monitoring_config { + snapshot_analysis { + disabled = false + monitoring_interval = "86400s" + } + } +} diff --git a/mmv1/templates/terraform/pre_delete/vertex_ai_force_delete.go.erb b/mmv1/templates/terraform/pre_delete/vertex_ai_force_delete.go.erb new file mode 100644 index 000000000000..adef3161c851 --- /dev/null +++ b/mmv1/templates/terraform/pre_delete/vertex_ai_force_delete.go.erb @@ -0,0 +1,7 @@ + +if v, ok := d.GetOk("force_destroy"); ok { + url, err = addQueryParams(url, map[string]string{"force": v.(string)}) + if err != nil { + return err + } +} diff --git a/mmv1/templates/terraform/resource.erb b/mmv1/templates/terraform/resource.erb index 3646081dde6c..267edae8b3c8 100644 --- a/mmv1/templates/terraform/resource.erb +++ b/mmv1/templates/terraform/resource.erb @@ -51,6 +51,12 @@ import ( timeouts ||= Api::Timeouts.new -%> +<% if object.async&.is_a? Api::OpAsync -%> + <% if object.async.include_project -%> +var project string + <% end -%> +<% end -%> + func resource<%= resource_name -%>() *schema.Resource { return &schema.Resource{ Create: resource<%= resource_name -%>Create, @@ -261,7 +267,7 @@ func resource<%= resource_name -%>Create(d *schema.ResourceData, meta interface{ // identity fields and d.Id() before read var opRes map[string]interface{} err = <%= client_name_camel -%>OperationWaitTimeWithResponse( - config, res, &opRes, <% if has_project -%> project, <% end -%> "Creating <%= object.name -%>", userAgent, + config, res, &opRes, <% if has_project || object.async.include_project -%> project, <% end -%> "Creating <%= object.name -%>", userAgent, d.Timeout(schema.TimeoutCreate)) if err != nil { <% if object.custom_code.post_create_failure -%> @@ -311,7 +317,7 @@ func resource<%= resource_name -%>Create(d *schema.ResourceData, meta interface{ <% else -%> err = <%= client_name_camel -%>OperationWaitTime( - config, res, <% if has_project -%> project, <% end -%> "Creating <%= object.name -%>", userAgent, + config, res, <% if has_project || object.async.include_project -%> project, <% end -%> "Creating <%= object.name -%>", userAgent, d.Timeout(schema.TimeoutCreate)) if err != nil { @@ -660,7 +666,7 @@ if len(updateMask) > 0 { <% if object.async&.allow?('update') -%> <% if object.async.is_a? Api::OpAsync -%> err = <%= client_name_camel -%>OperationWaitTime( - config, res, <% if has_project -%> project, <% end -%> "Updating <%= object.name -%>", userAgent, + config, res, <% if has_project || object.async.include_project -%> project, <% end -%> "Updating <%= object.name -%>", userAgent, d.Timeout(schema.TimeoutUpdate)) if err != nil { @@ -792,7 +798,7 @@ if <%= props.map { |prop| "d.HasChange(\"#{prop.name.underscore}\")" }.join ' || <% if object.async&.allow?('update') -%> <% if object.async.is_a? Api::OpAsync-%> err = <%= client_name_camel -%>OperationWaitTime( - config, res, <% if has_project -%> project, <% end -%> "Updating <%= object.name -%>", userAgent, + config, res, <% if has_project || object.async.include_project -%> project, <% end -%> "Updating <%= object.name -%>", userAgent, d.Timeout(schema.TimeoutUpdate)) if err != nil { return err @@ -909,7 +915,7 @@ func resource<%= resource_name -%>Delete(d *schema.ResourceData, meta interface{ } <% else -%> err = <%= client_name_camel -%>OperationWaitTime( - config, res, <% if has_project -%> project, <% end -%> "Deleting <%= object.name -%>", userAgent, + config, res, <% if has_project || object.async.include_project-%> project, <% end -%> "Deleting <%= object.name -%>", userAgent, d.Timeout(schema.TimeoutDelete)) if err != nil {