From 7da31113140ca859ebf853c2f443728d78235d37 Mon Sep 17 00:00:00 2001 From: Esha Goel Date: Wed, 13 Mar 2024 04:43:38 +0530 Subject: [PATCH] Add new resource Workload for Apphub (#10155) * Add new resource Workload for Apphub * Fix lint error * Add billing account * Remove extra delay from test * Resolve comments * Add handwritten update test * Fix lint error * Add data source changes * Remove merged changes * Remove merged changes * Batch all update tests into one step to make test fast * Add a delay between discovered resource fetch and managed instance group manager creation * Add back org_id * Update region from us-east1 to us-central1 * Use standard diff suppress function --------- Co-authored-by: praseedhaPK <161299686+praseedhaPK@users.noreply.github.com> Co-authored-by: Krishnan Gopal --- mmv1/products/apphub/Workload.yaml | 228 ++++++++++++++++++ .../examples/apphub_workload_basic.tf.erb | 126 ++++++++++ .../examples/apphub_workload_full.tf.erb | 147 +++++++++++ .../apphub/resource_apphub_workload_test.go | 180 ++++++++++++++ 4 files changed, 681 insertions(+) create mode 100644 mmv1/products/apphub/Workload.yaml create mode 100644 mmv1/templates/terraform/examples/apphub_workload_basic.tf.erb create mode 100644 mmv1/templates/terraform/examples/apphub_workload_full.tf.erb create mode 100644 mmv1/third_party/terraform/services/apphub/resource_apphub_workload_test.go diff --git a/mmv1/products/apphub/Workload.yaml b/mmv1/products/apphub/Workload.yaml new file mode 100644 index 000000000000..b0c3466bb2d9 --- /dev/null +++ b/mmv1/products/apphub/Workload.yaml @@ -0,0 +1,228 @@ +# 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 +base_url: projects/{{project}}/locations/{{location}}/applications/{{application_id}}/workloads +create_url: projects/{{project}}/locations/{{location}}/applications/{{application_id}}/workloads?workloadId={{workload_id}} +self_link: projects/{{project}}/locations/{{location}}/applications/{{application_id}}/workloads/{{workload_id}} +id_format: projects/{{project}}/locations/{{location}}/applications/{{application_id}}/workloads/{{workload_id}} +import_format: + - projects/{{project}}/locations/{{location}}/applications/{{application_id}}/workloads/{{workload_id}} +name: Workload +description: 'Workload represents a binary deployment (such as Managed Instance Groups (MIGs), GKE deployments, etc.) that performs the smallest logical subset of business functionality. + It registers identified workload to the Application.' +autogen_async: true +examples: + - !ruby/object:Provider::Terraform::Examples + name: "apphub_workload_basic" + pull_external: true + primary_resource_id: "example" + vars: + application_id: "example-application-1" + service_project_attachment_id: "project-1" + ilb_network: "l7-ilb-network" + ilb_subnet: "l7-ilb-subnet" + instance_template: "l7-ilb-mig-template" + mig: "l7-ilb-mig1" + test_env_vars: + org_id: :ORG_ID + billing_account: :BILLING_ACCT + - !ruby/object:Provider::Terraform::Examples + name: "apphub_workload_full" + pull_external: true + primary_resource_id: "example" + vars: + application_id: "example-application-1" + service_project_attachment_id: "project-1" + display_name: "Example Service Full" + description: "Register service for testing" + business_name: "Alice" + business_email: "alice@google.com" + developer_name: "Bob" + developer_email: "bob@google.com" + operator_name: "Charlie" + operator_email: "charlie@google.com" + ilb_network: "l7-ilb-network" + ilb_subnet: "l7-ilb-subnet" + instance_template: "l7-ilb-mig-template" + mig: "l7-ilb-mig1" + test_env_vars: + org_id: :ORG_ID + billing_account: :BILLING_ACCT +async: !ruby/object:Api::OpAsync + operation: !ruby/object:Api::OpAsync::Operation + path: name + base_url: "{{op_id}}" + wait_ms: 1000 + timeouts: + result: !ruby/object:Api::OpAsync::Result + path: response + resource_inside_response: true + status: !ruby/object:Api::OpAsync::Status + path: done + complete: true + allowed: + - true + - false + error: !ruby/object:Api::OpAsync::Error + path: error + message: message +update_verb: :PATCH +update_mask: true +parameters: + - !ruby/object:Api::Type::String + name: location + description: 'Part of `parent`. Full resource name of a parent Application. Example: projects/{HOST_PROJECT_ID}/locations/{LOCATION}/applications/{APPLICATION_ID} ' + url_param_only: true + required: true + immutable: true + - !ruby/object:Api::Type::String + name: applicationId + description: 'Part of `parent`. Full resource name of a parent Application. Example: projects/{HOST_PROJECT_ID}/locations/{LOCATION}/applications/{APPLICATION_ID}' + url_param_only: true + required: true + immutable: true + - !ruby/object:Api::Type::String + name: workloadId + description: 'The Workload identifier. ' + url_param_only: true + required: true + immutable: true +properties: + - !ruby/object:Api::Type::String + name: name + output: true + description: "Identifier. The resource name of the Workload. Format:\"projects/{host-project-id}/locations/{location}/applications/{application-id}/workloads/{workload-id}\" " + - !ruby/object:Api::Type::String + name: displayName + description: 'User-defined name for the Workload. ' + - !ruby/object:Api::Type::String + name: description + description: 'User-defined description of a Workload. ' + - !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: uri + description: 'Output only. The underlying compute resource uri. ' + output: true + output: true + name: workloadReference + description: 'Reference of an underlying compute resource represented by the Workload. ' + - !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: gcpProject + description: "Output only. The service project identifier that the underlying + cloud resource resides in. Empty for non cloud resources. " + output: true + - !ruby/object:Api::Type::String + name: location + description: "Output only. The location that the underlying compute resource resides + in (e.g us-west1). " + output: true + - !ruby/object:Api::Type::String + name: zone + description: "Output only. The location that the underlying compute resource resides + in if it is zonal (e.g us-west1-a). " + output: true + output: true + name: workloadProperties + description: 'Properties of an underlying compute resource represented by the Workload. ' + - !ruby/object:Api::Type::String + name: discoveredWorkload + diff_suppress_func: 'tpgresource.ProjectNumberDiffSuppress' + description: 'Immutable. The resource name of the original discovered workload. ' + required: true + immutable: true + - !ruby/object:Api::Type::NestedObject + name: attributes + description: 'Consumer provided attributes. ' + properties: + - !ruby/object:Api::Type::NestedObject + name: criticality + description: 'Criticality of the Application, Service, or Workload ' + properties: + - !ruby/object:Api::Type::Enum + name: type + description: 'Criticality type. ' + required: true + values: + - :MISSION_CRITICAL + - :HIGH + - :MEDIUM + - :LOW + - !ruby/object:Api::Type::NestedObject + name: environment + description: 'Environment of the Application, Service, or Workload ' + properties: + - !ruby/object:Api::Type::Enum + name: type + description: 'Environment type. ' + required: true + values: + - :PRODUCTION + - :STAGING + - :TEST + - :DEVELOPMENT + - !ruby/object:Api::Type::Array + name: developerOwners + description: 'Developer team that owns development and coding. ' + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: displayName + description: 'Contact''s name. ' + - !ruby/object:Api::Type::String + name: email + description: 'Email address of the contacts. ' + required: true + - !ruby/object:Api::Type::Array + name: operatorOwners + description: 'Operator team that ensures runtime and operations. ' + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: displayName + description: 'Contact''s name. ' + - !ruby/object:Api::Type::String + name: email + description: 'Email address of the contacts. ' + required: true + - !ruby/object:Api::Type::Array + name: businessOwners + description: 'Business team that ensures user needs are met and value is delivered ' + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: displayName + description: 'Contact''s name. ' + - !ruby/object:Api::Type::String + name: email + description: 'Email address of the contacts. ' + required: true + - !ruby/object:Api::Type::String + name: createTime + description: 'Output only. Create time. ' + output: true + - !ruby/object:Api::Type::String + name: updateTime + description: 'Output only. Update time. ' + output: true + - !ruby/object:Api::Type::String + name: uid + description: "Output only. A universally unique identifier (UUID) for the `Workload` in the UUID4 format. " + output: true + - !ruby/object:Api::Type::String + name: state + description: "Output only. Workload state. Possible values: STATE_UNSPECIFIED CREATING ACTIVE DELETING DETACHED" + output: true diff --git a/mmv1/templates/terraform/examples/apphub_workload_basic.tf.erb b/mmv1/templates/terraform/examples/apphub_workload_basic.tf.erb new file mode 100644 index 000000000000..0c9a57b3ce94 --- /dev/null +++ b/mmv1/templates/terraform/examples/apphub_workload_basic.tf.erb @@ -0,0 +1,126 @@ +resource "google_apphub_application" "application" { + location = "us-central1" + application_id = "<%= ctx[:vars]['application_id'] %>" + scope { + type = "REGIONAL" + } +} + +resource "google_project" "service_project" { + project_id ="<%= ctx[:vars]['service_project_attachment_id'] %>" + name = "Service Project" + org_id = "<%= ctx[:test_env_vars]['org_id'] %>" + billing_account = "<%= ctx[:test_env_vars]['billing_account'] %>" +} + +# Enable Compute API +resource "google_project_service" "compute_service_project" { + project = google_project.service_project.project_id + service = "compute.googleapis.com" +} + +resource "time_sleep" "wait_120s" { + depends_on = [google_project_service.compute_service_project] + + create_duration = "120s" +} + +resource "google_apphub_service_project_attachment" "service_project_attachment" { + service_project_attachment_id = google_project.service_project.project_id + depends_on = [time_sleep.wait_120s] +} + + +# Discovered workload +data "google_apphub_discovered_workload" "catalog-workload" { + location = "us-central1" + workload_uri = "${replace(google_compute_region_instance_group_manager.mig.instance_group, "https://www.googleapis.com/compute/v1", "//compute.googleapis.com")}" + depends_on = [time_sleep.wait_120s_for_resource_ingestion] +} + +resource "time_sleep" "wait_120s_for_resource_ingestion" { + depends_on = [google_compute_region_instance_group_manager.mig] + create_duration = "120s" +} + +resource "google_apphub_workload" "<%= ctx[:primary_resource_id] %>" { + location = "us-central1" + application_id = google_apphub_application.application.application_id + workload_id = google_compute_region_instance_group_manager.mig.name + discovered_workload = data.google_apphub_discovered_workload.catalog-workload.name +} + +#Workload creation + + +# VPC network +resource "google_compute_network" "ilb_network" { + name = "<%= ctx[:vars]['ilb_network'] %>" + project = google_project.service_project.project_id + auto_create_subnetworks = false + depends_on = [time_sleep.wait_120s] +} + +# backend subnet +resource "google_compute_subnetwork" "ilb_subnet" { + name = "<%= ctx[:vars]['ilb_subnet'] %>" + project = google_project.service_project.project_id + ip_cidr_range = "10.0.1.0/24" + region = "us-central1" + network = google_compute_network.ilb_network.id +} + +# instance template +resource "google_compute_instance_template" "instance_template" { + name = "<%= ctx[:vars]['instance_template'] %>" + project = google_project.service_project.project_id + 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 + } +} + +resource "google_compute_region_instance_group_manager" "mig" { + name = "<%= ctx[:vars]['mig'] %>" + project = google_project.service_project.project_id + region = "us-central1" + version { + instance_template = google_compute_instance_template.instance_template.id + name = "primary" + } + base_instance_name = "vm" + target_size = 2 +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/apphub_workload_full.tf.erb b/mmv1/templates/terraform/examples/apphub_workload_full.tf.erb new file mode 100644 index 000000000000..a81dd6326175 --- /dev/null +++ b/mmv1/templates/terraform/examples/apphub_workload_full.tf.erb @@ -0,0 +1,147 @@ +resource "google_apphub_application" "application" { + location = "us-central1" + application_id = "<%= ctx[:vars]['application_id'] %>" + scope { + type = "REGIONAL" + } +} + +resource "google_project" "service_project" { + project_id ="<%= ctx[:vars]['service_project_attachment_id'] %>" + name = "Service Project" + org_id = "<%= ctx[:test_env_vars]['org_id'] %>" + billing_account = "<%= ctx[:test_env_vars]['billing_account'] %>" +} + +# Enable Compute API +resource "google_project_service" "compute_service_project" { + project = google_project.service_project.project_id + service = "compute.googleapis.com" +} + +resource "time_sleep" "wait_120s" { + depends_on = [google_project_service.compute_service_project] + + create_duration = "120s" +} + +resource "google_apphub_service_project_attachment" "service_project_attachment" { + service_project_attachment_id = google_project.service_project.project_id + depends_on = [time_sleep.wait_120s] +} + +# Discovered workload +data "google_apphub_discovered_workload" "catalog-workload" { + location = "us-central1" + workload_uri = "${replace(google_compute_region_instance_group_manager.mig.instance_group, "https://www.googleapis.com/compute/v1", "//compute.googleapis.com")}" + depends_on = [time_sleep.wait_120s_for_resource_ingestion] +} + +resource "time_sleep" "wait_120s_for_resource_ingestion" { + depends_on = [google_compute_region_instance_group_manager.mig] + create_duration = "120s" +} + +resource "google_apphub_workload" "<%= ctx[:primary_resource_id] %>" { + location = "us-central1" + application_id = google_apphub_application.application.application_id + workload_id = google_compute_region_instance_group_manager.mig.name + discovered_workload = data.google_apphub_discovered_workload.catalog-workload.name + display_name = "<%= ctx[:vars]['display_name'] %>" + description = "<%= ctx[:vars]['description'] %>" + attributes { + environment { + type = "STAGING" + } + criticality { + type = "MISSION_CRITICAL" + } + business_owners { + display_name = "<%= ctx[:vars]['business_name'] %>" + email = "<%= ctx[:vars]['business_email'] %>" + } + developer_owners { + display_name = "<%= ctx[:vars]['developer_name'] %>" + email = "<%= ctx[:vars]['developer_email'] %>" + } + operator_owners { + display_name = "<%= ctx[:vars]['operator_name'] %>" + email = "<%= ctx[:vars]['operator_email'] %>" + } + } +} + +#Workload creation + + +# VPC network +resource "google_compute_network" "ilb_network" { + name = "<%= ctx[:vars]['ilb_network'] %>" + project = google_project.service_project.project_id + auto_create_subnetworks = false + depends_on = [time_sleep.wait_120s] +} + +# backend subnet +resource "google_compute_subnetwork" "ilb_subnet" { + name = "<%= ctx[:vars]['ilb_subnet'] %>" + project = google_project.service_project.project_id + ip_cidr_range = "10.0.1.0/24" + region = "us-central1" + network = google_compute_network.ilb_network.id +} + +# instance template +resource "google_compute_instance_template" "instance_template" { + name = "<%= ctx[:vars]['instance_template'] %>" + project = google_project.service_project.project_id + 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 + } +} + +resource "google_compute_region_instance_group_manager" "mig" { + name = "<%= ctx[:vars]['mig'] %>" + project = google_project.service_project.project_id + region = "us-central1" + version { + instance_template = google_compute_instance_template.instance_template.id + name = "primary" + } + base_instance_name = "vm" + target_size = 2 +} \ No newline at end of file diff --git a/mmv1/third_party/terraform/services/apphub/resource_apphub_workload_test.go b/mmv1/third_party/terraform/services/apphub/resource_apphub_workload_test.go new file mode 100644 index 000000000000..2897307d1a93 --- /dev/null +++ b/mmv1/third_party/terraform/services/apphub/resource_apphub_workload_test.go @@ -0,0 +1,180 @@ +package apphub_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +func TestAccApphubWorkload_apphubWorkloadUpdate(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "billing_account": envvar.GetTestBillingAccountFromEnv(t), + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + "time": {}, + }, + CheckDestroy: testAccCheckApphubWorkloadDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccApphubWorkload_apphubWorkloadFullExample(context), + }, + { + ResourceName: "google_apphub_workload.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "application_id", "workload_id"}, + }, + { + Config: testAccApphubWorkload_apphubWorkloadUpdate(context), + }, + { + ResourceName: "google_apphub_workload.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "application_id", "workload_id"}, + }, + }, + }) +} + +func testAccApphubWorkload_apphubWorkloadUpdate(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_apphub_application" "application" { + location = "us-central1" + application_id = "tf-test-example-application-1%{random_suffix}" + scope { + type = "REGIONAL" + } +} + +resource "google_project" "service_project" { + project_id ="tf-test-project-1%{random_suffix}" + name = "Service Project" + org_id = "%{org_id}" + billing_account = "%{billing_account}" +} + +# Enable Compute API +resource "google_project_service" "compute_service_project" { + project = google_project.service_project.project_id + service = "compute.googleapis.com" +} + +resource "time_sleep" "wait_120s" { + depends_on = [google_project_service.compute_service_project] + + create_duration = "120s" +} + +resource "google_apphub_service_project_attachment" "service_project_attachment" { + service_project_attachment_id = google_project.service_project.project_id + depends_on = [time_sleep.wait_120s] +} + +# Discovered workload +data "google_apphub_discovered_workload" "catalog-workload" { + location = "us-central1" + workload_uri = "${replace(google_compute_region_instance_group_manager.mig.instance_group, "https://www.googleapis.com/compute/v1", "//compute.googleapis.com")}" + depends_on = [time_sleep.wait_120s_for_resource_ingestion] +} + +resource "time_sleep" "wait_120s_for_resource_ingestion" { + depends_on = [google_compute_region_instance_group_manager.mig] + create_duration = "120s" +} + +resource "google_apphub_workload" "example" { + location = "us-central1" + application_id = google_apphub_application.application.application_id + workload_id = google_compute_region_instance_group_manager.mig.name + discovered_workload = data.google_apphub_discovered_workload.catalog-workload.name +} + +#Workload creation + + +# VPC network +resource "google_compute_network" "ilb_network" { + name = "tf-test-l7-ilb-network%{random_suffix}" + project = google_project.service_project.project_id + auto_create_subnetworks = false + depends_on = [time_sleep.wait_120s] +} + +# backend subnet +resource "google_compute_subnetwork" "ilb_subnet" { + name = "tf-test-l7-ilb-subnet%{random_suffix}" + project = google_project.service_project.project_id + ip_cidr_range = "10.0.1.0/24" + region = "us-central1" + network = google_compute_network.ilb_network.id +} + +# instance template +resource "google_compute_instance_template" "instance_template" { + name = "tf-test-l7-ilb-mig-template%{random_suffix}" + project = google_project.service_project.project_id + 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 + } +} + +resource "google_compute_region_instance_group_manager" "mig" { + name = "tf-test-l7-ilb-mig1%{random_suffix}" + project = google_project.service_project.project_id + region = "us-central1" + version { + instance_template = google_compute_instance_template.instance_template.id + name = "primary" + } + base_instance_name = "vm" + target_size = 2 +} +`, context) +}