diff --git a/build/docs/deploy.adoc b/build/docs/deploy.adoc index db777ed..f7e9197 100644 --- a/build/docs/deploy.adoc +++ b/build/docs/deploy.adoc @@ -18,7 +18,7 @@ This task runs the following terraform commands in sequence: - `terraform apply` to apply the changes to the target environment. -It is assumed that secrets needed to connected to the infrastructure managed by terraform are provided with environment variables. The task by default expects a kubernetes secret which is used to derived the needed environment variables from. This can be switched off by setting the `env-from-secret` to "false" in case variables are already provided by other means (such as a podTemplate) or not needed. +It is assumed that secrets needed to connected to the infrastructure managed by terraform are provided with environment variables. The task by default expects a kubernetes secret which is used to derived the needed environment variables from. This can be switched off by setting `env-from-secret` to "false" in case variables are already provided by other means (such as a podTemplate) or not needed. This mechanism is also the means to provide terraform input variables. @@ -32,5 +32,7 @@ needs to define the environment specific values for the subcomponents. The following artifacts are generated by the task and placed into `.ods/artifacts/` * `deployments/` - ** `plan-.txt` - ** `-plan-.txt` + ** `[-]plan-.txt` + ** `-[-]plan-.txt` + +where is only used if parameter `terraform-dir` is not the default (`./terraform`) diff --git a/build/tasks/deploy.yaml b/build/tasks/deploy.yaml index 8828e6a..da86937 100644 --- a/build/tasks/deploy.yaml +++ b/build/tasks/deploy.yaml @@ -32,6 +32,10 @@ spec: If set to true, the task will do a terraform plan, and then stop. type: string default: 'false' + - name: env-from-secret + description: Whether to derive env variables from the k8s secret terraform-var-{target-environment}. + type: string + default: 'true' - name: verbose description: More verbose output. DEBUG also implies verbose type: string @@ -60,6 +64,7 @@ spec: -apply-extra-args=$(params.apply-extra-args) \ -plan-extra-args=$(params.plan-extra-args) \ -plan-only=$(params.plan-only) \ + -env-from-secret=$(params.env-from-secret) \ -verbose=$(params.verbose) volumeMounts: - mountPath: /etc/ssl/certs/private-cert.pem diff --git a/cmd/deploy-terraform/steps.go b/cmd/deploy-terraform/steps.go index 6a986bc..f42776b 100644 --- a/cmd/deploy-terraform/steps.go +++ b/cmd/deploy-terraform/steps.go @@ -17,8 +17,7 @@ import ( ) const ( - tokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" - subTerraformDir = "terraform" + tokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" ) type TerraformStep func(d *deployTerraform) (*deployTerraform, error) @@ -163,8 +162,8 @@ func detectDeploymentArtifacts() TerraformStep { } func (d *deployTerraform) isTerraformDir(dir string) bool { - if _, err := os.Stat(subTerraformDir); os.IsNotExist(err) { - d.logger.Debugf("No terraform directory found at %s: %w", subTerraformDir, err) + if _, err := os.Stat(dir); os.IsNotExist(err) { + d.logger.Debugf("No terraform directory found at %s: %w", dir, err) return false } // perhaps additional checks make sense to see whether dir is a terraform dir diff --git a/cmd/deploy-terraform/terraform.go b/cmd/deploy-terraform/terraform.go index ea53ef2..b4c3bc0 100644 --- a/cmd/deploy-terraform/terraform.go +++ b/cmd/deploy-terraform/terraform.go @@ -116,9 +116,11 @@ func (d *deployTerraform) commonTerraformArgs() []string { "-input=false", "-no-color", } - if d.opts.debug { - args = append([]string{"-debug"}, args...) - } + // https://support.hashicorp.com/hc/en-us/articles/360001113727-Enabling-trace-level-logs-in-Terraform-CLI-Cloud-or-Enterprise + // todo: is there some kind of debug flag? + // if d.opts.debug { + // args = append([]string{"-debug"}, args...) + // } return args } diff --git a/docs/deploy.adoc b/docs/deploy.adoc index 70e4f1c..4dcc1e9 100644 --- a/docs/deploy.adoc +++ b/docs/deploy.adoc @@ -22,7 +22,7 @@ This task runs the following terraform commands in sequence: - `terraform apply` to apply the changes to the target environment. -It is assumed that secrets needed to connected to the infrastructure managed by terraform are provided with environment variables. The task by default expects a kubernetes secret which is used to derived the needed environment variables from. This can be switched off by setting the `env-from-secret` to "false" in case variables are already provided by other means (such as a podTemplate) or not needed. +It is assumed that secrets needed to connected to the infrastructure managed by terraform are provided with environment variables. The task by default expects a kubernetes secret which is used to derived the needed environment variables from. This can be switched off by setting `env-from-secret` to "false" in case variables are already provided by other means (such as a podTemplate) or not needed. This mechanism is also the means to provide terraform input variables. @@ -36,8 +36,10 @@ needs to define the environment specific values for the subcomponents. The following artifacts are generated by the task and placed into `.ods/artifacts/` * `deployments/` - ** `plan-.txt` - ** `-plan-.txt` + ** `[-]plan-.txt` + ** `-[-]plan-.txt` + +where is only used if parameter `terraform-dir` is not the default (`./terraform`) == Parameters @@ -72,6 +74,11 @@ The following artifacts are generated by the task and placed into `.ods/artifact +| env-from-secret +| true +| Whether to derive env variables from the k8s secret terraform-var-{target-environment}. + + | verbose | false | More verbose output. DEBUG also implies verbose diff --git a/go.mod b/go.mod index 66cb0ec..1449189 100644 --- a/go.mod +++ b/go.mod @@ -71,6 +71,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/otiai10/copy v1.14.0 github.com/sonatype-nexus-community/gonexus v0.59.0 // indirect golang.org/x/net v0.11.0 // indirect golang.org/x/oauth2 v0.9.0 // indirect diff --git a/tasks/deploy.yaml b/tasks/deploy.yaml index 4d819ff..e173163 100644 --- a/tasks/deploy.yaml +++ b/tasks/deploy.yaml @@ -34,6 +34,10 @@ spec: If set to true, the task will do a terraform plan, and then stop. type: string default: 'false' + - name: env-from-secret + description: Whether to derive env variables from the k8s secret terraform-var-{target-environment}. + type: string + default: 'true' - name: verbose description: More verbose output. DEBUG also implies verbose type: string @@ -62,6 +66,7 @@ spec: -apply-extra-args=$(params.apply-extra-args) \ -plan-extra-args=$(params.plan-extra-args) \ -plan-only=$(params.plan-only) \ + -env-from-secret=$(params.env-from-secret) \ -verbose=$(params.verbose) volumeMounts: - mountPath: /etc/ssl/certs/private-cert.pem diff --git a/test/e2e/terraform_deploy_test.go b/test/e2e/terraform_deploy_test.go index 8ec9a56..83097b2 100644 --- a/test/e2e/terraform_deploy_test.go +++ b/test/e2e/terraform_deploy_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "os" "path/filepath" "testing" @@ -34,7 +35,7 @@ func TestPlanTerraformTask(t *testing.T) { "verbose": "true", }, ), - ott.WithGitSourceWorkspace(t, "../testdata/workspaces/terraform-sample", namespaceConfig.Name), + withWorkspace(t, "terraform-sample"), ttr.AfterRun(func(config *ttr.TaskRunConfig, run *tekton.TaskRun, logs bytes.Buffer) { dir := config.WorkspaceConfigs["source"].Dir fmt.Println(dir) @@ -79,7 +80,7 @@ func TestApplyTerraformTask(t *testing.T) { "verbose": "true", }, ), - ott.WithGitSourceWorkspace(t, "../testdata/workspaces/terraform-sample", namespaceConfig.Name), + withWorkspace(t, "terraform-sample"), ttr.AfterRun(func(config *ttr.TaskRunConfig, run *tekton.TaskRun, logs bytes.Buffer) { dir := config.WorkspaceConfigs["source"].Dir fmt.Println(dir) @@ -109,6 +110,55 @@ func TestApplyTerraformTask(t *testing.T) { t.Fatal(err) } } +func TestApplyTerraformTaskWithDifferentDir(t *testing.T) { + k8sClient := newK8sClient(t) + _, nsCleanup := createRoleBindingsOrFatal(t, k8sClient, namespaceConfig.Name) + defer nsCleanup() + _, secretCleanup := createSecretsOrFatal(t, k8sClient, namespaceConfig.Name, map[string]string{ + "TF_VAR_hello": "Hello ods-pipeline-terraform!", + }) + tfDir := "tf" + defer secretCleanup() + if err := runTask( + ttr.WithStringParams( + map[string]string{ + "terraform-dir": fmt.Sprintf("./%s", tfDir), + "verbose": "true", + }, + ), + withWorkspace(t, "terraform-sample", func(c *ttr.WorkspaceConfig) error { + renameTerraformDir(t, c.Dir, "terraform", tfDir) + return nil + }), + ttr.AfterRun(func(config *ttr.TaskRunConfig, run *tekton.TaskRun, logs bytes.Buffer) { + dir := config.WorkspaceConfigs["source"].Dir + fmt.Println(dir) + ott.AssertFileContentContains(t, + dir, + filepath.Join(pipelinectxt.DeploymentsPath, fmt.Sprintf("%s-plan-%s.txt", tfDir, "dev")), + "Terraform used the selected providers to generate the following execution", + "plan. Resource actions are indicated with the following symbols:", + " + create", + "", + "Terraform will perform the following actions:", + "", + " # tfcoremock_simple_resource.example will be created", + " + resource \"tfcoremock_simple_resource\" \"example\" {", + "+ bool = true", + "+ float = 42.23", + "+ id = \"my-simple-resource\"", + "+ integer = 11", + "+ number = 42", + "+ string = \"Hello ods-pipeline-terraform!\"", + "}", + "", + "Plan: 1 to add, 0 to change, 0 to destroy.", + ) + }), + ); err != nil { + t.Fatal(err) + } +} func newK8sClient(t *testing.T) *kubernetes.Clientset { home := homedir.HomeDir() @@ -188,3 +238,20 @@ func createSecrets(clientset *kubernetes.Clientset, ctxtNamespace string, vars m return createdSecret, err } + +func renameTerraformDir(t *testing.T, wsDir string, originalDir, targetDir string) { + err := os.Rename( + filepath.Join(wsDir, originalDir), + filepath.Join(wsDir, targetDir), + ) + if err != nil { + t.Fatal(err) + } +} + +func withWorkspace(t *testing.T, dir string, opts ...ttr.WorkspaceOpt) ttr.TaskRunOpt { + return ott.WithGitSourceWorkspace( + t, filepath.Join("../testdata/workspaces", dir), namespaceConfig.Name, + opts..., + ) +}