diff --git a/cmd/abapEnvironmentPullGitRepo.go b/cmd/abapEnvironmentPullGitRepo.go index c0013cc29..6c08d4fe4 100644 --- a/cmd/abapEnvironmentPullGitRepo.go +++ b/cmd/abapEnvironmentPullGitRepo.go @@ -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"` -} diff --git a/cmd/abapEnvironmentPullGitRepo_test.go b/cmd/abapEnvironmentPullGitRepo_test.go index 6cc51d72d..00f725c43 100644 --- a/cmd/abapEnvironmentPullGitRepo_test.go +++ b/cmd/abapEnvironmentPullGitRepo_test.go @@ -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") }) diff --git a/cmd/abapEnvironmentRunATCCheck.go b/cmd/abapEnvironmentRunATCCheck.go index 79cd4ea0b..2ab0a9b24 100644 --- a/cmd/abapEnvironmentRunATCCheck.go +++ b/cmd/abapEnvironmentRunATCCheck.go @@ -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") diff --git a/cmd/abapEnvironmentRunATCCheck_test.go b/cmd/abapEnvironmentRunATCCheck_test.go index 70d4a6c65..07b5607b7 100644 --- a/cmd/abapEnvironmentRunATCCheck_test.go +++ b/cmd/abapEnvironmentRunATCCheck_test.go @@ -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/", diff --git a/pkg/abaputils/abaputils.go b/pkg/abaputils/abaputils.go new file mode 100644 index 000000000..bfec8cd34 --- /dev/null +++ b/pkg/abaputils/abaputils.go @@ -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"` +} diff --git a/pkg/abaputils/abaputils_test.go b/pkg/abaputils/abaputils_test.go new file mode 100644 index 000000000..d70ce9c05 --- /dev/null +++ b/pkg/abaputils/abaputils_test.go @@ -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) + }) +} diff --git a/pkg/cloudfoundry/CloudFoundry_test.go b/pkg/cloudfoundry/CloudFoundry_test.go index 486f5a86c..126e7d4a5 100644 --- a/pkg/cloudfoundry/CloudFoundry_test.go +++ b/pkg/cloudfoundry/CloudFoundry_test.go @@ -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) }) } diff --git a/pkg/cloudfoundry/Services.go b/pkg/cloudfoundry/Services.go index c15037bc8..c229070ae 100644 --- a/pkg/cloudfoundry/Services.go +++ b/pkg/cloudfoundry/Services.go @@ -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"` -}