1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-16 05:16:08 +02:00

piper: Use stageName from parametersJSON, log value and source (#1920)

stageName from parametersJSON was ignored up until now and there was a TODO in the code. The precedence from the TODO is not clear to me. It said "first env.STAGE_NAME, second: parametersJSON, third: flag". So maybe I implemented it wrongly, but it would be easy to change. Right now, STAGE_NAME is always in the ENV when piper is called from Jenkins, but to mimic the behavior of being able to pass `stageName` in the parameters and override the ENV value, this is what happens in go as well now.

The reason why `stageName` needs to be evaluated before other step parameters is that it affects config evaluation. So I've added a function which is called first in `PrepareConfig()`.
This commit is contained in:
Stephan Aßmus 2020-08-13 17:55:12 +02:00 committed by GitHub
parent 6a71feeafd
commit b1a0cb089c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 6 deletions

View File

@ -33,6 +33,7 @@ func ConfigCommand() *cobra.Command {
path, _ := os.Getwd()
fatalHook := &log.FatalHook{CorrelationID: GeneralConfig.CorrelationID, Path: path}
log.RegisterHook(fatalHook)
initStageName(false)
},
Run: func(cmd *cobra.Command, _ []string) {
err := generateConfig()

View File

@ -52,7 +52,6 @@ var rootCmd = &cobra.Command{
This project 'Piper' binary provides a CI/CD step library.
It contains many steps which can be used within CI/CD systems as well as directly on e.g. a developer's machine.
`,
//ToDo: respect stageName to also come from parametersJSON -> first env.STAGE_NAME, second: parametersJSON, third: flag
}
// GeneralConfig contains global configuration flags for piper binary
@ -113,7 +112,7 @@ func addRootFlags(rootCmd *cobra.Command) {
rootCmd.PersistentFlags().BoolVar(&GeneralConfig.IgnoreCustomDefaults, "ignoreCustomDefaults", false, "Disables evaluation of the parameter 'customDefaults' in the pipeline configuration file")
rootCmd.PersistentFlags().StringVar(&GeneralConfig.ParametersJSON, "parametersJSON", os.Getenv("PIPER_parametersJSON"), "Parameters to be considered in JSON format")
rootCmd.PersistentFlags().StringVar(&GeneralConfig.EnvRootPath, "envRootPath", ".pipeline", "Root path to Piper pipeline shared environments")
rootCmd.PersistentFlags().StringVar(&GeneralConfig.StageName, "stageName", os.Getenv("STAGE_NAME"), "Name of the stage for which configuration should be included")
rootCmd.PersistentFlags().StringVar(&GeneralConfig.StageName, "stageName", "", "Name of the stage for which configuration should be included")
rootCmd.PersistentFlags().StringVar(&GeneralConfig.StepConfigJSON, "stepConfigJSON", os.Getenv("PIPER_stepConfigJSON"), "Step configuration in JSON format")
rootCmd.PersistentFlags().BoolVar(&GeneralConfig.NoTelemetry, "noTelemetry", false, "Disables telemetry reporting")
rootCmd.PersistentFlags().BoolVarP(&GeneralConfig.Verbose, "verbose", "v", false, "verbose output")
@ -121,9 +120,60 @@ func addRootFlags(rootCmd *cobra.Command) {
}
const stageNameEnvKey = "STAGE_NAME"
// initStageName initializes GeneralConfig.StageName from either GeneralConfig.ParametersJSON
// or the environment variable 'STAGE_NAME', unless it has been provided as command line option.
// Log output needs to be suppressed via outputToLog by the getConfig step.
func initStageName(outputToLog bool) {
var stageNameSource string
if outputToLog {
defer func() {
log.Entry().Infof("Using stageName '%s' from %s", GeneralConfig.StageName, stageNameSource)
}()
}
if GeneralConfig.StageName != "" {
// Means it was given as command line argument and has the highest precedence
stageNameSource = "command line arguments"
return
}
// Use stageName from ENV as fall-back, for when extracting it from parametersJSON fails below
GeneralConfig.StageName = os.Getenv(stageNameEnvKey)
stageNameSource = fmt.Sprintf("env variable '%s'", stageNameEnvKey)
if len(GeneralConfig.ParametersJSON) == 0 {
return
}
var params map[string]interface{}
err := json.Unmarshal([]byte(GeneralConfig.ParametersJSON), &params)
if err != nil {
if outputToLog {
log.Entry().Infof("Failed to extract 'stageName' from parametersJSON: %v", err)
}
return
}
stageName, hasKey := params["stageName"]
if !hasKey {
return
}
if stageNameString, ok := stageName.(string); ok && stageNameString != "" {
stageNameSource = "parametersJSON"
GeneralConfig.StageName = stageNameString
}
}
// PrepareConfig reads step configuration from various sources and merges it (defaults, config file, flags, ...)
func PrepareConfig(cmd *cobra.Command, metadata *config.StepData, stepName string, options interface{}, openFile func(s string) (io.ReadCloser, error)) error {
log.SetFormatter(GeneralConfig.LogFormat)
initStageName(true)
filters := metadata.GetParameterFilters()
// add telemetry parameter "collectTelemetryData" to ALL, GENERAL and PARAMETER filters
@ -138,8 +188,6 @@ func PrepareConfig(cmd *cobra.Command, metadata *config.StepData, stepName strin
var myConfig config.Config
var stepConfig config.StepConfig
log.SetFormatter(GeneralConfig.LogFormat)
if len(GeneralConfig.StepConfigJSON) != 0 {
// ignore config & defaults in favor of passed stepConfigJSON
stepConfig = config.GetStepConfigWithJSON(flagValues, GeneralConfig.StepConfigJSON, filters)

View File

@ -31,6 +31,56 @@ func TestAddRootFlags(t *testing.T) {
}
func TestAdoptStageNameFromParametersJSON(t *testing.T) {
tt := []struct {
name string
stageNameArg string
stageNameEnv string
stageNameJSON string
}{
{name: "no stage name", stageNameArg: "", stageNameEnv: "", stageNameJSON: ""},
{name: "stage name arg+env", stageNameArg: "arg", stageNameEnv: "env", stageNameJSON: "json"},
{name: "stage name env", stageNameArg: "", stageNameEnv: "env", stageNameJSON: "json"},
{name: "stage name json", stageNameArg: "", stageNameEnv: "", stageNameJSON: "json"},
{name: "stage name arg", stageNameArg: "arg", stageNameEnv: "", stageNameJSON: "json"},
}
for _, test := range tt {
t.Run(test.name, func(t *testing.T) {
// init
GeneralConfig.StageName = test.stageNameArg
resetValue := os.Getenv(stageNameEnvKey)
defer func() { _ = os.Setenv(stageNameEnvKey, resetValue) }()
err := os.Setenv(stageNameEnvKey, test.stageNameEnv)
if err != nil {
t.Fatalf("could not set env var %s", stageNameEnvKey)
}
if test.stageNameJSON != "" {
GeneralConfig.ParametersJSON = fmt.Sprintf("{\"stageName\":\"%s\"}", test.stageNameJSON)
} else {
GeneralConfig.ParametersJSON = "{}"
}
// test
initStageName(false)
// assert
// Order of if-clauses reflects wanted precedence.
if test.stageNameArg != "" {
assert.Equal(t, test.stageNameArg, GeneralConfig.StageName)
} else if test.stageNameJSON != "" {
assert.Equal(t, test.stageNameJSON, GeneralConfig.StageName)
} else if test.stageNameEnv != "" {
assert.Equal(t, test.stageNameEnv, GeneralConfig.StageName)
} else {
assert.Equal(t, "", GeneralConfig.StageName)
}
})
}
}
func TestPrepareConfig(t *testing.T) {
defaultsBak := GeneralConfig.DefaultConfig
GeneralConfig.DefaultConfig = []string{"testDefaults.yml"}
@ -73,7 +123,7 @@ func TestPrepareConfig(t *testing.T) {
}
err := PrepareConfig(testCmd, &metadata, "testStep", &testOptions, mock.OpenFileMock)
assert.NoError(t, err, "no error expected but error occured")
assert.NoError(t, err, "no error expected but error occurred")
//assert config
assert.Equal(t, "testValue", testOptions.TestParam, "wrong value retrieved from config")
@ -93,7 +143,7 @@ func TestPrepareConfig(t *testing.T) {
metadata := config.StepData{}
err := PrepareConfig(testCmd, &metadata, "testStep", &testOptions, mock.OpenFileMock)
assert.Error(t, err, "error expected but none occured")
assert.Error(t, err, "error expected but none occurred")
})
})
}