mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-11-28 08:49:44 +02:00
Add terraformExecuteStep (#2679)
* Add terraformExectueStep * Update terraformExecute.go * fmt Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com>
This commit is contained in:
parent
b612a1fb3f
commit
fe6ea643fa
@ -69,6 +69,7 @@ func GetAllStepMetadata() map[string]config.StepData {
|
||||
"protecodeExecuteScan": protecodeExecuteScanMetadata(),
|
||||
"containerSaveImage": containerSaveImageMetadata(),
|
||||
"sonarExecuteScan": sonarExecuteScanMetadata(),
|
||||
"terraformExecute": terraformExecuteMetadata(),
|
||||
"transportRequestUploadCTS": transportRequestUploadCTSMetadata(),
|
||||
"transportRequestUploadSOLMAN": transportRequestUploadSOLMANMetadata(),
|
||||
"uiVeri5ExecuteTests": uiVeri5ExecuteTestsMetadata(),
|
||||
|
@ -136,6 +136,7 @@ func Execute() {
|
||||
rootCmd.AddCommand(IntegrationArtifactDownloadCommand())
|
||||
rootCmd.AddCommand(AbapEnvironmentAssembleConfirmCommand())
|
||||
rootCmd.AddCommand(IntegrationArtifactUploadCommand())
|
||||
rootCmd.AddCommand(TerraformExecuteCommand())
|
||||
rootCmd.AddCommand(ContainerExecuteStructureTestsCommand())
|
||||
|
||||
addRootFlags(rootCmd)
|
||||
|
60
cmd/terraformExecute.go
Normal file
60
cmd/terraformExecute.go
Normal file
@ -0,0 +1,60 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/SAP/jenkins-library/pkg/command"
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"github.com/SAP/jenkins-library/pkg/piperutils"
|
||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
||||
)
|
||||
|
||||
type terraformExecuteUtils interface {
|
||||
command.ExecRunner
|
||||
|
||||
FileExists(filename string) (bool, error)
|
||||
}
|
||||
|
||||
type terraformExecuteUtilsBundle struct {
|
||||
*command.Command
|
||||
*piperutils.Files
|
||||
}
|
||||
|
||||
func newTerraformExecuteUtils() terraformExecuteUtils {
|
||||
utils := terraformExecuteUtilsBundle{
|
||||
Command: &command.Command{},
|
||||
Files: &piperutils.Files{},
|
||||
}
|
||||
// Reroute command output to logging framework
|
||||
utils.Stdout(log.Writer())
|
||||
utils.Stderr(log.Writer())
|
||||
return &utils
|
||||
}
|
||||
|
||||
func terraformExecute(config terraformExecuteOptions, telemetryData *telemetry.CustomData) {
|
||||
utils := newTerraformExecuteUtils()
|
||||
|
||||
err := runTerraformExecute(&config, telemetryData, utils)
|
||||
if err != nil {
|
||||
log.Entry().WithError(err).Fatal("step execution failed")
|
||||
}
|
||||
}
|
||||
|
||||
func runTerraformExecute(config *terraformExecuteOptions, telemetryData *telemetry.CustomData, utils terraformExecuteUtils) error {
|
||||
args := []string{config.Command}
|
||||
|
||||
if config.Command == "apply" {
|
||||
args = append(args, "-auto-approve")
|
||||
}
|
||||
|
||||
if (config.Command == "apply" || config.Command == "plan") && config.TerraformSecrets != "" {
|
||||
args = append(args, fmt.Sprintf("-var-file=%s", config.TerraformSecrets))
|
||||
}
|
||||
|
||||
if config.AdditionalArgs != nil {
|
||||
args = append(args, config.AdditionalArgs...)
|
||||
}
|
||||
|
||||
utils.RunExecutable("terraform", args...)
|
||||
|
||||
return nil
|
||||
}
|
134
cmd/terraformExecute_generated.go
Normal file
134
cmd/terraformExecute_generated.go
Normal file
@ -0,0 +1,134 @@
|
||||
// Code generated by piper's step-generator. DO NOT EDIT.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/config"
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"github.com/SAP/jenkins-library/pkg/telemetry"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type terraformExecuteOptions struct {
|
||||
Command string `json:"command,omitempty"`
|
||||
TerraformSecrets string `json:"terraformSecrets,omitempty"`
|
||||
AdditionalArgs []string `json:"additionalArgs,omitempty"`
|
||||
}
|
||||
|
||||
// TerraformExecuteCommand Executes Terraform
|
||||
func TerraformExecuteCommand() *cobra.Command {
|
||||
const STEP_NAME = "terraformExecute"
|
||||
|
||||
metadata := terraformExecuteMetadata()
|
||||
var stepConfig terraformExecuteOptions
|
||||
var startTime time.Time
|
||||
|
||||
var createTerraformExecuteCmd = &cobra.Command{
|
||||
Use: STEP_NAME,
|
||||
Short: "Executes Terraform",
|
||||
Long: `This step executes the terraform binary with the given command, and is able to fetch additional variables from vault.`,
|
||||
PreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
startTime = time.Now()
|
||||
log.SetStepName(STEP_NAME)
|
||||
log.SetVerbose(GeneralConfig.Verbose)
|
||||
|
||||
path, _ := os.Getwd()
|
||||
fatalHook := &log.FatalHook{CorrelationID: GeneralConfig.CorrelationID, Path: path}
|
||||
log.RegisterHook(fatalHook)
|
||||
|
||||
err := PrepareConfig(cmd, &metadata, STEP_NAME, &stepConfig, config.OpenPiperFile)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorConfiguration)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 {
|
||||
sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID)
|
||||
log.RegisterHook(&sentryHook)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
telemetryData := telemetry.CustomData{}
|
||||
telemetryData.ErrorCode = "1"
|
||||
handler := func() {
|
||||
config.RemoveVaultSecretFiles()
|
||||
telemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds())
|
||||
telemetryData.ErrorCategory = log.GetErrorCategory().String()
|
||||
telemetry.Send(&telemetryData)
|
||||
}
|
||||
log.DeferExitHandler(handler)
|
||||
defer handler()
|
||||
telemetry.Initialize(GeneralConfig.NoTelemetry, STEP_NAME)
|
||||
terraformExecute(stepConfig, &telemetryData)
|
||||
telemetryData.ErrorCode = "0"
|
||||
log.Entry().Info("SUCCESS")
|
||||
},
|
||||
}
|
||||
|
||||
addTerraformExecuteFlags(createTerraformExecuteCmd, &stepConfig)
|
||||
return createTerraformExecuteCmd
|
||||
}
|
||||
|
||||
func addTerraformExecuteFlags(cmd *cobra.Command, stepConfig *terraformExecuteOptions) {
|
||||
cmd.Flags().StringVar(&stepConfig.Command, "command", `plan`, "")
|
||||
cmd.Flags().StringVar(&stepConfig.TerraformSecrets, "terraformSecrets", os.Getenv("PIPER_terraformSecrets"), "")
|
||||
cmd.Flags().StringSliceVar(&stepConfig.AdditionalArgs, "additionalArgs", []string{}, "")
|
||||
|
||||
}
|
||||
|
||||
// retrieve step metadata
|
||||
func terraformExecuteMetadata() config.StepData {
|
||||
var theMetaData = config.StepData{
|
||||
Metadata: config.StepMetadata{
|
||||
Name: "terraformExecute",
|
||||
Aliases: []config.Alias{},
|
||||
Description: "Executes Terraform",
|
||||
},
|
||||
Spec: config.StepSpec{
|
||||
Inputs: config.StepInputs{
|
||||
Parameters: []config.StepParameters{
|
||||
{
|
||||
Name: "command",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "terraformSecrets",
|
||||
ResourceRef: []config.ResourceReference{
|
||||
{
|
||||
Name: "",
|
||||
Paths: []string{"$(vaultPath)/terraformExecute", "$(vaultBasePath)/$(vaultPipelineName)/terraformExecute", "$(vaultBasePath)/GROUP-SECRETS/terraformExecute"},
|
||||
Type: "vaultSecretFile",
|
||||
},
|
||||
},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "additionalArgs",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "[]string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
},
|
||||
},
|
||||
Containers: []config.Container{
|
||||
{Name: "terraform", Image: "hashicorp/terraform:0.14.7"},
|
||||
},
|
||||
},
|
||||
}
|
||||
return theMetaData
|
||||
}
|
17
cmd/terraformExecute_generated_test.go
Normal file
17
cmd/terraformExecute_generated_test.go
Normal file
@ -0,0 +1,17 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTerraformExecuteCommand(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCmd := TerraformExecuteCommand()
|
||||
|
||||
// only high level testing performed - details are tested in step generation procedure
|
||||
assert.Equal(t, "terraformExecute", testCmd.Use, "command name incorrect")
|
||||
|
||||
}
|
83
cmd/terraformExecute_test.go
Normal file
83
cmd/terraformExecute_test.go
Normal file
@ -0,0 +1,83 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/SAP/jenkins-library/pkg/mock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type terraformExecuteMockUtils struct {
|
||||
*mock.ExecMockRunner
|
||||
*mock.FilesMock
|
||||
}
|
||||
|
||||
func newTerraformExecuteTestsUtils() terraformExecuteMockUtils {
|
||||
utils := terraformExecuteMockUtils{
|
||||
ExecMockRunner: &mock.ExecMockRunner{},
|
||||
FilesMock: &mock.FilesMock{},
|
||||
}
|
||||
return utils
|
||||
}
|
||||
|
||||
func TestRunTerraformExecute(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tt := []struct {
|
||||
terraformExecuteOptions
|
||||
expectedArgs []string
|
||||
}{
|
||||
{
|
||||
terraformExecuteOptions{
|
||||
Command: "apply",
|
||||
}, []string{"apply", "-auto-approve"},
|
||||
},
|
||||
{
|
||||
terraformExecuteOptions{
|
||||
Command: "apply",
|
||||
TerraformSecrets: "/tmp/test",
|
||||
}, []string{"apply", "-auto-approve", "-var-file=/tmp/test"},
|
||||
},
|
||||
{
|
||||
terraformExecuteOptions{
|
||||
Command: "plan",
|
||||
}, []string{"plan"},
|
||||
},
|
||||
{
|
||||
terraformExecuteOptions{
|
||||
Command: "plan",
|
||||
TerraformSecrets: "/tmp/test",
|
||||
}, []string{"plan", "-var-file=/tmp/test"},
|
||||
},
|
||||
{
|
||||
terraformExecuteOptions{
|
||||
Command: "plan",
|
||||
TerraformSecrets: "/tmp/test",
|
||||
AdditionalArgs: []string{"-arg1"},
|
||||
}, []string{"plan", "-var-file=/tmp/test", "-arg1"},
|
||||
},
|
||||
{
|
||||
terraformExecuteOptions{
|
||||
Command: "apply",
|
||||
TerraformSecrets: "/tmp/test",
|
||||
AdditionalArgs: []string{"-arg1"},
|
||||
}, []string{"apply", "-auto-approve", "-var-file=/tmp/test", "-arg1"},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tt {
|
||||
t.Run(fmt.Sprintf("That arguemtns are correct %d", i), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// init
|
||||
config := test.terraformExecuteOptions
|
||||
utils := newTerraformExecuteTestsUtils()
|
||||
|
||||
// test
|
||||
err := runTerraformExecute(&config, nil, utils)
|
||||
|
||||
// assert
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, mock.ExecCall{Exec: "terraform", Params: test.expectedArgs}, utils.Calls[0])
|
||||
})
|
||||
}
|
||||
}
|
36
resources/metadata/terraformExecute.yaml
Normal file
36
resources/metadata/terraformExecute.yaml
Normal file
@ -0,0 +1,36 @@
|
||||
metadata:
|
||||
name: terraformExecute
|
||||
description: Executes Terraform
|
||||
longDescription: |
|
||||
This step executes the terraform binary with the given command, and is able to fetch additional variables from vault.
|
||||
spec:
|
||||
inputs:
|
||||
params:
|
||||
- name: command
|
||||
type: string
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
default: plan
|
||||
- name: terraformSecrets
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
type: string
|
||||
resourceRef:
|
||||
- type: vaultSecretFile
|
||||
paths:
|
||||
- $(vaultPath)/terraformExecute
|
||||
- $(vaultBasePath)/$(vaultPipelineName)/terraformExecute
|
||||
- $(vaultBasePath)/GROUP-SECRETS/terraformExecute
|
||||
- name: additionalArgs
|
||||
type: "[]string"
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
containers:
|
||||
- name: terraform
|
||||
image: hashicorp/terraform:0.14.7
|
@ -173,6 +173,7 @@ public class CommonStepsTest extends BasePiperTest{
|
||||
'vaultRotateSecretId', //implementing new golang pattern without fields
|
||||
'deployIntegrationArtifact', //implementing new golang pattern without fields
|
||||
'newmanExecute', //implementing new golang pattern without fields
|
||||
'terraformExecute', //implementing new golang pattern without fields
|
||||
'whitesourceExecuteScan', //implementing new golang pattern without fields
|
||||
'uiVeri5ExecuteTests', //implementing new golang pattern without fields
|
||||
'integrationArtifactDeploy', //implementing new golang pattern without fields
|
||||
|
9
vars/terraformExecute.groovy
Normal file
9
vars/terraformExecute.groovy
Normal file
@ -0,0 +1,9 @@
|
||||
import groovy.transform.Field
|
||||
|
||||
@Field String STEP_NAME = getClass().getName()
|
||||
@Field String METADATA_FILE = 'metadata/terraformExecute.yaml'
|
||||
|
||||
void call(Map parameters = [:]) {
|
||||
List credentials = [[type: 'file', id: 'terraformSecrets', env: ['PIPER_terraformSecrets']]]
|
||||
piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials)
|
||||
}
|
Loading…
Reference in New Issue
Block a user