зеркало из https://github.com/microsoft/cobalt.git
Adding unit and integration tests for azure-simple template. (#142)
This commit is contained in:
Родитель
6a872abb9e
Коммит
a65bbe2214
|
@ -46,6 +46,8 @@ variables:
|
|||
value: 'infra-test-harness'
|
||||
- name: TERM
|
||||
value: 'xterm-color'
|
||||
- name: TF_WARN_OUTPUT_ERRORS
|
||||
value: 1
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
|
@ -91,6 +93,7 @@ steps:
|
|||
-e TF_VAR_remote_state_account=$(TF_VAR_remote_state_account) \
|
||||
-e TF_VAR_remote_state_container=$(TF_VAR_remote_state_container) \
|
||||
-e ARM_ACCESS_KEY=$(ARM-ACCESS-KEY) \
|
||||
-e TF_WARN_OUTPUT_ERRORS=$(TF_WARN_OUTPUT_ERRORS) \
|
||||
--rm $BUILD_TEST_RUN_IMAGE:$BUILD_BUILDID
|
||||
fi
|
||||
displayName: 'run the test harness container which deploys, validates and tears down modified terraform templates'
|
||||
displayName: 'Run the test harness container which deploys, validates and tears down modified terraform templates'
|
||||
|
|
|
@ -13,7 +13,7 @@ data "azurerm_app_service_plan" "appsvc" {
|
|||
}
|
||||
|
||||
resource "azurerm_app_service" "appsvc" {
|
||||
name = "${element(keys(var.app_service_name), count.index)}-${terraform.workspace}"
|
||||
name = "${lower(element(keys(var.app_service_name), count.index))}-${lower(terraform.workspace)}"
|
||||
resource_group_name = "${data.azurerm_resource_group.appsvc.name}"
|
||||
location = "${data.azurerm_resource_group.appsvc.location}"
|
||||
app_service_plan_id = "${data.azurerm_app_service_plan.appsvc.id}"
|
||||
|
@ -41,7 +41,7 @@ resource "azurerm_app_service" "appsvc" {
|
|||
|
||||
resource "azurerm_app_service_slot" "appsvc_staging_slot" {
|
||||
name = "staging"
|
||||
app_service_name = "${element(keys(var.app_service_name), count.index)}-${terraform.workspace}"
|
||||
app_service_name = "${lower(element(keys(var.app_service_name), count.index))}-${lower(terraform.workspace)}"
|
||||
count = "${length(keys(var.app_service_name))}"
|
||||
location = "${data.azurerm_resource_group.appsvc.location}"
|
||||
resource_group_name = "${data.azurerm_resource_group.appsvc.name}"
|
||||
|
@ -55,7 +55,7 @@ resource "azurerm_template_deployment" "access_restriction" {
|
|||
resource_group_name = "${data.azurerm_resource_group.appsvc.name}"
|
||||
|
||||
parameters = {
|
||||
service_name = "${element(keys(var.app_service_name), count.index)}-${terraform.workspace}"
|
||||
service_name = "${lower(element(keys(var.app_service_name), count.index))}-${lower(terraform.workspace)}"
|
||||
vnet_subnet_id = "${var.vnet_subnet_id}"
|
||||
access_restriction_name = "${local.access_restriction_name}"
|
||||
access_restriction_description = "${local.access_restriction_description}"
|
||||
|
|
|
@ -44,7 +44,14 @@ DOT_ENV=<path to your .env file>
|
|||
export $(cat $DOT_ENV | xargs)
|
||||
```
|
||||
|
||||
2. Execute the following commands to set up your terraform environment
|
||||
2. Execute the following command to configure your local Azure CLI. **Note**: This is a temporary measure until we are able to break the dependency on the Azure CLI. This work is being tracked as a part of [Issue 153](https://github.com/microsoft/cobalt/issues/153)
|
||||
|
||||
```bash
|
||||
# This logs your local Azure CLI in using the configured service principal.
|
||||
az login --service-principal -u $ARM_CLIENT_ID -p $ARM_CLIENT_SECRET --tenant $ARM_TENANT_ID
|
||||
```
|
||||
|
||||
3. Execute the following commands to set up your terraform environment
|
||||
```bash
|
||||
# This configures terraform to leverage a remote backend that will help you and your
|
||||
# team keep consistent state
|
||||
|
@ -55,7 +62,7 @@ terraform init -backend-config "storage_account_name=${TF_VAR_remote_state_accou
|
|||
terraform workspace new $USER || terraform workspace select $USER
|
||||
```
|
||||
|
||||
3. Create a new terraform template directory and add a `main.tf` file. Here's a sample that uses the `azure-simple` template.
|
||||
4. Create a new terraform template directory and add a `main.tf` file. Here's a sample that uses the `azure-simple` template.
|
||||
|
||||
```HCL
|
||||
module "azure-simple" {
|
||||
|
@ -66,7 +73,7 @@ module "azure-simple" {
|
|||
}
|
||||
```
|
||||
|
||||
4. Execute the following commands to orchestrate a deployment
|
||||
5. Execute the following commands to orchestrate a deployment
|
||||
|
||||
```bash
|
||||
# See what terraform will try to deploy without actually deploying
|
||||
|
@ -74,7 +81,11 @@ terraform plan
|
|||
|
||||
# Execute a deployment
|
||||
terraform apply
|
||||
```
|
||||
|
||||
6. Optionally execute the following command to teardown your deployment and delete your resources
|
||||
|
||||
```bash
|
||||
# Destroy resources and tear down deployment. Only do this if you want to destroy your deployment.
|
||||
terraform destroy
|
||||
```
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
locals {
|
||||
prefix = "${var.name}-${terraform.workspace}"
|
||||
prefix_short = "${substr(local.prefix, 0, 20)}"
|
||||
prefix = "${lower(var.name)}-${lower(terraform.workspace)}"
|
||||
prefix_short = "${format("%.20s", local.prefix)}"
|
||||
tm_profile_name = "${local.prefix}-tf"
|
||||
vnet_name = "${local.prefix}-vnet"
|
||||
tm_endpoint_name = "${var.resource_group_location}_${var.name}"
|
||||
|
|
|
@ -9,3 +9,7 @@ output "app_gateway_health_probe_backend_address" {
|
|||
output "tm_fqdn" {
|
||||
value = "${module.traffic_manager.public_pip_fqdn}"
|
||||
}
|
||||
|
||||
output "public_cert" {
|
||||
value = "${module.keyvault_certificate.public_cert}"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gruntwork-io/terratest/modules/random"
|
||||
"github.com/gruntwork-io/terratest/modules/shell"
|
||||
"github.com/gruntwork-io/terratest/modules/terraform"
|
||||
"github.com/microsoft/cobalt/test-harness/infratests"
|
||||
)
|
||||
|
||||
var name = "azsimp"
|
||||
var region = "eastus"
|
||||
var workspace = "azsimp-" + strings.ToLower(random.UniqueId())
|
||||
|
||||
var tfOptions = &terraform.Options{
|
||||
TerraformDir: "../../",
|
||||
Upgrade: true,
|
||||
Vars: map[string]interface{}{
|
||||
"name": name,
|
||||
"resource_group_location": region,
|
||||
},
|
||||
BackendConfig: map[string]interface{}{
|
||||
"storage_account_name": os.Getenv("TF_VAR_remote_state_account"),
|
||||
"container_name": os.Getenv("TF_VAR_remote_state_container"),
|
||||
},
|
||||
}
|
||||
|
||||
// Validates that the front-end endpoint is available via HTTPS using SSL
|
||||
func verifyHTTPSSuccessOnFrontEnd(goTest *testing.T, output infratests.TerraformOutput) {
|
||||
frontEndHost := output["tm_fqdn"].(string)
|
||||
httpClient := configureHTTPSClient(output)
|
||||
|
||||
response, err := httpClient.Get("https://" + frontEndHost)
|
||||
if err != nil {
|
||||
goTest.Fatal(err)
|
||||
}
|
||||
|
||||
if response.StatusCode != 200 {
|
||||
goTest.Fatal(fmt.Errorf("expected status 200 but got %d", response.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
// Validates that the front-end endpoint is not available via HTTP
|
||||
func verifyHTTPFailsOnFrontEnd(goTest *testing.T, output infratests.TerraformOutput) {
|
||||
frontEndHost := output["tm_fqdn"].(string)
|
||||
verifyRequestFails(goTest, frontEndHost, "https", &http.Client{})
|
||||
}
|
||||
|
||||
// Validates that the back-end endpoint is not available via HTTPS
|
||||
func verifyHTTPSFailsOnBackEnd(goTest *testing.T, output infratests.TerraformOutput) {
|
||||
backEndHost := output["app_gateway_health_probe_backend_address"].(string)
|
||||
verifyRequestFails(goTest, backEndHost, "https", configureHTTPSClient(output))
|
||||
}
|
||||
|
||||
// Validates that the back-end endpoint is not available via HTTP
|
||||
func verifyHTTPFailsOnBackEnd(goTest *testing.T, output infratests.TerraformOutput) {
|
||||
backEndHost := output["app_gateway_health_probe_backend_address"].(string)
|
||||
verifyRequestFails(goTest, backEndHost, "http", &http.Client{})
|
||||
}
|
||||
|
||||
// Configures an HTTPS client for the web app
|
||||
func configureHTTPSClient(output infratests.TerraformOutput) *http.Client {
|
||||
cert := output["public_cert"].(string)
|
||||
backEndHost := output["app_gateway_health_probe_backend_address"].(string)
|
||||
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AppendCertsFromPEM([]byte("-----BEGIN CERTIFICATE-----\n" + cert + "\n-----END CERTIFICATE-----"))
|
||||
|
||||
return &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
ServerName: backEndHost,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Validates that a request fails
|
||||
func verifyRequestFails(goTest *testing.T, host string, protocol string, client *http.Client) {
|
||||
response, err := client.Get(protocol + "://" + host)
|
||||
if err != nil || response.StatusCode != 200 {
|
||||
return
|
||||
}
|
||||
|
||||
goTest.Fatal(fmt.Errorf("expected HTTP request to fail but got status code %d", response.StatusCode))
|
||||
}
|
||||
|
||||
func TestAzureSimple(t *testing.T) {
|
||||
azureLogin(t)
|
||||
testFixture := infratests.IntegrationTestFixture{
|
||||
GoTest: t,
|
||||
TfOptions: tfOptions,
|
||||
Workspace: workspace,
|
||||
ExpectedTfOutputCount: 4,
|
||||
ExpectedTfOutput: infratests.TerraformOutput{
|
||||
"tm_fqdn": name + "-" + workspace + "-ip-dns." + region + ".cloudapp.azure.com",
|
||||
"app_gateway_health_probe_backend_address": "cobalt-backend-api-" + workspace + ".azurewebsites.net",
|
||||
"app_gateway_health_probe_backend_status": "Healthy",
|
||||
},
|
||||
TfOutputAssertions: []infratests.TerraformOutputValidation{
|
||||
verifyHTTPSSuccessOnFrontEnd,
|
||||
verifyHTTPFailsOnFrontEnd,
|
||||
verifyHTTPSFailsOnBackEnd,
|
||||
verifyHTTPFailsOnBackEnd,
|
||||
},
|
||||
}
|
||||
infratests.RunIntegrationTests(&testFixture)
|
||||
}
|
||||
|
||||
func azureLogin(t *testing.T) {
|
||||
shell.RunCommand(t, shell.Command{
|
||||
Command: "az",
|
||||
Args: []string{
|
||||
"login",
|
||||
"--service-principal",
|
||||
"-u",
|
||||
os.Getenv("ARM_CLIENT_ID"),
|
||||
"-p",
|
||||
os.Getenv("ARM_CLIENT_SECRET"),
|
||||
"--tenant",
|
||||
os.Getenv("ARM_TENANT_ID"),
|
||||
},
|
||||
})
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gruntwork-io/terratest/modules/terraform"
|
||||
"github.com/microsoft/cobalt/test-harness/infratests"
|
||||
)
|
||||
|
||||
var region = "eastus"
|
||||
var workspace = "azsimple"
|
||||
|
||||
var tf_options = &terraform.Options{
|
||||
TerraformDir: "../../",
|
||||
Upgrade: true,
|
||||
Vars: map[string]interface{}{
|
||||
"resource_group_location": region,
|
||||
},
|
||||
BackendConfig: map[string]interface{}{
|
||||
"storage_account_name": os.Getenv("TF_VAR_remote_state_account"),
|
||||
"container_name": os.Getenv("TF_VAR_remote_state_container"),
|
||||
},
|
||||
}
|
||||
|
||||
func TestTemplate(t *testing.T) {
|
||||
testFixture := infratests.UnitTestFixture{
|
||||
GoTest: t,
|
||||
TfOptions: tf_options,
|
||||
Workspace: workspace,
|
||||
PlanAssertions: nil,
|
||||
ExpectedResourceCount: 28,
|
||||
ExpectedResourceAttributeValues: infratests.ResourceDescription{
|
||||
"azurerm_resource_group.svcplan": infratests.AttributeValueMapping{
|
||||
"location": region,
|
||||
"name": "cobalt-az-simple-" + workspace,
|
||||
},
|
||||
"azurerm_app_service_slot.appsvc_staging_slot": infratests.AttributeValueMapping{
|
||||
"name": "staging",
|
||||
},
|
||||
"azurerm_app_service.appsvc": infratests.AttributeValueMapping{
|
||||
"name": "cobalt-backend-api-" + workspace,
|
||||
"site_config.0.linux_fx_version": "DOCKER|msftcse/cobalt-azure-simple:0.1",
|
||||
},
|
||||
"data.azurerm_resource_group.appgateway": infratests.AttributeValueMapping{
|
||||
"name": "cobalt-az-simple-" + workspace,
|
||||
},
|
||||
"azurerm_application_gateway.appgateway": infratests.AttributeValueMapping{
|
||||
"authentication_certificate.0.name": "gateway-public-key",
|
||||
"frontend_port.0.port": "443",
|
||||
"http_listener.0.protocol": "Https",
|
||||
"backend_http_settings.0.port": "443",
|
||||
"backend_http_settings.0.protocol": "Https",
|
||||
"probe.0.protocol": "Https",
|
||||
"probe.0.timeout": "30",
|
||||
"probe.0.unhealthy_threshold": "3",
|
||||
"sku.0.capacity": "2",
|
||||
"sku.0.name": "WAF_Medium",
|
||||
"sku.0.tier": "WAF",
|
||||
},
|
||||
"data.azurerm_resource_group.appinsights": infratests.AttributeValueMapping{
|
||||
"name": "cobalt-az-simple-" + workspace,
|
||||
},
|
||||
"azurerm_monitor_autoscale_setting.app_service_auto_scale": infratests.AttributeValueMapping{
|
||||
"enabled": "true",
|
||||
"name": "cobalt-az-simple-" + workspace + "-sp-autoscale",
|
||||
"notification.0.email.0.send_to_subscription_administrator": "true",
|
||||
"notification.0.email.0.send_to_subscription_co_administrator": "true",
|
||||
"profile.0.rule.0.metric_trigger.0.metric_name": "CpuPercentage",
|
||||
"profile.0.rule.0.metric_trigger.0.operator": "GreaterThan",
|
||||
"profile.0.rule.0.metric_trigger.0.statistic": "Average",
|
||||
"profile.0.rule.0.metric_trigger.0.threshold": "70",
|
||||
"profile.0.rule.0.metric_trigger.0.time_aggregation": "Average",
|
||||
"profile.0.rule.0.metric_trigger.0.time_grain": "PT1M",
|
||||
"profile.0.rule.0.metric_trigger.0.time_window": "PT5M",
|
||||
"profile.0.rule.0.scale_action.0.cooldown": "PT10M",
|
||||
"profile.0.rule.0.scale_action.0.direction": "Increase",
|
||||
"profile.0.rule.0.scale_action.0.type": "ChangeCount",
|
||||
"profile.0.rule.0.scale_action.0.value": "1",
|
||||
"profile.0.rule.1.metric_trigger.0.metric_name": "CpuPercentage",
|
||||
"profile.0.rule.1.metric_trigger.0.operator": "GreaterThan",
|
||||
"profile.0.rule.1.metric_trigger.0.statistic": "Average",
|
||||
"profile.0.rule.1.metric_trigger.0.threshold": "25",
|
||||
"profile.0.rule.1.metric_trigger.0.time_aggregation": "Average",
|
||||
"profile.0.rule.1.metric_trigger.0.time_grain": "PT1M",
|
||||
"profile.0.rule.1.metric_trigger.0.time_window": "PT5M",
|
||||
"profile.0.rule.1.scale_action.0.cooldown": "PT1M",
|
||||
"profile.0.rule.1.scale_action.0.direction": "Decrease",
|
||||
"profile.0.rule.1.scale_action.0.type": "ChangeCount",
|
||||
"profile.0.rule.1.scale_action.0.value": "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
infratests.RunUnitTests(&testFixture)
|
||||
}
|
Загрузка…
Ссылка в новой задаче