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

feat(vertexai): add google_vertex_ai_index for Vertex AI Matching Engine #6728

Merged
merged 20 commits into from
Nov 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7333ffa
feat: add google_vertex_ai_index for Vertex AI Matching Engine
shotarok Oct 21, 2022
c4fbed0
fix: increase timeouts to 60 min because 20 wasn't enough for creation
shotarok Oct 21, 2022
efd9549
fix: change coe to make name computed instead of an input
shotarok Oct 22, 2022
b9ba3f6
fix: use costom flatten code to ignore_read a nested property's field
shotarok Oct 22, 2022
36ac11a
fix: add skip_import_test: true to the auto-gen test
shotarok Oct 25, 2022
b2d7df2
feat: add a test with a manually updated ImportStateVerifyIgnore
shotarok Oct 25, 2022
5ece43a
Apply suggestions from code review [ci skip]
shotarok Nov 10, 2022
dae899c
refactor: use ignore_read_extra instead of a manual test
shotarok Nov 13, 2022
b7cb7c2
fix: use an empty object for bruteForceConfig
shotarok Nov 13, 2022
60f02bb
feat: define additional fields to api.yaml
shotarok Nov 13, 2022
f1fbffe
feat: add an example to increase test coverage
shotarok Nov 13, 2022
d55968c
feat: deal with contentsDeltaUri as an updatable field
shotarok Nov 13, 2022
38ddf5e
fix: fix the error because the cosine distance type only supports uni…
shotarok Nov 13, 2022
d764090
feat: remove approximate_neighbors_count from an example with brute_f…
shotarok Nov 13, 2022
64f9a14
test: add a handwritten test for patch
shotarok Nov 16, 2022
6435466
fix: add update_mask: true to use the mask as a url param
shotarok Nov 17, 2022
92a91d1
refactor: put 'input: true' on the fields patch couldn't update
shotarok Nov 21, 2022
cda37c4
feat: use custom pre update code for a nested object
shotarok Nov 21, 2022
15001b3
fix: update the handwritten test accordingly
shotarok Nov 21, 2022
4661d7c
feat: add custom flatten code for is_complete_overwrite
shotarok Nov 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 191 additions & 1 deletion mmv1/products/vertexai/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -433,4 +433,194 @@ objects:
description: |
The disk utilization of the MetadataStore in bytes.
output: true

# Vertex AI Matching Engine Index
- !ruby/object:Api::Resource
name: Index
base_url: projects/{{project}}/locations/{{region}}/indexes
create_url: projects/{{project}}/locations/{{region}}/indexes
self_link: projects/{{project}}/locations/{{region}}/indexes/{{name}}
update_verb: :PATCH
Copy link
Member

Choose a reason for hiding this comment

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

If you set update_mask: true here, that will make it so that only updated fields are included in the PATCH request - it sounds like the PATCH request has to exclude contentsDeltaUri if it wants to update other fields. This should resolve the issue.

You might also need to set update_mask_fields for all nested fields in order for them to be updated properly... it varies from API to API. I'd recommend testing without update_mask_fields first.

Suggested change
update_verb: :PATCH
update_verb: :PATCH
update_mask: true

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you so much for your clear advice! I'll give it a try with a non-nested field first

Copy link
Contributor Author

@shotarok shotarok Nov 17, 2022

Choose a reason for hiding this comment

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

yes, update_mask: true worked for the case updating the description locally. I'll add the other update_mask_fields later,

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 tried to update update_mask_fields for multi-nested objects. Only metadata.{isCompleteOverwrite,contentsDeltaUri} are applied. Therefore, I'll put all fields in update_mask_fields of metadata.

details
diff --git a/mmv1/products/vertexai/terraform.yaml b/mmv1/products/vertexai/terraform.yaml
index 634cc9991..ae1af17c3 100644
--- a/mmv1/products/vertexai/terraform.yaml
+++ b/mmv1/products/vertexai/terraform.yaml
@@ -96,6 +96,20 @@ overrides: !ruby/object:Overrides::ResourceOverrides
         ignore_read: true
       metadata.contentsDeltaUri: !ruby/object:Overrides::Terraform::PropertyOverride
         custom_flatten: templates/terraform/custom_flatten/vertex_ai_index_ignore_contents_delta_uri.go.erb
+      metadata: !ruby/object:Overrides::Terraform::PropertyOverride
+        update_mask_fields:
+          - metadata.contentsDeltaUri
+          - metadata.isCompleteOverwrite
+      metadata.config: !ruby/object:Overrides::Terraform::PropertyOverride
+        update_mask_fields:
+          - metadata.config.dimensions
+          - metadata.config.approximateNeighborsCount
+          - metadata.config.distanceMeasureType
+          - metadata.config.featureNormType
+      metadata.config.algorithmConfig.treeAhConfig: !ruby/object:Overrides::Terraform::PropertyOverride
+        update_mask_fields:
+          - metadata.config.algorithmConfig.treeAhConfig.leafNodeEmbeddingCount
+          - metadata.config.algorithmConfig.treeAhConfig.leafNodesToSearchPercent
   FeaturestoreEntitytype: !ruby/object:Overrides::Terraform::ResourceOverride
     import_format: ["{{%featurestore}}/entityTypes/{{name}}"]
     autogen_async: false
diff --git a/google/resource_vertex_ai_index.go b/google/resource_vertex_ai_index.go
index d61d06d45..3982566cd 100644
--- a/google/resource_vertex_ai_index.go
+++ b/google/resource_vertex_ai_index.go
@@ -21,7 +21,15 @@ import (
 	"strings"
 	"time"
 
+	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
 )
 
 func resourceVertexAIIndex() *schema.Resource {
@@ -494,7 +502,8 @@ func resourceVertexAIIndexUpdate(d *schema.ResourceData, meta interface{}) error
 	}
 
 	if d.HasChange("metadata") {
-		updateMask = append(updateMask, "metadata")
+		updateMask = append(updateMask, "metadata.contentsDeltaUri",
+			"metadata.isCompleteOverwrite")
 	}
 
 	if d.HasChange("labels") {

Copy link
Contributor Author

@shotarok shotarok Nov 20, 2022

Choose a reason for hiding this comment

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

I tested to update metadata.config.dimensions with the above strategy. However, metadata.config.dimensions couldn't be updated. I wonder if we put metadata.contentsDeltaUri in the updateMask query parameter, we can't update the other metadata fields at the same time based on this quote:

Please note that if you set the contentsDeltaUri field when calling IndexService.UpdateIndex, then no other index fields (such as displayName, description, or userLabels) can be also updated as part of the same call.

details
diff --git a/mmv1/products/vertexai/terraform.yaml b/mmv1/products/vertexai/terraform.yaml
index 634cc9991..25bcf7543 100644
--- a/mmv1/products/vertexai/terraform.yaml
+++ b/mmv1/products/vertexai/terraform.yaml
@@ -96,6 +96,16 @@ overrides: !ruby/object:Overrides::ResourceOverrides
         ignore_read: true
       metadata.contentsDeltaUri: !ruby/object:Overrides::Terraform::PropertyOverride
         custom_flatten: templates/terraform/custom_flatten/vertex_ai_index_ignore_contents_delta_uri.go.erb
+      metadata: !ruby/object:Overrides::Terraform::PropertyOverride
+        update_mask_fields:
+          - metadata.contentsDeltaUri
+          - metadata.isCompleteOverwrite
+          - metadata.config.dimensions
+          - metadata.config.approximateNeighborsCount
+          - metadata.config.distanceMeasureType
+          - metadata.config.featureNormType
+          - metadata.config.algorithmConfig.treeAhConfig.leafNodeEmbeddingCount
+          - metadata.config.algorithmConfig.treeAhConfig.leafNodesToSearchPercent
   FeaturestoreEntitytype: !ruby/object:Overrides::Terraform::ResourceOverride
     import_format: ["{{%featurestore}}/entityTypes/{{name}}"]
     autogen_async: false
diff --git a/mmv1/third_party/terraform/tests/resource_vertex_ai_index_test.go b/mmv1/third_party/terraform/tests/resource_vertex_ai_index_test.go
index f2afdc34c..3cc179047 100644
--- a/mmv1/third_party/terraform/tests/resource_vertex_ai_index_test.go
+++ b/mmv1/third_party/terraform/tests/resource_vertex_ai_index_test.go
@@ -119,7 +119,7 @@ resource "google_vertex_ai_index" "index" {
     contents_delta_uri = "gs://${google_storage_bucket.bucket.name}/contents"
     config {
       dimensions = 2
-      approximate_neighbors_count = 150
+      approximate_neighbors_count = 100
       distance_measure_type = "DOT_PRODUCT_DISTANCE"
       algorithm_config {
         tree_ah_config {
---[ REQUEST ]---------------------------------------
PATCH /v1/projects/kouzoh-p-kohama/locations/us-central1/indexes/5511570315064377344?alt=json&updateMask=description%2Cmetadata.contentsDeltaUri%2Cmetadata.isCompleteOverwrite%2Cmetadata.config.dimensions%2Cmetadata.config.approximateNeighborsCount%2Cmetadata.config.distanceMeasureType%2Cmetadata.config.featureNormType%2Cmetadata.config.algorithmConfig.treeAhConfig.leafNodeEmbeddingCount%2Cmetadata.config.algorithmConfig.treeAhConfig.leafNodesToSearchPercent HTTP/1.1^M
Host: us-central1-aiplatform.googleapis.com^M
User-Agent: Terraform/1.1.4 (+https://www.terraform.io) Terraform-Plugin-SDK/2.10.1 terraform-provider-google/acc^M
Content-Length: 496^M
Content-Type: application/json^M
Accept-Encoding: gzip^M
^M
{
 "description": "index for test (updated)",
 "displayName": "tf-test-test-indexjpuf16f07i",
 "indexUpdateMethod": "BATCH_UPDATE",
 "labels": {
  "foo": "bar"
 },
 "metadata": {
  "config": {
   "algorithmConfig": {
    "bruteForceConfig": null,
    "treeAhConfig": {
     "leafNodeEmbeddingCount": 500,
     "leafNodesToSearchPercent": 7
    }
   },
   "approximateNeighborsCount": 100,
   "dimensions": 2,
   "distanceMeasureType": "DOT_PRODUCT_DISTANCE",
   "featureNormType": "NONE"
  },
  "contentsDeltaUri": "gs://kouzoh-p-kohama-tf-test-vertex-ai-index-testjpuf16f07i/contents"
 }
}

-----------------------------------------------------
2022/11/19 23:35:06 [DEBUG] Google API Response Details:
---[ RESPONSE ]--------------------------------------
HTTP/2.0 200 OK^M
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"^M
Cache-Control: private^M
Content-Type: application/json; charset=UTF-8^M
Date: Sun, 20 Nov 2022 07:35:06 GMT^M
Server: ESF^M
Vary: Origin^M
Vary: X-Origin^M
Vary: Referer^M
X-Content-Type-Options: nosniff^M
X-Frame-Options: SAMEORIGIN^M
X-Xss-Protection: 0^M
^M
{
  "name": "projects/415528280434/locations/us-central1/indexes/5511570315064377344/operations/6200380608602963968",
  "metadata": {
    "@type": "type.googleapis.com/google.cloud.aiplatform.v1.UpdateIndexOperationMetadata",
    "genericMetadata": {
      "createTime": "2022-11-20T07:35:06.726769Z",
      "updateTime": "2022-11-20T07:35:06.726769Z"
    }
  },
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.cloud.aiplatform.v1.Index",
    "name": "projects/415528280434/locations/us-central1/indexes/5511570315064377344"
  }
}

-----------------------------------------------------
 | After applying this test step, the plan was not empty.
  | stdout:
  |
  |
  | Terraform used the selected providers to generate the following execution
  | plan. Resource actions are indicated with the following symbols:
  |   ~ update in-place
  |
  | Terraform will perform the following actions:
  |
  |   # google_vertex_ai_index.index will be updated in-place
  |   ~ resource "google_vertex_ai_index" "index" {
  |         id                  = "projects/kouzoh-p-kohama/locations/us-central1/indexes/5511570315064377344"
  |         name                = "5511570315064377344"
  |         # (11 unchanged attributes hidden)
  |
  |       ~ metadata {
  |             # (2 unchanged attributes hidden)
  |
  |           ~ config {
  |               ~ approximate_neighbors_count = 150 -> 100
  |                 # (3 unchanged attributes hidden)
  |
  |                 # (1 unchanged block hidden)
  |             }
  |         }
  |     }
  |
  | Plan: 0 to add, 1 to change, 0 to destroy.

It seems the current update_mask_fields can deal with only top-level nested objects. I should use a custom code with pre_update.

Copy link
Contributor Author

@shotarok shotarok Nov 21, 2022

Choose a reason for hiding this comment

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

I could use granular update masks with the pre_update custom code. However, it turns out the patch can't update all fields in metadata.config and index_update_method.

It seems the patch can update only contentsDelta, isCompleteOverwrite under the metadata in-place. Because even those fields don't appear in a response of get, we need to ignore_read_extra for them, though...

Anyway, I'll push code to use granular update masks. And, I'll put input: true on the unpatchable fields.

update_mask: true
create_verb: :POST
references: !ruby/object:Api::Resource::ReferenceLinks
api: https://cloud.google.com/vertex-ai/docs/reference/rest/v1/projects.locations.indexes/
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 representation of a collection of database items organized in a way that allows for approximate nearest neighbor (a.k.a ANN) algorithms search.
parameters:
- !ruby/object:Api::Type::String
name: region
description: The region of the Metadata Store. eg us-central1
url_param_only: true
input: true
properties:
- !ruby/object:Api::Type::String
name: 'name'
description: The resource name of the Index.
output: true
- !ruby/object:Api::Type::String
name: 'displayName'
description: The display name of the Index. The name can be up to 128 characters long and can consist of any UTF-8 characters.
required: true
- !ruby/object:Api::Type::String
name: 'description'
description: The description of the Index.
# Please take a look at the following links for the original definition:
# https://cloud.google.com/vertex-ai/docs/matching-engine/create-manage-index#create_index-drest
# https://cloud.google.com/vertex-ai/docs/matching-engine/configuring-indexes
- !ruby/object:Api::Type::NestedObject
name: 'metadata'
description: An additional information about the Index
properties:
- !ruby/object:Api::Type::String
name: 'contentsDeltaUri'
description: |-
Allows inserting, updating or deleting the contents of the Matching Engine Index.
The string must be a valid Cloud Storage directory path. If this
field is set when calling IndexService.UpdateIndex, then no other
Index field can be also updated as part of the same call.
The expected structure and format of the files this URI points to is
described at https://cloud.google.com/vertex-ai/docs/matching-engine/using-matching-engine#input-data-format
- !ruby/object:Api::Type::Boolean
name: 'isCompleteOverwrite'
description: |-
If this field is set together with contentsDeltaUri when calling IndexService.UpdateIndex,
then existing content of the Index will be replaced by the data from the contentsDeltaUri.
default_value: false
- !ruby/object:Api::Type::NestedObject
name: 'config'
input: true
description: The configuration of the Matching Engine Index.
properties:
- !ruby/object:Api::Type::Integer
name: 'dimensions'
description: The number of dimensions of the input vectors.
required: true
- !ruby/object:Api::Type::Integer
name: 'approximateNeighborsCount'
description: |-
The default number of neighbors to find via approximate search before exact reordering is
performed. Exact reordering is a procedure where results returned by an
approximate search algorithm are reordered via a more expensive distance computation.
Required if tree-AH algorithm is used.
- !ruby/object:Api::Type::String
name: 'distanceMeasureType'
description: |-
The distance measure used in nearest neighbor search. The value must be one of the followings:
* SQUARED_L2_DISTANCE: Euclidean (L_2) Distance
* L1_DISTANCE: Manhattan (L_1) Distance
* COSINE_DISTANCE: Cosine Distance. Defined as 1 - cosine similarity.
* DOT_PRODUCT_DISTANCE: Dot Product Distance. Defined as a negative of the dot product
default_value: "DOT_PRODUCT_DISTANCE"
- !ruby/object:Api::Type::String
name: 'featureNormType'
description: |-
Type of normalization to be carried out on each vector. The value must be one of the followings:
* UNIT_L2_NORM: Unit L2 normalization type
* NONE: No normalization type is specified.
default_value: "NONE"
- !ruby/object:Api::Type::NestedObject
name: 'algorithmConfig'
description: The configuration with regard to the algorithms used for efficient search.
properties:
- !ruby/object:Api::Type::NestedObject
name: 'treeAhConfig'
exactly_one_of:
- treeAhConfig
- bruteForceConfig
description: |-
Configuration options for using the tree-AH algorithm (Shallow tree + Asymmetric Hashing).
Please refer to this paper for more details: https://arxiv.org/abs/1908.10396
properties:
- !ruby/object:Api::Type::Integer
name: 'leafNodeEmbeddingCount'
description: Number of embeddings on each leaf node. The default value is 1000 if not set.
default_value: 1000
- !ruby/object:Api::Type::Integer
name: 'leafNodesToSearchPercent'
description: |-
The default percentage of leaf nodes that any query may be searched. Must be in
range 1-100, inclusive. The default value is 10 (means 10%) if not set.
default_value: 10
- !ruby/object:Api::Type::NestedObject
name: 'bruteForceConfig'
allow_empty_object: true
send_empty_value: true
properties: []
exactly_one_of:
- treeAhConfig
- bruteForceConfig
description: |-
Configuration options for using brute force search, which simply implements the
standard linear search in the database for each query.
- !ruby/object:Api::Type::String
name: 'metadataSchemaUri'
description: |-
Points to a YAML file stored on Google Cloud Storage describing additional information about the Index, that is specific to it. Unset if the Index does not have any additional information.
output: true
- !ruby/object:Api::Type::Array
name: 'deployedIndexes'
output: true
description: The pointers to DeployedIndexes created from this Index. An Index can be only deleted if all its DeployedIndexes had been undeployed first.
item_type: !ruby/object:Api::Type::NestedObject
properties:
- !ruby/object:Api::Type::String
name: 'indexEndpoint'
output: true
description: A resource name of the IndexEndpoint.
- !ruby/object:Api::Type::String
name: 'deployedIndexId'
output: true
description: The ID of the DeployedIndex in the above IndexEndpoint.
- !ruby/object:Api::Type::String
name: 'etag'
description: Used to perform consistent read-modify-write updates.
output: true
- !ruby/object:Api::Type::KeyValuePairs
name: 'labels'
description: The labels with user-defined metadata to organize your Indexes.
- !ruby/object:Api::Type::String
name: 'createTime'
output: true
description: The timestamp of when the Index 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 Index was last updated in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits.
- !ruby/object:Api::Type::NestedObject
name: 'indexStats'
output: true
description: Stats of the index resource.
properties:
- !ruby/object:Api::Type::String
name: 'vectorsCount'
output: true
description: The number of vectors in the Index.
- !ruby/object:Api::Type::Integer
name: 'shardsCount'
output: true
description: The number of shards in the Index.
- !ruby/object:Api::Type::String
name: 'indexUpdateMethod'
input: true
default_value: BATCH_UPDATE
description: |-
The update method to use with this Index. The value must be the followings. If not set, BATCH_UPDATE will be used by default.
* BATCH_UPDATE: user can call indexes.patch with files on Cloud Storage of datapoints to update.
* STREAM_UPDATE: user can call indexes.upsertDatapoints/DeleteDatapoints to update the Index and the updates will be applied in corresponding DeployedIndexes in nearly real-time.
40 changes: 40 additions & 0 deletions mmv1/products/vertexai/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,46 @@ overrides: !ruby/object:Overrides::ResourceOverrides
name: 'force_destroy'
description: 'If set to true, any EntityTypes and Features for this Featurestore will also be deleted'
default_value: false
Index: !ruby/object:Overrides::Terraform::ResourceOverride
autogen_async: false
timeouts: !ruby/object:Api::Timeouts
insert_minutes: 60
melinath marked this conversation as resolved.
Show resolved Hide resolved
update_minutes: 60
delete_minutes: 60
examples:
- !ruby/object:Provider::Terraform::Examples
name: "vertex_ai_index"
primary_resource_id: "index"
vars:
display_name: "test-index"
bucket_name: "vertex-ai-index-test"
test_env_vars:
project: :PROJECT_NAME
ignore_read_extra:
- metadata.0.contents_delta_uri
- metadata.0.is_complete_overwrite
- !ruby/object:Provider::Terraform::Examples
name: "vertex_ai_index_streaming"
primary_resource_id: "index"
vars:
display_name: "test-index"
bucket_name: "vertex-ai-index-test"
test_env_vars:
project: :PROJECT_NAME
ignore_read_extra:
- metadata.0.contents_delta_uri
- metadata.0.is_complete_overwrite
properties:
name: !ruby/object:Overrides::Terraform::PropertyOverride
custom_flatten: templates/terraform/custom_flatten/name_from_self_link.erb
etag: !ruby/object:Overrides::Terraform::PropertyOverride
ignore_read: true
metadata.contentsDeltaUri: !ruby/object:Overrides::Terraform::PropertyOverride
custom_flatten: templates/terraform/custom_flatten/vertex_ai_index_ignore_contents_delta_uri.go.erb
metadata.isCompleteOverwrite: !ruby/object:Overrides::Terraform::PropertyOverride
custom_flatten: templates/terraform/custom_flatten/vertex_ai_index_ignore_is_complete_overwrite.go.erb
custom_code: !ruby/object:Provider::Terraform::CustomCode
pre_update: templates/terraform/pre_update/vertex_ai_index.go.erb
FeaturestoreEntitytype: !ruby/object:Overrides::Terraform::ResourceOverride
import_format: ["{{%featurestore}}/entityTypes/{{name}}"]
autogen_async: false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<%# The license inside this block applies to this file.
# Copyright 2022 Google Inc.
# 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.
-%>
func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *Config) interface{} {
// We want to ignore read on this field, but cannot because it is nested
return d.Get("metadata.0.contents_delta_uri")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<%# The license inside this block applies to this file.
# Copyright 2022 Google Inc.
# 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.
-%>
func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *Config) interface{} {
// We want to ignore read on this field, but cannot because it is nested
return d.Get("metadata.0.is_complete_overwrite")
}
40 changes: 40 additions & 0 deletions mmv1/templates/terraform/examples/vertex_ai_index.tf.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
resource "google_storage_bucket" "bucket" {
name = "<%= ctx[:test_env_vars]['project'] %>-<%= ctx[:vars]['bucket_name'] %>" # Every bucket name must be globally unique
location = "us-central1"
uniform_bucket_level_access = true
}

# The sample data comes from the following link:
# https://cloud.google.com/vertex-ai/docs/matching-engine/filtering#specify-namespaces-tokens
resource "google_storage_bucket_object" "data" {
name = "contents/data.json"
bucket = google_storage_bucket.bucket.name
content = <<EOF
{"id": "42", "embedding": [0.5, 1.0], "restricts": [{"namespace": "class", "allow": ["cat", "pet"]},{"namespace": "category", "allow": ["feline"]}]}
{"id": "43", "embedding": [0.6, 1.0], "restricts": [{"namespace": "class", "allow": ["dog", "pet"]},{"namespace": "category", "allow": ["canine"]}]}
EOF
}

resource "google_vertex_ai_index" "index" {
labels = {
foo = "bar"
}
region = "us-central1"
display_name = "<%= ctx[:vars]['display_name'] %>"
description = "index for test"
metadata {
contents_delta_uri = "gs://${google_storage_bucket.bucket.name}/contents"
config {
dimensions = 2
approximate_neighbors_count = 150
distance_measure_type = "DOT_PRODUCT_DISTANCE"
algorithm_config {
tree_ah_config {
leaf_node_embedding_count = 500
leaf_nodes_to_search_percent = 7
}
}
}
}
index_update_method = "BATCH_UPDATE"
}
37 changes: 37 additions & 0 deletions mmv1/templates/terraform/examples/vertex_ai_index_streaming.tf.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
resource "google_storage_bucket" "bucket" {
name = "<%= ctx[:test_env_vars]['project'] %>-<%= ctx[:vars]['bucket_name'] %>" # Every bucket name must be globally unique
location = "us-central1"
uniform_bucket_level_access = true
}

# The sample data comes from the following link:
# https://cloud.google.com/vertex-ai/docs/matching-engine/filtering#specify-namespaces-tokens
resource "google_storage_bucket_object" "data" {
name = "contents/data.json"
bucket = google_storage_bucket.bucket.name
content = <<EOF
{"id": "42", "embedding": [0.5, 1.0], "restricts": [{"namespace": "class", "allow": ["cat", "pet"]},{"namespace": "category", "allow": ["feline"]}]}
{"id": "43", "embedding": [0.6, 1.0], "restricts": [{"namespace": "class", "allow": ["dog", "pet"]},{"namespace": "category", "allow": ["canine"]}]}
EOF
}

resource "google_vertex_ai_index" "index" {
labels = {
foo = "bar"
}
region = "us-central1"
display_name = "<%= ctx[:vars]['display_name'] %>"
description = "index for test"
metadata {
contents_delta_uri = "gs://${google_storage_bucket.bucket.name}/contents"
config {
dimensions = 2
distance_measure_type = "COSINE_DISTANCE"
feature_norm_type = "UNIT_L2_NORM"
algorithm_config {
brute_force_config {}
}
}
}
index_update_method = "STREAM_UPDATE"
}
22 changes: 22 additions & 0 deletions mmv1/templates/terraform/pre_update/vertex_ai_index.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
newUpdateMask := []string{}

if d.HasChange("metadata.0.contents_delta_uri") {
// Use the current value of isCompleteOverwrite when updating contentsDeltaUri
newUpdateMask = append(newUpdateMask, "metadata.contentsDeltaUri")
newUpdateMask = append(newUpdateMask, "metadata.isCompleteOverwrite")
}

for _, mask := range updateMask {
// Use granular update masks instead of 'metadata' to avoid the following error:
// 'If `contents_delta_gcs_uri` is set as part of `index.metadata`, then no other Index fields can be also updated as part of the same update call.'
if mask == "metadata" {
continue
}
newUpdateMask = append(newUpdateMask, mask)
}

// Refreshing updateMask after adding extra schema entries
url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(newUpdateMask, ",")})
if err != nil {
return err
}
Loading