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