From dbc39cc0d5d8208558aa2771d43022dcc29239e6 Mon Sep 17 00:00:00 2001 From: JT <100274846+jiaweitao001@users.noreply.github.com> Date: Fri, 28 Jul 2023 13:22:59 +0800 Subject: [PATCH] Add registry block to the module to enable personal repos (#11) * Add registry block to the module to enable personal repos --------- Co-authored-by: lonegunmanb --- .gitignore | 6 +- examples/acr/main.tf | 258 +++++++++++++++++++++++++++++++++ examples/acr/outputs.tf | 3 + examples/acr/variables.tf | 14 ++ examples/acr/versions.tf | 26 ++++ examples/dapr/main.tf | 24 +-- main.tf | 10 ++ test/e2e/terraform_e2e_test.go | 34 +++++ test/go.mod | 2 +- 9 files changed, 363 insertions(+), 14 deletions(-) create mode 100644 examples/acr/main.tf create mode 100644 examples/acr/outputs.tf create mode 100644 examples/acr/variables.tf create mode 100644 examples/acr/versions.tf diff --git a/.gitignore b/.gitignore index 0bdfcae..8d159bc 100644 --- a/.gitignore +++ b/.gitignore @@ -50,5 +50,9 @@ Gemfile.lock .tflint_example.hcl tfmod-scaffold README-generated.md +**/override.tf scripts -log \ No newline at end of file +log + +/TestRecord +**/TestRecord.md.tmp \ No newline at end of file diff --git a/examples/acr/main.tf b/examples/acr/main.tf new file mode 100644 index 0000000..9f569c6 --- /dev/null +++ b/examples/acr/main.tf @@ -0,0 +1,258 @@ +resource "random_id" "rg_name" { + byte_length = 8 +} + +resource "random_id" "env_name" { + byte_length = 8 +} + +resource "random_id" "container_name" { + byte_length = 4 +} + +resource "null_resource" "docker_push" { + provisioner "local-exec" { + command = "docker login -u ${azurerm_container_registry_token.pushtoken.name} -p ${azurerm_container_registry_token_password.pushtokenpassword.password1[0].value} https://${azurerm_container_registry.acr.login_server}" + } + provisioner "local-exec" { + command = "docker push ${docker_tag.nginx.target_image}" + } +} + +resource "azurerm_resource_group" "test" { + location = var.location + name = "example-container-app-${random_id.rg_name.hex}" +} + +module "public_ip" { + source = "lonegunmanb/public-ip/lonegunmanb" + version = "0.1.0" +} + +locals { + public_ip = module.public_ip.public_ip +} + +resource "azurerm_virtual_network" "vnet" { + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.test.location + name = "virtualnetwork1" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_subnet" "subnet" { + address_prefixes = ["10.0.0.0/23"] + name = "subnet1" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.vnet.name + private_endpoint_network_policies_enabled = false + private_link_service_network_policies_enabled = false + service_endpoints = ["Microsoft.ContainerRegistry"] +} + +resource "azurerm_private_endpoint" "pep" { + location = azurerm_resource_group.test.location + name = "mype" + resource_group_name = azurerm_resource_group.test.name + subnet_id = azurerm_subnet.subnet.id + + private_service_connection { + is_manual_connection = false + name = "countainerregistryprivatelink" + private_connection_resource_id = azurerm_container_registry.acr.id + subresource_names = ["registry"] + } +} + +resource "azurerm_private_dns_zone" "pdz" { + name = "privatelink.azurecr.io" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_private_dns_zone_virtual_network_link" "vnetlink_private" { + name = "mydnslink" + private_dns_zone_name = azurerm_private_dns_zone.pdz.name + resource_group_name = azurerm_resource_group.test.name + virtual_network_id = azurerm_virtual_network.vnet.id +} + +locals { + acr_login_server = [ + for c in azurerm_private_endpoint.pep.custom_dns_configs : c.ip_addresses[0] + if c.fqdn == "${azurerm_container_registry.acr.name}.azurecr.io" + ][0] +} + +resource "azurerm_private_dns_a_record" "private" { + name = azurerm_container_registry.acr.name + records = [local.acr_login_server] + resource_group_name = azurerm_resource_group.test.name + ttl = 3600 + zone_name = azurerm_private_dns_zone.pdz.name +} + +locals { + data_endpoint_ips = { for e in azurerm_private_endpoint.pep.custom_dns_configs : e.fqdn => e.ip_addresses[0] } +} + +resource "azurerm_private_dns_a_record" "data" { + name = "${azurerm_container_registry.acr.name}.${var.location}.data" + records = [ + local.data_endpoint_ips["${azurerm_container_registry.acr.name}.${var.location}.data.azurecr.io"] + ] + resource_group_name = azurerm_resource_group.test.name + ttl = 3600 + zone_name = azurerm_private_dns_zone.pdz.name +} + +resource "azurerm_container_registry" "acr" { + #checkov:skip=CKV_AZURE_139: Public network access is required for the test + #checkov:skip=CKV_AZURE_166: Quarantine would block our test so we skip it + location = azurerm_resource_group.test.location + name = "acr${random_id.container_name.hex}" + resource_group_name = azurerm_resource_group.test.name + sku = "Premium" + admin_enabled = false + public_network_access_enabled = true + + georeplications { + location = var.backup_location1 + tags = {} + zone_redundancy_enabled = true + } + georeplications { + location = var.backup_location2 + tags = {} + zone_redundancy_enabled = true + } + network_rule_set { + default_action = "Deny" + + ip_rule { + action = "Allow" + ip_range = "${local.public_ip}/32" + } + virtual_network { + action = "Allow" + subnet_id = azurerm_subnet.subnet.id + } + } + retention_policy { + days = 7 + enabled = true + } + trust_policy { + enabled = true + } +} + +data "azurerm_container_registry_scope_map" "push_repos" { + container_registry_name = azurerm_container_registry.acr.name + name = "_repositories_push" + resource_group_name = azurerm_container_registry.acr.resource_group_name +} + +data "azurerm_container_registry_scope_map" "pull_repos" { + container_registry_name = azurerm_container_registry.acr.name + name = "_repositories_pull" + resource_group_name = azurerm_container_registry.acr.resource_group_name +} + +resource "azurerm_container_registry_token" "pushtoken" { + container_registry_name = azurerm_container_registry.acr.name + name = "pushtoken" + resource_group_name = azurerm_container_registry.acr.resource_group_name + scope_map_id = data.azurerm_container_registry_scope_map.push_repos.id +} + +resource "azurerm_container_registry_token" "pulltoken" { + container_registry_name = azurerm_container_registry.acr.name + name = "pulltoken" + resource_group_name = azurerm_container_registry.acr.resource_group_name + scope_map_id = data.azurerm_container_registry_scope_map.pull_repos.id +} + +resource "azurerm_container_registry_token_password" "pushtokenpassword" { + container_registry_token_id = azurerm_container_registry_token.pushtoken.id + + password1 { + expiry = timeadd(timestamp(), "24h") + } + lifecycle { + ignore_changes = [password1] + } +} + +resource "azurerm_container_registry_token_password" "pulltokenpassword" { + container_registry_token_id = azurerm_container_registry_token.pulltoken.id + + password1 { + expiry = timeadd(timestamp(), "24h") + } + lifecycle { + ignore_changes = [password1] + } +} + +resource "docker_image" "nginx" { + name = "nginx:latest" +} + +resource "docker_tag" "nginx" { + source_image = docker_image.nginx.name + target_image = "${azurerm_container_registry.acr.login_server}/${docker_image.nginx.name}" +} + +module "container_apps" { + source = "../.." + + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + log_analytics_workspace_name = "loganalytics-${random_id.rg_name.hex}" + container_app_environment_name = "example-env-${random_id.env_name.hex}" + container_app_environment_infrastructure_subnet_id = azurerm_subnet.subnet.id + + container_apps = { + nginx = { + name = "nginx" + revision_mode = "Single" + + template = { + containers = [ + { + name = "nginx" + memory = "0.5Gi" + cpu = 0.25 + image = "${azurerm_container_registry.acr.login_server}/nginx" + } + ] + } + ingress = { + allow_insecure_connections = false + external_enabled = true + target_port = 80 + traffic_weight = { + latest_revision = true + percentage = 100 + } + } + registry = [ + { + server = azurerm_container_registry.acr.login_server + username = azurerm_container_registry_token.pulltoken.name + password_secret_name = "secname" + } + ] + } + } + + container_app_secrets = { + nginx = [ + { + name = "secname" + value = azurerm_container_registry_token_password.pulltokenpassword.password1[0].value + } + ] + } + depends_on = [null_resource.docker_push] +} diff --git a/examples/acr/outputs.tf b/examples/acr/outputs.tf new file mode 100644 index 0000000..08d0559 --- /dev/null +++ b/examples/acr/outputs.tf @@ -0,0 +1,3 @@ +output "app_url" { + value = module.container_apps.container_app_fqdn +} diff --git a/examples/acr/variables.tf b/examples/acr/variables.tf new file mode 100644 index 0000000..c9af7d9 --- /dev/null +++ b/examples/acr/variables.tf @@ -0,0 +1,14 @@ +variable "backup_location1" { + type = string + default = "northeurope" +} + +variable "backup_location2" { + type = string + default = "westeurope" +} + +variable "location" { + type = string + default = "eastus" +} diff --git a/examples/acr/versions.tf b/examples/acr/versions.tf new file mode 100644 index 0000000..da348c1 --- /dev/null +++ b/examples/acr/versions.tf @@ -0,0 +1,26 @@ +terraform { + required_version = ">= 1.2" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 3.11, < 4.0" + } + docker = { + source = "kreuzwerker/docker" + version = "3.0.2" + } + null = { + source = "hashicorp/null" + version = ">= 2.0.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.0.0" + } + } +} + +provider "azurerm" { + features {} +} diff --git a/examples/dapr/main.tf b/examples/dapr/main.tf index 2d9692c..cea21d8 100644 --- a/examples/dapr/main.tf +++ b/examples/dapr/main.tf @@ -263,10 +263,6 @@ module "containerapps" { revision_mode = "Single" template = { - dapr = { - app_id = "pythonapp" - app_port = 0 - } containers = [ { name = "pythonapp" @@ -276,6 +272,10 @@ module "containerapps" { } ] } + dapr = { + app_id = "pythonapp" + app_port = 0 + } tags = { "environment" = "dev" } @@ -285,14 +285,6 @@ module "containerapps" { revision_mode = "Single" template = { - dapr = { - app_id = "nodeapp" - app_port = 3000 - } - identity = { - type = "UserAssigned" - user_assigned_identity_id = azurerm_user_assigned_identity.test.id - } containers = [ { name = "nodeapp" @@ -308,6 +300,14 @@ module "containerapps" { } ] } + dapr = { + app_id = "nodeapp" + app_port = 3000 + } + identity = { + type = "UserAssigned" + identity_ids = [azurerm_user_assigned_identity.test.id] + } } } } \ No newline at end of file diff --git a/main.tf b/main.tf index e9d9846..af81b79 100644 --- a/main.tf +++ b/main.tf @@ -227,6 +227,16 @@ resource "azurerm_container_app" "container_app" { } } } + dynamic "registry" { + for_each = each.value.registry == null ? [] : each.value.registry + + content { + server = registry.value.server + identity = registry.value.identity + password_secret_name = registry.value.password_secret_name + username = registry.value.username + } + } dynamic "secret" { for_each = nonsensitive(toset([for pair in lookup(var.container_app_secrets, each.key, []) : pair.name])) diff --git a/test/e2e/terraform_e2e_test.go b/test/e2e/terraform_e2e_test.go index 002608b..07f0ac9 100644 --- a/test/e2e/terraform_e2e_test.go +++ b/test/e2e/terraform_e2e_test.go @@ -1,11 +1,15 @@ package e2e import ( + "io" + "net/http" "os" "testing" test_helper "github.com/Azure/terraform-module-test-helper" "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestExamplesDapr(t *testing.T) { @@ -32,3 +36,33 @@ func TestExamplesStartup(t *testing.T) { Vars: vars, }, func(t *testing.T, output test_helper.TerraformOutput) {}) } + +func TestExampleAcr(t *testing.T) { + test_helper.RunE2ETest(t, "../..", "examples/acr", terraform.Options{ + Upgrade: true, + }, func(t *testing.T, output test_helper.TerraformOutput) { + urls, ok := output["app_url"].(map[string]any) + require.True(t, ok) + url := urls["nginx"].(string) + html, err := getHTML(url) + require.NoError(t, err) + assert.Contains(t, html, "nginx") + }) +} + +func getHTML(url string) (string, error) { + resp, err := http.Get(url) // #nosec G107 + if err != nil { + return "", err + } + defer func() { + _ = resp.Body.Close() + }() + + bytes, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + return string(bytes), nil +} diff --git a/test/go.mod b/test/go.mod index 93e8aaf..0b4ce76 100644 --- a/test/go.mod +++ b/test/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( github.com/Azure/terraform-module-test-helper v0.12.0 github.com/gruntwork-io/terratest v0.41.15 + github.com/stretchr/testify v1.8.2 ) require ( @@ -68,7 +69,6 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.8.2 // indirect github.com/tmccombs/hcl2json v0.3.3 // indirect github.com/ulikunitz/xz v0.5.10 // indirect github.com/urfave/cli v1.22.2 // indirect