github.com/xgoffin/jenkins-library@v1.154.0/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.Namespace == "" || br.repo.PackageType == "" || br.repo.PackageName == "" {
   142  		return errors.New("Parameters missing. Please provide software component name, version, sp-level, namespace, 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: "NAMESPACE",
   156  				Value:   br.repo.Namespace,
   157  			},
   158  			{
   159  				ValueID: "PACKAGE_TYPE",
   160  				Value:   br.repo.PackageType,
   161  			},
   162  			{
   163  				ValueID: "PACKAGE_NAME_" + br.repo.PackageType,
   164  				Value:   br.repo.PackageName,
   165  			},
   166  		},
   167  	}
   168  	if br.repo.PredecessorCommitID != "" {
   169  		valuesInput.Values = append(valuesInput.Values,
   170  			abapbuild.Value{ValueID: "PREVIOUS_DELIVERY_COMMIT",
   171  				Value: br.repo.PredecessorCommitID})
   172  	}
   173  	if br.repo.CommitID != "" {
   174  		valuesInput.Values = append(valuesInput.Values,
   175  			abapbuild.Value{ValueID: "ACTUAL_DELIVERY_COMMIT",
   176  				Value: br.repo.CommitID})
   177  	}
   178  	if len(br.repo.Languages) > 0 {
   179  		valuesInput.Values = append(valuesInput.Values,
   180  			abapbuild.Value{ValueID: "SSDC_EXPORT_LANGUAGE_VECTOR",
   181  				Value: br.repo.GetAakAasLanguageVector()})
   182  	}
   183  
   184  	phase := "BUILD_" + br.repo.PackageType
   185  	log.Entry().Infof("Starting assembly of package %s", br.repo.PackageName)
   186  	return br.build.Start(phase, valuesInput)
   187  }
   188  
   189  func downloadResultToFile(builds []buildWithRepository, resultName string, publish bool) ([]piperutils.Path, error) {
   190  	envPath := filepath.Join(GeneralConfig.EnvRootPath, "abapBuild")
   191  	var filesToPublish []piperutils.Path
   192  
   193  	for i, b := range builds {
   194  		if b.repo.Status != "P" {
   195  			continue
   196  		}
   197  		buildResult, err := b.build.GetResult(resultName)
   198  		if err != nil {
   199  			return filesToPublish, err
   200  		}
   201  		var fileName string
   202  		if len(buildResult.AdditionalInfo) <= 255 {
   203  			fileName = buildResult.AdditionalInfo
   204  		} else {
   205  			fileName = buildResult.Name
   206  		}
   207  		downloadPath := filepath.Join(envPath, path.Base(fileName))
   208  		log.Entry().Infof("Downloading %s file %s to %s", resultName, path.Base(fileName), downloadPath)
   209  		err = buildResult.Download(downloadPath)
   210  		if err != nil {
   211  			return filesToPublish, err
   212  		}
   213  		if resultName == "SAR_XML" {
   214  			builds[i].repo.SarXMLFilePath = downloadPath
   215  		}
   216  		if publish {
   217  			log.Entry().Infof("Add %s to be published", resultName)
   218  			filesToPublish = append(filesToPublish, piperutils.Path{Target: downloadPath, Name: resultName, Mandatory: true})
   219  		}
   220  	}
   221  	return filesToPublish, nil
   222  }
   223  
   224  func checkIfFailedAndPrintLogs(builds []buildWithRepository) error {
   225  	var buildFailed bool = false
   226  	for i := range builds {
   227  		if builds[i].build.RunState == abapbuild.Failed {
   228  			log.Entry().Errorf("Assembly of %s failed", builds[i].repo.PackageName)
   229  			buildFailed = true
   230  		}
   231  		builds[i].build.PrintLogs()
   232  	}
   233  	if buildFailed {
   234  		return errors.New("At least the assembly of one package failed")
   235  	}
   236  	return nil
   237  }
   238  
   239  func initAssemblePackagesConnection(conn *abapbuild.Connector, config *abapEnvironmentAssemblePackagesOptions, com abaputils.Communication, client abapbuild.HTTPSendLoader) error {
   240  	var connConfig abapbuild.ConnectorConfiguration
   241  	connConfig.CfAPIEndpoint = config.CfAPIEndpoint
   242  	connConfig.CfOrg = config.CfOrg
   243  	connConfig.CfSpace = config.CfSpace
   244  	connConfig.CfServiceInstance = config.CfServiceInstance
   245  	connConfig.CfServiceKeyName = config.CfServiceKeyName
   246  	connConfig.Host = config.Host
   247  	connConfig.Username = config.Username
   248  	connConfig.Password = config.Password
   249  	connConfig.AddonDescriptor = config.AddonDescriptor
   250  	connConfig.MaxRuntimeInMinutes = config.MaxRuntimeInMinutes
   251  
   252  	err := conn.InitBuildFramework(connConfig, com, client)
   253  	if err != nil {
   254  		return errors.Wrap(err, "Connector initialization for communication with the ABAP system failed")
   255  	}
   256  
   257  	return nil
   258  }