github.com/xgoffin/jenkins-library@v1.154.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 := []string{}
    95  		runOptions, err := resolveTemplate(config, collection)
    96  		if err != nil {
    97  			return err
    98  		}
    99  
   100  		commandSecrets := handleCfAppCredentials(config)
   101  
   102  		runOptions = append(runOptions, options...)
   103  		runOptions = append(runOptions, commandSecrets...)
   104  
   105  		if !config.FailOnError {
   106  			runOptions = append(runOptions, "--suppress-exit-code")
   107  		}
   108  
   109  		newmanPath := filepath.Join(utils.Getenv("HOME"), "/.npm-global/bin/newman")
   110  		err = utils.RunExecutable(newmanPath, runOptions...)
   111  		if err != nil {
   112  			return errors.Wrap(err, "The execution of the newman tests failed, see the log for details.")
   113  		}
   114  	}
   115  	return nil
   116  }
   117  
   118  func logVersions(utils newmanExecuteUtils) error {
   119  	err := utils.RunExecutable("node", "--version")
   120  	if err != nil {
   121  		log.SetErrorCategory(log.ErrorInfrastructure)
   122  		return errors.Wrap(err, "error logging node version")
   123  	}
   124  	err = utils.RunExecutable("npm", "--version")
   125  	if err != nil {
   126  		log.SetErrorCategory(log.ErrorInfrastructure)
   127  		return errors.Wrap(err, "error logging npm version")
   128  	}
   129  	return nil
   130  }
   131  
   132  func installNewman(newmanInstallCommand string, utils newmanExecuteUtils) error {
   133  	installCommandTokens := strings.Split(newmanInstallCommand, " ")
   134  	installCommandTokens = append(installCommandTokens, "--prefix=~/.npm-global")
   135  	err := utils.RunExecutable(installCommandTokens[0], installCommandTokens[1:]...)
   136  	if err != nil {
   137  		log.SetErrorCategory(log.ErrorConfiguration)
   138  		return errors.Wrap(err, "error installing newman")
   139  	}
   140  	return nil
   141  }
   142  
   143  func resolveOptions(config *newmanExecuteOptions) []string {
   144  	options := []string{}
   145  	if config.NewmanEnvironment != "" && !contains(config.RunOptions, "{{.Config.NewmanEnvironment}}") {
   146  		options = append(options, "--environment")
   147  		options = append(options, config.NewmanEnvironment)
   148  	}
   149  	if config.NewmanGlobals != "" && !contains(config.RunOptions, "{{.Config.NewmanGlobals}}") {
   150  		options = append(options, "--globals")
   151  		options = append(options, config.NewmanGlobals)
   152  	}
   153  	return options
   154  }
   155  
   156  func resolveTemplate(config *newmanExecuteOptions, collection string) ([]string, error) {
   157  	cmd := []string{}
   158  	collectionDisplayName := defineCollectionDisplayName(collection)
   159  
   160  	type TemplateConfig struct {
   161  		Config                interface{}
   162  		CollectionDisplayName string
   163  		NewmanCollection      string
   164  	}
   165  
   166  	for _, runOption := range config.RunOptions {
   167  		templ, err := template.New("template").Parse(runOption)
   168  		if err != nil {
   169  			log.SetErrorCategory(log.ErrorConfiguration)
   170  			return nil, errors.Wrap(err, "could not parse newman command template")
   171  		}
   172  		buf := new(bytes.Buffer)
   173  		err = templ.Execute(buf, TemplateConfig{
   174  			Config:                config,
   175  			CollectionDisplayName: collectionDisplayName,
   176  			NewmanCollection:      collection,
   177  		})
   178  		if err != nil {
   179  			log.SetErrorCategory(log.ErrorConfiguration)
   180  			return nil, errors.Wrap(err, "error on executing template")
   181  		}
   182  		cmd = append(cmd, buf.String())
   183  	}
   184  
   185  	return cmd, nil
   186  }
   187  
   188  func defineCollectionDisplayName(collection string) string {
   189  	replacedSeparators := strings.Replace(collection, string(filepath.Separator), "_", -1)
   190  	displayName := strings.Split(replacedSeparators, ".")
   191  	if displayName[0] == "" && len(displayName) >= 2 {
   192  		return displayName[1]
   193  	}
   194  	return displayName[0]
   195  }
   196  
   197  func handleCfAppCredentials(config *newmanExecuteOptions) []string {
   198  	commandSecrets := []string{}
   199  	if len(config.CfAppsWithSecrets) > 0 {
   200  		for _, appName := range config.CfAppsWithSecrets {
   201  			var clientID, clientSecret string
   202  			clientID = os.Getenv("PIPER_NEWMANEXECUTE_" + appName + "_clientid")
   203  			clientSecret = os.Getenv("PIPER_NEWMANEXECUTE_" + appName + "_clientsecret")
   204  			if clientID != "" && clientSecret != "" {
   205  				log.RegisterSecret(clientSecret)
   206  				secretVar := fmt.Sprintf("--env-var %v_clientid=%v --env-var %v_clientsecret=%v", appName, clientID, appName, clientSecret)
   207  				commandSecrets = append(commandSecrets, strings.Split(secretVar, " ")...)
   208  				log.Entry().Infof("secrets found for app %v and forwarded to newman as --env-var parameter", appName)
   209  			} else {
   210  				log.Entry().Errorf("cannot fetch secrets from environment variables for app %v", appName)
   211  			}
   212  		}
   213  	}
   214  	return commandSecrets
   215  }
   216  
   217  func contains(slice []string, substr string) bool {
   218  	for _, e := range slice {
   219  		if strings.Contains(e, substr) {
   220  			return true
   221  		}
   222  	}
   223  	return false
   224  }
   225  
   226  func (utils newmanExecuteUtilsBundle) Getenv(key string) string {
   227  	return os.Getenv(key)
   228  }