From 4e3fa38dd302d441e6d2cc1b5bf92c2d3e904f2f Mon Sep 17 00:00:00 2001 From: Vyacheslav Starostin <32613074+vstarostin@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:43:26 +0600 Subject: [PATCH] Clean-up checkIfStepActive (#4814) * Clean-up checkIfStepActive * Mark --useV1 deprecated * Clean up tests * Update test * Add warning message * Update warning msg --- cmd/checkIfStepActive.go | 41 +-- cmd/checkIfStepActive_test.go | 13 +- cmd/getDefaults.go | 8 +- pkg/config/evaluation.go | 231 +--------------- pkg/config/evaluation_test.go | 507 ---------------------------------- pkg/config/run.go | 76 ----- pkg/config/run_test.go | 245 ---------------- 7 files changed, 27 insertions(+), 1094 deletions(-) diff --git a/cmd/checkIfStepActive.go b/cmd/checkIfStepActive.go index ae39623f89..aff56baaf3 100644 --- a/cmd/checkIfStepActive.go +++ b/cmd/checkIfStepActive.go @@ -6,12 +6,12 @@ import ( "io" "os" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/SAP/jenkins-library/pkg/config" "github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/piperutils" - "github.com/bmatcuk/doublestar" - "github.com/pkg/errors" - "github.com/spf13/cobra" ) type checkStepActiveCommandOptions struct { @@ -63,6 +63,9 @@ func checkIfStepActive(utils piperutils.FileUtils) error { if checkStepActiveOptions.stageName == "" { return errors.New("stage name must not be empty") } + if checkStepActiveOptions.v1Active { + log.Entry().Warning("Please do not use --useV1 flag since it is deprecated and will be removed in future releases") + } var pConfig config.Config // load project config and defaults @@ -78,31 +81,15 @@ func checkIfStepActive(utils piperutils.FileUtils) error { } defer stageConfigFile.Close() - var runSteps map[string]map[string]bool - var runStages map[string]bool - // load and evaluate step conditions - if checkStepActiveOptions.v1Active { - runConfig := config.RunConfig{StageConfigFile: stageConfigFile} - runConfigV1 := &config.RunConfigV1{RunConfig: runConfig} - err = runConfigV1.InitRunConfigV1(projectConfig, utils, GeneralConfig.EnvRootPath) - if err != nil { - return err - } - runSteps = runConfigV1.RunSteps - runStages = runConfigV1.RunStages - } else { - log.Entry().Warning("This step is using deprecated format of stage conditions which will be removed in Jan 2024. " + - "To avoid pipeline breakage, please call checkIfStepActive command with --useV1 flag.", - ) - runConfig := &config.RunConfig{StageConfigFile: stageConfigFile} - err = runConfig.InitRunConfig(projectConfig, nil, nil, nil, nil, doublestar.Glob, checkStepActiveOptions.openFile) - if err != nil { - return err - } - runSteps = runConfig.RunSteps - runStages = runConfig.RunStages + runConfig := config.RunConfig{StageConfigFile: stageConfigFile} + runConfigV1 := &config.RunConfigV1{RunConfig: runConfig} + err = runConfigV1.InitRunConfigV1(projectConfig, utils, GeneralConfig.EnvRootPath) + if err != nil { + return err } + runSteps := runConfigV1.RunSteps + runStages := runConfigV1.RunStages log.Entry().Debugf("RunSteps: %v", runSteps) log.Entry().Debugf("RunStages: %v", runStages) @@ -149,7 +136,7 @@ func addCheckStepActiveFlags(cmd *cobra.Command) { "Default config of piper pipeline stages") cmd.Flags().StringVar(&checkStepActiveOptions.stepName, "step", "", "Name of the step being checked") cmd.Flags().StringVar(&checkStepActiveOptions.stageName, "stage", "", "Name of the stage in which contains the step being checked") - cmd.Flags().BoolVar(&checkStepActiveOptions.v1Active, "useV1", false, "Use new CRD-style stage configuration") + cmd.Flags().BoolVar(&checkStepActiveOptions.v1Active, "useV1", false, "Use new CRD-style stage configuration (deprecated)") cmd.Flags().StringVar(&checkStepActiveOptions.stageOutputFile, "stageOutputFile", "", "Defines a file path. If set, the stage output will be written to the defined file") cmd.Flags().StringVar(&checkStepActiveOptions.stepOutputFile, "stepOutputFile", "", "Defines a file path. If set, the step output will be written to the defined file") _ = cmd.MarkFlagRequired("step") diff --git a/cmd/checkIfStepActive_test.go b/cmd/checkIfStepActive_test.go index b6cee5542e..444975e92c 100644 --- a/cmd/checkIfStepActive_test.go +++ b/cmd/checkIfStepActive_test.go @@ -25,11 +25,14 @@ stages: steps:` case "stage-config.yml": fileContent = ` -stages: - testStage: - stepConditions: - testStep: - config: testConfig` +spec: + stages: + - name: testStage + displayName: testStage + steps: + - name: testStep + conditions: + - configKey: testConfig` case ".pipeline/config.yml": fileContent = ` steps: diff --git a/cmd/getDefaults.go b/cmd/getDefaults.go index c69ddd056e..855400c909 100644 --- a/cmd/getDefaults.go +++ b/cmd/getDefaults.go @@ -6,11 +6,12 @@ import ( "io" "os" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/SAP/jenkins-library/pkg/config" "github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/piperutils" - "github.com/pkg/errors" - "github.com/spf13/cobra" ) type defaultsCommandOptions struct { @@ -81,9 +82,6 @@ func getDefaults() ([]map[string]string, error) { var yamlContent string if !defaultsOptions.useV1 { - log.Entry().Warning("This step is using deprecated format of stage conditions which will be removed in Jan 2024. " + - "To avoid pipeline breakage, please call getDefaults command with --useV1 flag.", - ) var c config.Config c.ReadConfig(fc) diff --git a/pkg/config/evaluation.go b/pkg/config/evaluation.go index 150d74e1a8..1775181a51 100644 --- a/pkg/config/evaluation.go +++ b/pkg/config/evaluation.go @@ -3,14 +3,13 @@ package config import ( "encoding/json" "fmt" - "io" "path" "strings" + "github.com/pkg/errors" + "github.com/SAP/jenkins-library/pkg/orchestrator" "github.com/SAP/jenkins-library/pkg/piperutils" - - "github.com/pkg/errors" ) const ( @@ -254,232 +253,6 @@ func checkConfigKeyV1(config map[string]interface{}, configKey []string) (bool, return checkConfigKeyV1(castedValue, configKey[1:]) } -// EvaluateConditions validates stage conditions and updates runSteps in runConfig -func (r *RunConfig) evaluateConditions(config *Config, filters map[string]StepFilters, parameters map[string][]StepParameters, - secrets map[string][]StepSecrets, stepAliases map[string][]Alias, glob func(pattern string) (matches []string, err error)) error { - for stageName, stepConditions := range r.StageConfig.Stages { - runStep := map[string]bool{} - for stepName, stepCondition := range stepConditions.Conditions { - stepActive := false - stepConfig, err := r.getStepConfig(config, stageName, stepName, filters, parameters, secrets, stepAliases) - if err != nil { - return err - } - - if active, ok := stepConfig.Config[stepName].(bool); ok { - // respect explicit activation/de-activation if available - stepActive = active - } else { - for conditionName, condition := range stepCondition { - var err error - switch conditionName { - case configCondition: - if stepActive, err = checkConfig(condition, stepConfig, stepName); err != nil { - return errors.Wrapf(err, "error: check config condition failed") - } - case configKeysCondition: - if stepActive, err = checkConfigKeys(condition, stepConfig, stepName); err != nil { - return errors.Wrapf(err, "error: check configKeys condition failed") - } - case filePatternFromConfigCondition: - if stepActive, err = checkForFilesWithPatternFromConfig(condition, stepConfig, stepName, glob); err != nil { - return errors.Wrapf(err, "error: check filePatternFromConfig condition failed") - } - case filePatternCondition: - if stepActive, err = checkForFilesWithPattern(condition, stepConfig, stepName, glob); err != nil { - return errors.Wrapf(err, "error: check filePattern condition failed") - } - case npmScriptsCondition: - if stepActive, err = checkForNpmScriptsInPackages(condition, stepConfig, stepName, glob, r.OpenFile); err != nil { - return errors.Wrapf(err, "error: check npmScripts condition failed") - } - default: - return errors.Errorf("unknown condition %s", conditionName) - } - if stepActive { - break - } - } - } - runStep[stepName] = stepActive - r.RunSteps[stageName] = runStep - } - } - return nil -} - -func checkConfig(condition interface{}, config StepConfig, stepName string) (bool, error) { - switch condition := condition.(type) { - case string: - if configValue := stepConfigLookup(config.Config, stepName, condition); configValue != nil { - return true, nil - } - case map[string]interface{}: - for conditionConfigKey, conditionConfigValue := range condition { - configValue := stepConfigLookup(config.Config, stepName, conditionConfigKey) - if configValue == nil { - return false, nil - } - configValueStr, ok := configValue.(string) - if !ok { - return false, errors.Errorf("error: config value of %v to compare with is not a string", configValue) - } - condConfigValueArr, ok := conditionConfigValue.([]interface{}) - if !ok { - return false, errors.Errorf("error: type assertion to []interface{} failed: %T", conditionConfigValue) - } - for _, item := range condConfigValueArr { - itemStr, ok := item.(string) - if !ok { - return false, errors.Errorf("error: type assertion to string failed: %T", conditionConfigValue) - } - if configValueStr == itemStr { - return true, nil - } - } - } - default: - return false, errors.Errorf("error: condidiion type invalid: %T, possible types: string, map[string]interface{}", condition) - } - - return false, nil -} - -func checkConfigKey(configKey string, config StepConfig, stepName string) (bool, error) { - if configValue := stepConfigLookup(config.Config, stepName, configKey); configValue != nil { - return true, nil - } - return false, nil -} - -func checkConfigKeys(condition interface{}, config StepConfig, stepName string) (bool, error) { - arrCondition, ok := condition.([]interface{}) - if !ok { - return false, errors.Errorf("error: type assertion to []interface{} failed: %T", condition) - } - for _, configKey := range arrCondition { - if configValue := stepConfigLookup(config.Config, stepName, configKey.(string)); configValue != nil { - return true, nil - } - } - return false, nil -} - -func checkForFilesWithPatternFromConfig(condition interface{}, config StepConfig, stepName string, - glob func(pattern string) (matches []string, err error)) (bool, error) { - filePatternConfig, ok := condition.(string) - if !ok { - return false, errors.Errorf("error: type assertion to string failed: %T", condition) - } - filePatternFromConfig := stepConfigLookup(config.Config, stepName, filePatternConfig) - if filePatternFromConfig == nil { - return false, nil - } - filePattern, ok := filePatternFromConfig.(string) - if !ok { - return false, errors.Errorf("error: type assertion to string failed: %T", filePatternFromConfig) - } - matches, err := glob(filePattern) - if err != nil { - return false, errors.Wrap(err, "error: failed to check if file-exists") - } - if len(matches) > 0 { - return true, nil - } - return false, nil -} - -func checkForFilesWithPattern(condition interface{}, config StepConfig, stepName string, - glob func(pattern string) (matches []string, err error)) (bool, error) { - switch condition := condition.(type) { - case string: - filePattern := condition - matches, err := glob(filePattern) - if err != nil { - return false, errors.Wrap(err, "error: failed to check if file-exists") - } - if len(matches) > 0 { - return true, nil - } - case []interface{}: - filePatterns := condition - for _, filePattern := range filePatterns { - filePatternStr, ok := filePattern.(string) - if !ok { - return false, errors.Errorf("error: type assertion to string failed: %T", filePatternStr) - } - matches, err := glob(filePatternStr) - if err != nil { - return false, errors.Wrap(err, "error: failed to check if file-exists") - } - if len(matches) > 0 { - return true, nil - } - } - default: - return false, errors.Errorf("error: condidiion type invalid: %T, possible types: string, []interface{}", condition) - } - return false, nil -} - -func checkForNpmScriptsInPackages(condition interface{}, config StepConfig, stepName string, - glob func(pattern string) (matches []string, err error), openFile func(s string, t map[string]string) (io.ReadCloser, error)) (bool, error) { - packages, err := glob("**/package.json") - if err != nil { - return false, errors.Wrap(err, "error: failed to check if file-exists") - } - for _, pack := range packages { - packDirs := strings.Split(path.Dir(pack), "/") - isNodeModules := false - for _, dir := range packDirs { - if dir == "node_modules" { - isNodeModules = true - break - } - } - if isNodeModules { - continue - } - - jsonFile, err := openFile(pack, nil) - if err != nil { - return false, errors.Errorf("error: failed to open file %s: %v", pack, err) - } - defer jsonFile.Close() - packageJSON := map[string]interface{}{} - if err := json.NewDecoder(jsonFile).Decode(&packageJSON); err != nil { - return false, errors.Errorf("error: failed to unmarshal json file %s: %v", pack, err) - } - npmScripts, ok := packageJSON["scripts"] - if !ok { - continue - } - scriptsMap, ok := npmScripts.(map[string]interface{}) - if !ok { - return false, errors.Errorf("error: type assertion to map[string]interface{} failed: %T", npmScripts) - } - switch condition := condition.(type) { - case string: - if _, ok := scriptsMap[condition]; ok { - return true, nil - } - case []interface{}: - for _, conditionNpmScript := range condition { - conditionNpmScriptStr, ok := conditionNpmScript.(string) - if !ok { - return false, errors.Errorf("error: type assertion to string failed: %T", conditionNpmScript) - } - if _, ok := scriptsMap[conditionNpmScriptStr]; ok { - return true, nil - } - } - default: - return false, errors.Errorf("error: condidiion type invalid: %T, possible types: string, []interface{}", condition) - } - } - return false, nil -} - func checkForNpmScriptsInPackagesV1(npmScript string, config StepConfig, utils piperutils.FileUtils) (bool, error) { packages, err := utils.Glob("**/package.json") if err != nil { diff --git a/pkg/config/evaluation_test.go b/pkg/config/evaluation_test.go index 8d90a28ac0..73417b7ac3 100644 --- a/pkg/config/evaluation_test.go +++ b/pkg/config/evaluation_test.go @@ -4,7 +4,6 @@ package config import ( - "errors" "fmt" "io" "os" @@ -445,512 +444,6 @@ func TestEvaluateV1(t *testing.T) { } } -func TestEvaluateConditions(t *testing.T) { - tests := []struct { - name string - customConfig *Config - stageConfig io.ReadCloser - runStepsExpected map[string]map[string]bool - globFunc func(pattern string) ([]string, error) - wantErr bool - }{ - { - name: "test config condition - success", - customConfig: &Config{ - General: map[string]interface{}{ - "testGeneral": "myVal1", - }, - Stages: map[string]map[string]interface{}{ - "testStage2": { - "testStage": "myVal2", - }, - }, - Steps: map[string]map[string]interface{}{ - "thirdStep": { - "testStep1": "myVal3", - }, - }, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - config: testGeneral - testStage2: - stepConditions: - secondStep: - config: testStage - testStage3: - stepConditions: - thirdStep: - config: testStep1 - forthStep: - config: testStep2 - `)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": {"firstStep": true}, - "testStage2": {"secondStep": true}, - "testStage3": { - "thirdStep": true, - "forthStep": false, - }, - }, - wantErr: false, - }, - { - name: "test config condition - wrong usage with list", - customConfig: &Config{ - General: map[string]interface{}{}, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{}, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - config: - - testGeneral - `)), - runStepsExpected: map[string]map[string]bool{}, - wantErr: true, - }, - { - name: "test config value condition - success", - customConfig: &Config{ - General: map[string]interface{}{ - "testGeneral": "myVal1", - }, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{ - "thirdStep": { - "testStep": "myVal3", - }, - }, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - config: - testGeneral: - - myValx - - myVal1 - testStage2: - stepConditions: - secondStep: - config: - testStage: - - maValXyz - testStage3: - stepConditions: - thirdStep: - config: - testStep: - - myVal3 - `)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": {"firstStep": true}, - "testStage2": {"secondStep": false}, - "testStage3": {"thirdStep": true}, - }, - wantErr: false, - }, - { - name: "test configKey condition - success", - customConfig: &Config{ - General: map[string]interface{}{ - "myKey1_1": "myVal1_1", - }, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{ - "thirdStep": { - "myKey3_1": "myVal3_1", - }, - }, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - configKeys: - - myKey1_1 - - myKey1_2 - testStage2: - stepConditions: - secondStep: - configKeys: - - myKey2_1 - testStage3: - stepConditions: - thirdStep: - configKeys: - - myKey3_1 - `)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": {"firstStep": true}, - "testStage2": {"secondStep": false}, - "testStage3": {"thirdStep": true}, - }, - wantErr: false, - }, - { - name: "test configKey condition - not list", - customConfig: &Config{ - General: map[string]interface{}{ - "myKey1_1": "myVal1_1", - }, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{}, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - configKeys: myKey1_1 - `)), - wantErr: true, - }, - { - name: "test configKey condition - success", - customConfig: &Config{ - General: map[string]interface{}{ - "myKey1_1": "myVal1_1", - }, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{ - "thirdStep": { - "myKey3_1": "myVal3_1", - }, - }, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - configKeys: - - myKey1_1 - - myKey1_2 - testStage2: - stepConditions: - secondStep: - configKeys: - - myKey2_1 - testStage3: - stepConditions: - thirdStep: - configKeys: - - myKey3_1 - `)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": {"firstStep": true}, - "testStage2": {"secondStep": false}, - "testStage3": {"thirdStep": true}, - }, - wantErr: false, - }, - { - name: "test filePattern condition - success", - globFunc: evaluateConditionsGlobMock, - customConfig: &Config{ - General: map[string]interface{}{}, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{}, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - filePattern: '**/conf.js' - secondStep: - filePattern: '**/conf.jsx' - `)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": { - "firstStep": true, - "secondStep": false, - }, - }, - wantErr: false, - }, - { - name: "test filePattern condition - error while searching files by pattern", - customConfig: &Config{ - General: map[string]interface{}{}, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{}, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - filePattern: '**/conf.js' - `)), - runStepsExpected: map[string]map[string]bool{}, - globFunc: func(pattern string) ([]string, error) { - return nil, errors.New("failed to check if file exists") - }, - wantErr: true, - }, - { - name: "test filePattern condition with list - success", - globFunc: evaluateConditionsGlobMock, - customConfig: &Config{ - General: map[string]interface{}{}, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{}, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - filePattern: - - '**/conf.js' - - 'myCollection.json' - secondStep: - filePattern: - - '**/conf.jsx' - `)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": { - "firstStep": true, - "secondStep": false, - }, - }, - wantErr: false, - }, - { - name: "test filePattern condition with list - error while searching files by pattern", - customConfig: &Config{ - General: map[string]interface{}{}, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{}, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - filePattern: - - '**/conf.js' - - 'myCollection.json' - `)), - runStepsExpected: map[string]map[string]bool{}, - globFunc: func(pattern string) ([]string, error) { - return nil, errors.New("failed to check if file exists") - }, - wantErr: true, - }, - { - name: "test filePatternFromConfig condition - success", - globFunc: evaluateConditionsGlobMock, - customConfig: &Config{ - General: map[string]interface{}{}, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{ - "firstStep": { - "myVal1": "**/conf.js", - }, - "thirdStep": { - "myVal3": "**/conf.jsx", - }, - }, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - filePatternFromConfig: myVal1 - secondStep: - filePatternFromConfig: myVal2 - thirdStep: - filePatternFromConfig: myVal3 - `)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": { - "firstStep": true, - "secondStep": false, - "thirdStep": false, - }, - }, - wantErr: false, - }, - { - name: "test filePatternFromConfig condition - error while searching files by pattern", - customConfig: &Config{ - General: map[string]interface{}{}, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{ - "firstStep": { - "myVal1": "**/conf.js", - }, - }, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - filePatternFromConfig: myVal1 - `)), - runStepsExpected: map[string]map[string]bool{}, - globFunc: func(pattern string) ([]string, error) { - return nil, errors.New("failed to check if file exists") - }, - wantErr: true, - }, - { - name: "test npmScripts condition - success", - globFunc: evaluateConditionsGlobMock, - customConfig: &Config{ - General: map[string]interface{}{}, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{}, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - npmScripts: 'npmScript' - secondStep: - npmScripts: 'npmScript1' - `)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": { - "firstStep": true, - "secondStep": false, - }, - }, - wantErr: false, - }, - { - name: "test npmScripts condition with list - success", - globFunc: evaluateConditionsGlobMock, - customConfig: &Config{ - General: map[string]interface{}{}, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{}, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - npmScripts: - - 'npmScript' - - 'npmScript2' - secondStep: - npmScripts: - - 'npmScript3' - - 'npmScript4' - `)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": { - "firstStep": true, - "secondStep": false, - }, - }, - wantErr: false, - }, - { - name: "test npmScripts condition - json with wrong format", - customConfig: &Config{ - General: map[string]interface{}{}, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{}, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - npmScripts: - - 'npmScript3' - `)), - runStepsExpected: map[string]map[string]bool{}, - globFunc: func(pattern string) ([]string, error) { - return []string{"_package.json"}, nil - }, - wantErr: true, - }, - { - name: "test npmScripts condition - error while searching package.json", - customConfig: &Config{ - General: map[string]interface{}{}, - Stages: map[string]map[string]interface{}{}, - Steps: map[string]map[string]interface{}{}, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - npmScripts: - - 'npmScript3' - `)), - runStepsExpected: map[string]map[string]bool{}, - globFunc: func(pattern string) ([]string, error) { - return nil, errors.New("failed to check if file exists") - }, - wantErr: true, - }, - { - name: "test explicit activation / de-activation of step", - customConfig: &Config{ - Stages: map[string]map[string]interface{}{ - "testStage1": { - "firstStep": true, - "fisecondStep": false, - }, - }, - }, - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - config: testGeneral - secondStep: - config: testStage -`)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": { - "firstStep": true, - "secondStep": false, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - runConfig := RunConfig{ - StageConfigFile: tt.stageConfig, - RunSteps: map[string]map[string]bool{}, - OpenFile: evaluateConditionsOpenFileMock, - } - err := runConfig.loadConditions() - assert.NoError(t, err) - err = runConfig.evaluateConditions(tt.customConfig, nil, nil, nil, nil, tt.globFunc) - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.runStepsExpected, runConfig.RunSteps) - } - }) - } -} - func TestAnyOtherStepIsActive(t *testing.T) { targetStep := "step3" diff --git a/pkg/config/run.go b/pkg/config/run.go index d2f325d2b8..f65c4e23cd 100644 --- a/pkg/config/run.go +++ b/pkg/config/run.go @@ -94,27 +94,6 @@ func (r *RunConfigV1) InitRunConfigV1(config *Config, utils piperutils.FileUtils return nil } -// InitRunConfig ... -func (r *RunConfig) InitRunConfig(config *Config, filters map[string]StepFilters, parameters map[string][]StepParameters, - secrets map[string][]StepSecrets, stepAliases map[string][]Alias, glob func(pattern string) (matches []string, err error), - openFile func(s string, t map[string]string) (io.ReadCloser, error)) error { - r.OpenFile = openFile - r.RunSteps = map[string]map[string]bool{} - - if len(r.StageConfig.Stages) == 0 { - if err := r.loadConditions(); err != nil { - return errors.Wrap(err, "failed to load pipeline run conditions") - } - } - - err := r.evaluateConditions(config, filters, parameters, secrets, stepAliases, glob) - if err != nil { - return errors.Wrap(err, "failed to evaluate step conditions: %v") - } - - return nil -} - // ToDo: optimize parameter handling func (r *RunConfig) getStepConfig(config *Config, stageName, stepName string, filters map[string]StepFilters, parameters map[string][]StepParameters, secrets map[string][]StepSecrets, stepAliases map[string][]Alias) (StepConfig, error) { @@ -139,20 +118,6 @@ func (r *RunConfig) getStepConfig(config *Config, stageName, stepName string, fi return config.GetStepConfig(flagValues, paramJSON, nil, nil, false, filters[stepName], stepMeta, envParameters, stageName, stepName) } -func (r *RunConfig) loadConditions() error { - defer r.StageConfigFile.Close() - content, err := io.ReadAll(r.StageConfigFile) - if err != nil { - return errors.Wrapf(err, "error: failed to read the stageConfig file") - } - - err = yaml.Unmarshal(content, &r.StageConfig) - if err != nil { - return errors.Errorf("format of configuration is invalid %q: %v", content, err) - } - return nil -} - // LoadConditionsV1 loads stage conditions (in CRD-style) into PipelineConfig func (r *RunConfigV1) LoadConditionsV1() error { defer r.StageConfigFile.Close() @@ -167,44 +132,3 @@ func (r *RunConfigV1) LoadConditionsV1() error { } return nil } - -func stepConfigLookup(m map[string]interface{}, stepName, key string) interface{} { - // flat map: key is on top level - if m[key] != nil { - return m[key] - } - // lookup for step config with following format - // general: - // : - // stages: - // : - // : - // steps: - // : - // : - if m["general"] != nil { - general := m["general"].(map[string]interface{}) - if general[key] != nil { - return general[key] - } - } - if m["stages"] != nil { - stages := m["stages"].(map[string]interface{}) - if stages[stepName] != nil { - stageStepConfig := stages[stepName].(map[string]interface{}) - if stageStepConfig[key] != nil { - return stageStepConfig[key] - } - } - } - if m["steps"] != nil { - steps := m["steps"].(map[string]interface{}) - if steps[stepName] != nil { - stepConfig := steps[stepName].(map[string]interface{}) - if stepConfig[key] != nil { - return stepConfig[key] - } - } - } - return nil -} diff --git a/pkg/config/run_test.go b/pkg/config/run_test.go index 1fe7a1cc40..2bfcd9c7be 100644 --- a/pkg/config/run_test.go +++ b/pkg/config/run_test.go @@ -6,7 +6,6 @@ package config import ( "fmt" "io" - "reflect" "strings" "testing" @@ -71,247 +70,3 @@ func TestInitRunConfigV1(t *testing.T) { } } - -func TestInitRunConfig(t *testing.T) { - tests := []struct { - name string - customConfig io.ReadCloser - stageConfig io.ReadCloser - runStepsExpected map[string]map[string]bool - wantErr bool - }{ - { - name: "init run config with config condition - success", - customConfig: io.NopCloser(strings.NewReader(` -general: - testGeneral: 'myVal1' -stages: - testStage2: - testStage: 'myVal2' -steps: - thirdStep: - testStep: 'myVal3' - `)), - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - config: testGeneral - testStage2: - stepConditions: - secondStep: - config: testStage - testStage3: - stepConditions: - thirdStep: - config: testStep - `)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": { - "firstStep": true, - }, - "testStage2": { - "secondStep": true, - }, - "testStage3": { - "thirdStep": true, - }, - }, - wantErr: false, - }, - { - name: "init run config with filePattern condition - success", - customConfig: io.NopCloser(strings.NewReader(` -general: - testGeneral: 'myVal1' -stages: - testStage2: - testStage: 'myVal2' -steps: - thirdStep: - testStep: 'myVal3' - `)), - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage1: - stepConditions: - firstStep: - filePattern: "**/file1" - testStage2: - stepConditions: - secondStep: - filePattern: "directory/file2" - testStage3: - stepConditions: - thirdStep: - filePattern: "file3" - `)), - runStepsExpected: map[string]map[string]bool{ - "testStage1": { - "firstStep": true, - }, - "testStage2": { - "secondStep": true, - }, - "testStage3": { - "thirdStep": false, - }, - }, - wantErr: false, - }, - { - name: "init run config - unknown condition in stage config", - customConfig: io.NopCloser(strings.NewReader(` -steps: - testStep: - testConfig: 'testVal' - `)), - stageConfig: io.NopCloser(strings.NewReader(` -stages: - testStage: - stepConditions: - testStep: - wrongCondition: "condVal" - `)), - runStepsExpected: map[string]map[string]bool{}, - wantErr: true, - }, - { - name: "init run config - load conditions with invalid format", - stageConfig: io.NopCloser(strings.NewReader("wrong stage config format")), - runStepsExpected: map[string]map[string]bool{}, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - runConfig := RunConfig{StageConfigFile: tt.stageConfig} - filter := StepFilters{All: []string{}, General: []string{}, Stages: []string{}, Steps: []string{}, Env: []string{}} - projectConfig := Config{} - _, err := projectConfig.GetStepConfig(map[string]interface{}{}, "", tt.customConfig, - []io.ReadCloser{}, false, filter, StepData{}, nil, "", "") - assert.NoError(t, err) - err = runConfig.InitRunConfig(&projectConfig, nil, nil, nil, nil, initRunConfigGlobMock, nil) - if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.runStepsExpected, runConfig.RunSteps) - } - }) - } -} - -func TestRunConfigLoadConditions(t *testing.T) { - stageConfigContent := `stages: - 'testStage1': - stepConditions: - firstStep: - filePattern: '**/my.file' -` - t.Run("load conditions - file of invalid format", func(t *testing.T) { - runConfig := &RunConfig{StageConfigFile: io.NopCloser(strings.NewReader("-- {{ \\ wrong } file format }"))} - err := runConfig.loadConditions() - assert.Error(t, err, "format of configuration is invalid") - }) - - t.Run("load conditions - success", func(t *testing.T) { - runConfig := &RunConfig{StageConfigFile: io.NopCloser(strings.NewReader(stageConfigContent))} - - err := runConfig.loadConditions() - assert.NoError(t, err) - condition := map[string]interface{}{ - "filePattern": "**/my.file", - } - - assert.Equal(t, 1, len(runConfig.StageConfig.Stages)) - assert.Equal(t, 1, len(runConfig.StageConfig.Stages["testStage1"].Conditions)) - assert.Equal(t, condition, runConfig.StageConfig.Stages["testStage1"].Conditions["firstStep"]) - }) -} - -func Test_stepConfigLookup(t *testing.T) { - - testConfig := map[string]interface{}{ - "general": map[string]interface{}{ - "generalKey": "generalValue", - }, - "stages": map[string]interface{}{ - "testStep": map[string]interface{}{ - "stagesKey": "stagesValue", - }, - }, - "steps": map[string]interface{}{ - "testStep": map[string]interface{}{ - "stepKey": "stepValue", - "stepKeyStringSlice": []string{"val1", "val2"}, - }, - }, - "configKey": "configValue", - } - - type args struct { - m map[string]interface{} - stepName string - key string - } - tests := []struct { - name string - args args - want interface{} - }{ - { - name: "empty map", - args: args{nil, "", ""}, - want: nil, - }, - { - name: "key not in map, invalid stepName", - args: args{testConfig, "some step", "some key"}, - want: nil, - }, - { - name: "key not in map, valid stepName", - args: args{testConfig, "testStep", "some key"}, - want: nil, - }, - { - name: "key in map under general", - args: args{testConfig, "some step", "generalKey"}, - want: "generalValue", - }, - { - name: "key in map under stages", - args: args{testConfig, "testStep", "stagesKey"}, - want: "stagesValue", - }, - { - name: "key in map under general", - args: args{testConfig, "testStep", "stepKey"}, - want: "stepValue", - }, - { - name: "key in map under general", - args: args{testConfig, "testStep", "stepKeyStringSlice"}, - want: []string{"val1", "val2"}, - }, - { - name: "key in map on top level string", - args: args{testConfig, "", "configKey"}, - want: "configValue", - }, - { - name: "key in map on top level map", - args: args{testConfig, "", "general"}, - want: map[string]interface{}{"generalKey": "generalValue"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := stepConfigLookup(tt.args.m, tt.args.stepName, tt.args.key); !reflect.DeepEqual(got, tt.want) { - t.Errorf("stepConfigLookup() = %v, want %v", got, tt.want) - } - }) - } -}