1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-03-27 21:49:15 +02:00

AAKaaS holistic pv check (#4893)

* new step abapAddonAssemblyKitCheck
This commit is contained in:
tiloKo 2024-04-22 13:12:38 +02:00 committed by GitHub
parent 787176b6da
commit 265105efa1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 831 additions and 12 deletions

View File

@ -0,0 +1,94 @@
package cmd
import (
"github.com/SAP/jenkins-library/pkg/abap/aakaas"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
)
func abapAddonAssemblyKitCheck(config abapAddonAssemblyKitCheckOptions, telemetryData *telemetry.CustomData, commonPipelineEnvironment *abapAddonAssemblyKitCheckCommonPipelineEnvironment) {
utils := aakaas.NewAakBundle()
err := runAbapAddonAssemblyKitCheck(&config, telemetryData, utils, commonPipelineEnvironment)
if err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
}
func runAbapAddonAssemblyKitCheck(config *abapAddonAssemblyKitCheckOptions, telemetryData *telemetry.CustomData, utils aakaas.AakUtils, commonPipelineEnvironment *abapAddonAssemblyKitCheckCommonPipelineEnvironment) error {
log.Entry().Info("╔═══════════════════════════╗")
log.Entry().Info("║ abapAddonAssemblyKitCheck ║")
log.Entry().Info("╚═══════════════════════════╝")
conn := new(abapbuild.Connector)
if err := conn.InitAAKaaS(config.AbapAddonAssemblyKitEndpoint, config.Username, config.Password, utils, "", config.AbapAddonAssemblyKitCertificateFile, config.AbapAddonAssemblyKitCertificatePass); err != nil {
return err
}
log.Entry().Infof("reading addonDescriptor (aka addon.yml) file: %s", config.AddonDescriptorFileName)
addonDescriptor, err := utils.ReadAddonDescriptor(config.AddonDescriptorFileName)
if err != nil {
return err
}
log.Entry().Info("building product modelling (and resolving potential wildcards)")
pvh, err := aakaas.NewProductVersionHeader(&addonDescriptor, conn)
if err != nil {
return err
}
printProductVersionHeader(*pvh)
log.Entry().Info("calling AAKaaS to check product modelling...")
if err := pvh.CheckAndResolveVersion(conn); err != nil {
return err
}
log.Entry().Info("... success!")
pvh.SyncAddonDescriptorVersionFields(&addonDescriptor)
log.Entry().Info("resolved version fields:")
printAddonDescriptorVersionFields(addonDescriptor)
log.Entry().Info("transferring addonDescriptor to commonPipelineEnvironment for usage by subsequent steps of the pipeline")
commonPipelineEnvironment.abap.addonDescriptor = string(addonDescriptor.AsJSON())
publishAddonYaml(config, utils)
return nil
}
func printProductVersionHeader(pvh aakaas.ProductVersionHeader) {
logLine30 := "──────────────────────────────"
log.Entry().Infof("┌─%-30v─┬─%-30v─┐", logLine30, logLine30)
log.Entry().Infof("│ %-30v │ %-30v │", "Product Name", pvh.ProductName)
log.Entry().Infof("│ %-30v │ %-30v │", "Product Version", pvh.SemanticProductVersion)
log.Entry().Infof("├─%-30v─┼─%-30v─┤", logLine30, logLine30)
log.Entry().Infof("│ %-30v │ %-30v │", "Software Component Name", "Software Component Version")
log.Entry().Infof("├─%-30v─┼─%-30v─┤", logLine30, logLine30)
for _, pvc := range pvh.Content {
log.Entry().Infof("│ %-30v │ %-30v │", pvc.SoftwareComponentName, pvc.SemanticSoftwareComponentVersion)
}
log.Entry().Infof("└─%-30v─┴─%-30v─┘", logLine30, logLine30)
}
func printAddonDescriptorVersionFields(addonDescriptor abaputils.AddonDescriptor) {
logLine30 := "──────────────────────────────"
logLine4 := "────"
log.Entry().Infof("┌─%-30v─┬─%-4v─┬─%-4v─┬─%-4v─┐", logLine30, logLine4, logLine4, logLine4)
log.Entry().Infof("│ %-30v │ %-4v │ %-4v │ %-4v │", "Name", "Vers", "SP", "Pat.")
log.Entry().Infof("├─%-30v─┼─%-4v─┼─%-4v─┼─%-4v─┤", logLine30, logLine4, logLine4, logLine4)
log.Entry().Infof("│ %-30v │ %-4v │ %-4v │ %-4v │", addonDescriptor.AddonProduct, addonDescriptor.AddonVersion, addonDescriptor.AddonSpsLevel, addonDescriptor.AddonPatchLevel)
for _, repo := range addonDescriptor.Repositories {
log.Entry().Infof("│ %-30v │ %-4v │ %-4v │ %-4v │", repo.Name, repo.Version, repo.SpLevel, repo.PatchLevel)
}
log.Entry().Infof("└─%-30v─┴─%-4v─┴─%-4v─┴─%-4v─┘", logLine30, logLine4, logLine4, logLine4)
}
func publishAddonYaml(config *abapAddonAssemblyKitCheckOptions, utils aakaas.AakUtils) {
var filesToPublish []piperutils.Path
log.Entry().Infof("adding %s to be published", config.AddonDescriptorFileName)
filesToPublish = append(filesToPublish, piperutils.Path{Target: config.AddonDescriptorFileName, Name: "AddonDescriptor", Mandatory: true})
log.Entry().Infof("publishing %v files", len(filesToPublish))
if err := piperutils.PersistReportsAndLinks("abapAddonAssemblyKitCheckPV", "", utils, filesToPublish, nil); err != nil {
log.Entry().WithError(err).Error("failed to persist report information")
}
}

View File

@ -0,0 +1,295 @@
// Code generated by piper's step-generator. DO NOT EDIT.
package cmd
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperenv"
"github.com/SAP/jenkins-library/pkg/splunk"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/SAP/jenkins-library/pkg/validation"
"github.com/spf13/cobra"
)
type abapAddonAssemblyKitCheckOptions struct {
AbapAddonAssemblyKitCertificateFile string `json:"abapAddonAssemblyKitCertificateFile,omitempty"`
AbapAddonAssemblyKitCertificatePass string `json:"abapAddonAssemblyKitCertificatePass,omitempty"`
AbapAddonAssemblyKitEndpoint string `json:"abapAddonAssemblyKitEndpoint,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
AddonDescriptorFileName string `json:"addonDescriptorFileName,omitempty"`
AddonDescriptor string `json:"addonDescriptor,omitempty"`
}
type abapAddonAssemblyKitCheckCommonPipelineEnvironment struct {
abap struct {
addonDescriptor string
}
}
func (p *abapAddonAssemblyKitCheckCommonPipelineEnvironment) persist(path, resourceName string) {
content := []struct {
category string
name string
value interface{}
}{
{category: "abap", name: "addonDescriptor", value: p.abap.addonDescriptor},
}
errCount := 0
for _, param := range content {
err := piperenv.SetResourceParameter(path, resourceName, filepath.Join(param.category, param.name), param.value)
if err != nil {
log.Entry().WithError(err).Error("Error persisting piper environment.")
errCount++
}
}
if errCount > 0 {
log.Entry().Error("failed to persist Piper environment")
}
}
// AbapAddonAssemblyKitCheckCommand This step calls AAKaaS to check the validity of the Addon Product Modelling.
func AbapAddonAssemblyKitCheckCommand() *cobra.Command {
const STEP_NAME = "abapAddonAssemblyKitCheck"
metadata := abapAddonAssemblyKitCheckMetadata()
var stepConfig abapAddonAssemblyKitCheckOptions
var startTime time.Time
var commonPipelineEnvironment abapAddonAssemblyKitCheckCommonPipelineEnvironment
var logCollector *log.CollectorHook
var splunkClient *splunk.Splunk
telemetryClient := &telemetry.Telemetry{}
var createAbapAddonAssemblyKitCheckCmd = &cobra.Command{
Use: STEP_NAME,
Short: "This step calls AAKaaS to check the validity of the Addon Product Modelling.",
Long: `This step does the following:<ul>
<li>[The Addon Product Modelling](https://www.project-piper.io/scenarios/abapEnvironmentAddons/#add-on-descriptor-file) is read from the <b>addonDescriptorFileName</b> (e.g. addon.yml)</li>
<li>A connection to AAKaaS (Addon Assembly Kit as a Service) is established and the Addon Product Modelling is transfered for detailed [checks](https://www.project-piper.io/scenarios/abapEnvironmentAddons/#versioning-rules)</li>
<li>The semantic versions are resolved and stored into the piper commonPipelineEnviroment for usage of subsequent pipeline steps</li>
</ul>
<br />
For logon to AAKaaS you can either provide a credential with basic authorization (username and password) or two secret text credentials containing the technical s-users certificate (see note [2805811](https://me.sap.com/notes/2805811) for download) as base64 encoded string and the password to decrypt the file
<br />
For Terminology refer to the [Scenario Description](https://www.project-piper.io/scenarios/abapEnvironmentAddons/).`,
PreRunE: func(cmd *cobra.Command, _ []string) error {
startTime = time.Now()
log.SetStepName(STEP_NAME)
log.SetVerbose(GeneralConfig.Verbose)
GeneralConfig.GitHubAccessTokens = ResolveAccessTokens(GeneralConfig.GitHubTokens)
path, _ := os.Getwd()
fatalHook := &log.FatalHook{CorrelationID: GeneralConfig.CorrelationID, Path: path}
log.RegisterHook(fatalHook)
err := PrepareConfig(cmd, &metadata, STEP_NAME, &stepConfig, config.OpenPiperFile)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return err
}
log.RegisterSecret(stepConfig.AbapAddonAssemblyKitCertificateFile)
log.RegisterSecret(stepConfig.AbapAddonAssemblyKitCertificatePass)
log.RegisterSecret(stepConfig.Username)
log.RegisterSecret(stepConfig.Password)
if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 {
sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID)
log.RegisterHook(&sentryHook)
}
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 || len(GeneralConfig.HookConfig.SplunkConfig.ProdCriblEndpoint) > 0 {
splunkClient = &splunk.Splunk{}
logCollector = &log.CollectorHook{CorrelationID: GeneralConfig.CorrelationID}
log.RegisterHook(logCollector)
}
if err = log.RegisterANSHookIfConfigured(GeneralConfig.CorrelationID); err != nil {
log.Entry().WithError(err).Warn("failed to set up SAP Alert Notification Service log hook")
}
validation, err := validation.New(validation.WithJSONNamesForStructFields(), validation.WithPredefinedErrorMessages())
if err != nil {
return err
}
if err = validation.ValidateStruct(stepConfig); err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return err
}
return nil
},
Run: func(_ *cobra.Command, _ []string) {
stepTelemetryData := telemetry.CustomData{}
stepTelemetryData.ErrorCode = "1"
handler := func() {
commonPipelineEnvironment.persist(GeneralConfig.EnvRootPath, "commonPipelineEnvironment")
config.RemoveVaultSecretFiles()
stepTelemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds())
stepTelemetryData.ErrorCategory = log.GetErrorCategory().String()
stepTelemetryData.PiperCommitHash = GitCommit
telemetryClient.SetData(&stepTelemetryData)
telemetryClient.Send()
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
splunkClient.Initialize(GeneralConfig.CorrelationID,
GeneralConfig.HookConfig.SplunkConfig.Dsn,
GeneralConfig.HookConfig.SplunkConfig.Token,
GeneralConfig.HookConfig.SplunkConfig.Index,
GeneralConfig.HookConfig.SplunkConfig.SendLogs)
splunkClient.Send(telemetryClient.GetData(), logCollector)
}
if len(GeneralConfig.HookConfig.SplunkConfig.ProdCriblEndpoint) > 0 {
splunkClient.Initialize(GeneralConfig.CorrelationID,
GeneralConfig.HookConfig.SplunkConfig.ProdCriblEndpoint,
GeneralConfig.HookConfig.SplunkConfig.ProdCriblToken,
GeneralConfig.HookConfig.SplunkConfig.ProdCriblIndex,
GeneralConfig.HookConfig.SplunkConfig.SendLogs)
splunkClient.Send(telemetryClient.GetData(), logCollector)
}
}
log.DeferExitHandler(handler)
defer handler()
telemetryClient.Initialize(GeneralConfig.NoTelemetry, STEP_NAME, GeneralConfig.HookConfig.PendoConfig.Token)
abapAddonAssemblyKitCheck(stepConfig, &stepTelemetryData, &commonPipelineEnvironment)
stepTelemetryData.ErrorCode = "0"
log.Entry().Info("SUCCESS")
},
}
addAbapAddonAssemblyKitCheckFlags(createAbapAddonAssemblyKitCheckCmd, &stepConfig)
return createAbapAddonAssemblyKitCheckCmd
}
func addAbapAddonAssemblyKitCheckFlags(cmd *cobra.Command, stepConfig *abapAddonAssemblyKitCheckOptions) {
cmd.Flags().StringVar(&stepConfig.AbapAddonAssemblyKitCertificateFile, "abapAddonAssemblyKitCertificateFile", os.Getenv("PIPER_abapAddonAssemblyKitCertificateFile"), "base64 encoded certificate pfx file (PKCS12 format) see note [2805811](https://me.sap.com/notes/2805811)")
cmd.Flags().StringVar(&stepConfig.AbapAddonAssemblyKitCertificatePass, "abapAddonAssemblyKitCertificatePass", os.Getenv("PIPER_abapAddonAssemblyKitCertificatePass"), "password to decrypt the certificate file")
cmd.Flags().StringVar(&stepConfig.AbapAddonAssemblyKitEndpoint, "abapAddonAssemblyKitEndpoint", `https://apps.support.sap.com`, "Base URL to the Addon Assembly Kit as a Service (AAKaaS) system")
cmd.Flags().StringVar(&stepConfig.Username, "username", os.Getenv("PIPER_username"), "User for the Addon Assembly Kit as a Service (AAKaaS) system")
cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "Password for the Addon Assembly Kit as a Service (AAKaaS) system")
cmd.Flags().StringVar(&stepConfig.AddonDescriptorFileName, "addonDescriptorFileName", `addon.yml`, "File name of the YAML file which describes the Product Version and corresponding Software Component Versions")
cmd.Flags().StringVar(&stepConfig.AddonDescriptor, "addonDescriptor", os.Getenv("PIPER_addonDescriptor"), "Structure in the commonPipelineEnvironment containing information about the Product Version and corresponding Software Component Versions")
cmd.MarkFlagRequired("abapAddonAssemblyKitEndpoint")
cmd.MarkFlagRequired("addonDescriptorFileName")
}
// retrieve step metadata
func abapAddonAssemblyKitCheckMetadata() config.StepData {
var theMetaData = config.StepData{
Metadata: config.StepMetadata{
Name: "abapAddonAssemblyKitCheck",
Aliases: []config.Alias{},
Description: "This step calls AAKaaS to check the validity of the Addon Product Modelling.",
},
Spec: config.StepSpec{
Inputs: config.StepInputs{
Secrets: []config.StepSecrets{
{Name: "abapAddonAssemblyKitCredentialsId", Description: "CredentialsId stored in Jenkins for the Addon Assembly Kit as a Service (AAKaaS) system", Type: "jenkins"},
{Name: "abapAddonAssemblyKitCertificateFileCredentialsId", Description: "Jenkins secret text credential ID containing the base64 encoded certificate pfx file (PKCS12 format) see note [2805811](https://me.sap.com/notes/2805811)", Type: "jenkins"},
{Name: "abapAddonAssemblyKitCertificatePassCredentialsId", Description: "Jenkins secret text credential ID containing the password to decrypt the certificate file stored in abapAddonAssemblyKitCertificateFileCredentialsId", Type: "jenkins"},
},
Parameters: []config.StepParameters{
{
Name: "abapAddonAssemblyKitCertificateFile",
ResourceRef: []config.ResourceReference{
{
Name: "abapAddonAssemblyKitCertificateFileCredentialsId",
Param: "abapAddonAssemblyKitCertificateFile",
Type: "secret",
},
},
Scope: []string{"PARAMETERS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_abapAddonAssemblyKitCertificateFile"),
},
{
Name: "abapAddonAssemblyKitCertificatePass",
ResourceRef: []config.ResourceReference{
{
Name: "abapAddonAssemblyKitCertificatePassCredentialsId",
Param: "abapAddonAssemblyKitCertificatePass",
Type: "secret",
},
},
Scope: []string{"PARAMETERS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_abapAddonAssemblyKitCertificatePass"),
},
{
Name: "abapAddonAssemblyKitEndpoint",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
Default: `https://apps.support.sap.com`,
},
{
Name: "username",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_username"),
},
{
Name: "password",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_password"),
},
{
Name: "addonDescriptorFileName",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
Default: `addon.yml`,
},
{
Name: "addonDescriptor",
ResourceRef: []config.ResourceReference{
{
Name: "commonPipelineEnvironment",
Param: "abap/addonDescriptor",
},
},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_addonDescriptor"),
},
},
},
Outputs: config.StepOutputs{
Resources: []config.StepResources{
{
Name: "commonPipelineEnvironment",
Type: "piperEnvironment",
Parameters: []map[string]interface{}{
{"name": "abap/addonDescriptor"},
},
},
},
},
},
}
return theMetaData
}

View File

@ -0,0 +1,20 @@
//go:build unit
// +build unit
package cmd
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestAbapAddonAssemblyKitCheckCommand(t *testing.T) {
t.Parallel()
testCmd := AbapAddonAssemblyKitCheckCommand()
// only high level testing performed - details are tested in step generation procedure
assert.Equal(t, "abapAddonAssemblyKitCheck", testCmd.Use, "command name incorrect")
}

View File

@ -0,0 +1,60 @@
//go:build unit
// +build unit
package cmd
import (
"testing"
"github.com/SAP/jenkins-library/pkg/abap/aakaas"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/stretchr/testify/assert"
)
func TestRunAbapAddonAssemblyKitCheck(t *testing.T) {
var config abapAddonAssemblyKitCheckOptions
var cpe abapAddonAssemblyKitCheckCommonPipelineEnvironment
bundle := aakaas.NewAakBundleMock()
utils := bundle.GetUtils()
config.Username = "dummyUser"
config.Password = "dummyPassword"
t.Run("happy path", func(t *testing.T) {
config.AddonDescriptorFileName = "addon.yml.mock"
bundle.SetBody(aakaas.ResponseCheck)
bundle.MockAddonDescriptor = abaputils.AddonDescriptor{
AddonProduct: "/DRNMSPC/PRD01",
AddonVersionYAML: "2.0.0",
Repositories: []abaputils.Repository{
{
Name: "/DRNMSPC/COMP01",
VersionYAML: "2.0.0",
},
{
Name: "/DRNMSPC/COMP02",
VersionYAML: "1.0.0",
},
},
}
err := runAbapAddonAssemblyKitCheck(&config, nil, utils, &cpe)
assert.NoError(t, err)
})
t.Run("error path", func(t *testing.T) {
config.AddonDescriptorFileName = "addon.yml.mock"
bundle.SetBody(aakaas.ResponseCheck)
bundle.MockAddonDescriptor = abaputils.AddonDescriptor{
AddonProduct: "/DRNMSPC/PRD01",
AddonVersionYAML: "2.0.0",
Repositories: []abaputils.Repository{
//no repos should fail during pvh creation...
},
}
err := runAbapAddonAssemblyKitCheck(&config, nil, utils, &cpe)
assert.EqualError(t, err, "addonDescriptor must contain at least one software component repository")
})
}

View File

@ -7,6 +7,7 @@ import "github.com/SAP/jenkins-library/pkg/config"
// GetStepMetadata return a map with all the step metadata mapped to their stepName
func GetAllStepMetadata() map[string]config.StepData {
return map[string]config.StepData{
"abapAddonAssemblyKitCheck": abapAddonAssemblyKitCheckMetadata(),
"abapAddonAssemblyKitCheckCVs": abapAddonAssemblyKitCheckCVsMetadata(),
"abapAddonAssemblyKitCheckPV": abapAddonAssemblyKitCheckPVMetadata(),
"abapAddonAssemblyKitCreateTargetVector": abapAddonAssemblyKitCreateTargetVectorMetadata(),

View File

@ -154,6 +154,7 @@ func Execute() {
rootCmd.AddCommand(AbapEnvironmentAssemblePackagesCommand())
rootCmd.AddCommand(AbapAddonAssemblyKitCheckCVsCommand())
rootCmd.AddCommand(AbapAddonAssemblyKitCheckPVCommand())
rootCmd.AddCommand(AbapAddonAssemblyKitCheckCommand())
rootCmd.AddCommand(AbapAddonAssemblyKitCreateTargetVectorCommand())
rootCmd.AddCommand(AbapAddonAssemblyKitPublishTargetVectorCommand())
rootCmd.AddCommand(AbapAddonAssemblyKitRegisterPackagesCommand())

View File

@ -0,0 +1,42 @@
# ${docGenStepName}
## ${docGenDescription}
### Artifacts
- addonDescriptorFile (addon.yml)
The addonDescriptorFile as specified in parameter addonDescriptorFileName is archived as artifact. This is done as this file is the main configuration and usually changed with every run. Thus it simplifies support if the corresponding configuration file is directly accessible in the pipeline.
## Prerequisites
* The credentials to access the AAKaaS (Technical Communication User) must be stored in the Jenkins Credential Store
* The step needs an addon.yml containing information about the Product Version and corresponding Software Component Versions/Repositories
A detailed description of all prerequisites of the scenario and how to configure them can be found in the [Scenario Description](https://www.project-piper.io/scenarios/abapEnvironmentAddons/).
## ${docGenParameters}
## ${docGenConfiguration}
## ${docJenkinsPluginDependencies}
## Examples
### Configuration in the config.yml
The recommended way to configure your pipeline is via the config.yml file. In this case, calling the step in the Jenkinsfile is reduced to one line:
```groovy
abapAddonAssemblyKitCheck script: this
```
If the step is to be configured individually the config.yml should look like this:
```yaml
steps:
abapAddonAssemblyKitCheck:
abapAddonAssemblyKitCredentialsId: 'abapAddonAssemblyKitCredentialsId',
addonDescriptorFileName: 'addon.yml'
```
More convenient ways of configuration (e.g. on stage level) are described in the respective scenario/pipeline documentation.

View File

@ -51,13 +51,15 @@ nav:
- 'Set up a Pipeline-Based ABAP Development and Testing Process Using Git-Enabled Change and Transport System': scenarios/gCTS_Scenario.md
- Extensibility: extensibility.md
- 'Library steps':
- abapAddonAssemblyKitCheckCVs: steps/abapAddonAssemblyKitCheckCVs.md
- abapAddonAssemblyKitCheckPV: steps/abapAddonAssemblyKitCheckPV.md
- abapAddonAssemblyKitCreateTargetVector: steps/abapAddonAssemblyKitCreateTargetVector.md
- abapAddonAssemblyKitPublishTargetVector: steps/abapAddonAssemblyKitPublishTargetVector.md
- abapAddonAssemblyKitRegisterPackages: steps/abapAddonAssemblyKitRegisterPackages.md
- abapAddonAssemblyKitReleasePackages: steps/abapAddonAssemblyKitReleasePackages.md
- abapAddonAssemblyKitReserveNextPackages: steps/abapAddonAssemblyKitReserveNextPackages.md
- 'abapAddonAssemblyKit':
- Check: steps/abapAddonAssemblyKitCheck.md
- CheckCVs: steps/abapAddonAssemblyKitCheckCVs.md
- CheckPV: steps/abapAddonAssemblyKitCheckPV.md
- CreateTargetVector: steps/abapAddonAssemblyKitCreateTargetVector.md
- PublishTargetVector: steps/abapAddonAssemblyKitPublishTargetVector.md
- RegisterPackages: steps/abapAddonAssemblyKitRegisterPackages.md
- ReleasePackages: steps/abapAddonAssemblyKitReleasePackages.md
- ReserveNextPackages: steps/abapAddonAssemblyKitReserveNextPackages.md
- abapEnvironmentBuild: steps/abapEnvironmentBuild.md
- abapEnvironmentAssemblePackages: steps/abapEnvironmentAssemblePackages.md
- abapEnvironmentAssembleConfirm: steps/abapEnvironmentAssembleConfirm.md

View File

@ -17,7 +17,8 @@ type AakBundleMock struct {
*mock.ExecMockRunner
*abaputils.ClientMock
*mock.FilesMock
maxRuntime time.Duration
maxRuntime time.Duration
MockAddonDescriptor abaputils.AddonDescriptor
}
func NewAakBundleMock() *AakBundleMock {
@ -99,6 +100,10 @@ func (bundle *AakBundleMock) ReadAddonDescriptor(FileName string) (abaputils.Add
{
err = errors.New("error in ReadAddonDescriptor")
}
case "addon.yml.mock":
{
return bundle.MockAddonDescriptor, nil
}
}
return addonDescriptor, err
}

View File

@ -0,0 +1,147 @@
package aakaas
import (
"encoding/json"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/pkg/errors"
)
type jsonProductVersionHeader struct {
Pvh *ProductVersionHeader `json:"d"`
}
type ProductVersionHeader struct {
ProductName string
SemanticProductVersion string `json:"SemProductVersion"`
ProductVersion string
SpsLevel string
PatchLevel string
Vendor string
VendorType string
Content []ProductVersionContent `json:"-"` //for developer access
JsonContent ProductVersionContents `json:"Content"` //for json (Un)Marshaling
}
type ProductVersionContents struct {
Content []ProductVersionContent `json:"results"`
}
type ProductVersionContent struct {
ProductName string
SemanticProductVersion string `json:"SemProductVersion"`
SoftwareComponentName string `json:"ScName"`
SemanticSoftwareComponentVersion string `json:"SemScVersion"`
SoftwareComponentVersion string `json:"ScVersion"`
SpLevel string
PatchLevel string
Vendor string
VendorType string
}
func NewProductVersionHeader(addonDescriptor *abaputils.AddonDescriptor, conn *abapbuild.Connector) (*ProductVersionHeader, error) {
productVersion := new(ProductVersion)
if err := productVersion.ConstructProductversion(*addonDescriptor, *conn); err != nil {
return nil, err
}
pvh := ProductVersionHeader{
ProductName: productVersion.Name,
SemanticProductVersion: productVersion.Version,
Content: []ProductVersionContent{},
}
for _, repo := range addonDescriptor.Repositories {
componentVersion := new(ComponentVersion)
if err := componentVersion.ConstructComponentVersion(repo, *conn); err != nil {
return nil, err
}
pvc := ProductVersionContent{
ProductName: pvh.ProductName,
SemanticProductVersion: pvh.SemanticProductVersion,
SoftwareComponentName: componentVersion.Name,
SemanticSoftwareComponentVersion: componentVersion.Version,
}
pvh.Content = append(pvh.Content, pvc)
}
if len(pvh.Content) == 0 {
return nil, errors.New("addonDescriptor must contain at least one software component repository")
}
return &pvh, nil
}
func (pv *ProductVersionHeader) CheckAndResolveVersion(conn *abapbuild.Connector) error {
conn.GetToken("/odata/aas_ocs_package")
pv.JsonContent = ProductVersionContents{
Content: pv.Content,
}
requestJson, err := json.Marshal(pv)
if err != nil {
return err
}
appendum := "/odata/aas_ocs_package/ProductVersionHeaderSet"
responseBody, err := conn.Post(appendum, string(requestJson))
if err != nil {
return errors.Wrap(err, "Checking Product Modeling in AAkaaS failed")
}
var resultPv jsonProductVersionHeader
if err := json.Unmarshal(responseBody, &resultPv); err != nil {
return errors.Wrap(err, "Unexpected AAKaaS response for checking Product Modeling "+string(responseBody))
}
pv.ProductVersion = resultPv.Pvh.ProductVersion
pv.SpsLevel = resultPv.Pvh.SpsLevel
pv.PatchLevel = resultPv.Pvh.PatchLevel
for pvc_index, pvc := range pv.Content {
foundPvc := ProductVersionContent{}
for _, resultPvc := range resultPv.Pvh.JsonContent.Content {
if pvc.SoftwareComponentName == resultPvc.SoftwareComponentName && foundPvc.SoftwareComponentName == "" {
foundPvc = resultPvc
} else if pvc.SoftwareComponentName == resultPvc.SoftwareComponentName {
return errors.New("Software Component Name must be unique in the ProductVersionContent")
}
}
if foundPvc.SoftwareComponentName == "" {
return errors.New("Software Component Name not found in the ProductVersionContent")
}
pv.Content[pvc_index].PatchLevel = foundPvc.PatchLevel
pv.Content[pvc_index].SpLevel = foundPvc.SpLevel
pv.Content[pvc_index].SoftwareComponentVersion = foundPvc.SoftwareComponentVersion
}
pv.JsonContent = ProductVersionContents{}
return nil
}
func (pv *ProductVersionHeader) SyncAddonDescriptorVersionFields(addonDescriptor *abaputils.AddonDescriptor) error {
addonDescriptor.AddonVersion = pv.ProductVersion
addonDescriptor.AddonSpsLevel = pv.SpsLevel
addonDescriptor.AddonPatchLevel = pv.PatchLevel
//in NewPvh function pvh was build up 1:1 based on addonDescriptor
//in checkAndResolve pvh was synced from AAKaaS reply assuming it does not contain more content than before(if it does it is ignored)
for repo_index, repo := range addonDescriptor.Repositories {
foundPvc := ProductVersionContent{}
for _, pvc := range pv.Content {
if pvc.SoftwareComponentName == repo.Name && foundPvc.SoftwareComponentName == "" {
foundPvc = pvc
} else if pvc.SoftwareComponentName == repo.Name {
return errors.New("Software Component Name must be unique in addon descriptor(aka addon.yml)")
}
}
if foundPvc.SoftwareComponentName == "" {
return errors.New("ProductVersionContent & addon descriptor (aka addon.yml) out of sync")
}
addonDescriptor.Repositories[repo_index].PatchLevel = foundPvc.PatchLevel
addonDescriptor.Repositories[repo_index].SpLevel = foundPvc.SpLevel
addonDescriptor.Repositories[repo_index].Version = foundPvc.SoftwareComponentVersion
}
return nil
}

View File

@ -34,6 +34,44 @@ var ResponseCheckCVs = `{
}
}`
var ResponseCheck = `{
"d": {
"ProductName": "/DRNMSPC/PRD01",
"SemProductVersion": "2.0.0",
"ProductVersion": "0002",
"SpsLevel": "0000",
"PatchLevel": "0000",
"Vendor": "",
"VendorType": "",
"Content": {
"results": [
{
"ProductName": "/DRNMSPC/PRD01",
"SemProductVersion": "2.0.0",
"ScName": "/DRNMSPC/COMP01",
"SemScVersion": "2.0.0",
"ScVersion": "0002",
"SpLevel": "0000",
"PatchLevel": "0000",
"Vendor": "",
"VendorType": ""
},
{
"ProductName": "/DRNMSPC/PRD01",
"SemProductVersion": "2.0.0",
"ScName": "/DRNMSPC/COMP02",
"SemScVersion": "1.0.0",
"ScVersion": "0001",
"SpLevel": "0000",
"PatchLevel": "0000",
"Vendor": "",
"VendorType": ""
}
]
}
}
}`
var emptyResultBody = `{
"d": {
"results": []

View File

@ -0,0 +1,101 @@
metadata:
name: abapAddonAssemblyKitCheck
description: This step calls AAKaaS to check the validity of the Addon Product Modelling.
longDescription: |
This step does the following:<ul>
<li>[The Addon Product Modelling](https://www.project-piper.io/scenarios/abapEnvironmentAddons/#add-on-descriptor-file) is read from the <b>addonDescriptorFileName</b> (e.g. addon.yml)</li>
<li>A connection to AAKaaS (Addon Assembly Kit as a Service) is established and the Addon Product Modelling is transfered for detailed [checks](https://www.project-piper.io/scenarios/abapEnvironmentAddons/#versioning-rules)</li>
<li>The semantic versions are resolved and stored into the piper commonPipelineEnviroment for usage of subsequent pipeline steps</li>
</ul>
<br />
For logon to AAKaaS you can either provide a credential with basic authorization (username and password) or two secret text credentials containing the technical s-users certificate (see note [2805811](https://me.sap.com/notes/2805811) for download) as base64 encoded string and the password to decrypt the file
<br />
For Terminology refer to the [Scenario Description](https://www.project-piper.io/scenarios/abapEnvironmentAddons/).
spec:
inputs:
secrets:
- name: abapAddonAssemblyKitCredentialsId
description: CredentialsId stored in Jenkins for the Addon Assembly Kit as a Service (AAKaaS) system
type: jenkins
- name: abapAddonAssemblyKitCertificateFileCredentialsId
description: Jenkins secret text credential ID containing the base64 encoded certificate pfx file (PKCS12 format) see note [2805811](https://me.sap.com/notes/2805811)
type: jenkins
- name: abapAddonAssemblyKitCertificatePassCredentialsId
description: Jenkins secret text credential ID containing the password to decrypt the certificate file stored in abapAddonAssemblyKitCertificateFileCredentialsId
type: jenkins
params:
- name: abapAddonAssemblyKitCertificateFile
type: string
description: base64 encoded certificate pfx file (PKCS12 format) see note [2805811](https://me.sap.com/notes/2805811)
scope:
- PARAMETERS
mandatory: false
secret: true
resourceRef:
- name: abapAddonAssemblyKitCertificateFileCredentialsId
type: secret
param: abapAddonAssemblyKitCertificateFile
- name: abapAddonAssemblyKitCertificatePass
type: string
description: password to decrypt the certificate file
scope:
- PARAMETERS
mandatory: false
secret: true
resourceRef:
- name: abapAddonAssemblyKitCertificatePassCredentialsId
type: secret
param: abapAddonAssemblyKitCertificatePass
- name: abapAddonAssemblyKitEndpoint
type: string
description: Base URL to the Addon Assembly Kit as a Service (AAKaaS) system
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
mandatory: true
default: https://apps.support.sap.com
- name: username
type: string
description: User for the Addon Assembly Kit as a Service (AAKaaS) system
scope:
- PARAMETERS
- STAGES
- STEPS
mandatory: false
secret: true
- name: password
type: string
description: Password for the Addon Assembly Kit as a Service (AAKaaS) system
scope:
- PARAMETERS
mandatory: false
secret: true
- name: addonDescriptorFileName
type: string
description: File name of the YAML file which describes the Product Version and corresponding Software Component Versions
mandatory: true
default: addon.yml
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
- name: addonDescriptor
type: string
description: Structure in the commonPipelineEnvironment containing information about the Product Version and corresponding Software Component Versions
mandatory: false
scope:
- PARAMETERS
- STAGES
- STEPS
resourceRef:
- name: commonPipelineEnvironment
param: abap/addonDescriptor
outputs:
resources:
- name: commonPipelineEnvironment
type: piperEnvironment
params:
- name: abap/addonDescriptor

View File

@ -106,6 +106,7 @@ public class CommonStepsTest extends BasePiperTest{
}
private static fieldRelatedWhitelist = [
'abapAddonAssemblyKitCheck', //implementing new golang pattern without fields
'abapAddonAssemblyKitCheckCVs', //implementing new golang pattern without fields
'abapAddonAssemblyKitCheckPV', //implementing new golang pattern without fields
'abapAddonAssemblyKitCreateTargetVector', //implementing new golang pattern without fields

View File

@ -0,0 +1,13 @@
import groovy.transform.Field
@Field String STEP_NAME = getClass().getName()
@Field String METADATA_FILE = 'metadata/abapAddonAssemblyKitCheck.yaml'
void call(Map parameters = [:]) {
List credentials = [
[type: 'usernamePassword', id: 'abapAddonAssemblyKitCredentialsId', env: ['PIPER_username', 'PIPER_password']],
[type: 'token', id: 'abapAddonAssemblyKitCertificateFileCredentialsId', env: ['PIPER_abapAddonAssemblyKitCertificateFile']],
[type: 'token', id: 'abapAddonAssemblyKitCertificatePassCredentialsId', env: ['PIPER_abapAddonAssemblyKitCertificatePass']]
]
piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials, false, false, true)
}

View File

@ -6,8 +6,8 @@ import static com.sap.piper.Prerequisites.checkScript
@Field String STEP_NAME = getClass().getName()
@Field Set GENERAL_CONFIG_KEYS = []
@Field STAGE_STEP_KEYS = [
'abapAddonAssemblyKitCheckPV',
'abapAddonAssemblyKitCheckCVs'
'abapAddonAssemblyKitCheck',
'abapAddonAssemblyKitReserveNextPackages'
]
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus(STAGE_STEP_KEYS)
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
@ -19,8 +19,7 @@ void call(Map parameters = [:]) {
def stageName = parameters.stageName?:env.STAGE_NAME
piperStageWrapper (script: script, stageName: stageName, stashContent: [], stageLocking: false) {
abapAddonAssemblyKitCheckPV script: parameters.script
abapAddonAssemblyKitCheckCVs script: parameters.script
abapAddonAssemblyKitCheck script: parameters.script
abapAddonAssemblyKitReserveNextPackages script: parameters.script
}