Skip to content

Commit f8b3c8c

Browse files
authored
Add Terraform Outputs to ServiceContext to use them in agent policy test config (#1272)
1 parent 0126081 commit f8b3c8c

File tree

32 files changed

+1242
-4
lines changed

32 files changed

+1242
-4
lines changed

.buildkite/hooks/pre-command

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ if [[ "$BUILDKITE_PIPELINE_SLUG" == "elastic-package" && "$BUILDKITE_STEP_KEY" =
4646
export GCP_PROJECT_ID=${ELASTIC_PACKAGE_GCP_PROJECT_SECRET}
4747
fi
4848

49-
if [[ "$BUILDKITE_PIPELINE_SLUG" == "elastic-package" && "$BUILDKITE_STEP_KEY" == "integration-parallel-aws" ]]; then
49+
if [[ "$BUILDKITE_PIPELINE_SLUG" == "elastic-package" && ("$BUILDKITE_STEP_KEY" == "integration-parallel-aws" || "$BUILDKITE_STEP_KEY" == "integration-parallel-aws_logs") ]]; then
5050
export ELASTIC_PACKAGE_AWS_SECRET_KEY=$(retry 5 vault kv get -field secret_key ${AWS_SERVICE_ACCOUNT_SECRET_PATH})
5151
export ELASTIC_PACKAGE_AWS_ACCESS_KEY=$(retry 5 vault kv get -field access_key ${AWS_SERVICE_ACCOUNT_SECRET_PATH})
5252

docs/howto/system_testing.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,65 @@ data "aws_ami" "latest-amzn" {
262262

263263
Notice the use of the `TEST_RUN_ID` variable. It contains a unique ID, which can help differentiate resources created in potential concurrent test runs.
264264

265+
#### Terraform Outputs
266+
267+
The outputs generated by the terraform service deployer can be accessed in the system test config using handlebars template.
268+
For example, if a `SQS queue` is configured in terraform and if the `queue_url` is configured as output , it can be used in the test config as a handlebars template `{{TF_OUTPUT_queue_url}}`
269+
270+
Sample Terraform definition
271+
272+
```
273+
resource "aws_sqs_queue" "test" {
274+
275+
}
276+
277+
output "queue_url"{
278+
value = aws_sqs_queue.test.url
279+
}
280+
```
281+
282+
Sample system test config
283+
284+
``` yaml
285+
data_stream:
286+
vars:
287+
period: 5m
288+
latency: 10m
289+
queue_url: '{{TF_OUTPUT_queue_url}}'
290+
tags_filter: |-
291+
- key: Name
292+
value: "elastic-package-test-{{TEST_RUN_ID}}"
293+
```
294+
295+
For complex outputs from terraform you can use `{{TF_OUTPUT_root_key.nested_key}}`
296+
297+
```
298+
output "root_key"{
299+
value = someoutput.nested_key_value
300+
}
301+
```
302+
``` json
303+
{
304+
"root_key": {
305+
"sensitive": false,
306+
"type": [
307+
"object",
308+
{
309+
"nested_key": "string"
310+
}
311+
],
312+
"value": {
313+
"nested_key": "this is a nested key"
314+
}
315+
}
316+
}
317+
```
318+
``` yaml
319+
data_stream:
320+
vars:
321+
queue_url: '{{TF_OUTPUT_root_key.nested_key}}'
322+
```
323+
265324
#### Environment variables
266325
267326
To use environment variables within the Terraform service deployer a `env.yml` file is required.

internal/configuration/locations/locations.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ var (
3232

3333
serviceLogsDir = filepath.Join(temporaryDir, "service_logs")
3434
kubernetesDeployerDir = filepath.Join(deployerDir, "kubernetes")
35+
serviceOutputDir = filepath.Join(temporaryDir, "output")
3536
)
3637

3738
// LocationManager maintains an instance of a config path location
@@ -90,6 +91,11 @@ func (loc LocationManager) ServiceLogDir() string {
9091
return filepath.Join(loc.stackPath, serviceLogsDir)
9192
}
9293

94+
// ServiceOutputDir returns the output directory
95+
func (loc LocationManager) ServiceOutputDir() string {
96+
return filepath.Join(loc.stackPath, serviceOutputDir)
97+
}
98+
9399
// FieldsCacheDir returns the directory with cached fields
94100
func (loc LocationManager) FieldsCacheDir() string {
95101
return filepath.Join(loc.stackPath, fieldsCachedDir)

internal/testrunner/runners/system/runner.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ func (r *runner) runTestPerVariant(result *testrunner.ResultComposer, locationMa
197197
ctxt.Logs.Folder.Local = locationManager.ServiceLogDir()
198198
ctxt.Logs.Folder.Agent = ServiceLogsAgentDir
199199
ctxt.Test.RunID = createTestRunID()
200+
201+
outputDir, err := createOutputDir(locationManager, ctxt.Test.RunID)
202+
if err != nil {
203+
return nil, fmt.Errorf("could not create output dir for terraform deployer %w", err)
204+
}
205+
ctxt.OutputDir = outputDir
206+
200207
testConfig, err := newConfig(filepath.Join(r.options.TestFolder.Path, cfgFile), ctxt, variantName)
201208
if err != nil {
202209
return result.WithError(errors.Wrapf(err, "unable to load system test case file '%s'", cfgFile))
@@ -224,6 +231,14 @@ func (r *runner) runTestPerVariant(result *testrunner.ResultComposer, locationMa
224231
return partial, nil
225232
}
226233

234+
func createOutputDir(locationManager *locations.LocationManager, runId string) (string, error) {
235+
outputDir := filepath.Join(locationManager.ServiceOutputDir(), runId)
236+
if err := os.MkdirAll(outputDir, 0755); err != nil {
237+
return "", fmt.Errorf("failed to create output directory: %w", err)
238+
}
239+
return outputDir, nil
240+
}
241+
227242
func createTestRunID() string {
228243
return fmt.Sprintf("%d", rand.Intn(testRunMaxID-testRunMinID)+testRunMinID)
229244
}

internal/testrunner/runners/system/servicedeployer/_static/terraform_deployer.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ services:
1313
- TF_VAR_REPO=${REPO:-unknown}
1414
volumes:
1515
- ${TF_DIR}:/stage
16+
- ${TF_OUTPUT_DIR}:/output

internal/testrunner/runners/system/servicedeployer/_static/terraform_deployer_run.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ terraform init
2222
terraform plan
2323
terraform apply -auto-approve && touch /tmp/tf-applied
2424

25+
terraform output -json > /output/tfOutputValues.json
26+
2527
echo "Terraform definitions applied."
2628

2729
set +x

internal/testrunner/runners/system/servicedeployer/compose.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ func (s *dockerComposeDeployedService) TearDown() error {
158158
if err != nil {
159159
logger.Errorf("could not remove the service logs (path: %s)", s.ctxt.Logs.Folder.Local)
160160
}
161+
// Remove the outputs generated by the service container
162+
if err = os.RemoveAll(s.ctxt.OutputDir); err != nil {
163+
logger.Errorf("could not remove the temporary output files %w", err)
164+
}
161165
}()
162166

163167
p, err := compose.NewProject(s.project, s.ymlPaths...)

internal/testrunner/runners/system/servicedeployer/context.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ type ServiceContext struct {
6060

6161
// CustomProperties store additional data used to boot up the service, e.g. AWS credentials.
6262
CustomProperties map[string]interface{}
63+
64+
// Directory to store any outputs generated
65+
OutputDir string
6366
}
6467

6568
// Aliases method returned aliases to properties of the service context.

internal/testrunner/runners/system/servicedeployer/terraform.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package servicedeployer
66

77
import (
88
_ "embed"
9+
"encoding/json"
910
"fmt"
1011
"os"
1112
"path/filepath"
@@ -26,6 +27,8 @@ const (
2627
terraformDeployerYml = "terraform-deployer.yml"
2728
terraformDeployerDockerfile = "Dockerfile"
2829
terraformDeployerRun = "run.sh"
30+
terraformOutputPrefix = "TF_OUTPUT_"
31+
terraformOutputJsonFile = "tfOutputValues.json"
2932
)
3033

3134
//go:embed _static/terraform_deployer.yml
@@ -42,6 +45,43 @@ type TerraformServiceDeployer struct {
4245
definitionsDir string
4346
}
4447

48+
// addTerraformOutputs method reads the terraform outputs generated in the json format and
49+
// adds them to the custom properties of ServiceContext and can be used in the handlebars template
50+
// like `{{TF_OUTPUT_queue_url}}` where `queue_url` is the output configured
51+
func addTerraformOutputs(outCtxt ServiceContext) error {
52+
// Read the `output.json` file where terraform outputs are generated
53+
outputFile := filepath.Join(outCtxt.OutputDir, terraformOutputJsonFile)
54+
content, err := os.ReadFile(outputFile)
55+
if err != nil {
56+
return fmt.Errorf("failed to read terraform output file: %w", err)
57+
}
58+
59+
// https://github.com/hashicorp/terraform/blob/v1.4.6/internal/command/views/output.go#L217-L222
60+
type OutputMeta struct {
61+
Value interface{} `json:"value"`
62+
}
63+
64+
// Unmarshall the data into `terraformOutputs`
65+
logger.Debug("Unmarshalling terraform output json")
66+
var terraformOutputs map[string]OutputMeta
67+
if err = json.Unmarshal(content, &terraformOutputs); err != nil {
68+
return fmt.Errorf("error during json Unmarshal %w", err)
69+
}
70+
71+
if len(terraformOutputs) == 0 {
72+
return nil
73+
}
74+
75+
if outCtxt.CustomProperties == nil {
76+
outCtxt.CustomProperties = make(map[string]any, len(terraformOutputs))
77+
}
78+
// Prefix variables names with TF_OUTPUT_
79+
for k, outputs := range terraformOutputs {
80+
outCtxt.CustomProperties[terraformOutputPrefix+k] = outputs.Value
81+
}
82+
return nil
83+
}
84+
4585
// NewTerraformServiceDeployer creates an instance of TerraformServiceDeployer.
4686
func NewTerraformServiceDeployer(definitionsDir string) (*TerraformServiceDeployer, error) {
4787
return &TerraformServiceDeployer{
@@ -117,6 +157,11 @@ func (tsd TerraformServiceDeployer) SetUp(inCtxt ServiceContext) (DeployedServic
117157
}
118158

119159
outCtxt.Agent.Host.NamePrefix = "docker-fleet-agent"
160+
161+
err = addTerraformOutputs(outCtxt)
162+
if err != nil {
163+
return nil, fmt.Errorf("could not handle terraform output %w", err)
164+
}
120165
service.ctxt = outCtxt
121166
return &service, nil
122167
}

internal/testrunner/runners/system/servicedeployer/terraform_env.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
const (
1616
tfDir = "TF_DIR"
17+
tfOutputDir = "TF_OUTPUT_DIR"
1718
tfTestRunID = "TF_VAR_TEST_RUN_ID"
1819

1920
envYmlFile = "env.yml"
@@ -24,6 +25,7 @@ func (tsd TerraformServiceDeployer) buildTerraformExecutorEnvironment(ctxt Servi
2425
vars[serviceLogsDirEnv] = ctxt.Logs.Folder.Local
2526
vars[tfTestRunID] = ctxt.Test.RunID
2627
vars[tfDir] = tsd.definitionsDir
28+
vars[tfOutputDir] = ctxt.OutputDir
2729

2830
var pairs []string
2931
for k, v := range vars {

0 commit comments

Comments
 (0)