2020-09-23 13:55:17 +02:00
package generator
import (
"fmt"
2021-09-21 13:06:32 +02:00
"path"
2020-09-23 13:55:17 +02:00
"sort"
"strings"
"github.com/SAP/jenkins-library/pkg/config"
)
// Replaces the Parameters placeholder with the content from the yaml
func createParametersSection ( stepData * config . StepData ) string {
2020-10-15 08:21:57 +02:00
var parameters = "## Parameters\n\n"
2020-09-23 13:55:17 +02:00
// sort parameters alphabetically with mandatory parameters first
sortStepParameters ( stepData , true )
2021-10-11 15:22:24 +02:00
parameters += "### Overview - Step\n\n"
parameters += createParameterOverview ( stepData , false )
parameters += "### Overview - Execution Environment\n\n"
parameters += "!!! note \"Orchestrator-specific only\"\n\n These parameters are relevant for orchestrator usage and not considered when using the command line option.\n\n"
parameters += createParameterOverview ( stepData , true )
2020-09-23 13:55:17 +02:00
// sort parameters alphabetically
sortStepParameters ( stepData , false )
parameters += "### Details\n\n"
parameters += createParameterDetails ( stepData )
return parameters
}
2021-10-12 12:49:38 +02:00
func parameterMandatoryInformation ( param config . StepParameters , furtherInfo string ) ( mandatory bool , mandatoryString string , mandatoryInfo string ) {
mandatory = param . Mandatory
mandatoryInfo = furtherInfo
mandatoryIf := param . MandatoryIf
if len ( mandatoryIf ) > 0 {
mandatory = true
if len ( mandatoryInfo ) > 0 {
mandatoryInfo += "<br />"
}
furtherInfoConditions := [ ] string { "mandatory in case of:" }
for _ , mandatoryCondition := range mandatoryIf {
furtherInfoConditions = append ( furtherInfoConditions , fmt . Sprintf ( "- [`%v`](#%v)=`%v`" , mandatoryCondition . Name , strings . ToLower ( mandatoryCondition . Name ) , mandatoryCondition . Value ) )
}
mandatoryInfo += strings . Join ( furtherInfoConditions , "<br />" )
}
mandatoryString = "**yes**"
if len ( mandatoryInfo ) > 0 {
mandatoryString = "**(yes)**"
}
return
}
2021-10-11 15:22:24 +02:00
func createParameterOverview ( stepData * config . StepData , executionEnvironment bool ) string {
2020-09-23 13:55:17 +02:00
var table = "| Name | Mandatory | Additional information |\n"
table += "| ---- | --------- | ---------------------- |\n"
for _ , param := range stepData . Spec . Inputs . Parameters {
2021-10-11 15:22:24 +02:00
furtherInfo , err := parameterFurtherInfo ( param . Name , stepData , executionEnvironment )
if err == nil {
2021-10-12 12:49:38 +02:00
var mandatory bool
var mandatoryString string
mandatory , mandatoryString , furtherInfo = parameterMandatoryInformation ( param , furtherInfo )
table += fmt . Sprintf ( "| [%v](#%v) | %v | %v |\n" , param . Name , strings . ToLower ( param . Name ) , ifThenElse ( mandatory , mandatoryString , "no" ) , furtherInfo )
2021-10-11 15:22:24 +02:00
}
2020-09-23 13:55:17 +02:00
}
table += "\n"
return table
}
2021-10-11 15:22:24 +02:00
func parameterFurtherInfo ( paramName string , stepData * config . StepData , executionEnvironment bool ) ( string , error ) {
2020-09-23 13:55:17 +02:00
// handle general parameters
// ToDo: add special handling once we have more than one general parameter to consider
if paramName == "verbose" {
2021-10-11 15:22:24 +02:00
return checkParameterInfo ( "activates debug output" , true , executionEnvironment )
2020-09-23 13:55:17 +02:00
}
if paramName == "script" {
2021-10-11 15:22:24 +02:00
return checkParameterInfo ( "[![Jenkins only](https://img.shields.io/badge/-Jenkins%20only-yellowgreen)](#) reference to Jenkins main pipeline script" , true , executionEnvironment )
2020-09-23 13:55:17 +02:00
}
2021-10-11 15:22:24 +02:00
// handle non-step parameters (e.g. Jenkins-specific parameters as well as execution environment parameters)
jenkinsParams := [ ] string { "containerCommand" , "containerName" , "containerShell" , "dockerVolumeBind" , "dockerWorkspace" , "sidecarReadyCommand" , "sidecarWorkspace" , "stashContent" }
2020-09-23 13:55:17 +02:00
if ! contains ( stepParameterNames , paramName ) {
for _ , secret := range stepData . Spec . Inputs . Secrets {
if paramName == secret . Name && secret . Type == "jenkins" {
2021-10-11 15:22:24 +02:00
return checkParameterInfo ( "[![Jenkins only](https://img.shields.io/badge/-Jenkins%20only-yellowgreen)](#) id of credentials ([using credentials](https://www.jenkins.io/doc/book/using/using-credentials/))" , true , executionEnvironment )
2020-09-23 13:55:17 +02:00
}
}
2021-10-11 15:22:24 +02:00
if contains ( jenkinsParams , paramName ) {
return checkParameterInfo ( "[![Jenkins only](https://img.shields.io/badge/-Jenkins%20only-yellowgreen)](#)" , false , executionEnvironment )
}
return checkParameterInfo ( "" , false , executionEnvironment )
2020-09-23 13:55:17 +02:00
}
2021-10-11 15:22:24 +02:00
// handle step-parameters (incl. secrets)
2020-09-23 13:55:17 +02:00
for _ , param := range stepData . Spec . Inputs . Parameters {
if paramName == param . Name {
if param . Secret {
secretInfo := "[![Secret](https://img.shields.io/badge/-Secret-yellowgreen)](#) pass via ENV or Jenkins credentials"
2022-01-14 16:03:29 +02:00
if param . GetReference ( "vaultSecret" ) != nil || param . GetReference ( "vaultSecretFile" ) != nil {
2020-11-16 12:53:52 +02:00
secretInfo = " [![Vault](https://img.shields.io/badge/-Vault-lightgrey)](#) [![Secret](https://img.shields.io/badge/-Secret-yellowgreen)](/) pass via ENV, Vault or Jenkins credentials"
}
2020-09-23 13:55:17 +02:00
for _ , res := range param . ResourceRef {
if res . Type == "secret" {
secretInfo += fmt . Sprintf ( " ([`%v`](#%v))" , res . Name , strings . ToLower ( res . Name ) )
}
}
2021-10-11 15:22:24 +02:00
return checkParameterInfo ( secretInfo , true , executionEnvironment )
2020-09-23 13:55:17 +02:00
}
2021-10-11 15:22:24 +02:00
return checkParameterInfo ( "" , true , executionEnvironment )
2020-09-23 13:55:17 +02:00
}
}
2021-10-11 15:22:24 +02:00
return checkParameterInfo ( "" , true , executionEnvironment )
}
func checkParameterInfo ( furtherInfo string , stepParam bool , executionEnvironment bool ) ( string , error ) {
if stepParam && ! executionEnvironment || ! stepParam && executionEnvironment {
return furtherInfo , nil
}
if executionEnvironment {
return "" , fmt . Errorf ( "step parameter not relevant as execution environment parameter" )
}
return "" , fmt . Errorf ( "execution environment parameter not relevant as step parameter" )
2020-09-23 13:55:17 +02:00
}
func createParameterDetails ( stepData * config . StepData ) string {
details := ""
//jenkinsParameters := append(jenkinsParameters(stepData), "script")
for _ , param := range stepData . Spec . Inputs . Parameters {
details += fmt . Sprintf ( "#### %v\n\n" , param . Name )
if ! contains ( stepParameterNames , param . Name ) {
details += "**Jenkins-specific:** Used for proper environment setup.\n\n"
}
if len ( param . LongDescription ) > 0 {
details += param . LongDescription + "\n\n"
} else {
details += param . Description + "\n\n"
}
details += "[back to overview](#parameters)\n\n"
details += "| Scope | Details |\n"
details += "| ---- | --------- |\n"
details += fmt . Sprintf ( "| Aliases | %v |\n" , aliasList ( param . Aliases ) )
details += fmt . Sprintf ( "| Type | `%v` |\n" , param . Type )
2021-10-12 12:49:38 +02:00
mandatory , mandatoryString , furtherInfo := parameterMandatoryInformation ( param , "" )
if mandatory && len ( furtherInfo ) > 0 {
mandatoryString = furtherInfo
}
details += fmt . Sprintf ( "| Mandatory | %v |\n" , ifThenElse ( mandatory , mandatoryString , "no" ) )
2020-09-23 13:55:17 +02:00
details += fmt . Sprintf ( "| Default | %v |\n" , formatDefault ( param , stepParameterNames ) )
if param . PossibleValues != nil {
details += fmt . Sprintf ( "| Possible values | %v |\n" , possibleValueList ( param . PossibleValues ) )
}
details += fmt . Sprintf ( "| Secret | %v |\n" , ifThenElse ( param . Secret , "**yes**" , "no" ) )
details += fmt . Sprintf ( "| Configuration scope | %v |\n" , scopeDetails ( param . Scope ) )
details += fmt . Sprintf ( "| Resource references | %v |\n" , resourceReferenceDetails ( param . ResourceRef ) )
details += "\n\n"
}
for _ , secret := range stepData . Spec . Inputs . Secrets {
details += fmt . Sprintf ( "#### %v\n\n" , secret . Name )
if ! contains ( stepParameterNames , secret . Name ) {
2020-09-28 11:45:21 +02:00
details += "**Jenkins-specific:** Used for proper environment setup. See *[using credentials](https://www.jenkins.io/doc/book/using/using-credentials/)* for details.\n\n"
2020-09-23 13:55:17 +02:00
}
details += secret . Description + "\n\n"
details += "[back to overview](#parameters)\n\n"
details += "| Scope | Details |\n"
details += "| ---- | --------- |\n"
details += fmt . Sprintf ( "| Aliases | %v |\n" , aliasList ( secret . Aliases ) )
details += fmt . Sprintf ( "| Type | `%v` |\n" , "string" )
details += fmt . Sprintf ( "| Configuration scope | %v |\n" , scopeDetails ( [ ] string { "PARAMETERS" , "GENERAL" , "STEPS" , "STAGES" } ) )
details += "\n\n"
}
return details
}
func formatDefault ( param config . StepParameters , stepParameterNames [ ] string ) string {
if param . Default == nil {
// Return environment variable for all step parameters (not for Jenkins-specific parameters) in case no default is available
if contains ( stepParameterNames , param . Name ) {
return fmt . Sprintf ( "`$PIPER_%v` (if set)" , param . Name )
}
return ""
}
//first consider conditional defaults
switch v := param . Default . ( type ) {
case [ ] conditionDefault :
defaults := [ ] string { }
for _ , condDef := range v {
//ToDo: add type-specific handling of default
defaults = append ( defaults , fmt . Sprintf ( "%v=`%v`: `%v`" , condDef . key , condDef . value , condDef . def ) )
}
return strings . Join ( defaults , "<br />" )
case [ ] interface { } :
// handle for example stashes which possibly contain a mixture of fix and conditional values
defaults := [ ] string { }
for _ , def := range v {
if condDef , ok := def . ( conditionDefault ) ; ok {
defaults = append ( defaults , fmt . Sprintf ( "%v=`%v`: `%v`" , condDef . key , condDef . value , condDef . def ) )
} else {
defaults = append ( defaults , fmt . Sprintf ( "- `%v`" , def ) )
}
}
return strings . Join ( defaults , "<br />" )
case map [ string ] string :
defaults := [ ] string { }
for key , def := range v {
defaults = append ( defaults , fmt . Sprintf ( "`%v`: `%v`" , key , def ) )
}
return strings . Join ( defaults , "<br />" )
case string :
if len ( v ) == 0 {
return "`''`"
}
return fmt . Sprintf ( "`%v`" , v )
default :
return fmt . Sprintf ( "`%v`" , param . Default )
}
}
func aliasList ( aliases [ ] config . Alias ) string {
switch len ( aliases ) {
case 0 :
return "-"
case 1 :
alias := fmt . Sprintf ( "`%v`" , aliases [ 0 ] . Name )
if aliases [ 0 ] . Deprecated {
alias += " (**deprecated**)"
}
return alias
default :
aList := make ( [ ] string , len ( aliases ) )
for i , alias := range aliases {
aList [ i ] = fmt . Sprintf ( "- `%v`" , alias . Name )
if alias . Deprecated {
aList [ i ] += " (**deprecated**)"
}
}
return strings . Join ( aList , "<br />" )
}
}
func possibleValueList ( possibleValues [ ] interface { } ) string {
if len ( possibleValues ) == 0 {
return ""
}
pList := make ( [ ] string , len ( possibleValues ) )
for i , possibleValue := range possibleValues {
pList [ i ] = fmt . Sprintf ( "- `%v`" , fmt . Sprint ( possibleValue ) )
}
return strings . Join ( pList , "<br />" )
}
func scopeDetails ( scope [ ] string ) string {
scopeDetails := "<ul>"
scopeDetails += fmt . Sprintf ( "<li>%v parameter</li>" , ifThenElse ( contains ( scope , "PARAMETERS" ) , "☒" , "☐" ) )
scopeDetails += fmt . Sprintf ( "<li>%v general</li>" , ifThenElse ( contains ( scope , "GENERAL" ) , "☒" , "☐" ) )
scopeDetails += fmt . Sprintf ( "<li>%v steps</li>" , ifThenElse ( contains ( scope , "STEPS" ) , "☒" , "☐" ) )
scopeDetails += fmt . Sprintf ( "<li>%v stages</li>" , ifThenElse ( contains ( scope , "STAGES" ) , "☒" , "☐" ) )
scopeDetails += "</ul>"
return scopeDetails
}
func resourceReferenceDetails ( resourceRef [ ] config . ResourceReference ) string {
if len ( resourceRef ) == 0 {
return "none"
}
resourceDetails := ""
for _ , resource := range resourceRef {
if resource . Name == "commonPipelineEnvironment" {
resourceDetails += "_commonPipelineEnvironment_:<br />"
resourceDetails += fmt . Sprintf ( " reference to: `%v`<br />" , resource . Param )
continue
}
if resource . Type == "secret" {
resourceDetails += "Jenkins credential id:<br />"
for i , alias := range resource . Aliases {
if i == 0 {
resourceDetails += " aliases:<br />"
}
resourceDetails += fmt . Sprintf ( " - `%v`%v<br />" , alias . Name , ifThenElse ( alias . Deprecated , " (**Deprecated**)" , "" ) )
}
resourceDetails += fmt . Sprintf ( " id: [`%v`](#%v)<br />" , resource . Name , strings . ToLower ( resource . Name ) )
2020-11-26 08:51:43 +02:00
if resource . Param != "" {
resourceDetails += fmt . Sprintf ( " reference to: `%v`<br />" , resource . Param )
}
2020-09-23 13:55:17 +02:00
continue
}
2020-11-16 12:53:52 +02:00
2020-11-26 08:51:43 +02:00
resourceDetails = addVaultResourceDetails ( resource , resourceDetails )
2020-09-23 13:55:17 +02:00
}
return resourceDetails
}
2020-11-26 08:51:43 +02:00
func addVaultResourceDetails ( resource config . ResourceReference , resourceDetails string ) string {
2022-01-14 16:03:29 +02:00
if resource . Type == "vaultSecret" || resource . Type == "vaultSecretFile" {
2020-11-26 08:51:43 +02:00
resourceDetails += "<br/>Vault paths: <br />"
resourceDetails += "<ul>"
2021-09-21 13:06:32 +02:00
for _ , rootPath := range config . VaultRootPaths {
resourceDetails += fmt . Sprintf ( "<li>`%s`</li>" , path . Join ( rootPath , resource . Default ) )
2020-11-26 08:51:43 +02:00
}
resourceDetails += "</ul>"
}
return resourceDetails
}
2020-09-23 13:55:17 +02:00
func sortStepParameters ( stepData * config . StepData , considerMandatory bool ) {
if stepData . Spec . Inputs . Parameters != nil {
parameters := stepData . Spec . Inputs . Parameters
if considerMandatory {
sort . SliceStable ( parameters [ : ] , func ( i , j int ) bool {
2021-10-12 12:49:38 +02:00
if ( parameters [ i ] . Mandatory || len ( parameters [ i ] . MandatoryIf ) > 0 ) == ( parameters [ j ] . Mandatory || len ( parameters [ j ] . MandatoryIf ) > 0 ) {
2020-09-23 13:55:17 +02:00
return strings . Compare ( parameters [ i ] . Name , parameters [ j ] . Name ) < 0
2021-10-12 12:49:38 +02:00
} else if parameters [ i ] . Mandatory || len ( parameters [ i ] . MandatoryIf ) > 0 {
2020-09-23 13:55:17 +02:00
return true
}
return false
} )
} else {
sort . SliceStable ( parameters [ : ] , func ( i , j int ) bool {
return strings . Compare ( parameters [ i ] . Name , parameters [ j ] . Name ) < 0
} )
}
}
}