Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Probe support for Azure Container Instances #3118

Merged
merged 15 commits into from
Apr 8, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions azurerm/helpers/azure/container_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package azure

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/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,
},
},

"httpget": {
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
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{
string(containerinstance.HTTP),
string(containerinstance.HTTPS),
}, true),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we change this ti false? we are moving towards case sensitivity for properties like this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@katbyte I am struggling a bit with this one. The Azure API always returns Http or Https. When setting ignoreCase to false tests on this value begin to fail.

My assumption is that these tests should work.

    liveness_probe {
      httpget {
        path   = "/"
        port   = 443
        scheme = "Http"
      }
      inital_delay_seconds = 1
      period_seconds       = 1
      failure_threashold   = 1
      sucess_threashold    = 1
      timeout_seconds      = 1
    }

and then:

resource.TestCheckResourceAttr(resourceName,"container.0.liveness_probe.0.httpget.0.scheme", "Http"),

However, it seems I am hitting this, which returns:

testing.go:538: Step 0 error: config is invalid: azurerm_container_group.test: expected container.0.liveness_probe.0.httpget.0.scheme to be one of [http https], got Http

I am still investigating but if you have any advice, I would appreciate it :).

Thanks

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

turns out this is a bug in the Swagger/SDK - where the constants are defined in lower-case, when the API returns them in Title Case; we can work around this for the moment by making this:

ValidateFunc: validation.StringInSlice([]string{
  // TODO: switch to using Constants once the casings fixed
  "Http",
  "Https",
}, false),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, Tom. I figured something was going on here and had considered this approach. Appreciate the assist and validation.

},
},
},
},

"inital_delay_seconds": {
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},

"period_seconds": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},

"failure_threashold": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo here:

Suggested change
"failure_threashold": {
"failure_threshold": {

Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},

"sucess_threashold": {
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},

"timeout_seconds": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
},
},
},
}
}
139 changes: 138 additions & 1 deletion azurerm/resource_arm_container_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -307,6 +307,10 @@ func resourceArmContainerGroup() *schema.Resource {
},
},
},

"liveness_probe": azure.SchemaContainerGroupProbe(),

"readiness_probe": azure.SchemaContainerGroupProbe(),
},
},
},
Expand Down Expand Up @@ -661,6 +665,15 @@ 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 {
readinessProbe := expandContainerProbe(v)
container.ContainerProperties.ReadinessProbe = readinessProbe
neilpeterson marked this conversation as resolved.
Show resolved Hide resolved
}

containers = append(containers, container)
}

Expand Down Expand Up @@ -762,6 +775,67 @@ 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["inital_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_threashold"].(int); v > 0 {
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
probe.FailureThreshold = utils.Int32(int32(v))
}

if v := probeConfig["sucess_threashold"].(int); v > 0 {
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
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["httpget"].([]interface{})
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
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)

ContainerHTTPGet := containerinstance.ContainerHTTPGet{
Path: utils.String(path),
Port: utils.Int32(int32(port)),
Scheme: containerinstance.Scheme(scheme),
}
probe.HTTPGet = &ContainerHTTPGet
}
}
}
return &probe
}

func flattenContainerImageRegistryCredentials(d *schema.ResourceData, input *[]containerinstance.ImageRegistryCredential) []interface{} {
if input == nil {
return nil
Expand Down Expand Up @@ -907,6 +981,14 @@ func flattenContainerGroupContainers(d *schema.ResourceData, containers *[]conta
containerConfig["volume"] = flattenContainerVolumes(container.VolumeMounts, containerGroupVolumes, containerVolumesConfig)
}

if container.LivenessProbe != nil {
containerConfig["liveness_probe"] = flattenContainerProbes(container.LivenessProbe)
}

if container.ReadinessProbe != nil {
containerConfig["readiness_probe"] = flattenContainerProbes(container.ReadinessProbe)
}
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved

containerCfg = append(containerCfg, containerConfig)
}

Expand Down Expand Up @@ -1002,6 +1084,61 @@ 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
}

if v := input.HTTPGet; v != nil {
neilpeterson marked this conversation as resolved.
Show resolved Hide resolved
httpget := make(map[string]interface{})

if v := input.HTTPGet.Path; v != nil {
neilpeterson marked this conversation as resolved.
Show resolved Hide resolved
httpget["path"] = *v
}

if v := input.HTTPGet.Port; v != nil {
neilpeterson marked this conversation as resolved.
Show resolved Hide resolved
httpget["port"] = *v
}

if input.HTTPGet.Scheme != "" {
neilpeterson marked this conversation as resolved.
Show resolved Hide resolved
httpget["scheme"] = input.HTTPGet.Scheme
neilpeterson marked this conversation as resolved.
Show resolved Hide resolved
}

output["httpget"] = []interface{}{httpget}
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved

}

if v := input.FailureThreshold; v != nil {
output["failure_threashold"] = *v
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
}

if v := input.InitialDelaySeconds; v != nil {
output["inital_delay_seconds"] = *v
}

if v := input.PeriodSeconds; v != nil {
output["period_seconds"] = *v
}

if v := input.SuccessThreshold; v != nil {
output["sucess_threashold"] = *v
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
}

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
Expand Down
84 changes: 84 additions & 0 deletions azurerm/resource_arm_container_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.httpget.#", "0"),
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.inital_delay_seconds", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.period_seconds", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.failure_threashold", "1"),
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.sucess_threashold", "1"),
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
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_threashold", "1"),
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.httpget.#", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.httpget.0.path", "/"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.httpget.0.port", "443"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.httpget.0.scheme", "Http"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.inital_delay_seconds", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.period_seconds", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.sucess_threashold", "1"),
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.timeout_seconds", "1"),
),
},
{
Expand Down Expand Up @@ -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.httpget.#", "0"),
resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.inital_delay_seconds", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.period_seconds", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.failure_threashold", "1"),
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
resource.TestCheckResourceAttr(resourceName, "container.0.readiness_probe.0.sucess_threashold", "1"),
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
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_threashold", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.httpget.#", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.httpget.0.path", "/"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.httpget.0.port", "443"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.httpget.0.scheme", "Http"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.inital_delay_seconds", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.period_seconds", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.sucess_threashold", "1"),
resource.TestCheckResourceAttr(resourceName, "container.0.liveness_probe.0.timeout_seconds", "1"),
),
},
{
Expand Down Expand Up @@ -607,6 +647,28 @@ resource "azurerm_container_group" "test" {
"secureFoo1" = "secureBar1"
}

readiness_probe {
exec = ["cat","/tmp/healthy"]
inital_delay_seconds = 1
period_seconds = 1
failure_threashold = 1
sucess_threashold = 1
timeout_seconds = 1
}

liveness_probe {
httpget {
path = "/"
port = 443
scheme = "Http"
}
inital_delay_seconds = 1
period_seconds = 1
failure_threashold = 1
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
sucess_threashold = 1
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
timeout_seconds = 1
}

commands = ["cmd.exe", "echo", "hi"]
}

Expand Down Expand Up @@ -715,6 +777,28 @@ resource "azurerm_container_group" "test" {
secure_environment_variables = {
"secureFoo" = "secureBar"
"secureFoo1" = "secureBar1"
}

readiness_probe {
exec = ["cat","/tmp/healthy"]
inital_delay_seconds = 1
period_seconds = 1
failure_threashold = 1
sucess_threashold = 1
timeout_seconds = 1
}

liveness_probe {
httpget {
path = "/"
port = 443
scheme = "Http"
}
inital_delay_seconds = 1
period_seconds = 1
failure_threashold = 1
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
sucess_threashold = 1
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
timeout_seconds = 1
}

commands = ["/bin/bash", "-c", "ls"]
Expand Down
Loading