mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-12 10:55:20 +02:00
0fd9c4be84
* minor changes * Changed yaml with aliases * minor changes * minor changes * minor changes * minor changes * minor changes * minor changes * minor changes * minor changes * minor changes * minor changes * Changed yaml aliases * Adapted naming conventions * Removed error code at the end * Adapted configuration * Minor changes * Minor changes * Minor changes * Removed spaces * Removed docker-related config from groovy file * Minor changes * Minor changes * Removed container config * Corrected testing function name * Deleted unnecessary parts * Changed service deletion message * Changed service deletion message * Logging out before throwing error service deletion step fails * Minor changes * Minor changes * Minor changes * Delete .DS_Store * Delete .DS_Store * Delete .DS_Store * Delete .DS_Store * Minor changes * Minor changes * Minor changes * Added newline at end of file * Added newline at end of file * Changes for Pull request optimization * added documentaion * Adapted documentation * Adapted documentation * Adapted documentation * Adapted documentation * Adapted documentation * Added CFDeleteServiceKeys * Added ServiceKey deletion tests * added cfServiceKeys flag explanation to documentation * removed trailing spaces from documentation * resolving conflicts * Changed deletion message an variable naming * Changed tests * Changed tests * Changed tests * Changed tests * Changed CloudFoundryDeleteServiceOptions to options * Changed CloudFoundryDeleteServiceOptions to options * Minor changes * Minor changes * Changed variable naming * Changed error handling * Changed error handling and logging * Changed documentation * Simplified code * Fixed CodeClimate issues * Changed from returning err to nil where no errur returned needed * Add cloudFoundryCreateServiceKey Go Step * Changed Groovy File * Changed aliases * Removed unneccessary parts * Minor changes * Minor changes * Adapted documentation * Adapted tests * Adapted Groovy File * Changed Groovy file * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Removed Groovy Tests for cfCreateServiceKey * Minor changes * Added ATC Check YAML * Added ATC Check generated files * Added test class * Added abapEnvironmentRunATCCheck * Minor changes * Minor changes * Changed groovy * Minor changes * Changed groovy * Changed groovy * Minor changes * Adapted Groovy imports * Adapted Groovy imports * Adapted Groovy imports * Adapted Groovy * Getting ATC results * Changed error message * changed groovy * removed trailing spaces * Added login check * Minor changes * Added step to whitelistScriptReference * Added ATC error message handling * Added groovy file * Added step to groovy tests * corrected metadata file * Debugging * Debugging * Added yaml config parameter for ATC run * Adapted file location of ATC run config to jenkins specific location * Implementing universal pipeline logic for finding yaml config regardless of pipeline * Changed error handling for reading config yaml file * Changed atcrunconfig alias * minor changes * Minor changes * Minor changes * Changed back to dynamic file reading * Minor changes * filepath changes * Removing CF Login * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes * Removed whitespaces * Added CF functions unit tests * Added invalid parameter handling * Removed package and SC flag * Minor changes * Changed tests * Changed tests * Changed tests * Minor changes * Changed tests * removed unnecessary logout * Added documentation * Changed docu * Changed docu * Changed docu * Changed docu * Changed docu * Changed docu * Changed docu * Changed docu * Changed docu * Changed docu * Removed trailing spaces * Added newline at end of file * code climate fixes * code climate fixes * code climate fixes * Minor changes * Minor changes * Minor changes * Changed tests * Test changes * Splitted Cloud Foundry functions into two classes * Removed two steps from whtielistScriptReference * removed atcrunConfig alias * issue fixes * Changed docu * Changed docu * Changed docu * Removed trailing spaced from docu * Changed docu * Go generator run * Issue fixes * Remove unnecessary imports Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com> * Update whitelistScript Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com> * Adding piperutils for writing xml file * Persisting ATC Results with piperutils * Set failonMissingReports to true * Refactoring for CodeClimate * Changed result file name * Changed credentials aliases * changing secret name * Removing trailing spaces * Added secret name and alias to docu * Changing to workingDir Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com> Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com>
361 lines
12 KiB
Go
361 lines
12 KiB
Go
package cmd
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/http/cookiejar"
|
|
"path/filepath"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/SAP/jenkins-library/pkg/cloudfoundry"
|
|
"github.com/SAP/jenkins-library/pkg/command"
|
|
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
|
"github.com/SAP/jenkins-library/pkg/log"
|
|
"github.com/SAP/jenkins-library/pkg/piperutils"
|
|
"github.com/SAP/jenkins-library/pkg/telemetry"
|
|
"github.com/ghodss/yaml"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func abapEnvironmentRunATCCheck(config abapEnvironmentRunATCCheckOptions, telemetryData *telemetry.CustomData) {
|
|
|
|
var c = command.Command{}
|
|
|
|
var err error
|
|
|
|
c.Stdout(log.Entry().Writer())
|
|
c.Stderr(log.Entry().Writer())
|
|
|
|
client := piperhttp.Client{}
|
|
cookieJar, _ := cookiejar.New(nil)
|
|
clientOptions := piperhttp.ClientOptions{
|
|
CookieJar: cookieJar,
|
|
}
|
|
client.SetOptions(clientOptions)
|
|
|
|
var details 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)
|
|
}
|
|
var resp *http.Response
|
|
//Fetch Xcrsf-Token
|
|
if err == nil {
|
|
abapEndpoint = details.URL
|
|
credentialsOptions := piperhttp.ClientOptions{
|
|
Username: details.User,
|
|
Password: details.Password,
|
|
CookieJar: cookieJar,
|
|
}
|
|
client.SetOptions(credentialsOptions)
|
|
details.XCsrfToken, err = fetchXcsrfToken("GET", details, nil, &client)
|
|
}
|
|
if err == nil {
|
|
resp, err = triggerATCrun(config, details, &client, abapEndpoint)
|
|
}
|
|
if err == nil {
|
|
err = handleATCresults(resp, details, &client, abapEndpoint)
|
|
}
|
|
if err != nil {
|
|
log.Entry().WithError(err).Fatal("step execution failed")
|
|
}
|
|
|
|
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 {
|
|
var err error
|
|
location := resp.Header.Get("Location")
|
|
details.URL = abapEndpoint + location
|
|
location, err = pollATCRun(details, nil, client)
|
|
if err == nil {
|
|
details.URL = abapEndpoint + location
|
|
resp, err = getResultATCRun("GET", details, nil, client)
|
|
}
|
|
//Parse response
|
|
var body []byte
|
|
if err == nil {
|
|
body, err = ioutil.ReadAll(resp.Body)
|
|
}
|
|
if err == nil {
|
|
defer resp.Body.Close()
|
|
err = parseATCResult(body)
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("Handling ATC result failed: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func triggerATCrun(config abapEnvironmentRunATCCheckOptions, details 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
|
|
if err == nil {
|
|
filename, _ := filepath.Abs(filelocation[0])
|
|
atcConfigyamlFile, err = ioutil.ReadFile(filename)
|
|
}
|
|
var ATCConfig ATCconfig
|
|
if err == nil {
|
|
var result []byte
|
|
result, err = yaml.YAMLToJSON(atcConfigyamlFile)
|
|
json.Unmarshal(result, &ATCConfig)
|
|
}
|
|
var packageString string
|
|
var softwareComponentString string
|
|
if err == nil {
|
|
packageString, softwareComponentString, err = buildATCCheckBody(ATCConfig)
|
|
}
|
|
|
|
//Trigger ATC run
|
|
var resp *http.Response
|
|
var bodyString = `<?xml version="1.0" encoding="UTF-8"?><atc:runparameters xmlns:atc="http://www.sap.com/adt/atc" xmlns:obj="http://www.sap.com/adt/objectset"><obj:objectSet>` + softwareComponentString + packageString + `</obj:objectSet></atc:runparameters>`
|
|
var body = []byte(bodyString)
|
|
if err == nil {
|
|
details.URL = abapEndpoint + "/sap/bc/adt/api/atc/runs?clientWait=false"
|
|
resp, err = runATC("POST", details, body, client)
|
|
}
|
|
if err != nil {
|
|
return resp, fmt.Errorf("Triggering ATC run failed: %w", err)
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func buildATCCheckBody(ATCConfig ATCconfig) (string, string, error) {
|
|
if len(ATCConfig.Objects.Package) == 0 && len(ATCConfig.Objects.SoftwareComponent) == 0 {
|
|
return "", "", fmt.Errorf("Error while parsing ATC run config. Please provide the packages and/or the software components to be checked! %w", errors.New("No Package or Software Component specified. Please provide either one or both of them"))
|
|
}
|
|
|
|
var packageString string
|
|
var softwareComponentString string
|
|
|
|
//Build Package XML body
|
|
if len(ATCConfig.Objects.Package) != 0 {
|
|
packageString += "<obj:packages>"
|
|
for _, s := range ATCConfig.Objects.Package {
|
|
packageString += `<obj:package value="` + s.Name + `" includeSubpackages="` + strconv.FormatBool(s.IncludeSubpackages) + `"/>`
|
|
}
|
|
packageString += "</obj:packages>"
|
|
}
|
|
|
|
//Build SC XML body
|
|
if len(ATCConfig.Objects.SoftwareComponent) != 0 {
|
|
softwareComponentString += "<obj:softwarecomponents>"
|
|
for _, s := range ATCConfig.Objects.SoftwareComponent {
|
|
softwareComponentString += `<obj:softwarecomponent value="` + s.Name + `"/>`
|
|
}
|
|
softwareComponentString += "</obj:softwarecomponents>"
|
|
}
|
|
return packageString, softwareComponentString, nil
|
|
}
|
|
|
|
func parseATCResult(body []byte) error {
|
|
if len(body) == 0 {
|
|
return fmt.Errorf("Parsing ATC result failed: %w", errors.New("Body is empty, can't parse empty body"))
|
|
}
|
|
parsedXML := new(Result)
|
|
xml.Unmarshal([]byte(body), &parsedXML)
|
|
err := ioutil.WriteFile("ATCResults.xml", body, 0644)
|
|
if err == nil {
|
|
var reports []piperutils.Path
|
|
reports = append(reports, piperutils.Path{Target: "ATCResults.xml", Name: "ATC Results", Mandatory: true})
|
|
piperutils.PersistReportsAndLinks("abapEnvironmentRunATCCheck", "", reports, nil)
|
|
for _, s := range parsedXML.Files {
|
|
for _, t := range s.ATCErrors {
|
|
log.Entry().Error("Error in file " + s.Key + ": " + t.Key)
|
|
}
|
|
}
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("Writing results to XML file failed: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func runATC(requestType string, details connectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
|
|
|
|
log.Entry().WithField("ABAP endpoint: ", details.URL).Info("Triggering ATC run")
|
|
|
|
header := make(map[string][]string)
|
|
header["X-Csrf-Token"] = []string{details.XCsrfToken}
|
|
header["Content-Type"] = []string{"application/vnd.sap.atc.run.parameters.v1+xml; charset=utf-8;"}
|
|
|
|
req, err := client.SendRequest(requestType, details.URL, bytes.NewBuffer(body), header, nil)
|
|
if err != nil {
|
|
return req, fmt.Errorf("Triggering ATC run failed: %w", err)
|
|
}
|
|
defer req.Body.Close()
|
|
return req, err
|
|
}
|
|
|
|
func fetchXcsrfToken(requestType string, details connectionDetailsHTTP, body []byte, client piperhttp.Sender) (string, error) {
|
|
|
|
log.Entry().WithField("ABAP Endpoint: ", details.URL).Info("Fetching Xcrsf-Token")
|
|
|
|
details.URL += "/sap/bc/adt/api/atc/runs/00000000000000000000000000000000"
|
|
details.XCsrfToken = "fetch"
|
|
header := make(map[string][]string)
|
|
header["X-Csrf-Token"] = []string{details.XCsrfToken}
|
|
header["Accept"] = []string{"application/vnd.sap.atc.run.v1+xml"}
|
|
req, err := client.SendRequest(requestType, details.URL, bytes.NewBuffer(body), header, nil)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Fetching Xcsrf-Token failed: %w", err)
|
|
}
|
|
defer req.Body.Close()
|
|
token := req.Header.Get("X-Csrf-Token")
|
|
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
|
|
abapServiceKey, err = cloudfoundry.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
|
|
details.URL = config.Host
|
|
return details, err
|
|
}
|
|
|
|
func pollATCRun(details connectionDetailsHTTP, body []byte, client piperhttp.Sender) (string, error) {
|
|
|
|
log.Entry().WithField("ABAP endpoint", details.URL).Info("Polling ATC run status")
|
|
|
|
for {
|
|
resp, err := getHTTPResponseATCRun("GET", details, nil, client)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Getting HTTP response failed: %w", err)
|
|
}
|
|
bodyText, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Reading response body failed: %w", err)
|
|
}
|
|
|
|
x := new(Run)
|
|
xml.Unmarshal(bodyText, &x)
|
|
log.Entry().WithField("StatusCode", resp.StatusCode).Info("Status: " + x.Status)
|
|
|
|
if x.Status == "Not Created" {
|
|
return "", err
|
|
}
|
|
if x.Status == "Completed" {
|
|
return x.Link[0].Key, err
|
|
}
|
|
if x.Status == "" {
|
|
return "", fmt.Errorf("Could not get any response from ATC poll: %w", errors.New("Status from ATC run is empty. Either it's not an ABAP system or ATC run hasn't started"))
|
|
}
|
|
time.Sleep(5 * time.Second)
|
|
}
|
|
}
|
|
|
|
func getHTTPResponseATCRun(requestType string, details connectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
|
|
|
|
log.Entry().WithField("ABAP Endpoint: ", details.URL).Info("Polling ATC run status")
|
|
|
|
header := make(map[string][]string)
|
|
header["Accept"] = []string{"application/vnd.sap.atc.run.v1+xml"}
|
|
|
|
req, err := client.SendRequest(requestType, details.URL, bytes.NewBuffer(body), header, nil)
|
|
if err != nil {
|
|
return req, fmt.Errorf("Getting ATC run status failed: %w", err)
|
|
}
|
|
return req, err
|
|
}
|
|
|
|
func getResultATCRun(requestType string, details connectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
|
|
|
|
log.Entry().WithField("ABAP Endpoint: ", details.URL).Info("Getting ATC results")
|
|
|
|
header := make(map[string][]string)
|
|
header["x-csrf-token"] = []string{details.XCsrfToken}
|
|
header["Accept"] = []string{"application/vnd.sap.atc.checkstyle.v1+xml"}
|
|
|
|
req, err := client.SendRequest(requestType, details.URL, bytes.NewBuffer(body), header, nil)
|
|
if err != nil {
|
|
return req, fmt.Errorf("Getting ATC run results failed: %w", err)
|
|
}
|
|
return req, err
|
|
}
|
|
|
|
//ATCconfig object for parsing yaml config of software components and packages
|
|
type ATCconfig struct {
|
|
Objects ATCObjects `json:"atcobjects"`
|
|
}
|
|
|
|
//ATCObjects in form of packages and software components to be checked
|
|
type ATCObjects struct {
|
|
Package []Package `json:"package"`
|
|
SoftwareComponent []SoftwareComponent `json:"softwarecomponent"`
|
|
}
|
|
|
|
//Package for ATC run to be checked
|
|
type Package struct {
|
|
Name string `json:"name"`
|
|
IncludeSubpackages bool `json:"includesubpackage"`
|
|
}
|
|
|
|
//SoftwareComponent for ATC run to be checked
|
|
type SoftwareComponent struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
//Run Object for parsing XML
|
|
type Run struct {
|
|
XMLName xml.Name `xml:"run"`
|
|
Status string `xml:"status,attr"`
|
|
Link []Link `xml:"link"`
|
|
}
|
|
|
|
//Link of XML object
|
|
type Link struct {
|
|
Key string `xml:"href,attr"`
|
|
Value string `xml:",chardata"`
|
|
}
|
|
|
|
//Result from ATC check for all files that were checked
|
|
type Result struct {
|
|
XMLName xml.Name `xml:"checkstyle"`
|
|
Files []File `xml:"file"`
|
|
}
|
|
|
|
//File that contains ATC check with error for checked file
|
|
type File struct {
|
|
Key string `xml:"name,attr"`
|
|
Value string `xml:",chardata"`
|
|
ATCErrors []ATCError `xml:"error"`
|
|
}
|
|
|
|
//ATCError with message
|
|
type ATCError struct {
|
|
Key string `xml:"message,attr"`
|
|
Value string `xml:",chardata"`
|
|
}
|