diff --git a/infra/modules/providers/azure/app-insights/main.tf b/infra/modules/providers/azure/app-insights/main.tf index 41e3c38f545ed8cefe8b5a8c6f50417c88fc26a4..f520d9d99273b5cb308479bb981bceec8f759394 100755 --- a/infra/modules/providers/azure/app-insights/main.tf +++ b/infra/modules/providers/azure/app-insights/main.tf @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=3.90.0, <=4.17.0" + } + } +} + data "azurerm_resource_group" "appinsights" { name = var.service_plan_resource_group_name } diff --git a/infra/modules/providers/azure/app-monitoring/main.tf b/infra/modules/providers/azure/app-monitoring/main.tf index 8e177f646be4beadc460690eabd3345b1113b78e..b342f54cbd0194bc21b539b8b30541d28ff86074 100755 --- a/infra/modules/providers/azure/app-monitoring/main.tf +++ b/infra/modules/providers/azure/app-monitoring/main.tf @@ -1,4 +1,4 @@ -// Copyright © Microsoft Corporation +// Copyright © Microsoft Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=3.90.0, <=4.17.0" + } + } +} + locals { scaling_name = "Instance" scaling_operator = "Include" diff --git a/infra/modules/providers/azure/app-monitoring/test.sh b/infra/modules/providers/azure/app-monitoring/test.sh new file mode 100755 index 0000000000000000000000000000000000000000..e97fef177b3ddbdc4858c2b210886d7b330546ca --- /dev/null +++ b/infra/modules/providers/azure/app-monitoring/test.sh @@ -0,0 +1,143 @@ +#!/bin/bash + +# Exit on error +set -e + +############################### +# Source Common Functions +############################### +COMMON_LIB="../test-functions.sh" +if [ ! -f "$COMMON_LIB" ]; then + echo "Error: Common library not found at $COMMON_LIB" + exit 1 +fi +source "$COMMON_LIB" + + +############################### +# Script Configuration +############################### +SCRIPT_DIR=$(get_script_dir) +setup_test_directories "$SCRIPT_DIR" + + +############################### +# Required Environment Variables +############################### +validate_azure_credentials + + +############################### +# Optional Variables +############################### +# These can be overridden by setting them before running the script +RESOURCE_GROUP_PREFIX=${RESOURCE_GROUP_PREFIX:-"terraform-test"} +DEFAULT_LOCATION="eastus2" +ACTION_GROUP_NAME=${ACTION_GROUP_NAME:-""} # Will be auto-generated if not provided +METRIC_ALERT_NAME=${METRIC_ALERT_NAME:-""} # Will be auto-generated if not provided + + +############################### +# Help Documentation +############################### +print_help() { + print_common_help "App Monitoring" "\n- If not specified, unique names will be generated for Action Group and Metric Alert" +} + + +############################### +# Required Module Functions +############################### +setup_configuration() { + # Setup base configuration first + setup_base_configuration "$RESOURCE_GROUP_PREFIX" "$DEFAULT_LOCATION" "$@" + + # Generate names if not provided + if [ -z "$ACTION_GROUP_NAME" ]; then + ACTION_GROUP_NAME=$(generate_unique_name "" "ag") + fi + + if [ -z "$METRIC_ALERT_NAME" ]; then + METRIC_ALERT_NAME=$(generate_unique_name "" "alert") + fi + + # Export additional variables for Go tests + export ACTION_GROUP_NAME + export METRIC_ALERT_NAME +} + +create_tfvars_files() { + local tfvars_content=" +resource_group_name = \"$RESOURCE_GROUP_NAME\" +action_group_name = \"$ACTION_GROUP_NAME\" +action_group_email_receiver = \"test@example.com\" +action_group_email_receiver_name = \"E-mail Receiver\" +action_group_short_name = \"Notify\" +metric_alert_name = \"$METRIC_ALERT_NAME\" +metric_alert_frequency = \"PT1M\" +metric_alert_period = \"PT5M\" +metric_alert_criteria_namespace = \"Microsoft.Web/sites\" +metric_alert_criteria_name = \"Http5xx\" +metric_alert_criteria_aggregation = \"Total\" +metric_alert_criteria_operator = \"GreaterThan\" +metric_alert_criteria_threshold = \"0\" +monitoring_dimension_values = [\"*\"] +resource_ids = [\"/subscriptions/$ARM_SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP_NAME/providers/Microsoft.Web/sites/test-site\"]" + create_base_tfvars_files "$tfvars_content" +} + + +############################### +# Optional Module Functions +############################### +validate_variables() { + validate_base_variables + + # Validate Action Group specific variables + if [[ ! "$ACTION_GROUP_NAME" =~ ^[a-zA-Z0-9-]+$ ]]; then + log "Error: Action Group name must be alphanumeric with hyphens" 1 + exit 1 + fi + + # Validate Metric Alert name + if [[ ! "$METRIC_ALERT_NAME" =~ ^[a-zA-Z0-9-]+$ ]]; then + log "Error: Metric Alert name must be alphanumeric with hyphens" 1 + exit 1 + fi + + log "Using Action Group: $ACTION_GROUP_NAME" 6 + log "Using Metric Alert: $METRIC_ALERT_NAME" 6 +} + + +############################### +# Main Execution +############################### +main() { + # Trap cleanup on exit + trap 'cleanup; cleanup_resource_group "$RESOURCE_GROUP_NAME" "$ARM_SUBSCRIPTION_ID"' EXIT + + # Setup configuration + setup_configuration "$@" + + # Validate variables + validate_variables + + # Setup + setup_azure_with_rg + create_tfvars_files + + # Run all tests + run_standard_test_sequence +} + +# Check for help flag +case "$1" in + -h|--help) + print_help + exit 0 + ;; +esac + +# Execute main function +main "$@" \ No newline at end of file diff --git a/infra/modules/providers/azure/app-monitoring/testing/main.tf b/infra/modules/providers/azure/app-monitoring/testing/main.tf index e4e7ed2e36dbe5fa6778b8a954b124fc15918d3f..ea01a8fa285c38dead96d0599883a66b6f524084 100644 --- a/infra/modules/providers/azure/app-monitoring/testing/main.tf +++ b/infra/modules/providers/azure/app-monitoring/testing/main.tf @@ -16,27 +16,109 @@ provider "azurerm" { features {} } -module "resource_group" { - source = "../../resource-group" - name = "osdu-module" - location = "eastus2" +module "app_monitoring" { + source = "../" + + resource_group_name = var.resource_group_name + action_group_name = var.action_group_name + action_group_email_receiver = var.action_group_email_receiver + action_group_email_receiver_name = var.action_group_email_receiver_name + action_group_short_name = var.action_group_short_name + metric_alert_name = var.metric_alert_name + metric_alert_frequency = var.metric_alert_frequency + metric_alert_period = var.metric_alert_period + metric_alert_criteria_namespace = var.metric_alert_criteria_namespace + metric_alert_criteria_name = var.metric_alert_criteria_name + metric_alert_criteria_aggregation = var.metric_alert_criteria_aggregation + metric_alert_criteria_operator = var.metric_alert_criteria_operator + metric_alert_criteria_threshold = var.metric_alert_criteria_threshold + monitoring_dimension_values = var.monitoring_dimension_values + resource_ids = var.resource_ids } -module "app_monitoring" { - source = "../" - depends_on = [module.resource_group] - - resource_group_name = module.resource_group.name - action_group_name = var.action_group_name - - # action_group_email_receiver = "${var.action_group_email_receiver}" - # metric_alert_name = "${var.metric_alert_name}" - # metric_alert_frequency = "${var.metric_alert_frequency}" - # metric_alert_period = "${var.metric_alert_period}" - # metric_alert_criteria_namespace = "${var.metric_alert_criteria_namespace}" - # metric_alert_criteria_name = "${var.metric_alert_criteria_name}" - # metric_alert_criteria_aggregation = "${var.metric_alert_criteria_aggregation}" - # metric_alert_criteria_operator = "${var.metric_alert_criteria_operator}" - # metric_alert_criteria_threshold = "${var.metric_alert_criteria_threshold}" - # monitoring_dimension_values = "${var.monitoring_dimension_values}" +# Variables +variable "resource_group_name" { + type = string + description = "The name of the resource group" +} + +variable "action_group_name" { + type = string + description = "The name of the action group" +} + +variable "action_group_email_receiver" { + type = string + description = "The email address to receive alerts" + default = "test@example.com" +} + +variable "action_group_email_receiver_name" { + type = string + description = "The name of the email receiver" + default = "E-mail Receiver" +} + +variable "action_group_short_name" { + type = string + description = "The short name of the action group" + default = "Notify" +} + +variable "metric_alert_name" { + type = string + description = "The name of the metric alert" +} + +variable "metric_alert_frequency" { + type = string + description = "The frequency of the metric alert" + default = "PT1M" +} + +variable "metric_alert_period" { + type = string + description = "The period of the metric alert" + default = "PT5M" +} + +variable "metric_alert_criteria_namespace" { + type = string + description = "The namespace of the metric alert criteria" + default = "Microsoft.Web/sites" +} + +variable "metric_alert_criteria_name" { + type = string + description = "The name of the metric alert criteria" + default = "Http5xx" +} + +variable "metric_alert_criteria_aggregation" { + type = string + description = "The aggregation of the metric alert criteria" + default = "Total" +} + +variable "metric_alert_criteria_operator" { + type = string + description = "The operator of the metric alert criteria" + default = "GreaterThan" +} + +variable "metric_alert_criteria_threshold" { + type = string + description = "The threshold of the metric alert criteria" + default = "0" +} + +variable "monitoring_dimension_values" { + type = list(string) + description = "The dimension values to monitor" + default = ["*"] +} + +variable "resource_ids" { + type = list(string) + description = "List of resource IDs to monitor" } diff --git a/infra/modules/providers/azure/app-monitoring/testing/unit_test.go b/infra/modules/providers/azure/app-monitoring/testing/unit_test.go new file mode 100644 index 0000000000000000000000000000000000000000..512e3588e01ea7295da118d43e1e14fde49fd955 --- /dev/null +++ b/infra/modules/providers/azure/app-monitoring/testing/unit_test.go @@ -0,0 +1,109 @@ +// Copyright © Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/gruntwork-io/terratest/modules/random" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/microsoft/cobalt/test-harness/infratests" +) + +var workspace = "appmon-" + strings.ToLower(random.UniqueId()) +var count = 2 // One for action group and one for metric alert + +var actionGroupName = "ag" + strings.ToLower(random.UniqueId()) +var metricAlertName = "alert" + strings.ToLower(random.UniqueId()) + +var tfOptions = &terraform.Options{ + TerraformDir: "./", + Upgrade: true, + Vars: map[string]interface{}{ + "resource_group_name": "osdu-module", // Fixed name for unit tests + "action_group_name": actionGroupName, + "action_group_email_receiver": "test@example.com", + "metric_alert_name": metricAlertName, + "metric_alert_criteria_namespace": "Microsoft.Web/sites", + "metric_alert_criteria_name": "Http5xx", + "metric_alert_criteria_aggregation": "Total", + "metric_alert_criteria_operator": "GreaterThan", + "metric_alert_criteria_threshold": "0", + "monitoring_dimension_values": []string{"*"}, + "resource_ids": []string{"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/osdu-module/providers/Microsoft.Web/sites/test-site"}, + }, +} + +func asMap(t *testing.T, jsonString string) map[string]interface{} { + var theMap map[string]interface{} + if err := json.Unmarshal([]byte(jsonString), &theMap); err != nil { + t.Fatal(err) + } + return theMap +} + +func TestTemplate(t *testing.T) { + expectedActionGroup := asMap(t, `{ + "enabled": true, + "location": "global", + "name": "`+actionGroupName+`", + "resource_group_name": "osdu-module", + "short_name": "Notify", + "email_receiver": [{ + "email_address": "test@example.com", + "name": "E-mail Receiver" + }] + }`) + + expectedMetricAlert := asMap(t, `{ + "enabled": true, + "auto_mitigate": true, + "frequency": "PT1M", + "window_size": "PT5M", + "severity": 3, + "name": "`+metricAlertName+`", + "resource_group_name": "osdu-module", + "scopes": ["/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/osdu-module/providers/Microsoft.Web/sites/test-site"], + "criteria": [{ + "metric_namespace": "Microsoft.Web/sites", + "metric_name": "Http5xx", + "aggregation": "Total", + "operator": "GreaterThan", + "threshold": 0, + "skip_metric_validation": false, + "dimension": [{ + "name": "Instance", + "operator": "Include", + "values": ["*"] + }] + }] + }`) + + testFixture := infratests.UnitTestFixture{ + GoTest: t, + TfOptions: tfOptions, + Workspace: workspace, + PlanAssertions: nil, + ExpectedResourceCount: count, + ExpectedResourceAttributeValues: infratests.ResourceDescription{ + "module.app_monitoring.azurerm_monitor_action_group.appmonitoring[0]": expectedActionGroup, + "module.app_monitoring.azurerm_monitor_metric_alert.appmonitoring[0]": expectedMetricAlert, + }, + } + + infratests.RunUnitTests(&testFixture) +} diff --git a/infra/modules/providers/azure/app-monitoring/tests/tf_options.go b/infra/modules/providers/azure/app-monitoring/tests/tf_options.go new file mode 100644 index 0000000000000000000000000000000000000000..7f7c6758f130135a8bf046c89c2521d5a7432256 --- /dev/null +++ b/infra/modules/providers/azure/app-monitoring/tests/tf_options.go @@ -0,0 +1,68 @@ +// Copyright © Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tests + +import ( + "os" + + "github.com/gruntwork-io/terratest/modules/terraform" +) + +// ActionGroupName - The Action Group Name +var ActionGroupName = os.Getenv("ACTION_GROUP_NAME") + +// MetricAlertName - The Metric Alert Name +var MetricAlertName = os.Getenv("METRIC_ALERT_NAME") + +// ResourceGroupName - The Resource Group Name +var ResourceGroupName = os.Getenv("RESOURCE_GROUP_NAME") + +// AppMonitoringTFOptions common terraform options used for unit testing +var AppMonitoringTFOptions = &terraform.Options{ + TerraformDir: "../../", // Point to module directory for unit tests + Vars: map[string]interface{}{ + "resource_group_name": ResourceGroupName, + "action_group_name": ActionGroupName, + "action_group_email_receiver": "test@example.com", + "metric_alert_name": MetricAlertName, + "metric_alert_frequency": "PT1M", + "metric_alert_period": "PT5M", + "metric_alert_criteria_namespace": "Microsoft.Web/sites", + "metric_alert_criteria_name": "Http5xx", + "metric_alert_criteria_aggregation": "Total", + "metric_alert_criteria_operator": "GreaterThan", + "metric_alert_criteria_threshold": 0, + "monitoring_dimension_values": []string{"*"}, + }, +} + +// AppMonitoringIntegrationTFOptions terraform options used for integration testing +var AppMonitoringIntegrationTFOptions = &terraform.Options{ + TerraformDir: "../../testing", // Point to testing directory for integration tests + Vars: map[string]interface{}{ + "resource_group_name": ResourceGroupName, + "action_group_name": ActionGroupName, + "action_group_email_receiver": "test@example.com", + "metric_alert_name": MetricAlertName, + "metric_alert_frequency": "PT1M", + "metric_alert_period": "PT5M", + "metric_alert_criteria_namespace": "Microsoft.Web/sites", + "metric_alert_criteria_name": "Http5xx", + "metric_alert_criteria_aggregation": "Total", + "metric_alert_criteria_operator": "GreaterThan", + "metric_alert_criteria_threshold": 0, + "monitoring_dimension_values": []string{"*"}, + }, +} diff --git a/infra/modules/providers/azure/container-registry/main.tf b/infra/modules/providers/azure/container-registry/main.tf index 0e34df50ad6f8b4cfb69a065b8c5ae2766283269..a4ece597a88b8f3491090c1299205ef225643459 100644 --- a/infra/modules/providers/azure/container-registry/main.tf +++ b/infra/modules/providers/azure/container-registry/main.tf @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=3.90.0, <=4.17.0" + } + } +} + data "azurerm_resource_group" "container_registry" { name = var.resource_group_name } diff --git a/infra/modules/providers/azure/container-registry/test.sh b/infra/modules/providers/azure/container-registry/test.sh new file mode 100755 index 0000000000000000000000000000000000000000..11b8b59b6dbb905978088082f2d329017f2097a0 --- /dev/null +++ b/infra/modules/providers/azure/container-registry/test.sh @@ -0,0 +1,151 @@ +#!/bin/bash + +# Exit on error +set -e + +############################### +# Source Common Functions +############################### +COMMON_LIB="../test-functions.sh" +if [ ! -f "$COMMON_LIB" ]; then + echo "Error: Common library not found at $COMMON_LIB" + exit 1 +fi +source "$COMMON_LIB" + + +############################### +# Script Configuration +############################### +SCRIPT_DIR=$(get_script_dir) +setup_test_directories "$SCRIPT_DIR" + + +############################### +# Required Environment Variables +############################### +validate_azure_credentials + + +############################### +# Optional Variables +############################### +# These can be overridden by setting them before running the script +RESOURCE_GROUP_PREFIX=${RESOURCE_GROUP_PREFIX:-"terraform-test"} +DEFAULT_LOCATION="eastus2" +CONTAINER_REGISTRY_NAME=${CONTAINER_REGISTRY_NAME:-""} # Will be auto-generated if not provided +CONTAINER_REGISTRY_SKU=${CONTAINER_REGISTRY_SKU:-"Standard"} # Default to Standard SKU +CONTAINER_REGISTRY_ADMIN_ENABLED=${CONTAINER_REGISTRY_ADMIN_ENABLED:-"false"} + + +############################### +# Help Documentation +############################### +print_help() { + print_common_help "Container Registry" "\n- If not specified, a unique Container Registry name will be generated\n- Default SKU is 'Standard'\n- Default admin access is disabled" +} + + +############################### +# Required Module Functions +############################### +setup_configuration() { + # Setup base configuration first + setup_base_configuration "$RESOURCE_GROUP_PREFIX" "$DEFAULT_LOCATION" "$@" + + # Generate Container Registry name if not provided + if [ -z "$CONTAINER_REGISTRY_NAME" ]; then + CONTAINER_REGISTRY_NAME=$(generate_unique_name "" "acr") + fi + + # Export additional variables for Go tests + export CONTAINER_REGISTRY_NAME + export CONTAINER_REGISTRY_SKU + export CONTAINER_REGISTRY_ADMIN_ENABLED +} + +create_tfvars_files() { + local tfvars_content=" +resource_group_name = \"$RESOURCE_GROUP_NAME\" +container_registry_name = \"$CONTAINER_REGISTRY_NAME\" +container_registry_sku = \"$CONTAINER_REGISTRY_SKU\" +container_registry_admin_enabled = $CONTAINER_REGISTRY_ADMIN_ENABLED + +resource_tags = { + environment = \"testing\" + module = \"container-registry\" +}" + create_base_tfvars_files "$tfvars_content" +} + + +############################### +# Optional Module Functions +############################### +validate_variables() { + validate_base_variables + + # Validate Container Registry specific variables + if [[ ! "$CONTAINER_REGISTRY_NAME" =~ ^[a-zA-Z0-9]+$ ]]; then + log "Error: Container Registry name must be alphanumeric" 1 + exit 1 + fi + + # Validate SKU + local valid_skus=("Basic" "Standard" "Premium") + local sku_valid=false + for valid_sku in "${valid_skus[@]}"; do + if [ "$CONTAINER_REGISTRY_SKU" == "$valid_sku" ]; then + sku_valid=true + break + fi + done + + if [ "$sku_valid" != true ]; then + log "Error: Invalid SKU. Must be one of: ${valid_skus[*]}" 1 + exit 1 + fi + + # Validate boolean values + if [[ ! "$CONTAINER_REGISTRY_ADMIN_ENABLED" =~ ^(true|false)$ ]]; then + log "Error: container_registry_admin_enabled must be either 'true' or 'false'" 1 + exit 1 + fi + + log "Using Container Registry: $CONTAINER_REGISTRY_NAME" 6 + log "Using SKU: $CONTAINER_REGISTRY_SKU" 6 + log "Admin Enabled: $CONTAINER_REGISTRY_ADMIN_ENABLED" 6 +} + + +############################### +# Main Execution +############################### +main() { + # Trap cleanup on exit + trap 'cleanup; cleanup_resource_group "$RESOURCE_GROUP_NAME" "$ARM_SUBSCRIPTION_ID"' EXIT + + # Setup configuration + setup_configuration "$@" + + # Validate variables + validate_variables + + # Setup + setup_azure_with_rg + create_tfvars_files + + # Run all tests + run_standard_test_sequence +} + +# Check for help flag +case "$1" in + -h|--help) + print_help + exit 0 + ;; +esac + +# Execute main function +main "$@" \ No newline at end of file diff --git a/infra/modules/providers/azure/container-registry/testing/main.tf b/infra/modules/providers/azure/container-registry/testing/main.tf index 13be28e14fe69d8fc05d374d24ca237b1f13a6e5..17657b6aadef9a9a1603c887b5df8fc4ce8c5182 100644 --- a/infra/modules/providers/azure/container-registry/testing/main.tf +++ b/infra/modules/providers/azure/container-registry/testing/main.tf @@ -16,28 +16,81 @@ provider "azurerm" { features {} } -module "resource_group" { - source = "../../resource-group" +module "container-registry" { + source = "../" - name = "osdu-module" - location = "eastus2" + resource_group_name = var.resource_group_name + container_registry_name = var.container_registry_name + container_registry_admin_enabled = var.container_registry_admin_enabled + container_registry_sku = var.container_registry_sku + resource_tags = { + environment = "testing" + module = "container-registry" + } } -module "container-registry" { - source = "../" - depends_on = [module.resource_group] +# Variables +variable "resource_group_name" { + type = string + description = "The name of the resource group" +} - resource_group_name = module.resource_group.name - container_registry_name = "osdu-module-container-registry-${module.resource_group.random}" - container_registry_admin_enabled = true - container_registry_sku = "Standard" - resource_tags = { - osdu = "module" +variable "container_registry_name" { + type = string + description = "The name of the container registry" +} + +variable "container_registry_admin_enabled" { + type = bool + description = "Whether admin access is enabled" + default = false +} + +variable "container_registry_sku" { + type = string + description = "The SKU of the container registry" + default = "Standard" +} + +variable "location" { + type = string + description = "The location of the container registry" + default = "eastus2" +} + +variable "resource_tags" { + type = map(string) + description = "Resource tags" + default = { + environment = "testing" + module = "container-registry" } - subnet_id_whitelist = [ - "test-subnet-id" - ] - resource_ip_whitelist = [ - "10.0.0.0/24" - ] +} + +variable "subnet_id_whitelist" { + type = list(string) + description = "List of subnet IDs to whitelist" + default = [] +} + +variable "resource_ip_whitelist" { + type = list(string) + description = "List of IP addresses/ranges to whitelist" + default = [] +} + +# Outputs +output "container_registry_id" { + description = "The Container Registry ID" + value = module.container-registry.container_registry_id +} + +output "container_registry_login_server" { + description = "The URL that can be used to log into the container registry" + value = module.container-registry.container_registry_login_server +} + +output "container_registry_name" { + description = "The name of the container registry" + value = module.container-registry.container_registry_name } diff --git a/infra/modules/providers/azure/container-registry/testing/unit_test.go b/infra/modules/providers/azure/container-registry/testing/unit_test.go index 66a8a609e4650166361b4658c5437605f9523bf0..a971eb8e43b765c54df792ae1e231db727d7ab52 100644 --- a/infra/modules/providers/azure/container-registry/testing/unit_test.go +++ b/infra/modules/providers/azure/container-registry/testing/unit_test.go @@ -24,13 +24,18 @@ import ( "github.com/microsoft/cobalt/test-harness/infratests" ) -var location = "eastus" -var count = 6 -var workspace = "osdu-services-" + strings.ToLower(random.UniqueId()) +var workspace = "acr-" + strings.ToLower(random.UniqueId()) +var count = 1 // One for the container registry var tfOptions = &terraform.Options{ TerraformDir: "./", Upgrade: true, + Vars: map[string]interface{}{ + "container_registry_name": "acr" + strings.ToLower(random.UniqueId()), + "resource_group_name": "osdu-module", // Fixed name for unit tests + "container_registry_admin_enabled": false, + "container_registry_sku": "Standard", + }, } func asMap(t *testing.T, jsonString string) map[string]interface{} { @@ -42,24 +47,12 @@ func asMap(t *testing.T, jsonString string) map[string]interface{} { } func TestTemplate(t *testing.T) { - - expectedResult := asMap(t, `{ - "admin_enabled" : true, - "network_rule_set" : [ - { - "default_action" : "Deny", - "ip_rule" : [ - { - "action" : "Allow", - "ip_range" : "10.0.0.0/24" - } - ] - } - ], - "resource_group_name" : "osdu-module", - "sku" : "Standard", - "tags" : { - "osdu" : "module" + expectedContainerRegistry := asMap(t, `{ + "admin_enabled": false, + "sku": "Standard", + "tags": { + "environment": "testing", + "module": "container-registry" } }`) @@ -70,7 +63,7 @@ func TestTemplate(t *testing.T) { PlanAssertions: nil, ExpectedResourceCount: count, ExpectedResourceAttributeValues: infratests.ResourceDescription{ - "module.container-registry.azurerm_container_registry.container_registry": expectedResult, + "module.container-registry.azurerm_container_registry.container_registry": expectedContainerRegistry, }, } diff --git a/infra/modules/providers/azure/container-registry/tests/integration/acr_test.go b/infra/modules/providers/azure/container-registry/tests/integration/acr_test.go index 98faa38bbf4687c13b68db71acff982ade30655b..cf069f86c7e8ffffc80fa522289b637c2f90ea62 100644 --- a/infra/modules/providers/azure/container-registry/tests/integration/acr_test.go +++ b/infra/modules/providers/azure/container-registry/tests/integration/acr_test.go @@ -16,29 +16,27 @@ package integration import ( "fmt" - "os" "testing" - "github.com/microsoft/cobalt/infra/modules/providers/azure/storage-account/tests" + "github.com/microsoft/cobalt/infra/modules/providers/azure/container-registry/tests" "github.com/microsoft/cobalt/test-harness/infratests" ) -const outputVariableCount int = 3 - -var subscription_id = os.Getenv("ARM_SUBSCRIPTION_ID") +var outputVariableCount = 3 func TestServiceDeployment(t *testing.T) { - if tests.ContainerName == "" { + if tests.ResourceGroupName == "" { + t.Fatal(fmt.Errorf("tests.ResourceGroupName was not specified. Are all the required environment variables set?")) + } + + if tests.ContainerRegistryName == "" { t.Fatal(fmt.Errorf("Container Registry Name was not specified. Are all the required environment variables set?")) } testFixture := infratests.IntegrationTestFixture{ GoTest: t, - TfOptions: tests.StorageTFOptions, + TfOptions: tests.ContainerRegistryIntegrationTFOptions, ExpectedTfOutputCount: outputVariableCount, - TfOutputAssertions: []infratests.TerraformOutputValidation{ - InspectContainerRegistryOutputs(subscription_id, "resource_group_name", "container_registry_name"), - }, } infratests.RunIntegrationTests(&testFixture) } diff --git a/infra/modules/providers/azure/container-registry/tests/tf_options.go b/infra/modules/providers/azure/container-registry/tests/tf_options.go new file mode 100644 index 0000000000000000000000000000000000000000..b5995bf1d61c5beb5ad69c7974fd6df8c69990b3 --- /dev/null +++ b/infra/modules/providers/azure/container-registry/tests/tf_options.go @@ -0,0 +1,49 @@ +// Copyright © Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tests + +import ( + "os" + + "github.com/gruntwork-io/terratest/modules/terraform" +) + +// ContainerRegistryName - The Container Registry Name +var ContainerRegistryName = os.Getenv("CONTAINER_REGISTRY_NAME") + +// ResourceGroupName - The Resource Group Name +var ResourceGroupName = os.Getenv("RESOURCE_GROUP_NAME") + +// ContainerRegistryTFOptions common terraform options used for unit testing +var ContainerRegistryTFOptions = &terraform.Options{ + TerraformDir: "../../", // Point to module directory for unit tests + Vars: map[string]interface{}{ + "resource_group_name": ResourceGroupName, + "container_registry_name": ContainerRegistryName, + "container_registry_admin_enabled": false, + "container_registry_sku": "Standard", + }, +} + +// ContainerRegistryIntegrationTFOptions terraform options used for integration testing +var ContainerRegistryIntegrationTFOptions = &terraform.Options{ + TerraformDir: "../../testing", // Point to testing directory for integration tests + Vars: map[string]interface{}{ + "resource_group_name": ResourceGroupName, + "container_registry_name": ContainerRegistryName, + "container_registry_admin_enabled": false, + "container_registry_sku": "Standard", + }, +} diff --git a/infra/modules/providers/azure/keyvault-secret/main.tf b/infra/modules/providers/azure/keyvault-secret/main.tf index 2b1986fce0c2f1fb14ecf81fcbf99ccef08cdb31..f3fcd1c30b45d11de86cfa556e6276d2def9c05f 100644 --- a/infra/modules/providers/azure/keyvault-secret/main.tf +++ b/infra/modules/providers/azure/keyvault-secret/main.tf @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=3.90.0, <=4.17.0" + } + } +} + locals { secret_names = keys(var.secrets) } diff --git a/infra/modules/providers/azure/keyvault-secret/test.sh b/infra/modules/providers/azure/keyvault-secret/test.sh new file mode 100755 index 0000000000000000000000000000000000000000..58de717d6db8066630f04fa2042bba9e62588c4c --- /dev/null +++ b/infra/modules/providers/azure/keyvault-secret/test.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +# Exit on error +set -e + +############################### +# Source Common Functions +############################### +COMMON_LIB="../test-functions.sh" +if [ ! -f "$COMMON_LIB" ]; then + echo "Error: Common library not found at $COMMON_LIB" + exit 1 +fi +source "$COMMON_LIB" + + +############################### +# Script Configuration +############################### +SCRIPT_DIR=$(get_script_dir) +setup_test_directories "$SCRIPT_DIR" + + +############################### +# Required Environment Variables +############################### +validate_azure_credentials + + +############################### +# Optional Variables +############################### +# These can be overridden by setting them before running the script +RESOURCE_GROUP_PREFIX=${RESOURCE_GROUP_PREFIX:-"terraform-test"} +DEFAULT_LOCATION="eastus2" +KEYVAULT_NAME=${KEYVAULT_NAME:-""} # Will be auto-generated if not provided +SECRET_NAME=${SECRET_NAME:-""} # Will be auto-generated if not provided +SECRET_VALUE=${SECRET_VALUE:-"test-secret-value"} # Default test value + + +############################### +# Help Documentation +############################### +print_help() { + print_common_help "KeyVault Secret" "\n- If not specified, unique names will be generated for KeyVault and Secret\n- Default secret value is 'test-secret-value'" +} + + +############################### +# Required Module Functions +############################### +setup_configuration() { + # Setup base configuration first + setup_base_configuration "$RESOURCE_GROUP_PREFIX" "$DEFAULT_LOCATION" "$@" + + # Generate KeyVault name if not provided + if [ -z "$KEYVAULT_NAME" ]; then + KEYVAULT_NAME=$(generate_unique_name "" "kv") + fi + + # Generate Secret name if not provided + if [ -z "$SECRET_NAME" ]; then + SECRET_NAME=$(generate_unique_name "" "secret") + fi + + # Export additional variables for Go tests + export KEYVAULT_NAME + export SECRET_NAME + export SECRET_VALUE +} + +create_tfvars_files() { + local tfvars_content=" +resource_group_name = \"$RESOURCE_GROUP_NAME\" +keyvault_name = \"$KEYVAULT_NAME\" +secrets = { + \"$SECRET_NAME\" = \"$SECRET_VALUE\" +}" + create_base_tfvars_files "$tfvars_content" +} + + +############################### +# Optional Module Functions +############################### +validate_variables() { + validate_base_variables + + # Validate KeyVault name + if [[ ! "$KEYVAULT_NAME" =~ ^[a-zA-Z0-9-]{3,24}$ ]]; then + log "Error: KeyVault name must be alphanumeric with hyphens and between 3-24 characters" 1 + exit 1 + fi + + # Validate Secret name + if [[ ! "$SECRET_NAME" =~ ^[a-zA-Z0-9-]+$ ]]; then + log "Error: Secret name must be alphanumeric with hyphens" 1 + exit 1 + fi + + # Validate Secret value is not empty + if [ -z "$SECRET_VALUE" ]; then + log "Error: Secret value cannot be empty" 1 + exit 1 + fi + + log "Using Key Vault: $KEYVAULT_NAME" 6 + log "Using Secret Name: $SECRET_NAME" 6 +} + + +############################### +# Main Execution +############################### +main() { + # Trap cleanup on exit + trap 'cleanup; cleanup_resource_group "$RESOURCE_GROUP_NAME" "$ARM_SUBSCRIPTION_ID"' EXIT + + # Setup configuration + setup_configuration "$@" + + # Validate variables + validate_variables + + # Setup Azure and create resource group + setup_azure_with_rg + + # Create tfvars file + create_tfvars_files + + # Run all tests + run_standard_test_sequence +} + +# Check for help flag +case "$1" in + -h|--help) + print_help + exit 0 + ;; +esac + +# Execute main function +main "$@" \ No newline at end of file diff --git a/infra/modules/providers/azure/keyvault-secret/testing/main.tf b/infra/modules/providers/azure/keyvault-secret/testing/main.tf index 906c7a908d31f3ae19e1406b67259d789e234920..b12a47aafde239eefad571ccce999242aad74705 100644 --- a/infra/modules/providers/azure/keyvault-secret/testing/main.tf +++ b/infra/modules/providers/azure/keyvault-secret/testing/main.tf @@ -16,25 +16,44 @@ provider "azurerm" { features {} } -module "resource_group" { - source = "../../resource-group" - - name = "osdu-module" - location = "eastus2" -} +data "azurerm_client_config" "current" {} module "keyvault" { - source = "../../keyvault" - depends_on = [module.resource_group] + source = "../../keyvault" + + keyvault_name = var.keyvault_name + resource_group_name = var.resource_group_name + keyvault_sku = "standard" + public_network_access_enabled = true + keyvault_purge_protection_enabled = false - resource_group_name = module.resource_group.name + resource_tags = { + environment = "testing" + module = "keyvault" + } } module "keyvault-secret" { source = "../" keyvault_id = module.keyvault.keyvault_id - secrets = { - test = "test" - } + secrets = var.secrets + + depends_on = [module.keyvault] +} + +# Variables +variable "keyvault_name" { + type = string + description = "The name of the Key Vault" +} + +variable "resource_group_name" { + type = string + description = "The name of the resource group" +} + +variable "secrets" { + type = map(string) + description = "Key/value pair of keyvault secret names and corresponding secret value" } diff --git a/infra/modules/providers/azure/keyvault-secret/testing/unit_test.go b/infra/modules/providers/azure/keyvault-secret/testing/unit_test.go index 8ef10a8f1c1666eaf5327ec941e02c8133b4f581..7babee93bf7ea7c3c3b3fd771ac4b25a7fedb292 100644 --- a/infra/modules/providers/azure/keyvault-secret/testing/unit_test.go +++ b/infra/modules/providers/azure/keyvault-secret/testing/unit_test.go @@ -16,6 +16,7 @@ package test import ( "encoding/json" + "os" "strings" "testing" @@ -24,13 +25,19 @@ import ( "github.com/microsoft/cobalt/test-harness/infratests" ) -var workspace = "osdu-services-" + strings.ToLower(random.UniqueId()) -var location = "eastus" -var count = 8 +var workspace = "keyvault-secret-" + strings.ToLower(random.UniqueId()) +var count = 4 // One for the Key Vault, one for the secret, one for the data source, and one for the access policy var tfOptions = &terraform.Options{ TerraformDir: "./", Upgrade: true, + Vars: map[string]interface{}{ + "resource_group_name": os.Getenv("RESOURCE_GROUP_NAME"), + "keyvault_name": os.Getenv("KEYVAULT_NAME"), + "secrets": map[string]interface{}{ + os.Getenv("SECRET_NAME"): os.Getenv("SECRET_VALUE"), + }, + }, } func asMap(t *testing.T, jsonString string) map[string]interface{} { @@ -42,9 +49,30 @@ func asMap(t *testing.T, jsonString string) map[string]interface{} { } func TestTemplate(t *testing.T) { + expectedKeyVault := asMap(t, `{ + "sku_name": "standard", + "tags": { + "environment": "testing", + "module": "keyvault" + } + }`) + + expectedSecret := asMap(t, `{ + "name": "`+os.Getenv("SECRET_NAME")+`" + }`) - expectedResult := asMap(t, `{ - "name" : "test" + expectedAccessPolicy := asMap(t, `{ + "tenant_id": "58975fd3-4977-44d0-bea8-37af0baac100", + "object_id": "737f1668-4537-472e-87e9-13cabe43aa1d", + "secret_permissions": [ + "Set", + "Get", + "List", + "Delete", + "Recover", + "Restore", + "Purge" + ] }`) testFixture := infratests.UnitTestFixture{ @@ -54,7 +82,9 @@ func TestTemplate(t *testing.T) { PlanAssertions: nil, ExpectedResourceCount: count, ExpectedResourceAttributeValues: infratests.ResourceDescription{ - "module.keyvault-secret.azurerm_key_vault_secret.secret[0]": expectedResult, + "module.keyvault.azurerm_key_vault.keyvault": expectedKeyVault, + "module.keyvault-secret.azurerm_key_vault_secret.secret[0]": expectedSecret, + "module.keyvault.module.deployment_service_principal_keyvault_access_policies.azurerm_key_vault_access_policy.keyvault[0]": expectedAccessPolicy, }, } diff --git a/infra/modules/providers/azure/keyvault/main.tf b/infra/modules/providers/azure/keyvault/main.tf index d01814e1f741ef6d51e371b754f8faf2007db4fd..d3261834336ab5382b093ebcbb95fef971ec2185 100644 --- a/infra/modules/providers/azure/keyvault/main.tf +++ b/infra/modules/providers/azure/keyvault/main.tf @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=3.90.0, <=4.17.0" + } + } +} + data "azurerm_resource_group" "kv" { name = var.resource_group_name } diff --git a/infra/modules/providers/azure/log-analytics/main.tf b/infra/modules/providers/azure/log-analytics/main.tf index 3b6d6e19967669cf5f0114ba256375efc860079a..02ca3d0db6f6bc744d6953244abbe373f68a4b39 100644 --- a/infra/modules/providers/azure/log-analytics/main.tf +++ b/infra/modules/providers/azure/log-analytics/main.tf @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=3.90.0, <=4.17.0" + } + } +} + data "azurerm_resource_group" "main" { name = var.resource_group_name } diff --git a/infra/modules/providers/azure/log-analytics/testing/main.tf b/infra/modules/providers/azure/log-analytics/testing/main.tf index f9c7cecce1df32383b5d71e55c200354933fb918..97dec03abd5fbea9c0aff3f35a2aebc168e90114 100644 --- a/infra/modules/providers/azure/log-analytics/testing/main.tf +++ b/infra/modules/providers/azure/log-analytics/testing/main.tf @@ -12,14 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -# terraform { -# required_providers { -# azurerm = { -# source = "hashicorp/azurerm" -# version = "=3.90.0" -# } -# } -# } provider "azurerm" { features {} diff --git a/infra/modules/providers/azure/network/main.tf b/infra/modules/providers/azure/network/main.tf index d140ea00152438f96e7cd82206b920a0de3311b1..bc52a809de3fa68f8ea8135e4b446ae3462156bb 100644 --- a/infra/modules/providers/azure/network/main.tf +++ b/infra/modules/providers/azure/network/main.tf @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=3.90.0, <=4.17.0" + } + } +} + data "azurerm_resource_group" "main" { name = var.resource_group_name } diff --git a/infra/modules/providers/azure/network/test.sh b/infra/modules/providers/azure/network/test.sh new file mode 100755 index 0000000000000000000000000000000000000000..100cb6a532f358219c326412889256d663700d7a --- /dev/null +++ b/infra/modules/providers/azure/network/test.sh @@ -0,0 +1,148 @@ +#!/bin/bash + +# Exit on error +set -e + +############################### +# Source Common Functions +############################### +COMMON_LIB="../test-functions.sh" +if [ ! -f "$COMMON_LIB" ]; then + echo "Error: Common library not found at $COMMON_LIB" + exit 1 +fi +source "$COMMON_LIB" + + +############################### +# Script Configuration +############################### +SCRIPT_DIR=$(get_script_dir) +setup_test_directories "$SCRIPT_DIR" + + +############################### +# Required Environment Variables +############################### +validate_azure_credentials + + +############################### +# Optional Variables +############################### +# These can be overridden by setting them before running the script +RESOURCE_GROUP_PREFIX=${RESOURCE_GROUP_PREFIX:-"terraform-test"} +DEFAULT_LOCATION="eastus2" +VNET_NAME=${VNET_NAME:-""} # Will be auto-generated if not provided +ADDRESS_SPACE=${ADDRESS_SPACE:-"10.0.0.0/16"} +SUBNET_NAMES=${SUBNET_NAMES:-"[\"subnet1\", \"subnet2\"]"} +SUBNET_PREFIXES=${SUBNET_PREFIXES:-"[\"10.0.1.0/24\", \"10.0.2.0/24\"]"} + + +############################### +# Help Documentation +############################### +print_help() { + print_common_help "Network" "\n- If not specified, a unique VNET name will be generated\n- Default address space is 10.0.0.0/16\n- Default subnets will be created for testing" +} + + +############################### +# Required Module Functions +############################### +setup_configuration() { + # Setup base configuration first + setup_base_configuration "$RESOURCE_GROUP_PREFIX" "$DEFAULT_LOCATION" "$@" + + # Generate VNET name if not provided + if [ -z "$VNET_NAME" ]; then + VNET_NAME=$(generate_unique_name "" "vnet") + fi + + # Export additional variables for Go tests + export VNET_NAME + export ADDRESS_SPACE + export SUBNET_NAMES + export SUBNET_PREFIXES +} + +create_tfvars_files() { + local tfvars_content=" +name = \"$VNET_NAME\" +resource_group_name = \"$RESOURCE_GROUP_NAME\" +address_space = \"$ADDRESS_SPACE\" + +subnets = { + subnet1 = { + name = \"subnet1\" + address_prefixes = [\"10.0.1.0/24\"] + service_endpoints = [\"Microsoft.Storage\"] + } + subnet2 = { + name = \"subnet2\" + address_prefixes = [\"10.0.2.0/24\"] + service_endpoints = [\"Microsoft.Storage\"] + } +} + +resource_tags = { + osdu = \"module\" +}" + create_base_tfvars_files "$tfvars_content" +} + + +############################### +# Optional Module Functions +############################### +validate_variables() { + validate_base_variables + + # Validate VNET specific variables + if [[ ! "$VNET_NAME" =~ ^[a-zA-Z0-9-]+$ ]]; then + log "Error: VNET name must be alphanumeric with hyphens" 1 + exit 1 + fi + + # Validate address space format + if [[ ! "$ADDRESS_SPACE" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then + log "Error: Invalid address space format. Expected format: x.x.x.x/x" 1 + exit 1 + fi + + log "Using VNET: $VNET_NAME" 6 + log "Using Address Space: $ADDRESS_SPACE" 6 +} + + +############################### +# Main Execution +############################### +main() { + # Trap cleanup on exit + trap 'cleanup; cleanup_resource_group "$RESOURCE_GROUP_NAME" "$ARM_SUBSCRIPTION_ID"' EXIT + + # Setup configuration + setup_configuration "$@" + + # Validate variables + validate_variables + + # Setup + setup_azure_with_rg + create_tfvars_files + + # Run all tests + run_standard_test_sequence +} + +# Check for help flag +case "$1" in + -h|--help) + print_help + exit 0 + ;; +esac + +# Execute main function +main "$@" \ No newline at end of file diff --git a/infra/modules/providers/azure/network/testing/main.tf b/infra/modules/providers/azure/network/testing/main.tf index 80b0a199713fd68ed48e6fc96c7f0b78b516d62f..e8386d7ac10fb56638bc3fd9402520515d04a036 100644 --- a/infra/modules/providers/azure/network/testing/main.tf +++ b/infra/modules/providers/azure/network/testing/main.tf @@ -16,28 +16,63 @@ provider "azurerm" { features {} } -module "resource_group" { - source = "../../resource-group" +# Variables +variable "name" { + type = string + description = "The name of the virtual network" +} - name = "osdu-module" - location = "eastus2" +variable "resource_group_name" { + type = string + description = "The name of the resource group" } +variable "location" { + type = string + description = "The location of the virtual network" + default = "eastus2" +} -module "network" { - source = "../" - depends_on = [module.resource_group] - - name = "osdu-module-vnet-${module.resource_group.random}" - resource_group_name = module.resource_group.name - address_space = "10.0.1.0/24" - dns_servers = ["8.8.8.8"] - subnet_prefixes = ["10.0.1.0/26", "10.0.1.64/26", "10.0.1.128/26", "10.0.1.192/27", "10.0.1.224/28"] - subnet_names = ["Web-Tier", "App-Tier", "Data-Tier", "Mgmt-Tier", "GatewaySubnet"] - - # Tags - resource_tags = { +variable "address_space" { + type = string + description = "The address space for the virtual network" + default = "10.0.1.0/24" +} + +variable "dns_servers" { + type = list(string) + description = "The DNS servers to be used with the virtual network" + default = ["8.8.8.8"] +} + +variable "subnet_prefixes" { + type = list(string) + description = "The address prefixes to use for the subnets" + default = ["10.0.1.0/26", "10.0.1.64/26", "10.0.1.128/26", "10.0.1.192/27", "10.0.1.224/28"] +} + +variable "subnet_names" { + type = list(string) + description = "The names of the subnets" + default = ["Web-Tier", "App-Tier", "Data-Tier", "Mgmt-Tier", "GatewaySubnet"] +} + +variable "resource_tags" { + type = map(string) + description = "Resource tags" + default = { osdu = "module" } +} + +module "network" { + source = "../" + name = var.name + resource_group_name = var.resource_group_name + address_space = var.address_space + dns_servers = var.dns_servers + subnet_prefixes = var.subnet_prefixes + subnet_names = var.subnet_names + resource_tags = var.resource_tags } diff --git a/infra/modules/providers/azure/network/testing/unit_test.go b/infra/modules/providers/azure/network/testing/unit_test.go index 7926a5f16cab88b568140297d10af329d5780529..3effd345c1c874fa87796294a96424809534130e 100644 --- a/infra/modules/providers/azure/network/testing/unit_test.go +++ b/infra/modules/providers/azure/network/testing/unit_test.go @@ -16,6 +16,7 @@ package test import ( "encoding/json" + "strings" "testing" "github.com/gruntwork-io/terratest/modules/random" @@ -23,13 +24,20 @@ import ( "github.com/microsoft/cobalt/test-harness/infratests" ) -var name = "network-" -var location = "eastus" -var count = 9 +var workspace = "network-" + strings.ToLower(random.UniqueId()) +var count = 6 // One for the vnet and five for subnets, plus one data source var tfOptions = &terraform.Options{ TerraformDir: "./", Upgrade: true, + Vars: map[string]interface{}{ + "name": "vnet" + strings.ToLower(random.UniqueId()), + "resource_group_name": "osdu-module", // Fixed name for unit tests + "address_space": "10.0.1.0/24", + "dns_servers": []string{"8.8.8.8"}, + "subnet_prefixes": []string{"10.0.1.0/26", "10.0.1.64/26", "10.0.1.128/26", "10.0.1.192/27", "10.0.1.224/28"}, + "subnet_names": []string{"Web-Tier", "App-Tier", "Data-Tier", "Mgmt-Tier", "GatewaySubnet"}, + }, } func asMap(t *testing.T, jsonString string) map[string]interface{} { @@ -41,57 +49,29 @@ func asMap(t *testing.T, jsonString string) map[string]interface{} { } func TestTemplate(t *testing.T) { - - expectedResult := asMap(t, `{ + expectedVNet := asMap(t, `{ "address_space": ["10.0.1.0/24"], - "dns_servers": ["8.8.8.8"] - }`) - - expectedSubnet0 := asMap(t, `{ - "name": "Web-Tier", - "address_prefixes": [ - "10.0.1.0/26" - ] - }`) - - expectedSubnet1 := asMap(t, `{ - "address_prefixes": [ - "10.0.1.64/26" - ] - }`) - - expectedSubnet2 := asMap(t, `{ - "address_prefixes": [ - "10.0.1.128/26" - ] - }`) - - expectedSubnet3 := asMap(t, `{ - "address_prefixes": [ - "10.0.1.192/27" - ] + "dns_servers": ["8.8.8.8"], + "tags": { + "osdu": "module" + } }`) - expectedSubnet4 := asMap(t, `{ - "name": "GatewaySubnet", - "address_prefixes": [ - "10.0.1.224/28" - ] + expectedSubnet := asMap(t, `{ + "address_prefixes": ["10.0.1.0/26"], + "private_endpoint_network_policies": "Disabled", + "private_link_service_network_policies_enabled": true }`) testFixture := infratests.UnitTestFixture{ GoTest: t, TfOptions: tfOptions, - Workspace: name + random.UniqueId(), + Workspace: workspace, PlanAssertions: nil, ExpectedResourceCount: count, ExpectedResourceAttributeValues: infratests.ResourceDescription{ - "module.network.azurerm_virtual_network.main": expectedResult, - "module.network.azurerm_subnet.main[0]": expectedSubnet0, - "module.network.azurerm_subnet.main[1]": expectedSubnet1, - "module.network.azurerm_subnet.main[2]": expectedSubnet2, - "module.network.azurerm_subnet.main[3]": expectedSubnet3, - "module.network.azurerm_subnet.main[4]": expectedSubnet4, + "module.network.azurerm_virtual_network.main": expectedVNet, + "module.network.azurerm_subnet.main[0]": expectedSubnet, }, } diff --git a/infra/modules/providers/azure/resource-group/main.tf b/infra/modules/providers/azure/resource-group/main.tf index 4cfa691ac1127ff8bb1c3a9be14682488e25a515..dd664bfc29527a73151c346ef9e211f6257daf53 100644 --- a/infra/modules/providers/azure/resource-group/main.tf +++ b/infra/modules/providers/azure/resource-group/main.tf @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">=3.90.0, <=4.17.0" + } + } +} + resource "azurerm_resource_group" "main" { name = var.name location = var.location diff --git a/infra/modules/providers/azure/service-bus/main.tf b/infra/modules/providers/azure/service-bus/main.tf index 5af31ccb1024a05ee14ffabbc239d26d7b95a9da..353619d28474367e1c34e7ba0883ff0fd3f1adc1 100644 --- a/infra/modules/providers/azure/service-bus/main.tf +++ b/infra/modules/providers/azure/service-bus/main.tf @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "=3.90.0" + } + } +} + locals { authorization_rules = [ for rule in var.authorization_rules : merge({ diff --git a/infra/modules/providers/azure/service-bus/test.sh b/infra/modules/providers/azure/service-bus/test.sh new file mode 100755 index 0000000000000000000000000000000000000000..6d73129a29d311b3cac7b6a031d7080bbf9fe264 --- /dev/null +++ b/infra/modules/providers/azure/service-bus/test.sh @@ -0,0 +1,163 @@ +#!/bin/bash + +# Exit on error +set -e + +############################### +# Source Common Functions +############################### +COMMON_LIB="../test-functions.sh" +if [ ! -f "$COMMON_LIB" ]; then + echo "Error: Common library not found at $COMMON_LIB" + exit 1 +fi +source "$COMMON_LIB" + + +############################### +# Script Configuration +############################### +SCRIPT_DIR=$(get_script_dir) +setup_test_directories "$SCRIPT_DIR" + + +############################### +# Required Environment Variables +############################### +validate_azure_credentials + + +############################### +# Optional Variables +############################### +# These can be overridden by setting them before running the script +RESOURCE_GROUP_PREFIX=${RESOURCE_GROUP_PREFIX:-"terraform-test"} +DEFAULT_LOCATION="eastus2" +SERVICE_BUS_NAME=${SERVICE_BUS_NAME:-""} # Will be auto-generated if not provided +SERVICE_BUS_SKU=${SERVICE_BUS_SKU:-"Standard"} # Default to Standard SKU + + +############################### +# Help Documentation +############################### +print_help() { + print_common_help "Service Bus" "\n- If not specified, a unique Service Bus name will be generated\n- Default SKU is 'Standard'\n- Topics and subscriptions will be created for testing" +} + + +############################### +# Required Module Functions +############################### +setup_configuration() { + # Setup base configuration first + setup_base_configuration "$RESOURCE_GROUP_PREFIX" "$DEFAULT_LOCATION" "$@" + + # Generate Service Bus name if not provided + if [ -z "$SERVICE_BUS_NAME" ]; then + SERVICE_BUS_NAME=$(generate_unique_name "" "sb") + fi + + # Export additional variables for Go tests + export SERVICE_BUS_NAME + export SERVICE_BUS_SKU +} + +create_tfvars_files() { + local tfvars_content=" +name = \"$SERVICE_BUS_NAME\" +resource_group_name = \"$RESOURCE_GROUP_NAME\" +sku = \"$SERVICE_BUS_SKU\" + +topics = [ + { + name = \"topic_test\" + enable_partitioning = true + max_size = 5120 + enable_duplicate_detection = false + enable_ordering = false + status = \"Active\" + auto_delete_on_idle = \"P10675199DT2H48M5.4775807S\" + default_message_ttl = \"PT12H\" + subscriptions = [ + { + name = \"sub_test\" + max_delivery_count = 1 + lock_duration = \"PT5M\" + forward_to = \"\" + enable_dead_lettering_on_message_expiration = true + status = \"Active\" + } + ] + } +] + +resource_tags = { + osdu = \"module\" +}" + create_base_tfvars_files "$tfvars_content" +} + + +############################### +# Optional Module Functions +############################### +validate_variables() { + validate_base_variables + + # Validate Service Bus specific variables + if [[ ! "$SERVICE_BUS_NAME" =~ ^[a-zA-Z0-9-]+$ ]]; then + log "Error: Service Bus name must be alphanumeric with hyphens" 1 + exit 1 + fi + + # Validate SKU + local valid_skus=("Basic" "Standard" "Premium") + local sku_valid=false + for valid_sku in "${valid_skus[@]}"; do + if [ "$SERVICE_BUS_SKU" == "$valid_sku" ]; then + sku_valid=true + break + fi + done + + if [ "$sku_valid" != true ]; then + log "Error: Invalid SKU. Must be one of: ${valid_skus[*]}" 1 + exit 1 + fi + + log "Using Service Bus: $SERVICE_BUS_NAME" 6 + log "Using SKU: $SERVICE_BUS_SKU" 6 +} + + +############################### +# Main Execution +############################### +main() { + # Trap cleanup on exit + trap 'cleanup; cleanup_resource_group "$RESOURCE_GROUP_NAME" "$ARM_SUBSCRIPTION_ID"' EXIT + + # Setup configuration + setup_configuration "$@" + + # Validate variables + validate_variables + + # Setup + setup_azure_with_rg + create_tfvars_files + + # Run all tests + run_standard_test_sequence +} + +# Check for help flag +case "$1" in + -h|--help) + print_help + exit 0 + ;; +esac + +# Execute main function +main "$@" \ No newline at end of file diff --git a/infra/modules/providers/azure/service-bus/testing/main.tf b/infra/modules/providers/azure/service-bus/testing/main.tf index 00bcdb69320670acdff0244a41aed0c4e6e122b0..10e44e132dbced18646fd711c6969c4a0f30891d 100644 --- a/infra/modules/providers/azure/service-bus/testing/main.tf +++ b/infra/modules/providers/azure/service-bus/testing/main.tf @@ -16,37 +16,141 @@ provider "azurerm" { features {} } -module "resource_group" { - source = "../../resource-group" - - name = "osdu-module" - location = "eastus2" -} - module "service_bus" { - source = "../" - depends_on = [module.resource_group] + source = "../" - name = "osdu-module-service-bus-${module.resource_group.random}" - resource_group_name = module.resource_group.name + name = var.name + resource_group_name = var.resource_group_name sku = "Standard" + resource_tags = { + osdu = "module" + } topics = [ { - name = "topic_test" - enable_partitioning = true + name = "topic_test" + enable_partitioning = true + max_size = 5120 + enable_duplicate_detection = false + enable_ordering = false + status = "Active" + auto_delete_on_idle = "P10675199DT2H48M5.4775807S" + default_message_ttl = "PT12H" subscriptions = [ { - name = "sub_test" - max_delivery_count = 1 - lock_duration = "PT5M" - forward_to = "" + name = "sub_test" + max_delivery_count = 1 + lock_duration = "PT5M" + forward_to = "" + status = "Active" + enable_dead_lettering_on_message_expiration = true } ] } ] +} - resource_tags = { - source = "terraform", +# Variables +variable "name" { + type = string + description = "The name of the Service Bus namespace" +} + +variable "resource_group_name" { + type = string + description = "The name of the resource group" +} + +variable "location" { + type = string + description = "The location of the Service Bus namespace" + default = "eastus2" +} + +variable "sku" { + type = string + description = "The SKU of the Service Bus namespace" + default = "Standard" +} + +variable "topics" { + type = list(object({ + name = string + enable_partitioning = bool + max_size = number + enable_duplicate_detection = bool + enable_ordering = bool + status = string + auto_delete_on_idle = string + default_message_ttl = string + subscriptions = list(object({ + name = string + max_delivery_count = number + lock_duration = string + forward_to = string + status = string + enable_dead_lettering_on_message_expiration = bool + })) + })) + description = "List of topics and their subscriptions" + default = [ + { + name = "topic_test" + enable_partitioning = true + max_size = 5120 + enable_duplicate_detection = false + enable_ordering = false + status = "Active" + auto_delete_on_idle = "P10675199DT2H48M5.4775807S" + default_message_ttl = "PT12H" + subscriptions = [ + { + name = "sub_test" + max_delivery_count = 1 + lock_duration = "PT5M" + forward_to = "" + status = "Active" + enable_dead_lettering_on_message_expiration = true + } + ] + } + ] +} + +variable "resource_tags" { + type = map(string) + description = "Resource tags" + default = { + osdu = "module" } } + +output "servicebus_namespace_id" { + value = module.service_bus.id +} + +output "servicebus_namespace_name" { + value = module.service_bus.name +} + +output "servicebus_namespace_default_connection_string" { + value = module.service_bus.default_connection_string + sensitive = true +} + +output "servicebus_namespace_authorization_rules" { + value = module.service_bus.authorization_rules + sensitive = true +} + +output "topics" { + value = module.service_bus.topics +} + +output "queues" { + value = module.service_bus.queues +} + +output "topics_map" { + value = module.service_bus.topicsmap +} diff --git a/infra/modules/providers/azure/service-bus/testing/unit_test.go b/infra/modules/providers/azure/service-bus/testing/unit_test.go index 5752786d6ea886a658ad89eb24fdc9892f1f79f8..02f7c5322a5481be7ed86aef698b2fb5ac2adfec 100644 --- a/infra/modules/providers/azure/service-bus/testing/unit_test.go +++ b/infra/modules/providers/azure/service-bus/testing/unit_test.go @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -package unit +package test -//might be package test import ( "encoding/json" + "os" "strings" "testing" @@ -25,12 +25,37 @@ import ( "github.com/microsoft/cobalt/test-harness/infratests" ) -var workspace = "osdu-services-" + strings.ToLower(random.UniqueId()) -var count = 6 +var workspace = "servicebus-" + strings.ToLower(random.UniqueId()) +var count = 3 // One for namespace, one for topic, one for subscription + +var subscription = map[string]interface{}{ + "name": "sub_test", + "max_delivery_count": 1, + "lock_duration": "PT5M", + "forward_to": "", + "enable_batched_operations": true, + "enable_session": false, + "enable_dead_lettering_on_message_expiration": true, +} + +var topic = map[string]interface{}{ + "name": "topic_test", + "enable_partitioning": true, + "enable_batched_operations": true, + "enable_express": false, + "max_size": 5120, + "enable_duplicate_detection": false, + "enable_ordering": false, + "subscriptions": []interface{}{subscription}, +} var tfOptions = &terraform.Options{ TerraformDir: "./", - Upgrade: false, + Upgrade: true, + Vars: map[string]interface{}{ + "name": "sb" + strings.ToLower(random.UniqueId()), + "resource_group_name": os.Getenv("RESOURCE_GROUP_NAME"), + }, } // helper function to parse blocks of JSON into a generic Go map @@ -43,27 +68,33 @@ func asMap(t *testing.T, jsonString string) map[string]interface{} { } func TestTemplate(t *testing.T) { + expectedSBNamespace := asMap(t, `{ + "capacity": 0, + "sku": "Standard", + "tags": { + "osdu": "module" + } + }`) - expectedSBNamespace := map[string]interface{}{ - "capacity": 0.0, - "sku": "Standard", - "tags": map[string]interface{}{ - "source": "terraform", - }, - } - - expectedSubscription := map[string]interface{}{ - "name": "sub_test", - "max_delivery_count": 1.0, - "lock_duration": "PT5M", - "dead_lettering_on_filter_evaluation_error": true, - } - - expectedTopic := map[string]interface{}{ - "name": "topic_test", + expectedTopic := asMap(t, `{ + "name": "topic_test", "enable_partitioning": true, - "status": "Active", - } + "max_size_in_megabytes": 5120, + "requires_duplicate_detection": false, + "support_ordering": false, + "status": "Active", + "auto_delete_on_idle": "P10675199DT2H48M5.4775807S", + "default_message_ttl": "PT12H" + }`) + + expectedSubscription := asMap(t, `{ + "name": "sub_test", + "max_delivery_count": 1, + "lock_duration": "PT5M", + "status": "Active", + "client_scoped_subscription_enabled": false, + "dead_lettering_on_filter_evaluation_error": true + }`) testFixture := infratests.UnitTestFixture{ GoTest: t, diff --git a/infra/modules/providers/azure/storage-account/testing/main.tf b/infra/modules/providers/azure/storage-account/testing/main.tf index 1c55217352b970503c725ff838dbed57ac5c0a13..c9539be47c7e5f7eeeb412acce087f346098bb8c 100644 --- a/infra/modules/providers/azure/storage-account/testing/main.tf +++ b/infra/modules/providers/azure/storage-account/testing/main.tf @@ -1,11 +1,16 @@ -terraform { - required_providers { - azurerm = { - source = "hashicorp/azurerm" - version = "=3.90.0" - } - } -} +// Copyright © Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. provider "azurerm" { features {}