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  }