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

Implementation of Azure container groups #311

Closed
wants to merge 0 commits into from

Conversation

abhijeetgaiha
Copy link

This has a basic implementation for #287 .

The following features are TBD:

  • Command lines
  • Volume Mounts
  • Environment Variables
  • Private container registries

Copy link
Contributor

@tombuildsstuff tombuildsstuff left a comment

Choose a reason for hiding this comment

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

Hey @abhijeetgaiha

Thanks for this PR :)

I've taken a look through and left some comments in-line, but this is off to a good start :)

Thanks!

"ip_address_type": {
Type: schema.TypeString,
Required: true,
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm assuming there's a limited set of options here? if so - could we add some validation to this, e.g.

ValidateFunc: validation.StringInSlice([]string{
  "foo",
  "bar",
}, true)

Copy link
Contributor

Choose a reason for hiding this comment

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

(as below) we should be able to make this Optional and Default: "Public" given `Public" is the only option at this time, which would mean users don't need to specify it :)

Copy link
Author

Choose a reason for hiding this comment

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

Done

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this also needs to be ForceNew?

Copy link
Author

Choose a reason for hiding this comment

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

Done.


"resource_group_name": {
Type: schema.TypeString,
Required: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this also needs to be ForceNew?

Copy link
Author

Choose a reason for hiding this comment

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

Done.

"os_type": {
Type: schema.TypeString,
Required: true,
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm assuming there's a limited set of options here? if so - could we add some validation to this, e.g.

ValidateFunc: validation.StringInSlice([]string{
  "foo",
  "bar",
}, true)

Copy link
Author

Choose a reason for hiding this comment

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

Done.

}

_, error := containerGroupsClient.CreateOrUpdate(resGroup, name, containerGroup)
if error != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

minor can we swap error out for err to match the other resources?

Copy link
Author

Choose a reason for hiding this comment

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

Done.

}
```

## Example Usage (Windows)
Copy link
Contributor

Choose a reason for hiding this comment

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

given the only difference between these two is the OS field - I think we can just use the Linux example (and just rename that to Example Usage)?

Copy link
Author

Choose a reason for hiding this comment

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

I've updated the linux example to use two containers.

Copy link
Contributor

Choose a reason for hiding this comment

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

could we consolidate this into a single example, as they're fundamentally the same (compared to say Container Service in which some fields aren't valid for a non-Kubernetes cluster)?

Copy link
Author

Choose a reason for hiding this comment

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

you mean two container groups in one file?

Copy link
Contributor

Choose a reason for hiding this comment

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

I was referring to picking either the Windows or the Linux example and removing the other (since they're virtually the same)

Copy link
Author

Choose a reason for hiding this comment

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

Done.


* `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. Changing this forces a new resource to be created.

* `ip_address_type` - (Required) Specifies the ip address type of the container. `public` is the only acceptable value at this time. Changing this forces a new resource to be created.
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is the only acceptable value at this time, we can make this field Optional and Default it to Public for the moment - so users don't have to specify it (in the schema)

Copy link
Author

Choose a reason for hiding this comment

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

Done.


The following attributes are exported:

* `ip_address` - The IP address allocated to the container group.
Copy link
Contributor

Choose a reason for hiding this comment

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

there's also the ID of the container group as id

Copy link
Author

Choose a reason for hiding this comment

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

Done.

"container": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Resource{
Copy link
Contributor

Choose a reason for hiding this comment

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

given only a single container's currently supported - can we add MaxItems: 1 here?

Copy link
Author

Choose a reason for hiding this comment

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

Actually, more than 1 container is supported for Linux. Updated docs.


"name": {
Type: schema.TypeString,
Required: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

these are marked as ForceNew in the documentation below - if so, can we add ForceNew: true to all of these fields in the container group?

Copy link
Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

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

I might have missed it, but I can't see these changes?

Copy link
Author

Choose a reason for hiding this comment

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

I fixed the docs. This shouldn't force the whole group to be updated.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

ValidateFunc: validation.StringInSlice([]string{
"tcp",
"udp",
}, true),
Copy link
Contributor

Choose a reason for hiding this comment

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

given the validation function allows for differences in case (the true) can we add a DiffSuppressFunc to this to ignore the case?

Copy link
Author

Choose a reason for hiding this comment

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

Done.

IPAddressType := d.Get("ip_address_type").(string)
tags := d.Get("tags").(map[string]interface{})

containersConfig := d.Get("container").([]interface{})
Copy link
Contributor

Choose a reason for hiding this comment

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

can we pull all of this out into a separate function expandContainerGroupContainers which returns the deserialized object?

Copy link
Author

Choose a reason for hiding this comment

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

Done.

},
}

if port != 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

given this is an Optional field, we should be able to do this instead:

if v, ok := data["port"]; ok {
  ...
}

Copy link
Author

Choose a reason for hiding this comment

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

Done.

Port: &port,
}

if protocol != "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

(same as for port) for the Protocol field

Copy link
Author

Choose a reason for hiding this comment

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

Done.

// envVarsList = append(envVarsList, envVar)
// }
// container.EnvironmentVariables = &envVarsList
// }
Copy link
Contributor

Choose a reason for hiding this comment

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

can we remove this, since it's not being used?

Copy link
Author

Choose a reason for hiding this comment

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

Done.

_, err = containterGroupsClient.Delete(resGroup, name)

if err != nil {
return err
Copy link
Contributor

Choose a reason for hiding this comment

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

can we check for both a 404 and a dropped connection before throwing an error? we can do this via:

resp, err = containterGroupsClient.Delete(resGroup, name)
if err != nil && !utils.ResponseWasNotFound(resp) {
  return nil
}

return nil

Copy link
Author

Choose a reason for hiding this comment

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

Done.

resource "azurerm_container_group" "test" {

name = "acctestcontainergroup-%d"
location = "%s"
Copy link
Contributor

Choose a reason for hiding this comment

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

rather than threading this through in multiple places, can we update this to "${azurerm_resource_group.test.location}"?

Copy link
Author

Choose a reason for hiding this comment

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

Done.

resource "azurerm_container_group" "test" {

name = "acctestcontainergroup-%d"
location = "%s"
Copy link
Contributor

Choose a reason for hiding this comment

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

rather than threading this through in multiple places, can we update this to "${azurerm_resource_group.test.location}"?

Copy link
Author

Choose a reason for hiding this comment

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

Done.


resource "azurerm_container_group" "aci-test" {
name = "mynginx"
location = "west us"
Copy link
Contributor

Choose a reason for hiding this comment

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

minor since Location is available on the Resource Group resource, could we update this to "${azurerm_resource_group.aci-rg.location}" to keep it a little DRY-er?, rather than in each resource? (I'm slowly working through the documentation for each resource to show this)

Copy link
Author

Choose a reason for hiding this comment

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

Done.

resource "azurerm_container_group" "winapp" {

name = "mywinapp"
location = "west us"
Copy link
Contributor

Choose a reason for hiding this comment

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

minor can we update this to be "${azurerm_resource_group.aci-rg.location}"

Copy link
Author

Choose a reason for hiding this comment

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

Removed the windows example.

resource "azurerm_container_group" "aci-helloworld" {

name = "aci-hw"
location = "west us"
Copy link
Contributor

Choose a reason for hiding this comment

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

minor can we update this to be "${azurerm_resource_group.aci-rg.location}"

Copy link
Author

Choose a reason for hiding this comment

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

Done.


* `os_type` - (Required) The OS for the container group. Allowed values are `linux` and `windows` Changing this forces a new resource to be created.

* `container` - (Required) The definition of a container that is part of the group. Currently, only single containers are supported for Windows OS and multiple containers are supported for Linux OS.
Copy link
Contributor

Choose a reason for hiding this comment

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

We'd tend to phrase this as:

A `container` block as defined below.

~> **Note:** if `os_type` is set to `Windows` currently only a single container block is supported

Copy link
Author

Choose a reason for hiding this comment

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

Done.


The `container` block supports:

* `name` - (Required) Specifies the name of the Container.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we suffix this with "Changing this value forces a new resource to be created"?


* `name` - (Required) Specifies the name of the Container.

* `image` - (Required) The container image name.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we suffix this with "Changing this value forces a new resource to be created"?

Copy link
Author

Choose a reason for hiding this comment

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

Done.


* `image` - (Required) The container image name.

* `cpu` - (Required) The required number of CPU cores of the containers.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we suffix this with "Changing this value forces a new resource to be created"?

Copy link
Author

Choose a reason for hiding this comment

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

Done.


* `cpu` - (Required) The required number of CPU cores of the containers.

* `memory` - (Required) The required memory of the containers in GB.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we suffix this with "Changing this value forces a new resource to be created"?

Copy link
Author

Choose a reason for hiding this comment

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

Done.


* `memory` - (Required) The required memory of the containers in GB.

* `port` - (Optional) A public port for the container.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we suffix this with "Changing this value forces a new resource to be created"?

Copy link
Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

@tombuildsstuff tombuildsstuff left a comment

Choose a reason for hiding this comment

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

A few minor points - but this otherwise LGTM (I'm just running the tests now :)

"ip_address": {
Type: schema.TypeString,
Computed: true,
ForceNew: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

given this is Computed, it doesn't need to be ForceNew - so we can remove this

Copy link
Author

Choose a reason for hiding this comment

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

Done.

}

containerConfigs := flattenContainerGroupContainers(resp.Containers)
d.Set("container", containerConfigs)
Copy link
Contributor

Choose a reason for hiding this comment

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

minor given this is setting a complex object, could we update this too:

containerConfigs := flattenContainerGroupContainers(resp.Containers)
if err = d.Set("container", containerConfigs) {
  return fmt.Errorf("Error setting `container`: %+v", err)
}

Copy link
Author

Choose a reason for hiding this comment

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

Done.

containerConfig["name"] = *container.Name
containerConfig["image"] = *container.Image

resourceRequests := *(*container.Resources).Requests
Copy link
Contributor

Choose a reason for hiding this comment

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

could we add an ifcheck around this to avoid Swagger/API issues? i.e.

if resources := container.Resources; resources != nil {
  if resourceRequests := resources.Requests; resourceRequests != nil {
    # ...
  }
}

Copy link
Author

Choose a reason for hiding this comment

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

The API will always return these values. Changing just in case.


if v, ok := data["protocol"]; ok {
protocol := v.(string)
containerGroupPort.Protocol = containerinstance.ContainerGroupNetworkProtocol(strings.ToUpper(protocol))
Copy link
Contributor

Choose a reason for hiding this comment

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

does this actually need to be strings.ToUpper?

Copy link
Author

Choose a reason for hiding this comment

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

It's needed as the ContainerGroupNetworkProtocol enum is composed of only all uppercase strings.

@tombuildsstuff
Copy link
Contributor

Hey @abhijeetgaiha

Sorry, I've unintentionally closed this by force-pushing the changes we discussed (to add an update test).. :( I'll re-open this in a separate PT (which will keep your commits)

@ghost
Copy link

ghost commented Apr 1, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 [email protected]. Thanks!

@ghost ghost locked and limited conversation to collaborators Apr 1, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants