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 }