package cmd import ( "encoding/json" "fmt" "io" "os" "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 { openFile func(s string, t map[string]string) (io.ReadCloser, error) fileExists func(filename string) (bool, error) stageConfigFile string stepName string stageName string v1Active bool stageOutputFile string stepOutputFile string } var checkStepActiveOptions checkStepActiveCommandOptions // CheckStepActiveCommand is the entry command for checking if a step is active in a defined stage func CheckStepActiveCommand() *cobra.Command { checkStepActiveOptions.openFile = config.OpenPiperFile checkStepActiveOptions.fileExists = piperutils.FileExists var checkStepActiveCmd = &cobra.Command{ Use: "checkIfStepActive", Short: "Checks if a step is active in a defined stage.", PreRun: func(cmd *cobra.Command, _ []string) { path, _ := os.Getwd() fatalHook := &log.FatalHook{CorrelationID: GeneralConfig.CorrelationID, Path: path} log.RegisterHook(fatalHook) initStageName(false) log.SetVerbose(GeneralConfig.Verbose) GeneralConfig.GitHubAccessTokens = ResolveAccessTokens(GeneralConfig.GitHubTokens) }, Run: func(cmd *cobra.Command, _ []string) { utils := &piperutils.Files{} err := checkIfStepActive(utils) if err != nil { log.SetErrorCategory(log.ErrorConfiguration) log.Entry().WithError(err).Fatal("Checking for an active step failed") } }, } addCheckStepActiveFlags(checkStepActiveCmd) return checkStepActiveCmd } func checkIfStepActive(utils piperutils.FileUtils) error { // make the stageName the leading parameter if len(checkStepActiveOptions.stageName) == 0 && GeneralConfig.StageName != "" { checkStepActiveOptions.stageName = GeneralConfig.StageName } if checkStepActiveOptions.stageName == "" { return errors.New("stage name must not be empty") } var pConfig config.Config // load project config and defaults projectConfig, err := initializeConfig(&pConfig) if err != nil { log.Entry().Errorf("Failed to load project config: %v", err) return errors.Wrapf(err, "Failed to load project config failed") } stageConfigFile, err := checkStepActiveOptions.openFile(checkStepActiveOptions.stageConfigFile, GeneralConfig.GitHubAccessTokens) if err != nil { return errors.Wrapf(err, "config: open stage configuration file '%v' failed", checkStepActiveOptions.stageConfigFile) } 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 { 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 } log.Entry().Debugf("RunSteps: %v", runSteps) log.Entry().Debugf("RunStages: %v", runStages) if len(checkStepActiveOptions.stageOutputFile) > 0 || len(checkStepActiveOptions.stepOutputFile) > 0 { if len(checkStepActiveOptions.stageOutputFile) > 0 { result, err := json.Marshal(runStages) if err != nil { return fmt.Errorf("error marshalling json: %w", err) } log.Entry().Infof("Writing stage condition file %v", checkStepActiveOptions.stageOutputFile) err = utils.FileWrite(checkStepActiveOptions.stageOutputFile, result, 0666) if err != nil { return fmt.Errorf("error writing file '%v': %w", checkStepActiveOptions.stageOutputFile, err) } } if len(checkStepActiveOptions.stepOutputFile) > 0 { result, err := json.Marshal(runSteps) if err != nil { return fmt.Errorf("error marshalling json: %w", err) } log.Entry().Infof("Writing step condition file %v", checkStepActiveOptions.stepOutputFile) err = utils.FileWrite(checkStepActiveOptions.stepOutputFile, result, 0666) if err != nil { return fmt.Errorf("error writing file '%v': %w", checkStepActiveOptions.stepOutputFile, err) } } // do not perform a check if output files are written return nil } if !runSteps[checkStepActiveOptions.stageName][checkStepActiveOptions.stepName] { return errors.Errorf("Step %s in stage %s is not active", checkStepActiveOptions.stepName, checkStepActiveOptions.stageName) } log.Entry().Infof("Step %s in stage %s is active", checkStepActiveOptions.stepName, checkStepActiveOptions.stageName) return nil } func addCheckStepActiveFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&checkStepActiveOptions.stageConfigFile, "stageConfig", ".resources/piper-stage-config.yml", "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().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") } func initializeConfig(pConfig *config.Config) (*config.Config, error) { projectConfigFile := getProjectConfigFile(GeneralConfig.CustomConfig) var customConfig io.ReadCloser var err error //accept that config file cannot be loaded as its not mandatory here if exists, err := checkStepActiveOptions.fileExists(projectConfigFile); exists { log.Entry().Infof("Project config: '%s'", projectConfigFile) customConfig, err = checkStepActiveOptions.openFile(projectConfigFile, GeneralConfig.GitHubAccessTokens) if err != nil { return nil, errors.Wrapf(err, "config: open configuration file '%v' failed", projectConfigFile) } defer customConfig.Close() } else { log.Entry().Infof("Project config: NONE ('%s' does not exist)", projectConfigFile) } defaultConfig := []io.ReadCloser{} for _, f := range GeneralConfig.DefaultConfig { fc, err := checkStepActiveOptions.openFile(f, GeneralConfig.GitHubAccessTokens) // only create error for non-default values if err != nil && f != ".pipeline/defaults.yaml" { return nil, errors.Wrapf(err, "config: getting defaults failed: '%v'", f) } if err == nil { defaultConfig = append(defaultConfig, fc) } } var flags map[string]interface{} filter := config.StepFilters{ All: []string{}, General: []string{}, Stages: []string{}, Steps: []string{}, Env: []string{}, } _, err = pConfig.GetStepConfig(flags, "", customConfig, defaultConfig, GeneralConfig.IgnoreCustomDefaults, filter, config.StepData{}, nil, "", "") if err != nil { return nil, errors.Wrap(err, "getting step config failed") } return pConfig, nil }