1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00

Add ABAP step: createTag (#3633)

* Cloud Platform -> BTP

* Initial generation of new step

* add flag

* wip

* Fix warnings

* Add command

* Added Tag Decription

* Add status check

* Improve handling

* Improve handling

* Add test for happy path

* Add test reports to gitignore

* Add second test

* Improve createTag

* Add testcase

* Adaptions

* Add test

* Update cmd/abapEnvironmentCreateTag.go

Co-authored-by: tiloKo <70266685+tiloKo@users.noreply.github.com>

* Update cmd/abapEnvironmentCreateTag.go

Co-authored-by: tiloKo <70266685+tiloKo@users.noreply.github.com>

* Adapt error handling

* Improve coding

* Add info

* Disallow repositories and repositoryName at the
same time

* Regenerate

* Adapt to feedback

* Update cmd/abapEnvironmentCreateTag.go

Co-authored-by: tiloKo <70266685+tiloKo@users.noreply.github.com>

* Update cmd/abapEnvironmentCreateTag.go

Co-authored-by: tiloKo <70266685+tiloKo@users.noreply.github.com>

Co-authored-by: tiloKo <70266685+tiloKo@users.noreply.github.com>
This commit is contained in:
Daniel Mieg 2022-05-23 15:15:22 +02:00 committed by GitHub
parent d30395931b
commit cda6e71ab9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1121 additions and 6 deletions

5
.gitignore vendored
View File

@ -45,3 +45,8 @@ ATCResults.xml
AUnitResults.xml
ATCResults.html
AUnitResults.html
cmd/checkmarx/piper_checkmarx_report.json
cmd/fortify/piper_fortify_report.html
cmd/fortify/piper_fortify_report.json
cmd/toolruns/toolrun_malwarescan_20220519143229.json
cmd/toolruns/toolrun_protecode_20220519143230.json

View File

@ -0,0 +1,259 @@
package cmd
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http/cookiejar"
"strings"
"time"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/SAP/jenkins-library/pkg/command"
piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/pkg/errors"
)
func abapEnvironmentCreateTag(config abapEnvironmentCreateTagOptions, telemetryData *telemetry.CustomData) {
c := command.Command{}
c.Stdout(log.Writer())
c.Stderr(log.Writer())
var autils = abaputils.AbapUtils{
Exec: &c,
}
client := piperhttp.Client{}
if err := runAbapEnvironmentCreateTag(&config, telemetryData, &autils, &client); err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
}
func runAbapEnvironmentCreateTag(config *abapEnvironmentCreateTagOptions, telemetryData *telemetry.CustomData, com abaputils.Communication, client piperhttp.Sender) error {
connectionDetails, errorGetInfo := com.GetAbapCommunicationArrangementInfo(convertTagConfig(config), "")
if errorGetInfo != nil {
return errors.Wrap(errorGetInfo, "Parameters for the ABAP Connection not available")
}
// Configuring the HTTP Client and CookieJar
cookieJar, errorCookieJar := cookiejar.New(nil)
if errorCookieJar != nil {
return errors.Wrap(errorCookieJar, "Could not create a Cookie Jar")
}
client.SetOptions(piperhttp.ClientOptions{
MaxRequestDuration: 180 * time.Second,
CookieJar: cookieJar,
Username: connectionDetails.User,
Password: connectionDetails.Password,
})
backlog, errorPrepare := prepareBacklog(config)
if errorPrepare != nil {
return fmt.Errorf("Something failed during the tag creation: %w", errorPrepare)
}
return createTags(backlog, telemetryData, connectionDetails, client, com)
}
func createTags(backlog []CreateTagBacklog, telemetryData *telemetry.CustomData, con abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, com abaputils.Communication) (err error) {
connection := con
connection.XCsrfToken = "fetch"
connection.URL = con.URL + "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Tags"
resp, err := abaputils.GetHTTPResponse("HEAD", connection, nil, client)
if err != nil {
return abaputils.HandleHTTPError(resp, err, "Authentication on the ABAP system failed", con)
}
defer resp.Body.Close()
log.Entry().WithField("StatusCode", resp.Status).WithField("ABAP Endpoint", connection.URL).Debug("Authentication on the ABAP system successful")
connection.XCsrfToken = resp.Header.Get("X-Csrf-Token")
errorOccurred := false
for _, item := range backlog {
err = createTagsForSingleItem(item, telemetryData, connection, client, com)
if err != nil {
errorOccurred = true
}
}
if errorOccurred {
message := "At least one tag has not been created"
log.Entry().Errorf(message)
return errors.New(message)
}
return nil
}
func createTagsForSingleItem(item CreateTagBacklog, telemetryData *telemetry.CustomData, con abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, com abaputils.Communication) (err error) {
errorOccurred := false
for index := range item.tags {
err = createSingleTag(item, index, telemetryData, con, client, com)
if err != nil {
errorOccurred = true
}
}
if errorOccurred {
message := "At least one tag has not been created"
err = errors.New(message)
}
return err
}
func createSingleTag(item CreateTagBacklog, index int, telemetryData *telemetry.CustomData, con abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, com abaputils.Communication) (err error) {
requestBodyStruct := CreateTagBody{RepositoryName: item.repositoryName, CommitID: item.commitID, Tag: item.tags[index].tagName, Description: item.tags[index].tagDescription}
requestBodyJson, err := json.Marshal(&requestBodyStruct)
if err != nil {
return err
}
log.Entry().Debugf("Request body: %s", requestBodyJson)
resp, err := abaputils.GetHTTPResponse("POST", con, requestBodyJson, client)
if err != nil {
errorMessage := "Could not create tag " + requestBodyStruct.Tag + " for repository " + requestBodyStruct.RepositoryName + " with commitID " + requestBodyStruct.CommitID
err = abaputils.HandleHTTPError(resp, err, errorMessage, con)
return err
}
defer resp.Body.Close()
// Parse response
var createTagResponse CreateTagResponse
var abapResp map[string]*json.RawMessage
bodyText, _ := ioutil.ReadAll(resp.Body)
if err = json.Unmarshal(bodyText, &abapResp); err != nil {
return err
}
if err = json.Unmarshal(*abapResp["d"], &createTagResponse); err != nil {
return err
}
con.URL = con.Host + "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull(guid'" + createTagResponse.UUID + "')"
err = checkStatus(con, client, com)
if err == nil {
log.Entry().Info("Created tag " + requestBodyStruct.Tag + " for repository " + requestBodyStruct.RepositoryName + " with commitID " + requestBodyStruct.CommitID)
} else {
log.Entry().Error("NOT created: Tag " + requestBodyStruct.Tag + " for repository " + requestBodyStruct.RepositoryName + " with commitID " + requestBodyStruct.CommitID)
}
return err
}
func checkStatus(con abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, com abaputils.Communication) (err error) {
status := "R"
pollIntervall := com.GetPollIntervall()
count := 0
for {
count += 1
entity, _, err := abaputils.GetStatus("Could not create Tag", con, client)
if err != nil {
return err
}
status = entity.Status
if status != "R" {
if status == "E" {
err = errors.New("Could not create Tag")
}
return err
}
if count >= 200 {
return errors.New("Could not create Tag (Timeout)")
}
time.Sleep(pollIntervall)
}
}
func prepareBacklog(config *abapEnvironmentCreateTagOptions) (backlog []CreateTagBacklog, err error) {
if config.Repositories != "" && config.RepositoryName != "" {
return nil, errors.New("Configuring the parameter repositories and the parameter repositoryName at the same time is not allowed")
}
if config.RepositoryName != "" && config.CommitID != "" {
backlog = append(backlog, CreateTagBacklog{repositoryName: config.RepositoryName, commitID: config.CommitID})
}
if config.Repositories != "" {
descriptor, err := abaputils.ReadAddonDescriptor(config.Repositories) //config.Repositories should contain a file name
if err != nil {
return nil, err
}
for _, repo := range descriptor.Repositories {
backlogInstance := CreateTagBacklog{repositoryName: repo.Name, commitID: repo.CommitID}
if config.GenerateTagForAddonComponentVersion && repo.VersionYAML != "" {
tag := Tag{tagName: "v" + repo.VersionYAML, tagDescription: "Generated by the ABAP Environment Pipeline"}
backlogInstance.tags = append(backlogInstance.tags, tag)
}
backlog = append(backlog, backlogInstance)
}
if config.GenerateTagForAddonProductVersion {
if descriptor.AddonProduct != "" && descriptor.AddonVersionYAML != "" {
addonProductDash := strings.Replace(descriptor.AddonProduct, "/", "-", 2)
backlog = addTagToList(backlog, addonProductDash+"-"+descriptor.AddonVersionYAML, "Generated by the ABAP Environment Pipeline")
} else {
log.Entry().WithField("generateTagForAddonProductVersion", config.GenerateTagForAddonProductVersion).WithField("AddonProduct", descriptor.AddonProduct).WithField("AddonVersion", descriptor.AddonVersionYAML).Infof("Not all required values are provided to create an addon product version tag")
}
}
}
if config.TagName != "" {
backlog = addTagToList(backlog, config.TagName, config.TagDescription)
}
return backlog, nil
}
func addTagToList(backlog []CreateTagBacklog, tag string, description string) []CreateTagBacklog {
for i, item := range backlog {
tag := Tag{tagName: tag, tagDescription: description}
backlog[i].tags = append(item.tags, tag)
}
return backlog
}
func convertTagConfig(config *abapEnvironmentCreateTagOptions) abaputils.AbapEnvironmentOptions {
subOptions := abaputils.AbapEnvironmentOptions{}
subOptions.CfAPIEndpoint = config.CfAPIEndpoint
subOptions.CfServiceInstance = config.CfServiceInstance
subOptions.CfServiceKeyName = config.CfServiceKeyName
subOptions.CfOrg = config.CfOrg
subOptions.CfSpace = config.CfSpace
subOptions.Host = config.Host
subOptions.Password = config.Password
subOptions.Username = config.Username
return subOptions
}
type CreateTagBacklog struct {
repositoryName string
commitID string
tags []Tag
}
type Tag struct {
tagName string
tagDescription string
}
type CreateTagBody struct {
RepositoryName string `json:"sc_name"`
CommitID string `json:"commit_id"`
Tag string `json:"tag_name"`
Description string `json:"tag_description"`
}
type CreateTagResponse struct {
UUID string `json:"uuid"`
}

View File

@ -0,0 +1,321 @@
// Code generated by piper's step-generator. DO NOT EDIT.
package cmd
import (
"fmt"
"os"
"time"
"github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/log"
"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 abapEnvironmentCreateTagOptions struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Repositories string `json:"repositories,omitempty"`
RepositoryName string `json:"repositoryName,omitempty"`
CommitID string `json:"commitID,omitempty"`
TagName string `json:"tagName,omitempty"`
TagDescription string `json:"tagDescription,omitempty"`
GenerateTagForAddonProductVersion bool `json:"generateTagForAddonProductVersion,omitempty"`
GenerateTagForAddonComponentVersion bool `json:"generateTagForAddonComponentVersion,omitempty"`
Host string `json:"host,omitempty"`
CfAPIEndpoint string `json:"cfApiEndpoint,omitempty"`
CfOrg string `json:"cfOrg,omitempty"`
CfSpace string `json:"cfSpace,omitempty"`
CfServiceInstance string `json:"cfServiceInstance,omitempty"`
CfServiceKeyName string `json:"cfServiceKeyName,omitempty"`
}
// AbapEnvironmentCreateTagCommand Creates a tag for a git repository to a SAP BTP ABAP Environment system
func AbapEnvironmentCreateTagCommand() *cobra.Command {
const STEP_NAME = "abapEnvironmentCreateTag"
metadata := abapEnvironmentCreateTagMetadata()
var stepConfig abapEnvironmentCreateTagOptions
var startTime time.Time
var logCollector *log.CollectorHook
var splunkClient *splunk.Splunk
telemetryClient := &telemetry.Telemetry{}
var createAbapEnvironmentCreateTagCmd = &cobra.Command{
Use: STEP_NAME,
Short: "Creates a tag for a git repository to a SAP BTP ABAP Environment system",
Long: `Creates tags for specific commits of one or multiple repositories / software components. The tag can be specified explicitly as well as being generated by an addon product version or an addon component version.
Please provide either of the following options:
* The host and credentials the BTP ABAP Environment system itself. The credentials must be configured for the Communication Scenario [SAP_COM_0510](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/b04a9ae412894725a2fc539bfb1ca055.html).
* The Cloud Foundry parameters (API endpoint, organization, space), credentials, the service instance for the ABAP service and the service key for the Communication Scenario SAP_COM_0510.
* Only provide one of those options with the respective credentials. If all values are provided, the direct communication (via host) has priority.`,
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.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 {
splunkClient = &splunk.Splunk{}
logCollector = &log.CollectorHook{CorrelationID: GeneralConfig.CorrelationID}
log.RegisterHook(logCollector)
}
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() {
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.Send(telemetryClient.GetData(), logCollector)
}
}
log.DeferExitHandler(handler)
defer handler()
telemetryClient.Initialize(GeneralConfig.NoTelemetry, STEP_NAME)
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)
}
abapEnvironmentCreateTag(stepConfig, &stepTelemetryData)
stepTelemetryData.ErrorCode = "0"
log.Entry().Info("SUCCESS")
},
}
addAbapEnvironmentCreateTagFlags(createAbapEnvironmentCreateTagCmd, &stepConfig)
return createAbapEnvironmentCreateTagCmd
}
func addAbapEnvironmentCreateTagFlags(cmd *cobra.Command, stepConfig *abapEnvironmentCreateTagOptions) {
cmd.Flags().StringVar(&stepConfig.Username, "username", os.Getenv("PIPER_username"), "User for either the Cloud Foundry API or the Communication Arrangement for SAP_COM_0510")
cmd.Flags().StringVar(&stepConfig.Password, "password", os.Getenv("PIPER_password"), "Password for either the Cloud Foundry API or the Communication Arrangement for SAP_COM_0510")
cmd.Flags().StringVar(&stepConfig.Repositories, "repositories", os.Getenv("PIPER_repositories"), "Specifies a YAML file containing the repositories configuration")
cmd.Flags().StringVar(&stepConfig.RepositoryName, "repositoryName", os.Getenv("PIPER_repositoryName"), "Specifies a repository (Software Components) on the SAP BTP ABAP Environment system")
cmd.Flags().StringVar(&stepConfig.CommitID, "commitID", os.Getenv("PIPER_commitID"), "Specifies a commitID, for which a tag will be created")
cmd.Flags().StringVar(&stepConfig.TagName, "tagName", os.Getenv("PIPER_tagName"), "Specifies a tagName that will be created for the repositories on the SAP BTP ABAP Environment system")
cmd.Flags().StringVar(&stepConfig.TagDescription, "tagDescription", os.Getenv("PIPER_tagDescription"), "Specifies a description for the created tag")
cmd.Flags().BoolVar(&stepConfig.GenerateTagForAddonProductVersion, "generateTagForAddonProductVersion", false, "Specifies if a tag will be created for the repositories on the SAP BTP ABAP Environment system")
cmd.Flags().BoolVar(&stepConfig.GenerateTagForAddonComponentVersion, "generateTagForAddonComponentVersion", false, "Specifies if a tag will be created for the repositories on the SAP BTP ABAP Environment system")
cmd.Flags().StringVar(&stepConfig.Host, "host", os.Getenv("PIPER_host"), "Specifies the host address of the SAP BTP ABAP Environment system")
cmd.Flags().StringVar(&stepConfig.CfAPIEndpoint, "cfApiEndpoint", os.Getenv("PIPER_cfApiEndpoint"), "Cloud Foundry API Enpoint")
cmd.Flags().StringVar(&stepConfig.CfOrg, "cfOrg", os.Getenv("PIPER_cfOrg"), "Cloud Foundry target organization")
cmd.Flags().StringVar(&stepConfig.CfSpace, "cfSpace", os.Getenv("PIPER_cfSpace"), "Cloud Foundry target space")
cmd.Flags().StringVar(&stepConfig.CfServiceInstance, "cfServiceInstance", os.Getenv("PIPER_cfServiceInstance"), "Cloud Foundry Service Instance")
cmd.Flags().StringVar(&stepConfig.CfServiceKeyName, "cfServiceKeyName", os.Getenv("PIPER_cfServiceKeyName"), "Cloud Foundry Service Key")
cmd.MarkFlagRequired("username")
cmd.MarkFlagRequired("password")
}
// retrieve step metadata
func abapEnvironmentCreateTagMetadata() config.StepData {
var theMetaData = config.StepData{
Metadata: config.StepMetadata{
Name: "abapEnvironmentCreateTag",
Aliases: []config.Alias{},
Description: "Creates a tag for a git repository to a SAP BTP ABAP Environment system",
},
Spec: config.StepSpec{
Inputs: config.StepInputs{
Secrets: []config.StepSecrets{
{Name: "abapCredentialsId", Description: "Jenkins credentials ID containing user and password to authenticate to the BTP ABAP Environment system or the Cloud Foundry API", Type: "jenkins", Aliases: []config.Alias{{Name: "cfCredentialsId", Deprecated: false}, {Name: "credentialsId", Deprecated: false}}},
},
Parameters: []config.StepParameters{
{
Name: "username",
ResourceRef: []config.ResourceReference{
{
Name: "abapCredentialsId",
Param: "username",
Type: "secret",
},
},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_username"),
},
{
Name: "password",
ResourceRef: []config.ResourceReference{
{
Name: "abapCredentialsId",
Param: "password",
Type: "secret",
},
},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_password"),
},
{
Name: "repositories",
ResourceRef: []config.ResourceReference{},
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_repositories"),
},
{
Name: "repositoryName",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_repositoryName"),
},
{
Name: "commitID",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_commitID"),
},
{
Name: "tagName",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_tagName"),
},
{
Name: "tagDescription",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_tagDescription"),
},
{
Name: "generateTagForAddonProductVersion",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
Default: false,
},
{
Name: "generateTagForAddonComponentVersion",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
Aliases: []config.Alias{},
Default: false,
},
{
Name: "host",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_host"),
},
{
Name: "cfApiEndpoint",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "cloudFoundry/apiEndpoint"}},
Default: os.Getenv("PIPER_cfApiEndpoint"),
},
{
Name: "cfOrg",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "cloudFoundry/org"}},
Default: os.Getenv("PIPER_cfOrg"),
},
{
Name: "cfSpace",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "cloudFoundry/space"}},
Default: os.Getenv("PIPER_cfSpace"),
},
{
Name: "cfServiceInstance",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "cloudFoundry/serviceInstance"}},
Default: os.Getenv("PIPER_cfServiceInstance"),
},
{
Name: "cfServiceKeyName",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS", "GENERAL"},
Type: "string",
Mandatory: false,
Aliases: []config.Alias{{Name: "cloudFoundry/serviceKey"}, {Name: "cloudFoundry/serviceKeyName"}, {Name: "cfServiceKey"}},
Default: os.Getenv("PIPER_cfServiceKeyName"),
},
},
},
Containers: []config.Container{
{Name: "cf", Image: "ppiper/cf-cli:7"},
},
},
}
return theMetaData
}

View File

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

View File

@ -0,0 +1,349 @@
package cmd
import (
"io/ioutil"
"os"
"testing"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
)
type abapEnvironmentCreateTagMockUtils struct {
*mock.ExecMockRunner
*mock.FilesMock
}
func newAbapEnvironmentCreateTagTestsUtils() abapEnvironmentCreateTagMockUtils {
utils := abapEnvironmentCreateTagMockUtils{
ExecMockRunner: &mock.ExecMockRunner{},
FilesMock: &mock.FilesMock{},
}
return utils
}
func TestRunAbapEnvironmentCreateTag(t *testing.T) {
t.Run("happy path", func(t *testing.T) {
var autils = &abaputils.AUtilsMock{}
defer autils.Cleanup()
autils.ReturnedConnectionDetailsHTTP.Password = "password"
autils.ReturnedConnectionDetailsHTTP.User = "user"
autils.ReturnedConnectionDetailsHTTP.URL = "https://example.com"
autils.ReturnedConnectionDetailsHTTP.XCsrfToken = "xcsrftoken"
dir, errDir := ioutil.TempDir("", "test read addon descriptor")
if errDir != nil {
t.Fatal("Failed to create temporary directory")
}
oldCWD, _ := os.Getwd()
_ = os.Chdir(dir)
// clean up tmp dir
defer func() {
_ = os.Chdir(oldCWD)
_ = os.RemoveAll(dir)
}()
body := `---
addonVersion: "1.2.3"
addonProduct: "/DMO/PRODUCT"
repositories:
- name: /DMO/SWC
branch: main
commitID: 1234abcd
version: "4.5.6"
`
file, _ := os.Create("repo.yml")
file.Write([]byte(body))
config := &abapEnvironmentCreateTagOptions{
Username: "dummy",
Password: "dummy",
Host: "https://test.com",
Repositories: "repo.yml",
TagName: "tag",
TagDescription: "desc",
GenerateTagForAddonProductVersion: true,
GenerateTagForAddonComponentVersion: true,
}
client := &abaputils.ClientMock{
BodyList: []string{
`{"d" : { "Status" : "S" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "Status" : "S" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "Status" : "S" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "empty" : "body" } }`,
},
Token: "myToken",
StatusCode: 200,
}
_, hook := test.NewNullLogger()
log.RegisterHook(hook)
err := runAbapEnvironmentCreateTag(config, nil, autils, client)
assert.NoError(t, err, "Did not expect error")
assert.Equal(t, 3, len(hook.Entries), "Expected a different number of entries")
assert.Equal(t, `Created tag v4.5.6 for repository /DMO/SWC with commitID 1234abcd`, hook.AllEntries()[0].Message, "Expected a different message")
assert.Equal(t, `Created tag -DMO-PRODUCT-1.2.3 for repository /DMO/SWC with commitID 1234abcd`, hook.AllEntries()[1].Message, "Expected a different message")
assert.Equal(t, `Created tag tag for repository /DMO/SWC with commitID 1234abcd`, hook.AllEntries()[2].Message, "Expected a different message")
hook.Reset()
})
t.Run("backend error", func(t *testing.T) {
var autils = &abaputils.AUtilsMock{}
defer autils.Cleanup()
autils.ReturnedConnectionDetailsHTTP.Password = "password"
autils.ReturnedConnectionDetailsHTTP.User = "user"
autils.ReturnedConnectionDetailsHTTP.URL = "https://example.com"
autils.ReturnedConnectionDetailsHTTP.XCsrfToken = "xcsrftoken"
dir, errDir := ioutil.TempDir("", "test read addon descriptor")
if errDir != nil {
t.Fatal("Failed to create temporary directory")
}
oldCWD, _ := os.Getwd()
_ = os.Chdir(dir)
// clean up tmp dir
defer func() {
_ = os.Chdir(oldCWD)
_ = os.RemoveAll(dir)
}()
body := `---
addonVersion: "1.2.3"
addonProduct: "/DMO/PRODUCT"
repositories:
- name: /DMO/SWC
branch: main
commitID: 1234abcd
version: "4.5.6"
`
file, _ := os.Create("repo.yml")
file.Write([]byte(body))
config := &abapEnvironmentCreateTagOptions{
Username: "dummy",
Password: "dummy",
Host: "https://test.com",
Repositories: "repo.yml",
TagName: "tag",
TagDescription: "desc",
GenerateTagForAddonProductVersion: true,
GenerateTagForAddonComponentVersion: true,
}
client := &abaputils.ClientMock{
BodyList: []string{
`{"d" : { "Status" : "E" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "Status" : "E" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "Status" : "E" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "empty" : "body" } }`,
},
Token: "myToken",
StatusCode: 200,
}
_, hook := test.NewNullLogger()
log.RegisterHook(hook)
err := runAbapEnvironmentCreateTag(config, nil, autils, client)
assert.Error(t, err, "Did expect error")
assert.Equal(t, 4, len(hook.Entries), "Expected a different number of entries")
assert.Equal(t, `NOT created: Tag v4.5.6 for repository /DMO/SWC with commitID 1234abcd`, hook.AllEntries()[0].Message, "Expected a different message")
assert.Equal(t, `NOT created: Tag -DMO-PRODUCT-1.2.3 for repository /DMO/SWC with commitID 1234abcd`, hook.AllEntries()[1].Message, "Expected a different message")
assert.Equal(t, `NOT created: Tag tag for repository /DMO/SWC with commitID 1234abcd`, hook.AllEntries()[2].Message, "Expected a different message")
assert.Equal(t, `At least one tag has not been created`, hook.AllEntries()[3].Message, "Expected a different message")
hook.Reset()
})
}
func TestRunAbapEnvironmentCreateTagConfigurations(t *testing.T) {
t.Run("no repo.yml", func(t *testing.T) {
var autils = &abaputils.AUtilsMock{}
defer autils.Cleanup()
autils.ReturnedConnectionDetailsHTTP.Password = "password"
autils.ReturnedConnectionDetailsHTTP.User = "user"
autils.ReturnedConnectionDetailsHTTP.URL = "https://example.com"
autils.ReturnedConnectionDetailsHTTP.XCsrfToken = "xcsrftoken"
config := &abapEnvironmentCreateTagOptions{
Username: "dummy",
Password: "dummy",
Host: "https://test.com",
RepositoryName: "/DMO/SWC",
CommitID: "1234abcd",
TagName: "tag",
TagDescription: "desc",
GenerateTagForAddonProductVersion: true,
GenerateTagForAddonComponentVersion: true,
}
client := &abaputils.ClientMock{
BodyList: []string{
`{"d" : { "Status" : "S" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "empty" : "body" } }`,
},
Token: "myToken",
StatusCode: 200,
}
_, hook := test.NewNullLogger()
log.RegisterHook(hook)
err := runAbapEnvironmentCreateTag(config, nil, autils, client)
assert.NoError(t, err, "Did not expect error")
assert.Equal(t, 1, len(hook.Entries), "Expected a different number of entries")
assert.Equal(t, `Created tag tag for repository /DMO/SWC with commitID 1234abcd`, hook.AllEntries()[0].Message, "Expected a different message")
hook.Reset()
})
t.Run("backend error", func(t *testing.T) {
var autils = &abaputils.AUtilsMock{}
defer autils.Cleanup()
autils.ReturnedConnectionDetailsHTTP.Password = "password"
autils.ReturnedConnectionDetailsHTTP.User = "user"
autils.ReturnedConnectionDetailsHTTP.URL = "https://example.com"
autils.ReturnedConnectionDetailsHTTP.XCsrfToken = "xcsrftoken"
dir, errDir := ioutil.TempDir("", "test read addon descriptor")
if errDir != nil {
t.Fatal("Failed to create temporary directory")
}
oldCWD, _ := os.Getwd()
_ = os.Chdir(dir)
// clean up tmp dir
defer func() {
_ = os.Chdir(oldCWD)
_ = os.RemoveAll(dir)
}()
body := `---
addonVersion: "1.2.3"
addonProduct: "/DMO/PRODUCT"
repositories:
- name: /DMO/SWC
branch: main
commitID: 1234abcd
version: "4.5.6"
`
file, _ := os.Create("repo.yml")
file.Write([]byte(body))
config := &abapEnvironmentCreateTagOptions{
Username: "dummy",
Password: "dummy",
Host: "https://test.com",
Repositories: "repo.yml",
RepositoryName: "/DMO/SWC2",
CommitID: "1234abcde",
TagName: "tag",
TagDescription: "desc",
GenerateTagForAddonProductVersion: true,
GenerateTagForAddonComponentVersion: true,
}
client := &abaputils.ClientMock{
BodyList: []string{
`{"d" : { "Status" : "S" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "Status" : "S" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "Status" : "S" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "Status" : "S" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "Status" : "S" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "empty" : "body" } }`,
},
Token: "myToken",
StatusCode: 200,
}
err := runAbapEnvironmentCreateTag(config, nil, autils, client)
assert.Error(t, err, "Did expect error")
assert.Equal(t, "Something failed during the tag creation: Configuring the parameter repositories and the parameter repositoryName at the same time is not allowed", err.Error(), "Expected different error message")
})
t.Run("flags false", func(t *testing.T) {
var autils = &abaputils.AUtilsMock{}
defer autils.Cleanup()
autils.ReturnedConnectionDetailsHTTP.Password = "password"
autils.ReturnedConnectionDetailsHTTP.User = "user"
autils.ReturnedConnectionDetailsHTTP.URL = "https://example.com"
autils.ReturnedConnectionDetailsHTTP.XCsrfToken = "xcsrftoken"
dir, errDir := ioutil.TempDir("", "test read addon descriptor")
if errDir != nil {
t.Fatal("Failed to create temporary directory")
}
oldCWD, _ := os.Getwd()
_ = os.Chdir(dir)
// clean up tmp dir
defer func() {
_ = os.Chdir(oldCWD)
_ = os.RemoveAll(dir)
}()
body := `---
addonVersion: "1.2.3"
addonProduct: "/DMO/PRODUCT"
repositories:
- name: /DMO/SWC
branch: main
commitID: 1234abcd
version: "4.5.6"
`
file, _ := os.Create("repo.yml")
file.Write([]byte(body))
config := &abapEnvironmentCreateTagOptions{
Username: "dummy",
Password: "dummy",
Host: "https://test.com",
Repositories: "repo.yml",
TagName: "tag",
TagDescription: "desc",
GenerateTagForAddonProductVersion: false,
GenerateTagForAddonComponentVersion: false,
}
client := &abaputils.ClientMock{
BodyList: []string{
`{"d" : { "Status" : "S" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "Status" : "S" } }`,
`{"d" : { "uuid" : "abc" } }`,
`{"d" : { "empty" : "body" } }`,
},
Token: "myToken",
StatusCode: 200,
}
_, hook := test.NewNullLogger()
log.RegisterHook(hook)
err := runAbapEnvironmentCreateTag(config, nil, autils, client)
assert.NoError(t, err, "Did not expect error")
assert.Equal(t, 1, len(hook.Entries), "Expected a different number of entries")
assert.Equal(t, `Created tag tag for repository /DMO/SWC with commitID 1234abcd`, hook.AllEntries()[0].Message, "Expected a different message")
hook.Reset()
})
}

View File

@ -20,6 +20,7 @@ func GetAllStepMetadata() map[string]config.StepData {
"abapEnvironmentCheckoutBranch": abapEnvironmentCheckoutBranchMetadata(),
"abapEnvironmentCloneGitRepo": abapEnvironmentCloneGitRepoMetadata(),
"abapEnvironmentCreateSystem": abapEnvironmentCreateSystemMetadata(),
"abapEnvironmentCreateTag": abapEnvironmentCreateTagMetadata(),
"abapEnvironmentPullGitRepo": abapEnvironmentPullGitRepoMetadata(),
"abapEnvironmentPushATCSystemConfig": abapEnvironmentPushATCSystemConfigMetadata(),
"abapEnvironmentRunATCCheck": abapEnvironmentRunATCCheckMetadata(),

View File

@ -108,6 +108,7 @@ func Execute() {
rootCmd.AddCommand(AbapEnvironmentPullGitRepoCommand())
rootCmd.AddCommand(AbapEnvironmentCloneGitRepoCommand())
rootCmd.AddCommand(AbapEnvironmentCheckoutBranchCommand())
rootCmd.AddCommand(AbapEnvironmentCreateTagCommand())
rootCmd.AddCommand(AbapEnvironmentCreateSystemCommand())
rootCmd.AddCommand(CheckmarxExecuteScanCommand())
rootCmd.AddCommand(FortifyExecuteScanCommand())

View File

@ -14,6 +14,8 @@ import (
"github.com/pkg/errors"
)
const failureMessageClonePull = "Could not pull the Repository / Software Component "
// PollEntity periodically polls the pull/import entity to get the status. Check if the import is still running
func PollEntity(repositoryName string, connectionDetails ConnectionDetailsHTTP, client piperhttp.Sender, pollIntervall time.Duration) (string, error) {
@ -21,7 +23,7 @@ func PollEntity(repositoryName string, connectionDetails ConnectionDetailsHTTP,
var status string = "R"
for {
pullEntity, responseStatus, err := GetPullStatus(repositoryName, connectionDetails, client)
pullEntity, responseStatus, err := GetStatus(failureMessageClonePull+repositoryName, connectionDetails, client)
if err != nil {
return status, err
}
@ -78,7 +80,7 @@ func serviceContainsNewLogEntities(connectionDetails ConnectionDetailsHTTP, clie
func PrintLogs(repositoryName string, connectionDetails ConnectionDetailsHTTP, client piperhttp.Sender) {
connectionDetails.URL = connectionDetails.URL + "?$expand=to_Log_Overview,to_Log_Overview/to_Log_Protocol"
entity, _, err := GetPullStatus(repositoryName, connectionDetails, client)
entity, _, err := GetStatus(failureMessageClonePull+repositoryName, connectionDetails, client)
if err != nil {
return
}
@ -162,7 +164,7 @@ func printLog(logEntry LogResultsV2) {
func PrintLegacyLogs(repositoryName string, connectionDetails ConnectionDetailsHTTP, client piperhttp.Sender, errorOnSystem bool) {
connectionDetails.URL = connectionDetails.URL + "?$expand=to_Transport_log,to_Execution_log"
entity, _, err := GetPullStatus(repositoryName, connectionDetails, client)
entity, _, err := GetStatus(failureMessageClonePull+repositoryName, connectionDetails, client)
if err != nil {
return
}
@ -212,11 +214,11 @@ func PrintLegacyLogs(repositoryName string, connectionDetails ConnectionDetailsH
}
func GetPullStatus(repositoryName string, connectionDetails ConnectionDetailsHTTP, client piperhttp.Sender) (body PullEntity, status string, err error) {
func GetStatus(failureMessage string, connectionDetails ConnectionDetailsHTTP, client piperhttp.Sender) (body PullEntity, status string, err error) {
resp, err := GetHTTPResponse("GET", connectionDetails, nil, client)
if err != nil {
log.SetErrorCategory(log.ErrorInfrastructure)
err = HandleHTTPError(resp, err, "Could not pull the Repository / Software Component "+repositoryName, connectionDetails)
err = HandleHTTPError(resp, err, failureMessage, connectionDetails)
return body, resp.Status, err
}
defer resp.Body.Close()
@ -229,7 +231,7 @@ func GetPullStatus(repositoryName string, connectionDetails ConnectionDetailsHTT
json.Unmarshal(*abapResp["d"], &body)
if reflect.DeepEqual(PullEntity{}, body) {
log.Entry().WithField("StatusCode", resp.Status).WithField("repositoryName", repositoryName).Error("Could not pull the Repository / Software Component")
log.Entry().WithField("StatusCode", resp.Status).Error(failureMessage)
log.SetErrorCategory(log.ErrorInfrastructure)
var err = errors.New("Request to ABAP System not successful")
return body, resp.Status, err

View File

@ -0,0 +1,160 @@
metadata:
name: abapEnvironmentCreateTag
description: Creates a tag for a git repository to a SAP BTP ABAP Environment system
longDescription: |
Creates tags for specific commits of one or multiple repositories / software components. The tag can be specified explicitly as well as being generated by an addon product version or an addon component version.
Please provide either of the following options:
* The host and credentials the BTP ABAP Environment system itself. The credentials must be configured for the Communication Scenario [SAP_COM_0510](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/b04a9ae412894725a2fc539bfb1ca055.html).
* The Cloud Foundry parameters (API endpoint, organization, space), credentials, the service instance for the ABAP service and the service key for the Communication Scenario SAP_COM_0510.
* Only provide one of those options with the respective credentials. If all values are provided, the direct communication (via host) has priority.
spec:
inputs:
secrets:
- name: abapCredentialsId
description: Jenkins credentials ID containing user and password to authenticate to the BTP ABAP Environment system or the Cloud Foundry API
type: jenkins
aliases:
- name: cfCredentialsId
- name: credentialsId
params:
- name: username
type: string
description: User for either the Cloud Foundry API or the Communication Arrangement for SAP_COM_0510
scope:
- PARAMETERS
- STAGES
- STEPS
mandatory: true
secret: true
resourceRef:
- name: abapCredentialsId
type: secret
param: username
- name: password
type: string
description: Password for either the Cloud Foundry API or the Communication Arrangement for SAP_COM_0510
scope:
- PARAMETERS
- STAGES
- STEPS
mandatory: true
secret: true
resourceRef:
- name: abapCredentialsId
type: secret
param: password
- name: repositories
type: string
description: Specifies a YAML file containing the repositories configuration
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
- name: repositoryName
type: string
description: Specifies a repository (Software Components) on the SAP BTP ABAP Environment system
scope:
- PARAMETERS
- STAGES
- STEPS
- name: commitID
type: string
description: Specifies a commitID, for which a tag will be created
scope:
- PARAMETERS
- STAGES
- STEPS
- name: tagName
type: string
description: Specifies a tagName that will be created for the repositories on the SAP BTP ABAP Environment system
scope:
- PARAMETERS
- STAGES
- STEPS
- name: tagDescription
type: string
description: Specifies a description for the created tag
scope:
- PARAMETERS
- STAGES
- STEPS
- name: generateTagForAddonProductVersion
type: bool
description: Specifies if a tag will be created for the repositories on the SAP BTP ABAP Environment system
scope:
- PARAMETERS
- STAGES
- STEPS
- name: generateTagForAddonComponentVersion
type: bool
description: Specifies if a tag will be created for the repositories on the SAP BTP ABAP Environment system
scope:
- PARAMETERS
- STAGES
- STEPS
- name: host
type: string
description: Specifies the host address of the SAP BTP ABAP Environment system
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
- name: cfApiEndpoint
type: string
description: Cloud Foundry API Enpoint
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
aliases:
- name: cloudFoundry/apiEndpoint
- name: cfOrg
type: string
description: Cloud Foundry target organization
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
aliases:
- name: cloudFoundry/org
- name: cfSpace
type: string
description: Cloud Foundry target space
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
aliases:
- name: cloudFoundry/space
- name: cfServiceInstance
type: string
description: Cloud Foundry Service Instance
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
aliases:
- name: cloudFoundry/serviceInstance
- name: cfServiceKeyName
type: string
description: Cloud Foundry Service Key
scope:
- PARAMETERS
- STAGES
- STEPS
- GENERAL
aliases:
- name: cloudFoundry/serviceKey
- name: cloudFoundry/serviceKeyName
- name: cfServiceKey
containers:
- name: cf
image: ppiper/cf-cli:7