Refactor compute-mig module for Terraform 1.3 (#931)

* wip: autoscaler

* wip: fix autoscaler

* wip: health check

* wip: untested

* wip: tests and examples missing

* wip: examples

* wip: consumers

* blueprint tests

* fast
This commit is contained in:
Ludovico Magnocavallo 2022-11-01 09:38:59 +01:00 committed by GitHub
parent d7e0af75b2
commit a30c186f1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1019 additions and 816 deletions

View File

@ -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
}
}
}

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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. | <code title="object&#40;&#123;&#10; instance_template &#61; string&#10; name &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [location](variables.tf#L64) | Compute zone, or region if `regional` is set to true. | <code>string</code> | ✓ | |
| [name](variables.tf#L68) | Managed group name. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L79) | Project id. | <code>string</code> | ✓ | |
| [auto_healing_policies](variables.tf#L17) | Auto-healing policies for this group. | <code title="object&#40;&#123;&#10; health_check &#61; string&#10; initial_delay_sec &#61; number&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [autoscaler_config](variables.tf#L26) | Optional autoscaler configuration. Only one of 'cpu_utilization_target' 'load_balancing_utilization_target' or 'metric' can be not null. | <code title="object&#40;&#123;&#10; max_replicas &#61; number&#10; min_replicas &#61; number&#10; cooldown_period &#61; number&#10; cpu_utilization_target &#61; number&#10; load_balancing_utilization_target &#61; number&#10; metric &#61; object&#40;&#123;&#10; name &#61; string&#10; single_instance_assignment &#61; number&#10; target &#61; number&#10; type &#61; string &#35; GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE&#10; filter &#61; string&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [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. | <code title="object&#40;&#123;&#10; type &#61; string &#35; http https tcp ssl http2&#10; check &#61; map&#40;any&#41; &#35; actual health check block attributes&#10; config &#61; map&#40;number&#41; &#35; interval, thresholds, timeout&#10; logging &#61; bool&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [named_ports](variables.tf#L73) | Named ports. | <code>map&#40;number&#41;</code> | | <code>null</code> |
| [regional](variables.tf#L84) | Use regional instance group. When set, `location` should be set to the region. | <code>bool</code> | | <code>false</code> |
| [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. | <code title="object&#40;&#123;&#10; per_instance_config &#61; map&#40;object&#40;&#123;&#10; stateful_disks &#61; map&#40;object&#40;&#123;&#10; source &#61; string&#10; mode &#61; string &#35; READ_WRITE &#124; READ_ONLY &#10; delete_rule &#61; string &#35; NEVER &#124; ON_PERMANENT_INSTANCE_DELETION&#10; &#125;&#41;&#41;&#10; metadata &#61; map&#40;string&#41;&#10; update_config &#61; object&#40;&#123;&#10; minimal_action &#61; string &#35; NONE &#124; REPLACE &#124; RESTART &#124; REFRESH&#10; most_disruptive_allowed_action &#61; string &#35; REPLACE &#124; RESTART &#124; REFRESH &#124; NONE&#10; remove_instance_state_on_destroy &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#41;&#10;&#10;&#10; mig_config &#61; object&#40;&#123;&#10; stateful_disks &#61; map&#40;object&#40;&#123;&#10; delete_rule &#61; string &#35; NEVER &#124; ON_PERMANENT_INSTANCE_DELETION&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10;&#10;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [target_pools](variables.tf#L121) | Optional list of URLs for target pools to which new instances in the group are added. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [target_size](variables.tf#L127) | Group target size, leave null when using an autoscaler. | <code>number</code> | | <code>null</code> |
| [update_policy](variables.tf#L133) | Update policy. Type can be 'OPPORTUNISTIC' or 'PROACTIVE', action 'REPLACE' or 'restart', surge type 'fixed' or 'percent'. | <code title="object&#40;&#123;&#10; instance_redistribution_type &#61; optional&#40;string, &#34;PROACTIVE&#34;&#41; &#35; NONE &#124; PROACTIVE. The attribute is ignored if regional is set to false.&#10; max_surge_type &#61; string &#35; fixed &#124; percent&#10; max_surge &#61; number&#10; max_unavailable_type &#61; string&#10; max_unavailable &#61; number&#10; minimal_action &#61; string &#35; REPLACE &#124; RESTART&#10; min_ready_sec &#61; number&#10; type &#61; string &#35; OPPORTUNISTIC &#124; PROACTIVE&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [versions](variables.tf#L148) | Additional application versions, target_type is either 'fixed' or 'percent'. | <code title="map&#40;object&#40;&#123;&#10; instance_template &#61; string&#10; target_type &#61; string &#35; fixed &#124; percent&#10; target_size &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>null</code> |
| [wait_for_instances](variables.tf#L158) | Wait for all instances to be created/updated before returning. | <code>bool</code> | | <code>null</code> |
| [instance_template](variables.tf#L150) | Instance template for the default version. | <code>string</code> | ✓ | |
| [location](variables.tf#L155) | Compute zone or region. | <code>string</code> | ✓ | |
| [name](variables.tf#L160) | Managed group name. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L171) | Project id. | <code>string</code> | ✓ | |
| [all_instances_config](variables.tf#L17) | Metadata and labels set to all instances in the group. | <code title="object&#40;&#123;&#10; labels &#61; optional&#40;map&#40;string&#41;&#41;&#10; metadata &#61; optional&#40;map&#40;string&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [auto_healing_policies](variables.tf#L26) | Auto-healing policies for this group. | <code title="object&#40;&#123;&#10; health_check &#61; optional&#40;string&#41;&#10; initial_delay_sec &#61; number&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [autoscaler_config](variables.tf#L35) | Optional autoscaler configuration. | <code title="object&#40;&#123;&#10; max_replicas &#61; number&#10; min_replicas &#61; number&#10; cooldown_period &#61; optional&#40;number&#41;&#10; mode &#61; optional&#40;string&#41; &#35; OFF, ONLY_UP, ON&#10; scaling_control &#61; optional&#40;object&#40;&#123;&#10; down &#61; optional&#40;object&#40;&#123;&#10; max_replicas_fixed &#61; optional&#40;number&#41;&#10; max_replicas_percent &#61; optional&#40;number&#41;&#10; time_window_sec &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10; in &#61; optional&#40;object&#40;&#123;&#10; max_replicas_fixed &#61; optional&#40;number&#41;&#10; max_replicas_percent &#61; optional&#40;number&#41;&#10; time_window_sec &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; scaling_signals &#61; optional&#40;object&#40;&#123;&#10; cpu_utilization &#61; optional&#40;object&#40;&#123;&#10; target &#61; number&#10; optimize_availability &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; load_balancing_utilization &#61; optional&#40;object&#40;&#123;&#10; target &#61; number&#10; &#125;&#41;&#41;&#10; metrics &#61; optional&#40;list&#40;object&#40;&#123;&#10; name &#61; string&#10; type &#61; string &#35; GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE&#10; target_value &#61; number&#10; single_instance_assignment &#61; optional&#40;number&#41;&#10; time_series_filter &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#41;&#10; schedules &#61; optional&#40;list&#40;object&#40;&#123;&#10; duration_sec &#61; number&#10; name &#61; string&#10; min_required_replicas &#61; number&#10; cron_schedule &#61; string&#10; description &#61; optional&#40;bool&#41;&#10; timezone &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [default_version_name](variables.tf#L83) | Name used for the default version. | <code>string</code> | | <code>&#34;default&#34;</code> |
| [description](variables.tf#L89) | Optional description used for all resources managed by this module. | <code>string</code> | | <code>&#34;Terraform managed.&#34;</code> |
| [distribution_policy](variables.tf#L95) | DIstribution policy for regional MIG. | <code title="object&#40;&#123;&#10; target_shape &#61; optional&#40;string&#41;&#10; zones &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [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. | <code title="object&#40;&#123;&#10; check_interval_sec &#61; optional&#40;number&#41;&#10; description &#61; optional&#40;string, &#34;Terraform managed.&#34;&#41;&#10; enable_logging &#61; optional&#40;bool, false&#41;&#10; healthy_threshold &#61; optional&#40;number&#41;&#10; timeout_sec &#61; optional&#40;number&#41;&#10; unhealthy_threshold &#61; optional&#40;number&#41;&#10; grpc &#61; optional&#40;object&#40;&#123;&#10; port &#61; optional&#40;number&#41;&#10; port_name &#61; optional&#40;string&#41;&#10; port_specification &#61; optional&#40;string&#41; &#35; USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT&#10; service_name &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; http &#61; optional&#40;object&#40;&#123;&#10; host &#61; optional&#40;string&#41;&#10; port &#61; optional&#40;number&#41;&#10; port_name &#61; optional&#40;string&#41;&#10; port_specification &#61; optional&#40;string&#41; &#35; USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT&#10; proxy_header &#61; optional&#40;string&#41;&#10; request_path &#61; optional&#40;string&#41;&#10; response &#61; optional&#40;string&#41;&#10; use_protocol &#61; optional&#40;string, &#34;http&#34;&#41; &#35; http http2 https&#10; &#125;&#41;&#41;&#10; tcp &#61; optional&#40;object&#40;&#123;&#10; port &#61; optional&#40;number&#41;&#10; port_name &#61; optional&#40;string&#41;&#10; port_specification &#61; optional&#40;string&#41; &#35; USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT&#10; proxy_header &#61; optional&#40;string&#41;&#10; request &#61; optional&#40;string&#41;&#10; response &#61; optional&#40;string&#41;&#10; use_ssl &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [named_ports](variables.tf#L165) | Named ports. | <code>map&#40;number&#41;</code> | | <code>null</code> |
| [stateful_config](variables.tf#L183) | Stateful configuration for individual instances. | <code title="map&#40;object&#40;&#123;&#10; minimal_action &#61; optional&#40;string&#41;&#10; most_disruptive_action &#61; optional&#40;string&#41;&#10; remove_state_on_destroy &#61; optional&#40;bool&#41;&#10; preserved_state &#61; optional&#40;object&#40;&#123;&#10; disks &#61; optional&#40;map&#40;object&#40;&#123;&#10; source &#61; string&#10; delete_on_instance_deletion &#61; optional&#40;bool&#41;&#10; read_only &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#41;&#10; metadata &#61; optional&#40;map&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [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. | <code>map&#40;bool&#41;</code> | | <code>&#123;&#125;</code> |
| [target_pools](variables.tf#L202) | Optional list of URLs for target pools to which new instances in the group are added. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [target_size](variables.tf#L208) | Group target size, leave null when using an autoscaler. | <code>number</code> | | <code>null</code> |
| [update_policy](variables.tf#L214) | Update policy. Minimal action and type are required. | <code title="object&#40;&#123;&#10; minimal_action &#61; string&#10; type &#61; string&#10; max_surge &#61; optional&#40;object&#40;&#123;&#10; fixed &#61; optional&#40;number&#41;&#10; percent &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10; max_unavailable &#61; optional&#40;object&#40;&#123;&#10; fixed &#61; optional&#40;number&#41;&#10; percent &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10; min_ready_sec &#61; optional&#40;number&#41;&#10; most_disruptive_action &#61; optional&#40;string&#41;&#10; regional_redistribution_type &#61; optional&#40;string&#41;&#10; replacement_method &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [versions](variables.tf#L235) | Additional application versions, target_size is optional. | <code title="map&#40;object&#40;&#123;&#10; instance_template &#61; string&#10; target_size &#61; optional&#40;object&#40;&#123;&#10; fixed &#61; optional&#40;number&#41;&#10; percent &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [wait_for_instances](variables.tf#L248) | Wait for all instances to be created/updated before returning. | <code title="object&#40;&#123;&#10; enabled &#61; bool&#10; status &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
## Outputs

View File

@ -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
}
}
}
}

View File

@ -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
}
}
}

View File

@ -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
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 {
labels = try(var.all_instances_config.labels, null)
metadata = try(var.all_instances_config.metadata, null)
}
}
dynamic "auto_healing_policies" {
for_each = var.auto_healing_policies == null ? [] : [var.auto_healing_policies]
for_each = var.auto_healing_policies == null ? [] : [""]
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
content {
device_name = config.key
delete_rule = config.value.delete_rule
}
}
dynamic "update_policy" {
for_each = var.update_policy == null ? [] : [var.update_policy]
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 "stateful_disk" {
for_each = var.stateful_disks
content {
device_name = stateful_disk.key
delete_rule = stateful_disk.value
}
}
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
}
}
version {
instance_template = var.instance_template
name = var.default_version_name
}
dynamic "version" {
for_each = var.versions == null ? {} : var.versions
iterator = version
for_each = var.versions
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
)
dynamic "target_size" {
for_each = version.value.target_size == null ? [] : [""]
content {
fixed = version.value.target_size.fixed
percent = version.value.target_size.percent
}
}
}
}
}
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
}
}
}
}
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
}
}
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
}
}
}
}
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
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 {
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
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 "stateful_disk" {
for_each = var.stateful_disks
content {
device_name = stateful_disk.key
delete_rule = stateful_disk.value
}
}
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
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
}
}
version {
instance_template = var.instance_template
name = var.default_version_name
}
dynamic "version" {
for_each = var.versions == null ? {} : var.versions
iterator = version
for_each = var.versions
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
)
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_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) ? [""] : []
content {
enable = true
}
}
}
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) ? [""] : []
content {
enable = true
}
}
}
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)
}
dynamic "log_config" {
for_each = try(var.health_check_config.logging, false) ? [""] : []
content {
enable = true
}
}
}

View File

@ -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
)
}

View File

@ -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"
}
}
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
})
}))
type = any
default = {}
}
mig_config = object({
stateful_disks = map(object({
#device_name is the key
delete_rule = string # NEVER | ON_PERMANENT_INSTANCE_DELETION
}))
})
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
}

View File

@ -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