diff --git a/.changelog/9632.txt b/.changelog/9632.txt new file mode 100644 index 0000000000..edde62c469 --- /dev/null +++ b/.changelog/9632.txt @@ -0,0 +1,9 @@ +```release-note:enhancement +vertexai: added `force_destroy` field to `google_vertex_ai_feature_online_store` resource +``` +```release-note:enhancement +vertexai: added `optimized`, `dedicated_serving_endpoint` and `embedding_management` fields to `google_vertex_ai_feature_online_store` resource (beta) +``` +```release-note:bug +vertexai: changed `region` field from required to optional in `google_vertex_ai_feature_online_store` resource +``` diff --git a/google-beta/services/vertexai/resource_vertex_ai_feature_online_store.go b/google-beta/services/vertexai/resource_vertex_ai_feature_online_store.go index 5cbbfcb721..35e32a3b26 100644 --- a/google-beta/services/vertexai/resource_vertex_ai_feature_online_store.go +++ b/google-beta/services/vertexai/resource_vertex_ai_feature_online_store.go @@ -58,13 +58,7 @@ func ResourceVertexAIFeatureOnlineStore() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - Description: `The resource name of the Feature Online Store.`, - }, - "region": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: `The region of feature online store. eg us-central1`, + Description: `The resource name of the Feature Online Store. This value may be up to 60 characters, and valid characters are [a-z0-9_]. The first character cannot be a number.`, }, "bigtable": { Type: schema.TypeList, @@ -101,6 +95,68 @@ func ResourceVertexAIFeatureOnlineStore() *schema.Resource { }, }, }, + ExactlyOneOf: []string{"bigtable", "optimized"}, + }, + "dedicated_serving_endpoint": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `The dedicated serving endpoint for this FeatureOnlineStore, which is different from common vertex service endpoint. Only need to set when you choose Optimized storage type or enable EmbeddingManagement. Will use public endpoint by default.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "private_service_connect_config": { + Type: schema.TypeList, + Optional: true, + Description: `Private service connect config.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable_private_service_connect": { + Type: schema.TypeBool, + Required: true, + Description: `If set to true, customers will use private service connection to send request. Otherwise, the connection will set to public endpoint.`, + }, + "project_allowlist": { + Type: schema.TypeList, + Optional: true, + Description: `A list of Projects from which the forwarding rule will target the service attachment.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "public_endpoint_domain_name": { + Type: schema.TypeString, + Computed: true, + Description: `Domain name to use for this FeatureOnlineStore`, + }, + "service_attachment": { + Type: schema.TypeString, + Computed: true, + Description: `Name of the service attachment resource. Applicable only if private service connect is enabled and after FeatureViewSync is created.`, + }, + }, + }, + }, + "embedding_management": { + Type: schema.TypeList, + Optional: true, + Description: `The settings for embedding management in FeatureOnlineStore. Embedding management can only be used with BigTable.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Description: `Enable embedding management.`, + }, + }, + }, + ConflictsWith: []string{"optimized"}, }, "labels": { Type: schema.TypeMap, @@ -111,6 +167,24 @@ func ResourceVertexAIFeatureOnlineStore() *schema.Resource { Please refer to the field 'effective_labels' for all of the labels present on the resource.`, Elem: &schema.Schema{Type: schema.TypeString}, }, + "optimized": { + Type: schema.TypeList, + Optional: true, + Description: `Settings for the Optimized store that will be created to serve featureValues for all FeatureViews under this FeatureOnlineStore`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{}, + }, + ConflictsWith: []string{"embedding_management"}, + ExactlyOneOf: []string{"bigtable", "optimized"}, + }, + "region": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + Description: `The region of feature online store. eg us-central1`, + }, "create_time": { Type: schema.TypeString, Computed: true, @@ -144,6 +218,12 @@ Please refer to the field 'effective_labels' for all of the labels present on th Computed: true, Description: `The timestamp of when the feature online store was last updated in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits.`, }, + "force_destroy": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: `If set to true, any FeatureViews and Features for this FeatureOnlineStore will also be deleted.`, + }, "project": { Type: schema.TypeString, Optional: true, @@ -163,18 +243,30 @@ func resourceVertexAIFeatureOnlineStoreCreate(d *schema.ResourceData, meta inter } obj := make(map[string]interface{}) - nameProp, err := expandVertexAIFeatureOnlineStoreName(d.Get("name"), d, config) - if err != nil { - return err - } else if v, ok := d.GetOkExists("name"); !tpgresource.IsEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { - obj["name"] = nameProp - } bigtableProp, err := expandVertexAIFeatureOnlineStoreBigtable(d.Get("bigtable"), d, config) if err != nil { return err } else if v, ok := d.GetOkExists("bigtable"); !tpgresource.IsEmptyValue(reflect.ValueOf(bigtableProp)) && (ok || !reflect.DeepEqual(v, bigtableProp)) { obj["bigtable"] = bigtableProp } + optimizedProp, err := expandVertexAIFeatureOnlineStoreOptimized(d.Get("optimized"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("optimized"); ok || !reflect.DeepEqual(v, optimizedProp) { + obj["optimized"] = optimizedProp + } + dedicatedServingEndpointProp, err := expandVertexAIFeatureOnlineStoreDedicatedServingEndpoint(d.Get("dedicated_serving_endpoint"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("dedicated_serving_endpoint"); !tpgresource.IsEmptyValue(reflect.ValueOf(dedicatedServingEndpointProp)) && (ok || !reflect.DeepEqual(v, dedicatedServingEndpointProp)) { + obj["dedicatedServingEndpoint"] = dedicatedServingEndpointProp + } + embeddingManagementProp, err := expandVertexAIFeatureOnlineStoreEmbeddingManagement(d.Get("embedding_management"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("embedding_management"); !tpgresource.IsEmptyValue(reflect.ValueOf(embeddingManagementProp)) && (ok || !reflect.DeepEqual(v, embeddingManagementProp)) { + obj["embeddingManagement"] = embeddingManagementProp + } labelsProp, err := expandVertexAIFeatureOnlineStoreEffectiveLabels(d.Get("effective_labels"), d, config) if err != nil { return err @@ -234,10 +326,6 @@ func resourceVertexAIFeatureOnlineStoreCreate(d *schema.ResourceData, meta inter return fmt.Errorf("Error waiting to create FeatureOnlineStore: %s", err) } - if err := d.Set("name", flattenVertexAIFeatureOnlineStoreName(opRes["name"], d, config)); err != nil { - return err - } - // This may have caused the ID to update - update it if so. id, err = tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{region}}/featureOnlineStores/{{name}}") if err != nil { @@ -286,13 +374,16 @@ func resourceVertexAIFeatureOnlineStoreRead(d *schema.ResourceData, meta interfa return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("VertexAIFeatureOnlineStore %q", d.Id())) } + // Explicitly set virtual fields to default values if unset + if _, ok := d.GetOkExists("force_destroy"); !ok { + if err := d.Set("force_destroy", false); err != nil { + return fmt.Errorf("Error setting force_destroy: %s", err) + } + } if err := d.Set("project", project); err != nil { return fmt.Errorf("Error reading FeatureOnlineStore: %s", err) } - if err := d.Set("name", flattenVertexAIFeatureOnlineStoreName(res["name"], d, config)); err != nil { - return fmt.Errorf("Error reading FeatureOnlineStore: %s", err) - } if err := d.Set("create_time", flattenVertexAIFeatureOnlineStoreCreateTime(res["createTime"], d, config)); err != nil { return fmt.Errorf("Error reading FeatureOnlineStore: %s", err) } @@ -308,6 +399,15 @@ func resourceVertexAIFeatureOnlineStoreRead(d *schema.ResourceData, meta interfa if err := d.Set("bigtable", flattenVertexAIFeatureOnlineStoreBigtable(res["bigtable"], d, config)); err != nil { return fmt.Errorf("Error reading FeatureOnlineStore: %s", err) } + if err := d.Set("optimized", flattenVertexAIFeatureOnlineStoreOptimized(res["optimized"], d, config)); err != nil { + return fmt.Errorf("Error reading FeatureOnlineStore: %s", err) + } + if err := d.Set("dedicated_serving_endpoint", flattenVertexAIFeatureOnlineStoreDedicatedServingEndpoint(res["dedicatedServingEndpoint"], d, config)); err != nil { + return fmt.Errorf("Error reading FeatureOnlineStore: %s", err) + } + if err := d.Set("embedding_management", flattenVertexAIFeatureOnlineStoreEmbeddingManagement(res["embeddingManagement"], d, config)); err != nil { + return fmt.Errorf("Error reading FeatureOnlineStore: %s", err) + } if err := d.Set("terraform_labels", flattenVertexAIFeatureOnlineStoreTerraformLabels(res["labels"], d, config)); err != nil { return fmt.Errorf("Error reading FeatureOnlineStore: %s", err) } @@ -340,6 +440,24 @@ func resourceVertexAIFeatureOnlineStoreUpdate(d *schema.ResourceData, meta inter } else if v, ok := d.GetOkExists("bigtable"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, bigtableProp)) { obj["bigtable"] = bigtableProp } + optimizedProp, err := expandVertexAIFeatureOnlineStoreOptimized(d.Get("optimized"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("optimized"); ok || !reflect.DeepEqual(v, optimizedProp) { + obj["optimized"] = optimizedProp + } + dedicatedServingEndpointProp, err := expandVertexAIFeatureOnlineStoreDedicatedServingEndpoint(d.Get("dedicated_serving_endpoint"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("dedicated_serving_endpoint"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, dedicatedServingEndpointProp)) { + obj["dedicatedServingEndpoint"] = dedicatedServingEndpointProp + } + embeddingManagementProp, err := expandVertexAIFeatureOnlineStoreEmbeddingManagement(d.Get("embedding_management"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("embedding_management"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, embeddingManagementProp)) { + obj["embeddingManagement"] = embeddingManagementProp + } labelsProp, err := expandVertexAIFeatureOnlineStoreEffectiveLabels(d.Get("effective_labels"), d, config) if err != nil { return err @@ -359,6 +477,18 @@ func resourceVertexAIFeatureOnlineStoreUpdate(d *schema.ResourceData, meta inter updateMask = append(updateMask, "bigtable") } + if d.HasChange("optimized") { + updateMask = append(updateMask, "optimized") + } + + if d.HasChange("dedicated_serving_endpoint") { + updateMask = append(updateMask, "dedicatedServingEndpoint") + } + + if d.HasChange("embedding_management") { + updateMask = append(updateMask, "embeddingManagement") + } + if d.HasChange("effective_labels") { updateMask = append(updateMask, "labels") } @@ -425,6 +555,13 @@ func resourceVertexAIFeatureOnlineStoreDelete(d *schema.ResourceData, meta inter } var obj map[string]interface{} + + if v, ok := d.GetOk("force_destroy"); ok { + url, err = transport_tpg.AddQueryParams(url, map[string]string{"force": fmt.Sprintf("%v", v)}) + if err != nil { + return err + } + } log.Printf("[DEBUG] Deleting FeatureOnlineStore %q", d.Id()) // err == nil indicates that the billing_project value was found @@ -475,14 +612,12 @@ func resourceVertexAIFeatureOnlineStoreImport(d *schema.ResourceData, meta inter } d.SetId(id) - return []*schema.ResourceData{d}, nil -} - -func flattenVertexAIFeatureOnlineStoreName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { - if v == nil { - return v + // Explicitly set virtual fields to default values on import + if err := d.Set("force_destroy", false); err != nil { + return nil, fmt.Errorf("Error setting force_destroy: %s", err) } - return tpgresource.NameFromSelfLinkStateFunc(v) + + return []*schema.ResourceData{d}, nil } func flattenVertexAIFeatureOnlineStoreCreateTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { @@ -593,6 +728,79 @@ func flattenVertexAIFeatureOnlineStoreBigtableAutoScalingCpuUtilizationTarget(v return v // let terraform core handle it otherwise } +func flattenVertexAIFeatureOnlineStoreOptimized(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + transformed := make(map[string]interface{}) + return []interface{}{transformed} +} + +func flattenVertexAIFeatureOnlineStoreDedicatedServingEndpoint(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["public_endpoint_domain_name"] = + flattenVertexAIFeatureOnlineStoreDedicatedServingEndpointPublicEndpointDomainName(original["publicEndpointDomainName"], d, config) + transformed["service_attachment"] = + flattenVertexAIFeatureOnlineStoreDedicatedServingEndpointServiceAttachment(original["serviceAttachment"], d, config) + transformed["private_service_connect_config"] = + flattenVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfig(original["privateServiceConnectConfig"], d, config) + return []interface{}{transformed} +} +func flattenVertexAIFeatureOnlineStoreDedicatedServingEndpointPublicEndpointDomainName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenVertexAIFeatureOnlineStoreDedicatedServingEndpointServiceAttachment(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["enable_private_service_connect"] = + flattenVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(original["enablePrivateServiceConnect"], d, config) + transformed["project_allowlist"] = + flattenVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigProjectAllowlist(original["projectAllowlist"], d, config) + return []interface{}{transformed} +} +func flattenVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigProjectAllowlist(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenVertexAIFeatureOnlineStoreEmbeddingManagement(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["enabled"] = + flattenVertexAIFeatureOnlineStoreEmbeddingManagementEnabled(original["enabled"], d, config) + return []interface{}{transformed} +} +func flattenVertexAIFeatureOnlineStoreEmbeddingManagementEnabled(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + func flattenVertexAIFeatureOnlineStoreTerraformLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { if v == nil { return v @@ -612,10 +820,6 @@ func flattenVertexAIFeatureOnlineStoreEffectiveLabels(v interface{}, d *schema.R return v } -func expandVertexAIFeatureOnlineStoreName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { - return v, nil -} - func expandVertexAIFeatureOnlineStoreBigtable(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { l := v.([]interface{}) if len(l) == 0 || l[0] == nil { @@ -680,6 +884,119 @@ func expandVertexAIFeatureOnlineStoreBigtableAutoScalingCpuUtilizationTarget(v i return v, nil } +func expandVertexAIFeatureOnlineStoreOptimized(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 { + return nil, nil + } + + if l[0] == nil { + transformed := make(map[string]interface{}) + return transformed, nil + } + transformed := make(map[string]interface{}) + + return transformed, nil +} + +func expandVertexAIFeatureOnlineStoreDedicatedServingEndpoint(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedPublicEndpointDomainName, err := expandVertexAIFeatureOnlineStoreDedicatedServingEndpointPublicEndpointDomainName(original["public_endpoint_domain_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPublicEndpointDomainName); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["publicEndpointDomainName"] = transformedPublicEndpointDomainName + } + + transformedServiceAttachment, err := expandVertexAIFeatureOnlineStoreDedicatedServingEndpointServiceAttachment(original["service_attachment"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedServiceAttachment); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["serviceAttachment"] = transformedServiceAttachment + } + + transformedPrivateServiceConnectConfig, err := expandVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfig(original["private_service_connect_config"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrivateServiceConnectConfig); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["privateServiceConnectConfig"] = transformedPrivateServiceConnectConfig + } + + return transformed, nil +} + +func expandVertexAIFeatureOnlineStoreDedicatedServingEndpointPublicEndpointDomainName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandVertexAIFeatureOnlineStoreDedicatedServingEndpointServiceAttachment(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnablePrivateServiceConnect, err := expandVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(original["enable_private_service_connect"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnablePrivateServiceConnect); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["enablePrivateServiceConnect"] = transformedEnablePrivateServiceConnect + } + + transformedProjectAllowlist, err := expandVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigProjectAllowlist(original["project_allowlist"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedProjectAllowlist); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["projectAllowlist"] = transformedProjectAllowlist + } + + return transformed, nil +} + +func expandVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandVertexAIFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigProjectAllowlist(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandVertexAIFeatureOnlineStoreEmbeddingManagement(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnabled, err := expandVertexAIFeatureOnlineStoreEmbeddingManagementEnabled(original["enabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnabled); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["enabled"] = transformedEnabled + } + + return transformed, nil +} + +func expandVertexAIFeatureOnlineStoreEmbeddingManagementEnabled(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + func expandVertexAIFeatureOnlineStoreEffectiveLabels(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) { if v == nil { return map[string]string{}, nil diff --git a/google-beta/services/vertexai/resource_vertex_ai_feature_online_store_generated_test.go b/google-beta/services/vertexai/resource_vertex_ai_feature_online_store_generated_test.go index 9fb63baa08..9fad900219 100644 --- a/google-beta/services/vertexai/resource_vertex_ai_feature_online_store_generated_test.go +++ b/google-beta/services/vertexai/resource_vertex_ai_feature_online_store_generated_test.go @@ -49,7 +49,7 @@ func TestAccVertexAIFeatureOnlineStore_vertexAiFeatureOnlineStoreExample(t *test ResourceName: "google_vertex_ai_feature_online_store.feature_online_store", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag", "region", "labels", "terraform_labels"}, + ImportStateVerifyIgnore: []string{"name", "etag", "region", "force_destroy", "labels", "terraform_labels"}, }, }, }) @@ -57,20 +57,121 @@ func TestAccVertexAIFeatureOnlineStore_vertexAiFeatureOnlineStoreExample(t *test func testAccVertexAIFeatureOnlineStore_vertexAiFeatureOnlineStoreExample(context map[string]interface{}) string { return acctest.Nprintf(` -resource google_vertex_ai_feature_online_store "feature_online_store" { - name = "tf_test_example_feature_online_store%{random_suffix}" - region = "us-central1" - labels = { - label-one = "value-one" +resource "google_vertex_ai_feature_online_store" "feature_online_store" { + name = "tf_test_example_feature_online_store%{random_suffix}" + labels = { + foo = "bar" + } + region = "us-central1" + bigtable { + auto_scaling { + min_node_count = 1 + max_node_count = 3 + cpu_utilization_target = 50 } + } +} +`, context) +} + +func TestAccVertexAIFeatureOnlineStore_vertexAiFeatureonlinestoreWithBetaFieldsOptimizedExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + CheckDestroy: testAccCheckVertexAIFeatureOnlineStoreDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccVertexAIFeatureOnlineStore_vertexAiFeatureonlinestoreWithBetaFieldsOptimizedExample(context), + }, + { + ResourceName: "google_vertex_ai_feature_online_store.featureonlinestore", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "etag", "region", "force_destroy", "labels", "terraform_labels"}, + }, + }, + }) +} + +func testAccVertexAIFeatureOnlineStore_vertexAiFeatureonlinestoreWithBetaFieldsOptimizedExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_vertex_ai_feature_online_store" "featureonlinestore" { + provider = google-beta + name = "tf_test_example_feature_online_store_optimized%{random_suffix}" + labels = { + foo = "bar" + } + region = "us-central1" + optimized {} + dedicated_serving_endpoint { + private_service_connect_config { + enable_private_service_connect = true + project_allowlist = [data.google_project.project.number] + } + } +} - bigtable { - auto_scaling { - min_node_count = 1 - max_node_count = 2 - cpu_utilization_target = 60 - } +data "google_project" "project" { + provider = google-beta +} +`, context) +} + +func TestAccVertexAIFeatureOnlineStore_vertexAiFeatureonlinestoreWithBetaFieldsBigtableExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + CheckDestroy: testAccCheckVertexAIFeatureOnlineStoreDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccVertexAIFeatureOnlineStore_vertexAiFeatureonlinestoreWithBetaFieldsBigtableExample(context), + }, + { + ResourceName: "google_vertex_ai_feature_online_store.featureonlinestore", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "etag", "region", "force_destroy", "labels", "terraform_labels"}, + }, + }, + }) +} + +func testAccVertexAIFeatureOnlineStore_vertexAiFeatureonlinestoreWithBetaFieldsBigtableExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_vertex_ai_feature_online_store" "featureonlinestore" { + provider = google-beta + name = "tf_test_example_feature_online_store_beta_bigtable%{random_suffix}" + labels = { + foo = "bar" + } + region = "us-central1" + bigtable { + auto_scaling { + min_node_count = 1 + max_node_count = 2 + cpu_utilization_target = 80 } + } + embedding_management { + enabled = true + } + force_destroy = true +} + +data "google_project" "project" { + provider = google-beta } `, context) } diff --git a/google-beta/services/vertexai/resource_vertex_ai_feature_online_store_sweeper.go b/google-beta/services/vertexai/resource_vertex_ai_feature_online_store_sweeper.go deleted file mode 100644 index d57ab5956d..0000000000 --- a/google-beta/services/vertexai/resource_vertex_ai_feature_online_store_sweeper.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -// ---------------------------------------------------------------------------- -// -// *** AUTO GENERATED CODE *** Type: MMv1 *** -// -// ---------------------------------------------------------------------------- -// -// This file is automatically generated by Magic Modules and manual -// changes will be clobbered when the file is regenerated. -// -// Please read more about how to change this file in -// .github/CONTRIBUTING.md. -// -// ---------------------------------------------------------------------------- - -package vertexai - -import ( - "context" - "log" - "strings" - "testing" - - "github.com/hashicorp/terraform-provider-google-beta/google-beta/envvar" - "github.com/hashicorp/terraform-provider-google-beta/google-beta/sweeper" - "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" - transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport" -) - -func init() { - sweeper.AddTestSweepers("VertexAIFeatureOnlineStore", testSweepVertexAIFeatureOnlineStore) -} - -// At the time of writing, the CI only passes us-central1 as the region -func testSweepVertexAIFeatureOnlineStore(region string) error { - resourceName := "VertexAIFeatureOnlineStore" - log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) - - config, err := sweeper.SharedConfigForRegion(region) - if err != nil { - log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) - return err - } - - err = config.LoadAndValidate(context.Background()) - if err != nil { - log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) - return err - } - - t := &testing.T{} - billingId := envvar.GetTestBillingAccountFromEnv(t) - - // Setup variables to replace in list template - d := &tpgresource.ResourceDataMock{ - FieldsInSchema: map[string]interface{}{ - "project": config.Project, - "region": region, - "location": region, - "zone": "-", - "billing_account": billingId, - }, - } - - listTemplate := strings.Split("https://{{region}}-aiplatform.googleapis.com/v1beta1/projects/{{project}}/locations/{{region}}/featureOnlineStores", "?")[0] - listUrl, err := tpgresource.ReplaceVars(d, config, listTemplate) - if err != nil { - log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) - return nil - } - - res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ - Config: config, - Method: "GET", - Project: config.Project, - RawURL: listUrl, - UserAgent: config.UserAgent, - }) - if err != nil { - log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) - return nil - } - - resourceList, ok := res["featureOnlineStores"] - if !ok { - log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") - return nil - } - - rl := resourceList.([]interface{}) - - log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) - // Keep count of items that aren't sweepable for logging. - nonPrefixCount := 0 - for _, ri := range rl { - obj := ri.(map[string]interface{}) - if obj["name"] == nil { - log.Printf("[INFO][SWEEPER_LOG] %s resource name was nil", resourceName) - return nil - } - - name := tpgresource.GetResourceNameFromSelfLink(obj["name"].(string)) - // Skip resources that shouldn't be sweeped - if !sweeper.IsSweepableTestResource(name) { - nonPrefixCount++ - continue - } - - deleteTemplate := "https://{{region}}-aiplatform.googleapis.com/v1beta1/projects/{{project}}/locations/{{region}}/featureOnlineStores/{{name}}" - deleteUrl, err := tpgresource.ReplaceVars(d, config, deleteTemplate) - if err != nil { - log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) - return nil - } - deleteUrl = deleteUrl + name - - // Don't wait on operations as we may have a lot to delete - _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ - Config: config, - Method: "DELETE", - Project: config.Project, - RawURL: deleteUrl, - UserAgent: config.UserAgent, - }) - if err != nil { - log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) - } else { - log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) - } - } - - if nonPrefixCount > 0 { - log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) - } - - return nil -} diff --git a/google-beta/services/vertexai/resource_vertex_ai_feature_online_store_test.go b/google-beta/services/vertexai/resource_vertex_ai_feature_online_store_test.go index 40460a771c..0adfc01957 100644 --- a/google-beta/services/vertexai/resource_vertex_ai_feature_online_store_test.go +++ b/google-beta/services/vertexai/resource_vertex_ai_feature_online_store_test.go @@ -29,7 +29,7 @@ func TestAccVertexAIFeatureOnlineStore_updated(t *testing.T) { ResourceName: "google_vertex_ai_feature_online_store.feature_online_store", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag", "region", "labels", "terraform_labels"}, + ImportStateVerifyIgnore: []string{"name", "etag", "region", "force_destroy", "labels", "terraform_labels"}, }, { Config: testAccVertexAIFeatureOnlineStore_updated(context), @@ -38,7 +38,7 @@ func TestAccVertexAIFeatureOnlineStore_updated(t *testing.T) { ResourceName: "google_vertex_ai_feature_online_store.feature_online_store", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"etag", "region", "labels", "terraform_labels"}, + ImportStateVerifyIgnore: []string{"name", "etag", "region", "force_destroy", "labels", "terraform_labels"}, }, }, }) @@ -60,6 +60,7 @@ resource google_vertex_ai_feature_online_store "feature_online_store" { cpu_utilization_target = 60 } } + force_destroy = true } `, context) } @@ -80,6 +81,7 @@ resource google_vertex_ai_feature_online_store "feature_online_store" { max_node_count = 3 } } + force_destroy = true } `, context) } diff --git a/website/docs/r/vertex_ai_feature_online_store.html.markdown b/website/docs/r/vertex_ai_feature_online_store.html.markdown index 535f802a0f..668271f08f 100644 --- a/website/docs/r/vertex_ai_feature_online_store.html.markdown +++ b/website/docs/r/vertex_ai_feature_online_store.html.markdown @@ -25,6 +25,8 @@ Vertex AI Feature Online Store provides a centralized repository for serving ML To get more information about FeatureOnlineStore, see: * [API documentation](https://cloud.google.com/vertex-ai/docs/reference/rest/v1/projects.locations.featureOnlineStores) +* How-to Guides + * [Official Documentation](https://cloud.google.com/vertex-ai/docs)
@@ -35,20 +37,81 @@ To get more information about FeatureOnlineStore, see: ```hcl -resource google_vertex_ai_feature_online_store "feature_online_store" { - name = "example_feature_online_store" - region = "us-central1" - labels = { - label-one = "value-one" +resource "google_vertex_ai_feature_online_store" "feature_online_store" { + name = "example_feature_online_store" + labels = { + foo = "bar" + } + region = "us-central1" + bigtable { + auto_scaling { + min_node_count = 1 + max_node_count = 3 + cpu_utilization_target = 50 } + } +} +``` +
+ + Open in Cloud Shell + +
+## Example Usage - Vertex Ai Featureonlinestore With Beta Fields Optimized + + +```hcl +resource "google_vertex_ai_feature_online_store" "featureonlinestore" { + provider = google-beta + name = "example_feature_online_store_optimized" + labels = { + foo = "bar" + } + region = "us-central1" + optimized {} + dedicated_serving_endpoint { + private_service_connect_config { + enable_private_service_connect = true + project_allowlist = [data.google_project.project.number] + } + } +} + +data "google_project" "project" { + provider = google-beta +} +``` +
+ + Open in Cloud Shell + +
+## Example Usage - Vertex Ai Featureonlinestore With Beta Fields Bigtable - bigtable { - auto_scaling { - min_node_count = 1 - max_node_count = 2 - cpu_utilization_target = 60 - } + +```hcl +resource "google_vertex_ai_feature_online_store" "featureonlinestore" { + provider = google-beta + name = "example_feature_online_store_beta_bigtable" + labels = { + foo = "bar" + } + region = "us-central1" + bigtable { + auto_scaling { + min_node_count = 1 + max_node_count = 2 + cpu_utilization_target = 80 } + } + embedding_management { + enabled = true + } + force_destroy = true +} + +data "google_project" "project" { + provider = google-beta } ``` @@ -59,11 +122,7 @@ The following arguments are supported: * `name` - (Required) - The resource name of the Feature Online Store. - -* `region` - - (Required) - The region of feature online store. eg us-central1 + The resource name of the Feature Online Store. This value may be up to 60 characters, and valid characters are [a-z0-9_]. The first character cannot be a number. - - - @@ -80,9 +139,28 @@ The following arguments are supported: Settings for Cloud Bigtable instance that will be created to serve featureValues for all FeatureViews under this FeatureOnlineStore. Structure is [documented below](#nested_bigtable). +* `optimized` - + (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) + Settings for the Optimized store that will be created to serve featureValues for all FeatureViews under this FeatureOnlineStore + +* `dedicated_serving_endpoint` - + (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) + The dedicated serving endpoint for this FeatureOnlineStore, which is different from common vertex service endpoint. Only need to set when you choose Optimized storage type or enable EmbeddingManagement. Will use public endpoint by default. + Structure is [documented below](#nested_dedicated_serving_endpoint). + +* `embedding_management` - + (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) + The settings for embedding management in FeatureOnlineStore. Embedding management can only be used with BigTable. + Structure is [documented below](#nested_embedding_management). + +* `region` - + (Optional) + The region of feature online store. eg us-central1 + * `project` - (Optional) The ID of the project in which the resource belongs. If it is not provided, the provider project is used. +* `force_destroy` - (Optional) If set to true, any FeatureViews and Features for this FeatureOnlineStore will also be deleted. The `bigtable` block supports: @@ -106,6 +184,38 @@ The following arguments are supported: (Optional) A percentage of the cluster's CPU capacity. Can be from 10% to 80%. When a cluster's CPU utilization exceeds the target that you have set, Bigtable immediately adds nodes to the cluster. When CPU utilization is substantially lower than the target, Bigtable removes nodes. If not set will default to 50%. +The `dedicated_serving_endpoint` block supports: + +* `public_endpoint_domain_name` - + (Output) + Domain name to use for this FeatureOnlineStore + +* `service_attachment` - + (Output) + Name of the service attachment resource. Applicable only if private service connect is enabled and after FeatureViewSync is created. + +* `private_service_connect_config` - + (Optional) + Private service connect config. + Structure is [documented below](#nested_private_service_connect_config). + + +The `private_service_connect_config` block supports: + +* `enable_private_service_connect` - + (Required) + If set to true, customers will use private service connection to send request. Otherwise, the connection will set to public endpoint. + +* `project_allowlist` - + (Optional) + A list of Projects from which the forwarding rule will target the service attachment. + +The `embedding_management` block supports: + +* `enabled` - + (Optional) + Enable embedding management. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: