From 7a028c4149a86e66fd1db97f9ccd3f3d0244001d Mon Sep 17 00:00:00 2001 From: Christian Luttenberger <42861202+bluesbrother84@users.noreply.github.com> Date: Wed, 30 Sep 2020 16:40:36 +0200 Subject: [PATCH] Refactor build framework steps (#2068) * adding my steps * messy step * Update abapEnvironmentAssembly.go * clean up * change yaml * corrections * Update cloudFoundryDeploy.go * update * delete simulation step * remove simulate * Update PiperGoUtils.groovy * Update PiperGoUtils.groovy * Update CommonStepsTest.groovy * add docu * Update abapEnvironmentAssembly.md * changes due to PR * Update .gitignore * b * CV list * Update abapEnvironmentAssembly.go * testing with simulation * Update abapEnvironmentAssembly.go * remove simulation * renaming * Update mkdocs.yml * moving service key to yaml and fixing code climate * Update abapEnvironmentAssemblePackages.go * Update abapEnvironmentAssemblePackages.go * Update abapEnvironmentAssemblePackages.go * Update abapEnvironmentAssemblePackages.go * change input * Update abapEnvironmentAssemblePackages.go * change json tag * fixed error handling * documentation * Update abapEnvironmentAssemblePackages.md * Update abapEnvironmentAssemblePackages.md * fixing code climate issues * fixing code climate issues * Update abapEnvironmentAssemblePackages.yaml * fixing code climate issues * Update abapEnvironmentAssemblePackages.yaml * adding unittests * adding unittests and improved logging * yaml -> json * change scope of cfServiceKeyName * correct indentation * Update CommonStepsTest.groovy * maintain correct step order * Move Connector to connector.go * Refactor bfw with unit tests * remove spaces * CodeClimate Fix for unexported type * ABAP BF - Adding Error Handling Unmarshal * Revert Unmarshal Co-authored-by: rosemarieB <45030247+rosemarieB@users.noreply.github.com> Co-authored-by: Daniel Mieg <56156797+DanielMieg@users.noreply.github.com> Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com> Co-authored-by: Koerner Co-authored-by: tiloKo <70266685+tiloKo@users.noreply.github.com> --- cmd/abapEnvironmentAssemblePackages.go | 552 ++------------------ cmd/abapEnvironmentAssemblePackages_test.go | 475 +---------------- pkg/abap/build/bfw.go | 375 +++++++++++++ pkg/abap/build/bfw_mock.go | 345 ++++++++++++ pkg/abap/build/bfw_test.go | 115 ++++ pkg/abap/build/connector.go | 59 ++- 6 files changed, 952 insertions(+), 969 deletions(-) create mode 100644 pkg/abap/build/bfw.go create mode 100644 pkg/abap/build/bfw_mock.go create mode 100644 pkg/abap/build/bfw_test.go diff --git a/cmd/abapEnvironmentAssemblePackages.go b/cmd/abapEnvironmentAssemblePackages.go index 04558fc86..3302ae42c 100644 --- a/cmd/abapEnvironmentAssemblePackages.go +++ b/cmd/abapEnvironmentAssemblePackages.go @@ -1,18 +1,12 @@ package cmd import ( - "bytes" + "encoding/json" "path" "path/filepath" - "sort" "time" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/http/cookiejar" - + abapbuild "github.com/SAP/jenkins-library/pkg/abap/build" "github.com/SAP/jenkins-library/pkg/abaputils" "github.com/SAP/jenkins-library/pkg/command" piperhttp "github.com/SAP/jenkins-library/pkg/http" @@ -21,6 +15,11 @@ import ( "github.com/pkg/errors" ) +type buildWithRepository struct { + build abapbuild.Build + repo abaputils.Repository +} + func abapEnvironmentAssemblePackages(config abapEnvironmentAssemblePackagesOptions, telemetryData *telemetry.CustomData, cpe *abapEnvironmentAssemblePackagesCommonPipelineEnvironment) { // for command execution use Command c := command.Command{} @@ -39,22 +38,25 @@ func abapEnvironmentAssemblePackages(config abapEnvironmentAssemblePackagesOptio } } -// ******************************************************************************************************************************* -// ********************************************************** Step logic ********************************************************* -// ******************************************************************************************************************************* func runAbapEnvironmentAssemblePackages(config *abapEnvironmentAssemblePackagesOptions, telemetryData *telemetry.CustomData, com abaputils.Communication, client piperhttp.Sender, cpe *abapEnvironmentAssemblePackagesCommonPipelineEnvironment) error { - conn := new(connector) - err := conn.init(config, com, client) + conn := new(abapbuild.Connector) + var connConfig abapbuild.ConnectorConfiguration + err := conn.InitBuildFramework(connConfig, com, client) if err != nil { return err } var addonDescriptor abaputils.AddonDescriptor - json.Unmarshal([]byte(config.AddonDescriptor), &addonDescriptor) + err = json.Unmarshal([]byte(config.AddonDescriptor), &addonDescriptor) + if err != nil { + return err + } builds, buildsAlreadyReleased, err := starting(addonDescriptor.Repositories, *conn) if err != nil { return err } - err = polling(builds, time.Duration(config.MaxRuntimeInMinutes), 60) + maxRuntimeInMinutes := time.Duration(config.MaxRuntimeInMinutes) * time.Minute + pollIntervalsInSeconds := time.Duration(60 * time.Second) + err = polling(builds, maxRuntimeInMinutes, pollIntervalsInSeconds) if err != nil { return err } @@ -82,14 +84,14 @@ func downloadSARXML(builds []buildWithRepository) ([]abaputils.Repository, error resultName := "SAR_XML" envPath := filepath.Join(GeneralConfig.EnvRootPath, "commonPipelineEnvironment", "abap") for i, b := range builds { - resultSARXML, err := b.build.getResult(resultName) + resultSARXML, err := b.build.GetResult(resultName) if err != nil { return reposBackToCPE, err } sarPackage := resultSARXML.AdditionalInfo downloadPath := filepath.Join(envPath, path.Base(sarPackage)) log.Entry().Infof("Downloading SAR file %s to %s", path.Base(sarPackage), downloadPath) - err = resultSARXML.download(downloadPath) + err = resultSARXML.Download(downloadPath) if err != nil { return reposBackToCPE, err } @@ -101,13 +103,12 @@ func downloadSARXML(builds []buildWithRepository) ([]abaputils.Repository, error func checkIfFailedAndPrintLogs(builds []buildWithRepository) error { var buildFailed bool = false - for _, bR := range builds { - b := bR.build - if b.RunState == failed { - log.Entry().Errorf("Assembly of %s failed", b.BuildID) + for i := range builds { + if builds[i].build.RunState == abapbuild.Failed { + log.Entry().Errorf("Assembly of %s failed", builds[i].repo.PackageName) buildFailed = true } - b.printLogs() + builds[i].build.PrintLogs() } if buildFailed { return errors.New("At least the assembly of one package failed") @@ -115,12 +116,12 @@ func checkIfFailedAndPrintLogs(builds []buildWithRepository) error { return nil } -func starting(repos []abaputils.Repository, conn connector) ([]buildWithRepository, []buildWithRepository, error) { +func starting(repos []abaputils.Repository, conn abapbuild.Connector) ([]buildWithRepository, []buildWithRepository, error) { var builds []buildWithRepository var buildsAlreadyReleased []buildWithRepository for _, repo := range repos { - assemblyBuild := build{ - connector: conn, + assemblyBuild := abapbuild.Build{ + Connector: conn, } buildRepo := buildWithRepository{ build: assemblyBuild, @@ -133,7 +134,7 @@ func starting(repos []abaputils.Repository, conn connector) ([]buildWithReposito } builds = append(builds, buildRepo) } else { - log.Entry().Infof("Packages %s is already released. No need to run the assembly", repo.PackageName) + log.Entry().Infof("Packages %s is in status '%s'. No need to run the assembly", repo.PackageName, repo.Status) buildsAlreadyReleased = append(buildsAlreadyReleased, buildRepo) } } @@ -141,8 +142,8 @@ func starting(repos []abaputils.Repository, conn connector) ([]buildWithReposito } func polling(builds []buildWithRepository, maxRuntimeInMinutes time.Duration, pollIntervalsInSeconds time.Duration) error { - timeout := time.After(maxRuntimeInMinutes * time.Minute) - ticker := time.Tick(pollIntervalsInSeconds * time.Second) + timeout := time.After(maxRuntimeInMinutes) + ticker := time.Tick(pollIntervalsInSeconds) for { select { case <-timeout: @@ -150,12 +151,10 @@ func polling(builds []buildWithRepository, maxRuntimeInMinutes time.Duration, po case <-ticker: var allFinished bool = true for i := range builds { + builds[i].build.Get() if !builds[i].build.IsFinished() { - builds[i].build.get() - if !builds[i].build.IsFinished() { - log.Entry().Infof("Assembly of %s is not yet finished, check again in %02d seconds", builds[i].repo.PackageName, pollIntervalsInSeconds) - allFinished = false - } + log.Entry().Infof("Assembly of %s is not yet finished, check again in %s", builds[i].repo.PackageName, pollIntervalsInSeconds) + allFinished = false } } if allFinished { @@ -169,8 +168,8 @@ func (b *buildWithRepository) start() error { if b.repo.Name == "" || b.repo.Version == "" || b.repo.SpLevel == "" || b.repo.Namespace == "" || b.repo.PackageType == "" || b.repo.PackageName == "" { return errors.New("Parameters missing. Please provide software component name, version, sp-level, namespace, packagetype and packagename") } - valuesInput := values{ - Values: []value{ + valuesInput := abapbuild.Values{ + Values: []abapbuild.Value{ { ValueID: "SWC", Value: b.repo.Name, @@ -191,489 +190,10 @@ func (b *buildWithRepository) start() error { } if b.repo.PredecessorCommitID != "" { valuesInput.Values = append(valuesInput.Values, - value{ValueID: "PREVIOUS_DELIVERY_COMMIT", + abapbuild.Value{ValueID: "PREVIOUS_DELIVERY_COMMIT", Value: b.repo.PredecessorCommitID}) } phase := "BUILD_" + b.repo.PackageType log.Entry().Infof("Starting assembly of package %s", b.repo.PackageName) - return b.build.start(phase, valuesInput) -} - -type buildWithRepository struct { - build build - repo abaputils.Repository -} - -// ******************************************************************************************************************************* -// ************************************************************ REUSE ************************************************************ -// ******************************************************************************************************************************* - -// ********************************************************************* -// ******************************* Funcs ******************************* -// ********************************************************************* - -// ******** technical communication settings ******** - -func (conn *connector) init(config *abapEnvironmentAssemblePackagesOptions, com abaputils.Communication, inputclient piperhttp.Sender) error { - conn.Client = inputclient - conn.Header = make(map[string][]string) - conn.Header["Accept"] = []string{"application/json"} - conn.Header["Content-Type"] = []string{"application/json"} - conn.DownloadClient = &piperhttp.Client{} - conn.DownloadClient.SetOptions(piperhttp.ClientOptions{TransportTimeout: 20 * time.Second}) - // Mapping for options - subOptions := abaputils.AbapEnvironmentOptions{} - subOptions.CfAPIEndpoint = config.CfAPIEndpoint - subOptions.CfServiceInstance = config.CfServiceInstance - subOptions.CfServiceKeyName = config.CfServiceKeyName - subOptions.CfOrg = config.CfOrg - subOptions.CfSpace = config.CfSpace - subOptions.Host = config.Host - subOptions.Password = config.Password - subOptions.Username = config.Username - - // Determine the host, user and password, either via the input parameters or via a cloud foundry service key - connectionDetails, err := com.GetAbapCommunicationArrangementInfo(subOptions, "/sap/opu/odata/BUILD/CORE_SRV") - if err != nil { - return errors.Wrap(err, "Parameters for the ABAP Connection not available") - } - - conn.DownloadClient.SetOptions(piperhttp.ClientOptions{ - Username: connectionDetails.User, - Password: connectionDetails.Password, - }) - cookieJar, _ := cookiejar.New(nil) - conn.Client.SetOptions(piperhttp.ClientOptions{ - Username: connectionDetails.User, - Password: connectionDetails.Password, - CookieJar: cookieJar, - }) - conn.Baseurl = connectionDetails.URL - return nil -} - -// ******** technical communication calls ******** - -func (conn *connector) getToken(appendum string) error { - url := conn.Baseurl + appendum - conn.Header["X-CSRF-Token"] = []string{"Fetch"} - response, err := conn.Client.SendRequest("HEAD", url, nil, conn.Header, nil) - if err != nil { - if response == nil { - return errors.Wrap(err, "Fetching X-CSRF-Token failed") - } - defer response.Body.Close() - errorbody, _ := ioutil.ReadAll(response.Body) - return errors.Wrapf(err, "Fetching X-CSRF-Token failed: %v", string(errorbody)) - - } - defer response.Body.Close() - token := response.Header.Get("X-CSRF-Token") - conn.Header["X-CSRF-Token"] = []string{token} - return nil -} - -func (conn connector) get(appendum string) ([]byte, error) { - url := conn.Baseurl + appendum - response, err := conn.Client.SendRequest("GET", url, nil, conn.Header, nil) - if err != nil { - if response == nil { - return nil, errors.Wrap(err, "Get failed") - } - defer response.Body.Close() - errorbody, _ := ioutil.ReadAll(response.Body) - return errorbody, errors.Wrapf(err, "Get failed: %v", string(errorbody)) - - } - defer response.Body.Close() - body, err := ioutil.ReadAll(response.Body) - return body, err -} - -func (conn connector) post(appendum string, importBody string) ([]byte, error) { - url := conn.Baseurl + appendum - var response *http.Response - var err error - if importBody == "" { - response, err = conn.Client.SendRequest("POST", url, nil, conn.Header, nil) - } else { - response, err = conn.Client.SendRequest("POST", url, bytes.NewBuffer([]byte(importBody)), conn.Header, nil) - } - if err != nil { - if response == nil { - return nil, errors.Wrap(err, "Post failed") - } - defer response.Body.Close() - errorbody, _ := ioutil.ReadAll(response.Body) - return errorbody, errors.Wrapf(err, "Post failed: %v", string(errorbody)) - - } - defer response.Body.Close() - body, err := ioutil.ReadAll(response.Body) - return body, err -} - -func (conn connector) download(appendum string, downloadPath string) error { - url := conn.Baseurl + appendum - err := conn.DownloadClient.DownloadFile(url, downloadPath, nil, nil) - return err -} - -// ******** BUILD logic ******** - -func (b *build) start(phase string, inputValues values) error { - if err := b.getToken(""); err != nil { - return err - } - importBody := inputForPost{ - phase: phase, - values: inputValues, - }.String() - - body, err := b.connector.post("/builds", importBody) - if err != nil { - return err - } - - var jBuild jsonBuild - json.Unmarshal(body, &jBuild) - b.BuildID = jBuild.Build.BuildID - b.RunState = jBuild.Build.RunState - b.ResultState = jBuild.Build.ResultState - b.Phase = jBuild.Build.Phase - b.Entitytype = jBuild.Build.Entitytype - b.Startedby = jBuild.Build.Startedby - b.StartedAt = jBuild.Build.StartedAt - b.FinishedAt = jBuild.Build.FinishedAt - return nil -} - -func (b *build) get() error { - appendum := "/builds('" + b.BuildID + "')" - body, err := b.connector.get(appendum) - if err != nil { - return err - } - var jBuild jsonBuild - json.Unmarshal(body, &jBuild) - b.RunState = jBuild.Build.RunState - b.ResultState = jBuild.Build.ResultState - b.Phase = jBuild.Build.Phase - b.Entitytype = jBuild.Build.Entitytype - b.Startedby = jBuild.Build.Startedby - b.StartedAt = jBuild.Build.StartedAt - b.FinishedAt = jBuild.Build.FinishedAt - return nil -} - -func (b *build) getTasks() error { - if len(b.Tasks) == 0 { - appendum := "/builds('" + b.BuildID + "')/tasks" - body, err := b.connector.get(appendum) - if err != nil { - return err - } - var jTasks jsonTasks - json.Unmarshal(body, &jTasks) - b.Tasks = jTasks.ResultTasks.Tasks - sort.Slice(b.Tasks, func(i, j int) bool { - return b.Tasks[i].TaskID < b.Tasks[j].TaskID - }) - for i := range b.Tasks { - b.Tasks[i].connector = b.connector - } - } - return nil -} - -func (b *build) getValues() error { - if len(b.Values) == 0 { - appendum := "/builds('" + b.BuildID + "')/values" - body, err := b.connector.get(appendum) - if err != nil { - return err - } - var jValues jsonValues - json.Unmarshal(body, &jValues) - b.Values = jValues.ResultValues.Values - for i := range b.Values { - b.Values[i].connector = b.connector - } - } - return nil -} - -func (b *build) getLogs() error { - if err := b.getTasks(); err != nil { - return err - } - for i := range b.Tasks { - if err := b.Tasks[i].getLogs(); err != nil { - return err - } - } - return nil -} - -func (b *build) printLogs() error { - if err := b.getTasks(); err != nil { - return err - } - for i := range b.Tasks { - if err := b.Tasks[i].printLogs(); err != nil { - return err - } - } - return nil -} - -func (b *build) getResults() error { - if err := b.getTasks(); err != nil { - return err - } - for i := range b.Tasks { - if err := b.Tasks[i].getResults(); err != nil { - return err - } - } - return nil -} - -func (t *task) printLogs() error { - if err := t.getLogs(); err != nil { - return err - } - for _, logs := range t.Logs { - logs.print() - } - return nil -} - -func (b *build) getResult(name string) (result, error) { - var Results []result - var returnResult result - if err := b.getResults(); err != nil { - return returnResult, err - } - for _, task := range b.Tasks { - for _, result := range task.Results { - if result.Name == name { - Results = append(Results, result) - } - } - } - switch len(Results) { - case 0: - return returnResult, errors.New("No result named " + name + " was found") - case 1: - return Results[0], nil - default: - return returnResult, errors.New("More than one result with the name " + name + " was found") - } -} - -func (b *build) IsFinished() bool { - if b.RunState == finished || b.RunState == failed { - return true - } - return false -} - -func (t *task) getLogs() error { - if len(t.Logs) == 0 { - appendum := fmt.Sprint("/tasks(build_id='", t.BuildID, "',task_id=", t.TaskID, ")/logs") - body, err := t.connector.get(appendum) - if err != nil { - return err - } - var jLogs jsonLogs - json.Unmarshal(body, &jLogs) - t.Logs = jLogs.ResultLogs.Logs - } - return nil -} - -func (t *task) getResults() error { - if len(t.Results) == 0 { - appendum := fmt.Sprint("/tasks(build_id='", t.BuildID, "',task_id=", t.TaskID, ")/results") - body, err := t.connector.get(appendum) - if err != nil { - return err - } - var jResults jsonResults - json.Unmarshal(body, &jResults) - t.Results = jResults.ResultResults.Results - for i := range t.Results { - t.Results[i].connector = t.connector - } - } - return nil -} - -func (result *result) download(downloadPath string) error { - appendum := fmt.Sprint("/results(build_id='", result.BuildID, "',task_id=", result.TaskID, ",name='", result.Name, "')/$value") - err := result.connector.download(appendum, downloadPath) - return err -} - -func (logging *logStruct) print() { - switch logging.Msgty { - case loginfo: - log.Entry().WithField("Timestamp", logging.Timestamp).Info(logging.Logline) - case logwarning: - log.Entry().WithField("Timestamp", logging.Timestamp).Warn(logging.Logline) - case logerror: - log.Entry().WithField("Timestamp", logging.Timestamp).Error(logging.Logline) - case logaborted: - log.Entry().WithField("Timestamp", logging.Timestamp).Error(logging.Logline) - default: - } -} - -// ******** parsing ******** -func (v value) String() string { - return fmt.Sprintf( - `{ "value_id": "%s", "value": "%s" }`, - v.ValueID, - v.Value) -} - -func (vs values) String() string { - returnString := "" - for _, value := range vs.Values { - returnString = returnString + value.String() + ",\n" - } - returnString = returnString[:len(returnString)-2] //removes last , - return returnString -} - -func (in inputForPost) String() string { - return fmt.Sprintf(`{ "phase": "%s", "values": [%s]}`, in.phase, in.values.String()) -} - -// ********************************************************************* -// ****************************** Structs ****************************** -// ********************************************************************* - -type resultState string -type runState string -type msgty string - -const ( - successful resultState = "SUCCESSFUL" - warning resultState = "WARNING" - erroneous resultState = "ERRONEOUS" - aborted resultState = "ABORTED" - initializing runState = "INITIALIZING" - accepted runState = "ACCEPTED" - running runState = "RUNNING" - finished runState = "FINISHED" - failed runState = "FAILED" - loginfo msgty = "I" - logwarning msgty = "W" - logerror msgty = "E" - logaborted msgty = "A" -) - -type connector struct { - Client piperhttp.Sender - DownloadClient piperhttp.Downloader - Header map[string][]string - Baseurl string -} - -//******** structs needed for json conversion ******** - -type jsonBuild struct { - Build *build `json:"d"` -} - -type jsonTasks struct { - ResultTasks struct { - Tasks []task `json:"results"` - } `json:"d"` -} - -type jsonLogs struct { - ResultLogs struct { - Logs []logStruct `json:"results"` - } `json:"d"` -} - -type jsonResults struct { - ResultResults struct { - Results []result `json:"results"` - } `json:"d"` -} - -type jsonValues struct { - ResultValues struct { - Values []value `json:"results"` - } `json:"d"` -} - -// ******** resembling data model in backend ******** - -type build struct { - connector - BuildID string `json:"build_id"` - RunState runState `json:"run_state"` - ResultState resultState `json:"result_state"` - Phase string `json:"phase"` - Entitytype string `json:"entitytype"` - Startedby string `json:"startedby"` - StartedAt string `json:"started_at"` - FinishedAt string `json:"finished_at"` - Tasks []task - Values []value -} - -type task struct { - connector - BuildID string `json:"build_id"` - TaskID int `json:"task_id"` - LogID string `json:"log_id"` - PluginClass string `json:"plugin_class"` - StartedAt string `json:"started_at"` - FinishedAt string `json:"finished_at"` - ResultState resultState `json:"result_state"` - Logs []logStruct - Results []result -} - -type logStruct struct { - BuildID string `json:"build_id"` - TaskID int `json:"task_id"` - LogID string `json:"log_id"` - Msgty msgty `json:"msgty"` - Detlevel string `json:"detlevel"` - Logline string `json:"log_line"` - Timestamp string `json:"TIME_STMP"` -} - -type result struct { - connector - BuildID string `json:"build_id"` - TaskID int `json:"task_id"` - Name string `json:"name"` - AdditionalInfo string `json:"additional_info"` - Mimetype string `json:"mimetype"` -} - -type value struct { - connector - BuildID string `json:"build_id"` - ValueID string `json:"value_id"` - Value string `json:"value"` -} - -// ******** import structure to post call ******** - -type inputForPost struct { - phase string - values values -} - -type values struct { - Values []value `json:"results"` + return b.build.Start(phase, valuesInput) } diff --git a/cmd/abapEnvironmentAssemblePackages_test.go b/cmd/abapEnvironmentAssemblePackages_test.go index 761f65084..1ac33cbe8 100644 --- a/cmd/abapEnvironmentAssemblePackages_test.go +++ b/cmd/abapEnvironmentAssemblePackages_test.go @@ -1,27 +1,23 @@ package cmd import ( - "bytes" - "io" - "net/http" "path/filepath" - "strings" "testing" + "time" + abapbuild "github.com/SAP/jenkins-library/pkg/abap/build" "github.com/SAP/jenkins-library/pkg/abaputils" piperhttp "github.com/SAP/jenkins-library/pkg/http" "github.com/stretchr/testify/assert" - - "io/ioutil" ) -func testSetup(client piperhttp.Sender, buildID string) build { - conn := new(connector) +func testSetup(client piperhttp.Sender, buildID string) abapbuild.Build { + conn := new(abapbuild.Connector) conn.Client = client - conn.DownloadClient = &downloadClientMock{} + conn.DownloadClient = &abapbuild.DownloadClientMock{} conn.Header = make(map[string][]string) - b := build{ - connector: *conn, + b := abapbuild.Build{ + Connector: *conn, BuildID: buildID, } return b @@ -30,8 +26,8 @@ func testSetup(client piperhttp.Sender, buildID string) build { func TestCheckIfFailedAndPrintLogsWithError(t *testing.T) { t.Run("checkIfFailedAndPrintLogs with failed build", func(t *testing.T) { var repo abaputils.Repository - b := testSetup(&clMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") - b.RunState = failed + b := testSetup(&abapbuild.ClMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") + b.RunState = abapbuild.Failed var buildsWithRepo []buildWithRepository bWR := buildWithRepository{ build: b, @@ -46,8 +42,8 @@ func TestCheckIfFailedAndPrintLogsWithError(t *testing.T) { func TestCheckIfFailedAndPrintLogs(t *testing.T) { t.Run("checkIfFailedAndPrintLogs", func(t *testing.T) { var repo abaputils.Repository - b := testSetup(&clMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") - b.RunState = finished + b := testSetup(&abapbuild.ClMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") + b.RunState = abapbuild.Finished var buildsWithRepo []buildWithRepository bWR := buildWithRepository{ build: b, @@ -61,10 +57,10 @@ func TestCheckIfFailedAndPrintLogs(t *testing.T) { func TestStarting(t *testing.T) { t.Run("Run starting", func(t *testing.T) { - client := &clMock{ + client := &abapbuild.ClMock{ Token: "MyToken", } - conn := new(connector) + conn := new(abapbuild.Connector) conn.Client = client conn.Header = make(map[string][]string) var repos []abaputils.Repository @@ -86,17 +82,17 @@ func TestStarting(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 1, len(builds)) assert.Equal(t, 1, len(buildsAlreadyReleased)) - assert.Equal(t, accepted, builds[0].build.RunState) - assert.Equal(t, runState(""), buildsAlreadyReleased[0].build.RunState) + assert.Equal(t, abapbuild.Accepted, builds[0].build.RunState) + assert.Equal(t, abapbuild.RunState(""), buildsAlreadyReleased[0].build.RunState) }) } func TestStartingInvalidInput(t *testing.T) { t.Run("Run starting", func(t *testing.T) { - client := &clMock{ + client := &abapbuild.ClMock{ Token: "MyToken", } - conn := new(connector) + conn := new(abapbuild.Connector) conn.Client = client conn.Header = make(map[string][]string) var repos []abaputils.Repository @@ -113,23 +109,25 @@ func TestStartingInvalidInput(t *testing.T) { func TestPolling(t *testing.T) { t.Run("Run polling", func(t *testing.T) { var repo abaputils.Repository - b := testSetup(&clMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") + b := testSetup(&abapbuild.ClMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") var buildsWithRepo []buildWithRepository bWR := buildWithRepository{ build: b, repo: repo, } buildsWithRepo = append(buildsWithRepo, bWR) - err := polling(buildsWithRepo, 600, 1) + timeout := time.Duration(600 * time.Second) + pollInterval := time.Duration(1 * time.Second) + err := polling(buildsWithRepo, timeout, pollInterval) assert.NoError(t, err) - assert.Equal(t, finished, buildsWithRepo[0].build.RunState) + assert.Equal(t, abapbuild.Finished, buildsWithRepo[0].build.RunState) }) } func TestDownloadSARXML(t *testing.T) { t.Run("Run downloadSARXML", func(t *testing.T) { var repo abaputils.Repository - b := testSetup(&clMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") + b := testSetup(&abapbuild.ClMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") var buildsWithRepo []buildWithRepository bWR := buildWithRepository{ build: b, @@ -142,430 +140,3 @@ func TestDownloadSARXML(t *testing.T) { assert.Equal(t, downloadPath, repos[0].SarXMLFilePath) }) } - -// ******************************************************************************************************************************* -// ************************************************* Tests for REUSE Part ******************************************************** -// ******************************************************************************************************************************* - -func TestSTart(t *testing.T) { - t.Run("Run start", func(t *testing.T) { - client := &clMock{ - Token: "MyToken", - } - b := testSetup(client, "") - inputValues := values{ - Values: []value{ - { - ValueID: "PACKAGES", - Value: "/BUILD/CORE", - }, - { - ValueID: "season", - Value: "winter", - }, - }, - } - err := b.start("test", inputValues) - assert.NoError(t, err) - assert.Equal(t, accepted, b.RunState) - }) -} - -func TestGet(t *testing.T) { - t.Run("Run Get", func(t *testing.T) { - b := testSetup(&clMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") - err := b.get() - assert.NoError(t, err) - assert.Equal(t, finished, b.RunState) - assert.Equal(t, 0, len(b.Tasks)) - }) -} - -func TestGetTasks(t *testing.T) { - t.Run("Run getTasks", func(t *testing.T) { - b := testSetup(&clMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") - assert.Equal(t, 0, len(b.Tasks)) - err := b.getTasks() - assert.NoError(t, err) - assert.Equal(t, b.Tasks[0].TaskID, 0) - assert.Equal(t, b.Tasks[0].PluginClass, "") - assert.Equal(t, b.Tasks[1].TaskID, 1) - assert.Equal(t, b.Tasks[1].PluginClass, "/BUILD/CL_TEST_PLUGIN_OK") - }) -} - -func TestGetLogs(t *testing.T) { - t.Run("Run getLogs", func(t *testing.T) { - b := testSetup(&clMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") - err := b.getLogs() - assert.NoError(t, err) - assert.Equal(t, "I:/BUILD/LOG:000 ABAP Build Framework", b.Tasks[0].Logs[0].Logline) - assert.Equal(t, loginfo, b.Tasks[0].Logs[0].Msgty) - assert.Equal(t, "W:/BUILD/LOG:000 We can even have warnings!", b.Tasks[1].Logs[1].Logline) - assert.Equal(t, logwarning, b.Tasks[1].Logs[1].Msgty) - }) -} - -func TestGetValues(t *testing.T) { - t.Run("Run getValues", func(t *testing.T) { - b := testSetup(&clMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") - assert.Equal(t, 0, len(b.Values)) - err := b.getValues() - assert.NoError(t, err) - assert.Equal(t, 4, len(b.Values)) - assert.Equal(t, "PHASE", b.Values[0].ValueID) - assert.Equal(t, "test1", b.Values[0].Value) - assert.Equal(t, "PACKAGES", b.Values[1].ValueID) - assert.Equal(t, "/BUILD/CORE", b.Values[1].Value) - assert.Equal(t, "season", b.Values[2].ValueID) - assert.Equal(t, "winter", b.Values[2].Value) - assert.Equal(t, "SUN", b.Values[3].ValueID) - assert.Equal(t, "FLOWER", b.Values[3].Value) - }) -} - -func TestGetResults(t *testing.T) { - t.Run("Run getResults", func(t *testing.T) { - b := testSetup(&clMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") - err := b.getResults() - assert.NoError(t, err) - assert.Equal(t, 0, len(b.Tasks[0].Results)) - assert.Equal(t, 2, len(b.Tasks[1].Results)) - assert.Equal(t, "image/jpeg", b.Tasks[1].Results[0].Mimetype) - assert.Equal(t, "application/octet-stream", b.Tasks[1].Results[1].Mimetype) - - _, err = b.getResult("does_not_exist") - assert.Error(t, err) - r, err := b.getResult("SAR_XML") - assert.Equal(t, "application/octet-stream", r.Mimetype) - assert.NoError(t, err) - }) -} - -type downloadClientMock struct{} - -func (dc *downloadClientMock) DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error { - return nil -} - -func (dc *downloadClientMock) SetOptions(opts piperhttp.ClientOptions) {} - -type clMock struct { - Token string - StatusCode int - Error error -} - -func (c *clMock) SetOptions(opts piperhttp.ClientOptions) {} - -func (c *clMock) SendRequest(method string, url string, bdy io.Reader, hdr http.Header, cookies []*http.Cookie) (*http.Response, error) { - if method == "GET" || method == "POST" { - var body []byte - body = []byte(fakeResponse(method, url)) - return &http.Response{ - StatusCode: c.StatusCode, - Body: ioutil.NopCloser(bytes.NewReader(body)), - }, c.Error - } else if method == "HEAD" { - var body []byte - header := http.Header{} - header.Set("X-Csrf-Token", c.Token) - body = []byte("") - return &http.Response{ - StatusCode: c.StatusCode, - Header: header, - Body: ioutil.NopCloser(bytes.NewReader(body)), - }, c.Error - } else { - return nil, c.Error - } -} - -func fakeResponse(method string, url string) string { - if method == "POST" { - return responsePOST - } - if strings.HasSuffix(url, "/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')") { - return responseGetBuild - } else if strings.HasSuffix(url, "/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')/tasks") { - return responseGetTasks - } else if strings.HasSuffix(url, "/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)/logs") { - return ResponseGetLog0 - } else if strings.HasSuffix(url, "/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)/logs") { - return ResponseGetLog1 - } else if strings.HasSuffix(url, "/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')/values") { - return responseGetValues - } else if strings.HasSuffix(url, "tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)/results") { - return responseGetResults0 - } else if strings.HasSuffix(url, "tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)/results") { - return responseGetResults1 - } - return "" -} - -var responseGetBuild = `{ - "d": { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_BUILDSType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "run_state": "FINISHED", - "result_state": "SUCCESSFUL", - "phase": "test1", - "entitytype": "P", - "startedby": "BENTELER", - "started_at": "/Date(1591718108103+0000)/", - "finished_at": "/Date(1591718129432+0000)/", - "tasks": { - "__deferred": { - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')/tasks" - } - }, - "values": { - "__deferred": { - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')/values" - } - } - } -}` - -var responsePOST = `{ - "d": { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPNVMOUQL2LHUFAUA')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPNVMOUQL2LHUFAUA')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_BUILDSType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "run_state": "ACCEPTED", - "result_state": "", - "phase": "test1", - "entitytype": "", - "startedby": "BENTELER", - "started_at": null, - "finished_at": null, - "tasks": { - "__deferred": { - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPNVMOUQL2LHUFAUA')/tasks" - } - }, - "values": { - "results": [] - } - } -}` - -var responseGetTasks = `{ - "d": { - "results": [ - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_TASKSType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "task_id": 1, - "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_1", - "plugin_class": "/BUILD/CL_TEST_PLUGIN_OK", - "started_at": "/Date(1591718128730+0000)/", - "finished_at": "/Date(1591718129369+0000)/", - "result_state": "SUCCESSFUL", - "logs": { - "__deferred": { - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)/logs" - } - }, - "results": { - "__deferred": { - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)/results" - } - } - }, - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_TASKSType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "task_id": 0, - "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_0", - "plugin_class": "", - "started_at": "/Date(1591718128728+0000)/", - "finished_at": "/Date(1591718129462+0000)/", - "result_state": "SUCCESSFUL", - "logs": { - "__deferred": { - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)/logs" - } - }, - "results": { - "__deferred": { - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)/results" - } - } - } - ] - } -}` - -var ResponseGetLog0 = `{ - "d": { - "results": [ - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_0')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_0')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_LOGSType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "task_id": 0, - "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_0", - "msgty": "I", - "detlevel": "3", - "log_line": "I:/BUILD/LOG:000 ABAP Build Framework", - "TIME_STMP": "20200721133523" - }, - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_0')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_0')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_LOGSType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "task_id": 0, - "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_0", - "msgty": "I", - "detlevel": "3", - "log_line": "I:/BUILD/LOG:000 ... Build Execution finished SUCCESSFUL", - "TIME_STMP": "20200721133528" - } - ] - } -}` - -var ResponseGetLog1 = `{ - "d": { - "results": [ - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_1')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_1')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_LOGSType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "task_id": 1, - "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_1", - "msgty": "I", - "detlevel": "1", - "log_line": "I:/BUILD/LOG:000 Hello Packages [1]: , /BUILD/CORE, here is your lovely test_ok plugin!", - "TIME_STMP": "20200721133528" - }, - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_1')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_1')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_LOGSType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "task_id": 1, - "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_1", - "msgty": "W", - "detlevel": "3", - "log_line": "W:/BUILD/LOG:000 We can even have warnings!", - "TIME_STMP": "20200721133528" - } - ] - } -}` - -var responseGetResults0 = `{ - "d": { - "results": [] - } -}` - -var responseGetResults1 = `{ - "d": { - "results": [ - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='HT-6111.JPG')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='HT-6111.JPG')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_RESULTSType", - "content_type": "image/jpeg", - "media_src": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='HT-6111.JPG')/$value" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "task_id": 1, - "name": "HT-6111.JPG", - "additional_info": "", - "mimetype": "image/jpeg" - }, - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='2times_hello')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='2times_hello')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_RESULTSType", - "content_type": "text/plain", - "media_src": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='2times_hello')/$value" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "task_id": 1, - "name": "SAR_XML", - "additional_info": "/usr/sap/trans/tmp/SAPK-001AAINITAPC1.SAR", - "mimetype": "application/octet-stream" - } - ] - } -}` - -var responseGetValues = `{ - "d": { - "results": [ - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='PHASE')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='PHASE')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_VALUESType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "value_id": "PHASE", - "value": "test1" - }, - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='PACKAGES')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='PACKAGES')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_VALUESType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "value_id": "PACKAGES", - "value": "/BUILD/CORE" - }, - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='season')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='season')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_VALUESType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "value_id": "season", - "value": "winter" - }, - { - "__metadata": { - "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='SUN')", - "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='SUN')", - "type": "BUILD.CORE_SRV.xBUILDxVIEW_VALUESType" - }, - "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", - "value_id": "SUN", - "value": "FLOWER" - } - ] - } -}` diff --git a/pkg/abap/build/bfw.go b/pkg/abap/build/bfw.go new file mode 100644 index 000000000..9c2fb0bce --- /dev/null +++ b/pkg/abap/build/bfw.go @@ -0,0 +1,375 @@ +package build + +import ( + "encoding/json" + "errors" + "fmt" + "sort" + + "github.com/SAP/jenkins-library/pkg/log" +) + +// RunState : Current Status of the Build +type RunState string +type resultState string +type msgty string + +const ( + successful resultState = "SUCCESSFUL" + warning resultState = "WARNING" + erroneous resultState = "ERRONEOUS" + aborted resultState = "ABORTED" + // Initializing : Build Framework prepared + Initializing RunState = "INITIALIZING" + // Accepted : Build Framework triggered + Accepted RunState = "ACCEPTED" + // Running : Build Framework performs build + Running RunState = "RUNNING" + // Finished : Build Framework ended successful + Finished RunState = "FINISHED" + // Failed : Build Framework endded with error + Failed RunState = "FAILED" + loginfo msgty = "I" + logwarning msgty = "W" + logerror msgty = "E" + logaborted msgty = "A" +) + +//******** structs needed for json convertion ******** + +type jsonBuild struct { + Build *Build `json:"d"` +} + +type jsonTasks struct { + ResultTasks struct { + Tasks []task `json:"results"` + } `json:"d"` +} + +type jsonLogs struct { + ResultLogs struct { + Logs []logStruct `json:"results"` + } `json:"d"` +} + +type jsonResults struct { + ResultResults struct { + Results []Result `json:"results"` + } `json:"d"` +} + +type jsonValues struct { + ResultValues struct { + Values []Value `json:"results"` + } `json:"d"` +} + +// ******** resembling data model in backend ******** + +// Build : Information for all data comming from Build Framework +type Build struct { + Connector Connector + BuildID string `json:"build_id"` + RunState RunState `json:"run_state"` + ResultState resultState `json:"result_state"` + Phase string `json:"phase"` + Entitytype string `json:"entitytype"` + Startedby string `json:"startedby"` + StartedAt string `json:"started_at"` + FinishedAt string `json:"finished_at"` + Tasks []task + Values []Value +} + +type task struct { + connector Connector + BuildID string `json:"build_id"` + TaskID int `json:"task_id"` + LogID string `json:"log_id"` + PluginClass string `json:"plugin_class"` + StartedAt string `json:"started_at"` + FinishedAt string `json:"finished_at"` + ResultState resultState `json:"result_state"` + Logs []logStruct + Results []Result +} + +type logStruct struct { + BuildID string `json:"build_id"` + TaskID int `json:"task_id"` + LogID string `json:"log_id"` + Msgty msgty `json:"msgty"` + Detlevel string `json:"detlevel"` + Logline string `json:"log_line"` + Timestamp string `json:"TIME_STMP"` +} + +// Result : Artefact from Build Framework step +type Result struct { + connector Connector + BuildID string `json:"build_id"` + TaskID int `json:"task_id"` + Name string `json:"name"` + AdditionalInfo string `json:"additional_info"` + Mimetype string `json:"mimetype"` +} + +// Value : Returns Build Runtime Value +type Value struct { + connector Connector + BuildID string `json:"build_id"` + ValueID string `json:"value_id"` + Value string `json:"value"` +} + +// Values : Returns Build Runtime Values +type Values struct { + Values []Value `json:"results"` +} + +type inputForPost struct { + phase string + values Values +} + +// ********************************************************************* +// ******************************* Funcs ******************************* +// ********************************************************************* + +// Start : Starts the Build Framework +func (b *Build) Start(phase string, inputValues Values) error { + if err := b.Connector.GetToken(""); err != nil { + return err + } + importBody := inputForPost{ + phase: phase, + values: inputValues, + }.String() + + body, err := b.Connector.Post("/builds", importBody) + if err != nil { + return err + } + + var jBuild jsonBuild + json.Unmarshal(body, &jBuild) + b.BuildID = jBuild.Build.BuildID + b.RunState = jBuild.Build.RunState + b.ResultState = jBuild.Build.ResultState + b.Phase = jBuild.Build.Phase + b.Entitytype = jBuild.Build.Entitytype + b.Startedby = jBuild.Build.Startedby + b.StartedAt = jBuild.Build.StartedAt + b.FinishedAt = jBuild.Build.FinishedAt + fmt.Println("Hello") + return nil +} + +// Get : Get all Build tasks +func (b *Build) Get() error { + appendum := "/builds('" + b.BuildID + "')" + body, err := b.Connector.Get(appendum) + if err != nil { + return err + } + var jBuild jsonBuild + json.Unmarshal(body, &jBuild) + b.RunState = jBuild.Build.RunState + b.ResultState = jBuild.Build.ResultState + b.Phase = jBuild.Build.Phase + b.Entitytype = jBuild.Build.Entitytype + b.Startedby = jBuild.Build.Startedby + b.StartedAt = jBuild.Build.StartedAt + b.FinishedAt = jBuild.Build.FinishedAt + return nil +} + +func (b *Build) getTasks() error { + if len(b.Tasks) == 0 { + appendum := "/builds('" + b.BuildID + "')/tasks" + body, err := b.Connector.Get(appendum) + if err != nil { + return err + } + var jTasks jsonTasks + json.Unmarshal(body, &jTasks) + b.Tasks = jTasks.ResultTasks.Tasks + sort.Slice(b.Tasks, func(i, j int) bool { + return b.Tasks[i].TaskID < b.Tasks[j].TaskID + }) + for i := range b.Tasks { + b.Tasks[i].connector = b.Connector + } + } + return nil +} + +func (b *Build) getValues() error { + if len(b.Values) == 0 { + appendum := "/builds('" + b.BuildID + "')/values" + body, err := b.Connector.Get(appendum) + if err != nil { + return err + } + var jValues jsonValues + json.Unmarshal(body, &jValues) + b.Values = jValues.ResultValues.Values + for i := range b.Values { + b.Values[i].connector = b.Connector + } + } + return nil +} + +func (b *Build) getLogs() error { + if err := b.getTasks(); err != nil { + return err + } + for i := range b.Tasks { + if err := b.Tasks[i].getLogs(); err != nil { + return err + } + } + return nil +} + +// PrintLogs : Returns the Build logs +func (b *Build) PrintLogs() error { + if err := b.getTasks(); err != nil { + return err + } + for i := range b.Tasks { + if err := b.Tasks[i].printLogs(); err != nil { + return err + } + } + return nil +} + +func (b *Build) getResults() error { + if err := b.getTasks(); err != nil { + return err + } + for i := range b.Tasks { + if err := b.Tasks[i].getResults(); err != nil { + return err + } + } + return nil +} + +func (t *task) printLogs() error { + if err := t.getLogs(); err != nil { + return err + } + for _, logs := range t.Logs { + logs.print() + } + return nil +} + +// GetResult : Returns the last Build artefact created from build step +func (b *Build) GetResult(name string) (Result, error) { + var Results []Result + var returnResult Result + if err := b.getResults(); err != nil { + return returnResult, err + } + for _, task := range b.Tasks { + for _, result := range task.Results { + if result.Name == name { + Results = append(Results, result) + } + } + } + switch len(Results) { + case 0: + return returnResult, errors.New("No result named " + name + " was found") + case 1: + return Results[0], nil + default: + return returnResult, errors.New("More than one result with the name " + name + " was found") + } +} + +// IsFinished : Returns Build run state +func (b *Build) IsFinished() bool { + if b.RunState == Finished || b.RunState == Failed { + return true + } + return false +} + +func (t *task) getLogs() error { + if len(t.Logs) == 0 { + appendum := fmt.Sprint("/tasks(build_id='", t.BuildID, "',task_id=", t.TaskID, ")/logs") + body, err := t.connector.Get(appendum) + if err != nil { + return err + } + var jLogs jsonLogs + json.Unmarshal(body, &jLogs) + t.Logs = jLogs.ResultLogs.Logs + } + return nil +} + +func (t *task) getResults() error { + if len(t.Results) == 0 { + appendum := fmt.Sprint("/tasks(build_id='", t.BuildID, "',task_id=", t.TaskID, ")/results") + body, err := t.connector.Get(appendum) + if err != nil { + return err + } + var jResults jsonResults + json.Unmarshal(body, &jResults) + t.Results = jResults.ResultResults.Results + for i := range t.Results { + t.Results[i].connector = t.connector + } + } + return nil +} + +// Download : Provides the atrefact of build step +func (result *Result) Download(downloadPath string) error { + appendum := fmt.Sprint("/results(build_id='", result.BuildID, "',task_id=", result.TaskID, ",name='", result.Name, "')/$value") + err := result.connector.Download(appendum, downloadPath) + return err +} + +func (logging *logStruct) print() { + switch logging.Msgty { + case loginfo: + log.Entry().WithField("Timestamp", logging.Timestamp).Info(logging.Logline) + case logwarning: + log.Entry().WithField("Timestamp", logging.Timestamp).Warn(logging.Logline) + case logerror: + log.Entry().WithField("Timestamp", logging.Timestamp).Error(logging.Logline) + case logaborted: + log.Entry().WithField("Timestamp", logging.Timestamp).Error(logging.Logline) + default: + } +} + +// ******** parsing ******** +func (v Value) String() string { + return fmt.Sprintf( + `{ "value_id": "%s", "value": "%s" }`, + v.ValueID, + v.Value) +} + +func (vs Values) String() string { + returnString := "" + for _, value := range vs.Values { + returnString = returnString + value.String() + ",\n" + } + returnString = returnString[:len(returnString)-2] //removes last , + return returnString +} + +func (in inputForPost) String() string { + return fmt.Sprintf(`{ "phase": "%s", "values": [%s]}`, in.phase, in.values.String()) +} diff --git a/pkg/abap/build/bfw_mock.go b/pkg/abap/build/bfw_mock.go new file mode 100644 index 000000000..3cd2b50fe --- /dev/null +++ b/pkg/abap/build/bfw_mock.go @@ -0,0 +1,345 @@ +package build + +import ( + "bytes" + "io" + "io/ioutil" + "net/http" + "strings" + + piperhttp "github.com/SAP/jenkins-library/pkg/http" +) + +// DownloadClientMock : Mock for Download Client used for artefact test +type DownloadClientMock struct{} + +// DownloadFile : Empty file download +func (dc *DownloadClientMock) DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error { + return nil +} + +// SetOptions : Download Client options +func (dc *DownloadClientMock) SetOptions(opts piperhttp.ClientOptions) {} + +// ClMock : Mock for Build Framework Client used for BF test +type ClMock struct { + Token string + StatusCode int + Error error +} + +// SetOptions : BF Client options +func (c *ClMock) SetOptions(opts piperhttp.ClientOptions) {} + +// SendRequest : BF Send Fake request +func (c *ClMock) SendRequest(method string, url string, bdy io.Reader, hdr http.Header, cookies []*http.Cookie) (*http.Response, error) { + if method == "GET" || method == "POST" { + var body []byte + body = []byte(fakeResponse(method, url)) + return &http.Response{ + StatusCode: c.StatusCode, + Body: ioutil.NopCloser(bytes.NewReader(body)), + }, c.Error + } else if method == "HEAD" { + var body []byte + header := http.Header{} + header.Set("X-Csrf-Token", c.Token) + body = []byte("") + return &http.Response{ + StatusCode: c.StatusCode, + Header: header, + Body: ioutil.NopCloser(bytes.NewReader(body)), + }, c.Error + } else { + return nil, c.Error + } +} + +func fakeResponse(method string, url string) string { + if method == "POST" { + return responsePOST + } + if strings.HasSuffix(url, "/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')") { + return responseGetBuild + } else if strings.HasSuffix(url, "/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')/tasks") { + return responseGetTasks + } else if strings.HasSuffix(url, "/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)/logs") { + return responseGetLog0 + } else if strings.HasSuffix(url, "/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)/logs") { + return responseGetLog1 + } else if strings.HasSuffix(url, "/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')/values") { + return responseGetValues + } else if strings.HasSuffix(url, "tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)/results") { + return responseGetResults0 + } else if strings.HasSuffix(url, "tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)/results") { + return responseGetResults1 + } + return "" +} + +var responseGetBuild = `{ + "d": { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_BUILDSType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "run_state": "FINISHED", + "result_state": "SUCCESSFUL", + "phase": "test1", + "entitytype": "P", + "startedby": "BENTELER", + "started_at": "/Date(1591718108103+0000)/", + "finished_at": "/Date(1591718129432+0000)/", + "tasks": { + "__deferred": { + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')/tasks" + } + }, + "values": { + "__deferred": { + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPOVMXK4DNPBDRW2M')/values" + } + } + } +}` + +var responsePOST = `{ + "d": { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPNVMOUQL2LHUFAUA')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPNVMOUQL2LHUFAUA')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_BUILDSType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "run_state": "ACCEPTED", + "result_state": "", + "phase": "test1", + "entitytype": "", + "startedby": "BENTELER", + "started_at": null, + "finished_at": null, + "tasks": { + "__deferred": { + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/builds('ABIFNLDCSQPNVMOUQL2LHUFAUA')/tasks" + } + }, + "values": { + "results": [] + } + } +}` + +var responseGetTasks = `{ + "d": { + "results": [ + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_TASKSType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "task_id": 1, + "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_1", + "plugin_class": "/BUILD/CL_TEST_PLUGIN_OK", + "started_at": "/Date(1591718128730+0000)/", + "finished_at": "/Date(1591718129369+0000)/", + "result_state": "SUCCESSFUL", + "logs": { + "__deferred": { + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)/logs" + } + }, + "results": { + "__deferred": { + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1)/results" + } + } + }, + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_TASKSType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "task_id": 0, + "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_0", + "plugin_class": "", + "started_at": "/Date(1591718128728+0000)/", + "finished_at": "/Date(1591718129462+0000)/", + "result_state": "SUCCESSFUL", + "logs": { + "__deferred": { + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)/logs" + } + }, + "results": { + "__deferred": { + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/tasks(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0)/results" + } + } + } + ] + } +}` + +var responseGetLog0 = `{ + "d": { + "results": [ + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_0')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_0')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_LOGSType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "task_id": 0, + "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_0", + "msgty": "I", + "detlevel": "3", + "log_line": "I:/BUILD/LOG:000 ABAP Build Framework", + "TIME_STMP": "20200721133523" + }, + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_0')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=0,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_0')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_LOGSType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "task_id": 0, + "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_0", + "msgty": "I", + "detlevel": "3", + "log_line": "I:/BUILD/LOG:000 ... Build Execution finished SUCCESSFUL", + "TIME_STMP": "20200721133528" + } + ] + } +}` + +var responseGetLog1 = `{ + "d": { + "results": [ + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_1')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_1')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_LOGSType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "task_id": 1, + "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_1", + "msgty": "I", + "detlevel": "1", + "log_line": "I:/BUILD/LOG:000 Hello Packages [1]: , /BUILD/CORE, here is your lovely test_ok plugin!", + "TIME_STMP": "20200721133528" + }, + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_1')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/logs(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,log_id='ABIFNLDCSQPOVMXK4DNPBDRW2M_1')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_LOGSType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "task_id": 1, + "log_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M_1", + "msgty": "W", + "detlevel": "3", + "log_line": "W:/BUILD/LOG:000 We can even have warnings!", + "TIME_STMP": "20200721133528" + } + ] + } +}` + +var responseGetResults0 = `{ + "d": { + "results": [] + } +}` + +var responseGetResults1 = `{ + "d": { + "results": [ + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='HT-6111.JPG')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='HT-6111.JPG')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_RESULTSType", + "content_type": "image/jpeg", + "media_src": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='HT-6111.JPG')/$value" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "task_id": 1, + "name": "HT-6111.JPG", + "additional_info": "", + "mimetype": "image/jpeg" + }, + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='2times_hello')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='2times_hello')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_RESULTSType", + "content_type": "text/plain", + "media_src": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/results(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',task_id=1,name='2times_hello')/$value" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "task_id": 1, + "name": "SAR_XML", + "additional_info": "/usr/sap/trans/tmp/SAPK-001AAINITAPC1.SAR", + "mimetype": "application/octet-stream" + } + ] + } +}` + +var responseGetValues = `{ + "d": { + "results": [ + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='PHASE')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='PHASE')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_VALUESType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "value_id": "PHASE", + "value": "test1" + }, + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='PACKAGES')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='PACKAGES')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_VALUESType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "value_id": "PACKAGES", + "value": "/BUILD/CORE" + }, + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='season')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='season')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_VALUESType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "value_id": "season", + "value": "winter" + }, + { + "__metadata": { + "id": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='SUN')", + "uri": "https://ldai3yi3.wdf.sap.corp:44334/sap/opu/odata/BUILD/CORE_SRV/values(build_id='ABIFNLDCSQPOVMXK4DNPBDRW2M',value_id='SUN')", + "type": "BUILD.CORE_SRV.xBUILDxVIEW_VALUESType" + }, + "build_id": "ABIFNLDCSQPOVMXK4DNPBDRW2M", + "value_id": "SUN", + "value": "FLOWER" + } + ] + } +}` diff --git a/pkg/abap/build/bfw_test.go b/pkg/abap/build/bfw_test.go new file mode 100644 index 000000000..d8f40fd91 --- /dev/null +++ b/pkg/abap/build/bfw_test.go @@ -0,0 +1,115 @@ +package build + +import ( + "testing" + + piperhttp "github.com/SAP/jenkins-library/pkg/http" + "github.com/stretchr/testify/assert" +) + +func testSetup(client piperhttp.Sender, buildID string) Build { + conn := new(Connector) + conn.Client = client + conn.DownloadClient = &DownloadClientMock{} + conn.Header = make(map[string][]string) + b := Build{ + Connector: *conn, + BuildID: buildID, + } + return b +} + +func TestStart(t *testing.T) { + t.Run("Run start", func(t *testing.T) { + client := &ClMock{ + Token: "MyToken", + } + b := testSetup(client, "") + inputValues := Values{ + Values: []Value{ + { + ValueID: "PACKAGES", + Value: "/BUILD/CORE", + }, + { + ValueID: "season", + Value: "winter", + }, + }, + } + err := b.Start("test", inputValues) + assert.NoError(t, err) + assert.Equal(t, Accepted, b.RunState) + }) +} + +func TestGet(t *testing.T) { + t.Run("Run Get", func(t *testing.T) { + b := testSetup(&ClMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") + err := b.Get() + assert.NoError(t, err) + assert.Equal(t, Finished, b.RunState) + assert.Equal(t, 0, len(b.Tasks)) + }) +} + +func TestGetTasks(t *testing.T) { + t.Run("Run getTasks", func(t *testing.T) { + b := testSetup(&ClMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") + assert.Equal(t, 0, len(b.Tasks)) + err := b.getTasks() + assert.NoError(t, err) + assert.Equal(t, b.Tasks[0].TaskID, 0) + assert.Equal(t, b.Tasks[0].PluginClass, "") + assert.Equal(t, b.Tasks[1].TaskID, 1) + assert.Equal(t, b.Tasks[1].PluginClass, "/BUILD/CL_TEST_PLUGIN_OK") + }) +} + +func TestGetLogs(t *testing.T) { + t.Run("Run getLogs", func(t *testing.T) { + b := testSetup(&ClMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") + err := b.getLogs() + assert.NoError(t, err) + assert.Equal(t, "I:/BUILD/LOG:000 ABAP Build Framework", b.Tasks[0].Logs[0].Logline) + assert.Equal(t, loginfo, b.Tasks[0].Logs[0].Msgty) + assert.Equal(t, "W:/BUILD/LOG:000 We can even have warnings!", b.Tasks[1].Logs[1].Logline) + assert.Equal(t, logwarning, b.Tasks[1].Logs[1].Msgty) + }) +} + +func TestGetValues(t *testing.T) { + t.Run("Run getValues", func(t *testing.T) { + b := testSetup(&ClMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") + assert.Equal(t, 0, len(b.Values)) + err := b.getValues() + assert.NoError(t, err) + assert.Equal(t, 4, len(b.Values)) + assert.Equal(t, "PHASE", b.Values[0].ValueID) + assert.Equal(t, "test1", b.Values[0].Value) + assert.Equal(t, "PACKAGES", b.Values[1].ValueID) + assert.Equal(t, "/BUILD/CORE", b.Values[1].Value) + assert.Equal(t, "season", b.Values[2].ValueID) + assert.Equal(t, "winter", b.Values[2].Value) + assert.Equal(t, "SUN", b.Values[3].ValueID) + assert.Equal(t, "FLOWER", b.Values[3].Value) + }) +} + +func TestGetResults(t *testing.T) { + t.Run("Run getResults", func(t *testing.T) { + b := testSetup(&ClMock{}, "ABIFNLDCSQPOVMXK4DNPBDRW2M") + err := b.getResults() + assert.NoError(t, err) + assert.Equal(t, 0, len(b.Tasks[0].Results)) + assert.Equal(t, 2, len(b.Tasks[1].Results)) + assert.Equal(t, "image/jpeg", b.Tasks[1].Results[0].Mimetype) + assert.Equal(t, "application/octet-stream", b.Tasks[1].Results[1].Mimetype) + + _, err = b.GetResult("does_not_exist") + assert.Error(t, err) + r, err := b.GetResult("SAR_XML") + assert.Equal(t, "application/octet-stream", r.Mimetype) + assert.NoError(t, err) + }) +} diff --git a/pkg/abap/build/connector.go b/pkg/abap/build/connector.go index 399d08926..0cad3d49f 100644 --- a/pkg/abap/build/connector.go +++ b/pkg/abap/build/connector.go @@ -5,7 +5,9 @@ import ( "io/ioutil" "net/http" "net/http/cookiejar" + "time" + "github.com/SAP/jenkins-library/pkg/abaputils" piperhttp "github.com/SAP/jenkins-library/pkg/http" "github.com/pkg/errors" ) @@ -18,6 +20,20 @@ type Connector struct { Baseurl string } +// ConnectorConfiguration : Handover Structure for Connector Creation +type ConnectorConfiguration struct { + CfAPIEndpoint string + CfOrg string + CfSpace string + CfServiceInstance string + CfServiceKeyName string + Host string + Username string + Password string + AddonDescriptor string + MaxRuntimeInMinutes int +} + // ******** technical communication calls ******** // GetToken : Get the X-CRSF Token from ABAP Backend for later post @@ -89,7 +105,7 @@ func (conn Connector) Download(appendum string, downloadPath string) error { return err } -// InitAAKaaS : initializie Connector for communication with AAKaaS backend +// InitAAKaaS : initialize Connector for communication with AAKaaS backend func (conn *Connector) InitAAKaaS(aAKaaSEndpoint string, username string, password string, inputclient piperhttp.Sender) { conn.Client = inputclient conn.Header = make(map[string][]string) @@ -105,6 +121,47 @@ func (conn *Connector) InitAAKaaS(aAKaaSEndpoint string, username string, passwo conn.Baseurl = aAKaaSEndpoint } +// InitBuildFramework : initialize Connector for communication with ABAP SCP instance +func (conn *Connector) InitBuildFramework(config ConnectorConfiguration, com abaputils.Communication, inputclient piperhttp.Sender) error { + conn.Client = inputclient + conn.Header = make(map[string][]string) + conn.Header["Accept"] = []string{"application/json"} + conn.Header["Content-Type"] = []string{"application/json"} + + conn.DownloadClient = &piperhttp.Client{} + conn.DownloadClient.SetOptions(piperhttp.ClientOptions{TransportTimeout: 20 * time.Second}) + // Mapping for options + subOptions := abaputils.AbapEnvironmentOptions{} + subOptions.CfAPIEndpoint = config.CfAPIEndpoint + subOptions.CfServiceInstance = config.CfServiceInstance + subOptions.CfServiceKeyName = config.CfServiceKeyName + subOptions.CfOrg = config.CfOrg + subOptions.CfSpace = config.CfSpace + subOptions.Host = config.Host + subOptions.Password = config.Password + subOptions.Username = config.Username + + // Determine the host, user and password, either via the input parameters or via a cloud foundry service key + connectionDetails, err := com.GetAbapCommunicationArrangementInfo(subOptions, "/sap/opu/odata/BUILD/CORE_SRV") + if err != nil { + return errors.Wrap(err, "Parameters for the ABAP Connection not available") + } + + conn.DownloadClient.SetOptions(piperhttp.ClientOptions{ + Username: connectionDetails.User, + Password: connectionDetails.Password, + }) + cookieJar, _ := cookiejar.New(nil) + conn.Client.SetOptions(piperhttp.ClientOptions{ + Username: connectionDetails.User, + Password: connectionDetails.Password, + CookieJar: cookieJar, + }) + conn.Baseurl = connectionDetails.URL + + return nil +} + // UploadSarFile : upload *.sar file func (conn Connector) UploadSarFile(appendum string, sarFile []byte) error { url := conn.Baseurl + appendum