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  }