github.com/SAP/jenkins-library@v1.362.0/cmd/newmanExecute.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"text/template"
    10  
    11  	"github.com/SAP/jenkins-library/pkg/command"
    12  	"github.com/SAP/jenkins-library/pkg/log"
    13  	"github.com/SAP/jenkins-library/pkg/piperutils"
    14  	"github.com/SAP/jenkins-library/pkg/telemetry"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  type newmanExecuteUtils interface {
    19  	Glob(pattern string) (matches []string, err error)
    20  	RunExecutable(executable string, params ...string) error
    21  	Getenv(key string) string
    22  }
    23  
    24  type newmanExecuteUtilsBundle struct {
    25  	*command.Command
    26  	*piperutils.Files
    27  }
    28  
    29  func newNewmanExecuteUtils() newmanExecuteUtils {
    30  	utils := newmanExecuteUtilsBundle{
    31  		Command: &command.Command{
    32  			ErrorCategoryMapping: map[string][]string{
    33  				log.ErrorConfiguration.String(): {
    34  					"ENOENT: no such file or directory",
    35  				},
    36  				log.ErrorTest.String(): {
    37  					"AssertionError",
    38  					"TypeError",
    39  				},
    40  			},
    41  		},
    42  		Files: &piperutils.Files{},
    43  	}
    44  	// Reroute command output to logging framework
    45  	utils.Stdout(log.Writer())
    46  	utils.Stderr(log.Writer())
    47  	return &utils
    48  }
    49  
    50  func newmanExecute(config newmanExecuteOptions, _ *telemetry.CustomData, influx *newmanExecuteInflux) {
    51  	utils := newNewmanExecuteUtils()
    52  
    53  	influx.step_data.fields.newman = false
    54  	err := runNewmanExecute(&config, utils)
    55  	if err != nil {
    56  		log.Entry().WithError(err).Fatal("step execution failed")
    57  	}
    58  	influx.step_data.fields.newman = true
    59  }
    60  
    61  func runNewmanExecute(config *newmanExecuteOptions, utils newmanExecuteUtils) error {
    62  	if config.NewmanRunCommand != "" {
    63  		log.Entry().Warn("found configuration for deprecated parameter newmanRunCommand, please use runOptions instead")
    64  		log.Entry().Warn("setting runOptions to value of deprecated parameter newmanRunCommand")
    65  		config.RunOptions = strings.Split(config.NewmanRunCommand, " ")
    66  	}
    67  
    68  	collectionList, err := utils.Glob(config.NewmanCollection)
    69  	if err != nil {
    70  		log.SetErrorCategory(log.ErrorConfiguration)
    71  		return errors.Wrapf(err, "Could not execute global search for '%v'", config.NewmanCollection)
    72  	}
    73  
    74  	if collectionList == nil {
    75  		log.SetErrorCategory(log.ErrorConfiguration)
    76  		return fmt.Errorf("no collection found with pattern '%v'", config.NewmanCollection)
    77  	}
    78  	log.Entry().Infof("Found the following newman collections: %v", collectionList)
    79  
    80  	err = logVersions(utils)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	err = installNewman(config.NewmanInstallCommand, utils)
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	// resolve environment and globals if not covered by templating
    91  	options := resolveOptions(config)
    92  
    93  	for _, collection := range collectionList {
    94  		runOptions, err := resolveTemplate(config, collection)
    95  		if err != nil {
    96  			return err
    97  		}
    98  
    99  		commandSecrets := handleCfAppCredentials(config)
   100  
   101  		runOptions = append(runOptions, options...)
   102  		runOptions = append(runOptions, commandSecrets...)
   103  
   104  		if !config.FailOnError {
   105  			runOptions = append(runOptions, "--suppress-exit-code")
   106  		}
   107  
   108  		newmanPath := filepath.Join(utils.Getenv("HOME"), "/.npm-global/bin/newman")
   109  		err = utils.RunExecutable(newmanPath, runOptions...)
   110  		if err != nil {
   111  			return errors.Wrap(err, "The execution of the newman tests failed, see the log for details.")
   112  		}
   113  	}
   114  	return nil
   115  }
   116  
   117  func logVersions(utils newmanExecuteUtils) error {
   118  	err := utils.RunExecutable("node", "--version")
   119  	if err != nil {
   120  		log.SetErrorCategory(log.ErrorInfrastructure)
   121  		return errors.Wrap(err, "error logging node version")
   122  	}
   123  	err = utils.RunExecutable("npm", "--version")
   124  	if err != nil {
   125  		log.SetErrorCategory(log.ErrorInfrastructure)
   126  		return errors.Wrap(err, "error logging npm version")
   127  	}
   128  	return nil
   129  }
   130  
   131  func installNewman(newmanInstallCommand string, utils newmanExecuteUtils) error {
   132  	installCommandTokens := strings.Split(newmanInstallCommand, " ")
   133  	installCommandTokens = append(installCommandTokens, "--prefix=~/.npm-global")
   134  	err := utils.RunExecutable(installCommandTokens[0], installCommandTokens[1:]...)
   135  	if err != nil {
   136  		log.SetErrorCategory(log.ErrorConfiguration)
   137  		return errors.Wrap(err, "error installing newman")
   138  	}
   139  	return nil
   140  }
   141  
   142  func resolveOptions(config *newmanExecuteOptions) []string {
   143  	options := []string{}
   144  	if config.NewmanEnvironment != "" && !contains(config.RunOptions, "{{.Config.NewmanEnvironment}}") {
   145  		options = append(options, "--environment")
   146  		options = append(options, config.NewmanEnvironment)
   147  	}
   148  	if config.NewmanGlobals != "" && !contains(config.RunOptions, "{{.Config.NewmanGlobals}}") {
   149  		options = append(options, "--globals")
   150  		options = append(options, config.NewmanGlobals)
   151  	}
   152  	return options
   153  }
   154  
   155  func resolveTemplate(config *newmanExecuteOptions, collection string) ([]string, error) {
   156  	cmd := []string{}
   157  	collectionDisplayName := defineCollectionDisplayName(collection)
   158  
   159  	type TemplateConfig struct {
   160  		Config                interface{}
   161  		CollectionDisplayName string
   162  		NewmanCollection      string
   163  	}
   164  
   165  	for _, runOption := range config.RunOptions {
   166  		templ, err := template.New("template").Funcs(template.FuncMap{
   167  			"getenv": func(varName string) string {
   168  				return os.Getenv(varName)
   169  			},
   170  		}).Parse(runOption)
   171  		if err != nil {
   172  			log.SetErrorCategory(log.ErrorConfiguration)
   173  			return nil, errors.Wrap(err, "could not parse newman command template")
   174  		}
   175  		buf := new(bytes.Buffer)
   176  		err = templ.Execute(buf, TemplateConfig{
   177  			Config:                config,
   178  			CollectionDisplayName: collectionDisplayName,
   179  			NewmanCollection:      collection,
   180  		})
   181  		if err != nil {
   182  			log.SetErrorCategory(log.ErrorConfiguration)
   183  			return nil, errors.Wrap(err, "error on executing template")
   184  		}
   185  		cmd = append(cmd, buf.String())
   186  	}
   187  
   188  	return cmd, nil
   189  }
   190  
   191  func defineCollectionDisplayName(collection string) string {
   192  	replacedSeparators := strings.Replace(collection, string(filepath.Separator), "_", -1)
   193  	displayName := strings.Split(replacedSeparators, ".")
   194  	if displayName[0] == "" && len(displayName) >= 2 {
   195  		return displayName[1]
   196  	}
   197  	return displayName[0]
   198  }
   199  
   200  func handleCfAppCredentials(config *newmanExecuteOptions) []string {
   201  	commandSecrets := []string{}
   202  	if len(config.CfAppsWithSecrets) > 0 {
   203  		for _, appName := range config.CfAppsWithSecrets {
   204  			var clientID, clientSecret string
   205  			clientID = os.Getenv("PIPER_NEWMANEXECUTE_" + appName + "_clientid")
   206  			clientSecret = os.Getenv("PIPER_NEWMANEXECUTE_" + appName + "_clientsecret")
   207  			if clientID != "" && clientSecret != "" {
   208  				log.RegisterSecret(clientSecret)
   209  				secretVar := fmt.Sprintf("--env-var %v_clientid=%v --env-var %v_clientsecret=%v", appName, clientID, appName, clientSecret)
   210  				commandSecrets = append(commandSecrets, strings.Split(secretVar, " ")...)
   211  				log.Entry().Infof("secrets found for app %v and forwarded to newman as --env-var parameter", appName)
   212  			} else {
   213  				log.Entry().Errorf("cannot fetch secrets from environment variables for app %v", appName)
   214  			}
   215  		}
   216  	}
   217  	return commandSecrets
   218  }
   219  
   220  func contains(slice []string, substr string) bool {
   221  	for _, e := range slice {
   222  		if strings.Contains(e, substr) {
   223  			return true
   224  		}
   225  	}
   226  	return false
   227  }
   228  
   229  func (utils newmanExecuteUtilsBundle) Getenv(key string) string {
   230  	return os.Getenv(key)
   231  }