From 83063a6713d8804e8f5ef2dd1fdf32f5ff8fd0ef Mon Sep 17 00:00:00 2001 From: The Magician Date: Tue, 7 Nov 2023 16:00:01 -0800 Subject: [PATCH] feat(vertexai): Make it possible to use Private Service Connect in Vertex AI Index Endpoint (#8851) (#16471) [upstream:20ab4e9c5fefe34ee1514a19825501b4f806733a] Signed-off-by: Modular Magician --- .changelog/8851.txt | 3 + .../resource_vertex_ai_index_endpoint.go | 101 ++++++++++++++++++ ...vertex_ai_index_endpoint_generated_test.go | 83 ++++++++++++++ .../r/vertex_ai_index_endpoint.html.markdown | 42 ++++++++ 4 files changed, 229 insertions(+) create mode 100644 .changelog/8851.txt diff --git a/.changelog/8851.txt b/.changelog/8851.txt new file mode 100644 index 00000000000..e754e08b337 --- /dev/null +++ b/.changelog/8851.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +vertexai: added `private_service_connect_config` to `google_vertex_ai_index_endpoint` +``` diff --git a/google/services/vertexai/resource_vertex_ai_index_endpoint.go b/google/services/vertexai/resource_vertex_ai_index_endpoint.go index e35c2792772..828740e0a7d 100644 --- a/google/services/vertexai/resource_vertex_ai_index_endpoint.go +++ b/google/services/vertexai/resource_vertex_ai_index_endpoint.go @@ -81,6 +81,35 @@ Please refer to the field 'effective_labels' for all of the labels present on th Private services access must already be configured for the network. If left unspecified, the index endpoint is not peered with any network. [Format](https://cloud.google.com/compute/docs/reference/rest/v1/networks/insert): 'projects/{project}/global/networks/{network}'. Where '{project}' is a project number, as in '12345', and '{network}' is network name.`, + ConflictsWith: []string{"private_service_connect_config"}, + }, + "private_service_connect_config": { + Type: schema.TypeList, + Computed: true, + Optional: true, + ForceNew: true, + Description: `Optional. Configuration for private service connect. 'network' and 'privateServiceConnectConfig' are mutually exclusive.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable_private_service_connect": { + Type: schema.TypeBool, + Required: true, + ForceNew: true, + Description: `If set to true, the IndexEndpoint is created without private service access.`, + }, + "project_allowlist": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Description: `A list of Projects from which the forwarding rule will target the service attachment.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + ConflictsWith: []string{"network"}, }, "public_endpoint_enabled": { Type: schema.TypeBool, @@ -169,6 +198,12 @@ func resourceVertexAIIndexEndpointCreate(d *schema.ResourceData, meta interface{ } else if v, ok := d.GetOkExists("network"); !tpgresource.IsEmptyValue(reflect.ValueOf(networkProp)) && (ok || !reflect.DeepEqual(v, networkProp)) { obj["network"] = networkProp } + privateServiceConnectConfigProp, err := expandVertexAIIndexEndpointPrivateServiceConnectConfig(d.Get("private_service_connect_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("private_service_connect_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(privateServiceConnectConfigProp)) && (ok || !reflect.DeepEqual(v, privateServiceConnectConfigProp)) { + obj["privateServiceConnectConfig"] = privateServiceConnectConfigProp + } publicEndpointEnabledProp, err := expandVertexAIIndexEndpointPublicEndpointEnabled(d.Get("public_endpoint_enabled"), d, config) if err != nil { return err @@ -311,6 +346,9 @@ func resourceVertexAIIndexEndpointRead(d *schema.ResourceData, meta interface{}) if err := d.Set("network", flattenVertexAIIndexEndpointNetwork(res["network"], d, config)); err != nil { return fmt.Errorf("Error reading IndexEndpoint: %s", err) } + if err := d.Set("private_service_connect_config", flattenVertexAIIndexEndpointPrivateServiceConnectConfig(res["privateServiceConnectConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading IndexEndpoint: %s", err) + } if err := d.Set("public_endpoint_domain_name", flattenVertexAIIndexEndpointPublicEndpointDomainName(res["publicEndpointDomainName"], d, config)); err != nil { return fmt.Errorf("Error reading IndexEndpoint: %s", err) } @@ -529,6 +567,35 @@ func flattenVertexAIIndexEndpointNetwork(v interface{}, d *schema.ResourceData, return v } +func flattenVertexAIIndexEndpointPrivateServiceConnectConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + transformed := make(map[string]interface{}) + + if v == nil { + // Disabled by default, but API will not return object if value is false + transformed["enable_private_service_connect"] = false + return []interface{}{transformed} + } + + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + + transformed["enable_private_service_connect"] = + flattenVertexAIIndexEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(original["enablePrivateServiceConnect"], d, config) + transformed["project_allowlist"] = + flattenVertexAIIndexEndpointPrivateServiceConnectConfigProjectAllowlist(original["projectAllowlist"], d, config) + return []interface{}{transformed} +} + +func flattenVertexAIIndexEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenVertexAIIndexEndpointPrivateServiceConnectConfigProjectAllowlist(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + func flattenVertexAIIndexEndpointPublicEndpointDomainName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { return v } @@ -564,6 +631,40 @@ func expandVertexAIIndexEndpointNetwork(v interface{}, d tpgresource.TerraformRe return v, nil } +func expandVertexAIIndexEndpointPrivateServiceConnectConfig(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 := expandVertexAIIndexEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(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 := expandVertexAIIndexEndpointPrivateServiceConnectConfigProjectAllowlist(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 expandVertexAIIndexEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandVertexAIIndexEndpointPrivateServiceConnectConfigProjectAllowlist(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + func expandVertexAIIndexEndpointPublicEndpointEnabled(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { return v, nil } diff --git a/google/services/vertexai/resource_vertex_ai_index_endpoint_generated_test.go b/google/services/vertexai/resource_vertex_ai_index_endpoint_generated_test.go index c8c83104c38..e6048982b46 100644 --- a/google/services/vertexai/resource_vertex_ai_index_endpoint_generated_test.go +++ b/google/services/vertexai/resource_vertex_ai_index_endpoint_generated_test.go @@ -76,6 +76,89 @@ data "google_project" "project" {} `, context) } +func TestAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithPscExample(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.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckVertexAIIndexEndpointDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithPscExample(context), + }, + { + ResourceName: "google_vertex_ai_index_endpoint.index_endpoint", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"etag", "public_endpoint_enabled", "region", "labels", "terraform_labels"}, + }, + }, + }) +} + +func testAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithPscExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_vertex_ai_index_endpoint" "index_endpoint" { + display_name = "sample-endpoint" + description = "A sample vertex endpoint" + region = "us-central1" + labels = { + label-one = "value-one" + } + + private_service_connect_config { + enable_private_service_connect = true + project_allowlist = [ + data.google_project.project.number, + ] + } +} + +data "google_project" "project" {} +`, context) +} + +func TestAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithFalsePscExample(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.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckVertexAIIndexEndpointDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithFalsePscExample(context), + }, + }, + }) +} + +func testAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithFalsePscExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_vertex_ai_index_endpoint" "index_endpoint" { + display_name = "sample-endpoint" + description = "A sample vertex endpoint" + region = "us-central1" + labels = { + label-one = "value-one" + } + + private_service_connect_config { + enable_private_service_connect = false + } +} +`, context) +} + func TestAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithPublicEndpointExample(t *testing.T) { t.Parallel() diff --git a/website/docs/r/vertex_ai_index_endpoint.html.markdown b/website/docs/r/vertex_ai_index_endpoint.html.markdown index 092abc933b4..9ea453ac94b 100644 --- a/website/docs/r/vertex_ai_index_endpoint.html.markdown +++ b/website/docs/r/vertex_ai_index_endpoint.html.markdown @@ -61,6 +61,33 @@ resource "google_compute_network" "vertex_network" { name = "network-name" } +data "google_project" "project" {} +``` + +## Example Usage - Vertex Ai Index Endpoint With Psc + + +```hcl +resource "google_vertex_ai_index_endpoint" "index_endpoint" { + display_name = "sample-endpoint" + description = "A sample vertex endpoint" + region = "us-central1" + labels = { + label-one = "value-one" + } + + private_service_connect_config { + enable_private_service_connect = true + project_allowlist = [ + data.google_project.project.number, + ] + } +} + data "google_project" "project" {} ```
@@ -114,6 +141,11 @@ The following arguments are supported: [Format](https://cloud.google.com/compute/docs/reference/rest/v1/networks/insert): `projects/{project}/global/networks/{network}`. Where `{project}` is a project number, as in `12345`, and `{network}` is network name. +* `private_service_connect_config` - + (Optional) + Optional. Configuration for private service connect. `network` and `privateServiceConnectConfig` are mutually exclusive. + Structure is [documented below](#nested_private_service_connect_config). + * `public_endpoint_enabled` - (Optional) If true, the deployed index will be accessible through public endpoint. @@ -126,6 +158,16 @@ The following arguments are supported: If it is not provided, the provider project is used. +The `private_service_connect_config` block supports: + +* `enable_private_service_connect` - + (Required) + If set to true, the IndexEndpoint is created without private service access. + +* `project_allowlist` - + (Optional) + A list of Projects from which the forwarding rule will target the service attachment. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: