Skip to content

Commit

Permalink
Add the vertex endpoint resource. (#6661) (#12858)
Browse files Browse the repository at this point in the history
* Add the vertex endpoint resource.

* Add actions block excluding update for vertex resources that do not use
operations for update.

* Fix declared but not used compile error

* Remove pre_update from vertex entity type and feature.

* Add a handwritten test that includes an update.

* Make endpoint name user-specified.

* Skip the vertex endpoint test.

* Make vertex endpoint name field required, url param only, and immutable.

* Unskip test, add link to cloud console to deployedModels description, and describe format of name field.

Signed-off-by: Modular Magician <magic-modules@google.com>

Signed-off-by: Modular Magician <magic-modules@google.com>
  • Loading branch information
modular-magician committed Oct 21, 2022
1 parent 0f155ac commit 836832f
Show file tree
Hide file tree
Showing 9 changed files with 1,557 additions and 46 deletions.
3 changes: 3 additions & 0 deletions .changelog/6661.txt
@@ -0,0 +1,3 @@
```release-note:new-resource
google_vertex_ai_endpoint
```
5 changes: 3 additions & 2 deletions google/provider.go
Expand Up @@ -910,9 +910,9 @@ func Provider() *schema.Provider {
return provider
}

// Generated resources: 242
// Generated resources: 243
// Generated IAM resources: 147
// Total generated resources: 389
// Total generated resources: 390
func ResourceMap() map[string]*schema.Resource {
resourceMap, _ := ResourceMapWithErrors()
return resourceMap
Expand Down Expand Up @@ -1296,6 +1296,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
"google_tags_tag_binding": resourceTagsTagBinding(),
"google_tpu_node": resourceTPUNode(),
"google_vertex_ai_dataset": resourceVertexAIDataset(),
"google_vertex_ai_endpoint": resourceVertexAIEndpoint(),
"google_vertex_ai_featurestore": resourceVertexAIFeaturestore(),
"google_vertex_ai_featurestore_entitytype": resourceVertexAIFeaturestoreEntitytype(),
"google_vertex_ai_featurestore_entitytype_feature": resourceVertexAIFeaturestoreEntitytypeFeature(),
Expand Down
8 changes: 0 additions & 8 deletions google/resource_vertex_ai_dataset.go
Expand Up @@ -323,14 +323,6 @@ func resourceVertexAIDatasetUpdate(d *schema.ResourceData, meta interface{}) err
log.Printf("[DEBUG] Finished updating Dataset %q: %#v", d.Id(), res)
}

err = vertexAIOperationWaitTime(
config, res, project, "Updating Dataset", userAgent,
d.Timeout(schema.TimeoutUpdate))

if err != nil {
return err
}

return resourceVertexAIDatasetRead(d, meta)
}

Expand Down
966 changes: 966 additions & 0 deletions google/resource_vertex_ai_endpoint.go

Large diffs are not rendered by default.

124 changes: 124 additions & 0 deletions google/resource_vertex_ai_endpoint_sweeper_test.go
@@ -0,0 +1,124 @@
// ----------------------------------------------------------------------------
//
// *** 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"
)

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

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

config, err := 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 := getTestBillingAccountFromEnv(t)

// Setup variables to replace in list template
d := &ResourceDataMock{
FieldsInSchema: map[string]interface{}{
"project": config.Project,
"region": region,
"location": region,
"zone": "-",
"billing_account": billingId,
},
}

listTemplate := strings.Split("https://{{region}}-aiplatform.googleapis.com/v1/projects/{{project}}/locations/{{location}}/endpoints", "?")[0]
listUrl, err := replaceVars(d, config, listTemplate)
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err)
return nil
}

res, err := sendRequest(config, "GET", config.Project, listUrl, config.userAgent, nil)
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err)
return nil
}

resourceList, ok := res["endpoints"]
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 := GetResourceNameFromSelfLink(obj["name"].(string))
// Skip resources that shouldn't be sweeped
if !isSweepableTestResource(name) {
nonPrefixCount++
continue
}

deleteTemplate := "https://{{region}}-aiplatform.googleapis.com/v1/projects/{{project}}/locations/{{location}}/endpoints/{{name}}"
deleteUrl, err := 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 = sendRequest(config, "DELETE", config.Project, deleteUrl, config.userAgent, nil)
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
}
188 changes: 188 additions & 0 deletions google/resource_vertex_ai_endpoint_test.go
@@ -0,0 +1,188 @@
// ----------------------------------------------------------------------------
//
// *** 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"
)

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

context := map[string]interface{}{
"endpoint_name": fmt.Sprint(randInt(t) % 9999999999),
"kms_key_name": BootstrapKMSKeyInLocation(t, "us-central1").CryptoKey.Name,
"network_name": BootstrapSharedTestNetwork(t, "vertex"),
"random_suffix": randString(t, 10),
}

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckVertexAIEndpointDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccVertexAIEndpoint_vertexAiEndpointNetwork(context),
},
{
ResourceName: "google_vertex_ai_endpoint.endpoint",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"etag", "location"},
},
{
Config: testAccVertexAIEndpoint_vertexAiEndpointNetworkUpdate(context),
},
{
ResourceName: "google_vertex_ai_endpoint.endpoint",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"etag", "location"},
},
},
})
}

func testAccVertexAIEndpoint_vertexAiEndpointNetwork(context map[string]interface{}) string {
return Nprintf(`
resource "google_vertex_ai_endpoint" "endpoint" {
name = "%{endpoint_name}"
display_name = "sample-endpoint"
description = "A sample vertex endpoint"
location = "us-central1"
labels = {
label-one = "value-one"
}
network = "projects/${data.google_project.project.number}/global/networks/${data.google_compute_network.vertex_network.name}"
encryption_spec {
kms_key_name = "%{kms_key_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}"
}
resource "google_kms_crypto_key_iam_member" "crypto_key" {
crypto_key_id = "%{kms_key_name}"
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-aiplatform.iam.gserviceaccount.com"
}
data "google_project" "project" {}
`, context)
}

func testAccVertexAIEndpoint_vertexAiEndpointNetworkUpdate(context map[string]interface{}) string {
return Nprintf(`
resource "google_vertex_ai_endpoint" "endpoint" {
name = "%{endpoint_name}"
display_name = "new-sample-endpoint"
description = "An updated sample vertex endpoint"
location = "us-central1"
labels = {
label-two = "value-two"
}
network = "projects/${data.google_project.project.number}/global/networks/${data.google_compute_network.vertex_network.name}"
encryption_spec {
kms_key_name = "%{kms_key_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}"
}
resource "google_kms_crypto_key_iam_member" "crypto_key" {
crypto_key_id = "%{kms_key_name}"
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-aiplatform.iam.gserviceaccount.com"
}
data "google_project" "project" {}
`, context)
}

func testAccCheckVertexAIEndpointDestroyProducer(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_endpoint" {
continue
}
if strings.HasPrefix(name, "data.") {
continue
}

config := googleProviderConfig(t)

url, err := replaceVarsForTest(config, rs, "{{VertexAIBasePath}}projects/{{project}}/locations/{{location}}/endpoints/{{name}}")
if err != nil {
return err
}

billingProject := ""

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

_, err = sendRequest(config, "GET", billingProject, url, config.userAgent, nil)
if err == nil {
return fmt.Errorf("VertexAIEndpoint still exists at %s", url)
}
}

return nil
}
}
18 changes: 0 additions & 18 deletions google/resource_vertex_ai_featurestore_entitytype.go
Expand Up @@ -231,7 +231,6 @@ func resourceVertexAIFeaturestoreEntitytypeRead(d *schema.ResourceData, meta int
}

func resourceVertexAIFeaturestoreEntitytypeUpdate(d *schema.ResourceData, meta interface{}) error {
var project string
config := meta.(*Config)
userAgent, err := generateUserAgentString(d, config.userAgent)
if err != nil {
Expand Down Expand Up @@ -275,15 +274,6 @@ func resourceVertexAIFeaturestoreEntitytypeUpdate(d *schema.ResourceData, meta i
if err != nil {
return err
}
if v, ok := d.GetOk("featurestore"); ok {
re := regexp.MustCompile("projects/([a-zA-Z0-9-]*)/(?:locations|regions)/([a-zA-Z0-9-]*)")
switch {
case re.MatchString(v.(string)):
if res := re.FindStringSubmatch(v.(string)); len(res) == 3 && res[1] != "" {
project = res[1]
}
}
}

// err == nil indicates that the billing_project value was found
if bp, err := getBillingProject(d, config); err == nil {
Expand All @@ -298,14 +288,6 @@ func resourceVertexAIFeaturestoreEntitytypeUpdate(d *schema.ResourceData, meta i
log.Printf("[DEBUG] Finished updating FeaturestoreEntitytype %q: %#v", d.Id(), res)
}

err = vertexAIOperationWaitTime(
config, res, project, "Updating FeaturestoreEntitytype", userAgent,
d.Timeout(schema.TimeoutUpdate))

if err != nil {
return err
}

return resourceVertexAIFeaturestoreEntitytypeRead(d, meta)
}

Expand Down

0 comments on commit 836832f

Please sign in to comment.