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

Datadog test script #636

Merged
merged 16 commits into from
Nov 2, 2020
56 changes: 56 additions & 0 deletions tests/datadog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Testing the Datadog provider

The CLI script provided is used to test importing Datadog resources with terraformer. The tool will create resources using Terraform CLI and import them using terraformer. The imported resources will be stored in the `generated/` directory.

_Note_: The script will create and destroy real resources. Never run this on a production Datadog organization.

### Requirements
* terraform version >= 0.12.x

### Script usage

**Terraform 0.12.x**

Run the script from the projects root directory:

```
go run ./tests/datadog/
```

- Test should run successfully without exiting

**Terraform 0.13.x**

Run the script from the projects root directory and pass the terraform version using DATADOG_TF_VERSION env var:

```
DATADOG_TF_VERSION=0.13.x LOG_CMD_OUTPUT=true go run ./tests/datadog/
```
- Terraformer currently generates resources using terraform version 0.12.29 and HCLv1 standards. When using terraform version 0.13.x, the script will fail due to `outputs` diffs when running `terraform plan` on the generated resources. This is due to differences in how outputs are references in state files.

- Manually ensure that generated diffs are regarding outputs only. E.g:

```
Plan: 0 to add, 0 to change, 0 to destroy.

Changes to Outputs:
~ datadog_dashboard_tfer--dashboard_gwh-002D-a7r-002D-cfs_id = "<resource-ID>" -> "datadog_dashboard.tfer--dashboard_gwh-002D-a7r-002D-cfs.id"
```

##### Available configuration options

| Configuration Options | Description |
| ------------- |:------------- |
| **DD_TEST_CLIENT_API_KEY** | Datadog api key |
| **DD_TEST_CLIENT_APP_KEY** | Datadog APP key |
| **DATADOG_HOST** | The API Url. if you're working with "EU" version of Datadog, use `https://api.datadoghq.eu/`. Default: `https://api.datadoghq.com/` |
| **DATADOG_TF_VERSION** | Terraform version installed. Pass the terraform version number if using Terraform version >= 0.13.x |
| **DATADOG_TERRAFORM_TARGET** | Colon separated list of resource addresses to [target](https://www.terraform.io/docs/commands/plan.html#resource-targeting). Example: `DATADOG_TERRAFORM_TARGET="datadog_dashboard.free_dashboard_example:datadog_monitor.monitor_example"` |
| **LOG_CMD_OUTPUT** | Print outputs to stderr and stdout when running `terraform` commands. Default `false` |

**Frequently Asked Questions**

```
2020/10/26 15:08:34 Message: Error while importing resources. Error: fork/exec : no such file or directory
```
- Above error indicates that Terraformer is unable to locate the datadog provider executable. Manually pass the dir of the plugin's directory using env var `TF_PLUGIN_DIR`. E.g. `TF_PLUGIN_DIR=~/.terraform.d/`
193 changes: 193 additions & 0 deletions tests/datadog/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package main

import (
"fmt"
"log"
"os"
"os/exec"
"regexp"
"strconv"
"strings"

datadog_terraforming "github.com/GoogleCloudPlatform/terraformer/providers/datadog"
)

var (
commandTerraformInit = "terraform init"
commandTerraformPlan = "terraform plan -detailed-exitcode"
commandTerraformDestroy = "terraform destroy -auto-approve"
commandTerraformApply = "terraform apply -auto-approve"
commandTerraformOutput = "terraform output"
commandTerraformV13Upgrade = "terraform 0.13upgrade -yes ."
datadogResourcesPath = "tests/datadog/resources/"
)

type DatadogConfig struct {
apiKey string
appKey string
apiURL string
}

type TerraformConfig struct {
target string
}

type Config struct {
Datadog DatadogConfig
Terraform TerraformConfig
logCMDOutput bool
rootPath string
tfVersion string
}

func getConfig() (*Config, error) {
logCMDOutput := false
if envVar := os.Getenv("LOG_CMD_OUTPUT"); envVar != "" {
logCMDOutputEnv, err := strconv.ParseBool(envVar)
if err != nil {
return nil, err
}
logCMDOutput = logCMDOutputEnv
}
rootPath, _ := os.Getwd()

return &Config{
Datadog: DatadogConfig{
apiKey: os.Getenv("DD_TEST_CLIENT_API_KEY"),
appKey: os.Getenv("DD_TEST_CLIENT_APP_KEY"),
apiURL: os.Getenv("DATADOG_HOST"),
},
Terraform: TerraformConfig{
target: os.Getenv("DATADOG_TERRAFORM_TARGET"),
},
logCMDOutput: logCMDOutput,
rootPath: rootPath,
tfVersion: os.Getenv("DATADOG_TF_VERSION"),
}, nil
}

func getAllServices(provider *datadog_terraforming.DatadogProvider) []string {
var services []string
for service := range provider.GetSupportedService() {
if service == "timeboard" {
continue
}
if service == "screenboard" {
continue
}
services = append(services, service)
}
return services
}

func initializeDatadogProvider(cfg *Config) error {
// Initialize the provider
log.Print("Initializing the Datadog provider")
if err := cmdRun(cfg, []string{commandTerraformInit}); err != nil {
return err
}
log.Print("Successfully initialized the Datadog provider")
return nil
}

func createDatadogResource(cfg *Config) (*map[string][]string, error) {
// Create terraform -target flags if targets are passed
var terraformTargets []string
if v := cfg.Terraform.target; v != "" {
vArr := strings.Split(v, ":")
for _, terraformTarget := range vArr {
terraformTargetFlag := fmt.Sprintf("-target=%s", terraformTarget)
terraformTargets = append(terraformTargets, terraformTargetFlag)
}
}

// Create resources
log.Print("Creating resources")
if err := cmdRun(cfg, []string{commandTerraformApply, strings.Join(terraformTargets, " ")}); err != nil {
return nil, err
}

// Get output of created resources and parse the data into a map
output, err := terraformOutput()
if err != nil {
log.Println(err)
return nil, err
}
resources := parseTerraformOutput(string(output))
log.Printf("Created resources: \n%s", string(output))

return resources, nil
}

func terraformOutput() ([]byte, error) {
output, err := exec.Command("sh", "-c", commandTerraformOutput).Output()
if err != nil {
log.Println(err)
return nil, err
}
return output, nil
}

func terraformPlan(cfg *Config) error {
log.Print("Running terraform plan against resources")
err := cmdRun(cfg, []string{commandTerraformPlan})
if err != nil {
return err
}

log.Print("terraform plan did not generate diffs")
return nil
}

func destroyDatadogResources(cfg *Config) error {
// Destroy created resources in the /tests/datadog/resources directory
err := os.Chdir(cfg.rootPath + "/" + datadogResourcesPath)
if err != nil {
return err
}
log.Print("Destroying resources")
if err := cmdRun(cfg, []string{commandTerraformDestroy}); err != nil {
return err
}
_ = os.Chdir(cfg.rootPath)

return nil
}

func parseTerraformOutput(output string) *map[string][]string {
outputArr := strings.Split(output, "\n")
resources := map[string][]string{}
for _, resourceOutput := range outputArr {
if len(resourceOutput) > 0 {
resourceArr := strings.Split(resourceOutput, " = ")
resourceID := resourceArr[len(resourceArr)-1]
// Get resource name
re := regexp.MustCompile("_(.*?)(--|_)")
match := re.FindStringSubmatch(resourceArr[0])
resourceName := match[1]

resources[resourceName] = append(resources[resourceName], resourceID)
}
}
return &resources
}

func cmdRun(cfg *Config, args []string) error {
terraformAPIKeyEnvVariable := fmt.Sprintf("DATADOG_API_KEY=%s", cfg.Datadog.apiKey)
terraformAppKeyEnvVariable := fmt.Sprintf("DATADOG_APP_KEY=%s", cfg.Datadog.appKey)

cmd := exec.Command("sh", "-c", strings.Join(args, " "))
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, terraformAPIKeyEnvVariable, terraformAppKeyEnvVariable)
if cfg.logCMDOutput {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}

err := cmd.Run()
if err != nil {
log.Println(err)
return err
}
return nil
}
Loading