github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/cmd/abapEnvironmentAssemblePackages.go (about) 1 package cmd 2 3 import ( 4 "path" 5 "path/filepath" 6 "time" 7 8 abapbuild "github.com/SAP/jenkins-library/pkg/abap/build" 9 "github.com/SAP/jenkins-library/pkg/abaputils" 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 type buildWithRepository struct { 19 build abapbuild.Build 20 repo abaputils.Repository 21 } 22 23 func abapEnvironmentAssemblePackages(config abapEnvironmentAssemblePackagesOptions, telemetryData *telemetry.CustomData, cpe *abapEnvironmentAssemblePackagesCommonPipelineEnvironment) { 24 // for command execution use Command 25 c := command.Command{} 26 // reroute command output to logging framework 27 c.Stdout(log.Writer()) 28 c.Stderr(log.Writer()) 29 30 var autils = abaputils.AbapUtils{ 31 Exec: &c, 32 } 33 34 client := piperhttp.Client{} 35 utils := piperutils.Files{} 36 err := runAbapEnvironmentAssemblePackages(&config, telemetryData, &autils, &utils, &client, cpe) 37 if err != nil { 38 log.Entry().WithError(err).Fatal("step execution failed") 39 } 40 } 41 42 func runAbapEnvironmentAssemblePackages(config *abapEnvironmentAssemblePackagesOptions, telemetryData *telemetry.CustomData, com abaputils.Communication, utils piperutils.FileUtils, client abapbuild.HTTPSendLoader, cpe *abapEnvironmentAssemblePackagesCommonPipelineEnvironment) error { 43 connBuild := new(abapbuild.Connector) 44 if errConBuild := initAssemblePackagesConnection(connBuild, config, com, client); errConBuild != nil { 45 return errConBuild 46 } 47 48 addonDescriptor := new(abaputils.AddonDescriptor) 49 if err := addonDescriptor.InitFromJSONstring(config.AddonDescriptor); err != nil { 50 return errors.Wrap(err, "Reading AddonDescriptor failed [Make sure abapAddonAssemblyKit...CheckCVs|CheckPV|ReserveNextPackages steps have been run before]") 51 } 52 53 builds, err := executeBuilds(addonDescriptor.Repositories, *connBuild, time.Duration(config.MaxRuntimeInMinutes)*time.Minute, time.Duration(config.PollIntervalsInMilliseconds)*time.Millisecond) 54 if err != nil { 55 return errors.Wrap(err, "Starting Builds for Repositories with reserved AAKaaS packages failed") 56 } 57 58 err = checkIfFailedAndPrintLogs(builds) 59 if err != nil { 60 return errors.Wrap(err, "Checking for failed Builds and Printing Build Logs failed") 61 } 62 63 _, err = downloadResultToFile(builds, "SAR_XML", false) //File is present in ABAP build system and uploaded to AAKaaS, no need to fill up jenkins with it 64 if err != nil { 65 return errors.Wrap(err, "Download of Build Artifact SAR_XML failed") 66 } 67 68 var filesToPublish []piperutils.Path 69 filesToPublish, err = downloadResultToFile(builds, "DELIVERY_LOGS.ZIP", true) 70 if err != nil { 71 return errors.Wrap(err, "Download of Build Artifact DELIVERY_LOGS.ZIP failed") 72 } 73 74 log.Entry().Infof("Publishing %v files", len(filesToPublish)) 75 piperutils.PersistReportsAndLinks("abapEnvironmentAssemblePackages", "", utils, filesToPublish, nil) 76 77 var reposBackToCPE []abaputils.Repository 78 for _, b := range builds { 79 reposBackToCPE = append(reposBackToCPE, b.repo) 80 } 81 addonDescriptor.SetRepositories(reposBackToCPE) 82 cpe.abap.addonDescriptor = addonDescriptor.AsJSONstring() 83 84 return nil 85 } 86 87 func executeBuilds(repos []abaputils.Repository, conn abapbuild.Connector, maxRuntimeInMinutes time.Duration, pollInterval time.Duration) ([]buildWithRepository, error) { 88 var builds []buildWithRepository 89 90 for _, repo := range repos { 91 92 buildRepo := buildWithRepository{ 93 build: abapbuild.Build{ 94 Connector: conn, 95 }, 96 repo: repo, 97 } 98 99 if repo.Status == "P" { 100 buildRepo.repo.InBuildScope = true 101 err := buildRepo.start() 102 if err != nil { 103 buildRepo.build.RunState = abapbuild.Failed 104 log.Entry().Error(err) 105 log.Entry().Info("Continueing with other builds (if any)") 106 } else { 107 err = buildRepo.waitToBeFinished(maxRuntimeInMinutes, pollInterval) 108 if err != nil { 109 buildRepo.build.RunState = abapbuild.Failed 110 log.Entry().Error(err) 111 log.Entry().Error("Continuing with other builds (if any) but keep in Mind that even if this build finishes beyond timeout the result is not trustworthy due to possible side effects!") 112 } 113 } 114 } else { 115 log.Entry().Infof("Packages %s is in status '%s'. No need to run the assembly", repo.PackageName, repo.Status) 116 } 117 118 builds = append(builds, buildRepo) 119 } 120 return builds, nil 121 } 122 123 func (br *buildWithRepository) waitToBeFinished(maxRuntimeInMinutes time.Duration, pollInterval time.Duration) error { 124 timeout := time.After(maxRuntimeInMinutes) 125 ticker := time.Tick(pollInterval) 126 for { 127 select { 128 case <-timeout: 129 return errors.Errorf("Timed out: (max Runtime %v reached)", maxRuntimeInMinutes) 130 case <-ticker: 131 if err := br.build.Get(); err != nil { 132 return err 133 } 134 if !br.build.IsFinished() { 135 log.Entry().Infof("Assembly of %s is not yet finished, check again in %s", br.repo.PackageName, pollInterval) 136 } else { 137 return nil 138 } 139 } 140 } 141 } 142 143 func (br *buildWithRepository) start() error { 144 if br.repo.Name == "" || br.repo.Version == "" || br.repo.SpLevel == "" || br.repo.PackageType == "" || br.repo.PackageName == "" { 145 return errors.New("Parameters missing. Please provide software component name, version, sp-level, packagetype and packagename") 146 } 147 valuesInput := abapbuild.Values{ 148 Values: []abapbuild.Value{ 149 { 150 ValueID: "SWC", 151 Value: br.repo.Name, 152 }, 153 { 154 ValueID: "CVERS", 155 Value: br.repo.Name + "." + br.repo.Version + "." + br.repo.SpLevel, 156 }, 157 { 158 ValueID: "PACKAGE_TYPE", 159 Value: br.repo.PackageType, 160 }, 161 { 162 ValueID: "PACKAGE_NAME_" + br.repo.PackageType, 163 Value: br.repo.PackageName, 164 }, 165 }, 166 } 167 if br.repo.Namespace != "" { 168 valuesInput.Values = append(valuesInput.Values, 169 abapbuild.Value{ValueID: "NAMESPACE", 170 Value: br.repo.Namespace}) 171 } 172 if br.repo.UseClassicCTS { 173 valuesInput.Values = append(valuesInput.Values, 174 abapbuild.Value{ValueID: "useClassicCTS", 175 Value: "true"}) 176 } 177 if br.repo.PredecessorCommitID != "" { 178 valuesInput.Values = append(valuesInput.Values, 179 abapbuild.Value{ValueID: "PREVIOUS_DELIVERY_COMMIT", 180 Value: br.repo.PredecessorCommitID}) 181 } 182 if br.repo.CommitID != "" { 183 // old value to be used until 2302 [can be deleted earliest with 2311] 184 valuesInput.Values = append(valuesInput.Values, 185 abapbuild.Value{ValueID: "ACTUAL_DELIVERY_COMMIT", 186 Value: br.repo.CommitID}) 187 // new value used as of 2302 188 valuesInput.Values = append(valuesInput.Values, 189 abapbuild.Value{ValueID: "CURRENT_DELIVERY_COMMIT", 190 Value: br.repo.CommitID}) 191 } 192 if br.repo.Tag != "" { 193 valuesInput.Values = append(valuesInput.Values, abapbuild.Value{ValueID: "CURRENT_DELIVERY_TAG", Value: br.repo.Tag}) 194 } 195 if len(br.repo.Languages) > 0 { 196 valuesInput.Values = append(valuesInput.Values, 197 abapbuild.Value{ValueID: "SSDC_EXPORT_LANGUAGE_VECTOR", 198 Value: br.repo.GetAakAasLanguageVector()}) 199 } 200 if br.repo.AdditionalPiecelist != "" { 201 valuesInput.Values = append(valuesInput.Values, 202 abapbuild.Value{ValueID: "ADDITIONAL_PIECELIST", 203 Value: br.repo.AdditionalPiecelist}) 204 } 205 phase := "BUILD_" + br.repo.PackageType 206 log.Entry().Infof("Starting assembly of package %s", br.repo.PackageName) 207 return br.build.Start(phase, valuesInput) 208 } 209 210 func downloadResultToFile(builds []buildWithRepository, resultName string, publish bool) ([]piperutils.Path, error) { 211 envPath := filepath.Join(GeneralConfig.EnvRootPath, "abapBuild") 212 var filesToPublish []piperutils.Path 213 214 for i, b := range builds { 215 if b.repo.Status != "P" { 216 continue 217 } 218 buildResult, err := b.build.GetResult(resultName) 219 if err != nil { 220 return filesToPublish, err 221 } 222 var fileName string 223 if len(buildResult.AdditionalInfo) <= 255 { 224 fileName = buildResult.AdditionalInfo 225 } else { 226 fileName = buildResult.Name 227 } 228 downloadPath := filepath.Join(envPath, path.Base(fileName)) 229 log.Entry().Infof("Downloading %s file %s to %s", resultName, path.Base(fileName), downloadPath) 230 err = buildResult.Download(downloadPath) 231 if err != nil { 232 return filesToPublish, err 233 } 234 if resultName == "SAR_XML" { 235 builds[i].repo.SarXMLFilePath = downloadPath 236 } 237 if publish { 238 log.Entry().Infof("Add %s to be published", resultName) 239 filesToPublish = append(filesToPublish, piperutils.Path{Target: downloadPath, Name: resultName, Mandatory: true}) 240 } 241 } 242 return filesToPublish, nil 243 } 244 245 func checkIfFailedAndPrintLogs(builds []buildWithRepository) error { 246 var buildFailed bool = false 247 for i := range builds { 248 if builds[i].build.RunState == abapbuild.Failed { 249 log.Entry().Errorf("Assembly of %s failed", builds[i].repo.PackageName) 250 buildFailed = true 251 } 252 if builds[i].build.BuildID != "" { 253 if err := builds[i].build.PrintLogs(); err != nil { 254 return err 255 } 256 } 257 } 258 if buildFailed { 259 return errors.New("At least the assembly of one package failed") 260 } 261 return nil 262 } 263 264 func initAssemblePackagesConnection(conn *abapbuild.Connector, config *abapEnvironmentAssemblePackagesOptions, com abaputils.Communication, client abapbuild.HTTPSendLoader) error { 265 var connConfig abapbuild.ConnectorConfiguration 266 connConfig.CfAPIEndpoint = config.CfAPIEndpoint 267 connConfig.CfOrg = config.CfOrg 268 connConfig.CfSpace = config.CfSpace 269 connConfig.CfServiceInstance = config.CfServiceInstance 270 connConfig.CfServiceKeyName = config.CfServiceKeyName 271 connConfig.Host = config.Host 272 connConfig.Username = config.Username 273 connConfig.Password = config.Password 274 connConfig.AddonDescriptor = config.AddonDescriptor 275 connConfig.MaxRuntimeInMinutes = config.MaxRuntimeInMinutes 276 connConfig.CertificateNames = config.CertificateNames 277 278 err := conn.InitBuildFramework(connConfig, com, client) 279 if err != nil { 280 return errors.Wrap(err, "Connector initialization for communication with the ABAP system failed") 281 } 282 283 return nil 284 }