Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add datafactory terratest module #1306

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions examples/azure/terraform-azure-datafactory-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Terraform Azure Data Factory Example

This folder contains a Terraform module that deploys resources in [Azure](https://azure.microsoft.com/) to demonstrate how you can use Terratest to write automated tests for your Azure Terraform code.
This module deploys a Data Factory.

- A [Azure MySQL Database](https://azure.microsoft.com/en-us/products/data-factory).

Check out [test/azure/terraform_azure_datafactory_example_test.go](./../../../test/azure/terraform_azure_datafactory_example_test.go) to see how you can write automated tests for this module and validate the configuration of the parameters and options.

**WARNING**: This module and the automated tests for it deploy real resources into your Azure account which can cost you money.

## Running this module manually
1. Sign up for [Azure](https://azure.microsoft.com/).
1. Configure your Azure credentials using one of the [supported methods for Azure CLI
tools](https://docs.microsoft.com/en-us/cli/azure/azure-cli-configuration?view=azure-cli-latest)
1. Install [Terraform](https://www.terraform.io/) and make sure it's on your `PATH`.
1. Ensure [environment variables](../README.md#review-environment-variables) are available
1. Run `terraform init`
1. Run `terraform apply`
1. When you're done, run `terraform destroy`.


## Running automated tests against this module
1. Sign up for [Azure](https://azure.microsoft.com/)
1. Configure your Azure credentials using one of the [supported methods for Azure CLI
tools](https://docs.microsoft.com/en-us/cli/azure/azure-cli-configuration?view=azure-cli-latest)
1. Install [Terraform](https://www.terraform.io/) and make sure it's on your `PATH`
1. Configure your Terratest [Go test environment](../README.md)
1. `cd test/azure`
1. `go build terraform_azure_datafactory_example_test.go`
2. `go test -v -timeout 60m -tags azure -run TestTerraformAzureDataFactoryExample`
49 changes: 49 additions & 0 deletions examples/azure/terraform-azure-datafactory-example/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# ---------------------------------------------------------------------------------------------------------------------
# DEPLOY AN AZURE DATA FACTORY
# This is an example of how to deploy an AZURE Data Factory
# See test/terraform_azure_example_test.go for how to write automated tests for this code.
# ---------------------------------------------------------------------------------------------------------------------


# ---------------------------------------------------------------------------------------------------------------------
# CONFIGURE OUR AZURE CONNECTION
# ---------------------------------------------------------------------------------------------------------------------

provider "azurerm" {
version = "~>2.93.0"
features {}
}

# ---------------------------------------------------------------------------------------------------------------------
# CREATE RANDOM PASSWORD
# ---------------------------------------------------------------------------------------------------------------------

# Random password is used as an example to simplify the deployment and improve the security of the database.
# This is not as a production recommendation as the password is stored in the Terraform state file.
resource "random_password" "password" {
length = 16
override_special = "-_%@"
min_upper = "1"
min_lower = "1"
min_numeric = "1"
min_special = "1"
}

# ---------------------------------------------------------------------------------------------------------------------
# DEPLOY A RESOURCE GROUP
# ---------------------------------------------------------------------------------------------------------------------

resource "azurerm_resource_group" "datafactory_rg" {
name = "terratest-datafactory-${var.postfix}"
location = var.location
}

# ---------------------------------------------------------------------------------------------------------------------
# DEPLOY A DATA FACTORY
# ---------------------------------------------------------------------------------------------------------------------

resource "azurerm_data_factory" "data_factory" {
name = "datafactory${var.postfix}"
location = azurerm_resource_group.datafactory_rg.location
resource_group_name = azurerm_resource_group.datafactory_rg.name
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "resource_group_name" {
value = azurerm_resource_group.datafactory_rg.name
}

output "datafactory_name" {
value = azurerm_data_factory.data_factory.name
}
31 changes: 31 additions & 0 deletions examples/azure/terraform-azure-datafactory-example/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# ---------------------------------------------------------------------------------------------------------------------
# ENVIRONMENT VARIABLES
# Define these secrets as environment variables
# ---------------------------------------------------------------------------------------------------------------------

# ARM_CLIENT_ID
# ARM_CLIENT_SECRET
# ARM_SUBSCRIPTION_ID
# ARM_TENANT_ID

# ---------------------------------------------------------------------------------------------------------------------
# REQUIRED PARAMETERS
# You must provide a value for each of these parameters.
# ---------------------------------------------------------------------------------------------------------------------

# ---------------------------------------------------------------------------------------------------------------------
# OPTIONAL PARAMETERS
# These parameters have reasonable defaults.
# ---------------------------------------------------------------------------------------------------------------------

variable "location" {
description = "The supported azure location where the resource exists"
type = string
default = "West US2"
}

variable "postfix" {
description = "A postfix string to centrally mitigate resource name collisions."
type = string
default = "resource"
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.19
require (
cloud.google.com/go v0.105.0 // indirect
cloud.google.com/go/storage v1.27.0
github.com/Azure/azure-sdk-for-go v50.2.0+incompatible
github.com/Azure/azure-sdk-for-go v51.0.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.20
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
Expand Down Expand Up @@ -53,6 +53,7 @@ require (
)

require (
cloud.google.com/go v0.105.0 // indirect
cloud.google.com/go/compute v1.12.1 // indirect
cloud.google.com/go/compute/metadata v0.2.1 // indirect
cloud.google.com/go/iam v0.7.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v50.2.0+incompatible h1:w9EF1btRfLLWbNEp6XvkMjeA6nQ3e1GZ2KNDqB/SjOQ=
github.com/Azure/azure-sdk-for-go v50.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v51.0.0+incompatible h1:p7blnyJSjJqf5jflHbSGhIhEpXIgIFmYZNg5uwqweso=
github.com/Azure/azure-sdk-for-go v51.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
Expand Down
30 changes: 30 additions & 0 deletions modules/azure/client_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance"
"github.com/Azure/azure-sdk-for-go/services/containerregistry/mgmt/2019-05-01/containerregistry"
"github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2019-11-01/containerservice"
"github.com/Azure/azure-sdk-for-go/services/datafactory/mgmt/2018-06-01/datafactory"
kvmng "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2016-10-01/keyvault"
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-09-01/network"
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2019-06-01/subscriptions"
Expand Down Expand Up @@ -771,6 +772,35 @@ func CreateFrontDoorFrontendEndpointClientE(subscriptionID string) (*frontdoor.F
return &client, nil
}

// CreateDataFactoriesClientE is a helper function that will setup a synapse client.
func CreateDataFactoriesClientE(subscriptionID string) (*datafactory.FactoriesClient, error) {
// Validate Azure subscription ID
subscriptionID, err := getTargetAzureSubscription(subscriptionID)
if err != nil {
return nil, err
}

// Lookup environment URI
baseURI, err := getBaseURI()
if err != nil {
return nil, err
}

// Create a synapse client
dataFactoryClient := datafactory.NewFactoriesClientWithBaseURI(baseURI, subscriptionID)

// Create an authorizer
authorizer, err := NewAuthorizer()
if err != nil {
return nil, err
}

// Attach authorizer to the client
dataFactoryClient.Authorizer = *authorizer

return &dataFactoryClient, nil
}

// GetKeyVaultURISuffixE returns the proper KeyVault URI suffix for the configured Azure environment.
// This function would fail the test if there is an error.
func GetKeyVaultURISuffixE() (string, error) {
Expand Down
56 changes: 56 additions & 0 deletions modules/azure/datafactory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package azure

import (
"context"

"github.com/Azure/azure-sdk-for-go/services/datafactory/mgmt/2018-06-01/datafactory"
"github.com/gruntwork-io/terratest/modules/testing"
"github.com/stretchr/testify/require"
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you add a helper to check dataFactoryExists. Similar to here

func FrontDoorExists(t testing.TestingT, frontDoorName string, resourceGroupName string, subscriptionID string) bool {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @HadwaAbdelhalem added

// DataFactoryExists indicates whether the Data Factory exists for the subscription.
// This function would fail the test if there is an error.
func DataFactoryExists(t testing.TestingT, dataFactoryName string, resourceGroupName string, subscriptionID string) bool {
exists, err := DataFactoryExistsE(dataFactoryName, resourceGroupName, subscriptionID)
require.NoError(t, err)
return exists
}

// DataFactoryExistsE indicates whether the specified Data Factory exists and may return an error.
func DataFactoryExistsE(dataFactoryName string, resourceGroupName string, subscriptionID string) (bool, error) {
_, err := GetDataFactoryE(subscriptionID, resourceGroupName, dataFactoryName)
if err != nil {
if ResourceNotFoundErrorExists(err) {
return false, nil
}
return false, err
}
return true, nil
}

// GetDataFactory is a helper function that gets the synapse workspace.
// This function would fail the test if there is an error.
func GetDataFactory(t testing.TestingT, resGroupName string, factoryName string, subscriptionID string) *datafactory.Factory {
Workspace, err := GetDataFactoryE(subscriptionID, resGroupName, factoryName)
require.NoError(t, err)

return Workspace
}

// GetDataFactoryE is a helper function that gets the workspace.
func GetDataFactoryE(subscriptionID string, resGroupName string, factoryName string) (*datafactory.Factory, error) {
// Create a datafactory client
datafactoryClient, err := CreateDataFactoriesClientE(subscriptionID)
if err != nil {
return nil, err
}

// Get the corresponding synapse workspace
dataFactory, err := datafactoryClient.Get(context.Background(), resGroupName, factoryName, "")
if err != nil {
return nil, err
}

//Return synapse workspace
return &dataFactory, nil
}
35 changes: 35 additions & 0 deletions modules/azure/datafactory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package azure

import (
"testing"

"github.com/stretchr/testify/require"
)

/*
The below tests are currently stubbed out, with the expectation that they will throw errors.
If/when CRUD methods are introduced for Azure Synapse, these tests can be extended
*/
func TestDataFactoryExists(t *testing.T) {
t.Parallel()

dataFactoryName := ""
resourceGroupName := ""
subscriptionID := ""

exists, err := DataFactoryExistsE(dataFactoryName, resourceGroupName, subscriptionID)

require.False(t, exists)
require.Error(t, err)
}

func TestGetDataFactoryE(t *testing.T) {
t.Parallel()

resGroupName := ""
subscriptionID := ""
dataFactoryName := ""

_, err := GetDataFactoryE(subscriptionID, resGroupName, dataFactoryName)
require.Error(t, err)
}
48 changes: 48 additions & 0 deletions test/azure/terraform_azure_datafactory_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package test
Copy link
Contributor

@HadwaAbdelhalem HadwaAbdelhalem Jun 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add the build tags for ci test execution?

Suggested change
package test
//go:build azure
// +build azure
// NOTE: We use build tags to differentiate azure testing because we currently do not have azure access setup for
// CircleCI.
package test

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Hadwa added,also updated the CI pipeline run above in description for datafactory and synapse passed

https://github.com/sweanan/terratest/actions/runs/5327539258/jobs/9651062907

--- PASS: TestTerraformAzureSynapseExample (963.67s)
--- PASS: TestTerraformAzureDataFactoryExample (65.98s)


import (
"strings"
"testing"

"github.com/gruntwork-io/terratest/modules/azure"
"github.com/gruntwork-io/terratest/modules/random"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)

func TestTerraformAzureDataFactoryExample(t *testing.T) {
t.Parallel()

uniquePostfix := strings.ToLower(random.UniqueId())
expectedDataFactoryProvisioningState := "Succeeded"
expectedLocation := "West US2"

// website::tag::1:: Configure Terraform setting up a path to Terraform code.
terraformOptions := &terraform.Options{
// The path to where our Terraform code is located
TerraformDir: "../../examples/azure/terraform-azure-datafactory-example",
Vars: map[string]interface{}{
"postfix": uniquePostfix,
"location": expectedLocation,
},
}

// website::tag::4:: At the end of the test, run `terraform destroy` to clean up any resources that were created
defer terraform.Destroy(t, terraformOptions)

// website::tag::2:: Run `terraform init` and `terraform apply`. Fail the test if there are any errors.
terraform.InitAndApply(t, terraformOptions)

// website::tag::3:: Run `terraform output` to get the values of output variables
expectedResourceGroupName := terraform.Output(t, terraformOptions, "resource_group_name")
expectedDataFactoryName := terraform.Output(t, terraformOptions, "datafactory_name")

// // website::tag::4:: Get synapse details and assert them against the terraform output
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you assert DataFactoryExists first

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @HadwaAbdelhalem moved

actualDataFactory := azure.GetDataFactory(t, expectedResourceGroupName, expectedDataFactoryName, "")

assert.Equal(t, expectedDataFactoryName, *actualDataFactory.Name)
assert.Equal(t, expectedDataFactoryProvisioningState, *actualDataFactory.FactoryProperties.ProvisioningState)

actualDataFactoryExits := azure.DataFactoryExists(t, expectedDataFactoryName, expectedResourceGroupName, "")
assert.True(t, actualDataFactoryExits)
}