Skip to content

Commit 83063a6

Browse files
feat(vertexai): Make it possible to use Private Service Connect in Vertex AI Index Endpoint (#8851) (#16471)
[upstream:20ab4e9c5fefe34ee1514a19825501b4f806733a] Signed-off-by: Modular Magician <[email protected]>
1 parent 2f25eb8 commit 83063a6

File tree

4 files changed

+229
-0
lines changed

4 files changed

+229
-0
lines changed

.changelog/8851.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
vertexai: added `private_service_connect_config` to `google_vertex_ai_index_endpoint`
3+
```

google/services/vertexai/resource_vertex_ai_index_endpoint.go

+101
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,35 @@ Please refer to the field 'effective_labels' for all of the labels present on th
8181
Private services access must already be configured for the network. If left unspecified, the index endpoint is not peered with any network.
8282
[Format](https://cloud.google.com/compute/docs/reference/rest/v1/networks/insert): 'projects/{project}/global/networks/{network}'.
8383
Where '{project}' is a project number, as in '12345', and '{network}' is network name.`,
84+
ConflictsWith: []string{"private_service_connect_config"},
85+
},
86+
"private_service_connect_config": {
87+
Type: schema.TypeList,
88+
Computed: true,
89+
Optional: true,
90+
ForceNew: true,
91+
Description: `Optional. Configuration for private service connect. 'network' and 'privateServiceConnectConfig' are mutually exclusive.`,
92+
MaxItems: 1,
93+
Elem: &schema.Resource{
94+
Schema: map[string]*schema.Schema{
95+
"enable_private_service_connect": {
96+
Type: schema.TypeBool,
97+
Required: true,
98+
ForceNew: true,
99+
Description: `If set to true, the IndexEndpoint is created without private service access.`,
100+
},
101+
"project_allowlist": {
102+
Type: schema.TypeList,
103+
Optional: true,
104+
ForceNew: true,
105+
Description: `A list of Projects from which the forwarding rule will target the service attachment.`,
106+
Elem: &schema.Schema{
107+
Type: schema.TypeString,
108+
},
109+
},
110+
},
111+
},
112+
ConflictsWith: []string{"network"},
84113
},
85114
"public_endpoint_enabled": {
86115
Type: schema.TypeBool,
@@ -169,6 +198,12 @@ func resourceVertexAIIndexEndpointCreate(d *schema.ResourceData, meta interface{
169198
} else if v, ok := d.GetOkExists("network"); !tpgresource.IsEmptyValue(reflect.ValueOf(networkProp)) && (ok || !reflect.DeepEqual(v, networkProp)) {
170199
obj["network"] = networkProp
171200
}
201+
privateServiceConnectConfigProp, err := expandVertexAIIndexEndpointPrivateServiceConnectConfig(d.Get("private_service_connect_config"), d, config)
202+
if err != nil {
203+
return err
204+
} else if v, ok := d.GetOkExists("private_service_connect_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(privateServiceConnectConfigProp)) && (ok || !reflect.DeepEqual(v, privateServiceConnectConfigProp)) {
205+
obj["privateServiceConnectConfig"] = privateServiceConnectConfigProp
206+
}
172207
publicEndpointEnabledProp, err := expandVertexAIIndexEndpointPublicEndpointEnabled(d.Get("public_endpoint_enabled"), d, config)
173208
if err != nil {
174209
return err
@@ -311,6 +346,9 @@ func resourceVertexAIIndexEndpointRead(d *schema.ResourceData, meta interface{})
311346
if err := d.Set("network", flattenVertexAIIndexEndpointNetwork(res["network"], d, config)); err != nil {
312347
return fmt.Errorf("Error reading IndexEndpoint: %s", err)
313348
}
349+
if err := d.Set("private_service_connect_config", flattenVertexAIIndexEndpointPrivateServiceConnectConfig(res["privateServiceConnectConfig"], d, config)); err != nil {
350+
return fmt.Errorf("Error reading IndexEndpoint: %s", err)
351+
}
314352
if err := d.Set("public_endpoint_domain_name", flattenVertexAIIndexEndpointPublicEndpointDomainName(res["publicEndpointDomainName"], d, config)); err != nil {
315353
return fmt.Errorf("Error reading IndexEndpoint: %s", err)
316354
}
@@ -529,6 +567,35 @@ func flattenVertexAIIndexEndpointNetwork(v interface{}, d *schema.ResourceData,
529567
return v
530568
}
531569

570+
func flattenVertexAIIndexEndpointPrivateServiceConnectConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
571+
transformed := make(map[string]interface{})
572+
573+
if v == nil {
574+
// Disabled by default, but API will not return object if value is false
575+
transformed["enable_private_service_connect"] = false
576+
return []interface{}{transformed}
577+
}
578+
579+
original := v.(map[string]interface{})
580+
if len(original) == 0 {
581+
return nil
582+
}
583+
584+
transformed["enable_private_service_connect"] =
585+
flattenVertexAIIndexEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(original["enablePrivateServiceConnect"], d, config)
586+
transformed["project_allowlist"] =
587+
flattenVertexAIIndexEndpointPrivateServiceConnectConfigProjectAllowlist(original["projectAllowlist"], d, config)
588+
return []interface{}{transformed}
589+
}
590+
591+
func flattenVertexAIIndexEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
592+
return v
593+
}
594+
595+
func flattenVertexAIIndexEndpointPrivateServiceConnectConfigProjectAllowlist(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
596+
return v
597+
}
598+
532599
func flattenVertexAIIndexEndpointPublicEndpointDomainName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
533600
return v
534601
}
@@ -564,6 +631,40 @@ func expandVertexAIIndexEndpointNetwork(v interface{}, d tpgresource.TerraformRe
564631
return v, nil
565632
}
566633

634+
func expandVertexAIIndexEndpointPrivateServiceConnectConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
635+
l := v.([]interface{})
636+
if len(l) == 0 || l[0] == nil {
637+
return nil, nil
638+
}
639+
raw := l[0]
640+
original := raw.(map[string]interface{})
641+
transformed := make(map[string]interface{})
642+
643+
transformedEnablePrivateServiceConnect, err := expandVertexAIIndexEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(original["enable_private_service_connect"], d, config)
644+
if err != nil {
645+
return nil, err
646+
} else if val := reflect.ValueOf(transformedEnablePrivateServiceConnect); val.IsValid() && !tpgresource.IsEmptyValue(val) {
647+
transformed["enablePrivateServiceConnect"] = transformedEnablePrivateServiceConnect
648+
}
649+
650+
transformedProjectAllowlist, err := expandVertexAIIndexEndpointPrivateServiceConnectConfigProjectAllowlist(original["project_allowlist"], d, config)
651+
if err != nil {
652+
return nil, err
653+
} else if val := reflect.ValueOf(transformedProjectAllowlist); val.IsValid() && !tpgresource.IsEmptyValue(val) {
654+
transformed["projectAllowlist"] = transformedProjectAllowlist
655+
}
656+
657+
return transformed, nil
658+
}
659+
660+
func expandVertexAIIndexEndpointPrivateServiceConnectConfigEnablePrivateServiceConnect(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
661+
return v, nil
662+
}
663+
664+
func expandVertexAIIndexEndpointPrivateServiceConnectConfigProjectAllowlist(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
665+
return v, nil
666+
}
667+
567668
func expandVertexAIIndexEndpointPublicEndpointEnabled(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
568669
return v, nil
569670
}

google/services/vertexai/resource_vertex_ai_index_endpoint_generated_test.go

+83
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,89 @@ data "google_project" "project" {}
7676
`, context)
7777
}
7878

79+
func TestAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithPscExample(t *testing.T) {
80+
t.Parallel()
81+
82+
context := map[string]interface{}{
83+
"random_suffix": acctest.RandString(t, 10),
84+
}
85+
86+
acctest.VcrTest(t, resource.TestCase{
87+
PreCheck: func() { acctest.AccTestPreCheck(t) },
88+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
89+
CheckDestroy: testAccCheckVertexAIIndexEndpointDestroyProducer(t),
90+
Steps: []resource.TestStep{
91+
{
92+
Config: testAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithPscExample(context),
93+
},
94+
{
95+
ResourceName: "google_vertex_ai_index_endpoint.index_endpoint",
96+
ImportState: true,
97+
ImportStateVerify: true,
98+
ImportStateVerifyIgnore: []string{"etag", "public_endpoint_enabled", "region", "labels", "terraform_labels"},
99+
},
100+
},
101+
})
102+
}
103+
104+
func testAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithPscExample(context map[string]interface{}) string {
105+
return acctest.Nprintf(`
106+
resource "google_vertex_ai_index_endpoint" "index_endpoint" {
107+
display_name = "sample-endpoint"
108+
description = "A sample vertex endpoint"
109+
region = "us-central1"
110+
labels = {
111+
label-one = "value-one"
112+
}
113+
114+
private_service_connect_config {
115+
enable_private_service_connect = true
116+
project_allowlist = [
117+
data.google_project.project.number,
118+
]
119+
}
120+
}
121+
122+
data "google_project" "project" {}
123+
`, context)
124+
}
125+
126+
func TestAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithFalsePscExample(t *testing.T) {
127+
t.Parallel()
128+
129+
context := map[string]interface{}{
130+
"random_suffix": acctest.RandString(t, 10),
131+
}
132+
133+
acctest.VcrTest(t, resource.TestCase{
134+
PreCheck: func() { acctest.AccTestPreCheck(t) },
135+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
136+
CheckDestroy: testAccCheckVertexAIIndexEndpointDestroyProducer(t),
137+
Steps: []resource.TestStep{
138+
{
139+
Config: testAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithFalsePscExample(context),
140+
},
141+
},
142+
})
143+
}
144+
145+
func testAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithFalsePscExample(context map[string]interface{}) string {
146+
return acctest.Nprintf(`
147+
resource "google_vertex_ai_index_endpoint" "index_endpoint" {
148+
display_name = "sample-endpoint"
149+
description = "A sample vertex endpoint"
150+
region = "us-central1"
151+
labels = {
152+
label-one = "value-one"
153+
}
154+
155+
private_service_connect_config {
156+
enable_private_service_connect = false
157+
}
158+
}
159+
`, context)
160+
}
161+
79162
func TestAccVertexAIIndexEndpoint_vertexAiIndexEndpointWithPublicEndpointExample(t *testing.T) {
80163
t.Parallel()
81164

website/docs/r/vertex_ai_index_endpoint.html.markdown

+42
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,33 @@ resource "google_compute_network" "vertex_network" {
6161
name = "network-name"
6262
}
6363
64+
data "google_project" "project" {}
65+
```
66+
<div class = "oics-button" style="float: right; margin: 0 0 -15px">
67+
<a href="https://console.cloud.google.com/cloudshell/open?cloudshell_git_repo=https%3A%2F%2Fgithub.jpy.wang%2Fterraform-google-modules%2Fdocs-examples.git&cloudshell_working_dir=vertex_ai_index_endpoint_with_psc&cloudshell_image=gcr.io%2Fcloudshell-images%2Fcloudshell%3Alatest&open_in_editor=main.tf&cloudshell_print=.%2Fmotd&cloudshell_tutorial=.%2Ftutorial.md" target="_blank">
68+
<img alt="Open in Cloud Shell" src="//gstatic.com/cloudssh/images/open-btn.svg" style="max-height: 44px; margin: 32px auto; max-width: 100%;">
69+
</a>
70+
</div>
71+
## Example Usage - Vertex Ai Index Endpoint With Psc
72+
73+
74+
```hcl
75+
resource "google_vertex_ai_index_endpoint" "index_endpoint" {
76+
display_name = "sample-endpoint"
77+
description = "A sample vertex endpoint"
78+
region = "us-central1"
79+
labels = {
80+
label-one = "value-one"
81+
}
82+
83+
private_service_connect_config {
84+
enable_private_service_connect = true
85+
project_allowlist = [
86+
data.google_project.project.number,
87+
]
88+
}
89+
}
90+
6491
data "google_project" "project" {}
6592
```
6693
<div class = "oics-button" style="float: right; margin: 0 0 -15px">
@@ -114,6 +141,11 @@ The following arguments are supported:
114141
[Format](https://cloud.google.com/compute/docs/reference/rest/v1/networks/insert): `projects/{project}/global/networks/{network}`.
115142
Where `{project}` is a project number, as in `12345`, and `{network}` is network name.
116143

144+
* `private_service_connect_config` -
145+
(Optional)
146+
Optional. Configuration for private service connect. `network` and `privateServiceConnectConfig` are mutually exclusive.
147+
Structure is [documented below](#nested_private_service_connect_config).
148+
117149
* `public_endpoint_enabled` -
118150
(Optional)
119151
If true, the deployed index will be accessible through public endpoint.
@@ -126,6 +158,16 @@ The following arguments are supported:
126158
If it is not provided, the provider project is used.
127159

128160

161+
<a name="nested_private_service_connect_config"></a>The `private_service_connect_config` block supports:
162+
163+
* `enable_private_service_connect` -
164+
(Required)
165+
If set to true, the IndexEndpoint is created without private service access.
166+
167+
* `project_allowlist` -
168+
(Optional)
169+
A list of Projects from which the forwarding rule will target the service attachment.
170+
129171
## Attributes Reference
130172

131173
In addition to the arguments listed above, the following computed attributes are exported:

0 commit comments

Comments
 (0)