1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-16 05:16:08 +02:00

Add abap utils pkg - ABAP Environment Steps (#1757)

* Add abaputils pkg and go files

* Add ReadServiceKeyAbapEnvironment function

* Fixes

* Add structs for SC, Pull and Branch

* Minor Improvements

* Adapt unit tests to new abaputils pkg

* Fixes

* Add adapted tests

* Fixes

* Fix cloudfoundry test

* Add check for host prefix (HTTPS)

* Fix tests + cleanup

* Fixes

* Fixes

* Fix

* Add mock for abaputils pkg unit tests

* Adapt abaputils comments

* Add unit test for missing params case

* Fix for missing mapping of CfSpace

* Fix host schema

* Remove LogoutOption param of unit tests and steps

* Fix unit test

* Fix unit test CF ReadServiceKey

Co-authored-by: Daniel Mieg <56156797+DanielMieg@users.noreply.github.com>
This commit is contained in:
MuellerHenrik 2020-07-23 10:26:50 +02:00 committed by GitHub
parent 4fd81eb7f5
commit e3f914e09d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 625 additions and 391 deletions

View File

@ -7,12 +7,12 @@ import (
"net/http"
"net/http/cookiejar"
"reflect"
"regexp"
"sort"
"strconv"
"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"
@ -20,11 +20,24 @@ import (
"github.com/pkg/errors"
)
func abapEnvironmentPullGitRepo(config abapEnvironmentPullGitRepoOptions, telemetryData *telemetry.CustomData) error {
func abapEnvironmentPullGitRepo(options abapEnvironmentPullGitRepoOptions, telemetryData *telemetry.CustomData) error {
// Mapping for options
subOptions := abaputils.AbapEnvironmentOptions{}
subOptions.CfAPIEndpoint = options.CfAPIEndpoint
subOptions.CfServiceInstance = options.CfServiceInstance
subOptions.CfServiceKeyName = options.CfServiceKeyName
subOptions.CfOrg = options.CfOrg
subOptions.CfSpace = options.CfSpace
subOptions.Host = options.Host
subOptions.Password = options.Password
subOptions.Username = options.Username
var c command.ExecRunner = &command.Command{}
// Determine the host, user and password, either via the input parameters or via a cloud foundry service key
c := command.Command{}
connectionDetails, errorGetInfo := getAbapCommunicationArrangementInfo(config, &c)
connectionDetails, errorGetInfo := abaputils.GetAbapCommunicationArrangementInfo(subOptions, c, "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull")
if errorGetInfo != nil {
log.Entry().WithError(errorGetInfo).Fatal("Parameters for the ABAP Connection not available")
}
@ -44,8 +57,8 @@ func abapEnvironmentPullGitRepo(config abapEnvironmentPullGitRepoOptions, teleme
client.SetOptions(clientOptions)
pollIntervall := 10 * time.Second
log.Entry().Infof("Start pulling %v repositories", len(config.RepositoryNames))
for _, repositoryName := range config.RepositoryNames {
log.Entry().Infof("Start pulling %v repositories", len(options.RepositoryNames))
for _, repositoryName := range options.RepositoryNames {
log.Entry().Info("-------------------------")
log.Entry().Info("Start pulling " + repositoryName)
@ -73,7 +86,7 @@ func abapEnvironmentPullGitRepo(config abapEnvironmentPullGitRepoOptions, teleme
return nil
}
func triggerPull(repositoryName string, pullConnectionDetails connectionDetailsHTTP, client piperhttp.Sender) (connectionDetailsHTTP, error) {
func triggerPull(repositoryName string, pullConnectionDetails abaputils.ConnectionDetailsHTTP, client piperhttp.Sender) (abaputils.ConnectionDetailsHTTP, error) {
uriConnectionDetails := pullConnectionDetails
uriConnectionDetails.URL = ""
@ -104,7 +117,7 @@ func triggerPull(repositoryName string, pullConnectionDetails connectionDetailsH
log.Entry().WithField("StatusCode", resp.Status).WithField("repositoryName", repositoryName).Info("Triggered Pull of Repository / Software Component")
// Parse Response
var body abapEntity
var body abaputils.PullEntity
var abapResp map[string]*json.RawMessage
bodyText, errRead := ioutil.ReadAll(resp.Body)
if errRead != nil {
@ -112,7 +125,7 @@ func triggerPull(repositoryName string, pullConnectionDetails connectionDetailsH
}
json.Unmarshal(bodyText, &abapResp)
json.Unmarshal(*abapResp["d"], &body)
if reflect.DeepEqual(abapEntity{}, body) {
if reflect.DeepEqual(abaputils.PullEntity{}, body) {
log.Entry().WithField("StatusCode", resp.Status).WithField("repositoryName", repositoryName).Error("Could not pull the Repository / Software Component")
err := errors.New("Request to ABAP System not successful")
return uriConnectionDetails, err
@ -123,7 +136,7 @@ func triggerPull(repositoryName string, pullConnectionDetails connectionDetailsH
return uriConnectionDetails, nil
}
func pollEntity(repositoryName string, connectionDetails connectionDetailsHTTP, client piperhttp.Sender, pollIntervall time.Duration) (string, error) {
func pollEntity(repositoryName string, connectionDetails abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, pollIntervall time.Duration) (string, error) {
log.Entry().Info("Start polling the status...")
var status string = "R"
@ -137,18 +150,18 @@ func pollEntity(repositoryName string, connectionDetails connectionDetailsHTTP,
defer resp.Body.Close()
// Parse response
var body abapEntity
var body abaputils.PullEntity
bodyText, _ := ioutil.ReadAll(resp.Body)
var abapResp map[string]*json.RawMessage
json.Unmarshal(bodyText, &abapResp)
json.Unmarshal(*abapResp["d"], &body)
if reflect.DeepEqual(abapEntity{}, body) {
if reflect.DeepEqual(abaputils.PullEntity{}, body) {
log.Entry().WithField("StatusCode", resp.Status).WithField("repositoryName", repositoryName).Error("Could not pull the Repository / Software Component")
var err = errors.New("Request to ABAP System not successful")
return "", err
}
status = body.Status
log.Entry().WithField("StatusCode", resp.Status).Info("Pull Status: " + body.StatusDescr)
log.Entry().WithField("StatusCode", resp.Status).Info("Pull Status: " + body.StatusDescription)
if body.Status != "R" {
printLogs(body)
break
@ -159,78 +172,7 @@ func pollEntity(repositoryName string, connectionDetails connectionDetailsHTTP,
return status, nil
}
func getAbapCommunicationArrangementInfo(config abapEnvironmentPullGitRepoOptions, c command.ExecRunner) (connectionDetailsHTTP, error) {
var connectionDetails connectionDetailsHTTP
var error error
if config.Host != "" {
// Host, User and Password are directly provided
matchedkey, error := regexp.MatchString(`^[hH][tT][tT][pP][sS]:\/\/.*`, config.Host)
if error != nil {
return connectionDetails, errors.New("Error occured while parsing the host parameter. Please check if this parameter has been seet correctly")
}
if matchedkey {
connectionDetails.URL = config.Host + "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull"
} else {
connectionDetails.URL = "https://" + config.Host + "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull"
}
connectionDetails.User = config.Username
connectionDetails.Password = config.Password
} else {
if config.CfAPIEndpoint == "" || config.CfOrg == "" || config.CfSpace == "" || config.CfServiceInstance == "" || config.CfServiceKeyName == "" {
var err = errors.New("Parameters missing. Please provide EITHER the Host of the ABAP server OR the Cloud Foundry ApiEndpoint, Organization, Space, Service Instance and a corresponding Service Key for the Communication Scenario SAP_COM_0510")
return connectionDetails, err
}
// Url, User and Password should be read from a cf service key
var abapServiceKey, error = readCfServiceKey(config, c)
if error != nil {
return connectionDetails, errors.Wrap(error, "Read service key failed")
}
connectionDetails.URL = abapServiceKey.URL + "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull"
connectionDetails.User = abapServiceKey.Abap.Username
connectionDetails.Password = abapServiceKey.Abap.Password
}
return connectionDetails, error
}
func readCfServiceKey(config abapEnvironmentPullGitRepoOptions, c command.ExecRunner) (serviceKey, error) {
var abapServiceKey serviceKey
c.Stderr(log.Writer())
// Logging into the Cloud Foundry via CF CLI
log.Entry().WithField("cfApiEndpoint", config.CfAPIEndpoint).WithField("cfSpace", config.CfSpace).WithField("cfOrg", config.CfOrg).WithField("User", config.Username).Info("Cloud Foundry parameters: ")
cfLoginSlice := []string{"login", "-a", config.CfAPIEndpoint, "-u", config.Username, "-p", config.Password, "-o", config.CfOrg, "-s", config.CfSpace}
errorRunExecutable := c.RunExecutable("cf", cfLoginSlice...)
if errorRunExecutable != nil {
log.Entry().Error("Login at cloud foundry failed.")
return abapServiceKey, errorRunExecutable
}
// Reading the Service Key via CF CLI
var serviceKeyBytes bytes.Buffer
c.Stdout(&serviceKeyBytes)
cfReadServiceKeySlice := []string{"service-key", config.CfServiceInstance, config.CfServiceKeyName}
errorRunExecutable = c.RunExecutable("cf", cfReadServiceKeySlice...)
var serviceKeyJSON string
if len(serviceKeyBytes.String()) > 0 {
var lines []string = strings.Split(serviceKeyBytes.String(), "\n")
serviceKeyJSON = strings.Join(lines[2:], "")
}
if errorRunExecutable != nil {
return abapServiceKey, errorRunExecutable
}
log.Entry().WithField("cfServiceInstance", config.CfServiceInstance).WithField("cfServiceKeyName", config.CfServiceKeyName).Info("Read service key for service instance")
json.Unmarshal([]byte(serviceKeyJSON), &abapServiceKey)
if abapServiceKey == (serviceKey{}) {
return abapServiceKey, errors.New("Parsing the service key failed")
}
return abapServiceKey, errorRunExecutable
}
func getHTTPResponse(requestType string, connectionDetails connectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
func getHTTPResponse(requestType string, connectionDetails abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
header := make(map[string][]string)
header["Content-Type"] = []string{"application/json"}
@ -241,7 +183,7 @@ func getHTTPResponse(requestType string, connectionDetails connectionDetailsHTTP
return req, err
}
func handleHTTPError(resp *http.Response, err error, message string, connectionDetails connectionDetailsHTTP) error {
func handleHTTPError(resp *http.Response, err error, message string, connectionDetails abaputils.ConnectionDetailsHTTP) error {
if resp == nil {
// Response is nil in case of a timeout
log.Entry().WithError(err).WithField("ABAP Endpoint", connectionDetails.URL).Error("Request failed")
@ -249,7 +191,7 @@ func handleHTTPError(resp *http.Response, err error, message string, connectionD
log.Entry().WithField("StatusCode", resp.Status).Error(message)
// Include the error message of the ABAP Environment system, if available
var abapErrorResponse abapError
var abapErrorResponse abaputils.AbapError
bodyText, readError := ioutil.ReadAll(resp.Body)
if readError != nil {
return readError
@ -257,7 +199,7 @@ func handleHTTPError(resp *http.Response, err error, message string, connectionD
var abapResp map[string]*json.RawMessage
json.Unmarshal(bodyText, &abapResp)
json.Unmarshal(*abapResp["error"], &abapErrorResponse)
if (abapError{}) != abapErrorResponse {
if (abaputils.AbapError{}) != abapErrorResponse {
log.Entry().WithField("ErrorCode", abapErrorResponse.Code).Error(abapErrorResponse.Message.Value)
abapError := errors.New(abapErrorResponse.Code + " - " + abapErrorResponse.Message.Value)
err = errors.Wrap(abapError, err.Error())
@ -267,7 +209,7 @@ func handleHTTPError(resp *http.Response, err error, message string, connectionD
return err
}
func printLogs(entity abapEntity) {
func printLogs(entity abaputils.PullEntity) {
// Sort logs
sort.SliceStable(entity.ToExecutionLog.Results, func(i, j int) bool {
@ -306,72 +248,3 @@ func convertTime(logTimeStamp string) time.Time {
t := time.Unix(n, 0).UTC()
return t
}
type abapEntity struct {
Metadata abapMetadata `json:"__metadata"`
UUID string `json:"uuid"`
ScName string `json:"sc_name"`
Namespace string `json:"namepsace"`
Status string `json:"status"`
StatusDescr string `json:"status_descr"`
ToExecutionLog abapLogs `json:"to_Execution_log"`
ToTransportLog abapLogs `json:"to_Transport_log"`
}
type abapMetadata struct {
URI string `json:"uri"`
}
type abapLogs struct {
Results []logResults `json:"results"`
}
type logResults struct {
Index string `json:"index_no"`
Type string `json:"type"`
Description string `json:"descr"`
Timestamp string `json:"timestamp"`
}
type serviceKey struct {
Abap abapConenction `json:"abap"`
Binding abapBinding `json:"binding"`
Systemid string `json:"systemid"`
URL string `json:"url"`
}
type deferred struct {
URI string `json:"uri"`
}
type abapConenction struct {
CommunicationArrangementID string `json:"communication_arrangement_id"`
CommunicationScenarioID string `json:"communication_scenario_id"`
CommunicationSystemID string `json:"communication_system_id"`
Password string `json:"password"`
Username string `json:"username"`
}
type abapBinding struct {
Env string `json:"env"`
ID string `json:"id"`
Type string `json:"type"`
Version string `json:"version"`
}
type connectionDetailsHTTP struct {
User string `json:"user"`
Password string `json:"password"`
URL string `json:"url"`
XCsrfToken string `json:"xcsrftoken"`
}
type abapError struct {
Code string `json:"code"`
Message abapErrorMessage `json:"message"`
}
type abapErrorMessage struct {
Lang string `json:"lang"`
Value string `json:"value"`
}

View File

@ -7,6 +7,7 @@ import (
"net/http"
"testing"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/pkg/errors"
@ -38,7 +39,7 @@ func TestTriggerPull(t *testing.T) {
RepositoryNames: []string{"testRepo1", "testRepo2"},
}
con := connectionDetailsHTTP{
con := abaputils.ConnectionDetailsHTTP{
User: "MY_USER",
Password: "MY_PW",
URL: "https://api.endpoint.com/Entity/",
@ -73,7 +74,7 @@ func TestTriggerPull(t *testing.T) {
RepositoryNames: []string{"testRepo1", "testRepo2"},
}
con := connectionDetailsHTTP{
con := abaputils.ConnectionDetailsHTTP{
User: "MY_USER",
Password: "MY_PW",
URL: "https://api.endpoint.com/Entity/",
@ -107,7 +108,7 @@ func TestPollEntity(t *testing.T) {
RepositoryNames: []string{"testRepo1", "testRepo2"},
}
con := connectionDetailsHTTP{
con := abaputils.ConnectionDetailsHTTP{
User: "MY_USER",
Password: "MY_PW",
URL: "https://api.endpoint.com/Entity/",
@ -138,7 +139,7 @@ func TestPollEntity(t *testing.T) {
RepositoryNames: []string{"testRepo1", "testRepo2"},
}
con := connectionDetailsHTTP{
con := abaputils.ConnectionDetailsHTTP{
User: "MY_USER",
Password: "MY_PW",
URL: "https://api.endpoint.com/Entity/",
@ -154,7 +155,7 @@ func TestGetAbapCommunicationArrangementInfo(t *testing.T) {
t.Run("Test cf cli command: success case", func(t *testing.T) {
config := abapEnvironmentPullGitRepoOptions{
config := abaputils.AbapEnvironmentOptions{
CfAPIEndpoint: "https://api.endpoint.com",
CfOrg: "testOrg",
CfSpace: "testSpace",
@ -164,50 +165,23 @@ func TestGetAbapCommunicationArrangementInfo(t *testing.T) {
Password: "testPassword",
}
execRunner := mock.ExecMockRunner{}
options := abaputils.AbapEnvironmentPullGitRepoOptions{
AbapEnvOptions: config,
}
getAbapCommunicationArrangementInfo(config, &execRunner)
execRunner := &mock.ExecMockRunner{}
abaputils.GetAbapCommunicationArrangementInfo(options.AbapEnvOptions, execRunner, "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull")
assert.Equal(t, "cf", execRunner.Calls[0].Exec, "Wrong command")
assert.Equal(t, []string{"login", "-a", "https://api.endpoint.com", "-u", "testUser", "-p", "testPassword", "-o", "testOrg", "-s", "testSpace"}, execRunner.Calls[0].Params, "Wrong parameters")
})
assert.Equal(t, []string{"login", "-a", "https://api.endpoint.com", "-o", "testOrg", "-s", "testSpace", "-u", "testUser", "-p", "testPassword"}, execRunner.Calls[0].Params, "Wrong parameters")
//assert.Equal(t, []string{"api", "https://api.endpoint.com"}, execRunner.Calls[0].Params, "Wrong parameters")
t.Run("Test host prefix: with https", func(t *testing.T) {
config := abapEnvironmentPullGitRepoOptions{
Host: "test.host.com",
Username: "testUser",
Password: "testPassword",
}
execRunner := mock.ExecMockRunner{}
con, err := getAbapCommunicationArrangementInfo(config, &execRunner)
if err == nil {
assert.Equal(t, "testUser", con.User)
assert.Equal(t, "testPassword", con.Password)
assert.Equal(t, "https://test.host.com/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull", con.URL)
}
})
t.Run("Test host prefix: with https", func(t *testing.T) {
config := abapEnvironmentPullGitRepoOptions{
Host: "https://test.host.com",
Username: "testUser",
Password: "testPassword",
}
execRunner := mock.ExecMockRunner{}
con, err := getAbapCommunicationArrangementInfo(config, &execRunner)
if err == nil {
assert.Equal(t, "testUser", con.User)
assert.Equal(t, "testPassword", con.Password)
assert.Equal(t, "https://test.host.com/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull", con.URL)
}
})
t.Run("Test cf cli command: params missing", func(t *testing.T) {
config := abapEnvironmentPullGitRepoOptions{
config := abaputils.AbapEnvironmentOptions{
//CfServiceKeyName: "testServiceKey", this parameter will be missing
CfAPIEndpoint: "https://api.endpoint.com",
CfOrg: "testOrg",
CfSpace: "testSpace",
@ -216,22 +190,30 @@ func TestGetAbapCommunicationArrangementInfo(t *testing.T) {
Password: "testPassword",
}
execRunner := mock.ExecMockRunner{}
options := abaputils.AbapEnvironmentPullGitRepoOptions{
AbapEnvOptions: config,
}
var _, err = getAbapCommunicationArrangementInfo(config, &execRunner)
execRunner := &mock.ExecMockRunner{}
var _, err = abaputils.GetAbapCommunicationArrangementInfo(options.AbapEnvOptions, execRunner, "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull")
assert.Equal(t, "Parameters missing. Please provide EITHER the Host of the ABAP server OR the Cloud Foundry ApiEndpoint, Organization, Space, Service Instance and a corresponding Service Key for the Communication Scenario SAP_COM_0510", err.Error(), "Different error message expected")
})
t.Run("Test cf cli command: params missing", func(t *testing.T) {
config := abapEnvironmentPullGitRepoOptions{
config := abaputils.AbapEnvironmentOptions{
Username: "testUser",
Password: "testPassword",
}
execRunner := mock.ExecMockRunner{}
options := abaputils.AbapEnvironmentPullGitRepoOptions{
AbapEnvOptions: config,
}
var _, err = getAbapCommunicationArrangementInfo(config, &execRunner)
execRunner := &mock.ExecMockRunner{}
var _, err = abaputils.GetAbapCommunicationArrangementInfo(options.AbapEnvOptions, execRunner, "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull")
assert.Equal(t, "Parameters missing. Please provide EITHER the Host of the ABAP server OR the Cloud Foundry ApiEndpoint, Organization, Space, Service Instance and a corresponding Service Key for the Communication Scenario SAP_COM_0510", err.Error(), "Different error message expected")
})

View File

@ -9,11 +9,10 @@ import (
"net/http"
"net/http/cookiejar"
"path/filepath"
"regexp"
"strconv"
"time"
"github.com/SAP/jenkins-library/pkg/cloudfoundry"
"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"
@ -23,10 +22,21 @@ import (
"github.com/pkg/errors"
)
func abapEnvironmentRunATCCheck(config abapEnvironmentRunATCCheckOptions, telemetryData *telemetry.CustomData) {
func abapEnvironmentRunATCCheck(options abapEnvironmentRunATCCheckOptions, telemetryData *telemetry.CustomData) {
var c = command.Command{}
// Mapping for options
subOptions := abaputils.AbapEnvironmentOptions{}
subOptions.CfAPIEndpoint = options.CfAPIEndpoint
subOptions.CfServiceInstance = options.CfServiceInstance
subOptions.CfServiceKeyName = options.CfServiceKeyName
subOptions.CfOrg = options.CfOrg
subOptions.CfSpace = options.CfSpace
subOptions.Host = options.Host
subOptions.Password = options.Password
subOptions.Username = options.Username
var c = &command.Command{}
var err error
c.Stdout(log.Entry().Writer())
@ -39,11 +49,11 @@ func abapEnvironmentRunATCCheck(config abapEnvironmentRunATCCheckOptions, teleme
}
client.SetOptions(clientOptions)
var details connectionDetailsHTTP
var details abaputils.ConnectionDetailsHTTP
var abapEndpoint string
//If Host flag is empty read ABAP endpoint from Service Key instead. Otherwise take ABAP system endpoint from config instead
if err == nil {
details, err = checkHost(config, details)
details, err = abaputils.GetAbapCommunicationArrangementInfo(subOptions, c, "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull")
}
var resp *http.Response
//Fetch Xcrsf-Token
@ -58,7 +68,7 @@ func abapEnvironmentRunATCCheck(config abapEnvironmentRunATCCheckOptions, teleme
details.XCsrfToken, err = fetchXcsrfToken("GET", details, nil, &client)
}
if err == nil {
resp, err = triggerATCrun(config, details, &client, abapEndpoint)
resp, err = triggerATCrun(options, details, &client, abapEndpoint)
}
if err == nil {
err = handleATCresults(resp, details, &client, abapEndpoint)
@ -70,7 +80,7 @@ func abapEnvironmentRunATCCheck(config abapEnvironmentRunATCCheckOptions, teleme
log.Entry().Info("ATC run completed succesfully. The respective run results are listed above.")
}
func handleATCresults(resp *http.Response, details connectionDetailsHTTP, client piperhttp.Sender, abapEndpoint string) error {
func handleATCresults(resp *http.Response, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, abapEndpoint string) error {
var err error
location := resp.Header.Get("Location")
details.URL = abapEndpoint + location
@ -94,7 +104,7 @@ func handleATCresults(resp *http.Response, details connectionDetailsHTTP, client
return nil
}
func triggerATCrun(config abapEnvironmentRunATCCheckOptions, details connectionDetailsHTTP, client piperhttp.Sender, abapEndpoint string) (*http.Response, error) {
func triggerATCrun(config abapEnvironmentRunATCCheckOptions, details abaputils.ConnectionDetailsHTTP, client piperhttp.Sender, abapEndpoint string) (*http.Response, error) {
var atcConfigyamlFile []byte
filelocation, err := filepath.Glob(config.AtcConfig)
//Parse YAML ATC run configuration as body for ATC run trigger
@ -179,7 +189,7 @@ func parseATCResult(body []byte) error {
return nil
}
func runATC(requestType string, details connectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
func runATC(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
log.Entry().WithField("ABAP endpoint: ", details.URL).Info("Triggering ATC run")
@ -195,7 +205,7 @@ func runATC(requestType string, details connectionDetailsHTTP, body []byte, clie
return req, err
}
func fetchXcsrfToken(requestType string, details connectionDetailsHTTP, body []byte, client piperhttp.Sender) (string, error) {
func fetchXcsrfToken(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (string, error) {
log.Entry().WithField("ABAP Endpoint: ", details.URL).Info("Fetching Xcrsf-Token")
@ -213,49 +223,7 @@ func fetchXcsrfToken(requestType string, details connectionDetailsHTTP, body []b
return token, err
}
func checkHost(config abapEnvironmentRunATCCheckOptions, details connectionDetailsHTTP) (connectionDetailsHTTP, error) {
var err error
if config.Host == "" {
cfconfig := cloudfoundry.ServiceKeyOptions{
CfAPIEndpoint: config.CfAPIEndpoint,
CfOrg: config.CfOrg,
CfSpace: config.CfSpace,
Username: config.Username,
Password: config.Password,
CfServiceInstance: config.CfServiceInstance,
CfServiceKey: config.CfServiceKeyName,
}
if cfconfig.CfServiceInstance == "" || cfconfig.CfOrg == "" || cfconfig.CfAPIEndpoint == "" || cfconfig.CfSpace == "" || cfconfig.CfServiceKey == "" {
return details, errors.New("Parameters missing. Please provide EITHER the Host of the ABAP server OR the Cloud Foundry ApiEndpoint, Organization, Space, Service Instance and a corresponding Service Key for the Communication Scenario SAP_COM_0510")
}
var abapServiceKey cloudfoundry.ServiceKey
cf := cloudfoundry.CFUtils{Exec: &command.Command{}}
abapServiceKey, err = cf.ReadServiceKeyAbapEnvironment(cfconfig, true)
if err != nil {
return details, fmt.Errorf("Reading Service Key failed: %w", err)
}
details.User = abapServiceKey.Abap.Username
details.Password = abapServiceKey.Abap.Password
details.URL = abapServiceKey.URL
return details, err
}
details.User = config.Username
details.Password = config.Password
matchedkey, err := regexp.MatchString(`^[hH][tT][tT][pP][sS]:\/\/.*`, config.Host)
if err != nil {
return details, errors.New("Error occured while parsing the host parameter. Please check if this parameter has been seet correctly")
}
if matchedkey {
details.URL = config.Host
} else {
details.URL = "https://" + config.Host
}
return details, err
}
func pollATCRun(details connectionDetailsHTTP, body []byte, client piperhttp.Sender) (string, error) {
func pollATCRun(details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (string, error) {
log.Entry().WithField("ABAP endpoint", details.URL).Info("Polling ATC run status")
@ -286,7 +254,7 @@ func pollATCRun(details connectionDetailsHTTP, body []byte, client piperhttp.Sen
}
}
func getHTTPResponseATCRun(requestType string, details connectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
func getHTTPResponseATCRun(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
log.Entry().WithField("ABAP Endpoint: ", details.URL).Info("Polling ATC run status")
@ -300,7 +268,7 @@ func getHTTPResponseATCRun(requestType string, details connectionDetailsHTTP, bo
return req, err
}
func getResultATCRun(requestType string, details connectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
func getResultATCRun(requestType string, details abaputils.ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
log.Entry().WithField("ABAP Endpoint: ", details.URL).Info("Getting ATC results")

View File

@ -5,43 +5,36 @@ import (
"os"
"testing"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
func TestHostConfig(t *testing.T) {
t.Run("Check Host: ABAP Endpoint with HTTPS prefix", func(t *testing.T) {
config := abapEnvironmentRunATCCheckOptions{
t.Run("Check Host: ABAP Endpoint", func(t *testing.T) {
config := abaputils.AbapEnvironmentOptions{
Username: "testUser",
Password: "testPassword",
Host: "https://api.endpoint.com",
}
var con connectionDetailsHTTP
con, error := checkHost(config, con)
options := abaputils.AbapEnvironmentRunATCCheckOptions{
AbapEnvOptions: config,
}
execRunner := &mock.ExecMockRunner{}
var con abaputils.ConnectionDetailsHTTP
con, error := abaputils.GetAbapCommunicationArrangementInfo(options.AbapEnvOptions, execRunner, "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull")
if error == nil {
assert.Equal(t, "testUser", con.User)
assert.Equal(t, "testPassword", con.Password)
assert.Equal(t, "https://api.endpoint.com", con.URL)
assert.Equal(t, "", con.XCsrfToken)
}
})
t.Run("Check Host: ABAP Endpoint without HTTPS prefix", func(t *testing.T) {
config := abapEnvironmentRunATCCheckOptions{
Username: "testUser",
Password: "testPassword",
Host: "api.endpoint.com",
}
var con connectionDetailsHTTP
con, error := checkHost(config, con)
if error == nil {
assert.Equal(t, "testUser", con.User)
assert.Equal(t, "testPassword", con.Password)
assert.Equal(t, "https://api.endpoint.com", con.URL)
assert.Equal(t, "https://api.endpoint.com/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull", con.URL)
assert.Equal(t, "", con.XCsrfToken)
}
})
t.Run("No host/ServiceKey configuration", func(t *testing.T) {
//Testing without CfOrg parameter
config := abapEnvironmentRunATCCheckOptions{
config := abaputils.AbapEnvironmentOptions{
CfAPIEndpoint: "https://api.endpoint.com",
CfSpace: "testSpace",
CfServiceInstance: "testInstance",
@ -49,19 +42,25 @@ func TestHostConfig(t *testing.T) {
Username: "testUser",
Password: "testPassword",
}
var con connectionDetailsHTTP
con, err := checkHost(config, con)
options := abaputils.AbapEnvironmentRunATCCheckOptions{
AbapEnvOptions: config,
}
execRunner := &mock.ExecMockRunner{}
_, err := abaputils.GetAbapCommunicationArrangementInfo(options.AbapEnvOptions, execRunner, "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull")
assert.EqualError(t, err, "Parameters missing. Please provide EITHER the Host of the ABAP server OR the Cloud Foundry ApiEndpoint, Organization, Space, Service Instance and a corresponding Service Key for the Communication Scenario SAP_COM_0510")
//Testing without ABAP Host
config = abapEnvironmentRunATCCheckOptions{
config = abaputils.AbapEnvironmentOptions{
Username: "testUser",
Password: "testPassword",
}
con, err = checkHost(config, con)
_, err = abaputils.GetAbapCommunicationArrangementInfo(options.AbapEnvOptions, execRunner, "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull")
assert.EqualError(t, err, "Parameters missing. Please provide EITHER the Host of the ABAP server OR the Cloud Foundry ApiEndpoint, Organization, Space, Service Instance and a corresponding Service Key for the Communication Scenario SAP_COM_0510")
})
t.Run("Check Host: CF Service Key", func(t *testing.T) {
config := abapEnvironmentRunATCCheckOptions{
config := abaputils.AbapEnvironmentOptions{
CfAPIEndpoint: "https://api.endpoint.com",
CfSpace: "testSpace",
CfOrg: "Test",
@ -70,8 +69,12 @@ func TestHostConfig(t *testing.T) {
Username: "testUser",
Password: "testPassword",
}
var con connectionDetailsHTTP
con, error := checkHost(config, con)
options := abaputils.AbapEnvironmentRunATCCheckOptions{
AbapEnvOptions: config,
}
execRunner := &mock.ExecMockRunner{}
var con abaputils.ConnectionDetailsHTTP
con, error := abaputils.GetAbapCommunicationArrangementInfo(options.AbapEnvOptions, execRunner, "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull")
if error == nil {
assert.Equal(t, "", con.User)
assert.Equal(t, "", con.Password)
@ -79,7 +82,6 @@ func TestHostConfig(t *testing.T) {
assert.Equal(t, "", con.XCsrfToken)
}
})
}
func TestATCTrigger(t *testing.T) {
@ -91,7 +93,7 @@ func TestATCTrigger(t *testing.T) {
Token: tokenExpected,
}
con := connectionDetailsHTTP{
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
@ -114,7 +116,7 @@ func TestFetchXcsrfToken(t *testing.T) {
Token: tokenExpected,
}
con := connectionDetailsHTTP{
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
@ -132,7 +134,7 @@ func TestFetchXcsrfToken(t *testing.T) {
Token: "",
}
con := connectionDetailsHTTP{
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
@ -153,7 +155,7 @@ func TestPollATCRun(t *testing.T) {
Token: tokenExpected,
}
con := connectionDetailsHTTP{
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
@ -173,7 +175,7 @@ func TestGetHTTPResponseATCRun(t *testing.T) {
Body: `HTTP response test`,
}
con := connectionDetailsHTTP{
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",
@ -195,7 +197,7 @@ func TestGetResultATCRun(t *testing.T) {
},
}
con := connectionDetailsHTTP{
con := abaputils.ConnectionDetailsHTTP{
User: "Test",
Password: "Test",
URL: "https://api.endpoint.com/Entity/",

222
pkg/abaputils/abaputils.go Normal file
View File

@ -0,0 +1,222 @@
package abaputils
import (
"encoding/json"
"regexp"
"github.com/SAP/jenkins-library/pkg/cloudfoundry"
"github.com/SAP/jenkins-library/pkg/command"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/pkg/errors"
)
// GetAbapCommunicationArrangementInfo function fetches the communcation arrangement information in SAP CP ABAP Environment
func GetAbapCommunicationArrangementInfo(options AbapEnvironmentOptions, c command.ExecRunner, oDataURL string) (ConnectionDetailsHTTP, error) {
var connectionDetails ConnectionDetailsHTTP
var error error
if options.Host != "" {
// Host, User and Password are directly provided -> check for host schema (double https)
match, err := regexp.MatchString(`^(https|HTTPS):\/\/.*`, options.Host)
if err != nil {
return connectionDetails, errors.Wrap(err, "Schema validation for host parameter failed. Check for https.")
}
var hostOdataURL = options.Host + oDataURL
if match {
connectionDetails.URL = hostOdataURL
} else {
connectionDetails.URL = "https://" + hostOdataURL
}
connectionDetails.User = options.Username
connectionDetails.Password = options.Password
} else {
if options.CfAPIEndpoint == "" || options.CfOrg == "" || options.CfSpace == "" || options.CfServiceInstance == "" || options.CfServiceKeyName == "" {
var err = errors.New("Parameters missing. Please provide EITHER the Host of the ABAP server OR the Cloud Foundry ApiEndpoint, Organization, Space, Service Instance and a corresponding Service Key for the Communication Scenario SAP_COM_0510")
return connectionDetails, err
}
// Url, User and Password should be read from a cf service key
var abapServiceKey, error = ReadServiceKeyAbapEnvironment(options, c)
if error != nil {
return connectionDetails, errors.Wrap(error, "Read service key failed")
}
connectionDetails.URL = abapServiceKey.URL + oDataURL
connectionDetails.User = abapServiceKey.Abap.Username
connectionDetails.Password = abapServiceKey.Abap.Password
}
return connectionDetails, error
}
// ReadServiceKeyAbapEnvironment from Cloud Foundry and returns it.
// Depending on user/developer requirements if he wants to perform further Cloud Foundry actions
func ReadServiceKeyAbapEnvironment(options AbapEnvironmentOptions, c command.ExecRunner) (AbapServiceKey, error) {
var abapServiceKey AbapServiceKey
var serviceKeyJSON string
var err error
cfconfig := cloudfoundry.ServiceKeyOptions{
CfAPIEndpoint: options.CfAPIEndpoint,
CfOrg: options.CfOrg,
CfSpace: options.CfSpace,
CfServiceInstance: options.CfServiceInstance,
CfServiceKeyName: options.CfServiceKeyName,
Username: options.Username,
Password: options.Password,
}
cf := cloudfoundry.CFUtils{Exec: c}
serviceKeyJSON, err = cf.ReadServiceKey(cfconfig)
if err != nil {
// Executing cfReadServiceKeyScript failed
return abapServiceKey, err
}
// parse
json.Unmarshal([]byte(serviceKeyJSON), &abapServiceKey)
if abapServiceKey == (AbapServiceKey{}) {
return abapServiceKey, errors.New("Parsing the service key failed")
}
log.Entry().Info("Service Key read successfully")
return abapServiceKey, nil
}
/****************************************
* Structs for the A4C_A2G_GHA service *
****************************************/
// PullEntity struct for the Pull/Import entity A4C_A2G_GHA_SC_IMP
type PullEntity struct {
Metadata AbapMetadata `json:"__metadata"`
UUID string `json:"uuid"`
Namespace string `json:"namepsace"`
ScName string `json:"sc_name"`
ImportType string `json:"import_type"`
BranchName string `json:"branch_name"`
StartedByUser string `json:"user_name"`
Status string `json:"status"`
StatusDescription string `json:"status_descr"`
CommitID string `json:"commit_id"`
StartTime string `json:"start_time"`
ChangeTime string `json:"change_time"`
ToExecutionLog AbapLogs `json:"to_Execution_log"`
ToTransportLog AbapLogs `json:"to_Transport_log"`
}
// BranchEntity struct for the Branch entity A4C_A2G_GHA_SC_BRANCH
type BranchEntity struct {
Metadata AbapMetadata `json:"__metadata"`
ScName string `json:"sc_name"`
Namespace string `json:"namepsace"`
BranchName string `json:"branch_name"`
ParentBranch string `json:"derived_from"`
CreatedBy string `json:"created_by"`
CreatedOn string `json:"created_on"`
IsActive bool `json:"is_active"`
CommitID string `json:"commit_id"`
CommitMessage string `json:"commit_message"`
LastCommitBy string `json:"last_commit_by"`
LastCommitOn string `json:"last_commit_on"`
}
// AbapLogs struct for ABAP logs
type AbapLogs struct {
Results []LogResults `json:"results"`
}
// LogResults struct for Execution and Transport Log entities A4C_A2G_GHA_SC_LOG_EXE and A4C_A2G_GHA_SC_LOG_TP
type LogResults struct {
Index string `json:"index_no"`
Type string `json:"type"`
Description string `json:"descr"`
Timestamp string `json:"timestamp"`
}
/*******************************
* Structs for specific steps *
*******************************/
// AbapEnvironmentPullGitRepoOptions struct for the PullGitRepo piper step
type AbapEnvironmentPullGitRepoOptions struct {
AbapEnvOptions AbapEnvironmentOptions
RepositoryNames []string `json:"repositoryNames,omitempty"`
}
// AbapEnvironmentRunATCCheckOptions struct for the RunATCCheck piper step
type AbapEnvironmentRunATCCheckOptions struct {
AbapEnvOptions AbapEnvironmentOptions
AtcConfig string `json:"atcConfig,omitempty"`
}
/********************************
* Structs for ABAP in general *
********************************/
//AbapEnvironmentOptions contains cloud foundry fields and the host parameter for connections to ABAP Environment instances
type AbapEnvironmentOptions struct {
Username string `json:"username,omitempty"`
Password string `json:"password,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"`
}
// AbapMetadata contains the URI of metadata files
type AbapMetadata struct {
URI string `json:"uri"`
}
// ConnectionDetailsHTTP contains fields for HTTP connections including the XCSRF token
type ConnectionDetailsHTTP struct {
User string `json:"user"`
Password string `json:"password"`
URL string `json:"url"`
XCsrfToken string `json:"xcsrftoken"`
}
// AbapError contains the error code and the error message for ABAP errors
type AbapError struct {
Code string `json:"code"`
Message AbapErrorMessage `json:"message"`
}
// AbapErrorMessage contains the lanuage and value fields for ABAP errors
type AbapErrorMessage struct {
Lang string `json:"lang"`
Value string `json:"value"`
}
// AbapServiceKey contains information about an ABAP service key
type AbapServiceKey struct {
SapCloudService string `json:"sap.cloud.service"`
URL string `json:"url"`
SystemID string `json:"systemid"`
Abap AbapConnection `json:"abap"`
Binding AbapBinding `json:"binding"`
PreserveHostHeader bool `json:"preserve_host_header"`
}
// AbapConnection contains information about the ABAP connection for the ABAP endpoint
type AbapConnection struct {
Username string `json:"username"`
Password string `json:"password"`
CommunicationScenarioID string `json:"communication_scenario_id"`
CommunicationArrangementID string `json:"communication_arrangement_id"`
CommunicationSystemID string `json:"communication_system_id"`
CommunicationInboundUserID string `json:"communication_inbound_user_id"`
CommunicationInboundUserAuthMode string `json:"communication_inbound_user_auth_mode"`
}
// AbapBinding contains information about service binding in Cloud Foundry
type AbapBinding struct {
ID string `json:"id"`
Type string `json:"type"`
Version string `json:"version"`
Env string `json:"env"`
}

View File

@ -0,0 +1,209 @@
package abaputils
import (
"testing"
"github.com/SAP/jenkins-library/pkg/command"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
func TestCloudFoundryGetAbapCommunicationInfo(t *testing.T) {
t.Run("CF GetAbapCommunicationArrangementInfo - Error - parameters missing", func(t *testing.T) {
//given
options := AbapEnvironmentOptions{
//CfAPIEndpoint: "https://api.endpoint.com",
CfSpace: "testSpace",
CfOrg: "testOrg",
CfServiceInstance: "testInstance",
Username: "testUser",
Password: "testPassword",
CfServiceKeyName: "testServiceKeyName",
}
//when
var connectionDetails ConnectionDetailsHTTP
var err error
connectionDetails, err = GetAbapCommunicationArrangementInfo(options, &command.Command{}, "")
//then
assert.Equal(t, "", connectionDetails.URL)
assert.Equal(t, "", connectionDetails.User)
assert.Equal(t, "", connectionDetails.Password)
assert.Equal(t, "", connectionDetails.XCsrfToken)
assert.EqualError(t, err, "Parameters missing. Please provide EITHER the Host of the ABAP server OR the Cloud Foundry ApiEndpoint, Organization, Space, Service Instance and a corresponding Service Key for the Communication Scenario SAP_COM_0510")
assert.Error(t, err)
})
t.Run("CF GetAbapCommunicationArrangementInfo - Error - reading service Key", func(t *testing.T) {
//given
options := AbapEnvironmentOptions{
CfAPIEndpoint: "https://api.endpoint.com",
CfSpace: "testSpace",
CfOrg: "testOrg",
CfServiceInstance: "testInstance",
Username: "testUser",
Password: "testPassword",
CfServiceKeyName: "testServiceKeyName",
}
//when
var connectionDetails ConnectionDetailsHTTP
var err error
connectionDetails, err = GetAbapCommunicationArrangementInfo(options, &command.Command{}, "")
//then
assert.Equal(t, "", connectionDetails.URL)
assert.Equal(t, "", connectionDetails.User)
assert.Equal(t, "", connectionDetails.Password)
assert.Equal(t, "", connectionDetails.XCsrfToken)
assert.Error(t, err)
})
t.Run("CF GetAbapCommunicationArrangementInfo - Success", func(t *testing.T) {
//given
m := &mock.ExecMockRunner{}
const testURL = "https://testurl.com"
const oDataURL = "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull"
const username = "test_user"
const password = "test_password"
const serviceKey = `
cf comment test \n\n
{"sap.cloud.service":"com.sap.cloud.abap","url": "` + testURL + `" ,"systemid":"H01","abap":{"username":"` + username + `","password":"` + password + `","communication_scenario_id": "SAP_COM_0510","communication_arrangement_id": "SK_I6CBIRFZPPJDKYNATQA32W","communication_system_id": "SK_I6CBIRFZPPJDKYNATQA32W","communication_inbound_user_id": "CC0000000001","communication_inbound_user_auth_mode": "2"},"binding":{"env": "cf","version": "0.0.1.1","type": "basic","id": "i6cBiRfZppJdKynaTqa32W"},"preserve_host_header": true}`
options := AbapEnvironmentOptions{
CfAPIEndpoint: "https://api.endpoint.com",
CfSpace: "testSpace",
CfOrg: "testOrg",
CfServiceInstance: "testInstance",
Username: "testUser",
Password: "testPassword",
CfServiceKeyName: "testServiceKeyName",
}
m.StdoutReturn = map[string]string{"cf service-key testInstance testServiceKeyName": serviceKey}
//when
var connectionDetails ConnectionDetailsHTTP
var err error
connectionDetails, err = GetAbapCommunicationArrangementInfo(options, m, oDataURL)
//then
assert.Equal(t, testURL+oDataURL, connectionDetails.URL)
assert.Equal(t, username, connectionDetails.User)
assert.Equal(t, password, connectionDetails.Password)
assert.Equal(t, "", connectionDetails.XCsrfToken)
assert.NoError(t, err)
})
}
func TestHostGetAbapCommunicationInfo(t *testing.T) {
t.Run("HOST GetAbapCommunicationArrangementInfo - Success", func(t *testing.T) {
//given
m := &mock.ExecMockRunner{}
const testURL = "https://testurl.com"
const oDataURL = "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull"
const username = "test_user"
const password = "test_password"
const serviceKey = `
cf comment test \n\n
{"sap.cloud.service":"com.sap.cloud.abap","url": "` + testURL + `" ,"systemid":"XYZ","abap":{"username":"` + username + `","password":"` + password + `","communication_scenario_id": "SAP_COM_XYZ","communication_arrangement_id": "SK_testing","communication_system_id": "SK_testing","communication_inbound_user_id": "CC0000000000","communication_inbound_user_auth_mode": "2"},"binding":{"env": "cf","version": "0.0.1.1","type": "basic","id": "i6cBiRfZppJdtestKynaTqa32W"},"preserve_host_header": true}`
options := AbapEnvironmentOptions{
Host: testURL,
Username: username,
Password: password,
}
m.StdoutReturn = map[string]string{"cf service-key testInstance testServiceKeyName": serviceKey}
//when
var connectionDetails ConnectionDetailsHTTP
var err error
connectionDetails, err = GetAbapCommunicationArrangementInfo(options, m, oDataURL)
//then
assert.Equal(t, testURL+oDataURL, connectionDetails.URL)
assert.Equal(t, username, connectionDetails.User)
assert.Equal(t, password, connectionDetails.Password)
assert.Equal(t, "", connectionDetails.XCsrfToken)
assert.NoError(t, err)
})
t.Run("HOST GetAbapCommunicationArrangementInfo - Success - w/o https", func(t *testing.T) {
//given
m := &mock.ExecMockRunner{}
const testURL = "testurl.com"
const oDataURL = "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull"
const username = "test_user"
const password = "test_password"
const serviceKey = `
cf comment test \n\n
{"sap.cloud.service":"com.sap.cloud.abap","url": "` + testURL + `" ,"systemid":"H01","abap":{"username":"` + username + `","password":"` + password + `","communication_scenario_id": "SAP_COM_0510","communication_arrangement_id": "SK_I6CBIRFZPPJDKYNATQA32W","communication_system_id": "SK_I6CBIRFZPPJDKYNATQA32W","communication_inbound_user_id": "CC0000000001","communication_inbound_user_auth_mode": "2"},"binding":{"env": "cf","version": "0.0.1.1","type": "basic","id": "i6cBiRfZppJdKynaTqa32W"},"preserve_host_header": true}`
options := AbapEnvironmentOptions{
Host: testURL,
Username: username,
Password: password,
}
m.StdoutReturn = map[string]string{"cf service-key testInstance testServiceKeyName": serviceKey}
//when
var connectionDetails ConnectionDetailsHTTP
var err error
connectionDetails, err = GetAbapCommunicationArrangementInfo(options, m, oDataURL)
//then
assert.Equal(t, "https://"+testURL+oDataURL, connectionDetails.URL)
assert.Equal(t, username, connectionDetails.User)
assert.Equal(t, password, connectionDetails.Password)
assert.Equal(t, "", connectionDetails.XCsrfToken)
assert.NoError(t, err)
})
}
func TestReadServiceKeyAbapEnvironment(t *testing.T) {
t.Run("CF ReadServiceKeyAbapEnvironment - Failed to login to Cloud Foundry", func(t *testing.T) {
//given .
options := AbapEnvironmentOptions{
Username: "testUser",
Password: "testPassword",
CfAPIEndpoint: "https://api.endpoint.com",
CfSpace: "testSpace",
CfOrg: "testOrg",
CfServiceInstance: "testInstance",
CfServiceKeyName: "testKey",
}
//when
var abapKey AbapServiceKey
var err error
abapKey, err = ReadServiceKeyAbapEnvironment(options, &command.Command{})
//then
assert.Equal(t, "", abapKey.Abap.Password)
assert.Equal(t, "", abapKey.Abap.Username)
assert.Equal(t, "", abapKey.Abap.CommunicationArrangementID)
assert.Equal(t, "", abapKey.Abap.CommunicationScenarioID)
assert.Equal(t, "", abapKey.Abap.CommunicationSystemID)
assert.Equal(t, "", abapKey.Binding.Env)
assert.Equal(t, "", abapKey.Binding.Type)
assert.Equal(t, "", abapKey.Binding.ID)
assert.Equal(t, "", abapKey.Binding.Version)
assert.Equal(t, "", abapKey.SystemID)
assert.Equal(t, "", abapKey.URL)
assert.Error(t, err)
})
}

View File

@ -2,10 +2,10 @@ package cloudfoundry
import (
"fmt"
"github.com/SAP/jenkins-library/pkg/command"
"testing"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
"testing"
)
func loginMockCleanup(m *mock.ExecMockRunner) {
@ -171,30 +171,55 @@ func TestCloudFoundryLogout(t *testing.T) {
}
func TestCloudFoundryReadServiceKeyAbapEnvironment(t *testing.T) {
t.Run("CF ReadServiceKeyAbapEnvironment", func(t *testing.T) {
t.Run("CF ReadServiceKey", func(t *testing.T) {
//given
m := &mock.ExecMockRunner{}
defer loginMockCleanup(m)
const testURL = "testurl.com"
const oDataURL = "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/Pull"
const username = "test_user"
const password = "test_password"
const serviceKey = `
cf comment test \n\n
{"sap.cloud.service":"com.sap.cloud.abap","url": "` + testURL + `" ,"systemid":"H01","abap":{"username":"` + username + `","password":"` + password + `","communication_scenario_id": "SAP_COM_0510","communication_arrangement_id": "SK_I6CBIRFZPPJDKYNATQA32W","communication_system_id": "SK_I6CBIRFZPPJDKYNATQA32W","communication_inbound_user_id": "CC0000000001","communication_inbound_user_auth_mode": "2"},"binding":{"env": "cf","version": "0.0.1.1","type": "basic","id": "i6cBiRfZppJdKynaTqa32W"},"preserve_host_header": true}`
m.StdoutReturn = map[string]string{"cf service-key testInstance testServiceKeyName": serviceKey}
cfconfig := ServiceKeyOptions{
CfAPIEndpoint: "https://api.endpoint.com",
CfSpace: "testSpace",
CfOrg: "testOrg",
CfServiceInstance: "testInstance",
CfServiceKey: "testKey",
CfServiceKeyName: "testServiceKeyName",
Username: "testUser",
Password: "testPassword",
}
var abapKey ServiceKey
cf := CFUtils{Exec: &command.Command{}}
abapKey, err := cf.ReadServiceKeyAbapEnvironment(cfconfig, true)
assert.Equal(t, "", abapKey.Abap.Password)
assert.Equal(t, "", abapKey.Abap.Username)
assert.Equal(t, "", abapKey.Abap.CommunicationArrangementID)
assert.Equal(t, "", abapKey.Abap.CommunicationScenarioID)
assert.Equal(t, "", abapKey.Abap.CommunicationSystemID)
assert.Equal(t, "", abapKey.Binding.Env)
assert.Equal(t, "", abapKey.Binding.Type)
assert.Equal(t, "", abapKey.Binding.ID)
assert.Equal(t, "", abapKey.Binding.Version)
assert.Equal(t, "", abapKey.Systemid)
assert.Equal(t, "", abapKey.URL)
assert.Error(t, err)
//when
var err error
var abapServiceKey string
cf := CFUtils{Exec: m}
abapServiceKey, err = cf.ReadServiceKey(cfconfig)
//then
if assert.NoError(t, err) {
assert.Equal(t, []mock.ExecCall{
mock.ExecCall{Exec: "cf", Params: []string{
"login",
"-a", "https://api.endpoint.com",
"-o", "testOrg",
"-s", "testSpace",
"-u", "testUser",
"-p", "testPassword",
}},
mock.ExecCall{Exec: "cf", Params: []string{"service-key", "testInstance", "testServiceKeyName"}},
mock.ExecCall{Exec: "cf", Params: []string{"logout"}},
}, m.Calls)
}
assert.Equal(t, ` {"sap.cloud.service":"com.sap.cloud.abap","url": "`+testURL+`" ,"systemid":"H01","abap":{"username":"`+username+`","password":"`+password+`","communication_scenario_id": "SAP_COM_0510","communication_arrangement_id": "SK_I6CBIRFZPPJDKYNATQA32W","communication_system_id": "SK_I6CBIRFZPPJDKYNATQA32W","communication_inbound_user_id": "CC0000000001","communication_inbound_user_auth_mode": "2"},"binding":{"env": "cf","version": "0.0.1.1","type": "basic","id": "i6cBiRfZppJdKynaTqa32W"},"preserve_host_header": true}`, abapServiceKey)
})
}

View File

@ -2,82 +2,60 @@ package cloudfoundry
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/SAP/jenkins-library/pkg/command"
"github.com/SAP/jenkins-library/pkg/log"
"strings"
)
//ReadServiceKeyAbapEnvironment from Cloud Foundry and returns it.
//Depending on user/developer requirements if he wants to perform further Cloud Foundry actions the cfLogoutOption parameters gives the option to logout after reading ABAP communication arrangement or not.
func (cf *CFUtils) ReadServiceKeyAbapEnvironment(options ServiceKeyOptions, cfLogoutOption bool) (ServiceKey, error) {
// ReadServiceKey reads a cloud foundry service key based on provided service instance and service key name parameters
func (cf *CFUtils) ReadServiceKey(options ServiceKeyOptions) (string, error) {
_c := cf.Exec
if _c == nil {
_c = &command.Command{}
}
var abapServiceKey ServiceKey
var err error
//Logging into Cloud Foundry
config := LoginOptions{
cfconfig := LoginOptions{
CfAPIEndpoint: options.CfAPIEndpoint,
CfOrg: options.CfOrg,
CfSpace: options.CfSpace,
Username: options.Username,
Password: options.Password,
}
err := cf.Login(cfconfig)
err = cf.Login(config)
if err != nil {
// error while trying to run cf login
return "", fmt.Errorf("Login to Cloud Foundry failed: %w", err)
}
var serviceKeyBytes bytes.Buffer
_c.Stdout(&serviceKeyBytes)
if err == nil {
//Reading Service Key
log.Entry().WithField("cfServiceInstance", options.CfServiceInstance).WithField("cfServiceKey", options.CfServiceKey).Info("Read service key for service instance")
cfReadServiceKeyScript := []string{"service-key", options.CfServiceInstance, options.CfServiceKey}
// we are logged in --> read service key
log.Entry().WithField("cfServiceInstance", options.CfServiceInstance).WithField("cfServiceKey", options.CfServiceKeyName).Info("Read service key for service instance")
cfReadServiceKeyScript := []string{"service-key", options.CfServiceInstance, options.CfServiceKeyName}
err = _c.RunExecutable("cf", cfReadServiceKeyScript...)
err = _c.RunExecutable("cf", cfReadServiceKeyScript...)
}
if err == nil {
var serviceKeyJSON string
if len(serviceKeyBytes.String()) > 0 {
var lines []string = strings.Split(serviceKeyBytes.String(), "\n")
serviceKeyJSON = strings.Join(lines[2:], "")
}
json.Unmarshal([]byte(serviceKeyJSON), &abapServiceKey)
if abapServiceKey == (ServiceKey{}) {
return abapServiceKey, errors.New("Parsing the service key failed")
}
log.Entry().Info("Service Key read successfully")
}
if err != nil {
if cfLogoutOption == true {
var logoutErr error
logoutErr = cf.Logout()
if logoutErr != nil {
return abapServiceKey, fmt.Errorf("Failed to Logout of Cloud Foundry: %w", err)
}
}
return abapServiceKey, fmt.Errorf("Reading Service Key failed: %w", err)
// error while reading service key
return "", fmt.Errorf("Reading service key failed: %w", err)
}
//Logging out of CF
if cfLogoutOption == true {
var logoutErr error
logoutErr = cf.Logout()
if logoutErr != nil {
return abapServiceKey, fmt.Errorf("Failed to Logout of Cloud Foundry: %w", err)
}
// parse and return service key
var serviceKeyJSON string
if len(serviceKeyBytes.String()) > 0 {
var lines []string = strings.Split(serviceKeyBytes.String(), "\n")
serviceKeyJSON = strings.Join(lines[2:], "")
}
return abapServiceKey, nil
err = cf.Logout()
if err != nil {
return serviceKeyJSON, fmt.Errorf("Logout of Cloud Foundry failed: %w", err)
}
return serviceKeyJSON, err
}
//ServiceKeyOptions for reading CF Service Key
@ -86,32 +64,7 @@ type ServiceKeyOptions struct {
CfOrg string
CfSpace string
CfServiceInstance string
CfServiceKey string
CfServiceKeyName string
Username string
Password string
}
//ServiceKey struct to parse CF Service Key
type ServiceKey struct {
Abap AbapConnection `json:"abap"`
Binding AbapBinding `json:"binding"`
Systemid string `json:"systemid"`
URL string `json:"url"`
}
//AbapConnection contains information about the ABAP connection for the ABAP endpoint
type AbapConnection struct {
CommunicationArrangementID string `json:"communication_arrangement_id"`
CommunicationScenarioID string `json:"communication_scenario_id"`
CommunicationSystemID string `json:"communication_system_id"`
Password string `json:"password"`
Username string `json:"username"`
}
//AbapBinding contains information about service binding in Cloud Foundry
type AbapBinding struct {
Env string `json:"env"`
ID string `json:"id"`
Type string `json:"type"`
Version string `json:"version"`
}