Skip to content

Commit

Permalink
feat(vertexai): add vertex_ai_index_endpoint for Vertex AI Matching E…
Browse files Browse the repository at this point in the history
…ngine (#6873) (#5738)

* feat: create vertex_ai_index_endpoint

* fix: remove a white line

* feat: add a comment about deployedIndexes [ci skip]

* feat: add an unit test to check update

* fix: fix the handwritten test name to avoid a conflict

* fix: fix unnecessary code from test

* fix: use immutable instead of input

* refactor: refactor test code with acctest and tpgresource

* test: update network_name to use the unique names

* test: fix an invalid label definition

* feat: increase timeout from 20m to 1h regarding vertex ai index endpoint

* test: increase the timeouts to 120m

* Apply suggestions from code review

* feat: update test and yaml to clarify the fields mutabilities

Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored Jun 7, 2023
1 parent 82b368b commit 9ea4f75
Show file tree
Hide file tree
Showing 7 changed files with 1,035 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .changelog/6873.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
vertex_ai_index_endpoint
```
5 changes: 3 additions & 2 deletions google-beta/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1033,9 +1033,9 @@ func DatasourceMapWithErrors() (map[string]*schema.Resource, error) {
})
}

// Generated resources: 345
// Generated resources: 346
// Generated IAM resources: 225
// Total generated resources: 570
// Total generated resources: 571
func ResourceMap() map[string]*schema.Resource {
resourceMap, _ := ResourceMapWithErrors()
return resourceMap
Expand Down Expand Up @@ -1600,6 +1600,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
"google_vertex_ai_featurestore_entitytype_iam_policy": tpgiamresource.ResourceIamPolicy(vertexai.VertexAIFeaturestoreEntitytypeIamSchema, vertexai.VertexAIFeaturestoreEntitytypeIamUpdaterProducer, vertexai.VertexAIFeaturestoreEntitytypeIdParseFunc),
"google_vertex_ai_featurestore_entitytype_feature": vertexai.ResourceVertexAIFeaturestoreEntitytypeFeature(),
"google_vertex_ai_index": vertexai.ResourceVertexAIIndex(),
"google_vertex_ai_index_endpoint": vertexai.ResourceVertexAIIndexEndpoint(),
"google_vertex_ai_metadata_store": vertexai.ResourceVertexAIMetadataStore(),
"google_vertex_ai_tensorboard": vertexai.ResourceVertexAITensorboard(),
"google_vmwareengine_network": vmwareengine.ResourceVmwareengineNetwork(),
Expand Down
133 changes: 133 additions & 0 deletions google-beta/resource_vertex_ai_index_endpoint_generated_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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 google

import (
"fmt"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"

"github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
)

func TestAccVertexAIIndexEndpoint_vertexAiIndexEndpointExample(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"network_name": BootstrapSharedTestNetwork(t, "vertex-ai-index-endpoint"),
"random_suffix": RandString(t, 10),
}

VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckVertexAIIndexEndpointDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccVertexAIIndexEndpoint_vertexAiIndexEndpointExample(context),
},
{
ResourceName: "google_vertex_ai_index_endpoint.index_endpoint",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"etag", "region"},
},
},
})
}

func testAccVertexAIIndexEndpoint_vertexAiIndexEndpointExample(context map[string]interface{}) string {
return tpgresource.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"
}
network = "projects/${data.google_project.project.number}/global/networks/${data.google_compute_network.vertex_network.name}"
depends_on = [
google_service_networking_connection.vertex_vpc_connection
]
}
resource "google_service_networking_connection" "vertex_vpc_connection" {
network = data.google_compute_network.vertex_network.id
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.vertex_range.name]
}
resource "google_compute_global_address" "vertex_range" {
name = "tf-test-address-name%{random_suffix}"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 24
network = data.google_compute_network.vertex_network.id
}
data "google_compute_network" "vertex_network" {
name = "%{network_name}"
}
data "google_project" "project" {}
`, context)
}

func testAccCheckVertexAIIndexEndpointDestroyProducer(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
for name, rs := range s.RootModule().Resources {
if rs.Type != "google_vertex_ai_index_endpoint" {
continue
}
if strings.HasPrefix(name, "data.") {
continue
}

config := GoogleProviderConfig(t)

url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{VertexAIBasePath}}projects/{{project}}/locations/{{region}}/indexEndpoints/{{name}}")
if err != nil {
return err
}

billingProject := ""

if config.BillingProject != "" {
billingProject = config.BillingProject
}

_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "GET",
Project: billingProject,
RawURL: url,
UserAgent: config.UserAgent,
})
if err == nil {
return fmt.Errorf("VertexAIIndexEndpoint still exists at %s", url)
}
}

return nil
}
}
143 changes: 143 additions & 0 deletions google-beta/resource_vertex_ai_index_endpoint_sweeper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// 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 google

import (
"context"
"log"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"

"github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
)

func init() {
resource.AddTestSweepers("VertexAIIndexEndpoint", &resource.Sweeper{
Name: "VertexAIIndexEndpoint",
F: testSweepVertexAIIndexEndpoint,
})
}

// At the time of writing, the CI only passes us-central1 as the region
func testSweepVertexAIIndexEndpoint(region string) error {
resourceName := "VertexAIIndexEndpoint"
log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName)

config, err := acctest.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 := acctest.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}}/indexEndpoints", "?")[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["indexEndpoints"]
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 !acctest.IsSweepableTestResource(name) {
nonPrefixCount++
continue
}

deleteTemplate := "https://{{region}}-aiplatform.googleapis.com/v1beta1/projects/{{project}}/locations/{{region}}/indexEndpoints/{{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
}
Loading

0 comments on commit 9ea4f75

Please sign in to comment.