Skip to content

Commit

Permalink
add container registry georeplications property
Browse files Browse the repository at this point in the history
  • Loading branch information
ms-henglu committed Apr 2, 2021
1 parent 5d7be37 commit e8823b5
Show file tree
Hide file tree
Showing 3 changed files with 269 additions and 72 deletions.
176 changes: 119 additions & 57 deletions azurerm/internal/services/containers/container_registry_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,41 @@ func resourceContainerRegistry() *schema.Resource {
Optional: true,
Default: false,
},

// TODO 3.0 - Remove below property
"georeplication_locations": {
Type: schema.TypeSet,
MinItems: 1,
Optional: true,
Type: schema.TypeSet,
Optional: true,
Deprecated: "Deprecated in favour of `georeplications`",
Computed: true,
ConflictsWith: []string{"georeplications"},
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringIsNotEmpty,
},
Set: location.HashCode,
},

"georeplications": {
Type: schema.TypeList,
Optional: true,
Computed: true, // TODO -- remove this when deprecation resolves
ConflictsWith: []string{"georeplication_locations"},
ConfigMode: schema.SchemaConfigModeAttr,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"location": {
Type: schema.TypeString,
Required: true,
ValidateFunc: location.EnhancedValidate,
StateFunc: location.StateFunc,
DiffSuppressFunc: location.DiffSuppressFunc,
},

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

"public_network_access_enabled": {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -222,8 +245,10 @@ func resourceContainerRegistry() *schema.Resource {
CustomizeDiff: func(d *schema.ResourceDiff, v interface{}) error {
sku := d.Get("sku").(string)
geoReplicationLocations := d.Get("georeplication_locations").(*schema.Set)
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 geoReplicationLocations != nil && geoReplicationLocations.Len() > 0 && !strings.EqualFold(sku, string(containerregistry.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 Down Expand Up @@ -282,6 +307,7 @@ func resourceContainerRegistryCreate(d *schema.ResourceData, meta interface{}) e
adminUserEnabled := d.Get("admin_enabled").(bool)
t := d.Get("tags").(map[string]interface{})
geoReplicationLocations := d.Get("georeplication_locations").(*schema.Set)
geoReplications := d.Get("georeplications").([]interface{})

networkRuleSet := expandNetworkRuleSet(d.Get("network_rule_set").([]interface{}))
if networkRuleSet != nil && !strings.EqualFold(sku, string(containerregistry.Premium)) {
Expand Down Expand Up @@ -341,8 +367,17 @@ func resourceContainerRegistryCreate(d *schema.ResourceData, meta interface{}) e
// locations have been specified for geo-replication
if geoReplicationLocations != nil && geoReplicationLocations.Len() > 0 {
// the ACR is being created so no previous geo-replication locations
oldGeoReplicationLocations := []interface{}{}
err = applyGeoReplicationLocations(d, meta, resourceGroup, name, oldGeoReplicationLocations, geoReplicationLocations.List())
var oldGeoReplicationLocations []*containerregistry.Replication
err = applyGeoReplicationLocations(d, meta, resourceGroup, name, oldGeoReplicationLocations, expandReplicationsFromLocations(geoReplicationLocations.List()))
if err != nil {
return fmt.Errorf("Error applying geo replications for Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err)
}
}
// geo replications have been specified
if len(geoReplications) > 0 {
// the ACR is being created so no previous geo-replication locations
var oldGeoReplicationLocations []*containerregistry.Replication
err = applyGeoReplicationLocations(d, meta, resourceGroup, name, oldGeoReplicationLocations, expandReplications(geoReplications))
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 @@ -381,10 +416,15 @@ func resourceContainerRegistryUpdate(d *schema.ResourceData, meta interface{}) e
t := d.Get("tags").(map[string]interface{})

old, new := d.GetChange("georeplication_locations")
hasGeoReplicationChanges := d.HasChange("georeplication_locations")
hasGeoReplicationLocationsChanges := d.HasChange("georeplication_locations")
oldGeoReplicationLocations := old.(*schema.Set)
newGeoReplicationLocations := new.(*schema.Set)

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

// handle upgrade to Premium SKU first
if skuChange && isPremiumSku {
if err := applyContainerRegistrySku(d, meta, sku, resourceGroup, name); err != nil {
Expand Down Expand Up @@ -419,13 +459,18 @@ func resourceContainerRegistryUpdate(d *schema.ResourceData, meta interface{}) e
}

// geo replication is only supported by Premium Sku
if hasGeoReplicationChanges && newGeoReplicationLocations.Len() > 0 && !strings.EqualFold(sku, string(containerregistry.Premium)) {
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 the registry had replications and is updated to another Sku than premium - remove old locations
if !strings.EqualFold(sku, string(containerregistry.Premium)) && oldGeoReplicationLocations != nil && oldGeoReplicationLocations.Len() > 0 {
err := applyGeoReplicationLocations(d, meta, resourceGroup, name, oldGeoReplicationLocations.List(), newGeoReplicationLocations.List())
if hasGeoReplicationsChanges {
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)
}
} else if hasGeoReplicationLocationsChanges {
err := applyGeoReplicationLocations(d, meta, resourceGroup, name, expandReplicationsFromLocations(oldGeoReplicationLocations.List()), expandReplicationsFromLocations(newGeoReplicationLocations.List()))
if err != nil {
return fmt.Errorf("Error applying geo replications for Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err)
}
Expand All @@ -440,13 +485,6 @@ func resourceContainerRegistryUpdate(d *schema.ResourceData, meta interface{}) e
return fmt.Errorf("Error waiting for update of Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if strings.EqualFold(sku, string(containerregistry.Premium)) && hasGeoReplicationChanges {
err = applyGeoReplicationLocations(d, meta, resourceGroup, name, oldGeoReplicationLocations.List(), newGeoReplicationLocations.List())
if err != nil {
return fmt.Errorf("Error applying geo replications for Container Registry %q (Resource Group %q): %+v", name, resourceGroup, err)
}
}

// downgrade to Basic or Standard SKU
if skuChange && (isBasicSku || isStandardSku) {
if err := applyContainerRegistrySku(d, meta, sku, resourceGroup, name); err != nil {
Expand Down Expand Up @@ -492,45 +530,37 @@ func applyContainerRegistrySku(d *schema.ResourceData, meta interface{}, sku str
return nil
}

func applyGeoReplicationLocations(d *schema.ResourceData, meta interface{}, resourceGroup string, name string, oldGeoReplicationLocations []interface{}, newGeoReplicationLocations []interface{}) error {
func applyGeoReplicationLocations(d *schema.ResourceData, meta interface{}, resourceGroup string, name string, oldGeoReplicationLocations []*containerregistry.Replication, newGeoReplicationLocations []*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.")

createLocations := make(map[string]bool)
createReplications := make(map[string]*containerregistry.Replication)

// loop on the new location values
for _, nl := range newGeoReplicationLocations {
newLocation := azure.NormalizeLocation(nl)
createLocations[newLocation] = true // the location needs to be created
if nl == nil {
continue
}
newLocation := azure.NormalizeLocation(*nl.Location)
createReplications[newLocation] = nl // the replication needs to be created
}

// loop on the old location values
for _, ol := range oldGeoReplicationLocations {
if ol == nil {
continue
}
// oldLocation was created from a previous deployment
oldLocation := azure.NormalizeLocation(ol)
oldLocation := azure.NormalizeLocation(*ol.Location)

// if the list of locations to create already contains the location
if _, ok := createLocations[oldLocation]; ok {
createLocations[oldLocation] = false // the location do not need to be created, it already exists
}
delete(createReplications, oldLocation) // the location do not need to be created, it already exists
}

// create new geo-replication locations
for locationToCreate := range createLocations {
// if false, the location does not need to be created, continue
if !createLocations[locationToCreate] {
continue
}

// create the new replication location
replication := containerregistry.Replication{
Location: &locationToCreate,
Name: &locationToCreate,
}

future, err := replicationClient.Create(ctx, resourceGroup, name, locationToCreate, replication)
for locationToCreate, replication := range createReplications {
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 All @@ -542,9 +572,12 @@ func applyGeoReplicationLocations(d *schema.ResourceData, meta interface{}, reso

// loop on the list of previously deployed locations
for _, ol := range oldGeoReplicationLocations {
oldLocation := azure.NormalizeLocation(ol)
if ol == nil {
continue
}
oldLocation := azure.NormalizeLocation(*ol.Location)
// if the old location is still in the list of locations, then continue
if _, ok := createLocations[oldLocation]; ok {
if _, ok := createReplications[oldLocation]; ok {
continue
}

Expand Down Expand Up @@ -640,24 +673,23 @@ func resourceContainerRegistryRead(d *schema.ResourceData, meta interface{}) err
return fmt.Errorf("Error making Read request on Azure Container Registry %s for replications: %s", name, err)
}

replicationValues := replications.Values()

// if there is more than one location (the main one and the replicas)
if replicationValues != nil || len(replicationValues) > 1 {
georeplication_locations := &schema.Set{F: schema.HashString}

for _, value := range replicationValues {
if value.Location != nil {
valueLocation := azure.NormalizeLocation(*value.Location)
if location != nil && valueLocation != azure.NormalizeLocation(*location) {
georeplication_locations.Add(valueLocation)
}
geoReplicationLocations := make([]interface{}, 0)
geoReplications := make([]interface{}, 0)
for _, value := range replications.Values() {
if value.Location != nil {
valueLocation := azure.NormalizeLocation(*value.Location)
if location != nil && valueLocation != azure.NormalizeLocation(*location) {
geoReplicationLocations = append(geoReplicationLocations, *value.Location)
replication := make(map[string]interface{})
replication["location"] = valueLocation
replication["tags"] = tags.Flatten(value.Tags)
geoReplications = append(geoReplications, replication)
}
}

d.Set("georeplication_locations", georeplication_locations)
}

d.Set("georeplication_locations", geoReplicationLocations)
d.Set("georeplications", geoReplications)
return tags.FlattenAndSet(d, resp.Tags)
}

Expand Down Expand Up @@ -781,6 +813,36 @@ func expandTrustPolicy(p []interface{}) *containerregistry.TrustPolicy {
return &trustPolicy
}

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

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{
Location: &location,
Name: &location,
Tags: tags,
})
}
return replications
}

func flattenNetworkRuleSet(networkRuleSet *containerregistry.NetworkRuleSet) []interface{} {
if networkRuleSet == nil {
return []interface{}{}
Expand Down
Loading

0 comments on commit e8823b5

Please sign in to comment.