From 47e1916e19d2c3fb69a06b1ae5d6cd3cd9cf5c68 Mon Sep 17 00:00:00 2001 From: Max Portocarrero CI&T <105444618+maxi-cit@users.noreply.github.com> Date: Tue, 14 May 2024 16:59:05 -0500 Subject: [PATCH] Add lb traffic extension resource (#10121) Co-authored-by: pweiber --- .../networkservices/LbTrafficExtension.yaml | 185 ++++ ...services_lb_traffic_extension_basic.tf.erb | 337 ++++++++ ...work_services_lb_traffic_extension_test.go | 801 ++++++++++++++++++ 3 files changed, 1323 insertions(+) create mode 100644 mmv1/products/networkservices/LbTrafficExtension.yaml create mode 100644 mmv1/templates/terraform/examples/network_services_lb_traffic_extension_basic.tf.erb create mode 100644 mmv1/third_party/terraform/services/networkservices/resource_network_services_lb_traffic_extension_test.go diff --git a/mmv1/products/networkservices/LbTrafficExtension.yaml b/mmv1/products/networkservices/LbTrafficExtension.yaml new file mode 100644 index 000000000000..0c307c54f7f6 --- /dev/null +++ b/mmv1/products/networkservices/LbTrafficExtension.yaml @@ -0,0 +1,185 @@ +# Copyright 2024 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- !ruby/object:Api::Resource +name: 'LbTrafficExtension' +description: | + LbTrafficExtension is a resource that lets the extension service modify the headers and payloads of both requests and responses without impacting the choice of backend services or any other security policies associated with the backend service. +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Configure a traffic extension': 'https://cloud.google.com/service-extensions/docs/configure-callout#configure_a_traffic_extension' + api: 'https://cloud.google.com/service-extensions/docs/reference/rest/v1beta1/projects.locations.lbTrafficExtensions' +base_url: 'projects/{{project}}/locations/{{location}}/lbTrafficExtensions' +self_link: 'projects/{{project}}/locations/{{location}}/lbTrafficExtensions/{{name}}' +timeouts: !ruby/object:Api::Timeouts + insert_minutes: 20 + update_minutes: 20 + delete_minutes: 20 +create_url: 'projects/{{project}}/locations/{{location}}/lbTrafficExtensions?lbTrafficExtensionId={{name}}' +update_verb: :PATCH +update_mask: true +autogen_async: true +async: !ruby/object:Api::OpAsync + operation: !ruby/object:Api::OpAsync::Operation + base_url: '{{op_id}}' +examples: + - !ruby/object:Provider::Terraform::Examples + name: 'network_services_lb_traffic_extension_basic' + primary_resource_id: 'default' + vars: + ilb_network_name: 'l7-ilb-network' + proxy_subnet_name: 'l7-ilb-proxy-subnet' + backend_subnet_name: 'l7-ilb-subnet' + forwarding_rule_name: 'l7-ilb-forwarding-rule' + target_http_proxy_name: 'l7-ilb-target-http-proxy' + regional_url_map_name: 'l7-ilb-regional-url-map' + backend_service_name: 'l7-ilb-backend-subnet' + mig_template_name: 'l7-ilb-mig-template' + hc_name: 'l7-ilb-hc' + mig_name: 'l7-ilb-mig1' + fw_allow_iap_hc_name: 'l7-ilb-fw-allow-iap-hc' + fw_allow_ilb_to_backends_name: 'l7-ilb-fw-allow-ilb-to-backends' + vm_test_name: 'l7-ilb-test-vm' + lb_traffic_extension_name: 'l7-ilb-traffic-ext' + callouts_instance_name: 'l7-ilb-callouts-ins' + callouts_instance_group: 'l7-ilb-callouts-ins-group' + callouts_hc_name: 'l7-ilb-callouts-hc' + callouts_backend_name: 'l7-ilb-callouts-backend' +parameters: + - !ruby/object:Api::Type::String + name: 'location' + required: true + immutable: true + url_param_only: true + description: | + The location of the traffic extension + - !ruby/object:Api::Type::String + name: 'name' + required: true + immutable: true + url_param_only: true + description: | + Name of the LbTrafficExtension resource in the following format: projects/{project}/locations/{location}/lbTrafficExtensions/{lbTrafficExtension}. +properties: + - !ruby/object:Api::Type::String + name: 'description' + description: | + A human-readable description of the resource. + - !ruby/object:Api::Type::KeyValueLabels + name: 'labels' + description: 'Set of labels associated with the LbTrafficExtension resource.' + - !ruby/object:Api::Type::Array + name: forwardingRules + description: | + A list of references to the forwarding rules to which this service extension is attached to. + At least one forwarding rule is required. There can be only one LBTrafficExtension resource per forwarding rule. + required: true + item_type: Api::Type::String + diff_suppress_func: 'tpgresource.ProjectNumberDiffSuppress' + - !ruby/object:Api::Type::Array + name: extensionChains + description: | + A set of ordered extension chains that contain the match conditions and extensions to execute. + Match conditions for each extension chain are evaluated in sequence for a given request. + The first extension chain that has a condition that matches the request is executed. + Any subsequent extension chains do not execute. Limited to 5 extension chains per resource. + required: true + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'name' + description: | + The name for this extension chain. The name is logged as part of the HTTP request logs. + The name must conform with RFC-1034, is restricted to lower-cased letters, numbers and hyphens, + and can have a maximum length of 63 characters. Additionally, the first character must be a letter + and the last a letter or a number. + required: true + - !ruby/object:Api::Type::NestedObject + name: 'matchCondition' + description: | + Conditions under which this chain is invoked for a request. + required: true + properties: + - !ruby/object:Api::Type::String + name: 'celExpression' + description: | + A Common Expression Language (CEL) expression that is used to match requests for which the extension chain is executed. + required: true + - !ruby/object:Api::Type::Array + name: 'extensions' + description: | + A set of extensions to execute for the matching request. + At least one extension is required. Up to 3 extensions can be defined for each extension chain for + LbTrafficExtension resource. LbRouteExtension chains are limited to 1 extension per extension chain. + required: true + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'name' + description: | + The name for this extension. The name is logged as part of the HTTP request logs. + The name must conform with RFC-1034, is restricted to lower-cased letters, numbers and hyphens, + and can have a maximum length of 63 characters. Additionally, the first character must be a letter + and the last a letter or a number. + required: true + - !ruby/object:Api::Type::String + name: 'authority' + description: | + The :authority header in the gRPC request sent from Envoy to the extension service. + required: true + - !ruby/object:Api::Type::String + name: 'service' + description: | + The reference to the service that runs the extension. Must be a reference to a backend service + required: true + diff_suppress_func: 'tpgresource.ProjectNumberDiffSuppress' + - !ruby/object:Api::Type::String + name: 'timeout' + description: | + Specifies the timeout for each individual message on the stream. The timeout must be between 10-1000 milliseconds. + A duration in seconds with up to nine fractional digits, ending with 's'. Example: "3.5s". + required: true + diff_suppress_func: 'tpgresource.DurationDiffSuppress' + - !ruby/object:Api::Type::Boolean + name: 'failOpen' + description: | + Determines how the proxy behaves if the call to the extension fails or times out. + When set to TRUE, request or response processing continues without error. + Any subsequent extensions in the extension chain are also executed. + When set to FALSE: * If response headers have not been delivered to the downstream client, + a generic 500 error is returned to the client. The error response can be tailored by + configuring a custom error response in the load balancer. + - !ruby/object:Api::Type::Array + name: 'forwardHeaders' + description: | + List of the HTTP headers to forward to the extension (from the client or backend). + If omitted, all headers are sent. Each element is a string indicating the header name. + item_type: Api::Type::String + - !ruby/object:Api::Type::Array + name: 'supportedEvents' + min_size: 1 + description: | + A set of events during request or response processing for which this extension is called. + This field is required for the LbTrafficExtension resource. It's not relevant for the LbRouteExtension + resource. Possible values:`EVENT_TYPE_UNSPECIFIED`, `REQUEST_HEADERS`, `REQUEST_BODY`, `RESPONSE_HEADERS`, + `RESPONSE_BODY`, `RESPONSE_BODY` and `RESPONSE_BODY`. + item_type: Api::Type::String + - !ruby/object:Api::Type::Enum + name: 'loadBalancingScheme' + description: | + All backend services and forwarding rules referenced by this extension must share the same load balancing scheme. + For more information, refer to Choosing a load balancer. + values: + - :LOAD_BALANCING_SCHEME_UNSPECIFIED + - :INTERNAL_MANAGED + - :EXTERNAL_MANAGED diff --git a/mmv1/templates/terraform/examples/network_services_lb_traffic_extension_basic.tf.erb b/mmv1/templates/terraform/examples/network_services_lb_traffic_extension_basic.tf.erb new file mode 100644 index 000000000000..a2ff13074a89 --- /dev/null +++ b/mmv1/templates/terraform/examples/network_services_lb_traffic_extension_basic.tf.erb @@ -0,0 +1,337 @@ +# Internal HTTP load balancer with a managed instance group backend +# VPC network +resource "google_compute_network" "ilb_network" { + name = "<%= ctx[:vars]['ilb_network_name'] %>" + auto_create_subnetworks = false +} + +# proxy-only subnet +resource "google_compute_subnetwork" "proxy_subnet" { + name = "<%= ctx[:vars]['proxy_subnet_name'] %>" + ip_cidr_range = "10.0.0.0/24" + region = "us-west1" + purpose = "REGIONAL_MANAGED_PROXY" + role = "ACTIVE" + network = google_compute_network.ilb_network.id +} + +# backend subnet +resource "google_compute_subnetwork" "ilb_subnet" { + name = "<%= ctx[:vars]['backend_subnet_name'] %>" + ip_cidr_range = "10.0.1.0/24" + region = "us-west1" + network = google_compute_network.ilb_network.id + + depends_on = [ + google_compute_subnetwork.proxy_subnet + ] +} + +# forwarding rule +resource "google_compute_forwarding_rule" "default" { + name = "<%= ctx[:vars]['forwarding_rule_name'] %>" + region = "us-west1" + ip_protocol = "TCP" + load_balancing_scheme = "INTERNAL_MANAGED" + port_range = "80" + target = google_compute_region_target_http_proxy.default.id + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id + network_tier = "PREMIUM" + + depends_on = [ + google_compute_subnetwork.proxy_subnet + ] +} + +# HTTP target proxy +resource "google_compute_region_target_http_proxy" "default" { + name = "<%= ctx[:vars]['target_http_proxy_name'] %>" + region = "us-west1" + url_map = google_compute_region_url_map.default.id +} + +# URL map +resource "google_compute_region_url_map" "default" { + name = "<%= ctx[:vars]['regional_url_map_name'] %>" + region = "us-west1" + default_service = google_compute_region_backend_service.default.id +} + +# backend service +resource "google_compute_region_backend_service" "default" { + name = "<%= ctx[:vars]['backend_service_name'] %>" + region = "us-west1" + protocol = "HTTP" + load_balancing_scheme = "INTERNAL_MANAGED" + timeout_sec = 10 + health_checks = [google_compute_region_health_check.default.id] + + backend { + group = google_compute_region_instance_group_manager.mig.instance_group + balancing_mode = "UTILIZATION" + capacity_scaler = 1.0 + } +} + +# instance template +resource "google_compute_instance_template" "instance_template" { + name = "<%= ctx[:vars]['mig_template_name'] %>" + machine_type = "e2-small" + tags = ["http-server"] + + network_interface { + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id + + access_config { + # add external ip to fetch packages + } + } + + disk { + source_image = "debian-cloud/debian-10" + auto_delete = true + boot = true + } + + # install nginx and serve a simple web page + metadata = { + startup-script = <<-EOF1 + #! /bin/bash + set -euo pipefail + + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y nginx-light jq + + NAME=$(curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/hostname") + IP=$(curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip") + METADATA=$(curl -f -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/attributes/?recursive=True" | jq 'del(.["startup-script"])') + + cat < /var/www/html/index.html +
+      Name: $NAME
+      IP: $IP
+      Metadata: $METADATA
+      
+ EOF + EOF1 + } + + lifecycle { + create_before_destroy = true + } +} + +# health check +resource "google_compute_region_health_check" "default" { + name = "<%= ctx[:vars]['hc_name'] %>" + region = "us-west1" + + http_health_check { + port_specification = "USE_SERVING_PORT" + } +} + +# MIG +resource "google_compute_region_instance_group_manager" "mig" { + name = "<%= ctx[:vars]['mig_name'] %>" + region = "us-west1" + + base_instance_name = "vm" + target_size = 2 + + version { + instance_template = google_compute_instance_template.instance_template.id + name = "primary" + } +} + +# allow all access from IAP and health check ranges +resource "google_compute_firewall" "fw_iap" { + name = "<%= ctx[:vars]['fw_allow_iap_hc_name'] %>" + direction = "INGRESS" + network = google_compute_network.ilb_network.id + source_ranges = ["130.211.0.0/22", "35.191.0.0/16", "35.235.240.0/20"] + + allow { + protocol = "tcp" + } +} + +# allow http from proxy subnet to backends +resource "google_compute_firewall" "fw_ilb_to_backends" { + name = "<%= ctx[:vars]['fw_allow_ilb_to_backends_name'] %>" + direction = "INGRESS" + network = google_compute_network.ilb_network.id + source_ranges = ["10.0.0.0/24"] + target_tags = ["http-server"] + + allow { + protocol = "tcp" + ports = ["80", "443", "8080"] + } + + depends_on = [ + google_compute_firewall.fw_iap + ] +} +# [END cloudloadbalancing_int_http_gce] + +# [START lb_traffic_extension] +resource "google_network_services_lb_traffic_extension" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['lb_traffic_extension_name'] %>" + description = "my traffic extension" + location = "us-west1" + + load_balancing_scheme = "INTERNAL_MANAGED" + forwarding_rules = [google_compute_forwarding_rule.default.self_link] + + extension_chains { + name = "chain1" + + match_condition { + cel_expression = "request.host == 'example.com'" + } + + extensions { + name = "ext11" + authority = "ext11.com" + service = google_compute_region_backend_service.callouts_backend.self_link + timeout = "0.1s" + fail_open = false + + supported_events = ["REQUEST_HEADERS"] + forward_headers = ["custom-header"] + } + } + + labels = { + foo = "bar" + } +} + +# test instance +resource "google_compute_instance" "vm_test" { + name = "<%= ctx[:vars]['vm_test_name'] %>" + zone = "us-west1-b" + machine_type = "e2-small" + + network_interface { + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id + } + + boot_disk { + initialize_params { + image = "debian-cloud/debian-11" + } + } +} + +# Traffic Extension Backend Instance +resource "google_compute_instance" "callouts_instance" { + name = "<%= ctx[:vars]['callouts_instance_name'] %>" + zone = "us-west1-a" + machine_type = "e2-small" + + labels = { + "container-vm" = "cos-stable-109-17800-147-54" + } + + tags = ["allow-ssh","load-balanced-backend"] + + network_interface { + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id + + access_config { + # add external ip to fetch packages + } + + } + + boot_disk { + auto_delete = true + + initialize_params { + type = "pd-standard" + size = 10 + image = "https://www.googleapis.com/compute/v1/projects/cos-cloud/global/images/cos-stable-109-17800-147-54" + } + } + + # Initialize an Envoy's Ext Proc gRPC API based on a docker container + metadata = { + gce-container-declaration = "# DISCLAIMER:\n# This container declaration format is not a public API and may change without\n# notice. Please use gcloud command-line tool or Google Cloud Console to run\n# Containers on Google Compute Engine.\n\nspec:\n containers:\n - image: us-docker.pkg.dev/service-extensions/ext-proc/service-callout-basic-example-python:latest\n name: callouts-vm\n securityContext:\n privileged: false\n stdin: false\n tty: false\n volumeMounts: []\n restartPolicy: Always\n volumes: []\n" + google-logging-enabled = "true" + } + + lifecycle { + create_before_destroy = true + } + + deletion_protection = false + + depends_on = [ + google_compute_instance.vm_test + ] +} + +// callouts instance group +resource "google_compute_instance_group" "callouts_instance_group" { + name = "<%= ctx[:vars]['callouts_instance_group'] %>" + description = "Terraform test instance group" + zone = "us-west1-a" + + instances = [ + google_compute_instance.callouts_instance.id, + ] + + named_port { + name = "http" + port = "80" + } + + named_port { + name = "grpc" + port = "443" + } +} + +# callout health check +resource "google_compute_region_health_check" "callouts_health_check" { + name = "<%= ctx[:vars]['callouts_hc_name'] %>" + region = "us-west1" + + http_health_check { + port = 80 + } + + depends_on = [ + google_compute_region_health_check.default + ] +} + +# callout backend service +resource "google_compute_region_backend_service" "callouts_backend" { + name = "<%= ctx[:vars]['callouts_backend_name'] %>" + region = "us-west1" + protocol = "HTTP2" + load_balancing_scheme = "INTERNAL_MANAGED" + timeout_sec = 10 + port_name = "grpc" + health_checks = [google_compute_region_health_check.callouts_health_check.id] + + backend { + group = google_compute_instance_group.callouts_instance_group.id + balancing_mode = "UTILIZATION" + capacity_scaler = 1.0 + } + + depends_on = [ + google_compute_region_backend_service.default + ] +} +# [END lb_traffic_extension] diff --git a/mmv1/third_party/terraform/services/networkservices/resource_network_services_lb_traffic_extension_test.go b/mmv1/third_party/terraform/services/networkservices/resource_network_services_lb_traffic_extension_test.go new file mode 100644 index 000000000000..23be8baec0e1 --- /dev/null +++ b/mmv1/third_party/terraform/services/networkservices/resource_network_services_lb_traffic_extension_test.go @@ -0,0 +1,801 @@ +package networkservices_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccNetworkServicesLbTrafficExtension_update(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: testAccCheckNetworkServicesLbTrafficExtensionDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetworkServicesLbTrafficExtension_basic(context), + }, + { + ResourceName: "google_network_services_lb_traffic_extension.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "name", "labels", "terraform_labels"}, + }, + { + Config: testAccNetworkServicesLbTrafficExtension_update(context), + }, + { + ResourceName: "google_network_services_lb_traffic_extension.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "name", "labels", "terraform_labels"}, + }, + }, + }) +} + +func testAccNetworkServicesLbTrafficExtension_basic(context map[string]interface{}) string { + return acctest.Nprintf(` +# Internal HTTP load balancer with a managed instance group backend +# VPC network +resource "google_compute_network" "ilb_network" { + name = "tf-test-l7-ilb-network%{random_suffix}" + auto_create_subnetworks = false +} + +# proxy-only subnet +resource "google_compute_subnetwork" "proxy_subnet" { + name = "tf-test-l7-ilb-proxy-subnet%{random_suffix}" + ip_cidr_range = "10.0.0.0/24" + region = "us-west1" + purpose = "REGIONAL_MANAGED_PROXY" + role = "ACTIVE" + network = google_compute_network.ilb_network.id +} + +# backend subnet +resource "google_compute_subnetwork" "ilb_subnet" { + name = "tf-test-l7-ilb-subnet%{random_suffix}" + ip_cidr_range = "10.0.1.0/24" + region = "us-west1" + network = google_compute_network.ilb_network.id + + depends_on = [ + google_compute_subnetwork.proxy_subnet + ] +} + +# forwarding rule +resource "google_compute_forwarding_rule" "default" { + name = "tf-test-l7-ilb-forwarding-rule%{random_suffix}" + region = "us-west1" + ip_protocol = "TCP" + load_balancing_scheme = "INTERNAL_MANAGED" + port_range = "80" + target = google_compute_region_target_http_proxy.default.id + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id + network_tier = "PREMIUM" + + depends_on = [ + google_compute_subnetwork.proxy_subnet + ] +} + +# HTTP target proxy +resource "google_compute_region_target_http_proxy" "default" { + name = "tf-test-l7-ilb-target-http-proxy%{random_suffix}" + region = "us-west1" + url_map = google_compute_region_url_map.default.id +} + +# URL map +resource "google_compute_region_url_map" "default" { + name = "tf-test-l7-ilb-regional-url-map%{random_suffix}" + region = "us-west1" + default_service = google_compute_region_backend_service.default.id +} + +# backend service +resource "google_compute_region_backend_service" "default" { + name = "tf-test-l7-ilb-backend-subnet%{random_suffix}" + region = "us-west1" + protocol = "HTTP" + load_balancing_scheme = "INTERNAL_MANAGED" + timeout_sec = 10 + health_checks = [google_compute_region_health_check.default.id] + + backend { + group = google_compute_region_instance_group_manager.mig.instance_group + balancing_mode = "UTILIZATION" + capacity_scaler = 1.0 + } +} + +# instance template +resource "google_compute_instance_template" "instance_template" { + name = "tf-test-l7-ilb-mig-template%{random_suffix}" + machine_type = "e2-small" + tags = ["http-server"] + + network_interface { + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id + + access_config { + # add external ip to fetch packages + } + } + + disk { + source_image = "debian-cloud/debian-10" + auto_delete = true + boot = true + } + + # install nginx and serve a simple web page + metadata = { + startup-script = <<-EOF1 + #! /bin/bash + set -euo pipefail + + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y nginx-light jq + + NAME=$(curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/hostname") + IP=$(curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip") + METADATA=$(curl -f -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/attributes/?recursive=True" | jq 'del(.["startup-script"])') + + cat < /var/www/html/index.html +
+      Name: $NAME
+      IP: $IP
+      Metadata: $METADATA
+      
+ EOF + EOF1 + } + + lifecycle { + create_before_destroy = true + } +} + +# health check +resource "google_compute_region_health_check" "default" { + name = "tf-test-l7-ilb-hc%{random_suffix}" + region = "us-west1" + + http_health_check { + port_specification = "USE_SERVING_PORT" + } +} + +# MIG +resource "google_compute_region_instance_group_manager" "mig" { + name = "tf-test-l7-ilb-mig1%{random_suffix}" + region = "us-west1" + + base_instance_name = "vm" + target_size = 2 + + version { + instance_template = google_compute_instance_template.instance_template.id + name = "primary" + } +} + +# allow all access from IAP and health check ranges +resource "google_compute_firewall" "fw_iap" { + name = "tf-test-l7-ilb-fw-allow-iap-hc%{random_suffix}" + direction = "INGRESS" + network = google_compute_network.ilb_network.id + source_ranges = ["130.211.0.0/22", "35.191.0.0/16", "35.235.240.0/20"] + + allow { + protocol = "tcp" + } +} + +# allow http from proxy subnet to backends +resource "google_compute_firewall" "fw_ilb_to_backends" { + name = "tf-test-l7-ilb-fw-allow-ilb-to-backends%{random_suffix}" + direction = "INGRESS" + network = google_compute_network.ilb_network.id + source_ranges = ["10.0.0.0/24"] + target_tags = ["http-server"] + + allow { + protocol = "tcp" + ports = ["80", "443", "8080"] + } + + depends_on = [ + google_compute_firewall.fw_iap + ] +} + +resource "google_network_services_lb_traffic_extension" "default" { + name = "tf-test-l7-ilb-traffic-ext%{random_suffix}" + description = "my traffic extension" + location = "us-west1" + + load_balancing_scheme = "INTERNAL_MANAGED" + forwarding_rules = [google_compute_forwarding_rule.default.self_link] + + extension_chains { + name = "chain1" + + match_condition { + cel_expression = "request.host == 'example.com'" + } + + extensions { + name = "ext11" + authority = "ext11.com" + service = google_compute_region_backend_service.callouts_backend.self_link + timeout = "0.1s" + fail_open = false + + supported_events = ["REQUEST_HEADERS"] + forward_headers = ["custom-header"] + } + } + + labels = { + foo = "bar" + } +} + +# Traffic Extension Backend Instance +resource "google_compute_instance" "callouts_instance" { + name = "tf-test-l7-ilb-callouts-ins%{random_suffix}" + zone = "us-west1-a" + machine_type = "e2-small" + + labels = { + "container-vm" = "cos-stable-109-17800-147-54" + } + + tags = ["allow-ssh","load-balanced-backend"] + + network_interface { + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id + + access_config { + # add external ip to fetch packages + } + } + + boot_disk { + auto_delete = true + + initialize_params { + type = "pd-standard" + size = 10 + image = "https://www.googleapis.com/compute/v1/projects/cos-cloud/global/images/cos-stable-109-17800-147-54" + } + } + + # Initialize an Envoy's Ext Proc gRPC API based on a docker container + metadata = { + gce-container-declaration = "# DISCLAIMER:\n# This container declaration format is not a public API and may change without\n# notice. Please use gcloud command-line tool or Google Cloud Console to run\n# Containers on Google Compute Engine.\n\nspec:\n containers:\n - image: us-docker.pkg.dev/service-extensions/ext-proc/service-callout-basic-example-python:latest\n name: callouts-vm\n securityContext:\n privileged: false\n stdin: false\n tty: false\n volumeMounts: []\n restartPolicy: Always\n volumes: []\n" + google-logging-enabled = "true" + } + + lifecycle { + create_before_destroy = true + } + + deletion_protection = false +} + +// callouts instance group +resource "google_compute_instance_group" "callouts_instance_group" { + name = "tf-test-l7-ilb-callouts-ins-group%{random_suffix}" + description = "Terraform test instance group" + zone = "us-west1-a" + + instances = [ + google_compute_instance.callouts_instance.id, + ] + + named_port { + name = "http" + port = "80" + } + + named_port { + name = "grpc" + port = "443" + } +} + +# callout health check +resource "google_compute_region_health_check" "callouts_health_check" { + name = "tf-test-l7-ilb-callouts-hc%{random_suffix}" + region = "us-west1" + + http_health_check { + port = 80 + } + + depends_on = [ + google_compute_region_health_check.default + ] +} + +# callout backend service +resource "google_compute_region_backend_service" "callouts_backend" { + name = "tf-test-l7-ilb-callouts-backend%{random_suffix}" + region = "us-west1" + protocol = "HTTP2" + load_balancing_scheme = "INTERNAL_MANAGED" + timeout_sec = 10 + port_name = "grpc" + health_checks = [google_compute_region_health_check.callouts_health_check.id] + + backend { + group = google_compute_instance_group.callouts_instance_group.id + balancing_mode = "UTILIZATION" + capacity_scaler = 1.0 + } + + depends_on = [ + google_compute_region_backend_service.default + ] +} +`, context) +} + +func testAccNetworkServicesLbTrafficExtension_update(context map[string]interface{}) string { + return acctest.Nprintf(` +# Internal HTTP load balancer with a managed instance group backend +# VPC network +resource "google_compute_network" "ilb_network" { + name = "tf-test-l7-ilb-network%{random_suffix}" + auto_create_subnetworks = false +} + +# proxy-only subnet +resource "google_compute_subnetwork" "proxy_subnet" { + name = "tf-test-l7-ilb-proxy-subnet%{random_suffix}" + ip_cidr_range = "10.0.0.0/24" + region = "us-west1" + purpose = "REGIONAL_MANAGED_PROXY" + role = "ACTIVE" + network = google_compute_network.ilb_network.id +} + +# backend subnet +resource "google_compute_subnetwork" "ilb_subnet" { + name = "tf-test-l7-ilb-subnet%{random_suffix}" + ip_cidr_range = "10.0.1.0/24" + region = "us-west1" + network = google_compute_network.ilb_network.id + + depends_on = [ + google_compute_subnetwork.proxy_subnet + ] +} + +# forwarding rule +resource "google_compute_forwarding_rule" "default" { + name = "tf-test-l7-ilb-forwarding-rule%{random_suffix}" + region = "us-west1" + ip_protocol = "TCP" + load_balancing_scheme = "INTERNAL_MANAGED" + port_range = "80" + target = google_compute_region_target_http_proxy.default.id + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id + network_tier = "PREMIUM" + + depends_on = [ + google_compute_subnetwork.proxy_subnet + ] +} + +# HTTP target proxy +resource "google_compute_region_target_http_proxy" "default" { + name = "tf-test-l7-ilb-target-http-proxy%{random_suffix}" + region = "us-west1" + url_map = google_compute_region_url_map.default.id +} + +# URL map +resource "google_compute_region_url_map" "default" { + name = "tf-test-l7-ilb-regional-url-map%{random_suffix}" + region = "us-west1" + default_service = google_compute_region_backend_service.default.id +} + +# backend service +resource "google_compute_region_backend_service" "default" { + name = "tf-test-l7-ilb-backend-subnet%{random_suffix}" + region = "us-west1" + protocol = "HTTP" + load_balancing_scheme = "INTERNAL_MANAGED" + timeout_sec = 10 + health_checks = [google_compute_region_health_check.default.id] + + backend { + group = google_compute_region_instance_group_manager.mig.instance_group + balancing_mode = "UTILIZATION" + capacity_scaler = 1.0 + } +} + +# instance template +resource "google_compute_instance_template" "instance_template" { + name = "tf-test-l7-ilb-mig-template%{random_suffix}" + machine_type = "e2-small" + tags = ["http-server"] + + network_interface { + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id + + access_config { + # add external ip to fetch packages + } + } + + disk { + source_image = "debian-cloud/debian-10" + auto_delete = true + boot = true + } + + # install nginx and serve a simple web page + metadata = { + startup-script = <<-EOF1 + #! /bin/bash + set -euo pipefail + + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y nginx-light jq + + NAME=$(curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/hostname") + IP=$(curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip") + METADATA=$(curl -f -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/attributes/?recursive=True" | jq 'del(.["startup-script"])') + + cat < /var/www/html/index.html +
+      Name: $NAME
+      IP: $IP
+      Metadata: $METADATA
+      
+ EOF + EOF1 + } + + lifecycle { + create_before_destroy = true + } +} + +# health check +resource "google_compute_region_health_check" "default" { + name = "tf-test-l7-ilb-hc%{random_suffix}" + region = "us-west1" + + http_health_check { + port_specification = "USE_SERVING_PORT" + } +} + +# MIG +resource "google_compute_region_instance_group_manager" "mig" { + name = "tf-test-l7-ilb-mig1%{random_suffix}" + region = "us-west1" + + base_instance_name = "vm" + target_size = 2 + + version { + instance_template = google_compute_instance_template.instance_template.id + name = "primary" + } +} + +# allow all access from IAP and health check ranges +resource "google_compute_firewall" "fw_iap" { + name = "tf-test-l7-ilb-fw-allow-iap-hc%{random_suffix}" + direction = "INGRESS" + network = google_compute_network.ilb_network.id + source_ranges = ["130.211.0.0/22", "35.191.0.0/16", "35.235.240.0/20"] + + allow { + protocol = "tcp" + } +} + +# allow http from proxy subnet to backends +resource "google_compute_firewall" "fw_ilb_to_backends" { + name = "tf-test-l7-ilb-fw-allow-ilb-to-backends%{random_suffix}" + direction = "INGRESS" + network = google_compute_network.ilb_network.id + source_ranges = ["10.0.0.0/24"] + target_tags = ["http-server"] + + allow { + protocol = "tcp" + ports = ["80", "443", "8080"] + } + + depends_on = [ + google_compute_firewall.fw_iap + ] +} + +resource "google_network_services_lb_traffic_extension" "default" { + name = "tf-test-l7-ilb-traffic-ext%{random_suffix}" + description = "my traffic extension" + location = "us-west1" + + load_balancing_scheme = "INTERNAL_MANAGED" + forwarding_rules = [google_compute_forwarding_rule.default.self_link] + + extension_chains { + name = "chain1" + + match_condition { + cel_expression = "request.host == 'example.com'" + } + + extensions { + name = "ext12" + authority = "ext12.com" + service = google_compute_region_backend_service.callouts_backend_2.self_link + timeout = "0.1s" + fail_open = false + + supported_events = ["REQUEST_HEADERS"] + forward_headers = ["custom-header"] + } + } + + extension_chains { + name = "chain2" + + match_condition { + cel_expression = "request.host == 'example.com'" + } + + extensions { + name = "ext11" + authority = "ext11.com" + service = google_compute_region_backend_service.callouts_backend.self_link + timeout = "0.1s" + fail_open = false + + supported_events = ["REQUEST_HEADERS"] + forward_headers = ["custom-header"] + } + } + + labels = { + foo = "bar" + } +} + +# traffic extension backend instance +resource "google_compute_instance" "callouts_instance" { + name = "tf-test-l7-ilb-callouts-ins%{random_suffix}" + zone = "us-west1-a" + machine_type = "e2-small" + + labels = { + "container-vm" = "cos-stable-109-17800-147-54" + } + + tags = ["allow-ssh","load-balanced-backend"] + + network_interface { + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id + + access_config { + # add external ip to fetch packages + } + } + + boot_disk { + auto_delete = true + + initialize_params { + type = "pd-standard" + size = 10 + image = "https://www.googleapis.com/compute/v1/projects/cos-cloud/global/images/cos-stable-109-17800-147-54" + } + } + + # Initialize an Envoy's Ext Proc gRPC API based on a docker container + metadata = { + gce-container-declaration = "# DISCLAIMER:\n# This container declaration format is not a public API and may change without\n# notice. Please use gcloud command-line tool or Google Cloud Console to run\n# Containers on Google Compute Engine.\n\nspec:\n containers:\n - image: us-docker.pkg.dev/service-extensions/ext-proc/service-callout-basic-example-python:latest\n name: callouts-vm\n securityContext:\n privileged: false\n stdin: false\n tty: false\n volumeMounts: []\n restartPolicy: Always\n volumes: []\n" + google-logging-enabled = "true" + } + + lifecycle { + create_before_destroy = true + } + + deletion_protection = false +} + +// callouts instance group +resource "google_compute_instance_group" "callouts_instance_group" { + name = "tf-test-l7-ilb-callouts-ins-group%{random_suffix}" + description = "Terraform test instance group" + zone = "us-west1-a" + + instances = [ + google_compute_instance.callouts_instance.id, + ] + + named_port { + name = "http" + port = "80" + } + + named_port { + name = "grpc" + port = "443" + } +} + +# callout health check +resource "google_compute_region_health_check" "callouts_health_check" { + name = "tf-test-l7-ilb-callouts-hc%{random_suffix}" + region = "us-west1" + + http_health_check { + port = 80 + } + + depends_on = [ + google_compute_region_health_check.default + ] +} + +# callout backend service +resource "google_compute_region_backend_service" "callouts_backend" { + name = "tf-test-l7-ilb-callouts-backend%{random_suffix}" + region = "us-west1" + protocol = "HTTP2" + load_balancing_scheme = "INTERNAL_MANAGED" + timeout_sec = 10 + port_name = "grpc" + health_checks = [google_compute_region_health_check.callouts_health_check.id] + + backend { + group = google_compute_instance_group.callouts_instance_group.id + balancing_mode = "UTILIZATION" + capacity_scaler = 1.0 + } + + depends_on = [ + google_compute_region_backend_service.default + ] +} + +# traffic extension backend instance 2 +resource "google_compute_instance" "callouts_instance_2" { + name = "tf-test-l7-ilb-callouts-ins-2%{random_suffix}" + zone = "us-west1-a" + machine_type = "e2-small" + + labels = { + "container-vm" = "cos-stable-109-17800-147-54" + } + + tags = ["allow-ssh","load-balanced-backend"] + + network_interface { + network = google_compute_network.ilb_network.id + subnetwork = google_compute_subnetwork.ilb_subnet.id + + access_config { + # add external ip to fetch packages + } + } + + boot_disk { + auto_delete = true + + initialize_params { + type = "pd-standard" + size = 10 + image = "https://www.googleapis.com/compute/v1/projects/cos-cloud/global/images/cos-stable-109-17800-147-54" + } + } + + # Initialize an Envoy's Ext Proc gRPC API based on a docker container + metadata = { + gce-container-declaration = "# DISCLAIMER:\n# This container declaration format is not a public API and may change without\n# notice. Please use gcloud command-line tool or Google Cloud Console to run\n# Containers on Google Compute Engine.\n\nspec:\n containers:\n - image: us-docker.pkg.dev/service-extensions/ext-proc/service-callout-basic-example-python:latest\n name: callouts-vm\n securityContext:\n privileged: false\n stdin: false\n tty: false\n volumeMounts: []\n restartPolicy: Always\n volumes: []\n" + google-logging-enabled = "true" + } + + lifecycle { + create_before_destroy = true + } + + deletion_protection = false + + depends_on = [ + google_compute_instance.callouts_instance + ] +} + +// callouts instance group 2 +resource "google_compute_instance_group" "callouts_instance_group_2" { + name = "tf-test-l7-ilb-callouts-ins-group-2%{random_suffix}" + description = "Terraform test instance group" + zone = "us-west1-a" + + instances = [ + google_compute_instance.callouts_instance_2.id, + ] + + named_port { + name = "http" + port = "80" + } + + named_port { + name = "grpc" + port = "443" + } + + depends_on = [ + google_compute_instance_group.callouts_instance_group + ] +} + +# callout health check 2 +resource "google_compute_region_health_check" "callouts_health_check_2" { + name = "tf-test-l7-ilb-callouts-hc-2%{random_suffix}" + region = "us-west1" + + http_health_check { + port = 80 + } + + depends_on = [ + google_compute_region_health_check.callouts_health_check + ] +} + +# callout backend service +resource "google_compute_region_backend_service" "callouts_backend_2" { + name = "tf-test-l7-ilb-callouts-backend-2%{random_suffix}" + region = "us-west1" + protocol = "HTTP2" + load_balancing_scheme = "INTERNAL_MANAGED" + timeout_sec = 10 + port_name = "grpc" + health_checks = [google_compute_region_health_check.callouts_health_check_2.id] + + backend { + group = google_compute_instance_group.callouts_instance_group_2.id + balancing_mode = "UTILIZATION" + capacity_scaler = 1.0 + } + + depends_on = [ + google_compute_region_backend_service.callouts_backend + ] +} +`, context) +}