From 1c07f28817fdef48515abbbcbe9c96264939356c Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Fri, 2 Dec 2022 20:39:52 +0000 Subject: [PATCH] Cloudrun v2 service (#6850) Signed-off-by: Modular Magician --- .changelog/6850.txt | 3 + google/provider.go | 5 +- google/resource_cloud_run_v2_service.go | 3439 +++++++++++++++++ ...rce_cloud_run_v2_service_generated_test.go | 411 ++ ...ource_cloud_run_v2_service_sweeper_test.go | 124 + google/resource_cloud_run_v2_service_test.go | 409 ++ .../docs/r/cloud_run_v2_service.html.markdown | 874 +++++ 7 files changed, 5263 insertions(+), 2 deletions(-) create mode 100644 .changelog/6850.txt create mode 100644 google/resource_cloud_run_v2_service.go create mode 100644 google/resource_cloud_run_v2_service_generated_test.go create mode 100644 google/resource_cloud_run_v2_service_sweeper_test.go create mode 100644 google/resource_cloud_run_v2_service_test.go create mode 100644 website/docs/r/cloud_run_v2_service.html.markdown diff --git a/.changelog/6850.txt b/.changelog/6850.txt new file mode 100644 index 0000000000..02ab56e2c1 --- /dev/null +++ b/.changelog/6850.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +`google_cloud_run_v2_service` +``` diff --git a/google/provider.go b/google/provider.go index f0b4161b65..0228b81dc1 100644 --- a/google/provider.go +++ b/google/provider.go @@ -934,9 +934,9 @@ func Provider() *schema.Provider { return provider } -// Generated resources: 249 +// Generated resources: 250 // Generated IAM resources: 156 -// Total generated resources: 405 +// Total generated resources: 406 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -1042,6 +1042,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_cloud_run_service_iam_member": ResourceIamMember(CloudRunServiceIamSchema, CloudRunServiceIamUpdaterProducer, CloudRunServiceIdParseFunc), "google_cloud_run_service_iam_policy": ResourceIamPolicy(CloudRunServiceIamSchema, CloudRunServiceIamUpdaterProducer, CloudRunServiceIdParseFunc), "google_cloud_run_v2_job": resourceCloudRunV2Job(), + "google_cloud_run_v2_service": resourceCloudRunV2Service(), "google_cloud_scheduler_job": resourceCloudSchedulerJob(), "google_cloud_tasks_queue": resourceCloudTasksQueue(), "google_cloud_tasks_queue_iam_binding": ResourceIamBinding(CloudTasksQueueIamSchema, CloudTasksQueueIamUpdaterProducer, CloudTasksQueueIdParseFunc), diff --git a/google/resource_cloud_run_v2_service.go b/google/resource_cloud_run_v2_service.go new file mode 100644 index 0000000000..c63cfd1108 --- /dev/null +++ b/google/resource_cloud_run_v2_service.go @@ -0,0 +1,3439 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + "log" + "reflect" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCloudRunV2Service() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudRunV2ServiceCreate, + Read: resourceCloudRunV2ServiceRead, + Update: resourceCloudRunV2ServiceUpdate, + Delete: resourceCloudRunV2ServiceDelete, + + Importer: &schema.ResourceImporter{ + State: resourceCloudRunV2ServiceImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(20 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `Name of the Service.`, + }, + "template": { + Type: schema.TypeList, + Required: true, + Description: `The template used to create revisions for this Service.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "containers": { + Type: schema.TypeList, + Optional: true, + Description: `Holds the single container that defines the unit of execution for this task.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "image": { + Type: schema.TypeString, + Required: true, + Description: `URL of the Container image in Google Container Registry or Google Artifact Registry. More info: https://kubernetes.io/docs/concepts/containers/images`, + }, + "args": { + Type: schema.TypeList, + Optional: true, + Description: `Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "command": { + Type: schema.TypeList, + Optional: true, + Description: `Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "env": { + Type: schema.TypeList, + Optional: true, + Description: `List of environment variables to set in the container.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: `Name of the environment variable. Must be a C_IDENTIFIER, and mnay not exceed 32768 characters.`, + }, + "value": { + Type: schema.TypeString, + Optional: true, + Description: `Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any route environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "", and the maximum length is 32768 bytes`, + }, + "value_source": { + Type: schema.TypeList, + Optional: true, + Description: `Source for the environment variable's value.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "secret_key_ref": { + Type: schema.TypeList, + Optional: true, + Description: `Selects a secret and a specific version from Cloud Secret Manager.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "secret": { + Type: schema.TypeString, + Required: true, + Description: `The name of the secret in Cloud Secret Manager. Format: {secretName} if the secret is in the same project. projects/{project}/secrets/{secretName} if the secret is in a different project.`, + }, + "version": { + Type: schema.TypeString, + Optional: true, + Description: `The Cloud Secret Manager secret version. Can be 'latest' for the latest value or an integer for a specific version.`, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "liveness_probe": { + Type: schema.TypeList, + Optional: true, + Description: `Periodic probe of container liveness. Container will be restarted if the probe fails. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "failure_threshold": { + Type: schema.TypeInt, + Optional: true, + Description: `Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.`, + Default: 3, + }, + "http_get": { + Type: schema.TypeList, + Optional: true, + Description: `HTTPGet specifies the http request to perform. Exactly one of HTTPGet or TCPSocket must be specified.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "http_headers": { + Type: schema.TypeList, + Optional: true, + Description: `Custom headers to set in the request. HTTP allows repeated headers.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: `The header field name`, + }, + "value": { + Type: schema.TypeString, + Optional: true, + Description: `The header field value`, + Default: "", + }, + }, + }, + }, + "path": { + Type: schema.TypeString, + Optional: true, + Description: `Path to access on the HTTP server. Defaults to '/'.`, + Default: "/", + }, + }, + }, + }, + "initial_delay_seconds": { + Type: schema.TypeInt, + Optional: true, + Description: `Number of seconds after the container has started before the probe is initiated. Defaults to 0 seconds. Minimum value is 0. Maximum value for liveness probe is 3600. Maximum value for startup probe is 240. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes`, + Default: 0, + }, + "period_seconds": { + Type: schema.TypeInt, + Optional: true, + Description: `How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. Maximum value for liveness probe is 3600. Maximum value for startup probe is 240. Must be greater or equal than timeoutSeconds`, + Default: 10, + }, + "tcp_socket": { + Type: schema.TypeList, + Optional: true, + Description: `TCPSocket specifies an action involving a TCP port. Exactly one of HTTPGet or TCPSocket must be specified.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "port": { + Type: schema.TypeInt, + Optional: true, + Description: `Port number to access on the container. Must be in the range 1 to 65535. If not specified, defaults to 8080.`, + }, + }, + }, + }, + "timeout_seconds": { + Type: schema.TypeInt, + Optional: true, + Description: `Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. Maximum value is 3600. Must be smaller than periodSeconds. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes`, + Default: 1, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Description: `Name of the container specified as a DNS_LABEL.`, + }, + "ports": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `List of ports to expose from the container. Only a single port can be specified. The specified ports must be listening on all interfaces (0.0.0.0) within the container to be accessible. + +If omitted, a port number will be chosen and passed to the container through the PORT environment variable for the container to listen on`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "container_port": { + Type: schema.TypeInt, + Optional: true, + Description: `Port number the container listens on. This must be a valid TCP port number, 0 < containerPort < 65536.`, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `If specified, used to specify which protocol to use. Allowed values are "http1" and "h2c".`, + }, + }, + }, + }, + "resources": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `Compute Resource requirements by this container. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cpu_idle": { + Type: schema.TypeBool, + Optional: true, + Description: `Determines whether CPU should be throttled or not outside of requests.`, + }, + "limits": { + Type: schema.TypeMap, + Computed: true, + Optional: true, + Description: `Only memory and CPU are supported. Note: The only supported values for CPU are '1', '2', '4', and '8'. Setting 4 CPU requires at least 2Gi of memory. The values of the map is string form of the 'quantity' k8s type: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "startup_probe": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `Startup probe of application within the container. All other probes are disabled if a startup probe is provided, until it succeeds. Container will not be added to service endpoints if the probe fails. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "failure_threshold": { + Type: schema.TypeInt, + Optional: true, + Description: `Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.`, + Default: 3, + }, + "http_get": { + Type: schema.TypeList, + Optional: true, + Description: `HTTPGet specifies the http request to perform. Exactly one of HTTPGet or TCPSocket must be specified.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "http_headers": { + Type: schema.TypeList, + Optional: true, + Description: `Custom headers to set in the request. HTTP allows repeated headers.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: `The header field name`, + }, + "value": { + Type: schema.TypeString, + Optional: true, + Description: `The header field value`, + Default: "", + }, + }, + }, + }, + "path": { + Type: schema.TypeString, + Optional: true, + Description: `Path to access on the HTTP server. Defaults to '/'.`, + Default: "/", + }, + }, + }, + }, + "initial_delay_seconds": { + Type: schema.TypeInt, + Optional: true, + Description: `Number of seconds after the container has started before the probe is initiated. Defaults to 0 seconds. Minimum value is 0. Maximum value for liveness probe is 3600. Maximum value for startup probe is 240. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes`, + Default: 0, + }, + "period_seconds": { + Type: schema.TypeInt, + Optional: true, + Description: `How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. Maximum value for liveness probe is 3600. Maximum value for startup probe is 240. Must be greater or equal than timeoutSeconds`, + Default: 10, + }, + "tcp_socket": { + Type: schema.TypeList, + Optional: true, + Description: `TCPSocket specifies an action involving a TCP port. Exactly one of HTTPGet or TCPSocket must be specified.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "port": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: `Port number to access on the container. Must be in the range 1 to 65535. If not specified, defaults to 8080.`, + }, + }, + }, + }, + "timeout_seconds": { + Type: schema.TypeInt, + Optional: true, + Description: `Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. Maximum value is 3600. Must be smaller than periodSeconds. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes`, + Default: 1, + }, + }, + }, + }, + "volume_mounts": { + Type: schema.TypeList, + Optional: true, + Description: `Volume to mount into the container's filesystem.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mount_path": { + Type: schema.TypeString, + Required: true, + Description: `Path within the container at which the volume should be mounted. Must not contain ':'. For Cloud SQL volumes, it can be left empty, or must otherwise be /cloudsql. All instances defined in the Volume will be available as /cloudsql/[instance]. For more information on Cloud SQL volumes, visit https://cloud.google.com/sql/docs/mysql/connect-run`, + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: `This must match the Name of a Volume.`, + }, + }, + }, + }, + "working_dir": { + Type: schema.TypeString, + Optional: true, + Description: `Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image.`, + }, + }, + }, + }, + "encryption_key": { + Type: schema.TypeString, + Optional: true, + Description: `A reference to a customer managed encryption key (CMEK) to use to encrypt this container image. For more information, go to https://cloud.google.com/run/docs/securing/using-cmek`, + }, + "execution_environment": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateEnum([]string{"EXECUTION_ENVIRONMENT_GEN1", "EXECUTION_ENVIRONMENT_GEN2", ""}), + Description: `The sandbox environment to host this Revision. Possible values: ["EXECUTION_ENVIRONMENT_GEN1", "EXECUTION_ENVIRONMENT_GEN2"]`, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `KRM-style labels for the resource.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "max_instance_request_concurrency": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: `Sets the maximum number of requests that each serving instance can receive.`, + }, + "revision": { + Type: schema.TypeString, + Optional: true, + Description: `The unique name for the revision. If this field is omitted, it will be automatically generated based on the Service name.`, + }, + "scaling": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `Scaling settings for this Revision.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_instance_count": { + Type: schema.TypeInt, + Optional: true, + Description: `Maximum number of serving instances that this resource should have.`, + }, + "min_instance_count": { + Type: schema.TypeInt, + Optional: true, + Description: `Minimum number of serving instances that this resource should have.`, + }, + }, + }, + }, + "service_account": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `Email address of the IAM service account associated with the revision of the service. The service account represents the identity of the running revision, and determines what permissions the revision has. If not provided, the revision will use the project's default service account.`, + }, + "timeout": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `Max allowed time for an instance to respond to a request. + +A duration in seconds with up to nine fractional digits, ending with 's'. Example: "3.5s".`, + }, + "volumes": { + Type: schema.TypeList, + Optional: true, + Description: `A list of Volumes to make available to containers.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: `Volume's name.`, + }, + "cloud_sql_instance": { + Type: schema.TypeList, + Optional: true, + Description: `For Cloud SQL volumes, contains the specific instances that should be mounted. Visit https://cloud.google.com/sql/docs/mysql/connect-run for more information on how to connect Cloud SQL and Cloud Run.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "instances": { + Type: schema.TypeList, + Optional: true, + Description: `The Cloud SQL instance connection names, as can be found in https://console.cloud.google.com/sql/instances. Visit https://cloud.google.com/sql/docs/mysql/connect-run for more information on how to connect Cloud SQL and Cloud Run. Format: {project}:{location}:{instance}`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "secret": { + Type: schema.TypeList, + Optional: true, + Description: `Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "secret": { + Type: schema.TypeString, + Required: true, + Description: `The name of the secret in Cloud Secret Manager. Format: {secret} if the secret is in the same project. projects/{project}/secrets/{secret} if the secret is in a different project.`, + }, + "default_mode": { + Type: schema.TypeInt, + Optional: true, + Description: `Integer representation of mode bits to use on created files by default. Must be a value between 0000 and 0777 (octal), defaulting to 0444. Directories within the path are not affected by this setting.`, + }, + "items": { + Type: schema.TypeList, + Optional: true, + Description: `If unspecified, the volume will expose a file whose name is the secret, relative to VolumeMount.mount_path. If specified, the key will be used as the version to fetch from Cloud Secret Manager and the path will be the name of the file exposed in the volume. When items are defined, they must specify a path and a version.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mode": { + Type: schema.TypeInt, + Required: true, + Description: `Integer octal mode bits to use on this file, must be a value between 01 and 0777 (octal). If 0 or not set, the Volume's default mode will be used.`, + }, + "path": { + Type: schema.TypeString, + Required: true, + Description: `The relative path of the secret in the container.`, + }, + "version": { + Type: schema.TypeString, + Optional: true, + Description: `The Cloud Secret Manager secret version. Can be 'latest' for the latest value or an integer for a specific version`, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "vpc_access": { + Type: schema.TypeList, + Optional: true, + Description: `VPC Access configuration to use for this Task. For more information, visit https://cloud.google.com/run/docs/configuring/connecting-vpc.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "connector": { + Type: schema.TypeString, + Optional: true, + Description: `VPC Access connector name. Format: projects/{project}/locations/{location}/connectors/{connector}, where {project} can be project id or number.`, + }, + "egress": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateEnum([]string{"ALL_TRAFFIC", "PRIVATE_RANGES_ONLY", ""}), + Description: `Traffic VPC egress settings. Possible values: ["ALL_TRAFFIC", "PRIVATE_RANGES_ONLY"]`, + }, + }, + }, + }, + }, + }, + }, + "binary_authorization": { + Type: schema.TypeList, + Optional: true, + Description: `Settings for the Binary Authorization feature.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "breakglass_justification": { + Type: schema.TypeString, + Optional: true, + Description: `If present, indicates to use Breakglass using this justification. If useDefault is False, then it must be empty. For more information on breakglass, see https://cloud.google.com/binary-authorization/docs/using-breakglass`, + }, + "use_default": { + Type: schema.TypeBool, + Optional: true, + Description: `If True, indicates to use the default project's binary authorization policy. If False, binary authorization will be disabled.`, + }, + }, + }, + }, + "client": { + Type: schema.TypeString, + Optional: true, + Description: `Arbitrary identifier for the API client.`, + }, + "client_version": { + Type: schema.TypeString, + Optional: true, + Description: `Arbitrary version identifier for the API client.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `User-provided description of the Service. This field currently has a 512-character limit.`, + }, + "ingress": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: validateEnum([]string{"INGRESS_TRAFFIC_ALL", "INGRESS_TRAFFIC_INTERNAL_ONLY", "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER", ""}), + Description: `Provides the ingress settings for this Service. On output, returns the currently observed ingress settings, or INGRESS_TRAFFIC_UNSPECIFIED if no revision is active. Possible values: ["INGRESS_TRAFFIC_ALL", "INGRESS_TRAFFIC_INTERNAL_ONLY", "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"]`, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `Map of string keys and values that can be used to organize and categorize objects. User-provided labels are shared with Google's billing system, so they can be used to filter, or break down billing charges by team, component, environment, state, etc. For more information, visit https://cloud.google.com/resource-manager/docs/creating-managing-labels or https://cloud.google.com/run/docs/configuring/labels Cloud Run will populate some labels with 'run.googleapis.com' or 'serving.knative.dev' namespaces. Those labels are read-only, and user changes will not be preserved.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "launch_stage": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: validateEnum([]string{"UNIMPLEMENTED", "PRELAUNCH", "EARLY_ACCESS", "ALPHA", "BETA", "GA", "DEPRECATED", ""}), + Description: `The launch stage as defined by Google Cloud Platform Launch Stages. Cloud Run supports ALPHA, BETA, and GA. If no value is specified, GA is assumed. Possible values: ["UNIMPLEMENTED", "PRELAUNCH", "EARLY_ACCESS", "ALPHA", "BETA", "GA", "DEPRECATED"]`, + }, + "location": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `The location of the cloud run service`, + }, + "traffic": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `Specifies how to distribute traffic over a collection of Revisions belonging to the Service. If traffic is empty or not provided, defaults to 100% traffic to the latest Ready Revision.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "percent": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + Description: `Specifies percent of the traffic to this Revision. This defaults to zero if unspecified.`, + }, + "revision": { + Type: schema.TypeString, + Optional: true, + Description: `Revision to which to send this portion of traffic, if traffic allocation is by revision.`, + }, + "tag": { + Type: schema.TypeString, + Optional: true, + Description: `Indicates a string to be part of the URI to exclusively reference this target.`, + }, + "type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateEnum([]string{"TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST", "TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION", ""}), + Description: `The allocation type for this traffic target. Possible values: ["TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST", "TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION"]`, + }, + }, + }, + }, + "conditions": { + Type: schema.TypeList, + Computed: true, + Description: `The Conditions of all other associated sub-resources. They contain additional diagnostics information in case the Service does not reach its Serving state. See comments in reconciling for additional information on reconciliation process in Cloud Run.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "execution_reason": { + Type: schema.TypeString, + Computed: true, + Description: `A reason for the execution condition.`, + }, + "last_transition_time": { + Type: schema.TypeString, + Computed: true, + Description: `Last time the condition transitioned from one status to another. + +A timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits. Examples: "2014-10-02T15:01:23Z" and "2014-10-02T15:01:23.045123456Z".`, + }, + "message": { + Type: schema.TypeString, + Computed: true, + Description: `Human readable message indicating details about the current status.`, + }, + "reason": { + Type: schema.TypeString, + Computed: true, + Description: `A common (service-level) reason for this condition.`, + }, + "revision_reason": { + Type: schema.TypeString, + Computed: true, + Description: `A reason for the revision condition.`, + }, + "severity": { + Type: schema.TypeString, + Computed: true, + Description: `How to interpret failures of this condition, one of Error, Warning, Info`, + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: `State of the condition.`, + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: `type is used to communicate the status of the reconciliation process. See also: https://github.com/knative/serving/blob/main/docs/spec/errors.md#error-conditions-and-reporting Types common to all resources include: * "Ready": True when the Resource is ready.`, + }, + }, + }, + }, + "etag": { + Type: schema.TypeString, + Computed: true, + Description: `A system-generated fingerprint for this version of the resource. May be used to detect modification conflict during updates.`, + }, + "generation": { + Type: schema.TypeString, + Computed: true, + Description: `A number that monotonically increases every time the user modifies the desired state. Please note that unlike v1, this is an int64 value. As with most Google APIs, its JSON representation will be a string instead of an integer.`, + }, + "latest_created_revision": { + Type: schema.TypeString, + Computed: true, + Description: `Name of the last created revision. See comments in reconciling for additional information on reconciliation process in Cloud Run.`, + }, + "latest_ready_revision": { + Type: schema.TypeString, + Computed: true, + Description: `Name of the latest revision that is serving traffic. See comments in reconciling for additional information on reconciliation process in Cloud Run.`, + }, + "observed_generation": { + Type: schema.TypeString, + Computed: true, + Description: `The generation of this Service currently serving traffic. See comments in reconciling for additional information on reconciliation process in Cloud Run. Please note that unlike v1, this is an int64 value. As with most Google APIs, its JSON representation will be a string instead of an integer.`, + }, + "reconciling": { + Type: schema.TypeBool, + Computed: true, + Description: `Returns true if the Service is currently being acted upon by the system to bring it into the desired state. + +When a new Service is created, or an existing one is updated, Cloud Run will asynchronously perform all necessary steps to bring the Service to the desired serving state. This process is called reconciliation. While reconciliation is in process, observedGeneration, latest_ready_revison, trafficStatuses, and uri will have transient values that might mismatch the intended state: Once reconciliation is over (and this field is false), there are two possible outcomes: reconciliation succeeded and the serving state matches the Service, or there was an error, and reconciliation failed. This state can be found in terminalCondition.state. + +If reconciliation succeeded, the following fields will match: traffic and trafficStatuses, observedGeneration and generation, latestReadyRevision and latestCreatedRevision. + +If reconciliation failed, trafficStatuses, observedGeneration, and latestReadyRevision will have the state of the last serving revision, or empty for newly created Services. Additional information on the failure can be found in terminalCondition and conditions.`, + }, + "terminal_condition": { + Type: schema.TypeList, + Computed: true, + Description: `The Condition of this Service, containing its readiness status, and detailed error information in case it did not reach a serving state. See comments in reconciling for additional information on reconciliation process in Cloud Run.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "execution_reason": { + Type: schema.TypeString, + Computed: true, + Description: `A reason for the execution condition.`, + }, + "last_transition_time": { + Type: schema.TypeString, + Computed: true, + Description: `Last time the condition transitioned from one status to another.`, + }, + "message": { + Type: schema.TypeString, + Computed: true, + Description: `Human readable message indicating details about the current status.`, + }, + "reason": { + Type: schema.TypeString, + Computed: true, + Description: `A common (service-level) reason for this condition.`, + }, + "revision_reason": { + Type: schema.TypeString, + Computed: true, + Description: `A reason for the revision condition.`, + }, + "severity": { + Type: schema.TypeString, + Computed: true, + Description: `How to interpret failures of this condition, one of Error, Warning, Info`, + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: `State of the condition.`, + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: `type is used to communicate the status of the reconciliation process. See also: https://github.com/knative/serving/blob/main/docs/spec/errors.md#error-conditions-and-reporting Types common to all resources include: * "Ready": True when the Resource is ready.`, + }, + }, + }, + }, + "traffic_statuses": { + Type: schema.TypeList, + Computed: true, + Description: `Detailed status information for corresponding traffic targets. See comments in reconciling for additional information on reconciliation process in Cloud Run.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "percent": { + Type: schema.TypeInt, + Computed: true, + Description: `Specifies percent of the traffic to this Revision.`, + }, + "revision": { + Type: schema.TypeString, + Computed: true, + Description: `Revision to which this traffic is sent.`, + }, + "tag": { + Type: schema.TypeString, + Computed: true, + Description: `Indicates the string used in the URI to exclusively reference this target.`, + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: `The allocation type for this traffic target.`, + }, + "uri": { + Type: schema.TypeString, + Computed: true, + Description: `Displays the target URI.`, + }, + }, + }, + }, + "uid": { + Type: schema.TypeString, + Computed: true, + Description: `Server assigned unique identifier for the trigger. The value is a UUID4 string and guaranteed to remain unchanged until the resource is deleted.`, + }, + "uri": { + Type: schema.TypeString, + Computed: true, + Description: `The main URI in which this Service is serving traffic.`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceCloudRunV2ServiceCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + descriptionProp, err := expandCloudRunV2ServiceDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + labelsProp, err := expandCloudRunV2ServiceLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + clientProp, err := expandCloudRunV2ServiceClient(d.Get("client"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("client"); !isEmptyValue(reflect.ValueOf(clientProp)) && (ok || !reflect.DeepEqual(v, clientProp)) { + obj["client"] = clientProp + } + clientVersionProp, err := expandCloudRunV2ServiceClientVersion(d.Get("client_version"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("client_version"); !isEmptyValue(reflect.ValueOf(clientVersionProp)) && (ok || !reflect.DeepEqual(v, clientVersionProp)) { + obj["clientVersion"] = clientVersionProp + } + ingressProp, err := expandCloudRunV2ServiceIngress(d.Get("ingress"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("ingress"); !isEmptyValue(reflect.ValueOf(ingressProp)) && (ok || !reflect.DeepEqual(v, ingressProp)) { + obj["ingress"] = ingressProp + } + launchStageProp, err := expandCloudRunV2ServiceLaunchStage(d.Get("launch_stage"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("launch_stage"); !isEmptyValue(reflect.ValueOf(launchStageProp)) && (ok || !reflect.DeepEqual(v, launchStageProp)) { + obj["launchStage"] = launchStageProp + } + binaryAuthorizationProp, err := expandCloudRunV2ServiceBinaryAuthorization(d.Get("binary_authorization"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("binary_authorization"); !isEmptyValue(reflect.ValueOf(binaryAuthorizationProp)) && (ok || !reflect.DeepEqual(v, binaryAuthorizationProp)) { + obj["binaryAuthorization"] = binaryAuthorizationProp + } + templateProp, err := expandCloudRunV2ServiceTemplate(d.Get("template"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("template"); !isEmptyValue(reflect.ValueOf(templateProp)) && (ok || !reflect.DeepEqual(v, templateProp)) { + obj["template"] = templateProp + } + trafficProp, err := expandCloudRunV2ServiceTraffic(d.Get("traffic"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("traffic"); !isEmptyValue(reflect.ValueOf(trafficProp)) && (ok || !reflect.DeepEqual(v, trafficProp)) { + obj["traffic"] = trafficProp + } + + url, err := replaceVars(d, config, "{{CloudRunV2BasePath}}projects/{{project}}/locations/{{location}}/services?serviceId={{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new Service: %#v", obj) + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Service: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating Service: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/locations/{{location}}/services/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + // Use the resource in the operation response to populate + // identity fields and d.Id() before read + var opRes map[string]interface{} + err = cloudRunV2OperationWaitTimeWithResponse( + config, res, &opRes, project, "Creating Service", userAgent, + d.Timeout(schema.TimeoutCreate)) + if err != nil { + // The resource didn't actually create + d.SetId("") + + return fmt.Errorf("Error waiting to create Service: %s", err) + } + + // This may have caused the ID to update - update it if so. + id, err = replaceVars(d, config, "projects/{{project}}/locations/{{location}}/services/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating Service %q: %#v", d.Id(), res) + + return resourceCloudRunV2ServiceRead(d, meta) +} + +func resourceCloudRunV2ServiceRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{CloudRunV2BasePath}}projects/{{project}}/locations/{{location}}/services/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Service: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("CloudRunV2Service %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + + if err := d.Set("description", flattenCloudRunV2ServiceDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("uid", flattenCloudRunV2ServiceUid(res["uid"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("generation", flattenCloudRunV2ServiceGeneration(res["generation"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("labels", flattenCloudRunV2ServiceLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("client", flattenCloudRunV2ServiceClient(res["client"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("client_version", flattenCloudRunV2ServiceClientVersion(res["clientVersion"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("ingress", flattenCloudRunV2ServiceIngress(res["ingress"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("launch_stage", flattenCloudRunV2ServiceLaunchStage(res["launchStage"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("binary_authorization", flattenCloudRunV2ServiceBinaryAuthorization(res["binaryAuthorization"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("template", flattenCloudRunV2ServiceTemplate(res["template"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("traffic", flattenCloudRunV2ServiceTraffic(res["traffic"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("observed_generation", flattenCloudRunV2ServiceObservedGeneration(res["observedGeneration"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("terminal_condition", flattenCloudRunV2ServiceTerminalCondition(res["terminalCondition"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("conditions", flattenCloudRunV2ServiceConditions(res["conditions"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("latest_ready_revision", flattenCloudRunV2ServiceLatestReadyRevision(res["latestReadyRevision"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("latest_created_revision", flattenCloudRunV2ServiceLatestCreatedRevision(res["latestCreatedRevision"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("traffic_statuses", flattenCloudRunV2ServiceTrafficStatuses(res["trafficStatuses"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("uri", flattenCloudRunV2ServiceUri(res["uri"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("reconciling", flattenCloudRunV2ServiceReconciling(res["reconciling"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + if err := d.Set("etag", flattenCloudRunV2ServiceEtag(res["etag"], d, config)); err != nil { + return fmt.Errorf("Error reading Service: %s", err) + } + + return nil +} + +func resourceCloudRunV2ServiceUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Service: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + descriptionProp, err := expandCloudRunV2ServiceDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + labelsProp, err := expandCloudRunV2ServiceLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + clientProp, err := expandCloudRunV2ServiceClient(d.Get("client"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("client"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, clientProp)) { + obj["client"] = clientProp + } + clientVersionProp, err := expandCloudRunV2ServiceClientVersion(d.Get("client_version"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("client_version"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, clientVersionProp)) { + obj["clientVersion"] = clientVersionProp + } + ingressProp, err := expandCloudRunV2ServiceIngress(d.Get("ingress"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("ingress"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, ingressProp)) { + obj["ingress"] = ingressProp + } + launchStageProp, err := expandCloudRunV2ServiceLaunchStage(d.Get("launch_stage"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("launch_stage"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, launchStageProp)) { + obj["launchStage"] = launchStageProp + } + binaryAuthorizationProp, err := expandCloudRunV2ServiceBinaryAuthorization(d.Get("binary_authorization"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("binary_authorization"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, binaryAuthorizationProp)) { + obj["binaryAuthorization"] = binaryAuthorizationProp + } + templateProp, err := expandCloudRunV2ServiceTemplate(d.Get("template"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("template"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, templateProp)) { + obj["template"] = templateProp + } + trafficProp, err := expandCloudRunV2ServiceTraffic(d.Get("traffic"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("traffic"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, trafficProp)) { + obj["traffic"] = trafficProp + } + + url, err := replaceVars(d, config, "{{CloudRunV2BasePath}}projects/{{project}}/locations/{{location}}/services/{{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating Service %q: %#v", d.Id(), obj) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating Service %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating Service %q: %#v", d.Id(), res) + } + + err = cloudRunV2OperationWaitTime( + config, res, project, "Updating Service", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + + return resourceCloudRunV2ServiceRead(d, meta) +} + +func resourceCloudRunV2ServiceDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Service: %s", err) + } + billingProject = project + + url, err := replaceVars(d, config, "{{CloudRunV2BasePath}}projects/{{project}}/locations/{{location}}/services/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting Service %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "DELETE", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "Service") + } + + err = cloudRunV2OperationWaitTime( + config, res, project, "Deleting Service", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting Service %q: %#v", d.Id(), res) + return nil +} + +func resourceCloudRunV2ServiceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/locations/(?P[^/]+)/services/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVars(d, config, "projects/{{project}}/locations/{{location}}/services/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenCloudRunV2ServiceDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceUid(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceGeneration(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceLabels(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceClient(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceClientVersion(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceIngress(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceLaunchStage(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceBinaryAuthorization(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["breakglass_justification"] = + flattenCloudRunV2ServiceBinaryAuthorizationBreakglassJustification(original["breakglassJustification"], d, config) + transformed["use_default"] = + flattenCloudRunV2ServiceBinaryAuthorizationUseDefault(original["useDefault"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceBinaryAuthorizationBreakglassJustification(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceBinaryAuthorizationUseDefault(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplate(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["revision"] = + flattenCloudRunV2ServiceTemplateRevision(original["revision"], d, config) + transformed["labels"] = + flattenCloudRunV2ServiceTemplateLabels(original["labels"], d, config) + transformed["scaling"] = + flattenCloudRunV2ServiceTemplateScaling(original["scaling"], d, config) + transformed["vpc_access"] = + flattenCloudRunV2ServiceTemplateVPCAccess(original["vpcAccess"], d, config) + transformed["timeout"] = + flattenCloudRunV2ServiceTemplateTimeout(original["timeout"], d, config) + transformed["service_account"] = + flattenCloudRunV2ServiceTemplateServiceAccount(original["serviceAccount"], d, config) + transformed["containers"] = + flattenCloudRunV2ServiceTemplateContainers(original["containers"], d, config) + transformed["volumes"] = + flattenCloudRunV2ServiceTemplateVolumes(original["volumes"], d, config) + transformed["execution_environment"] = + flattenCloudRunV2ServiceTemplateExecutionEnvironment(original["executionEnvironment"], d, config) + transformed["encryption_key"] = + flattenCloudRunV2ServiceTemplateEncryptionKey(original["encryptionKey"], d, config) + transformed["max_instance_request_concurrency"] = + flattenCloudRunV2ServiceTemplateMaxInstanceRequestConcurrency(original["maxInstanceRequestConcurrency"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateRevision(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateLabels(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateScaling(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["min_instance_count"] = + flattenCloudRunV2ServiceTemplateScalingMinInstanceCount(original["minInstanceCount"], d, config) + transformed["max_instance_count"] = + flattenCloudRunV2ServiceTemplateScalingMaxInstanceCount(original["maxInstanceCount"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateScalingMinInstanceCount(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateScalingMaxInstanceCount(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateVPCAccess(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["connector"] = + flattenCloudRunV2ServiceTemplateVPCAccessConnector(original["connector"], d, config) + transformed["egress"] = + flattenCloudRunV2ServiceTemplateVPCAccessEgress(original["egress"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateVPCAccessConnector(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateVPCAccessEgress(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateTimeout(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateServiceAccount(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainers(v interface{}, d *schema.ResourceData, config *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{}{ + "name": flattenCloudRunV2ServiceTemplateContainersName(original["name"], d, config), + "image": flattenCloudRunV2ServiceTemplateContainersImage(original["image"], d, config), + "command": flattenCloudRunV2ServiceTemplateContainersCommand(original["command"], d, config), + "args": flattenCloudRunV2ServiceTemplateContainersArgs(original["args"], d, config), + "env": flattenCloudRunV2ServiceTemplateContainersEnv(original["env"], d, config), + "resources": flattenCloudRunV2ServiceTemplateContainersResources(original["resources"], d, config), + "ports": flattenCloudRunV2ServiceTemplateContainersPorts(original["ports"], d, config), + "volume_mounts": flattenCloudRunV2ServiceTemplateContainersVolumeMounts(original["volumeMounts"], d, config), + "working_dir": flattenCloudRunV2ServiceTemplateContainersWorkingDir(original["workingDir"], d, config), + "liveness_probe": flattenCloudRunV2ServiceTemplateContainersLivenessProbe(original["livenessProbe"], d, config), + "startup_probe": flattenCloudRunV2ServiceTemplateContainersStartupProbe(original["startupProbe"], d, config), + }) + } + return transformed +} +func flattenCloudRunV2ServiceTemplateContainersName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersImage(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersCommand(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersArgs(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersEnv(v interface{}, d *schema.ResourceData, config *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{}{ + "name": flattenCloudRunV2ServiceTemplateContainersEnvName(original["name"], d, config), + "value": flattenCloudRunV2ServiceTemplateContainersEnvValue(original["value"], d, config), + "value_source": flattenCloudRunV2ServiceTemplateContainersEnvValueSource(original["valueSource"], d, config), + }) + } + return transformed +} +func flattenCloudRunV2ServiceTemplateContainersEnvName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersEnvValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersEnvValueSource(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["secret_key_ref"] = + flattenCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRef(original["secretKeyRef"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRef(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["secret"] = + flattenCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRefSecret(original["secret"], d, config) + transformed["version"] = + flattenCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRefVersion(original["version"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRefSecret(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRefVersion(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersResources(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["limits"] = + flattenCloudRunV2ServiceTemplateContainersResourcesLimits(original["limits"], d, config) + transformed["cpu_idle"] = + flattenCloudRunV2ServiceTemplateContainersResourcesCpuIdle(original["cpuIdle"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateContainersResourcesLimits(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersResourcesCpuIdle(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersPorts(v interface{}, d *schema.ResourceData, config *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{}{ + "name": flattenCloudRunV2ServiceTemplateContainersPortsName(original["name"], d, config), + "container_port": flattenCloudRunV2ServiceTemplateContainersPortsContainerPort(original["containerPort"], d, config), + }) + } + return transformed +} +func flattenCloudRunV2ServiceTemplateContainersPortsName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersPortsContainerPort(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateContainersVolumeMounts(v interface{}, d *schema.ResourceData, config *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{}{ + "name": flattenCloudRunV2ServiceTemplateContainersVolumeMountsName(original["name"], d, config), + "mount_path": flattenCloudRunV2ServiceTemplateContainersVolumeMountsMountPath(original["mountPath"], d, config), + }) + } + return transformed +} +func flattenCloudRunV2ServiceTemplateContainersVolumeMountsName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersVolumeMountsMountPath(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersWorkingDir(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersLivenessProbe(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["initial_delay_seconds"] = + flattenCloudRunV2ServiceTemplateContainersLivenessProbeInitialDelaySeconds(original["initialDelaySeconds"], d, config) + transformed["timeout_seconds"] = + flattenCloudRunV2ServiceTemplateContainersLivenessProbeTimeoutSeconds(original["timeoutSeconds"], d, config) + transformed["period_seconds"] = + flattenCloudRunV2ServiceTemplateContainersLivenessProbePeriodSeconds(original["periodSeconds"], d, config) + transformed["failure_threshold"] = + flattenCloudRunV2ServiceTemplateContainersLivenessProbeFailureThreshold(original["failureThreshold"], d, config) + transformed["http_get"] = + flattenCloudRunV2ServiceTemplateContainersLivenessProbeHttpGet(original["httpGet"], d, config) + transformed["tcp_socket"] = + flattenCloudRunV2ServiceTemplateContainersLivenessProbeTcpSocket(original["tcpSocket"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateContainersLivenessProbeInitialDelaySeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateContainersLivenessProbeTimeoutSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateContainersLivenessProbePeriodSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateContainersLivenessProbeFailureThreshold(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateContainersLivenessProbeHttpGet(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + transformed := make(map[string]interface{}) + transformed["path"] = + flattenCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetPath(original["path"], d, config) + transformed["http_headers"] = + flattenCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeaders(original["httpHeaders"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetPath(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeaders(v interface{}, d *schema.ResourceData, config *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{}{ + "name": flattenCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeadersName(original["name"], d, config), + "value": flattenCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeadersValue(original["value"], d, config), + }) + } + return transformed +} +func flattenCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeadersName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeadersValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersLivenessProbeTcpSocket(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + transformed := make(map[string]interface{}) + transformed["port"] = + flattenCloudRunV2ServiceTemplateContainersLivenessProbeTcpSocketPort(original["port"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateContainersLivenessProbeTcpSocketPort(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateContainersStartupProbe(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["initial_delay_seconds"] = + flattenCloudRunV2ServiceTemplateContainersStartupProbeInitialDelaySeconds(original["initialDelaySeconds"], d, config) + transformed["timeout_seconds"] = + flattenCloudRunV2ServiceTemplateContainersStartupProbeTimeoutSeconds(original["timeoutSeconds"], d, config) + transformed["period_seconds"] = + flattenCloudRunV2ServiceTemplateContainersStartupProbePeriodSeconds(original["periodSeconds"], d, config) + transformed["failure_threshold"] = + flattenCloudRunV2ServiceTemplateContainersStartupProbeFailureThreshold(original["failureThreshold"], d, config) + transformed["http_get"] = + flattenCloudRunV2ServiceTemplateContainersStartupProbeHttpGet(original["httpGet"], d, config) + transformed["tcp_socket"] = + flattenCloudRunV2ServiceTemplateContainersStartupProbeTcpSocket(original["tcpSocket"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateContainersStartupProbeInitialDelaySeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateContainersStartupProbeTimeoutSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateContainersStartupProbePeriodSeconds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateContainersStartupProbeFailureThreshold(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateContainersStartupProbeHttpGet(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + transformed := make(map[string]interface{}) + transformed["path"] = + flattenCloudRunV2ServiceTemplateContainersStartupProbeHttpGetPath(original["path"], d, config) + transformed["http_headers"] = + flattenCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeaders(original["httpHeaders"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateContainersStartupProbeHttpGetPath(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeaders(v interface{}, d *schema.ResourceData, config *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{}{ + "name": flattenCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeadersName(original["name"], d, config), + "value": flattenCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeadersValue(original["value"], d, config), + }) + } + return transformed +} +func flattenCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeadersName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeadersValue(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateContainersStartupProbeTcpSocket(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + transformed := make(map[string]interface{}) + transformed["port"] = + flattenCloudRunV2ServiceTemplateContainersStartupProbeTcpSocketPort(original["port"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateContainersStartupProbeTcpSocketPort(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateVolumes(v interface{}, d *schema.ResourceData, config *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{}{ + "name": flattenCloudRunV2ServiceTemplateVolumesName(original["name"], d, config), + "secret": flattenCloudRunV2ServiceTemplateVolumesSecret(original["secret"], d, config), + "cloud_sql_instance": flattenCloudRunV2ServiceTemplateVolumesCloudSqlInstance(original["cloudSqlInstance"], d, config), + }) + } + return transformed +} +func flattenCloudRunV2ServiceTemplateVolumesName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateVolumesSecret(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["secret"] = + flattenCloudRunV2ServiceTemplateVolumesSecretSecret(original["secret"], d, config) + transformed["default_mode"] = + flattenCloudRunV2ServiceTemplateVolumesSecretDefaultMode(original["defaultMode"], d, config) + transformed["items"] = + flattenCloudRunV2ServiceTemplateVolumesSecretItems(original["items"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateVolumesSecretSecret(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateVolumesSecretDefaultMode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateVolumesSecretItems(v interface{}, d *schema.ResourceData, config *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{}{ + "path": flattenCloudRunV2ServiceTemplateVolumesSecretItemsPath(original["path"], d, config), + "version": flattenCloudRunV2ServiceTemplateVolumesSecretItemsVersion(original["version"], d, config), + "mode": flattenCloudRunV2ServiceTemplateVolumesSecretItemsMode(original["mode"], d, config), + }) + } + return transformed +} +func flattenCloudRunV2ServiceTemplateVolumesSecretItemsPath(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateVolumesSecretItemsVersion(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateVolumesSecretItemsMode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTemplateVolumesCloudSqlInstance(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["instances"] = + flattenCloudRunV2ServiceTemplateVolumesCloudSqlInstanceInstances(original["instances"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTemplateVolumesCloudSqlInstanceInstances(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateExecutionEnvironment(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateEncryptionKey(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTemplateMaxInstanceRequestConcurrency(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTraffic(v interface{}, d *schema.ResourceData, config *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{}{ + "type": flattenCloudRunV2ServiceTrafficType(original["type"], d, config), + "revision": flattenCloudRunV2ServiceTrafficRevision(original["revision"], d, config), + "percent": flattenCloudRunV2ServiceTrafficPercent(original["percent"], d, config), + "tag": flattenCloudRunV2ServiceTrafficTag(original["tag"], d, config), + }) + } + return transformed +} +func flattenCloudRunV2ServiceTrafficType(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTrafficRevision(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTrafficPercent(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTrafficTag(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceObservedGeneration(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTerminalCondition(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["type"] = + flattenCloudRunV2ServiceTerminalConditionType(original["type"], d, config) + transformed["state"] = + flattenCloudRunV2ServiceTerminalConditionState(original["state"], d, config) + transformed["message"] = + flattenCloudRunV2ServiceTerminalConditionMessage(original["message"], d, config) + transformed["last_transition_time"] = + flattenCloudRunV2ServiceTerminalConditionLastTransitionTime(original["lastTransitionTime"], d, config) + transformed["severity"] = + flattenCloudRunV2ServiceTerminalConditionSeverity(original["severity"], d, config) + transformed["reason"] = + flattenCloudRunV2ServiceTerminalConditionReason(original["reason"], d, config) + transformed["revision_reason"] = + flattenCloudRunV2ServiceTerminalConditionRevisionReason(original["revisionReason"], d, config) + transformed["execution_reason"] = + flattenCloudRunV2ServiceTerminalConditionExecutionReason(original["executionReason"], d, config) + return []interface{}{transformed} +} +func flattenCloudRunV2ServiceTerminalConditionType(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTerminalConditionState(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTerminalConditionMessage(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTerminalConditionLastTransitionTime(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTerminalConditionSeverity(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTerminalConditionReason(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTerminalConditionRevisionReason(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTerminalConditionExecutionReason(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceConditions(v interface{}, d *schema.ResourceData, config *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{}{ + "type": flattenCloudRunV2ServiceConditionsType(original["type"], d, config), + "state": flattenCloudRunV2ServiceConditionsState(original["state"], d, config), + "message": flattenCloudRunV2ServiceConditionsMessage(original["message"], d, config), + "last_transition_time": flattenCloudRunV2ServiceConditionsLastTransitionTime(original["lastTransitionTime"], d, config), + "severity": flattenCloudRunV2ServiceConditionsSeverity(original["severity"], d, config), + "reason": flattenCloudRunV2ServiceConditionsReason(original["reason"], d, config), + "revision_reason": flattenCloudRunV2ServiceConditionsRevisionReason(original["revisionReason"], d, config), + "execution_reason": flattenCloudRunV2ServiceConditionsExecutionReason(original["executionReason"], d, config), + }) + } + return transformed +} +func flattenCloudRunV2ServiceConditionsType(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceConditionsState(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceConditionsMessage(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceConditionsLastTransitionTime(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceConditionsSeverity(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceConditionsReason(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceConditionsRevisionReason(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceConditionsExecutionReason(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceLatestReadyRevision(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceLatestCreatedRevision(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTrafficStatuses(v interface{}, d *schema.ResourceData, config *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{}{ + "type": flattenCloudRunV2ServiceTrafficStatusesType(original["type"], d, config), + "revision": flattenCloudRunV2ServiceTrafficStatusesRevision(original["revision"], d, config), + "percent": flattenCloudRunV2ServiceTrafficStatusesPercent(original["percent"], d, config), + "tag": flattenCloudRunV2ServiceTrafficStatusesTag(original["tag"], d, config), + "uri": flattenCloudRunV2ServiceTrafficStatusesUri(original["uri"], d, config), + }) + } + return transformed +} +func flattenCloudRunV2ServiceTrafficStatusesType(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTrafficStatusesRevision(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTrafficStatusesPercent(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenCloudRunV2ServiceTrafficStatusesTag(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceTrafficStatusesUri(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceUri(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceReconciling(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenCloudRunV2ServiceEtag(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func expandCloudRunV2ServiceDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandCloudRunV2ServiceClient(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceClientVersion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceIngress(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceLaunchStage(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceBinaryAuthorization(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedBreakglassJustification, err := expandCloudRunV2ServiceBinaryAuthorizationBreakglassJustification(original["breakglass_justification"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedBreakglassJustification); val.IsValid() && !isEmptyValue(val) { + transformed["breakglassJustification"] = transformedBreakglassJustification + } + + transformedUseDefault, err := expandCloudRunV2ServiceBinaryAuthorizationUseDefault(original["use_default"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedUseDefault); val.IsValid() && !isEmptyValue(val) { + transformed["useDefault"] = transformedUseDefault + } + + return transformed, nil +} + +func expandCloudRunV2ServiceBinaryAuthorizationBreakglassJustification(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceBinaryAuthorizationUseDefault(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedRevision, err := expandCloudRunV2ServiceTemplateRevision(original["revision"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRevision); val.IsValid() && !isEmptyValue(val) { + transformed["revision"] = transformedRevision + } + + transformedLabels, err := expandCloudRunV2ServiceTemplateLabels(original["labels"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedLabels); val.IsValid() && !isEmptyValue(val) { + transformed["labels"] = transformedLabels + } + + transformedScaling, err := expandCloudRunV2ServiceTemplateScaling(original["scaling"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedScaling); val.IsValid() && !isEmptyValue(val) { + transformed["scaling"] = transformedScaling + } + + transformedVPCAccess, err := expandCloudRunV2ServiceTemplateVPCAccess(original["vpc_access"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedVPCAccess); val.IsValid() && !isEmptyValue(val) { + transformed["vpcAccess"] = transformedVPCAccess + } + + transformedTimeout, err := expandCloudRunV2ServiceTemplateTimeout(original["timeout"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTimeout); val.IsValid() && !isEmptyValue(val) { + transformed["timeout"] = transformedTimeout + } + + transformedServiceAccount, err := expandCloudRunV2ServiceTemplateServiceAccount(original["service_account"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedServiceAccount); val.IsValid() && !isEmptyValue(val) { + transformed["serviceAccount"] = transformedServiceAccount + } + + transformedContainers, err := expandCloudRunV2ServiceTemplateContainers(original["containers"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedContainers); val.IsValid() && !isEmptyValue(val) { + transformed["containers"] = transformedContainers + } + + transformedVolumes, err := expandCloudRunV2ServiceTemplateVolumes(original["volumes"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedVolumes); val.IsValid() && !isEmptyValue(val) { + transformed["volumes"] = transformedVolumes + } + + transformedExecutionEnvironment, err := expandCloudRunV2ServiceTemplateExecutionEnvironment(original["execution_environment"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedExecutionEnvironment); val.IsValid() && !isEmptyValue(val) { + transformed["executionEnvironment"] = transformedExecutionEnvironment + } + + transformedEncryptionKey, err := expandCloudRunV2ServiceTemplateEncryptionKey(original["encryption_key"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEncryptionKey); val.IsValid() && !isEmptyValue(val) { + transformed["encryptionKey"] = transformedEncryptionKey + } + + transformedMaxInstanceRequestConcurrency, err := expandCloudRunV2ServiceTemplateMaxInstanceRequestConcurrency(original["max_instance_request_concurrency"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxInstanceRequestConcurrency); val.IsValid() && !isEmptyValue(val) { + transformed["maxInstanceRequestConcurrency"] = transformedMaxInstanceRequestConcurrency + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateRevision(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandCloudRunV2ServiceTemplateScaling(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedMinInstanceCount, err := expandCloudRunV2ServiceTemplateScalingMinInstanceCount(original["min_instance_count"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMinInstanceCount); val.IsValid() && !isEmptyValue(val) { + transformed["minInstanceCount"] = transformedMinInstanceCount + } + + transformedMaxInstanceCount, err := expandCloudRunV2ServiceTemplateScalingMaxInstanceCount(original["max_instance_count"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMaxInstanceCount); val.IsValid() && !isEmptyValue(val) { + transformed["maxInstanceCount"] = transformedMaxInstanceCount + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateScalingMinInstanceCount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateScalingMaxInstanceCount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateVPCAccess(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedConnector, err := expandCloudRunV2ServiceTemplateVPCAccessConnector(original["connector"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedConnector); val.IsValid() && !isEmptyValue(val) { + transformed["connector"] = transformedConnector + } + + transformedEgress, err := expandCloudRunV2ServiceTemplateVPCAccessEgress(original["egress"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEgress); val.IsValid() && !isEmptyValue(val) { + transformed["egress"] = transformedEgress + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateVPCAccessConnector(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateVPCAccessEgress(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateTimeout(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateServiceAccount(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainers(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedName, err := expandCloudRunV2ServiceTemplateContainersName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedImage, err := expandCloudRunV2ServiceTemplateContainersImage(original["image"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedImage); val.IsValid() && !isEmptyValue(val) { + transformed["image"] = transformedImage + } + + transformedCommand, err := expandCloudRunV2ServiceTemplateContainersCommand(original["command"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCommand); val.IsValid() && !isEmptyValue(val) { + transformed["command"] = transformedCommand + } + + transformedArgs, err := expandCloudRunV2ServiceTemplateContainersArgs(original["args"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedArgs); val.IsValid() && !isEmptyValue(val) { + transformed["args"] = transformedArgs + } + + transformedEnv, err := expandCloudRunV2ServiceTemplateContainersEnv(original["env"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnv); val.IsValid() && !isEmptyValue(val) { + transformed["env"] = transformedEnv + } + + transformedResources, err := expandCloudRunV2ServiceTemplateContainersResources(original["resources"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedResources); val.IsValid() && !isEmptyValue(val) { + transformed["resources"] = transformedResources + } + + transformedPorts, err := expandCloudRunV2ServiceTemplateContainersPorts(original["ports"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPorts); val.IsValid() && !isEmptyValue(val) { + transformed["ports"] = transformedPorts + } + + transformedVolumeMounts, err := expandCloudRunV2ServiceTemplateContainersVolumeMounts(original["volume_mounts"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedVolumeMounts); val.IsValid() && !isEmptyValue(val) { + transformed["volumeMounts"] = transformedVolumeMounts + } + + transformedWorkingDir, err := expandCloudRunV2ServiceTemplateContainersWorkingDir(original["working_dir"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedWorkingDir); val.IsValid() && !isEmptyValue(val) { + transformed["workingDir"] = transformedWorkingDir + } + + transformedLivenessProbe, err := expandCloudRunV2ServiceTemplateContainersLivenessProbe(original["liveness_probe"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedLivenessProbe); val.IsValid() && !isEmptyValue(val) { + transformed["livenessProbe"] = transformedLivenessProbe + } + + transformedStartupProbe, err := expandCloudRunV2ServiceTemplateContainersStartupProbe(original["startup_probe"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedStartupProbe); val.IsValid() && !isEmptyValue(val) { + transformed["startupProbe"] = transformedStartupProbe + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudRunV2ServiceTemplateContainersName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersImage(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersCommand(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersArgs(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersEnv(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedName, err := expandCloudRunV2ServiceTemplateContainersEnvName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedValue, err := expandCloudRunV2ServiceTemplateContainersEnvValue(original["value"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedValue); val.IsValid() && !isEmptyValue(val) { + transformed["value"] = transformedValue + } + + transformedValueSource, err := expandCloudRunV2ServiceTemplateContainersEnvValueSource(original["value_source"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedValueSource); val.IsValid() && !isEmptyValue(val) { + transformed["valueSource"] = transformedValueSource + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudRunV2ServiceTemplateContainersEnvName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersEnvValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersEnvValueSource(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedSecretKeyRef, err := expandCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRef(original["secret_key_ref"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSecretKeyRef); val.IsValid() && !isEmptyValue(val) { + transformed["secretKeyRef"] = transformedSecretKeyRef + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRef(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedSecret, err := expandCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRefSecret(original["secret"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSecret); val.IsValid() && !isEmptyValue(val) { + transformed["secret"] = transformedSecret + } + + transformedVersion, err := expandCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRefVersion(original["version"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedVersion); val.IsValid() && !isEmptyValue(val) { + transformed["version"] = transformedVersion + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRefSecret(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersEnvValueSourceSecretKeyRefVersion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersResources(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedLimits, err := expandCloudRunV2ServiceTemplateContainersResourcesLimits(original["limits"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedLimits); val.IsValid() && !isEmptyValue(val) { + transformed["limits"] = transformedLimits + } + + transformedCpuIdle, err := expandCloudRunV2ServiceTemplateContainersResourcesCpuIdle(original["cpu_idle"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCpuIdle); val.IsValid() && !isEmptyValue(val) { + transformed["cpuIdle"] = transformedCpuIdle + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateContainersResourcesLimits(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandCloudRunV2ServiceTemplateContainersResourcesCpuIdle(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersPorts(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedName, err := expandCloudRunV2ServiceTemplateContainersPortsName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedContainerPort, err := expandCloudRunV2ServiceTemplateContainersPortsContainerPort(original["container_port"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedContainerPort); val.IsValid() && !isEmptyValue(val) { + transformed["containerPort"] = transformedContainerPort + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudRunV2ServiceTemplateContainersPortsName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersPortsContainerPort(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersVolumeMounts(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedName, err := expandCloudRunV2ServiceTemplateContainersVolumeMountsName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedMountPath, err := expandCloudRunV2ServiceTemplateContainersVolumeMountsMountPath(original["mount_path"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMountPath); val.IsValid() && !isEmptyValue(val) { + transformed["mountPath"] = transformedMountPath + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudRunV2ServiceTemplateContainersVolumeMountsName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersVolumeMountsMountPath(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersWorkingDir(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbe(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedInitialDelaySeconds, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeInitialDelaySeconds(original["initial_delay_seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedInitialDelaySeconds); val.IsValid() && !isEmptyValue(val) { + transformed["initialDelaySeconds"] = transformedInitialDelaySeconds + } + + transformedTimeoutSeconds, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeTimeoutSeconds(original["timeout_seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTimeoutSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["timeoutSeconds"] = transformedTimeoutSeconds + } + + transformedPeriodSeconds, err := expandCloudRunV2ServiceTemplateContainersLivenessProbePeriodSeconds(original["period_seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPeriodSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["periodSeconds"] = transformedPeriodSeconds + } + + transformedFailureThreshold, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeFailureThreshold(original["failure_threshold"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFailureThreshold); val.IsValid() && !isEmptyValue(val) { + transformed["failureThreshold"] = transformedFailureThreshold + } + + transformedHttpGet, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeHttpGet(original["http_get"], d, config) + if err != nil { + return nil, err + } else { + transformed["httpGet"] = transformedHttpGet + } + + transformedTcpSocket, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeTcpSocket(original["tcp_socket"], d, config) + if err != nil { + return nil, err + } else { + transformed["tcpSocket"] = transformedTcpSocket + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbeInitialDelaySeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbeTimeoutSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbePeriodSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbeFailureThreshold(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbeHttpGet(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 { + return nil, nil + } + + if l[0] == nil { + transformed := make(map[string]interface{}) + return transformed, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedPath, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetPath(original["path"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPath); val.IsValid() && !isEmptyValue(val) { + transformed["path"] = transformedPath + } + + transformedHttpHeaders, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeaders(original["http_headers"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpHeaders); val.IsValid() && !isEmptyValue(val) { + transformed["httpHeaders"] = transformedHttpHeaders + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetPath(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeaders(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedName, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeadersName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedValue, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeadersValue(original["value"], d, config) + if err != nil { + return nil, err + } else { + transformed["value"] = transformedValue + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeadersName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbeHttpGetHttpHeadersValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbeTcpSocket(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 { + return nil, nil + } + + if l[0] == nil { + transformed := make(map[string]interface{}) + return transformed, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedPort, err := expandCloudRunV2ServiceTemplateContainersLivenessProbeTcpSocketPort(original["port"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPort); val.IsValid() && !isEmptyValue(val) { + transformed["port"] = transformedPort + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateContainersLivenessProbeTcpSocketPort(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbe(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedInitialDelaySeconds, err := expandCloudRunV2ServiceTemplateContainersStartupProbeInitialDelaySeconds(original["initial_delay_seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedInitialDelaySeconds); val.IsValid() && !isEmptyValue(val) { + transformed["initialDelaySeconds"] = transformedInitialDelaySeconds + } + + transformedTimeoutSeconds, err := expandCloudRunV2ServiceTemplateContainersStartupProbeTimeoutSeconds(original["timeout_seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTimeoutSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["timeoutSeconds"] = transformedTimeoutSeconds + } + + transformedPeriodSeconds, err := expandCloudRunV2ServiceTemplateContainersStartupProbePeriodSeconds(original["period_seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPeriodSeconds); val.IsValid() && !isEmptyValue(val) { + transformed["periodSeconds"] = transformedPeriodSeconds + } + + transformedFailureThreshold, err := expandCloudRunV2ServiceTemplateContainersStartupProbeFailureThreshold(original["failure_threshold"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFailureThreshold); val.IsValid() && !isEmptyValue(val) { + transformed["failureThreshold"] = transformedFailureThreshold + } + + transformedHttpGet, err := expandCloudRunV2ServiceTemplateContainersStartupProbeHttpGet(original["http_get"], d, config) + if err != nil { + return nil, err + } else { + transformed["httpGet"] = transformedHttpGet + } + + transformedTcpSocket, err := expandCloudRunV2ServiceTemplateContainersStartupProbeTcpSocket(original["tcp_socket"], d, config) + if err != nil { + return nil, err + } else { + transformed["tcpSocket"] = transformedTcpSocket + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbeInitialDelaySeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbeTimeoutSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbePeriodSeconds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbeFailureThreshold(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbeHttpGet(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 { + return nil, nil + } + + if l[0] == nil { + transformed := make(map[string]interface{}) + return transformed, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedPath, err := expandCloudRunV2ServiceTemplateContainersStartupProbeHttpGetPath(original["path"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPath); val.IsValid() && !isEmptyValue(val) { + transformed["path"] = transformedPath + } + + transformedHttpHeaders, err := expandCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeaders(original["http_headers"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHttpHeaders); val.IsValid() && !isEmptyValue(val) { + transformed["httpHeaders"] = transformedHttpHeaders + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbeHttpGetPath(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeaders(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedName, err := expandCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeadersName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedValue, err := expandCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeadersValue(original["value"], d, config) + if err != nil { + return nil, err + } else { + transformed["value"] = transformedValue + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeadersName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbeHttpGetHttpHeadersValue(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbeTcpSocket(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 { + return nil, nil + } + + if l[0] == nil { + transformed := make(map[string]interface{}) + return transformed, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedPort, err := expandCloudRunV2ServiceTemplateContainersStartupProbeTcpSocketPort(original["port"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPort); val.IsValid() && !isEmptyValue(val) { + transformed["port"] = transformedPort + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateContainersStartupProbeTcpSocketPort(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateVolumes(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedName, err := expandCloudRunV2ServiceTemplateVolumesName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedSecret, err := expandCloudRunV2ServiceTemplateVolumesSecret(original["secret"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSecret); val.IsValid() && !isEmptyValue(val) { + transformed["secret"] = transformedSecret + } + + transformedCloudSqlInstance, err := expandCloudRunV2ServiceTemplateVolumesCloudSqlInstance(original["cloud_sql_instance"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedCloudSqlInstance); val.IsValid() && !isEmptyValue(val) { + transformed["cloudSqlInstance"] = transformedCloudSqlInstance + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudRunV2ServiceTemplateVolumesName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateVolumesSecret(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedSecret, err := expandCloudRunV2ServiceTemplateVolumesSecretSecret(original["secret"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSecret); val.IsValid() && !isEmptyValue(val) { + transformed["secret"] = transformedSecret + } + + transformedDefaultMode, err := expandCloudRunV2ServiceTemplateVolumesSecretDefaultMode(original["default_mode"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDefaultMode); val.IsValid() && !isEmptyValue(val) { + transformed["defaultMode"] = transformedDefaultMode + } + + transformedItems, err := expandCloudRunV2ServiceTemplateVolumesSecretItems(original["items"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedItems); val.IsValid() && !isEmptyValue(val) { + transformed["items"] = transformedItems + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateVolumesSecretSecret(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateVolumesSecretDefaultMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateVolumesSecretItems(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedPath, err := expandCloudRunV2ServiceTemplateVolumesSecretItemsPath(original["path"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPath); val.IsValid() && !isEmptyValue(val) { + transformed["path"] = transformedPath + } + + transformedVersion, err := expandCloudRunV2ServiceTemplateVolumesSecretItemsVersion(original["version"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedVersion); val.IsValid() && !isEmptyValue(val) { + transformed["version"] = transformedVersion + } + + transformedMode, err := expandCloudRunV2ServiceTemplateVolumesSecretItemsMode(original["mode"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMode); val.IsValid() && !isEmptyValue(val) { + transformed["mode"] = transformedMode + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudRunV2ServiceTemplateVolumesSecretItemsPath(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateVolumesSecretItemsVersion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateVolumesSecretItemsMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateVolumesCloudSqlInstance(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedInstances, err := expandCloudRunV2ServiceTemplateVolumesCloudSqlInstanceInstances(original["instances"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedInstances); val.IsValid() && !isEmptyValue(val) { + transformed["instances"] = transformedInstances + } + + return transformed, nil +} + +func expandCloudRunV2ServiceTemplateVolumesCloudSqlInstanceInstances(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateExecutionEnvironment(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateEncryptionKey(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTemplateMaxInstanceRequestConcurrency(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTraffic(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + 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{}) + + transformedType, err := expandCloudRunV2ServiceTrafficType(original["type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedType); val.IsValid() && !isEmptyValue(val) { + transformed["type"] = transformedType + } + + transformedRevision, err := expandCloudRunV2ServiceTrafficRevision(original["revision"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRevision); val.IsValid() && !isEmptyValue(val) { + transformed["revision"] = transformedRevision + } + + transformedPercent, err := expandCloudRunV2ServiceTrafficPercent(original["percent"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPercent); val.IsValid() && !isEmptyValue(val) { + transformed["percent"] = transformedPercent + } + + transformedTag, err := expandCloudRunV2ServiceTrafficTag(original["tag"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTag); val.IsValid() && !isEmptyValue(val) { + transformed["tag"] = transformedTag + } + + req = append(req, transformed) + } + return req, nil +} + +func expandCloudRunV2ServiceTrafficType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTrafficRevision(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTrafficPercent(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandCloudRunV2ServiceTrafficTag(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google/resource_cloud_run_v2_service_generated_test.go b/google/resource_cloud_run_v2_service_generated_test.go new file mode 100644 index 0000000000..10e3f7d67e --- /dev/null +++ b/google/resource_cloud_run_v2_service_generated_test.go @@ -0,0 +1,411 @@ +// ---------------------------------------------------------------------------- +// +// *** 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 TestAccCloudRunV2Service_cloudrunv2ServiceBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceBasicExample(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + }, + }) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + location = "us-central1" + ingress = "INGRESS_TRAFFIC_ALL" + + binary_authorization { + use_default = true + breakglass_justification = "Some justification" + } + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + } + } +} +`, context) +} + +func TestAccCloudRunV2Service_cloudrunv2ServiceSqlExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "deletion_protection": false, + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceSqlExample(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + }, + }) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceSqlExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + location = "us-central1" + ingress = "INGRESS_TRAFFIC_ALL" + + template { + scaling { + max_instance_count = 2 + } + + volumes { + name = "cloudsql" + cloud_sql_instance { + instances = [google_sql_database_instance.instance.connection_name] + } + } + + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + + env { + name = "FOO" + value = "bar" + } + env { + name = "SECRET_ENV_VAR" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.secret.secret_id + version = "1" + } + } + } + volume_mounts { + name = "cloudsql" + mount_path = "/cloudsql" + } + } + } + + traffic { + type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST" + percent = 100 + } + depends_on = [google_secret_manager_secret_version.secret-version-data] +} + +data "google_project" "project" { +} + +resource "google_secret_manager_secret" "secret" { + secret_id = "tf-test-secret-1%{random_suffix}" + replication { + automatic = true + } +} + +resource "google_secret_manager_secret_version" "secret-version-data" { + secret = google_secret_manager_secret.secret.name + secret_data = "secret-data" +} + +resource "google_secret_manager_secret_iam_member" "secret-access" { + secret_id = google_secret_manager_secret.secret.id + role = "roles/secretmanager.secretAccessor" + member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" + depends_on = [google_secret_manager_secret.secret] +} + +resource "google_sql_database_instance" "instance" { + name = "tf-test-cloudrun-sql%{random_suffix}" + region = "us-central1" + database_version = "MYSQL_5_7" + settings { + tier = "db-f1-micro" + } + + deletion_protection = "%{deletion_protection}" +} +`, context) +} + +func TestAccCloudRunV2Service_cloudrunv2ServiceVpcaccessExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceVpcaccessExample(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + }, + }) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceVpcaccessExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + location = "us-central1" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + } + vpc_access{ + connector = google_vpc_access_connector.connector.id + egress = "ALL_TRAFFIC" + } + } +} + +resource "google_vpc_access_connector" "connector" { + name = "tf-test-run-vpc%{random_suffix}" + subnet { + name = google_compute_subnetwork.custom_test.name + } + machine_type = "e2-standard-4" + min_instances = 2 + max_instances = 3 + region = "us-central1" +} +resource "google_compute_subnetwork" "custom_test" { + name = "tf-test-run-subnetwork%{random_suffix}" + ip_cidr_range = "10.2.0.0/28" + region = "us-central1" + network = google_compute_network.custom_test.id +} +resource "google_compute_network" "custom_test" { + name = "tf-test-run-network%{random_suffix}" + auto_create_subnetworks = false +} +`, context) +} + +func TestAccCloudRunV2Service_cloudrunv2ServiceProbesExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceProbesExample(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + }, + }) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceProbesExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + location = "us-central1" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + startup_probe { + initial_delay_seconds = 0 + timeout_seconds = 1 + period_seconds = 3 + failure_threshold = 1 + tcp_socket { + port = 8080 + } + } + liveness_probe { + http_get { + path = "/" + } + } + } + } +} +`, context) +} + +func TestAccCloudRunV2Service_cloudrunv2ServiceSecretExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceSecretExample(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + }, + }) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceSecretExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + location = "us-central1" + ingress = "INGRESS_TRAFFIC_ALL" + + template { + volumes { + name = "a-volume" + secret { + secret = google_secret_manager_secret.secret.secret_id + default_mode = 292 # 0444 + items { + version = "1" + path = "my-secret" + mode = 256 # 0400 + } + } + } + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + volume_mounts { + name = "a-volume" + mount_path = "/secrets" + } + } + } + depends_on = [google_secret_manager_secret_version.secret-version-data] +} + +data "google_project" "project" { +} + +resource "google_secret_manager_secret" "secret" { + secret_id = "tf-test-secret-1%{random_suffix}" + replication { + automatic = true + } +} + +resource "google_secret_manager_secret_version" "secret-version-data" { + secret = google_secret_manager_secret.secret.name + secret_data = "secret-data" +} + +resource "google_secret_manager_secret_iam_member" "secret-access" { + secret_id = google_secret_manager_secret.secret.id + role = "roles/secretmanager.secretAccessor" + member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" + depends_on = [google_secret_manager_secret.secret] +} +`, context) +} + +func testAccCheckCloudRunV2ServiceDestroyProducer(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_cloud_run_v2_service" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{CloudRunV2BasePath}}projects/{{project}}/locations/{{location}}/services/{{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("CloudRunV2Service still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_cloud_run_v2_service_sweeper_test.go b/google/resource_cloud_run_v2_service_sweeper_test.go new file mode 100644 index 0000000000..9ad395b758 --- /dev/null +++ b/google/resource_cloud_run_v2_service_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("CloudRunV2Service", &resource.Sweeper{ + Name: "CloudRunV2Service", + F: testSweepCloudRunV2Service, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepCloudRunV2Service(region string) error { + resourceName := "CloudRunV2Service" + 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://run.googleapis.com/v2/projects/{{project}}/locations/{{location}}/services", "?")[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["services"] + 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://run.googleapis.com/v2/projects/{{project}}/locations/{{location}}/services/{{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 +} diff --git a/google/resource_cloud_run_v2_service_test.go b/google/resource_cloud_run_v2_service_test.go new file mode 100644 index 0000000000..13d3b7e11a --- /dev/null +++ b/google/resource_cloud_run_v2_service_test.go @@ -0,0 +1,409 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccCloudRunV2Service_cloudrunv2ServiceFullUpdate(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceFull(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceFullUpdate(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + }, + }) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceFull(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + description = "description creating" + location = "us-central1" + ingress = "INGRESS_TRAFFIC_ALL" + labels = { + label-1 = "value-1" + } + client = "client-1" + client_version = "client-version-1" + + template { + labels = { + label-1 = "value-1" + } + timeout = "300s" + service_account = google_service_account.service_account.email + execution_environment = "EXECUTION_ENVIRONMENT_GEN2" + scaling { + max_instance_count = 3 + min_instance_count = 1 + } + containers { + name = "container-1" + image = "us-docker.pkg.dev/cloudrun/container/hello" + env { + name = "SOURCE" + value = "remote" + } + env { + name = "TARGET" + value = "home" + } + ports { + name = "h2c" + container_port = 8080 + } + resources { + cpu_idle = true + limits = { + cpu = "4" + memory = "2Gi" + } + } + } + } + traffic { + type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST" + tag = "traffic-tag-1" + } +} + +resource "google_service_account" "service_account" { + account_id = "tf-test-my-account%{random_suffix}" + display_name = "Test Service Account" +} +`, context) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceFullUpdate(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + description = "description updating" + location = "us-central1" + ingress = "INGRESS_TRAFFIC_ALL" + binary_authorization { + use_default = true + breakglass_justification = "Some justification" + } + labels = { + label-1 = "value-update" + } + client = "client-update" + client_version = "client-version-update" + + template { + labels = { + label-1 = "value-update" + } + timeout = "500s" + service_account = google_service_account.service_account.email + execution_environment = "EXECUTION_ENVIRONMENT_GEN1" + scaling { + max_instance_count = 2 + min_instance_count = 1 + } + containers { + name = "container-update" + image = "us-docker.pkg.dev/cloudrun/container/hello" + env { + name = "SOURCE_UPDATE" + value = "remote-update" + } + env { + name = "TARGET_UPDATE" + value = "home-update" + } + ports { + name = "h2c" + container_port = 8080 + } + resources { + cpu_idle = true + limits = { + cpu = "2" + memory = "8Gi" + } + } + } + vpc_access{ + connector = google_vpc_access_connector.connector.id + egress = "ALL_TRAFFIC" + } + } + traffic { + type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST" + percent = 100 + tag = "traffic-tag-update" + } +} + +resource "google_service_account" "service_account" { + account_id = "tf-test-my-account%{random_suffix}" + display_name = "Test Service Account" +} + +resource "google_vpc_access_connector" "connector" { + name = "tf-test-run-vpc%{random_suffix}" + subnet { + name = google_compute_subnetwork.custom_test.name + } + machine_type = "e2-standard-4" + min_instances = 2 + max_instances = 3 + region = "us-central1" +} +resource "google_compute_subnetwork" "custom_test" { + name = "tf-test-run-subnetwork%{random_suffix}" + ip_cidr_range = "10.2.0.0/28" + region = "us-central1" + network = google_compute_network.custom_test.id +} +resource "google_compute_network" "custom_test" { + name = "tf-test-run-network%{random_suffix}" + auto_create_subnetworks = false +} +`, context) +} + +func TestAccCloudRunV2Service_cloudrunv2ServiceProbesUpdate(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceWithEmptyTCPStartupProbeAndHTTPLivenessProbe(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithTCPStartupProbeAndHTTPLivenessProbe(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithHTTPStartupProbeAndTCPLivenessProbe(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithEmptyHTTPStartupProbe(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithHTTPStartupProbe(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location"}, + }, + }, + }) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceWithEmptyTCPStartupProbeAndHTTPLivenessProbe(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + location = "us-central1" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + ports { + container_port = 8080 + } + startup_probe { + tcp_socket {} + } + liveness_probe { + http_get {} + } + } + } +} +`, context) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithTCPStartupProbeAndHTTPLivenessProbe(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + location = "us-central1" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + ports { + container_port = 8080 + } + startup_probe { + initial_delay_seconds = 2 + period_seconds = 1 + timeout_seconds = 5 + failure_threshold = 2 + tcp_socket { + port = 8080 + } + } + liveness_probe { + initial_delay_seconds = 2 + period_seconds = 1 + timeout_seconds = 5 + failure_threshold = 2 + http_get { + path = "/some-path" + http_headers { + name = "User-Agent" + value = "magic-modules" + } + http_headers { + name = "Some-Name" + } + } + } + } + } +} +`, context) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithHTTPStartupProbeAndTCPLivenessProbe(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + location = "us-central1" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + ports { + container_port = 8080 + } + startup_probe { + initial_delay_seconds = 3 + period_seconds = 2 + timeout_seconds = 6 + failure_threshold = 3 + http_get { + path = "/some-path" + http_headers { + name = "User-Agent" + value = "magic-modules" + } + http_headers { + name = "Some-Name" + } + } + } + liveness_probe { + initial_delay_seconds = 3 + period_seconds = 2 + timeout_seconds = 6 + failure_threshold = 3 + tcp_socket { + port = 8080 + } + } + } + } +} +`, context) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithEmptyHTTPStartupProbe(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + location = "us-central1" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + startup_probe { + http_get {} + } + } + } +} +`, context) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceUpdateWithHTTPStartupProbe(context map[string]interface{}) string { + return Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + location = "us-central1" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + startup_probe { + http_get { + path = "/some-path" + http_headers { + name = "User-Agent" + value = "magic-modules" + } + http_headers { + name = "Some-Name" + } + } + } + } + } +} +`, context) +} diff --git a/website/docs/r/cloud_run_v2_service.html.markdown b/website/docs/r/cloud_run_v2_service.html.markdown new file mode 100644 index 0000000000..e1ae561195 --- /dev/null +++ b/website/docs/r/cloud_run_v2_service.html.markdown @@ -0,0 +1,874 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** 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. +# +# ---------------------------------------------------------------------------- +subcategory: "Cloud Run (2nd gen)" +page_title: "Google: google_cloud_run_v2_service" +description: |- + Service acts as a top-level container that manages a set of configurations and revision templates which implement a network service. +--- + +# google\_cloud\_run\_v2\_service + +Service acts as a top-level container that manages a set of configurations and revision templates which implement a network service. Service exists to provide a singular abstraction which can be access controlled, reasoned about, and which encapsulates software lifecycle decisions such as rollout policy and team resource ownership. + + +To get more information about Service, see: + +* [API documentation](https://cloud.google.com/run/docs/reference/rest/v2/projects.locations.services) +* How-to Guides + * [Official Documentation](https://cloud.google.com/run/docs/) + + +## Example Usage - Cloudrunv2 Service Basic + + +```hcl +resource "google_cloud_run_v2_service" "default" { + name = "cloudrun-service" + location = "us-central1" + ingress = "INGRESS_TRAFFIC_ALL" + + binary_authorization { + use_default = true + breakglass_justification = "Some justification" + } + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + } + } +} +``` + +## Example Usage - Cloudrunv2 Service Sql + + +```hcl +resource "google_cloud_run_v2_service" "default" { + name = "cloudrun-service" + location = "us-central1" + ingress = "INGRESS_TRAFFIC_ALL" + + template { + scaling { + max_instance_count = 2 + } + + volumes { + name = "cloudsql" + cloud_sql_instance { + instances = [google_sql_database_instance.instance.connection_name] + } + } + + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + + env { + name = "FOO" + value = "bar" + } + env { + name = "SECRET_ENV_VAR" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.secret.secret_id + version = "1" + } + } + } + volume_mounts { + name = "cloudsql" + mount_path = "/cloudsql" + } + } + } + + traffic { + type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST" + percent = 100 + } + depends_on = [google_secret_manager_secret_version.secret-version-data] +} + +data "google_project" "project" { +} + +resource "google_secret_manager_secret" "secret" { + secret_id = "secret-1" + replication { + automatic = true + } +} + +resource "google_secret_manager_secret_version" "secret-version-data" { + secret = google_secret_manager_secret.secret.name + secret_data = "secret-data" +} + +resource "google_secret_manager_secret_iam_member" "secret-access" { + secret_id = google_secret_manager_secret.secret.id + role = "roles/secretmanager.secretAccessor" + member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" + depends_on = [google_secret_manager_secret.secret] +} + +resource "google_sql_database_instance" "instance" { + name = "cloudrun-sql" + region = "us-central1" + database_version = "MYSQL_5_7" + settings { + tier = "db-f1-micro" + } + + deletion_protection = "true" +} +``` + +## Example Usage - Cloudrunv2 Service Vpcaccess + + +```hcl +resource "google_cloud_run_v2_service" "default" { + name = "cloudrun-service" + location = "us-central1" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + } + vpc_access{ + connector = google_vpc_access_connector.connector.id + egress = "ALL_TRAFFIC" + } + } +} + +resource "google_vpc_access_connector" "connector" { + name = "run-vpc" + subnet { + name = google_compute_subnetwork.custom_test.name + } + machine_type = "e2-standard-4" + min_instances = 2 + max_instances = 3 + region = "us-central1" +} +resource "google_compute_subnetwork" "custom_test" { + name = "run-subnetwork" + ip_cidr_range = "10.2.0.0/28" + region = "us-central1" + network = google_compute_network.custom_test.id +} +resource "google_compute_network" "custom_test" { + name = "run-network" + auto_create_subnetworks = false +} +``` + +## Example Usage - Cloudrunv2 Service Probes + + +```hcl +resource "google_cloud_run_v2_service" "default" { + name = "cloudrun-service" + location = "us-central1" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + startup_probe { + initial_delay_seconds = 0 + timeout_seconds = 1 + period_seconds = 3 + failure_threshold = 1 + tcp_socket { + port = 8080 + } + } + liveness_probe { + http_get { + path = "/" + } + } + } + } +} +``` + +## Example Usage - Cloudrunv2 Service Secret + + +```hcl +resource "google_cloud_run_v2_service" "default" { + name = "cloudrun-service" + location = "us-central1" + ingress = "INGRESS_TRAFFIC_ALL" + + template { + volumes { + name = "a-volume" + secret { + secret = google_secret_manager_secret.secret.secret_id + default_mode = 292 # 0444 + items { + version = "1" + path = "my-secret" + mode = 256 # 0400 + } + } + } + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + volume_mounts { + name = "a-volume" + mount_path = "/secrets" + } + } + } + depends_on = [google_secret_manager_secret_version.secret-version-data] +} + +data "google_project" "project" { +} + +resource "google_secret_manager_secret" "secret" { + secret_id = "secret-1" + replication { + automatic = true + } +} + +resource "google_secret_manager_secret_version" "secret-version-data" { + secret = google_secret_manager_secret.secret.name + secret_data = "secret-data" +} + +resource "google_secret_manager_secret_iam_member" "secret-access" { + secret_id = google_secret_manager_secret.secret.id + role = "roles/secretmanager.secretAccessor" + member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" + depends_on = [google_secret_manager_secret.secret] +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `name` - + (Required) + Name of the Service. + +* `template` - + (Required) + The template used to create revisions for this Service. + Structure is [documented below](#nested_template). + + +The `template` block supports: + +* `revision` - + (Optional) + The unique name for the revision. If this field is omitted, it will be automatically generated based on the Service name. + +* `labels` - + (Optional) + KRM-style labels for the resource. + +* `scaling` - + (Optional) + Scaling settings for this Revision. + Structure is [documented below](#nested_scaling). + +* `vpc_access` - + (Optional) + VPC Access configuration to use for this Task. For more information, visit https://cloud.google.com/run/docs/configuring/connecting-vpc. + Structure is [documented below](#nested_vpc_access). + +* `timeout` - + (Optional) + Max allowed time for an instance to respond to a request. + A duration in seconds with up to nine fractional digits, ending with 's'. Example: "3.5s". + +* `service_account` - + (Optional) + Email address of the IAM service account associated with the revision of the service. The service account represents the identity of the running revision, and determines what permissions the revision has. If not provided, the revision will use the project's default service account. + +* `containers` - + (Optional) + Holds the single container that defines the unit of execution for this task. + Structure is [documented below](#nested_containers). + +* `volumes` - + (Optional) + A list of Volumes to make available to containers. + Structure is [documented below](#nested_volumes). + +* `execution_environment` - + (Optional) + The sandbox environment to host this Revision. + Possible values are `EXECUTION_ENVIRONMENT_GEN1` and `EXECUTION_ENVIRONMENT_GEN2`. + +* `encryption_key` - + (Optional) + A reference to a customer managed encryption key (CMEK) to use to encrypt this container image. For more information, go to https://cloud.google.com/run/docs/securing/using-cmek + +* `max_instance_request_concurrency` - + (Optional) + Sets the maximum number of requests that each serving instance can receive. + + +The `scaling` block supports: + +* `min_instance_count` - + (Optional) + Minimum number of serving instances that this resource should have. + +* `max_instance_count` - + (Optional) + Maximum number of serving instances that this resource should have. + +The `vpc_access` block supports: + +* `connector` - + (Optional) + VPC Access connector name. Format: projects/{project}/locations/{location}/connectors/{connector}, where {project} can be project id or number. + +* `egress` - + (Optional) + Traffic VPC egress settings. + Possible values are `ALL_TRAFFIC` and `PRIVATE_RANGES_ONLY`. + +The `containers` block supports: + +* `name` - + (Optional) + Name of the container specified as a DNS_LABEL. + +* `image` - + (Required) + URL of the Container image in Google Container Registry or Google Artifact Registry. More info: https://kubernetes.io/docs/concepts/containers/images + +* `command` - + (Optional) + Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + +* `args` - + (Optional) + Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + +* `env` - + (Optional) + List of environment variables to set in the container. + Structure is [documented below](#nested_env). + +* `resources` - + (Optional) + Compute Resource requirements by this container. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources + Structure is [documented below](#nested_resources). + +* `ports` - + (Optional) + List of ports to expose from the container. Only a single port can be specified. The specified ports must be listening on all interfaces (0.0.0.0) within the container to be accessible. + If omitted, a port number will be chosen and passed to the container through the PORT environment variable for the container to listen on + Structure is [documented below](#nested_ports). + +* `volume_mounts` - + (Optional) + Volume to mount into the container's filesystem. + Structure is [documented below](#nested_volume_mounts). + +* `working_dir` - + (Optional) + Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. + +* `liveness_probe` - + (Optional) + Periodic probe of container liveness. Container will be restarted if the probe fails. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + Structure is [documented below](#nested_liveness_probe). + +* `startup_probe` - + (Optional) + Startup probe of application within the container. All other probes are disabled if a startup probe is provided, until it succeeds. Container will not be added to service endpoints if the probe fails. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + Structure is [documented below](#nested_startup_probe). + + +The `env` block supports: + +* `name` - + (Required) + Name of the environment variable. Must be a C_IDENTIFIER, and mnay not exceed 32768 characters. + +* `value` - + (Optional) + Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any route environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "", and the maximum length is 32768 bytes + +* `value_source` - + (Optional) + Source for the environment variable's value. + Structure is [documented below](#nested_value_source). + + +The `value_source` block supports: + +* `secret_key_ref` - + (Optional) + Selects a secret and a specific version from Cloud Secret Manager. + Structure is [documented below](#nested_secret_key_ref). + + +The `secret_key_ref` block supports: + +* `secret` - + (Required) + The name of the secret in Cloud Secret Manager. Format: {secretName} if the secret is in the same project. projects/{project}/secrets/{secretName} if the secret is in a different project. + +* `version` - + (Optional) + The Cloud Secret Manager secret version. Can be 'latest' for the latest value or an integer for a specific version. + +The `resources` block supports: + +* `limits` - + (Optional) + Only memory and CPU are supported. Note: The only supported values for CPU are '1', '2', '4', and '8'. Setting 4 CPU requires at least 2Gi of memory. The values of the map is string form of the 'quantity' k8s type: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go + +* `cpu_idle` - + (Optional) + Determines whether CPU should be throttled or not outside of requests. + +The `ports` block supports: + +* `name` - + (Optional) + If specified, used to specify which protocol to use. Allowed values are "http1" and "h2c". + +* `container_port` - + (Optional) + Port number the container listens on. This must be a valid TCP port number, 0 < containerPort < 65536. + +The `volume_mounts` block supports: + +* `name` - + (Required) + This must match the Name of a Volume. + +* `mount_path` - + (Required) + Path within the container at which the volume should be mounted. Must not contain ':'. For Cloud SQL volumes, it can be left empty, or must otherwise be /cloudsql. All instances defined in the Volume will be available as /cloudsql/[instance]. For more information on Cloud SQL volumes, visit https://cloud.google.com/sql/docs/mysql/connect-run + +The `liveness_probe` block supports: + +* `initial_delay_seconds` - + (Optional) + Number of seconds after the container has started before the probe is initiated. Defaults to 0 seconds. Minimum value is 0. Maximum value for liveness probe is 3600. Maximum value for startup probe is 240. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + +* `timeout_seconds` - + (Optional) + Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. Maximum value is 3600. Must be smaller than periodSeconds. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + +* `period_seconds` - + (Optional) + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. Maximum value for liveness probe is 3600. Maximum value for startup probe is 240. Must be greater or equal than timeoutSeconds + +* `failure_threshold` - + (Optional) + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. + +* `http_get` - + (Optional) + HTTPGet specifies the http request to perform. Exactly one of HTTPGet or TCPSocket must be specified. + Structure is [documented below](#nested_http_get). + +* `tcp_socket` - + (Optional) + TCPSocket specifies an action involving a TCP port. Exactly one of HTTPGet or TCPSocket must be specified. + Structure is [documented below](#nested_tcp_socket). + + +The `http_get` block supports: + +* `path` - + (Optional) + Path to access on the HTTP server. Defaults to '/'. + +* `http_headers` - + (Optional) + Custom headers to set in the request. HTTP allows repeated headers. + Structure is [documented below](#nested_http_headers). + + +The `http_headers` block supports: + +* `name` - + (Required) + The header field name + +* `value` - + (Optional) + The header field value + +The `tcp_socket` block supports: + +* `port` - + (Optional) + Port number to access on the container. Must be in the range 1 to 65535. If not specified, defaults to 8080. + +The `startup_probe` block supports: + +* `initial_delay_seconds` - + (Optional) + Number of seconds after the container has started before the probe is initiated. Defaults to 0 seconds. Minimum value is 0. Maximum value for liveness probe is 3600. Maximum value for startup probe is 240. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + +* `timeout_seconds` - + (Optional) + Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. Maximum value is 3600. Must be smaller than periodSeconds. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + +* `period_seconds` - + (Optional) + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1. Maximum value for liveness probe is 3600. Maximum value for startup probe is 240. Must be greater or equal than timeoutSeconds + +* `failure_threshold` - + (Optional) + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1. + +* `http_get` - + (Optional) + HTTPGet specifies the http request to perform. Exactly one of HTTPGet or TCPSocket must be specified. + Structure is [documented below](#nested_http_get). + +* `tcp_socket` - + (Optional) + TCPSocket specifies an action involving a TCP port. Exactly one of HTTPGet or TCPSocket must be specified. + Structure is [documented below](#nested_tcp_socket). + + +The `http_get` block supports: + +* `path` - + (Optional) + Path to access on the HTTP server. Defaults to '/'. + +* `http_headers` - + (Optional) + Custom headers to set in the request. HTTP allows repeated headers. + Structure is [documented below](#nested_http_headers). + + +The `http_headers` block supports: + +* `name` - + (Required) + The header field name + +* `value` - + (Optional) + The header field value + +The `tcp_socket` block supports: + +* `port` - + (Optional) + Port number to access on the container. Must be in the range 1 to 65535. If not specified, defaults to 8080. + +The `volumes` block supports: + +* `name` - + (Required) + Volume's name. + +* `secret` - + (Optional) + Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + Structure is [documented below](#nested_secret). + +* `cloud_sql_instance` - + (Optional) + For Cloud SQL volumes, contains the specific instances that should be mounted. Visit https://cloud.google.com/sql/docs/mysql/connect-run for more information on how to connect Cloud SQL and Cloud Run. + Structure is [documented below](#nested_cloud_sql_instance). + + +The `secret` block supports: + +* `secret` - + (Required) + The name of the secret in Cloud Secret Manager. Format: {secret} if the secret is in the same project. projects/{project}/secrets/{secret} if the secret is in a different project. + +* `default_mode` - + (Optional) + Integer representation of mode bits to use on created files by default. Must be a value between 0000 and 0777 (octal), defaulting to 0444. Directories within the path are not affected by this setting. + +* `items` - + (Optional) + If unspecified, the volume will expose a file whose name is the secret, relative to VolumeMount.mount_path. If specified, the key will be used as the version to fetch from Cloud Secret Manager and the path will be the name of the file exposed in the volume. When items are defined, they must specify a path and a version. + Structure is [documented below](#nested_items). + + +The `items` block supports: + +* `path` - + (Required) + The relative path of the secret in the container. + +* `version` - + (Optional) + The Cloud Secret Manager secret version. Can be 'latest' for the latest value or an integer for a specific version + +* `mode` - + (Required) + Integer octal mode bits to use on this file, must be a value between 01 and 0777 (octal). If 0 or not set, the Volume's default mode will be used. + +The `cloud_sql_instance` block supports: + +* `instances` - + (Optional) + The Cloud SQL instance connection names, as can be found in https://console.cloud.google.com/sql/instances. Visit https://cloud.google.com/sql/docs/mysql/connect-run for more information on how to connect Cloud SQL and Cloud Run. Format: {project}:{location}:{instance} + +- - - + + +* `description` - + (Optional) + User-provided description of the Service. This field currently has a 512-character limit. + +* `labels` - + (Optional) + Map of string keys and values that can be used to organize and categorize objects. User-provided labels are shared with Google's billing system, so they can be used to filter, or break down billing charges by team, component, environment, state, etc. For more information, visit https://cloud.google.com/resource-manager/docs/creating-managing-labels or https://cloud.google.com/run/docs/configuring/labels Cloud Run will populate some labels with 'run.googleapis.com' or 'serving.knative.dev' namespaces. Those labels are read-only, and user changes will not be preserved. + +* `client` - + (Optional) + Arbitrary identifier for the API client. + +* `client_version` - + (Optional) + Arbitrary version identifier for the API client. + +* `ingress` - + (Optional) + Provides the ingress settings for this Service. On output, returns the currently observed ingress settings, or INGRESS_TRAFFIC_UNSPECIFIED if no revision is active. + Possible values are `INGRESS_TRAFFIC_ALL`, `INGRESS_TRAFFIC_INTERNAL_ONLY`, and `INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER`. + +* `launch_stage` - + (Optional) + The launch stage as defined by Google Cloud Platform Launch Stages. Cloud Run supports ALPHA, BETA, and GA. If no value is specified, GA is assumed. + Possible values are `UNIMPLEMENTED`, `PRELAUNCH`, `EARLY_ACCESS`, `ALPHA`, `BETA`, `GA`, and `DEPRECATED`. + +* `binary_authorization` - + (Optional) + Settings for the Binary Authorization feature. + Structure is [documented below](#nested_binary_authorization). + +* `traffic` - + (Optional) + Specifies how to distribute traffic over a collection of Revisions belonging to the Service. If traffic is empty or not provided, defaults to 100% traffic to the latest Ready Revision. + Structure is [documented below](#nested_traffic). + +* `location` - + (Optional) + The location of the cloud run service + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +The `binary_authorization` block supports: + +* `breakglass_justification` - + (Optional) + If present, indicates to use Breakglass using this justification. If useDefault is False, then it must be empty. For more information on breakglass, see https://cloud.google.com/binary-authorization/docs/using-breakglass + +* `use_default` - + (Optional) + If True, indicates to use the default project's binary authorization policy. If False, binary authorization will be disabled. + +The `traffic` block supports: + +* `type` - + (Optional) + The allocation type for this traffic target. + Possible values are `TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST` and `TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION`. + +* `revision` - + (Optional) + Revision to which to send this portion of traffic, if traffic allocation is by revision. + +* `percent` - + (Optional) + Specifies percent of the traffic to this Revision. This defaults to zero if unspecified. + +* `tag` - + (Optional) + Indicates a string to be part of the URI to exclusively reference this target. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `projects/{{project}}/locations/{{location}}/services/{{name}}` + +* `uid` - + Server assigned unique identifier for the trigger. The value is a UUID4 string and guaranteed to remain unchanged until the resource is deleted. + +* `generation` - + A number that monotonically increases every time the user modifies the desired state. Please note that unlike v1, this is an int64 value. As with most Google APIs, its JSON representation will be a string instead of an integer. + +* `observed_generation` - + The generation of this Service currently serving traffic. See comments in reconciling for additional information on reconciliation process in Cloud Run. Please note that unlike v1, this is an int64 value. As with most Google APIs, its JSON representation will be a string instead of an integer. + +* `terminal_condition` - + The Condition of this Service, containing its readiness status, and detailed error information in case it did not reach a serving state. See comments in reconciling for additional information on reconciliation process in Cloud Run. + Structure is [documented below](#nested_terminal_condition). + +* `conditions` - + The Conditions of all other associated sub-resources. They contain additional diagnostics information in case the Service does not reach its Serving state. See comments in reconciling for additional information on reconciliation process in Cloud Run. + Structure is [documented below](#nested_conditions). + +* `latest_ready_revision` - + Name of the latest revision that is serving traffic. See comments in reconciling for additional information on reconciliation process in Cloud Run. + +* `latest_created_revision` - + Name of the last created revision. See comments in reconciling for additional information on reconciliation process in Cloud Run. + +* `traffic_statuses` - + Detailed status information for corresponding traffic targets. See comments in reconciling for additional information on reconciliation process in Cloud Run. + Structure is [documented below](#nested_traffic_statuses). + +* `uri` - + The main URI in which this Service is serving traffic. + +* `reconciling` - + Returns true if the Service is currently being acted upon by the system to bring it into the desired state. + When a new Service is created, or an existing one is updated, Cloud Run will asynchronously perform all necessary steps to bring the Service to the desired serving state. This process is called reconciliation. While reconciliation is in process, observedGeneration, latest_ready_revison, trafficStatuses, and uri will have transient values that might mismatch the intended state: Once reconciliation is over (and this field is false), there are two possible outcomes: reconciliation succeeded and the serving state matches the Service, or there was an error, and reconciliation failed. This state can be found in terminalCondition.state. + If reconciliation succeeded, the following fields will match: traffic and trafficStatuses, observedGeneration and generation, latestReadyRevision and latestCreatedRevision. + If reconciliation failed, trafficStatuses, observedGeneration, and latestReadyRevision will have the state of the last serving revision, or empty for newly created Services. Additional information on the failure can be found in terminalCondition and conditions. + +* `etag` - + A system-generated fingerprint for this version of the resource. May be used to detect modification conflict during updates. + + +The `terminal_condition` block contains: + +* `type` - + type is used to communicate the status of the reconciliation process. See also: https://github.com/knative/serving/blob/main/docs/spec/errors.md#error-conditions-and-reporting Types common to all resources include: * "Ready": True when the Resource is ready. + +* `state` - + State of the condition. + +* `message` - + Human readable message indicating details about the current status. + +* `last_transition_time` - + Last time the condition transitioned from one status to another. + +* `severity` - + How to interpret failures of this condition, one of Error, Warning, Info + +* `reason` - + A common (service-level) reason for this condition. + +* `revision_reason` - + A reason for the revision condition. + +* `execution_reason` - + A reason for the execution condition. + +The `conditions` block contains: + +* `type` - + type is used to communicate the status of the reconciliation process. See also: https://github.com/knative/serving/blob/main/docs/spec/errors.md#error-conditions-and-reporting Types common to all resources include: * "Ready": True when the Resource is ready. + +* `state` - + State of the condition. + +* `message` - + Human readable message indicating details about the current status. + +* `last_transition_time` - + Last time the condition transitioned from one status to another. + A timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits. Examples: "2014-10-02T15:01:23Z" and "2014-10-02T15:01:23.045123456Z". + +* `severity` - + How to interpret failures of this condition, one of Error, Warning, Info + +* `reason` - + A common (service-level) reason for this condition. + +* `revision_reason` - + A reason for the revision condition. + +* `execution_reason` - + A reason for the execution condition. + +The `traffic_statuses` block contains: + +* `type` - + The allocation type for this traffic target. + +* `revision` - + Revision to which this traffic is sent. + +* `percent` - + Specifies percent of the traffic to this Revision. + +* `tag` - + Indicates the string used in the URI to exclusively reference this target. + +* `uri` - + Displays the target URI. + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 20 minutes. +- `update` - Default is 20 minutes. +- `delete` - Default is 20 minutes. + +## Import + + +Service can be imported using any of these accepted formats: + +``` +$ terraform import google_cloud_run_v2_service.default projects/{{project}}/locations/{{location}}/services/{{name}} +$ terraform import google_cloud_run_v2_service.default {{project}}/{{location}}/{{name}} +$ terraform import google_cloud_run_v2_service.default {{location}}/{{name}} +``` + +## User Project Overrides + +This resource supports [User Project Overrides](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference#user_project_override).