1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00
sap-jenkins-library/cmd/newmanExecute.go
Oliver Feldmann b639c98890
[newmanExecute] Allow env vars in the runOptions (#3966)
* Allow env vars in the runOptions

* Add documentation

* Regenerate for documentation

* Fix documentation
2023-01-11 11:59:09 +01:00

232 lines
6.9 KiB
Go

package cmd
import (
"bytes"
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
"github.com/SAP/jenkins-library/pkg/command"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/pkg/errors"
)
type newmanExecuteUtils interface {
Glob(pattern string) (matches []string, err error)
RunExecutable(executable string, params ...string) error
Getenv(key string) string
}
type newmanExecuteUtilsBundle struct {
*command.Command
*piperutils.Files
}
func newNewmanExecuteUtils() newmanExecuteUtils {
utils := newmanExecuteUtilsBundle{
Command: &command.Command{
ErrorCategoryMapping: map[string][]string{
log.ErrorConfiguration.String(): {
"ENOENT: no such file or directory",
},
log.ErrorTest.String(): {
"AssertionError",
"TypeError",
},
},
},
Files: &piperutils.Files{},
}
// Reroute command output to logging framework
utils.Stdout(log.Writer())
utils.Stderr(log.Writer())
return &utils
}
func newmanExecute(config newmanExecuteOptions, _ *telemetry.CustomData, influx *newmanExecuteInflux) {
utils := newNewmanExecuteUtils()
influx.step_data.fields.newman = false
err := runNewmanExecute(&config, utils)
if err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
influx.step_data.fields.newman = true
}
func runNewmanExecute(config *newmanExecuteOptions, utils newmanExecuteUtils) error {
if config.NewmanRunCommand != "" {
log.Entry().Warn("found configuration for deprecated parameter newmanRunCommand, please use runOptions instead")
log.Entry().Warn("setting runOptions to value of deprecated parameter newmanRunCommand")
config.RunOptions = strings.Split(config.NewmanRunCommand, " ")
}
collectionList, err := utils.Glob(config.NewmanCollection)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return errors.Wrapf(err, "Could not execute global search for '%v'", config.NewmanCollection)
}
if collectionList == nil {
log.SetErrorCategory(log.ErrorConfiguration)
return fmt.Errorf("no collection found with pattern '%v'", config.NewmanCollection)
}
log.Entry().Infof("Found the following newman collections: %v", collectionList)
err = logVersions(utils)
if err != nil {
return err
}
err = installNewman(config.NewmanInstallCommand, utils)
if err != nil {
return err
}
// resolve environment and globals if not covered by templating
options := resolveOptions(config)
for _, collection := range collectionList {
runOptions, err := resolveTemplate(config, collection)
if err != nil {
return err
}
commandSecrets := handleCfAppCredentials(config)
runOptions = append(runOptions, options...)
runOptions = append(runOptions, commandSecrets...)
if !config.FailOnError {
runOptions = append(runOptions, "--suppress-exit-code")
}
newmanPath := filepath.Join(utils.Getenv("HOME"), "/.npm-global/bin/newman")
err = utils.RunExecutable(newmanPath, runOptions...)
if err != nil {
return errors.Wrap(err, "The execution of the newman tests failed, see the log for details.")
}
}
return nil
}
func logVersions(utils newmanExecuteUtils) error {
err := utils.RunExecutable("node", "--version")
if err != nil {
log.SetErrorCategory(log.ErrorInfrastructure)
return errors.Wrap(err, "error logging node version")
}
err = utils.RunExecutable("npm", "--version")
if err != nil {
log.SetErrorCategory(log.ErrorInfrastructure)
return errors.Wrap(err, "error logging npm version")
}
return nil
}
func installNewman(newmanInstallCommand string, utils newmanExecuteUtils) error {
installCommandTokens := strings.Split(newmanInstallCommand, " ")
installCommandTokens = append(installCommandTokens, "--prefix=~/.npm-global")
err := utils.RunExecutable(installCommandTokens[0], installCommandTokens[1:]...)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return errors.Wrap(err, "error installing newman")
}
return nil
}
func resolveOptions(config *newmanExecuteOptions) []string {
options := []string{}
if config.NewmanEnvironment != "" && !contains(config.RunOptions, "{{.Config.NewmanEnvironment}}") {
options = append(options, "--environment")
options = append(options, config.NewmanEnvironment)
}
if config.NewmanGlobals != "" && !contains(config.RunOptions, "{{.Config.NewmanGlobals}}") {
options = append(options, "--globals")
options = append(options, config.NewmanGlobals)
}
return options
}
func resolveTemplate(config *newmanExecuteOptions, collection string) ([]string, error) {
cmd := []string{}
collectionDisplayName := defineCollectionDisplayName(collection)
type TemplateConfig struct {
Config interface{}
CollectionDisplayName string
NewmanCollection string
}
for _, runOption := range config.RunOptions {
templ, err := template.New("template").Funcs(template.FuncMap{
"getenv": func(varName string) string {
return os.Getenv(varName)
},
}).Parse(runOption)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return nil, errors.Wrap(err, "could not parse newman command template")
}
buf := new(bytes.Buffer)
err = templ.Execute(buf, TemplateConfig{
Config: config,
CollectionDisplayName: collectionDisplayName,
NewmanCollection: collection,
})
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return nil, errors.Wrap(err, "error on executing template")
}
cmd = append(cmd, buf.String())
}
return cmd, nil
}
func defineCollectionDisplayName(collection string) string {
replacedSeparators := strings.Replace(collection, string(filepath.Separator), "_", -1)
displayName := strings.Split(replacedSeparators, ".")
if displayName[0] == "" && len(displayName) >= 2 {
return displayName[1]
}
return displayName[0]
}
func handleCfAppCredentials(config *newmanExecuteOptions) []string {
commandSecrets := []string{}
if len(config.CfAppsWithSecrets) > 0 {
for _, appName := range config.CfAppsWithSecrets {
var clientID, clientSecret string
clientID = os.Getenv("PIPER_NEWMANEXECUTE_" + appName + "_clientid")
clientSecret = os.Getenv("PIPER_NEWMANEXECUTE_" + appName + "_clientsecret")
if clientID != "" && clientSecret != "" {
log.RegisterSecret(clientSecret)
secretVar := fmt.Sprintf("--env-var %v_clientid=%v --env-var %v_clientsecret=%v", appName, clientID, appName, clientSecret)
commandSecrets = append(commandSecrets, strings.Split(secretVar, " ")...)
log.Entry().Infof("secrets found for app %v and forwarded to newman as --env-var parameter", appName)
} else {
log.Entry().Errorf("cannot fetch secrets from environment variables for app %v", appName)
}
}
}
return commandSecrets
}
func contains(slice []string, substr string) bool {
for _, e := range slice {
if strings.Contains(e, substr) {
return true
}
}
return false
}
func (utils newmanExecuteUtilsBundle) Getenv(key string) string {
return os.Getenv(key)
}