github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/config/run.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	"github.com/SAP/jenkins-library/pkg/piperutils"
     8  	"github.com/ghodss/yaml"
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  // RunConfig ...
    13  type RunConfig struct {
    14  	StageConfigFile io.ReadCloser
    15  	StageConfig     StageConfig
    16  	RunStages       map[string]bool
    17  	RunSteps        map[string]map[string]bool
    18  	OpenFile        func(s string, t map[string]string) (io.ReadCloser, error)
    19  	FileUtils       *piperutils.Files
    20  }
    21  
    22  type RunConfigV1 struct {
    23  	RunConfig
    24  	PipelineConfig PipelineDefinitionV1
    25  }
    26  
    27  type StageConfig struct {
    28  	Stages map[string]StepConditions `json:"stages,omitempty"`
    29  }
    30  
    31  type StepConditions struct {
    32  	Conditions map[string]map[string]interface{} `json:"stepConditions,omitempty"`
    33  }
    34  
    35  type PipelineDefinitionV1 struct {
    36  	APIVersion string   `json:"apiVersion"`
    37  	Kind       string   `json:"kind"`
    38  	Metadata   Metadata `json:"metadata"`
    39  	Spec       Spec     `json:"spec"`
    40  	openFile   func(s string, t map[string]string) (io.ReadCloser, error)
    41  	runSteps   map[string]map[string]bool
    42  }
    43  
    44  type Metadata struct {
    45  	Name        string `json:"name,omitempty"`
    46  	DisplayName string `json:"displayName,omitempty"`
    47  	Description string `json:"description,omitempty"`
    48  }
    49  
    50  type Spec struct {
    51  	Stages []Stage `json:"stages"`
    52  }
    53  
    54  type Stage struct {
    55  	Name        string `json:"name,omitempty"`
    56  	DisplayName string `json:"displayName,omitempty"`
    57  	Description string `json:"description,omitempty"`
    58  	Steps       []Step `json:"steps,omitempty"`
    59  }
    60  
    61  type Step struct {
    62  	Name                string          `json:"name,omitempty"`
    63  	Description         string          `json:"description,omitempty"`
    64  	Conditions          []StepCondition `json:"conditions,omitempty"`
    65  	NotActiveConditions []StepCondition `json:"notActiveConditions,omitempty"`
    66  	Orchestrators       []string        `json:"orchestrators,omitempty"`
    67  }
    68  
    69  type StepCondition struct {
    70  	Config                    map[string][]interface{} `json:"config,omitempty"`
    71  	ConfigKey                 string                   `json:"configKey,omitempty"`
    72  	FilePattern               string                   `json:"filePattern,omitempty"`
    73  	FilePatternFromConfig     string                   `json:"filePatternFromConfig,omitempty"`
    74  	Inactive                  bool                     `json:"inactive,omitempty"`
    75  	OnlyActiveStepInStage     bool                     `json:"onlyActiveStepInStage,omitempty"`
    76  	NpmScript                 string                   `json:"npmScript,omitempty"`
    77  	CommonPipelineEnvironment map[string]interface{}   `json:"commonPipelineEnvironment,omitempty"`
    78  	PipelineEnvironmentFilled string                   `json:"pipelineEnvironmentFilled,omitempty"`
    79  }
    80  
    81  func (r *RunConfigV1) InitRunConfigV1(config *Config, utils piperutils.FileUtils, envRootPath string) error {
    82  
    83  	if len(r.PipelineConfig.Spec.Stages) == 0 {
    84  		if err := r.LoadConditionsV1(); err != nil {
    85  			return fmt.Errorf("failed to load pipeline run conditions: %w", err)
    86  		}
    87  	}
    88  
    89  	err := r.evaluateConditionsV1(config, utils, envRootPath)
    90  	if err != nil {
    91  		return fmt.Errorf("failed to evaluate step conditions: %w", err)
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  // InitRunConfig ...
    98  func (r *RunConfig) InitRunConfig(config *Config, filters map[string]StepFilters, parameters map[string][]StepParameters,
    99  	secrets map[string][]StepSecrets, stepAliases map[string][]Alias, glob func(pattern string) (matches []string, err error),
   100  	openFile func(s string, t map[string]string) (io.ReadCloser, error)) error {
   101  	r.OpenFile = openFile
   102  	r.RunSteps = map[string]map[string]bool{}
   103  
   104  	if len(r.StageConfig.Stages) == 0 {
   105  		if err := r.loadConditions(); err != nil {
   106  			return errors.Wrap(err, "failed to load pipeline run conditions")
   107  		}
   108  	}
   109  
   110  	err := r.evaluateConditions(config, filters, parameters, secrets, stepAliases, glob)
   111  	if err != nil {
   112  		return errors.Wrap(err, "failed to evaluate step conditions: %v")
   113  	}
   114  
   115  	return nil
   116  }
   117  
   118  // ToDo: optimize parameter handling
   119  func (r *RunConfig) getStepConfig(config *Config, stageName, stepName string, filters map[string]StepFilters,
   120  	parameters map[string][]StepParameters, secrets map[string][]StepSecrets, stepAliases map[string][]Alias) (StepConfig, error) {
   121  	// no support for flag values and envParameters
   122  	// so far not considered necessary
   123  
   124  	flagValues := map[string]interface{}{} // args of step from pipeline_generated.yml
   125  
   126  	envParameters := map[string]interface{}{}
   127  
   128  	// parameters via paramJSON not supported
   129  	// not considered releavant for pipeline yaml syntax resolution
   130  	paramJSON := ""
   131  
   132  	stepMeta := StepData{
   133  		Spec: StepSpec{
   134  			Inputs: StepInputs{Parameters: parameters[stepName], Secrets: secrets[stepName]},
   135  		},
   136  		Metadata: StepMetadata{Aliases: stepAliases[stepName]},
   137  	}
   138  
   139  	return config.GetStepConfig(flagValues, paramJSON, nil, nil, false, filters[stepName], stepMeta, envParameters, stageName, stepName)
   140  }
   141  
   142  func (r *RunConfig) loadConditions() error {
   143  	defer r.StageConfigFile.Close()
   144  	content, err := io.ReadAll(r.StageConfigFile)
   145  	if err != nil {
   146  		return errors.Wrapf(err, "error: failed to read the stageConfig file")
   147  	}
   148  
   149  	err = yaml.Unmarshal(content, &r.StageConfig)
   150  	if err != nil {
   151  		return errors.Errorf("format of configuration is invalid %q: %v", content, err)
   152  	}
   153  	return nil
   154  }
   155  
   156  // LoadConditionsV1 loads stage conditions (in CRD-style) into PipelineConfig
   157  func (r *RunConfigV1) LoadConditionsV1() error {
   158  	defer r.StageConfigFile.Close()
   159  	content, err := io.ReadAll(r.StageConfigFile)
   160  	if err != nil {
   161  		return errors.Wrapf(err, "error: failed to read the stageConfig file")
   162  	}
   163  
   164  	err = yaml.Unmarshal(content, &r.PipelineConfig)
   165  	if err != nil {
   166  		return errors.Errorf("format of configuration is invalid %q: %v", content, err)
   167  	}
   168  	return nil
   169  }
   170  
   171  func stepConfigLookup(m map[string]interface{}, stepName, key string) interface{} {
   172  	// flat map: key is on top level
   173  	if m[key] != nil {
   174  		return m[key]
   175  	}
   176  	// lookup for step config with following format
   177  	// general:
   178  	//   <key>: <value>
   179  	// stages:
   180  	//   <stepName>:
   181  	//     <key>: <value>
   182  	// steps:
   183  	//   <stepName>:
   184  	//     <key>: <value>
   185  	if m["general"] != nil {
   186  		general := m["general"].(map[string]interface{})
   187  		if general[key] != nil {
   188  			return general[key]
   189  		}
   190  	}
   191  	if m["stages"] != nil {
   192  		stages := m["stages"].(map[string]interface{})
   193  		if stages[stepName] != nil {
   194  			stageStepConfig := stages[stepName].(map[string]interface{})
   195  			if stageStepConfig[key] != nil {
   196  				return stageStepConfig[key]
   197  			}
   198  		}
   199  	}
   200  	if m["steps"] != nil {
   201  		steps := m["steps"].(map[string]interface{})
   202  		if steps[stepName] != nil {
   203  			stepConfig := steps[stepName].(map[string]interface{})
   204  			if stepConfig[key] != nil {
   205  				return stepConfig[key]
   206  			}
   207  		}
   208  	}
   209  	return nil
   210  }