From a9a98723a4692991604840fe91caf8bfef58c596 Mon Sep 17 00:00:00 2001 From: Nicholas Fish Date: Wed, 21 Aug 2019 02:07:39 +0200 Subject: [PATCH] Add Batch Pool Container Registry configuration (#4072) --- azurerm/data_source_batch_pool.go | 23 +++++- azurerm/data_source_batch_pool_test.go | 10 +++ azurerm/helpers/azure/batch_pool.go | 96 ++++++++++++++++++++++++- azurerm/resource_arm_batch_pool.go | 43 +++++++++-- azurerm/resource_arm_batch_pool_test.go | 11 +++ website/docs/d/batch_pool.html.markdown | 12 ++++ website/docs/r/batch_pool.html.markdown | 19 +++++ 7 files changed, 204 insertions(+), 10 deletions(-) diff --git a/azurerm/data_source_batch_pool.go b/azurerm/data_source_batch_pool.go index b1a811ed36ff..4220998db675 100644 --- a/azurerm/data_source_batch_pool.go +++ b/azurerm/data_source_batch_pool.go @@ -119,6 +119,27 @@ func dataSourceArmBatchPool() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "container_registries": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "registry_server": { + Type: schema.TypeString, + Computed: true, + }, + "user_name": { + Type: schema.TypeString, + Computed: true, + }, + "password": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + }, + }, + }, }, }, }, @@ -288,7 +309,7 @@ func dataSourceArmBatchPoolRead(d *schema.ResourceData, meta interface{}) error if dcfg := props.DeploymentConfiguration; dcfg != nil { if vmcfg := dcfg.VirtualMachineConfiguration; vmcfg != nil { - if err := d.Set("container_configuration", azure.FlattenBatchPoolContainerConfiguration(vmcfg.ContainerConfiguration)); err != nil { + if err := d.Set("container_configuration", azure.FlattenBatchPoolContainerConfiguration(d, vmcfg.ContainerConfiguration)); err != nil { return fmt.Errorf("error setting `container_configuration`: %v", err) } diff --git a/azurerm/data_source_batch_pool_test.go b/azurerm/data_source_batch_pool_test.go index 5745a7f21457..d348183b63a0 100644 --- a/azurerm/data_source_batch_pool_test.go +++ b/azurerm/data_source_batch_pool_test.go @@ -54,6 +54,9 @@ func TestAccDataSourceAzureRMBatchPool_complete(t *testing.T) { resource.TestCheckResourceAttr(dataSourceName, "certificate.0.visibility.3294600504", "StartTask"), resource.TestCheckResourceAttr(dataSourceName, "certificate.0.visibility.4077195354", "RemoteUser"), resource.TestCheckResourceAttr(dataSourceName, "container_configuration.0.type", "DockerCompatible"), + resource.TestCheckResourceAttr(dataSourceName, "container_configuration.0.container_registries.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "container_configuration.0.container_registries.0.registry_server", "myContainerRegistry.azurecr.io"), + resource.TestCheckResourceAttr(dataSourceName, "container_configuration.0.container_registries.0.user_name", "myUserName"), ), }, }, @@ -126,6 +129,13 @@ resource "azurerm_batch_pool" "test" { container_configuration { type = "DockerCompatible" + container_registries= [ + { + registry_server = "myContainerRegistry.azurecr.io" + user_name = "myUserName" + password = "myPassword" + }, + ] } start_task { diff --git a/azurerm/helpers/azure/batch_pool.go b/azurerm/helpers/azure/batch_pool.go index 8233618a8bf8..74af6ade8cd8 100644 --- a/azurerm/helpers/azure/batch_pool.go +++ b/azurerm/helpers/azure/batch_pool.go @@ -7,6 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) // FlattenBatchPoolAutoScaleSettings flattens the auto scale settings for a Batch pool @@ -192,8 +193,7 @@ func FlattenBatchPoolCertificateReferences(armCertificates *[]batch.CertificateR } // FlattenBatchPoolContainerConfiguration flattens a Batch pool container configuration -func FlattenBatchPoolContainerConfiguration(armContainerConfiguration *batch.ContainerConfiguration) interface{} { - +func FlattenBatchPoolContainerConfiguration(d *schema.ResourceData, armContainerConfiguration *batch.ContainerConfiguration) interface{} { result := make(map[string]interface{}) if armContainerConfiguration == nil { @@ -203,10 +203,68 @@ func FlattenBatchPoolContainerConfiguration(armContainerConfiguration *batch.Con if armContainerConfiguration.Type != nil { result["type"] = *armContainerConfiguration.Type } + result["container_registries"] = flattenBatchPoolContainerRegistries(d, armContainerConfiguration.ContainerRegistries) return []interface{}{result} } +func flattenBatchPoolContainerRegistries(d *schema.ResourceData, armContainerRegistries *[]batch.ContainerRegistry) []interface{} { + results := make([]interface{}, 0) + + if armContainerRegistries == nil { + return results + } + for _, armContainerRegistry := range *armContainerRegistries { + result := flattenBatchPoolContainerRegistry(d, &armContainerRegistry) + results = append(results, result) + } + return results +} + +func flattenBatchPoolContainerRegistry(d *schema.ResourceData, armContainerRegistry *batch.ContainerRegistry) map[string]interface{} { + result := make(map[string]interface{}) + + if armContainerRegistry == nil { + return result + } + if registryServer := armContainerRegistry.RegistryServer; registryServer != nil { + result["registry_server"] = *registryServer + } + if userName := armContainerRegistry.UserName; userName != nil { + result["user_name"] = *userName + } + + // If we didn't specify a registry server and user name, just return what we have now rather than trying to locate the password + if len(result) != 2 { + return result + } + + result["password"] = findBatchPoolContainerRegistryPassword(d, result["registry_server"].(string), result["user_name"].(string)) + + return result +} + +func findBatchPoolContainerRegistryPassword(d *schema.ResourceData, armServer string, armUsername string) interface{} { + numContainerRegistries := 0 + if n, ok := d.GetOk("container_configuration.0.container_registries.#"); ok { + numContainerRegistries = n.(int) + } else { + return "" + } + + for i := 0; i < numContainerRegistries; i++ { + if server, ok := d.GetOk(fmt.Sprintf("container_configuration.0.container_registries.%d.registry_server", i)); !ok || server != armServer { + continue + } + if username, ok := d.GetOk(fmt.Sprintf("container_configuration.0.container_registries.%d.user_name", i)); !ok || username != armUsername { + continue + } + return d.Get(fmt.Sprintf("container_configuration.0.container_registries.%d.password", i)) + } + + return "" +} + // ExpandBatchPoolImageReference expands Batch pool image reference func ExpandBatchPoolImageReference(list []interface{}) (*batch.ImageReference, error) { if len(list) == 0 { @@ -252,14 +310,46 @@ func ExpandBatchPoolContainerConfiguration(list []interface{}) (*batch.Container containerConfiguration := list[0].(map[string]interface{}) containerType := containerConfiguration["type"].(string) + containerRegistries, err := expandBatchPoolContainerRegistries(containerConfiguration["container_registries"].([]interface{})) + if err != nil { + return nil, err + } containerConf := &batch.ContainerConfiguration{ - Type: &containerType, + Type: &containerType, + ContainerRegistries: containerRegistries, } return containerConf, nil } +func expandBatchPoolContainerRegistries(list []interface{}) (*[]batch.ContainerRegistry, error) { + result := []batch.ContainerRegistry{} + + for _, tempItem := range list { + item := tempItem.(map[string]interface{}) + containerRegistry, err := expandBatchPoolContainerRegistry(item) + if err != nil { + return nil, err + } + result = append(result, *containerRegistry) + } + return &result, nil +} + +func expandBatchPoolContainerRegistry(ref map[string]interface{}) (*batch.ContainerRegistry, error) { + if len(ref) == 0 { + return nil, fmt.Errorf("Error: container registry reference should be defined") + } + + containerRegistry := batch.ContainerRegistry{ + RegistryServer: utils.String(ref["registry_server"].(string)), + UserName: utils.String(ref["user_name"].(string)), + Password: utils.String(ref["password"].(string)), + } + return &containerRegistry, nil +} + // ExpandBatchPoolCertificateReferences expands Batch pool certificate references func ExpandBatchPoolCertificateReferences(list []interface{}) (*[]batch.CertificateReference, error) { var result []batch.CertificateReference diff --git a/azurerm/resource_arm_batch_pool.go b/azurerm/resource_arm_batch_pool.go index 236706fab426..5174e0d74016 100644 --- a/azurerm/resource_arm_batch_pool.go +++ b/azurerm/resource_arm_batch_pool.go @@ -8,16 +8,17 @@ import ( "strings" "time" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" - - "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" "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/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" + "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" + + "github.com/Azure/azure-sdk-for-go/services/batch/mgmt/2018-12-01/batch" ) func resourceArmBatchPool() *schema.Resource { @@ -116,6 +117,7 @@ func resourceArmBatchPool() *schema.Resource { "container_configuration": { Type: schema.TypeList, Optional: true, + MinItems: 1, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -124,6 +126,35 @@ func resourceArmBatchPool() *schema.Resource { Optional: true, ValidateFunc: validate.NoEmptyStrings, }, + "container_registries": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + ConfigMode: schema.SchemaConfigModeAttr, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "registry_server": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "user_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "password": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, }, }, }, @@ -602,7 +633,7 @@ func resourceArmBatchPoolRead(d *schema.ResourceData, meta interface{}) error { if dcfg := props.DeploymentConfiguration; dcfg != nil { if vmcfg := dcfg.VirtualMachineConfiguration; vmcfg != nil { - d.Set("container_configuration", azure.FlattenBatchPoolContainerConfiguration(vmcfg.ContainerConfiguration)) + d.Set("container_configuration", azure.FlattenBatchPoolContainerConfiguration(d, vmcfg.ContainerConfiguration)) } } diff --git a/azurerm/resource_arm_batch_pool_test.go b/azurerm/resource_arm_batch_pool_test.go index 705c7fe5aedc..1b8ed3ab86b5 100644 --- a/azurerm/resource_arm_batch_pool_test.go +++ b/azurerm/resource_arm_batch_pool_test.go @@ -311,6 +311,10 @@ func TestAccAzureRMBatchPool_container(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckAzureRMBatchPoolExists(resourceName), resource.TestCheckResourceAttr(resourceName, "container_configuration.0.type", "DockerCompatible"), + resource.TestCheckResourceAttr(resourceName, "container_configuration.0.container_registries.#", "1"), + resource.TestCheckResourceAttr(resourceName, "container_configuration.0.container_registries.0.registry_server", "myContainerRegistry.azurecr.io"), + resource.TestCheckResourceAttr(resourceName, "container_configuration.0.container_registries.0.user_name", "myUserName"), + resource.TestCheckResourceAttr(resourceName, "container_configuration.0.container_registries.0.password", "myPassword"), ), }, }, @@ -1012,6 +1016,13 @@ resource "azurerm_batch_pool" "test" { container_configuration { type = "DockerCompatible" + container_registries= [ + { + registry_server = "myContainerRegistry.azurecr.io" + user_name = "myUserName" + password = "myPassword" + }, + ] } } `, rInt, location, rString, rString, rString) diff --git a/website/docs/d/batch_pool.html.markdown b/website/docs/d/batch_pool.html.markdown index af89faaa7be2..21eb6fd31cda 100644 --- a/website/docs/d/batch_pool.html.markdown +++ b/website/docs/d/batch_pool.html.markdown @@ -136,3 +136,15 @@ A `resource_file` block exports the following: A `container_configuration` block exports the following: * `type` - The type of container configuration. + +* `container_registries` - Additional container registries from which container images can be pulled by the pool's VMs. + +--- + +A `container_registries` block exports the following: + +* `registry_server` - The container registry URL. The default is "docker.io". + +* `user_name` - The user name to log into the registry server. + +* `password` - The password to log into the registry server. diff --git a/website/docs/r/batch_pool.html.markdown b/website/docs/r/batch_pool.html.markdown index 591a0fc0ecac..0c1cc07ecbd9 100644 --- a/website/docs/r/batch_pool.html.markdown +++ b/website/docs/r/batch_pool.html.markdown @@ -77,6 +77,13 @@ EOF container_configuration { type = "DockerCompatible" + container_registries = [ + { + registry_server = "docker.io" + user_name = "login" + password = "apassword" + }, + ] } start_task { @@ -229,6 +236,8 @@ A `container_configuration` block supports the following: * `type` - (Optional) The type of container configuration. Possible value is `DockerCompatible`. +* `container_registries` - (Optional) Additional container registries from which container images can be pulled by the pool's VMs. + --- A `resource_file` block supports the following: @@ -247,6 +256,16 @@ A `resource_file` block supports the following: ~> **Please Note:** Exactly one of `auto_storage_container_name`, `storage_container_url` and `auto_user` must be specified. +--- + +A `container_registries` block supports the following: + +* `registry_server` - (Optional) The container registry URL. The default is "docker.io". Changing this forces a new resource to be created. + +* `user_name` - (Optional) The user name to log into the registry server. Changing this forces a new resource to be created. + +* `password` - (Optional) The password to log into the registry server. Changing this forces a new resource to be created. + ## Attributes Reference The following attributes are exported: