github.com/jaylevin/jenkins-library@v1.230.4/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 }