diff --git a/azurerm/helpers/azure/container_group.go b/azurerm/helpers/azure/container_group.go new file mode 100644 index 000000000000..ca59efbfc616 --- /dev/null +++ b/azurerm/helpers/azure/container_group.go @@ -0,0 +1,90 @@ +package azure + +import ( + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" +) + +func SchemaContainerGroupProbe() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "exec": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + }, + + "http_get": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "path": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "port": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ValidateFunc: validate.PortNumber, + }, + "scheme": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "Http", + "Https", + }, false), + }, + }, + }, + }, + + "initial_delay_seconds": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + + "period_seconds": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + + "failure_threshold": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + + "success_threshold": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + + "timeout_seconds": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + }, + }, + } +} diff --git a/azurerm/resource_arm_container_group.go b/azurerm/resource_arm_container_group.go index da4dcde03d19..69fa0e810e60 100644 --- a/azurerm/resource_arm_container_group.go +++ b/azurerm/resource_arm_container_group.go @@ -12,7 +12,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" - + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" @@ -307,6 +307,10 @@ func resourceArmContainerGroup() *schema.Resource { }, }, }, + + "liveness_probe": azure.SchemaContainerGroupProbe(), + + "readiness_probe": azure.SchemaContainerGroupProbe(), }, }, }, @@ -661,6 +665,14 @@ func expandContainerGroupContainers(d *schema.ResourceData) (*[]containerinstanc } } + if v, ok := data["liveness_probe"]; ok { + container.ContainerProperties.LivenessProbe = expandContainerProbe(v) + } + + if v, ok := data["readiness_probe"]; ok { + container.ContainerProperties.ReadinessProbe = expandContainerProbe(v) + } + containers = append(containers, container) } @@ -762,6 +774,66 @@ func expandContainerVolumes(input interface{}) (*[]containerinstance.VolumeMount return &volumeMounts, &containerGroupVolumes } +func expandContainerProbe(input interface{}) *containerinstance.ContainerProbe { + probe := containerinstance.ContainerProbe{} + probeRaw := input.([]interface{}) + + if len(probeRaw) == 0 { + return nil + } + + for _, p := range probeRaw { + probeConfig := p.(map[string]interface{}) + + if v := probeConfig["initial_delay_seconds"].(int); v > 0 { + probe.InitialDelaySeconds = utils.Int32(int32(v)) + } + + if v := probeConfig["period_seconds"].(int); v > 0 { + probe.PeriodSeconds = utils.Int32(int32(v)) + } + + if v := probeConfig["failure_threshold"].(int); v > 0 { + probe.FailureThreshold = utils.Int32(int32(v)) + } + + if v := probeConfig["success_threshold"].(int); v > 0 { + probe.SuccessThreshold = utils.Int32(int32(v)) + } + + if v := probeConfig["timeout_seconds"].(int); v > 0 { + probe.TimeoutSeconds = utils.Int32(int32(v)) + } + + commands := probeConfig["exec"].([]interface{}) + if len(commands) > 0 { + exec := containerinstance.ContainerExec{ + Command: utils.ExpandStringArray(commands), + } + probe.Exec = &exec + } + + httpRaw := probeConfig["http_get"].([]interface{}) + if len(httpRaw) > 0 { + + for _, httpget := range httpRaw { + x := httpget.(map[string]interface{}) + + path := x["path"].(string) + port := x["port"].(int) + scheme := x["scheme"].(string) + + probe.HTTPGet = &containerinstance.ContainerHTTPGet{ + Path: utils.String(path), + Port: utils.Int32(int32(port)), + Scheme: containerinstance.Scheme(scheme), + } + } + } + } + return &probe +} + func flattenContainerImageRegistryCredentials(d *schema.ResourceData, input *[]containerinstance.ImageRegistryCredential) []interface{} { if input == nil { return nil @@ -907,6 +979,9 @@ func flattenContainerGroupContainers(d *schema.ResourceData, containers *[]conta containerConfig["volume"] = flattenContainerVolumes(container.VolumeMounts, containerGroupVolumes, containerVolumesConfig) } + containerConfig["liveness_probe"] = flattenContainerProbes(container.LivenessProbe) + containerConfig["readiness_probe"] = flattenContainerProbes(container.ReadinessProbe) + containerCfg = append(containerCfg, containerConfig) } @@ -1002,6 +1077,62 @@ func flattenContainerVolumes(volumeMounts *[]containerinstance.VolumeMount, cont return volumeConfigs } +func flattenContainerProbes(input *containerinstance.ContainerProbe) []interface{} { + outputs := make([]interface{}, 0) + if input == nil { + return outputs + } + + output := make(map[string]interface{}) + + if v := input.Exec; v != nil { + output["exec"] = *v.Command + } + + httpGets := make([]interface{}, 0) + if get := input.HTTPGet; get != nil { + httpGet := make(map[string]interface{}) + + if v := get.Path; v != nil { + httpGet["path"] = *v + } + + if v := get.Port; v != nil { + httpGet["port"] = *v + } + + if get.Scheme != "" { + httpGet["scheme"] = get.Scheme + } + + httpGets = append(httpGets, httpGet) + } + output["http_get"] = httpGets + + if v := input.FailureThreshold; v != nil { + output["failure_threshold"] = *v + } + + if v := input.InitialDelaySeconds; v != nil { + output["initial_delay_seconds"] = *v + } + + if v := input.PeriodSeconds; v != nil { + output["period_seconds"] = *v + } + + if v := input.SuccessThreshold; v != nil { + output["success_threshold"] = *v + } + + if v := input.TimeoutSeconds; v != nil { + output["timeout_seconds"] = *v + } + + outputs = append(outputs, output) + return outputs +} + func expandContainerGroupDiagnostics(input []interface{}) *containerinstance.ContainerGroupDiagnostics { if len(input) == 0 { return nil diff --git a/azurerm/resource_arm_container_group_test.go b/azurerm/resource_arm_container_group_test.go index 3e189e649e60..4eb117868703 100644 --- a/azurerm/resource_arm_container_group_test.go +++ b/azurerm/resource_arm_container_group_test.go @@ -225,6 +225,26 @@ func TestAccAzureRMContainerGroup_linuxComplete(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "diagnostics.0.log_analytics.0.metadata.%", "1"), resource.TestCheckResourceAttrSet(resourceName, "diagnostics.0.log_analytics.0.workspace_id"), resource.TestCheckResourceAttrSet(resourceName, "diagnostics.0.log_analytics.0.workspace_key"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.#", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.exec.#", "2"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.exec.0", "cat"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.exec.1", "/tmp/healthy"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.http_get.#", "0"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.initial_delay_seconds", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.period_seconds", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.failure_threshold", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.success_threshold", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.timeout_seconds", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.#", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.failure_threshold", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.http_get.#", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.http_get.0.path", "/"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.http_get.0.port", "443"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.http_get.0.scheme", "Http"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.initial_delay_seconds", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.period_seconds", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.success_threshold", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.timeout_seconds", "1"), ), }, { @@ -307,6 +327,26 @@ func TestAccAzureRMContainerGroup_windowsComplete(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "diagnostics.0.log_analytics.0.metadata.%", "1"), resource.TestCheckResourceAttrSet(resourceName, "diagnostics.0.log_analytics.0.workspace_id"), resource.TestCheckResourceAttrSet(resourceName, "diagnostics.0.log_analytics.0.workspace_key"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.#", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.exec.#", "2"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.exec.0", "cat"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.exec.1", "/tmp/healthy"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.http_get.#", "0"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.initial_delay_seconds", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.period_seconds", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.failure_threshold", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.success_threshold", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.timeout_seconds", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.#", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.failure_threshold", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.http_get.#", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.http_get.0.path", "/"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.http_get.0.port", "443"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.http_get.0.scheme", "Http"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.initial_delay_seconds", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.period_seconds", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.success_threshold", "1"), + resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.timeout_seconds", "1"), ), }, { @@ -595,7 +635,7 @@ resource "azurerm_container_group" "test" { ports { port = 80 protocol = "TCP" - } + } environment_variables = { "foo" = "bar" @@ -607,6 +647,28 @@ resource "azurerm_container_group" "test" { "secureFoo1" = "secureBar1" } + readiness_probe { + exec = ["cat","/tmp/healthy"] + initial_delay_seconds = 1 + period_seconds = 1 + failure_threshold = 1 + success_threshold = 1 + timeout_seconds = 1 + } + + liveness_probe { + http_get { + path = "/" + port = 443 + scheme = "Http" + } + initial_delay_seconds = 1 + period_seconds = 1 + failure_threshold = 1 + success_threshold = 1 + timeout_seconds = 1 + } + commands = ["cmd.exe", "echo", "hi"] } @@ -708,7 +770,7 @@ resource "azurerm_container_group" "test" { } environment_variables = { - "foo" = "bar" + "foo" = "bar" "foo1" = "bar1" } @@ -717,11 +779,33 @@ resource "azurerm_container_group" "test" { "secureFoo1" = "secureBar1" } + readiness_probe { + exec = ["cat","/tmp/healthy"] + initial_delay_seconds = 1 + period_seconds = 1 + failure_threshold = 1 + success_threshold = 1 + timeout_seconds = 1 + } + + liveness_probe { + http_get { + path = "/" + port = 443 + scheme = "Http" + } + initial_delay_seconds = 1 + period_seconds = 1 + failure_threshold = 1 + success_threshold = 1 + timeout_seconds = 1 + } + commands = ["/bin/bash", "-c", "ls"] } diagnostics { - log_analytics { + log_analytics { workspace_id = "${azurerm_log_analytics_workspace.test.workspace_id}" workspace_key = "${azurerm_log_analytics_workspace.test.primary_shared_key}" log_type = "ContainerInsights" diff --git a/website/docs/r/container_group.html.markdown b/website/docs/r/container_group.html.markdown index 47af628745d9..32b3a8157cc4 100644 --- a/website/docs/r/container_group.html.markdown +++ b/website/docs/r/container_group.html.markdown @@ -66,6 +66,14 @@ resource "azurerm_container_group" "aci-helloworld" { "ACCESS_KEY" = "secure_testing" } + readiness_probe { + exec = ["/bin/sh","-c","touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"] + } + + liveness_probe { + exec = ["cat", "/tmp/healthy"] + } + commands = ["/bin/bash", "-c", "'/path to/myscript.sh'"] volume { @@ -144,6 +152,10 @@ A `container` block supports: * `secure_environment_variables` - (Optional) A list of sensitive environment variables to be set on the container. Specified as a map of name/value pairs. Changing this forces a new resource to be created. +* `readiness_probe` - (Optional) The definition of a readiness probe for this container as documented in the `readiness_probe` block below. Changing this forces a new resource to be created. + +* `liveness_probe` - (Optional) The definition of a readiness probe for this container as documented in the `liveness_probe` block below. Changing this forces a new resource to be created. + * `command` - (Optional) A command line to be run on the container. ~> **NOTE:** The field `command` has been deprecated in favor of `commands` to better match the API. @@ -212,6 +224,52 @@ A `volume` block supports: * `share_name` - (Required) The Azure storage share that is to be mounted as a volume. This must be created on the storage account specified as above. Changing this forces a new resource to be created. +--- + +The `readiness_probe` block supports: + +* `exec` - (Optional) Commands to be run to validate container readiness. Changing this forces a new resource to be created. + +* `httpget` - (Optional) The definition of the httpget for this container as documented in the `httpget` block below. Changing this forces a new resource to be created. + +* `initial_delay_seconds` - (Optional) Number of seconds after the container has started before liveness or readiness probes are initiated. Changing this forces a new resource to be created. + +* `period_seconds` - (Optional) How often (in seconds) to perform the probe. The default value is `10` and the minimum value is `1`. Changing this forces a new resource to be created. + +* `failure_threshold` - (Optional) How many times to try the probe before restarting the container (liveness probe) or marking the container as unhealthy (readiness probe). The default value is `3` and the minimum value is `1`. Changing this forces a new resource to be created. + +* `success_threshold` - (Optional) Minimum consecutive successes for the probe to be considered successful after having failed. The default value is `1` and the minimum value is `1`. Changing this forces a new resource to be created. + +* `timeout_seconds` - (Optional) Number of seconds after which the probe times out. The default value is `1` and the minimum value is `1`. Changing this forces a new resource to be created. + +--- + +The `liveness_probe` block supports: + +* `exec` - (Optional) Commands to be run to validate container readiness. Changing this forces a new resource to be created. + +* `httpget` - (Optional) The definition of the httpget for this container as documented in the `httpget` block below. Changing this forces a new resource to be created. + +* `initial_delay_seconds` - (Optional) Number of seconds after the container has started before liveness or readiness probes are initiated. Changing this forces a new resource to be created. + +* `period_seconds` - (Optional) How often (in seconds) to perform the probe. The default value is `10` and the minimum value is `1`. Changing this forces a new resource to be created. + +* `failure_threshold` - (Optional) How many times to try the probe before restarting the container (liveness probe) or marking the container as unhealthy (readiness probe). The default value is `3` and the minimum value is `1`. Changing this forces a new resource to be created. + +* `success_threshold` - (Optional) Minimum consecutive successes for the probe to be considered successful after having failed. The default value is `1` and the minimum value is `1`. Changing this forces a new resource to be created. + +* `timeout_seconds` - (Optional) Number of seconds after which the probe times out. The default value is `1` and the minimum value is `1`. Changing this forces a new resource to be created. + +--- + +The `httpget` block supports: + +* `path` - (Optional) Path to access on the HTTP server. Changing this forces a new resource to be created. + +* `port` - (Optional) Number of the port to access on the container. Changing this forces a new resource to be created. + +* `scheme` - (Optional) Scheme to use for connecting to the host. Possible values are `Http` and `Https`. Changing this forces a new resource to be created. + ## Attributes Reference The following attributes are exported: