diff --git a/.changelog/16112.txt b/.changelog/16112.txt new file mode 100644 index 00000000000..7b9a1c24a0b --- /dev/null +++ b/.changelog/16112.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +vertex_ai: added `deployment_spec.psc_interface_config` to `google_vertex_ai_reasoning_engine` +``` \ No newline at end of file diff --git a/google/services/vertexai/resource_vertex_ai_reasoning_engine.go b/google/services/vertexai/resource_vertex_ai_reasoning_engine.go index b2bcb8a0be5..4cb02e26928 100644 --- a/google/services/vertexai/resource_vertex_ai_reasoning_engine.go +++ b/google/services/vertexai/resource_vertex_ai_reasoning_engine.go @@ -204,6 +204,59 @@ range is [1, 100].`, Description: `Optional. The minimum number of application instances that will be kept running at all times. Defaults to 1. Range: [0, 10].`, }, + "psc_interface_config": { + Type: schema.TypeList, + Optional: true, + Description: `Optional. Configuration for PSC-Interface.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dns_peering_configs": { + Type: schema.TypeList, + Optional: true, + Description: `Optional. DNS peering configurations. +When specified, Vertex AI will attempt to configure DNS +peering zones in the tenant project VPC to resolve the +specified domains using the target network's Cloud DNS. +The user must grant the dns.peer role to the Vertex AI +service Agent on the target project.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "domain": { + Type: schema.TypeString, + Required: true, + Description: `Required. The DNS name suffix of the zone being peered +to, e.g., "my-internal-domain.corp.". +Must end with a dot.`, + }, + "target_network": { + Type: schema.TypeString, + Required: true, + Description: `Required. The VPC network name in the targetProject +where the DNS zone specified by 'domain' is visible.`, + }, + "target_project": { + Type: schema.TypeString, + Required: true, + Description: `Required. The project id hosting the Cloud DNS managed +zone that contains the 'domain'. +The Vertex AI service Agent requires the dns.peer role +on this project.`, + }, + }, + }, + }, + "network_attachment": { + Type: schema.TypeString, + Optional: true, + Description: `Optional. The name of the Compute Engine network attachment +to attach to the resource within the region and user project. +To specify this field, you must have already created a network attachment. +This field is only used for resources using PSC-Interface.`, + }, + }, + }, + }, "resource_limits": { Type: schema.TypeMap, Computed: true, @@ -876,6 +929,8 @@ func flattenVertexAIReasoningEngineSpecDeploymentSpec(v interface{}, d *schema.R flattenVertexAIReasoningEngineSpecDeploymentSpecEnv(original["env"], d, config) transformed["secret_env"] = flattenVertexAIReasoningEngineSpecDeploymentSpecSecretEnv(original["secretEnv"], d, config) + transformed["psc_interface_config"] = + flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfig(original["pscInterfaceConfig"], d, config) transformed["resource_limits"] = flattenVertexAIReasoningEngineSpecDeploymentSpecResourceLimits(original["resourceLimits"], d, config) transformed["min_instances"] = @@ -959,6 +1014,57 @@ func flattenVertexAIReasoningEngineSpecDeploymentSpecSecretEnvSecretRefVersion(v return v } +func flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfig(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["network_attachment"] = + flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigNetworkAttachment(original["networkAttachment"], d, config) + transformed["dns_peering_configs"] = + flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigs(original["dnsPeeringConfigs"], d, config) + return []interface{}{transformed} +} +func flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigNetworkAttachment(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigs(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "domain": flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsDomain(original["domain"], d, config), + "target_project": flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsTargetProject(original["targetProject"], d, config), + "target_network": flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsTargetNetwork(original["targetNetwork"], d, config), + }) + } + return transformed +} +func flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsDomain(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsTargetProject(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsTargetNetwork(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + func flattenVertexAIReasoningEngineSpecDeploymentSpecResourceLimits(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { return v } @@ -1243,6 +1349,13 @@ func expandVertexAIReasoningEngineSpecDeploymentSpec(v interface{}, d tpgresourc transformed["secretEnv"] = transformedSecretEnv } + transformedPscInterfaceConfig, err := expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfig(original["psc_interface_config"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPscInterfaceConfig); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["pscInterfaceConfig"] = transformedPscInterfaceConfig + } + transformedResourceLimits, err := expandVertexAIReasoningEngineSpecDeploymentSpecResourceLimits(original["resource_limits"], d, config) if err != nil { return nil, err @@ -1389,6 +1502,90 @@ func expandVertexAIReasoningEngineSpecDeploymentSpecSecretEnvSecretRefVersion(v return v, nil } +func expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + if v == nil { + return nil, nil + } + 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{}) + + transformedNetworkAttachment, err := expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigNetworkAttachment(original["network_attachment"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNetworkAttachment); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["networkAttachment"] = transformedNetworkAttachment + } + + transformedDnsPeeringConfigs, err := expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigs(original["dns_peering_configs"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDnsPeeringConfigs); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["dnsPeeringConfigs"] = transformedDnsPeeringConfigs + } + + return transformed, nil +} + +func expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigNetworkAttachment(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigs(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + if v == nil { + return nil, nil + } + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDomain, err := expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsDomain(original["domain"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDomain); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["domain"] = transformedDomain + } + + transformedTargetProject, err := expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsTargetProject(original["target_project"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTargetProject); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["targetProject"] = transformedTargetProject + } + + transformedTargetNetwork, err := expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsTargetNetwork(original["target_network"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTargetNetwork); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["targetNetwork"] = transformedTargetNetwork + } + + req = append(req, transformed) + } + return req, nil +} + +func expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsDomain(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsTargetProject(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandVertexAIReasoningEngineSpecDeploymentSpecPscInterfaceConfigDnsPeeringConfigsTargetNetwork(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + func expandVertexAIReasoningEngineSpecDeploymentSpecResourceLimits(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/services/vertexai/resource_vertex_ai_reasoning_engine_generated_meta.yaml b/google/services/vertexai/resource_vertex_ai_reasoning_engine_generated_meta.yaml index a938d219ffb..8382c48dda8 100644 --- a/google/services/vertexai/resource_vertex_ai_reasoning_engine_generated_meta.yaml +++ b/google/services/vertexai/resource_vertex_ai_reasoning_engine_generated_meta.yaml @@ -20,6 +20,10 @@ fields: - api_field: spec.deploymentSpec.env.value - api_field: spec.deploymentSpec.maxInstances - api_field: spec.deploymentSpec.minInstances + - api_field: spec.deploymentSpec.pscInterfaceConfig.dnsPeeringConfigs.domain + - api_field: spec.deploymentSpec.pscInterfaceConfig.dnsPeeringConfigs.targetNetwork + - api_field: spec.deploymentSpec.pscInterfaceConfig.dnsPeeringConfigs.targetProject + - api_field: spec.deploymentSpec.pscInterfaceConfig.networkAttachment - api_field: spec.deploymentSpec.resourceLimits - api_field: spec.deploymentSpec.secretEnv.name - api_field: spec.deploymentSpec.secretEnv.secretRef.secret diff --git a/website/docs/r/vertex_ai_reasoning_engine.html.markdown b/website/docs/r/vertex_ai_reasoning_engine.html.markdown index 10070194540..c62f5595a42 100644 --- a/website/docs/r/vertex_ai_reasoning_engine.html.markdown +++ b/website/docs/r/vertex_ai_reasoning_engine.html.markdown @@ -60,6 +60,108 @@ resource "google_vertex_ai_reasoning_engine" "reasoning_engine" { } } ``` +## Example Usage - Vertex Ai Reasoning Engine Psc Interface + + +```hcl +# When PSC-I is configured, Agent deletion will fail, +# although the agent will be deleted. +# Bug at https://github.com/hashicorp/terraform-provider-google/issues/25637 + +resource "google_vertex_ai_reasoning_engine" "reasoning_engine" { + display_name = "reasoning-engine" + description = "A basic reasoning engine" + region = "us-central1" + + spec { + agent_framework = "google-adk" + + package_spec { + python_version = "3.11" + dependency_files_gcs_uri = "${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_dependencies_tar_gz.name}" + pickle_object_gcs_uri = "${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_pickle.name}" + requirements_gcs_uri = "${google_storage_bucket.bucket.url}/${google_storage_bucket_object.bucket_obj_requirements_txt.name}" + } + + deployment_spec { + + psc_interface_config { + network_attachment = google_compute_network_attachment.network_attachment.id + + dns_peering_configs { + domain = "example.com." + target_project = data.google_project.project.project_id + target_network = google_compute_network.network.name + } + } + } + } + + depends_on = [ + time_sleep.wait_35_minutes + ] +} + +resource "google_storage_bucket_object" "bucket_obj_requirements_txt" { + name = "requirements.txt" + bucket = google_storage_bucket.bucket.id + source = "./test-fixtures/requirements_adk.txt" +} + +resource "google_storage_bucket_object" "bucket_obj_pickle" { + name = "code.pkl" + bucket = google_storage_bucket.bucket.id + source = "./test-fixtures/pickle_adk.pkl" +} + +resource "google_storage_bucket_object" "bucket_obj_dependencies_tar_gz" { + name = "dependencies.tar.gz" + bucket = google_storage_bucket.bucket.id + source = "./test-fixtures/dependencies_adk.tar.gz" +} + +resource "google_storage_bucket" "bucket" { + name = "reasoning-engine" + location = "us-central1" + uniform_bucket_level_access = true + force_destroy = true +} + +# Destroy network attachment 35 minutes after reasoning engine is deleted. +# It guarantees that the network attachment has no more active PSC interfaces. +resource "time_sleep" "wait_35_minutes" { + destroy_duration = "35m" + + depends_on = [ + google_compute_network_attachment.network_attachment + ] +} + +resource "google_compute_network_attachment" "network_attachment" { + name = "network-attachment" + region = "us-central1" + connection_preference = "ACCEPT_MANUAL" + + subnetworks = [ + google_compute_subnetwork.subnetwork.id + ] +} + +resource "google_compute_subnetwork" "subnetwork" { + name = "subnetwork" + region = "us-central1" + ip_cidr_range = "10.0.0.0/16" + network = google_compute_network.network.id +} + +resource "google_compute_network" "network" { + name = "network" + auto_create_subnetworks = false +} + +data "google_project" "project" { +} +```
Open in Cloud Shell @@ -344,6 +446,11 @@ The following arguments are supported: Platform Reasoning Engine service Agent. Structure is [documented below](#nested_spec_deployment_spec_secret_env). +* `psc_interface_config` - + (Optional) + Optional. Configuration for PSC-Interface. + Structure is [documented below](#nested_spec_deployment_spec_psc_interface_config). + * `resource_limits` - (Optional) Optional. Resource limits for each container. @@ -419,6 +526,46 @@ The following arguments are supported: for the latest version, an integer for a specific version, or a version alias. +The `psc_interface_config` block supports: + +* `network_attachment` - + (Optional) + Optional. The name of the Compute Engine network attachment + to attach to the resource within the region and user project. + To specify this field, you must have already created a network attachment. + This field is only used for resources using PSC-Interface. + +* `dns_peering_configs` - + (Optional) + Optional. DNS peering configurations. + When specified, Vertex AI will attempt to configure DNS + peering zones in the tenant project VPC to resolve the + specified domains using the target network's Cloud DNS. + The user must grant the dns.peer role to the Vertex AI + service Agent on the target project. + Structure is [documented below](#nested_spec_deployment_spec_psc_interface_config_dns_peering_configs). + + +The `dns_peering_configs` block supports: + +* `domain` - + (Required) + Required. The DNS name suffix of the zone being peered + to, e.g., "my-internal-domain.corp.". + Must end with a dot. + +* `target_project` - + (Required) + Required. The project id hosting the Cloud DNS managed + zone that contains the 'domain'. + The Vertex AI service Agent requires the dns.peer role + on this project. + +* `target_network` - + (Required) + Required. The VPC network name in the targetProject + where the DNS zone specified by 'domain' is visible. + The `package_spec` block supports: * `dependency_files_gcs_uri` -