1
0
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:
Kevin Stiehl 2021-03-29 16:22:23 +02:00 committed by GitHub
parent b612a1fb3f
commit fe6ea643fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 342 additions and 0 deletions

View File

@ -69,6 +69,7 @@ func GetAllStepMetadata() map[string]config.StepData {
"protecodeExecuteScan": protecodeExecuteScanMetadata(),
"containerSaveImage": containerSaveImageMetadata(),
"sonarExecuteScan": sonarExecuteScanMetadata(),
"terraformExecute": terraformExecuteMetadata(),
"transportRequestUploadCTS": transportRequestUploadCTSMetadata(),
"transportRequestUploadSOLMAN": transportRequestUploadSOLMANMetadata(),
"uiVeri5ExecuteTests": uiVeri5ExecuteTestsMetadata(),

View File

@ -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
View 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
}

View 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
}

View 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")
}

View 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])
})
}
}

View 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

View File

@ -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

View 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)
}