Skip to content

Commit

Permalink
[Resource][Data Source] ACR Scope Map and Tokens (#11350)
Browse files Browse the repository at this point in the history
Supersede #9402 after email exchange with @Lucretius which didn't have time to complete it currently.
I fixed the comments from last review.

Data Source for azurerm_container_registry_token
Data Source for azurerm_container_registry_scope_map
Resource for azurerm_container_registry_token
Resource for azurerm_container_registry_scope_map
Documentation for each
Tests for each, they all pass running against my own Azure environment.
There are passwords for the token which I would like to expose but there is no way to delete them once generated so they didn't really fit into the lifecycle management of the token itself. Perhaps they might be better suited as a separate data source, similar to how we handle the SAS tokens?

Also note that these are still both preview features. As such I've vendor-included the SDK preview code and only used it in these specific preview features (see client.go)

This PR resolves #6505
  • Loading branch information
MattiasAng authored Apr 28, 2021
1 parent fc3f32b commit 0f8f856
Show file tree
Hide file tree
Showing 23 changed files with 2,050 additions and 0 deletions.
10 changes: 10 additions & 0 deletions azurerm/internal/services/containers/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type Client struct {
ReplicationsClient *containerregistry.ReplicationsClient
ServicesClient *legacy.ContainerServicesClient
WebhooksClient *containerregistry.WebhooksClient
TokensClient *containerregistry.TokensClient
ScopeMapsClient *containerregistry.ScopeMapsClient

Environment azure.Environment
}
Expand All @@ -31,6 +33,12 @@ func NewClient(o *common.ClientOptions) *Client {
replicationsClient := containerregistry.NewReplicationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&replicationsClient.Client, o.ResourceManagerAuthorizer)

tokensClient := containerregistry.NewTokensClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&tokensClient.Client, o.ResourceManagerAuthorizer)

scopeMapsClient := containerregistry.NewScopeMapsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&scopeMapsClient.Client, o.ResourceManagerAuthorizer)

groupsClient := containerinstance.NewContainerGroupsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&groupsClient.Client, o.ResourceManagerAuthorizer)

Expand All @@ -53,5 +61,7 @@ func NewClient(o *common.ClientOptions) *Client {
ReplicationsClient: &replicationsClient,
ServicesClient: &servicesClient,
Environment: o.Environment,
TokensClient: &tokensClient,
ScopeMapsClient: &scopeMapsClient,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package containers

import (
"fmt"
"time"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/containers/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func dataSourceContainerRegistryScopeMap() *schema.Resource {
return &schema.Resource{
Read: dataSourceContainerRegistryScopeMapRead,

Timeouts: &schema.ResourceTimeout{
Read: schema.DefaultTimeout(5 * time.Minute),
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"container_registry_name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.ContainerRegistryName,
},
"resource_group_name": azure.SchemaResourceGroupNameForDataSource(),
"description": {
Type: schema.TypeString,
Computed: true,
},
"actions": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
}
}

func dataSourceContainerRegistryScopeMapRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Containers.ScopeMapsClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

resourceGroup := d.Get("resource_group_name").(string)
containerRegistryName := d.Get("container_registry_name").(string)
name := d.Get("name").(string)

resp, err := client.Get(ctx, resourceGroup, containerRegistryName, name)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
return fmt.Errorf("Container Registry Scope Map %q was not found in Resource Group %q", name, resourceGroup)
}

return fmt.Errorf("Error making Read request on Scope Map %q (Azure Container Registry %q, Resource Group %q): %+v", name, containerRegistryName, resourceGroup, err)
}

if resp.ID == nil || *resp.ID == "" {
return fmt.Errorf("retrieving Container Registry Scope Map %q (Azure Container Registry %q, Resource Group %q): `id` was nil", name, containerRegistryName, resourceGroup)
}

d.SetId(*resp.ID)
d.Set("name", resp.Name)
d.Set("resource_group_name", resourceGroup)
d.Set("container_registry_name", containerRegistryName)
d.Set("description", resp.Description)
d.Set("actions", utils.FlattenStringSlice(resp.Actions))

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package containers_test

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance/check"
)

type ContainerRegistryScopeMapDataSource struct {
}

func TestAccDataSourceContainerRegistryScopeMap_complete(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azurerm_container_registry_scope_map", "test")
r := ContainerRegistryScopeMapDataSource{}

data.DataSourceTest(t, []resource.TestStep{
{
Config: r.complete(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).Key("name").Exists(),
check.That(data.ResourceName).Key("resource_group_name").Exists(),
check.That(data.ResourceName).Key("container_registry_name").Exists(),
check.That(data.ResourceName).Key("description").HasValue("A test scope map"),
check.That(data.ResourceName).Key("actions.#").HasValue("1"),
check.That(data.ResourceName).Key("actions.0").HasValue("repositories/testrepo/content/read"),
),
},
})
}

func (ContainerRegistryScopeMapDataSource) complete(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
resource "azurerm_container_registry_scope_map" "test" {
name = "testscopemap%d"
description = "A test scope map"
resource_group_name = azurerm_resource_group.test.name
container_registry_name = azurerm_container_registry.test.name
actions = ["repositories/testrepo/content/read"]
}
data "azurerm_container_registry_scope_map" "test" {
name = azurerm_container_registry_scope_map.test.name
container_registry_name = azurerm_container_registry.test.name
resource_group_name = azurerm_container_registry.test.resource_group_name
}
`, ContainerRegistryResource{}.basicManaged(data, "Premium"), data.RandomInteger)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package containers

import (
"fmt"
"log"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/containerregistry/mgmt/2020-11-01-preview/containerregistry"
"github.com/hashicorp/go-azure-helpers/response"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/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/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/containers/parse"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/containers/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceContainerRegistryScopeMap() *schema.Resource {
return &schema.Resource{
Create: resourceContainerRegistryScopeMapCreate,
Read: resourceContainerRegistryScopeMapRead,
Update: resourceContainerRegistryScopeMapUpdate,
Delete: resourceContainerRegistryScopeMapDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(30 * time.Minute),
Read: schema.DefaultTimeout(5 * time.Minute),
Update: schema.DefaultTimeout(30 * time.Minute),
Delete: schema.DefaultTimeout(30 * time.Minute),
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.ContainerRegistryName,
},

"description": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(1, 256),
},

"resource_group_name": azure.SchemaResourceGroupName(),

"container_registry_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.ContainerRegistryName,
},

"actions": {
Type: schema.TypeList,
Required: true,
MinItems: 1,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringIsNotEmpty,
},
},
},
}
}

func resourceContainerRegistryScopeMapCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Containers.ScopeMapsClient
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()

log.Printf("[INFO] preparing arguments for AzureRM Container Registry scope map creation.")
resourceGroup := d.Get("resource_group_name").(string)
containerRegistryName := d.Get("container_registry_name").(string)
name := d.Get("name").(string)

if d.IsNewResource() {
existing, err := client.Get(ctx, resourceGroup, containerRegistryName, name)
if err != nil {
if !utils.ResponseWasNotFound(existing.Response) {
return fmt.Errorf("Error checking for presence of existing scope map %q in Container Registry %q (Resource Group %q): %s", name, containerRegistryName, resourceGroup, err)
}
}

if existing.ID != nil && *existing.ID != "" {
return tf.ImportAsExistsError("azurerm_container_registry_scope_map", *existing.ID)
}
}

description := d.Get("description").(string)
actions := d.Get("actions").([]interface{})

parameters := containerregistry.ScopeMap{
ScopeMapProperties: &containerregistry.ScopeMapProperties{
Description: utils.String(description),
Actions: utils.ExpandStringSlice(actions),
},
}

future, err := client.Create(ctx, resourceGroup, containerRegistryName, name, parameters)
if err != nil {
return fmt.Errorf("Error creating scope map %q in Container Registry %q (Resource Group %q): %+v", name, containerRegistryName, resourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for creation of scope map %q (Container Registry %q, Resource Group %q): %+v", name, containerRegistryName, resourceGroup, err)
}

read, err := client.Get(ctx, resourceGroup, containerRegistryName, name)
if err != nil {
return fmt.Errorf("Error retrieving scope map %q for Container Registry %q (Resource Group %q): %+v", name, containerRegistryName, resourceGroup, err)
}

if read.ID == nil {
return fmt.Errorf("Cannot read scope map %q for Container Registry %q (resource group %q) ID", name, containerRegistryName, resourceGroup)
}

d.SetId(*read.ID)

return resourceContainerRegistryScopeMapRead(d, meta)
}

func resourceContainerRegistryScopeMapUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Containers.ScopeMapsClient
ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

log.Printf("[INFO] preparing arguments for AzureRM Container Registry scope map update.")
resourceGroup := d.Get("resource_group_name").(string)
containerRegistryName := d.Get("container_registry_name").(string)
name := d.Get("name").(string)
description := d.Get("description").(string)
actions := d.Get("actions").([]interface{})

parameters := containerregistry.ScopeMapUpdateParameters{
ScopeMapPropertiesUpdateParameters: &containerregistry.ScopeMapPropertiesUpdateParameters{
Description: utils.String(description),
Actions: utils.ExpandStringSlice(actions),
},
}

future, err := client.Update(ctx, resourceGroup, containerRegistryName, name, parameters)
if err != nil {
return fmt.Errorf("Error updating scope map %q for Container Registry %q (Resource Group %q): %+v", name, containerRegistryName, resourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for update of scope map %q (Container Registry %q, Resource Group %q): %+v", name, containerRegistryName, resourceGroup, err)
}

read, err := client.Get(ctx, resourceGroup, containerRegistryName, name)
if err != nil {
return fmt.Errorf("Error retrieving scope map %q (Container Registry %q, Resource Group %q): %+v", name, containerRegistryName, resourceGroup, err)
}

if read.ID == nil {
return fmt.Errorf("Cannot read scope map %q (Container Registry %q, resource group %q) ID", name, containerRegistryName, resourceGroup)
}

d.SetId(*read.ID)

return resourceContainerRegistryScopeMapRead(d, meta)
}

func resourceContainerRegistryScopeMapRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Containers.ScopeMapsClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.ContainerRegistryScopeMapID(d.Id())
if err != nil {
return err
}

resp, err := client.Get(ctx, id.ResourceGroup, id.RegistryName, id.ScopeMapName)

if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[DEBUG] Scope Map %q was not found in Container Registry %q in Resource Group %q", id.ScopeMapName, id.RegistryName, id.ResourceGroup)
d.SetId("")
return nil
}

return fmt.Errorf("Error making Read request on scope map %q in Azure Container Registry %q (Resource Group %q): %+v", id.ScopeMapName, id.RegistryName, id.ResourceGroup, err)
}

d.Set("name", resp.Name)
d.Set("resource_group_name", id.ResourceGroup)
d.Set("container_registry_name", id.RegistryName)
d.Set("description", resp.Description)
d.Set("actions", utils.FlattenStringSlice(resp.Actions))

return nil
}

func resourceContainerRegistryScopeMapDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Containers.ScopeMapsClient
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.ContainerRegistryScopeMapID(d.Id())
if err != nil {
return err
}

future, err := client.Delete(ctx, id.ResourceGroup, id.RegistryName, id.ScopeMapName)
if err != nil {
if response.WasNotFound(future.Response()) {
return nil
}
return fmt.Errorf("Error issuing Azure ARM delete request of Container Registry scope map '%s': %+v", id.ScopeMapName, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
if response.WasNotFound(future.Response()) {
return nil
}
return fmt.Errorf("Error issuing Azure ARM delete request of Container Registry scope map '%s': %+v", id.ScopeMapName, err)
}

return nil
}
Loading

0 comments on commit 0f8f856

Please sign in to comment.