github.com/SAP/jenkins-library@v1.362.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  	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, *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(addonDescriptor *abaputils.AddonDescriptor, conn abapbuild.Connector, maxRuntimeInMinutes time.Duration, pollInterval time.Duration) ([]buildWithRepository, error) {
    88  	var builds []buildWithRepository
    89  
    90  	for _, repo := range addonDescriptor.Repositories {
    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(addonDescriptor)
   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(addonDescriptor *abaputils.AddonDescriptor) 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: "SEMANTIC_VERSION",
   159  				Value:   br.repo.VersionYAML,
   160  			},
   161  			{
   162  				ValueID: "PACKAGE_TYPE",
   163  				Value:   br.repo.PackageType,
   164  			},
   165  			{
   166  				ValueID: "PACKAGE_NAME_" + br.repo.PackageType,
   167  				Value:   br.repo.PackageName,
   168  			},
   169  			{
   170  				ValueID: "addonDescriptor",
   171  				Value:   addonDescriptor.AsReducedJson(),
   172  			},
   173  		},
   174  	}
   175  	if br.repo.Namespace != "" {
   176  		valuesInput.Values = append(valuesInput.Values,
   177  			abapbuild.Value{ValueID: "NAMESPACE",
   178  				Value: br.repo.Namespace})
   179  	}
   180  	if br.repo.UseClassicCTS {
   181  		valuesInput.Values = append(valuesInput.Values,
   182  			abapbuild.Value{ValueID: "useClassicCTS",
   183  				Value: "true"})
   184  	}
   185  	if br.repo.PredecessorCommitID != "" {
   186  		valuesInput.Values = append(valuesInput.Values,
   187  			abapbuild.Value{ValueID: "PREVIOUS_DELIVERY_COMMIT",
   188  				Value: br.repo.PredecessorCommitID})
   189  	}
   190  	if br.repo.CommitID != "" {
   191  		valuesInput.Values = append(valuesInput.Values,
   192  			abapbuild.Value{ValueID: "CURRENT_DELIVERY_COMMIT",
   193  				Value: br.repo.CommitID})
   194  	}
   195  	if br.repo.Tag != "" {
   196  		valuesInput.Values = append(valuesInput.Values, abapbuild.Value{ValueID: "CURRENT_DELIVERY_TAG", Value: br.repo.Tag})
   197  	}
   198  	if len(br.repo.Languages) > 0 {
   199  		valuesInput.Values = append(valuesInput.Values,
   200  			abapbuild.Value{ValueID: "SSDC_EXPORT_LANGUAGE_VECTOR",
   201  				Value: br.repo.GetAakAasLanguageVector()})
   202  	}
   203  	if br.repo.AdditionalPiecelist != "" {
   204  		valuesInput.Values = append(valuesInput.Values,
   205  			abapbuild.Value{ValueID: "ADDITIONAL_PIECELIST",
   206  				Value: br.repo.AdditionalPiecelist})
   207  	}
   208  	phase := "BUILD_" + br.repo.PackageType
   209  	log.Entry().Infof("Starting assembly of package %s", br.repo.PackageName)
   210  	return br.build.Start(phase, valuesInput)
   211  }
   212  
   213  func downloadResultToFile(builds []buildWithRepository, resultName string, publish bool) ([]piperutils.Path, error) {
   214  	envPath := filepath.Join(GeneralConfig.EnvRootPath, "abapBuild")
   215  	var filesToPublish []piperutils.Path
   216  
   217  	for i, b := range builds {
   218  		if b.repo.Status != "P" {
   219  			continue
   220  		}
   221  		buildResult, err := b.build.GetResult(resultName)
   222  		if err != nil {
   223  			return filesToPublish, err
   224  		}
   225  		var fileName string
   226  		if len(buildResult.AdditionalInfo) <= 255 {
   227  			fileName = buildResult.AdditionalInfo
   228  		} else {
   229  			fileName = buildResult.Name
   230  		}
   231  		downloadPath := filepath.Join(envPath, path.Base(fileName))
   232  		log.Entry().Infof("Downloading %s file %s to %s", resultName, path.Base(fileName), downloadPath)
   233  		err = buildResult.Download(downloadPath)
   234  		if err != nil {
   235  			return filesToPublish, err
   236  		}
   237  		if resultName == "SAR_XML" {
   238  			builds[i].repo.SarXMLFilePath = downloadPath
   239  		}
   240  		if publish {
   241  			log.Entry().Infof("Add %s to be published", resultName)
   242  			filesToPublish = append(filesToPublish, piperutils.Path{Target: downloadPath, Name: resultName, Mandatory: true})
   243  		}
   244  	}
   245  	return filesToPublish, nil
   246  }
   247  
   248  func checkIfFailedAndPrintLogs(builds []buildWithRepository) error {
   249  	var buildFailed bool = false
   250  	for i := range builds {
   251  		if builds[i].build.RunState == abapbuild.Failed {
   252  			log.Entry().Errorf("Assembly of %s failed", builds[i].repo.PackageName)
   253  			buildFailed = true
   254  		}
   255  		if builds[i].build.BuildID != "" {
   256  			if err := builds[i].build.PrintLogs(); err != nil {
   257  				return err
   258  			}
   259  		}
   260  	}
   261  	if buildFailed {
   262  		return errors.New("At least the assembly of one package failed")
   263  	}
   264  	return nil
   265  }
   266  
   267  func initAssemblePackagesConnection(conn *abapbuild.Connector, config *abapEnvironmentAssemblePackagesOptions, com abaputils.Communication, client abapbuild.HTTPSendLoader) error {
   268  	var connConfig abapbuild.ConnectorConfiguration
   269  	connConfig.CfAPIEndpoint = config.CfAPIEndpoint
   270  	connConfig.CfOrg = config.CfOrg
   271  	connConfig.CfSpace = config.CfSpace
   272  	connConfig.CfServiceInstance = config.CfServiceInstance
   273  	connConfig.CfServiceKeyName = config.CfServiceKeyName
   274  	connConfig.Host = config.Host
   275  	connConfig.Username = config.Username
   276  	connConfig.Password = config.Password
   277  	connConfig.AddonDescriptor = config.AddonDescriptor
   278  	connConfig.MaxRuntimeInMinutes = config.MaxRuntimeInMinutes
   279  	connConfig.CertificateNames = config.CertificateNames
   280  
   281  	err := conn.InitBuildFramework(connConfig, com, client)
   282  	if err != nil {
   283  		return errors.Wrap(err, "Connector initialization for communication with the ABAP system failed")
   284  	}
   285  
   286  	return nil
   287  }