github.com/jaylevin/jenkins-library@v1.230.4/cmd/hadolintExecute.go (about) 1 package cmd 2 3 import ( 4 "bytes" 5 "io" 6 "net/http" 7 "os" 8 "time" 9 10 "github.com/SAP/jenkins-library/pkg/command" 11 piperhttp "github.com/SAP/jenkins-library/pkg/http" 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 const hadolintCommand = "hadolint" 19 20 // HadolintPiperFileUtils abstracts piperutils.Files 21 // mock generated with: mockery --name HadolintPiperFileUtils --dir cmd --output pkg/hadolint/mocks 22 type HadolintPiperFileUtils interface { 23 FileExists(filename string) (bool, error) 24 FileWrite(filename string, data []byte, perm os.FileMode) error 25 } 26 27 // HadolintClient abstracts http.Client 28 // mock generated with: mockery --name hadolintClient --dir cmd --output pkg/hadolint/mocks 29 type HadolintClient interface { 30 SetOptions(options piperhttp.ClientOptions) 31 DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error 32 } 33 34 // hadolintRunner abstracts command.Command 35 type hadolintRunner interface { 36 RunExecutable(executable string, params ...string) error 37 Stdout(err io.Writer) 38 Stderr(err io.Writer) 39 } 40 41 type hadolintUtils struct { 42 HadolintPiperFileUtils 43 HadolintClient 44 hadolintRunner 45 } 46 47 func hadolintExecute(config hadolintExecuteOptions, _ *telemetry.CustomData) { 48 runner := command.Command{ 49 ErrorCategoryMapping: map[string][]string{}, 50 } 51 // reroute runner output to logging framework 52 runner.Stdout(log.Writer()) 53 runner.Stderr(log.Writer()) 54 55 utils := hadolintUtils{ 56 HadolintPiperFileUtils: &piperutils.Files{}, 57 HadolintClient: &piperhttp.Client{}, 58 hadolintRunner: &runner, 59 } 60 61 if err := runHadolint(config, utils); err != nil { 62 log.Entry().WithError(err).Fatal("Execution failed") 63 } 64 } 65 66 func runHadolint(config hadolintExecuteOptions, utils hadolintUtils) error { 67 var outputBuffer bytes.Buffer 68 var errorBuffer bytes.Buffer 69 utils.Stdout(&outputBuffer) 70 utils.Stderr(&errorBuffer) 71 72 options := []string{"--format", "checkstyle"} 73 // load config file from URL 74 if !hasConfigurationFile(config.ConfigurationFile, utils) && len(config.ConfigurationURL) > 0 { 75 clientOptions := piperhttp.ClientOptions{ 76 TransportTimeout: 20 * time.Second, 77 TransportSkipVerification: true, 78 } 79 if len(config.CustomTLSCertificateLinks) > 0 { 80 clientOptions.TransportSkipVerification = false 81 clientOptions.TrustedCerts = config.CustomTLSCertificateLinks 82 } 83 84 if len(config.ConfigurationUsername) > 0 { 85 clientOptions.Username = config.ConfigurationUsername 86 clientOptions.Password = config.ConfigurationPassword 87 } 88 utils.SetOptions(clientOptions) 89 if err := loadConfigurationFile(config.ConfigurationURL, config.ConfigurationFile, utils); err != nil { 90 return errors.Wrap(err, "failed to load configuration file from URL") 91 } 92 } 93 // use config 94 if hasConfigurationFile(config.ConfigurationFile, utils) { 95 options = append(options, "--config", config.ConfigurationFile) 96 log.Entry().WithField("file", config.ConfigurationFile).Debug("Using configuration file") 97 } else { 98 log.Entry().Debug("No configuration file found.") 99 } 100 // execute scan command 101 err := utils.RunExecutable(hadolintCommand, append([]string{config.DockerFile}, options...)...) 102 103 //TODO: related to https://github.com/hadolint/hadolint/issues/391 104 // hadolint exists with 1 if there are processing issues but also if there are findings 105 // thus check stdout first if a report was created 106 if output := outputBuffer.String(); len(output) > 0 { 107 log.Entry().WithField("report", output).Debug("Report created") 108 utils.FileWrite(config.ReportFile, []byte(output), 0666) 109 } else if err != nil { 110 // if stdout is empty a processing issue occured 111 return errors.Wrap(err, errorBuffer.String()) 112 } 113 //TODO: mock away in tests 114 // persist report information 115 piperutils.PersistReportsAndLinks("hadolintExecute", "./", []piperutils.Path{{Target: config.ReportFile}}, []piperutils.Path{}) 116 return nil 117 } 118 119 // loadConfigurationFile loads a file from the provided url 120 func loadConfigurationFile(url, file string, utils hadolintUtils) error { 121 log.Entry().WithField("url", url).Debug("Loading configuration file from URL") 122 return utils.DownloadFile(url, file, nil, nil) 123 } 124 125 // hasConfigurationFile checks if the given file exists 126 func hasConfigurationFile(file string, utils hadolintUtils) bool { 127 exists, err := utils.FileExists(file) 128 if err != nil { 129 log.Entry().WithError(err).Error() 130 } 131 return exists 132 }