diff --git a/blueprints/networking/filtering-proxy/main.tf b/blueprints/networking/filtering-proxy/main.tf
index 97d6efec03..ca998bf92e 100644
--- a/blueprints/networking/filtering-proxy/main.tf
+++ b/blueprints/networking/filtering-proxy/main.tf
@@ -165,33 +165,31 @@ module "squid-vm" {
}
module "squid-mig" {
- count = var.mig ? 1 : 0
- source = "../../../modules/compute-mig"
- project_id = module.project-host.project_id
- location = "${var.region}-b"
- name = "squid-mig"
- target_size = 1
- autoscaler_config = {
- max_replicas = 10
- min_replicas = 1
- cooldown_period = 30
- cpu_utilization_target = 0.65
- load_balancing_utilization_target = null
- metric = null
+ count = var.mig ? 1 : 0
+ source = "../../../modules/compute-mig"
+ project_id = module.project-host.project_id
+ location = "${var.region}-b"
+ name = "squid-mig"
+ instance_template = module.squid-vm.template.self_link
+ target_size = 1
+ auto_healing_policies = {
+ initial_delay_sec = 60
}
- default_version = {
- instance_template = module.squid-vm.template.self_link
- name = "default"
+ autoscaler_config = {
+ max_replicas = 10
+ min_replicas = 1
+ cooldown_period = 30
+ scaling_signals = {
+ cpu_utilization = {
+ target = 0.65
+ }
+ }
}
health_check_config = {
- type = "tcp"
- check = { port = 3128 }
- config = {}
- logging = true
- }
- auto_healing_policies = {
- health_check = module.squid-mig.0.health_check.self_link
- initial_delay_sec = 60
+ enable_logging = true
+ tcp = {
+ port = 3128
+ }
}
}
diff --git a/blueprints/networking/glb-and-armor/main.tf b/blueprints/networking/glb-and-armor/main.tf
index 6e43bc15e4..836226090c 100644
--- a/blueprints/networking/glb-and-armor/main.tf
+++ b/blueprints/networking/glb-and-armor/main.tf
@@ -153,22 +153,20 @@ module "vm_siege" {
}
module "mig_ew1" {
- source = "../../../modules/compute-mig"
- project_id = module.project.project_id
- location = "europe-west1"
- name = "${local.prefix}europe-west1-mig"
- regional = true
- default_version = {
- instance_template = module.instance_template_ew1.template.self_link
- name = "default"
- }
+ source = "../../../modules/compute-mig"
+ project_id = module.project.project_id
+ location = "europe-west1"
+ name = "${local.prefix}europe-west1-mig"
+ instance_template = module.instance_template_ew1.template.self_link
autoscaler_config = {
- max_replicas = 5
- min_replicas = 1
- cooldown_period = 45
- cpu_utilization_target = 0.8
- load_balancing_utilization_target = null
- metric = null
+ max_replicas = 5
+ min_replicas = 1
+ cooldown_period = 45
+ scaling_signals = {
+ cpu_utilization = {
+ target = 0.65
+ }
+ }
}
named_ports = {
http = 80
@@ -179,22 +177,20 @@ module "mig_ew1" {
}
module "mig_ue1" {
- source = "../../../modules/compute-mig"
- project_id = module.project.project_id
- location = "us-east1"
- name = "${local.prefix}us-east1-mig"
- regional = true
- default_version = {
- instance_template = module.instance_template_ue1.template.self_link
- name = "default"
- }
+ source = "../../../modules/compute-mig"
+ project_id = module.project.project_id
+ location = "us-east1"
+ name = "${local.prefix}us-east1-mig"
+ instance_template = module.instance_template_ue1.template.self_link
autoscaler_config = {
- max_replicas = 5
- min_replicas = 1
- cooldown_period = 45
- cpu_utilization_target = 0.8
- load_balancing_utilization_target = null
- metric = null
+ max_replicas = 5
+ min_replicas = 1
+ cooldown_period = 45
+ scaling_signals = {
+ cpu_utilization = {
+ target = 0.65
+ }
+ }
}
named_ports = {
http = 80
diff --git a/fast/stages/02-networking-nva/nva.tf b/fast/stages/02-networking-nva/nva.tf
index 4e70d02fb4..d0afbd7253 100644
--- a/fast/stages/02-networking-nva/nva.tf
+++ b/fast/stages/02-networking-nva/nva.tf
@@ -15,7 +15,7 @@
*/
locals {
- # routing_config should be aligned to the NVA network interfaces - i.e.
+ # routing_config should be aligned to the NVA network interfaces - i.e.
# local.routing_config[0] sets up the first interface, and so on.
routing_config = [
{
@@ -94,27 +94,21 @@ module "nva-template" {
}
module "nva-mig" {
- for_each = local.nva_locality
- source = "../../../modules/compute-mig"
- project_id = module.landing-project.project_id
- regional = true
- location = each.value.region
- name = "nva-cos-${each.value.trigram}-${each.value.zone}"
- target_size = 1
- # FIXME: cycle
- # auto_healing_policies = {
- # health_check = module.nva-mig[each.key].health_check.self_link
- # initial_delay_sec = 30
- # }
- health_check_config = {
- type = "tcp"
- check = { port = 22 }
- config = {}
- logging = true
+ for_each = local.nva_locality
+ source = "../../../modules/compute-mig"
+ project_id = module.landing-project.project_id
+ location = each.value.region
+ name = "nva-cos-${each.value.trigram}-${each.value.zone}"
+ instance_template = module.nva-template[each.key].template.self_link
+ target_size = 1
+ auto_healing_policies = {
+ initial_delay_sec = 30
}
- default_version = {
- instance_template = module.nva-template[each.key].template.self_link
- name = "default"
+ health_check_config = {
+ enable_logging = true
+ tcp = {
+ port = 22
+ }
}
}
diff --git a/modules/compute-mig/README.md b/modules/compute-mig/README.md
index 1d421527db..4e5b5a45d7 100644
--- a/modules/compute-mig/README.md
+++ b/modules/compute-mig/README.md
@@ -2,7 +2,7 @@
This module allows creating a managed instance group supporting one or more application versions via instance templates. Optionally, a health check and an autoscaler can be created, and the managed instance group can be configured to be stateful.
-This module can be coupled with the [`compute-vm`](../compute-vm) module which can manage instance templates, and the [`net-ilb`](../net-ilb) module to assign the MIG to a backend wired to an Internal Load Balancer. The first use case is shown in the examples below.
+This module can be coupled with the [`compute-vm`](../compute-vm) module which can manage instance templates, and the [`net-ilb`](../net-ilb) module to assign the MIG to a backend wired to an Internal Load Balancer. The first use case is shown in the examples below.
Stateful disks can be created directly, as shown in the last example below.
@@ -39,15 +39,12 @@ module "nginx-template" {
}
module "nginx-mig" {
- source = "./fabric/modules/compute-mig"
- project_id = "my-project"
- location = "europe-west1-b"
- name = "mig-test"
- target_size = 2
- default_version = {
- instance_template = module.nginx-template.template.self_link
- name = "default"
- }
+ source = "./fabric/modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 2
+ instance_template = module.nginx-template.template.self_link
}
# tftest modules=2 resources=2
```
@@ -85,20 +82,18 @@ module "nginx-template" {
}
module "nginx-mig" {
- source = "./fabric/modules/compute-mig"
- project_id = "my-project"
- location = "europe-west1-b"
- name = "mig-test"
- target_size = 3
- default_version = {
- instance_template = module.nginx-template.template.self_link
- name = "default"
- }
+ source = "./fabric/modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 3
+ instance_template = module.nginx-template.template.self_link
versions = {
canary = {
instance_template = module.nginx-template.template.self_link
- target_type = "fixed"
- target_size = 1
+ target_size = {
+ fixed = 1
+ }
}
}
}
@@ -138,24 +133,20 @@ module "nginx-template" {
}
module "nginx-mig" {
- source = "./fabric/modules/compute-mig"
- project_id = "my-project"
- location = "europe-west1-b"
- name = "mig-test"
- target_size = 3
- default_version = {
- instance_template = module.nginx-template.template.self_link
- name = "default"
- }
+ source = "./fabric/modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 3
+ instance_template = module.nginx-template.template.self_link
auto_healing_policies = {
- health_check = module.nginx-mig.health_check.self_link
initial_delay_sec = 30
}
health_check_config = {
- type = "http"
- check = { port = 80 }
- config = {}
- logging = true
+ enable_logging = true
+ http = {
+ port = 80
+ }
}
}
# tftest modules=2 resources=3
@@ -194,22 +185,21 @@ module "nginx-template" {
}
module "nginx-mig" {
- source = "./fabric/modules/compute-mig"
- project_id = "my-project"
- location = "europe-west1-b"
- name = "mig-test"
- target_size = 3
- default_version = {
- instance_template = module.nginx-template.template.self_link
- name = "default"
- }
+ source = "./fabric/modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 3
+ instance_template = module.nginx-template.template.self_link
autoscaler_config = {
- max_replicas = 3
- min_replicas = 1
- cooldown_period = 30
- cpu_utilization_target = 0.65
- load_balancing_utilization_target = null
- metric = null
+ max_replicas = 3
+ min_replicas = 1
+ cooldown_period = 30
+ scaling_signals = {
+ cpu_utilization = {
+ target = 0.65
+ }
+ }
}
}
# tftest modules=2 resources=3
@@ -246,23 +236,19 @@ module "nginx-template" {
}
module "nginx-mig" {
- source = "./fabric/modules/compute-mig"
- project_id = "my-project"
- location = "europe-west1-b"
- name = "mig-test"
- target_size = 3
- default_version = {
- instance_template = module.nginx-template.template.self_link
- name = "default"
- }
+ source = "./fabric/modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 3
+ instance_template = module.nginx-template.template.self_link
update_policy = {
- type = "PROACTIVE"
minimal_action = "REPLACE"
+ type = "PROACTIVE"
min_ready_sec = 30
- max_surge_type = "fixed"
- max_surge = 1
- max_unavailable_type = null
- max_unavailable = null
+ max_surge = {
+ fixed = 1
+ }
}
}
# tftest modules=2 resources=2
@@ -270,7 +256,7 @@ module "nginx-mig" {
### Stateful MIGs - MIG Config
-Stateful MIGs have some limitations documented [here](https://cloud.google.com/compute/docs/instance-groups/configuring-stateful-migs#limitations). Enforcement of these requirements is the responsibility of users of this module.
+Stateful MIGs have some limitations documented [here](https://cloud.google.com/compute/docs/instance-groups/configuring-stateful-migs#limitations). Enforcement of these requirements is the responsibility of users of this module.
You can configure a disk defined in the instance template to be stateful for all instances in the MIG by configuring in the MIG's stateful policy, using the `stateful_disk_mig` variable. Alternatively, you can also configure stateful persistent disks individually per instance of the MIG by setting the `stateful_disk_instance` variable. A discussion on these scenarios can be found in the [docs](https://cloud.google.com/compute/docs/instance-groups/configuring-stateful-disks-in-migs).
@@ -278,7 +264,6 @@ An example using only the configuration at the MIG level can be seen below.
Note that when referencing the stateful disk, you use `device_name` and not `disk_name`.
-
```hcl
module "cos-nginx" {
source = "./fabric/modules/cloud-config-container/nginx"
@@ -319,40 +304,33 @@ module "nginx-template" {
}
module "nginx-mig" {
- source = "./fabric/modules/compute-mig"
- project_id = "my-project"
- location = "europe-west1-b"
- name = "mig-test"
- target_size = 3
- default_version = {
- instance_template = module.nginx-template.template.self_link
- name = "default"
- }
+ source = "./fabric/modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 3
+ instance_template = module.nginx-template.template.self_link
autoscaler_config = {
- max_replicas = 3
- min_replicas = 1
- cooldown_period = 30
- cpu_utilization_target = 0.65
- load_balancing_utilization_target = null
- metric = null
- }
- stateful_config = {
- per_instance_config = {},
- mig_config = {
- stateful_disks = {
- repd-1 = {
- delete_rule = "NEVER"
- }
+ max_replicas = 3
+ min_replicas = 1
+ cooldown_period = 30
+ scaling_signals = {
+ cpu_utilization = {
+ target = 0.65
}
}
}
+ stateful_disks = {
+ repd-1 = null
+ }
}
# tftest modules=2 resources=3
```
### Stateful MIGs - Instance Config
-Here is an example defining the stateful config at the instance level.
+
+Here is an example defining the stateful config at the instance level.
Note that you will need to know the instance name in order to use this configuration.
@@ -396,46 +374,36 @@ module "nginx-template" {
}
module "nginx-mig" {
- source = "./fabric/modules/compute-mig"
- project_id = "my-project"
- location = "europe-west1-b"
- name = "mig-test"
- target_size = 3
- default_version = {
- instance_template = module.nginx-template.template.self_link
- name = "default"
- }
+ source = "./fabric/modules/compute-mig"
+ project_id = "my-project"
+ location = "europe-west1-b"
+ name = "mig-test"
+ target_size = 3
+ instance_template = module.nginx-template.template.self_link
autoscaler_config = {
- max_replicas = 3
- min_replicas = 1
- cooldown_period = 30
- cpu_utilization_target = 0.65
- load_balancing_utilization_target = null
- metric = null
+ max_replicas = 3
+ min_replicas = 1
+ cooldown_period = 30
+ scaling_signals = {
+ cpu_utilization = {
+ target = 0.65
+ }
+ }
}
stateful_config = {
- per_instance_config = {
- # note that this needs to be the name of an existing instance within the Managed Instance Group
- instance-1 = {
- stateful_disks = {
+ # name needs to match a MIG instance name
+ instance-1 = {
+ minimal_action = "NONE",
+ most_disruptive_allowed_action = "REPLACE"
+ preserved_state = {
+ disks = {
persistent-disk-1 = {
source = "test-disk",
- mode = "READ_ONLY",
- delete_rule= "NEVER",
- },
- },
+ }
+ }
metadata = {
foo = "bar"
- },
- update_config = {
- minimal_action = "NONE",
- most_disruptive_allowed_action = "REPLACE",
- remove_instance_state_on_destroy = false,
- },
- },
- },
- mig_config = {
- stateful_disks = {
+ }
}
}
}
@@ -449,21 +417,25 @@ module "nginx-mig" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [default_version](variables.tf#L45) | Default application version template. Additional versions can be specified via the `versions` variable. | object({…})
| ✓ | |
-| [location](variables.tf#L64) | Compute zone, or region if `regional` is set to true. | string
| ✓ | |
-| [name](variables.tf#L68) | Managed group name. | string
| ✓ | |
-| [project_id](variables.tf#L79) | Project id. | string
| ✓ | |
-| [auto_healing_policies](variables.tf#L17) | Auto-healing policies for this group. | object({…})
| | null
|
-| [autoscaler_config](variables.tf#L26) | Optional autoscaler configuration. Only one of 'cpu_utilization_target' 'load_balancing_utilization_target' or 'metric' can be not null. | object({…})
| | null
|
-| [health_check_config](variables.tf#L53) | Optional auto-created health check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | object({…})
| | null
|
-| [named_ports](variables.tf#L73) | Named ports. | map(number)
| | null
|
-| [regional](variables.tf#L84) | Use regional instance group. When set, `location` should be set to the region. | bool
| | false
|
-| [stateful_config](variables.tf#L90) | Stateful configuration can be done by individual instances or for all instances in the MIG. They key in per_instance_config is the name of the specific instance. The key of the stateful_disks is the 'device_name' field of the resource. Please note that device_name is defined at the OS mount level, unlike the disk name. | object({…})
| | null
|
-| [target_pools](variables.tf#L121) | Optional list of URLs for target pools to which new instances in the group are added. | list(string)
| | []
|
-| [target_size](variables.tf#L127) | Group target size, leave null when using an autoscaler. | number
| | null
|
-| [update_policy](variables.tf#L133) | Update policy. Type can be 'OPPORTUNISTIC' or 'PROACTIVE', action 'REPLACE' or 'restart', surge type 'fixed' or 'percent'. | object({…})
| | null
|
-| [versions](variables.tf#L148) | Additional application versions, target_type is either 'fixed' or 'percent'. | map(object({…}))
| | null
|
-| [wait_for_instances](variables.tf#L158) | Wait for all instances to be created/updated before returning. | bool
| | null
|
+| [instance_template](variables.tf#L150) | Instance template for the default version. | string
| ✓ | |
+| [location](variables.tf#L155) | Compute zone or region. | string
| ✓ | |
+| [name](variables.tf#L160) | Managed group name. | string
| ✓ | |
+| [project_id](variables.tf#L171) | Project id. | string
| ✓ | |
+| [all_instances_config](variables.tf#L17) | Metadata and labels set to all instances in the group. | object({…})
| | null
|
+| [auto_healing_policies](variables.tf#L26) | Auto-healing policies for this group. | object({…})
| | null
|
+| [autoscaler_config](variables.tf#L35) | Optional autoscaler configuration. | object({…})
| | null
|
+| [default_version_name](variables.tf#L83) | Name used for the default version. | string
| | "default"
|
+| [description](variables.tf#L89) | Optional description used for all resources managed by this module. | string
| | "Terraform managed."
|
+| [distribution_policy](variables.tf#L95) | DIstribution policy for regional MIG. | object({…})
| | null
|
+| [health_check_config](variables.tf#L104) | Optional auto-created health check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | object({…})
| | null
|
+| [named_ports](variables.tf#L165) | Named ports. | map(number)
| | null
|
+| [stateful_config](variables.tf#L183) | Stateful configuration for individual instances. | map(object({…}))
| | {}
|
+| [stateful_disks](variables.tf#L176) | Stateful disk configuration applied at the MIG level to all instances, in device name => on permanent instance delete rule as boolean. | map(bool)
| | {}
|
+| [target_pools](variables.tf#L202) | Optional list of URLs for target pools to which new instances in the group are added. | list(string)
| | []
|
+| [target_size](variables.tf#L208) | Group target size, leave null when using an autoscaler. | number
| | null
|
+| [update_policy](variables.tf#L214) | Update policy. Minimal action and type are required. | object({…})
| | null
|
+| [versions](variables.tf#L235) | Additional application versions, target_size is optional. | map(object({…}))
| | {}
|
+| [wait_for_instances](variables.tf#L248) | Wait for all instances to be created/updated before returning. | object({…})
| | null
|
## Outputs
diff --git a/modules/compute-mig/autoscaler.tf b/modules/compute-mig/autoscaler.tf
new file mode 100644
index 0000000000..b8bd0acc80
--- /dev/null
+++ b/modules/compute-mig/autoscaler.tf
@@ -0,0 +1,229 @@
+/**
+ * Copyright 2022 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+# tfdoc:file:description Autoscaler resource.
+
+locals {
+ as_enabled = true
+ as_scaling = try(var.autoscaler_config.scaling_control, null)
+ as_signals = try(var.autoscaler_config.scaling_signals, null)
+}
+
+resource "google_compute_autoscaler" "default" {
+ provider = google-beta
+ count = local.is_regional || var.autoscaler_config == null ? 0 : 1
+ project = var.project_id
+ name = var.name
+ zone = var.location
+ description = var.description
+ target = google_compute_instance_group_manager.default.0.id
+
+ autoscaling_policy {
+ max_replicas = var.autoscaler_config.max_replicas
+ min_replicas = var.autoscaler_config.min_replicas
+ cooldown_period = var.autoscaler_config.cooldown_period
+
+ dynamic "scale_down_control" {
+ for_each = local.as_scaling.down == null ? [] : [""]
+ content {
+ time_window_sec = local.as_scaling.down.time_window_sec
+ dynamic "max_scaled_down_replicas" {
+ for_each = (
+ local.as_scaling.down.max_replicas_fixed == null &&
+ local.as_scaling.down.max_replicas_percent == null
+ ? []
+ : [""]
+ )
+ content {
+ fixed = local.as_scaling.down.max_replicas_fixed
+ percent = local.as_scaling.down.max_replicas_percent
+ }
+ }
+ }
+ }
+
+ dynamic "scale_in_control" {
+ for_each = local.as_scaling.in == null ? [] : [""]
+ content {
+ time_window_sec = local.as_scaling.in.time_window_sec
+ dynamic "max_scaled_in_replicas" {
+ for_each = (
+ local.as_scaling.in.max_replicas_fixed == null &&
+ local.as_scaling.in.max_replicas_percent == null
+ ? []
+ : [""]
+ )
+ content {
+ fixed = local.as_scaling.in.max_replicas_fixed
+ percent = local.as_scaling.in.max_replicas_percent
+ }
+ }
+ }
+ }
+
+ dynamic "cpu_utilization" {
+ for_each = local.as_signals.cpu_utilization == null ? [] : [""]
+ content {
+ target = local.as_signals.cpu_utilization.target
+ predictive_method = (
+ local.as_signals.cpu_utilization.optimize_availability == true
+ ? "OPTIMIZE_AVAILABILITY"
+ : null
+ )
+ }
+ }
+
+ dynamic "load_balancing_utilization" {
+ for_each = local.as_signals.load_balancing_utilization == null ? [] : [""]
+ content {
+ target = local.as_signals.load_balancing_utilization.target
+ }
+ }
+
+ dynamic "metric" {
+ for_each = toset(
+ local.as_signals.metrics == null ? [] : local.as_signals.metrics
+ )
+ content {
+ name = metric.value.name
+ type = metric.value.type
+ target = metric.value.target_value
+ single_instance_assignment = metric.value.single_instance_assignment
+ filter = metric.value.time_series_filter
+ }
+ }
+
+ dynamic "scaling_schedules" {
+ for_each = toset(
+ local.as_signals.schedules == null ? [] : local.as_signals.schedules
+ )
+ iterator = schedule
+ content {
+ duration_sec = schedule.value.duration_sec
+ min_required_replicas = schedule.value.min_required_replicas
+ name = schedule.value.name
+ schedule = schedule.value.cron_schedule
+ description = schedule.value.description
+ disabled = schedule.value.disabled
+ time_zone = schedule.value.timezone
+ }
+ }
+
+ }
+}
+
+resource "google_compute_region_autoscaler" "default" {
+ provider = google-beta
+ count = local.is_regional && var.autoscaler_config != null ? 1 : 0
+ project = var.project_id
+ name = var.name
+ region = var.location
+ description = var.description
+ target = google_compute_region_instance_group_manager.default.0.id
+
+ autoscaling_policy {
+ max_replicas = var.autoscaler_config.max_replicas
+ min_replicas = var.autoscaler_config.min_replicas
+ cooldown_period = var.autoscaler_config.cooldown_period
+
+ dynamic "scale_down_control" {
+ for_each = local.as_scaling.down == null ? [] : [""]
+ content {
+ time_window_sec = local.as_scaling.down.time_window_sec
+ dynamic "max_scaled_down_replicas" {
+ for_each = (
+ local.as_scaling.down.max_replicas_fixed == null &&
+ local.as_scaling.down.max_replicas_percent == null
+ ? []
+ : [""]
+ )
+ content {
+ fixed = local.as_scaling.down.max_replicas_fixed
+ percent = local.as_scaling.down.max_replicas_percent
+ }
+ }
+ }
+ }
+
+ dynamic "scale_in_control" {
+ for_each = local.as_scaling.in == null ? [] : [""]
+ content {
+ time_window_sec = local.as_scaling.in.time_window_sec
+ dynamic "max_scaled_in_replicas" {
+ for_each = (
+ local.as_scaling.in.max_replicas_fixed == null &&
+ local.as_scaling.in.max_replicas_percent == null
+ ? []
+ : [""]
+ )
+ content {
+ fixed = local.as_scaling.in.max_replicas_fixed
+ percent = local.as_scaling.in.max_replicas_percent
+ }
+ }
+ }
+ }
+
+ dynamic "cpu_utilization" {
+ for_each = local.as_signals.cpu_utilization == null ? [] : [""]
+ content {
+ target = local.as_signals.cpu_utilization.target
+ predictive_method = (
+ local.as_signals.cpu_utilization.optimize_availability == true
+ ? "OPTIMIZE_AVAILABILITY"
+ : null
+ )
+ }
+ }
+
+ dynamic "load_balancing_utilization" {
+ for_each = local.as_signals.load_balancing_utilization == null ? [] : [""]
+ content {
+ target = local.as_signals.load_balancing_utilization.target
+ }
+ }
+
+ dynamic "metric" {
+ for_each = toset(
+ local.as_signals.metrics == null ? [] : local.as_signals.metrics
+ )
+ content {
+ name = metric.value.name
+ type = metric.value.type
+ target = metric.value.target_value
+ single_instance_assignment = metric.value.single_instance_assignment
+ filter = metric.value.time_series_filter
+ }
+ }
+
+ dynamic "scaling_schedules" {
+ for_each = toset(
+ local.as_signals.schedules == null ? [] : local.as_signals.schedules
+ )
+ iterator = schedule
+ content {
+ duration_sec = schedule.value.duration_sec
+ min_required_replicas = schedule.value.min_required_replicas
+ name = schedule.value.name
+ schedule = schedule.cron_schedule
+ description = schedule.value.description
+ disabled = schedule.value.disabled
+ time_zone = schedule.value.timezone
+ }
+ }
+
+ }
+}
diff --git a/modules/compute-mig/health-check.tf b/modules/compute-mig/health-check.tf
new file mode 100644
index 0000000000..9a5d8d1f6b
--- /dev/null
+++ b/modules/compute-mig/health-check.tf
@@ -0,0 +1,128 @@
+/**
+ * Copyright 2022 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+# tfdoc:file:description Health check resource.
+
+locals {
+ hc = var.health_check_config
+ hc_grpc = try(local.hc.grpc, null) != null
+ hc_http = (
+ try(local.hc.http, null) != null &&
+ lower(try(local.hc.http.use_protocol, "")) == "http"
+ )
+ hc_http2 = (
+ try(local.hc.http, null) != null &&
+ lower(try(local.hc.http.use_protocol, "")) == "http2"
+ )
+ hc_https = (
+ try(local.hc.http, null) != null &&
+ lower(try(local.hc.http.use_protocol, "")) == "https"
+ )
+ hc_ssl = try(local.hc.tcp.use_ssl, null) == true
+ hc_tcp = try(local.hc.tcp, null) != null && !local.hc_ssl
+}
+
+resource "google_compute_health_check" "autohealing" {
+ provider = google-beta
+ count = local.hc != null ? 1 : 0
+ project = var.project_id
+ name = var.name
+ description = local.hc.description
+ check_interval_sec = local.hc.check_interval_sec
+ healthy_threshold = local.hc.healthy_threshold
+ timeout_sec = local.hc.timeout_sec
+ unhealthy_threshold = local.hc.unhealthy_threshold
+
+ dynamic "grpc_health_check" {
+ for_each = local.hc_grpc ? [""] : []
+ content {
+ port = local.hc.grpc.port
+ port_name = local.hc.grpc.port_name
+ port_specification = local.hc.grpc.port_specification
+ grpc_service_name = local.hc.grpc.service_name
+ }
+ }
+
+ dynamic "http_health_check" {
+ for_each = local.hc_http ? [""] : []
+ content {
+ host = local.hc.http.host
+ port = local.hc.http.port
+ port_name = local.hc.http.port_name
+ port_specification = local.hc.http.port_specification
+ proxy_header = local.hc.http.proxy_header
+ request_path = local.hc.http.request_path
+ response = local.hc.http.response
+ }
+ }
+
+ dynamic "http2_health_check" {
+ for_each = local.hc_http2 ? [""] : []
+ content {
+ host = local.hc.http.host
+ port = local.hc.http.port
+ port_name = local.hc.http.port_name
+ port_specification = local.hc.http.port_specification
+ proxy_header = local.hc.http.proxy_header
+ request_path = local.hc.http.request_path
+ response = local.hc.http.response
+ }
+ }
+
+ dynamic "https_health_check" {
+ for_each = local.hc_https ? [""] : []
+ content {
+ host = local.hc.http.host
+ port = local.hc.http.port
+ port_name = local.hc.http.port_name
+ port_specification = local.hc.http.port_specification
+ proxy_header = local.hc.http.proxy_header
+ request_path = local.hc.http.request_path
+ response = local.hc.http.response
+ }
+ }
+
+ dynamic "ssl_health_check" {
+ for_each = local.hc_ssl ? [""] : []
+ content {
+ port = local.hc.tcp.port
+ port_name = local.hc.tcp.port_name
+ port_specification = local.hc.tcp.port_specification
+ proxy_header = local.hc.tcp.proxy_header
+ request = local.hc.tcp.request
+ response = local.hc.tcp.response
+ }
+ }
+
+ dynamic "tcp_health_check" {
+ for_each = local.hc_tcp ? [""] : []
+ content {
+ port = local.hc.tcp.port
+ port_name = local.hc.tcp.port_name
+ port_specification = local.hc.tcp.port_specification
+ proxy_header = local.hc.tcp.proxy_header
+ request = local.hc.tcp.request
+ response = local.hc.tcp.response
+ }
+ }
+
+ dynamic "log_config" {
+ for_each = try(local.hc.enable_logging, null) == true ? [""] : []
+ content {
+ enable = true
+ }
+ }
+}
diff --git a/modules/compute-mig/main.tf b/modules/compute-mig/main.tf
index b5a71fac73..927389952e 100644
--- a/modules/compute-mig/main.tf
+++ b/modules/compute-mig/main.tf
@@ -14,105 +14,50 @@
* limitations under the License.
*/
-resource "google_compute_autoscaler" "default" {
- provider = google-beta
- count = var.regional || var.autoscaler_config == null ? 0 : 1
- project = var.project_id
- name = var.name
- description = "Terraform managed."
- zone = var.location
- target = google_compute_instance_group_manager.default.0.id
-
- autoscaling_policy {
- max_replicas = var.autoscaler_config.max_replicas
- min_replicas = var.autoscaler_config.min_replicas
- cooldown_period = var.autoscaler_config.cooldown_period
-
- dynamic "cpu_utilization" {
- for_each = (
- var.autoscaler_config.cpu_utilization_target == null ? [] : [""]
- )
- content {
- target = var.autoscaler_config.cpu_utilization_target
- }
- }
-
- dynamic "load_balancing_utilization" {
- for_each = (
- var.autoscaler_config.load_balancing_utilization_target == null ? [] : [""]
- )
- content {
- target = var.autoscaler_config.load_balancing_utilization_target
- }
- }
-
- dynamic "metric" {
- for_each = (
- var.autoscaler_config.metric == null
- ? []
- : [var.autoscaler_config.metric]
- )
- iterator = config
- content {
- name = config.value.name
- single_instance_assignment = config.value.single_instance_assignment
- target = config.value.target
- type = config.value.type
- filter = config.value.filter
- }
- }
- }
+locals {
+ health_check = (
+ try(var.auto_healing_policies.health_check, null) == null
+ ? try(google_compute_health_check.autohealing.0.self_link, null)
+ : try(var.auto_healing_policies.health_check, null)
+ )
+ instance_group_manager = (
+ local.is_regional ?
+ google_compute_region_instance_group_manager.default :
+ google_compute_instance_group_manager.default
+ )
+ is_regional = length(split("-", var.location)) == 2
}
-
resource "google_compute_instance_group_manager" "default" {
- provider = google-beta
- count = var.regional ? 0 : 1
- project = var.project_id
- zone = var.location
- name = var.name
- base_instance_name = var.name
- description = "Terraform-managed."
- target_size = var.target_size
- target_pools = var.target_pools
- wait_for_instances = var.wait_for_instances
- dynamic "auto_healing_policies" {
- for_each = var.auto_healing_policies == null ? [] : [var.auto_healing_policies]
- iterator = config
+ provider = google-beta
+ count = local.is_regional ? 0 : 1
+ project = var.project_id
+ zone = var.location
+ name = var.name
+ base_instance_name = var.name
+ description = var.description
+ target_size = var.target_size
+ target_pools = var.target_pools
+ wait_for_instances = try(var.wait_for_instances.enabled, null)
+ wait_for_instances_status = try(var.wait_for_instances.status, null)
+
+ dynamic "all_instances_config" {
+ for_each = var.all_instances_config == null ? [] : [""]
content {
- health_check = config.value.health_check
- initial_delay_sec = config.value.initial_delay_sec
+ labels = try(var.all_instances_config.labels, null)
+ metadata = try(var.all_instances_config.metadata, null)
}
}
- dynamic "stateful_disk" {
- for_each = try(var.stateful_config.mig_config.stateful_disks, {})
- iterator = config
- content {
- device_name = config.key
- delete_rule = config.value.delete_rule
- }
- }
- dynamic "update_policy" {
- for_each = var.update_policy == null ? [] : [var.update_policy]
+
+ dynamic "auto_healing_policies" {
+ for_each = var.auto_healing_policies == null ? [] : [""]
iterator = config
content {
- type = config.value.type
- minimal_action = config.value.minimal_action
- min_ready_sec = config.value.min_ready_sec
- max_surge_fixed = (
- config.value.max_surge_type == "fixed" ? config.value.max_surge : null
- )
- max_surge_percent = (
- config.value.max_surge_type == "percent" ? config.value.max_surge : null
- )
- max_unavailable_fixed = (
- config.value.max_unavailable_type == "fixed" ? config.value.max_unavailable : null
- )
- max_unavailable_percent = (
- config.value.max_unavailable_type == "percent" ? config.value.max_unavailable : null
- )
+ health_check = local.health_check
+ initial_delay_sec = var.auto_healing_policies.initial_delay_sec
}
}
+
dynamic "named_port" {
for_each = var.named_ports == null ? {} : var.named_ports
iterator = config
@@ -121,167 +66,88 @@ resource "google_compute_instance_group_manager" "default" {
port = config.value
}
}
- version {
- instance_template = var.default_version.instance_template
- name = var.default_version.name
- }
- dynamic "version" {
- for_each = var.versions == null ? {} : var.versions
- iterator = version
+
+ dynamic "stateful_disk" {
+ for_each = var.stateful_disks
content {
- name = version.key
- instance_template = version.value.instance_template
- target_size {
- fixed = (
- version.value.target_type == "fixed" ? version.value.target_size : null
- )
- percent = (
- version.value.target_type == "percent" ? version.value.target_size : null
- )
- }
+ device_name = stateful_disk.key
+ delete_rule = stateful_disk.value
}
}
-}
-
-locals {
- instance_group_manager = (
- var.regional ?
- google_compute_region_instance_group_manager.default :
- google_compute_instance_group_manager.default
- )
-}
-
-resource "google_compute_per_instance_config" "default" {
- for_each = try(var.stateful_config.per_instance_config, {})
- #for_each = var.stateful_config && var.stateful_config.per_instance_config == null ? {} : length(var.stateful_config.per_instance_config)
- zone = var.location
- # terraform error, solved with locals
- #instance_group_manager = var.regional ? google_compute_region_instance_group_manager.default : google_compute_instance_group_manager.default
- instance_group_manager = local.instance_group_manager[0].id
- name = each.key
- project = var.project_id
- minimal_action = try(each.value.update_config.minimal_action, null)
- most_disruptive_allowed_action = try(each.value.update_config.most_disruptive_allowed_action, null)
- remove_instance_state_on_destroy = try(each.value.update_config.remove_instance_state_on_destroy, null)
- preserved_state {
- metadata = each.value.metadata
-
- dynamic "disk" {
- for_each = try(each.value.stateful_disks, {})
- #for_each = var.stateful_config.mig_config.stateful_disks == null ? {} : var.stateful_config.mig_config.stateful_disks
- iterator = config
- content {
- device_name = config.key
- source = config.value.source
- mode = config.value.mode
- delete_rule = config.value.delete_rule
- }
+ dynamic "update_policy" {
+ for_each = var.update_policy == null ? [] : [var.update_policy]
+ iterator = p
+ content {
+ minimal_action = p.value.minimal_action
+ type = p.value.type
+ max_surge_fixed = try(p.value.max_surge.fixed, null)
+ max_surge_percent = try(p.value.max_surge.percent, null)
+ max_unavailable_fixed = try(p.value.max_unavailable.fixed, null)
+ max_unavailable_percent = try(p.value.max_unavailable.percent, null)
+ min_ready_sec = p.value.min_ready_sec
+ most_disruptive_allowed_action = p.value.most_disruptive_action
+ replacement_method = p.value.replacement_method
}
}
-}
-
-resource "google_compute_region_autoscaler" "default" {
- provider = google-beta
- count = var.regional && var.autoscaler_config != null ? 1 : 0
- project = var.project_id
- name = var.name
- description = "Terraform managed."
- region = var.location
- target = google_compute_region_instance_group_manager.default.0.id
-
- autoscaling_policy {
- max_replicas = var.autoscaler_config.max_replicas
- min_replicas = var.autoscaler_config.min_replicas
- cooldown_period = var.autoscaler_config.cooldown_period
- dynamic "cpu_utilization" {
- for_each = (
- var.autoscaler_config.cpu_utilization_target == null ? [] : [""]
- )
- content {
- target = var.autoscaler_config.cpu_utilization_target
- }
- }
-
- dynamic "load_balancing_utilization" {
- for_each = (
- var.autoscaler_config.load_balancing_utilization_target == null ? [] : [""]
- )
- content {
- target = var.autoscaler_config.load_balancing_utilization_target
- }
- }
+ version {
+ instance_template = var.instance_template
+ name = var.default_version_name
+ }
- dynamic "metric" {
- for_each = (
- var.autoscaler_config.metric == null
- ? []
- : [var.autoscaler_config.metric]
- )
- iterator = config
- content {
- name = config.value.name
- single_instance_assignment = config.value.single_instance_assignment
- target = config.value.target
- type = config.value.type
- filter = config.value.filter
+ dynamic "version" {
+ for_each = var.versions
+ content {
+ name = version.key
+ instance_template = version.value.instance_template
+ dynamic "target_size" {
+ for_each = version.value.target_size == null ? [] : [""]
+ content {
+ fixed = version.value.target_size.fixed
+ percent = version.value.target_size.percent
+ }
}
}
}
}
-
resource "google_compute_region_instance_group_manager" "default" {
provider = google-beta
- count = var.regional ? 1 : 0
+ count = local.is_regional ? 1 : 0
project = var.project_id
region = var.location
name = var.name
base_instance_name = var.name
- description = "Terraform-managed."
- target_size = var.target_size
- target_pools = var.target_pools
- wait_for_instances = var.wait_for_instances
- dynamic "auto_healing_policies" {
- for_each = var.auto_healing_policies == null ? [] : [var.auto_healing_policies]
- iterator = config
- content {
- health_check = config.value.health_check
- initial_delay_sec = config.value.initial_delay_sec
- }
- }
- dynamic "stateful_disk" {
- for_each = try(var.stateful_config.mig_config.stateful_disks, {})
- iterator = config
+ description = var.description
+ distribution_policy_target_shape = try(
+ var.distribution_policy.target_shape, null
+ )
+ distribution_policy_zones = try(
+ var.distribution_policy.zones, null
+ )
+ target_size = var.target_size
+ target_pools = var.target_pools
+ wait_for_instances = try(var.wait_for_instances.enabled, null)
+ wait_for_instances_status = try(var.wait_for_instances.status, null)
+
+ dynamic "all_instances_config" {
+ for_each = var.all_instances_config == null ? [] : [""]
content {
- device_name = config.key
- delete_rule = config.value.delete_rule
+ labels = try(var.all_instances_config.labels, null)
+ metadata = try(var.all_instances_config.metadata, null)
}
}
- dynamic "update_policy" {
- for_each = var.update_policy == null ? [] : [var.update_policy]
+ dynamic "auto_healing_policies" {
+ for_each = var.auto_healing_policies == null ? [] : [""]
iterator = config
content {
- instance_redistribution_type = config.value.instance_redistribution_type
- type = config.value.type
- minimal_action = config.value.minimal_action
- min_ready_sec = config.value.min_ready_sec
- max_surge_fixed = (
- config.value.max_surge_type == "fixed" ? config.value.max_surge : null
- )
- max_surge_percent = (
- config.value.max_surge_type == "percent" ? config.value.max_surge : null
- )
- max_unavailable_fixed = (
- config.value.max_unavailable_type == "fixed" ? config.value.max_unavailable : null
- )
- max_unavailable_percent = (
- config.value.max_unavailable_type == "percent" ? config.value.max_unavailable : null
- )
+ health_check = local.health_check
+ initial_delay_sec = var.auto_healing_policies.initial_delay_sec
}
}
+
dynamic "named_port" {
for_each = var.named_ports == null ? {} : var.named_ports
iterator = config
@@ -290,172 +156,49 @@ resource "google_compute_region_instance_group_manager" "default" {
port = config.value
}
}
- version {
- instance_template = var.default_version.instance_template
- name = var.default_version.name
- }
- dynamic "version" {
- for_each = var.versions == null ? {} : var.versions
- iterator = version
- content {
- name = version.key
- instance_template = version.value.instance_template
- target_size {
- fixed = (
- version.value.target_type == "fixed" ? version.value.target_size : null
- )
- percent = (
- version.value.target_type == "percent" ? version.value.target_size : null
- )
- }
- }
- }
-}
-
-resource "google_compute_health_check" "http" {
- provider = google-beta
- count = try(var.health_check_config.type, null) == "http" ? 1 : 0
- project = var.project_id
- name = var.name
- description = "Terraform managed."
-
- check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
- healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
- timeout_sec = try(var.health_check_config.config.timeout_sec, null)
- unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
-
- http_health_check {
- host = try(var.health_check_config.check.host, null)
- port = try(var.health_check_config.check.port, null)
- port_name = try(var.health_check_config.check.port_name, null)
- port_specification = try(var.health_check_config.check.port_specification, null)
- proxy_header = try(var.health_check_config.check.proxy_header, null)
- request_path = try(var.health_check_config.check.request_path, null)
- response = try(var.health_check_config.check.response, null)
- }
-
- dynamic "log_config" {
- for_each = try(var.health_check_config.logging, false) ? [""] : []
- content {
- enable = true
- }
- }
-}
-
-resource "google_compute_health_check" "https" {
- provider = google-beta
- count = try(var.health_check_config.type, null) == "https" ? 1 : 0
- project = var.project_id
- name = var.name
- description = "Terraform managed."
-
- check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
- healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
- timeout_sec = try(var.health_check_config.config.timeout_sec, null)
- unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
-
- https_health_check {
- host = try(var.health_check_config.check.host, null)
- port = try(var.health_check_config.check.port, null)
- port_name = try(var.health_check_config.check.port_name, null)
- port_specification = try(var.health_check_config.check.port_specification, null)
- proxy_header = try(var.health_check_config.check.proxy_header, null)
- request_path = try(var.health_check_config.check.request_path, null)
- response = try(var.health_check_config.check.response, null)
- }
-
- dynamic "log_config" {
- for_each = try(var.health_check_config.logging, false) ? [""] : []
- content {
- enable = true
- }
- }
-}
-
-resource "google_compute_health_check" "tcp" {
- provider = google-beta
- count = try(var.health_check_config.type, null) == "tcp" ? 1 : 0
- project = var.project_id
- name = var.name
- description = "Terraform managed."
-
- check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
- healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
- timeout_sec = try(var.health_check_config.config.timeout_sec, null)
- unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
-
- tcp_health_check {
- port = try(var.health_check_config.check.port, null)
- port_name = try(var.health_check_config.check.port_name, null)
- port_specification = try(var.health_check_config.check.port_specification, null)
- proxy_header = try(var.health_check_config.check.proxy_header, null)
- request = try(var.health_check_config.check.request, null)
- response = try(var.health_check_config.check.response, null)
- }
- dynamic "log_config" {
- for_each = try(var.health_check_config.logging, false) ? [""] : []
+ dynamic "stateful_disk" {
+ for_each = var.stateful_disks
content {
- enable = true
+ device_name = stateful_disk.key
+ delete_rule = stateful_disk.value
}
}
-}
-
-resource "google_compute_health_check" "ssl" {
- provider = google-beta
- count = try(var.health_check_config.type, null) == "ssl" ? 1 : 0
- project = var.project_id
- name = var.name
- description = "Terraform managed."
-
- check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
- healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
- timeout_sec = try(var.health_check_config.config.timeout_sec, null)
- unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
-
- ssl_health_check {
- port = try(var.health_check_config.check.port, null)
- port_name = try(var.health_check_config.check.port_name, null)
- port_specification = try(var.health_check_config.check.port_specification, null)
- proxy_header = try(var.health_check_config.check.proxy_header, null)
- request = try(var.health_check_config.check.request, null)
- response = try(var.health_check_config.check.response, null)
- }
- dynamic "log_config" {
- for_each = try(var.health_check_config.logging, false) ? [""] : []
+ dynamic "update_policy" {
+ for_each = var.update_policy == null ? [] : [var.update_policy]
+ iterator = p
content {
- enable = true
+ minimal_action = p.value.minimal_action
+ type = p.value.type
+ instance_redistribution_type = p.value.regional_redistribution_type
+ max_surge_fixed = try(p.value.max_surge.fixed, null)
+ max_surge_percent = try(p.value.max_surge.percent, null)
+ max_unavailable_fixed = try(p.value.max_unavailable.fixed, null)
+ max_unavailable_percent = try(p.value.max_unavailable.percent, null)
+ min_ready_sec = p.value.min_ready_sec
+ most_disruptive_allowed_action = p.value.most_disruptive_action
+ replacement_method = p.value.replacement_method
}
}
-}
-
-resource "google_compute_health_check" "http2" {
- provider = google-beta
- count = try(var.health_check_config.type, null) == "http2" ? 1 : 0
- project = var.project_id
- name = var.name
- description = "Terraform managed."
- check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
- healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
- timeout_sec = try(var.health_check_config.config.timeout_sec, null)
- unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
-
- http2_health_check {
- host = try(var.health_check_config.check.host, null)
- port = try(var.health_check_config.check.port, null)
- port_name = try(var.health_check_config.check.port_name, null)
- port_specification = try(var.health_check_config.check.port_specification, null)
- proxy_header = try(var.health_check_config.check.proxy_header, null)
- request_path = try(var.health_check_config.check.request_path, null)
- response = try(var.health_check_config.check.response, null)
+ version {
+ instance_template = var.instance_template
+ name = var.default_version_name
}
- dynamic "log_config" {
- for_each = try(var.health_check_config.logging, false) ? [""] : []
+ dynamic "version" {
+ for_each = var.versions
content {
- enable = true
+ name = version.key
+ instance_template = version.value.instance_template
+ dynamic "target_size" {
+ for_each = version.value.target_size == null ? [] : [""]
+ content {
+ fixed = version.value.target_size.fixed
+ percent = version.value.target_size.percent
+ }
+ }
}
}
}
diff --git a/modules/compute-mig/outputs.tf b/modules/compute-mig/outputs.tf
index 93de9223d3..a7be7d2ebb 100644
--- a/modules/compute-mig/outputs.tf
+++ b/modules/compute-mig/outputs.tf
@@ -37,13 +37,6 @@ output "health_check" {
value = (
var.health_check_config == null
? null
- : try(
- google_compute_health_check.http.0,
- google_compute_health_check.https.0,
- google_compute_health_check.tcp.0,
- google_compute_health_check.ssl.0,
- google_compute_health_check.http2.0,
- {}
- )
+ : google_compute_health_check.autohealing.0
)
}
diff --git a/modules/compute-mig/stateful-config.tf b/modules/compute-mig/stateful-config.tf
new file mode 100644
index 0000000000..1e9e056e56
--- /dev/null
+++ b/modules/compute-mig/stateful-config.tf
@@ -0,0 +1,91 @@
+/**
+ * Copyright 2022 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+# tfdoc:file:description Instance-level stateful configuration resources.
+
+resource "google_compute_per_instance_config" "default" {
+ for_each = local.is_regional ? {} : var.stateful_config
+ project = var.project_id
+ zone = var.location
+ name = each.key
+ instance_group_manager = try(
+ google_compute_instance_group_manager.default.0.id, null
+ )
+ minimal_action = each.value.minimal_action
+ most_disruptive_allowed_action = each.value.most_disruptive_action
+ remove_instance_state_on_destroy = each.value.remove_state_on_destroy
+
+ dynamic "preserved_state" {
+ for_each = each.value.preserved_state == null ? [] : [""]
+ content {
+ metadata = each.value.preserved_state.metadata
+ dynamic "disk" {
+ for_each = (
+ each.value.preserved_state.disks == null
+ ? {}
+ : each.value.preserved_state.disks
+ )
+ content {
+ device_name = disk.key
+ source = disk.value.source
+ delete_rule = (
+ disk.value.delete_on_instance_deletion == true
+ ? "ON_PERMANENT_INSTANCE_DELETION"
+ : "NEVER"
+ )
+ mode = disk.value.read_only == true ? "READ_ONLY" : "READ_WRITE"
+ }
+ }
+ }
+ }
+}
+
+resource "google_compute_region_per_instance_config" "default" {
+ for_each = local.is_regional ? var.stateful_config : {}
+ project = var.project_id
+ region = var.location
+ name = each.key
+ region_instance_group_manager = try(
+ google_compute_region_instance_group_manager.default.0.id, null
+ )
+ minimal_action = each.value.minimal_action
+ most_disruptive_allowed_action = each.value.most_disruptive_action
+ remove_instance_state_on_destroy = each.value.remove_state_on_destroy
+
+ dynamic "preserved_state" {
+ for_each = each.value.preserved_state == null ? [] : [""]
+ content {
+ metadata = each.value.preserved_state.metadata
+ dynamic "disk" {
+ for_each = (
+ each.value.preserved_state.disks == null
+ ? {}
+ : each.value.preserved_state.disks
+ )
+ content {
+ device_name = disk.key
+ source = disk.value.source
+ delete_rule = (
+ disk.value.delete_on_instance_deletion == true
+ ? "ON_PERMANENT_INSTANCE_DELETION"
+ : "NEVER"
+ )
+ mode = disk.value.read_only == true ? "READ_ONLY" : "READ_WRITE"
+ }
+ }
+ }
+ }
+}
diff --git a/modules/compute-mig/variables.tf b/modules/compute-mig/variables.tf
index 76f4fb215e..299dacc889 100644
--- a/modules/compute-mig/variables.tf
+++ b/modules/compute-mig/variables.tf
@@ -14,57 +14,149 @@
* limitations under the License.
*/
+variable "all_instances_config" {
+ description = "Metadata and labels set to all instances in the group."
+ type = object({
+ labels = optional(map(string))
+ metadata = optional(map(string))
+ })
+ default = null
+}
+
variable "auto_healing_policies" {
description = "Auto-healing policies for this group."
type = object({
- health_check = string
+ health_check = optional(string)
initial_delay_sec = number
})
default = null
}
variable "autoscaler_config" {
- description = "Optional autoscaler configuration. Only one of 'cpu_utilization_target' 'load_balancing_utilization_target' or 'metric' can be not null."
+ description = "Optional autoscaler configuration."
type = object({
- max_replicas = number
- min_replicas = number
- cooldown_period = number
- cpu_utilization_target = number
- load_balancing_utilization_target = number
- metric = object({
- name = string
- single_instance_assignment = number
- target = number
- type = string # GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE
- filter = string
- })
+ max_replicas = number
+ min_replicas = number
+ cooldown_period = optional(number)
+ mode = optional(string) # OFF, ONLY_UP, ON
+ scaling_control = optional(object({
+ down = optional(object({
+ max_replicas_fixed = optional(number)
+ max_replicas_percent = optional(number)
+ time_window_sec = optional(number)
+ }))
+ in = optional(object({
+ max_replicas_fixed = optional(number)
+ max_replicas_percent = optional(number)
+ time_window_sec = optional(number)
+ }))
+ }), {})
+ scaling_signals = optional(object({
+ cpu_utilization = optional(object({
+ target = number
+ optimize_availability = optional(bool)
+ }))
+ load_balancing_utilization = optional(object({
+ target = number
+ }))
+ metrics = optional(list(object({
+ name = string
+ type = string # GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE
+ target_value = number
+ single_instance_assignment = optional(number)
+ time_series_filter = optional(string)
+ })))
+ schedules = optional(list(object({
+ duration_sec = number
+ name = string
+ min_required_replicas = number
+ cron_schedule = string
+ description = optional(bool)
+ timezone = optional(string)
+ disabled = optional(bool)
+ })))
+ }), {})
})
default = null
}
-variable "default_version" {
- description = "Default application version template. Additional versions can be specified via the `versions` variable."
+variable "default_version_name" {
+ description = "Name used for the default version."
+ type = string
+ default = "default"
+}
+
+variable "description" {
+ description = "Optional description used for all resources managed by this module."
+ type = string
+ default = "Terraform managed."
+}
+
+variable "distribution_policy" {
+ description = "DIstribution policy for regional MIG."
type = object({
- instance_template = string
- name = string
+ target_shape = optional(string)
+ zones = optional(list(string))
})
+ default = null
}
variable "health_check_config" {
description = "Optional auto-created health check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage."
type = object({
- type = string # http https tcp ssl http2
- check = map(any) # actual health check block attributes
- config = map(number) # interval, thresholds, timeout
- logging = bool
+ check_interval_sec = optional(number)
+ description = optional(string, "Terraform managed.")
+ enable_logging = optional(bool, false)
+ healthy_threshold = optional(number)
+ timeout_sec = optional(number)
+ unhealthy_threshold = optional(number)
+ grpc = optional(object({
+ port = optional(number)
+ port_name = optional(string)
+ port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT
+ service_name = optional(string)
+ }))
+ http = optional(object({
+ host = optional(string)
+ port = optional(number)
+ port_name = optional(string)
+ port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT
+ proxy_header = optional(string)
+ request_path = optional(string)
+ response = optional(string)
+ use_protocol = optional(string, "http") # http http2 https
+ }))
+ tcp = optional(object({
+ port = optional(number)
+ port_name = optional(string)
+ port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT
+ proxy_header = optional(string)
+ request = optional(string)
+ response = optional(string)
+ use_ssl = optional(bool, false)
+ }))
})
default = null
+ validation {
+ condition = (
+ (try(var.health_check_config.grpc, null) == null ? 0 : 1) +
+ (try(var.health_check_config.http, null) == null ? 0 : 1) +
+ (try(var.health_check_config.tcp, null) == null ? 0 : 1) <= 1
+ )
+ error_message = "Only one health check type can be configured at a time."
+ }
+}
+
+variable "instance_template" {
+ description = "Instance template for the default version."
+ type = string
}
variable "location" {
- description = "Compute zone, or region if `regional` is set to true."
+ description = "Compute zone or region."
type = string
}
+
variable "name" {
description = "Managed group name."
type = string
@@ -81,41 +173,30 @@ variable "project_id" {
type = string
}
-variable "regional" {
- description = "Use regional instance group. When set, `location` should be set to the region."
- type = bool
- default = false
+variable "stateful_disks" {
+ description = "Stateful disk configuration applied at the MIG level to all instances, in device name => on permanent instance delete rule as boolean."
+ type = map(bool)
+ default = {}
+ nullable = false
}
variable "stateful_config" {
- description = "Stateful configuration can be done by individual instances or for all instances in the MIG. They key in per_instance_config is the name of the specific instance. The key of the stateful_disks is the 'device_name' field of the resource. Please note that device_name is defined at the OS mount level, unlike the disk name."
- type = object({
- per_instance_config = map(object({
- #name is the key
- #name = string
- stateful_disks = map(object({
- #device_name is the key
- source = string
- mode = string # READ_WRITE | READ_ONLY
- delete_rule = string # NEVER | ON_PERMANENT_INSTANCE_DELETION
- }))
- metadata = map(string)
- update_config = object({
- minimal_action = string # NONE | REPLACE | RESTART | REFRESH
- most_disruptive_allowed_action = string # REPLACE | RESTART | REFRESH | NONE
- remove_instance_state_on_destroy = bool
- })
+ description = "Stateful configuration for individual instances."
+ type = map(object({
+ minimal_action = optional(string)
+ most_disruptive_action = optional(string)
+ remove_state_on_destroy = optional(bool)
+ preserved_state = optional(object({
+ disks = optional(map(object({
+ source = string
+ delete_on_instance_deletion = optional(bool)
+ read_only = optional(bool)
+ })))
+ metadata = optional(map(string))
}))
-
- mig_config = object({
- stateful_disks = map(object({
- #device_name is the key
- delete_rule = string # NEVER | ON_PERMANENT_INSTANCE_DELETION
- }))
- })
-
- })
- default = null
+ }))
+ default = {}
+ nullable = false
}
variable "target_pools" {
@@ -131,32 +212,44 @@ variable "target_size" {
}
variable "update_policy" {
- description = "Update policy. Type can be 'OPPORTUNISTIC' or 'PROACTIVE', action 'REPLACE' or 'restart', surge type 'fixed' or 'percent'."
+ description = "Update policy. Minimal action and type are required."
type = object({
- instance_redistribution_type = optional(string, "PROACTIVE") # NONE | PROACTIVE. The attribute is ignored if regional is set to false.
- max_surge_type = string # fixed | percent
- max_surge = number
- max_unavailable_type = string
- max_unavailable = number
- minimal_action = string # REPLACE | RESTART
- min_ready_sec = number
- type = string # OPPORTUNISTIC | PROACTIVE
+ minimal_action = string
+ type = string
+ max_surge = optional(object({
+ fixed = optional(number)
+ percent = optional(number)
+ }))
+ max_unavailable = optional(object({
+ fixed = optional(number)
+ percent = optional(number)
+ }))
+ min_ready_sec = optional(number)
+ most_disruptive_action = optional(string)
+ regional_redistribution_type = optional(string)
+ replacement_method = optional(string)
})
default = null
}
variable "versions" {
- description = "Additional application versions, target_type is either 'fixed' or 'percent'."
+ description = "Additional application versions, target_size is optional."
type = map(object({
instance_template = string
- target_type = string # fixed | percent
- target_size = number
+ target_size = optional(object({
+ fixed = optional(number)
+ percent = optional(number)
+ }))
}))
- default = null
+ default = {}
+ nullable = false
}
variable "wait_for_instances" {
description = "Wait for all instances to be created/updated before returning."
- type = bool
- default = null
+ type = object({
+ enabled = bool
+ status = optional(string)
+ })
+ default = null
}
diff --git a/tests/modules/compute_mig/fixture/main.tf b/tests/modules/compute_mig/fixture/main.tf
index 5d87f40f7d..b91c0140eb 100644
--- a/tests/modules/compute_mig/fixture/main.tf
+++ b/tests/modules/compute_mig/fixture/main.tf
@@ -24,21 +24,18 @@ resource "google_compute_disk" "default" {
}
module "test" {
- source = "../../../../modules/compute-mig"
- project_id = "my-project"
- location = "europe-west1"
- name = "test-mig"
- target_size = 2
- default_version = {
- instance_template = "foo-template"
- name = "foo"
- }
- autoscaler_config = var.autoscaler_config
- health_check_config = var.health_check_config
- named_ports = var.named_ports
- regional = var.regional
- stateful_config = var.stateful_config
-
- update_policy = var.update_policy
- versions = var.versions
+ source = "../../../../modules/compute-mig"
+ project_id = "my-project"
+ name = "test-mig"
+ target_size = 2
+ default_version_name = "foo"
+ instance_template = "foo-template"
+ location = var.location
+ autoscaler_config = var.autoscaler_config
+ health_check_config = var.health_check_config
+ named_ports = var.named_ports
+ stateful_config = var.stateful_config
+ stateful_disks = var.stateful_disks
+ update_policy = var.update_policy
+ versions = var.versions
}
diff --git a/tests/modules/compute_mig/fixture/variables.tf b/tests/modules/compute_mig/fixture/variables.tf
index b9fde83435..70117838da 100644
--- a/tests/modules/compute_mig/fixture/variables.tf
+++ b/tests/modules/compute_mig/fixture/variables.tf
@@ -14,101 +14,82 @@
* limitations under the License.
*/
-variable "autoscaler_config" {
- type = object({
- max_replicas = number
- min_replicas = number
- cooldown_period = number
- cpu_utilization_target = number
- load_balancing_utilization_target = number
- metric = object({
- name = string
- single_instance_assignment = number
- target = number
- type = string # GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE
- filter = string
- })
- })
+variable "all_instances_config" {
+ type = any
default = null
}
variable "auto_healing_policies" {
- type = object({
- health_check = string
- initial_delay_sec = number
- })
+ type = any
+ default = null
+}
+
+variable "autoscaler_config" {
+ type = any
+ default = null
+}
+
+variable "default_version_name" {
+ type = any
+ default = "default"
+}
+
+variable "description" {
+ type = any
+ default = "Terraform managed."
+}
+
+variable "distribution_policy" {
+ type = any
default = null
}
variable "health_check_config" {
- type = object({
- type = string # http https tcp ssl http2
- check = map(any) # actual health check block attributes
- config = map(number) # interval, thresholds, timeout
- logging = bool
- })
+ type = any
default = null
}
+variable "location" {
+ type = any
+ default = "europe-west1-b"
+}
+
variable "named_ports" {
- type = map(number)
+ type = any
default = null
}
-variable "regional" {
- type = bool
- default = false
+variable "stateful_disks" {
+ type = any
+ default = {}
}
variable "stateful_config" {
- description = "Stateful configuration can be done by individual instances or for all instances in the MIG. They key in per_instance_config is the name of the specific instance. The key of the stateful_disks is the 'device_name' field of the resource. Please note that device_name is defined at the OS mount level, unlike the disk name."
- type = object({
- per_instance_config = map(object({
- #name is the key
- #name = string
- stateful_disks = map(object({
- #device_name is the key
- source = string
- mode = string # READ_WRITE | READ_ONLY
- delete_rule = string # NEVER | ON_PERMANENT_INSTANCE_DELETION
- }))
- metadata = map(string)
- update_config = object({
- minimal_action = string # NONE | REPLACE | RESTART | REFRESH
- most_disruptive_allowed_action = string # REPLACE | RESTART | REFRESH | NONE
- remove_instance_state_on_destroy = bool
- })
- }))
-
- mig_config = object({
- stateful_disks = map(object({
- #device_name is the key
- delete_rule = string # NEVER | ON_PERMANENT_INSTANCE_DELETION
- }))
- })
-
- })
+ type = any
+ default = {}
+}
+
+variable "target_pools" {
+ type = any
+ default = []
+}
+
+variable "target_size" {
+ type = any
default = null
}
variable "update_policy" {
- type = object({
- type = string # OPPORTUNISTIC | PROACTIVE
- minimal_action = string # REPLACE | RESTART
- min_ready_sec = number
- max_surge_type = string # fixed | percent
- max_surge = number
- max_unavailable_type = string
- max_unavailable = number
- })
+ type = any
default = null
}
variable "versions" {
- type = map(object({
- instance_template = string
- target_type = string # fixed | percent
- target_size = number
- }))
+ type = any
+ default = {}
+}
+
+variable "wait_for_instances" {
+ type = any
default = null
}
diff --git a/tests/modules/compute_mig/test_plan.py b/tests/modules/compute_mig/test_plan.py
index 253e27bc1c..e24a7ca70c 100644
--- a/tests/modules/compute_mig/test_plan.py
+++ b/tests/modules/compute_mig/test_plan.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+
def test_defaults(plan_runner):
"Test variable defaults."
_, resources = plan_runner()
@@ -21,7 +22,7 @@ def test_defaults(plan_runner):
assert mig['type'] == 'google_compute_instance_group_manager'
assert mig['values']['target_size'] == 2
assert mig['values']['zone']
- _, resources = plan_runner(regional='true')
+ _, resources = plan_runner(location='"europe-west1"')
assert len(resources) == 1
mig = resources[0]
assert mig['type'] == 'google_compute_region_instance_group_manager'
@@ -31,7 +32,12 @@ def test_defaults(plan_runner):
def test_health_check(plan_runner):
"Test health check resource."
- health_check_config = '{type="tcp", check={port=80}, config=null, logging=false}'
+ health_check_config = '''{
+ enable_logging = true
+ tcp = {
+ port = 80
+ }
+ }'''
_, resources = plan_runner(health_check_config=health_check_config)
assert len(resources) == 2
assert any(r['type'] == 'google_compute_health_check' for r in resources)
@@ -39,20 +45,26 @@ def test_health_check(plan_runner):
def test_autoscaler(plan_runner):
"Test autoscaler resource."
- autoscaler_config = (
- '{'
- 'max_replicas=3, min_replicas=1, cooldown_period=60,'
- 'cpu_utilization_target=65, load_balancing_utilization_target=null,'
- 'metric=null'
- '}'
- )
+ autoscaler_config = '''{
+ colldown_period = 60
+ max_replicas = 3
+ min_replicas = 1
+ scaling_signals = {
+ cpu_utilization = {
+ target = 65
+ }
+ }
+ }'''
_, resources = plan_runner(autoscaler_config=autoscaler_config)
assert len(resources) == 2
autoscaler = resources[0]
assert autoscaler['type'] == 'google_compute_autoscaler'
assert autoscaler['values']['autoscaling_policy'] == [{
'cooldown_period': 60,
- 'cpu_utilization': [{'predictive_method': 'NONE', 'target': 65}],
+ 'cpu_utilization': [{
+ 'predictive_method': 'NONE',
+ 'target': 65
+ }],
'load_balancing_utilization': [],
'max_replicas': 3,
'metric': [],
@@ -62,7 +74,7 @@ def test_autoscaler(plan_runner):
'scaling_schedules': [],
}]
_, resources = plan_runner(autoscaler_config=autoscaler_config,
- regional='true')
+ location='"europe-west1"')
assert len(resources) == 2
autoscaler = resources[0]
assert autoscaler['type'] == 'google_compute_region_autoscaler'
@@ -71,17 +83,10 @@ def test_autoscaler(plan_runner):
def test_stateful_mig(plan_runner):
"Test stateful instances - mig."
- stateful_config = (
- '{'
- 'per_instance_config = {},'
- 'mig_config = {'
- 'stateful_disks = {'
- 'persistent-disk-1 = {delete_rule="NEVER"}'
- '}'
- '}'
- '}'
- )
- _, resources = plan_runner(stateful_config=stateful_config)
+ stateful_disks = '''{
+ persistent-disk-1 = null
+ }'''
+ _, resources = plan_runner(stateful_disks=stateful_disks)
assert len(resources) == 1
statefuldisk = resources[0]
assert statefuldisk['type'] == 'google_compute_instance_group_manager'
@@ -93,35 +98,19 @@ def test_stateful_mig(plan_runner):
def test_stateful_instance(plan_runner):
"Test stateful instances - instance."
- stateful_config = (
- '{'
- 'per_instance_config = {'
- 'instance-1 = {'
- 'stateful_disks = {'
- 'persistent-disk-1 = {'
- 'source = "test-disk",'
- 'mode = "READ_ONLY",'
- 'delete_rule= "NEVER",'
- '},'
- '},'
- 'metadata = {'
- 'foo = "bar"'
- '},'
- 'update_config = {'
- 'minimal_action = "NONE",'
- 'most_disruptive_allowed_action = "REPLACE",'
- 'remove_instance_state_on_destroy = false,'
-
- '},'
- '},'
- '},'
- 'mig_config = {'
- 'stateful_disks = {'
- 'persistent-disk-1 = {delete_rule="NEVER"}'
- '}'
- '}'
- '}'
- )
+ stateful_config = '''{
+ instance-1 = {
+ most_disruptive_action = "REPLACE",
+ preserved_state = {
+ disks = {
+ persistent-disk-1 = {
+ source = "test-disk"
+ }
+ }
+ metadata = { foo = "bar" }
+ }
+ }
+ }'''
_, resources = plan_runner(stateful_config=stateful_config)
assert len(resources) == 2
instanceconfig = resources[0]
@@ -134,13 +123,12 @@ def test_stateful_instance(plan_runner):
'device_name': 'persistent-disk-1',
'delete_rule': 'NEVER',
'source': 'test-disk',
- 'mode': 'READ_ONLY',
+ 'mode': 'READ_WRITE',
}],
'metadata': {
'foo': 'bar'
}
}]
-
assert instanceconfig['values']['minimal_action'] == 'NONE'
assert instanceconfig['values']['most_disruptive_allowed_action'] == 'REPLACE'
assert instanceconfig['values']['remove_instance_state_on_destroy'] == False