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

azurerm_container_registry - Support for zone_redundancy_enabled for both the ACR and each georeplications block #11706

Merged
merged 2 commits into from
Jun 17, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
87 changes: 66 additions & 21 deletions azurerm/internal/services/containers/container_registry_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ func resourceContainerRegistry() *pluginsdk.Resource {
},

"georeplications": {
Type: pluginsdk.TypeSet,
// Don't make this a TypeSet since TypeSet has bugs when there is a nested property using `StateFunc`.
// See: https://github.com/hashicorp/terraform-plugin-sdk/issues/160
Type: pluginsdk.TypeList,
Optional: true,
Computed: true, // TODO -- remove this when deprecation resolves
ConflictsWith: []string{"georeplication_locations"},
Expand All @@ -110,6 +112,12 @@ func resourceContainerRegistry() *pluginsdk.Resource {
DiffSuppressFunc: location.DiffSuppressFunc,
},

"zone_redundancy_enabled": {
Type: pluginsdk.TypeBool,
Optional: true,
Default: false,
},

"tags": tags.Schema(),
},
},
Expand Down Expand Up @@ -314,14 +322,21 @@ func resourceContainerRegistry() *pluginsdk.Resource {
},
},

"zone_redundancy_enabled": {
Type: pluginsdk.TypeBool,
ForceNew: true,
Optional: true,
Default: false,
},

"tags": tags.Schema(),
},

CustomizeDiff: pluginsdk.CustomizeDiffShim(func(ctx context.Context, d *pluginsdk.ResourceDiff, v interface{}) error {
sku := d.Get("sku").(string)
geoReplicationLocations := d.Get("georeplication_locations").(*pluginsdk.Set)
geoReplications := d.Get("georeplications").(*pluginsdk.Set)
hasGeoReplicationsApplied := geoReplicationLocations.Len() > 0 || geoReplications.Len() > 0
geoReplications := d.Get("georeplications").([]interface{})
hasGeoReplicationsApplied := geoReplicationLocations.Len() > 0 || len(geoReplications) > 0
// if locations have been specified for geo-replication then, the SKU has to be Premium
if hasGeoReplicationsApplied && !strings.EqualFold(sku, string(containerregistry.Premium)) {
return fmt.Errorf("ACR geo-replication can only be applied when using the Premium Sku.")
Expand All @@ -346,6 +361,20 @@ func resourceContainerRegistry() *pluginsdk.Resource {
if ok && encryptionEnabled.(bool) && !strings.EqualFold(sku, string(containerregistry.Premium)) {
return fmt.Errorf("ACR encryption can only be applied when using the Premium Sku.")
}

// zone redundancy is only available for Premium Sku.
zoneRedundancyEnabled, ok := d.GetOk("zone_redundancy_enabled")
if ok && zoneRedundancyEnabled.(bool) && !strings.EqualFold(sku, string(containerregistry.Premium)) {
return fmt.Errorf("ACR zone redundancy can only be applied when using the Premium Sku")
}
for _, loc := range geoReplications {
loc := loc.(map[string]interface{})
zoneRedundancyEnabled, ok := loc["zone_redundancy_enabled"]
if ok && zoneRedundancyEnabled.(bool) && !strings.EqualFold(sku, string(containerregistry.Premium)) {
return fmt.Errorf("ACR zone redundancy can only be applied when using the Premium Sku")
}
}

return nil
}),
}
Expand Down Expand Up @@ -391,7 +420,7 @@ func resourceContainerRegistryCreate(d *pluginsdk.ResourceData, meta interface{}
adminUserEnabled := d.Get("admin_enabled").(bool)
t := d.Get("tags").(map[string]interface{})
geoReplicationLocations := d.Get("georeplication_locations").(*pluginsdk.Set)
geoReplications := d.Get("georeplications").(*pluginsdk.Set)
geoReplications := d.Get("georeplications").([]interface{})

networkRuleSet := expandNetworkRuleSet(d.Get("network_rule_set").([]interface{}))
if networkRuleSet != nil && !strings.EqualFold(sku, string(containerregistry.Premium)) {
Expand All @@ -416,6 +445,12 @@ func resourceContainerRegistryCreate(d *pluginsdk.ResourceData, meta interface{}
if !d.Get("public_network_access_enabled").(bool) {
publicNetworkAccess = containerregistry.PublicNetworkAccessDisabled
}

zoneRedundancy := containerregistry.ZoneRedundancyDisabled
if d.Get("zone_redundancy_enabled").(bool) {
zoneRedundancy = containerregistry.ZoneRedundancyEnabled
}

parameters := containerregistry.Registry{
Location: &location,
Sku: &containerregistry.Sku{
Expand All @@ -425,14 +460,15 @@ func resourceContainerRegistryCreate(d *pluginsdk.ResourceData, meta interface{}
Identity: identity,
RegistryProperties: &containerregistry.RegistryProperties{
AdminUserEnabled: utils.Bool(adminUserEnabled),
Encryption: encryption,
NetworkRuleSet: networkRuleSet,
Policies: &containerregistry.Policies{
QuarantinePolicy: quarantinePolicy,
RetentionPolicy: retentionPolicy,
TrustPolicy: trustPolicy,
},
PublicNetworkAccess: publicNetworkAccess,
Encryption: encryption,
ZoneRedundancy: zoneRedundancy,
},

Tags: tags.Expand(t),
Expand Down Expand Up @@ -460,11 +496,11 @@ func resourceContainerRegistryCreate(d *pluginsdk.ResourceData, meta interface{}
}

// the ACR is being created so no previous geo-replication locations
var oldGeoReplicationLocations, newGeoReplicationLocations []*containerregistry.Replication
var oldGeoReplicationLocations, newGeoReplicationLocations []containerregistry.Replication
if geoReplicationLocations != nil && geoReplicationLocations.Len() > 0 {
newGeoReplicationLocations = expandReplicationsFromLocations(geoReplicationLocations.List())
} else {
newGeoReplicationLocations = expandReplications(geoReplications.List())
newGeoReplicationLocations = expandReplications(geoReplications)
}
// geo replications have been specified
if len(newGeoReplicationLocations) > 0 {
Expand Down Expand Up @@ -513,8 +549,8 @@ func resourceContainerRegistryUpdate(d *pluginsdk.ResourceData, meta interface{}

oldReplicationsRaw, newReplicationsRaw := d.GetChange("georeplications")
hasGeoReplicationsChanges := d.HasChange("georeplications")
oldReplications := oldReplicationsRaw.(*pluginsdk.Set)
newReplications := newReplicationsRaw.(*pluginsdk.Set)
oldReplications := oldReplicationsRaw.([]interface{})
newReplications := newReplicationsRaw.([]interface{})

// handle upgrade to Premium SKU first
if skuChange && isPremiumSku {
Expand Down Expand Up @@ -560,13 +596,13 @@ func resourceContainerRegistryUpdate(d *pluginsdk.ResourceData, meta interface{}
}

// geo replication is only supported by Premium Sku
hasGeoReplicationsApplied := newGeoReplicationLocations.Len() > 0 || newReplications.Len() > 0
hasGeoReplicationsApplied := newGeoReplicationLocations.Len() > 0 || len(newReplications) > 0
if hasGeoReplicationsApplied && !strings.EqualFold(sku, string(containerregistry.Premium)) {
return fmt.Errorf("ACR geo-replication can only be applied when using the Premium Sku.")
}

if hasGeoReplicationsChanges {
err := applyGeoReplicationLocations(d, meta, resourceGroup, name, expandReplications(oldReplications.List()), expandReplications(newReplications.List()))
err := applyGeoReplicationLocations(d, meta, resourceGroup, name, expandReplications(oldReplications), expandReplications(newReplications))
if err != nil {
return fmt.Errorf("Error applying geo replications for Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err)
}
Expand Down Expand Up @@ -631,15 +667,15 @@ func applyContainerRegistrySku(d *pluginsdk.ResourceData, meta interface{}, sku
return nil
}

func applyGeoReplicationLocations(d *pluginsdk.ResourceData, meta interface{}, resourceGroup string, name string, oldGeoReplications []*containerregistry.Replication, newGeoReplications []*containerregistry.Replication) error {
func applyGeoReplicationLocations(d *pluginsdk.ResourceData, meta interface{}, resourceGroup string, name string, oldGeoReplications []containerregistry.Replication, newGeoReplications []containerregistry.Replication) error {
replicationClient := meta.(*clients.Client).Containers.ReplicationsClient
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()
log.Printf("[INFO] preparing to apply geo-replications for Container Registry.")

// delete previously deployed locations
for _, replication := range oldGeoReplications {
if replication == nil || len(*replication.Location) == 0 {
if replication.Location == nil {
continue
}
oldLocation := azure.NormalizeLocation(*replication.Location)
Expand All @@ -655,11 +691,11 @@ func applyGeoReplicationLocations(d *pluginsdk.ResourceData, meta interface{}, r

// create new geo-replication locations
for _, replication := range newGeoReplications {
if replication == nil || len(*replication.Location) == 0 {
if replication.Location == nil {
continue
}
locationToCreate := azure.NormalizeLocation(*replication.Location)
future, err := replicationClient.Create(ctx, resourceGroup, name, locationToCreate, *replication)
future, err := replicationClient.Create(ctx, resourceGroup, name, locationToCreate, replication)
if err != nil {
return fmt.Errorf("Error creating Container Registry Replication %q (Resource Group %q, Location %q): %+v", name, resourceGroup, locationToCreate, err)
}
Expand Down Expand Up @@ -730,6 +766,7 @@ func resourceContainerRegistryRead(d *pluginsdk.ResourceData, meta interface{})
if err := d.Set("encryption", flattenEncryption(properties.Encryption)); err != nil {
return fmt.Errorf("Error setting `encryption`: %+v", err)
}
d.Set("zone_redundancy_enabled", properties.ZoneRedundancy == containerregistry.ZoneRedundancyEnabled)
}

if sku := resp.Sku; sku != nil {
Expand Down Expand Up @@ -771,6 +808,7 @@ func resourceContainerRegistryRead(d *pluginsdk.ResourceData, meta interface{})
replication := make(map[string]interface{})
replication["location"] = valueLocation
replication["tags"] = tags.Flatten(value.Tags)
replication["zone_redundancy_enabled"] = value.ZoneRedundancy == containerregistry.ZoneRedundancyEnabled
geoReplications = append(geoReplications, replication)
}
}
Expand Down Expand Up @@ -895,31 +933,38 @@ func expandTrustPolicy(p []interface{}) *containerregistry.TrustPolicy {
return &trustPolicy
}

func expandReplicationsFromLocations(p []interface{}) []*containerregistry.Replication {
replications := make([]*containerregistry.Replication, 0)
func expandReplicationsFromLocations(p []interface{}) []containerregistry.Replication {
replications := make([]containerregistry.Replication, 0)
for _, value := range p {
location := azure.NormalizeLocation(value)
replications = append(replications, &containerregistry.Replication{
replications = append(replications, containerregistry.Replication{
Location: &location,
Name: &location,
})
}
return replications
}

func expandReplications(p []interface{}) []*containerregistry.Replication {
replications := make([]*containerregistry.Replication, 0)
func expandReplications(p []interface{}) []containerregistry.Replication {
replications := make([]containerregistry.Replication, 0)
if p == nil {
return replications
}
for _, v := range p {
value := v.(map[string]interface{})
location := azure.NormalizeLocation(value["location"])
tags := tags.Expand(value["tags"].(map[string]interface{}))
replications = append(replications, &containerregistry.Replication{
zoneRedundancy := containerregistry.ZoneRedundancyDisabled
if value["zone_redundancy_enabled"].(bool) {
zoneRedundancy = containerregistry.ZoneRedundancyEnabled
}
replications = append(replications, containerregistry.Replication{
Location: &location,
Name: &location,
Tags: tags,
ReplicationProperties: &containerregistry.ReplicationProperties{
ZoneRedundancy: zoneRedundancy,
},
})
}
return replications
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,36 @@ func TestAccContainerRegistry_identity(t *testing.T) {
})
}

func TestAccContainerRegistry_zoneRedundancy(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_container_registry", "test")
r := ContainerRegistryResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.zoneRedundancy(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccContainerRegistry_geoReplicationZoneRedundancy(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_container_registry", "test")
r := ContainerRegistryResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.geoReplicationZoneRedundancy(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func (t ContainerRegistryResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := azure.ParseAzureResourceID(state.ID)
if err != nil {
Expand Down Expand Up @@ -1035,3 +1065,44 @@ resource "azurerm_user_assigned_identity" "test" {
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger)
}

func (ContainerRegistryResource) zoneRedundancy(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-acr-%d"
location = "%s"
}
resource "azurerm_container_registry" "test" {
name = "testacccr%d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
sku = "Premium"
zone_redundancy_enabled = true
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger)
}

func (ContainerRegistryResource) geoReplicationZoneRedundancy(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-acr-%d"
location = "%s"
}
resource "azurerm_container_registry" "test" {
name = "testacccr%d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
sku = "Premium"
georeplications {
location = "%s"
zone_redundancy_enabled = true
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.Locations.Secondary)
}
Loading