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

305 lines
8.4 KiB
Go
Raw Normal View History

package cmd
import (
"io/ioutil"
"net/http/cookiejar"
"net/url"
"github.com/Jeffail/gabs/v2"
"github.com/SAP/jenkins-library/pkg/command"
piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/pkg/errors"
)
func gctsRollback(config gctsRollbackOptions, telemetryData *telemetry.CustomData) {
// for command execution use Command
c := command.Command{}
// reroute command output to logging framework
c.Stdout(log.Entry().Writer())
c.Stderr(log.Entry().Writer())
// for http calls import piperhttp "github.com/SAP/jenkins-library/pkg/http"
// and use a &piperhttp.Client{} in a custom system
// Example: step checkmarxExecuteScan.go
httpClient := &piperhttp.Client{}
// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end
err := rollback(&config, telemetryData, &c, httpClient)
if err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
}
func rollback(config *gctsRollbackOptions, telemetryData *telemetry.CustomData, command command.ExecRunner, httpClient piperhttp.Sender) error {
cookieJar, cookieErr := cookiejar.New(nil)
if cookieErr != nil {
return cookieErr
}
clientOptions := piperhttp.ClientOptions{
CookieJar: cookieJar,
Username: config.Username,
Password: config.Password,
}
httpClient.SetOptions(clientOptions)
repoInfo, err := getRepoInfo(config, telemetryData, httpClient)
if err != nil {
return errors.Wrap(err, "could not get local repository data")
}
if repoInfo.Result.URL == "" {
return errors.Errorf("no remote repository URL configured")
}
if err != nil {
return errors.Wrap(err, "could not parse remote repository URL as valid URL")
}
var deployOptions gctsDeployOptions
if config.Commit != "" {
log.Entry().Infof("rolling back to specified commit %v", config.Commit)
deployOptions = gctsDeployOptions{
Username: config.Username,
Password: config.Password,
Host: config.Host,
Repository: config.Repository,
Client: config.Client,
Commit: config.Commit,
}
} else {
repoHistory, err := getRepoHistory(config, telemetryData, httpClient)
if err != nil {
return errors.Wrap(err, "could not retrieve repository commit history")
}
if repoHistory.Result[0].FromCommit != "" {
log.Entry().WithField("repository", config.Repository).Infof("rolling back to last active commit %v", repoHistory.Result[0].FromCommit)
deployOptions = gctsDeployOptions{
Username: config.Username,
Password: config.Password,
Host: config.Host,
Repository: config.Repository,
Client: config.Client,
Commit: repoHistory.Result[0].FromCommit,
}
} else {
return errors.Errorf("no commit to rollback to (fromCommit) could be identified from the repository commit history")
}
}
deployErr := pullByCommit(&deployOptions, telemetryData, command, httpClient)
if deployErr != nil {
return errors.Wrap(deployErr, "rollback commit failed")
}
log.Entry().
WithField("repository", config.Repository).
Infof("rollback was successful")
return nil
}
func getLastSuccessfullCommit(config *gctsRollbackOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender, githubURL *url.URL, commitList []string) (string, error) {
cookieJar, cookieErr := cookiejar.New(nil)
if cookieErr != nil {
return "", cookieErr
}
clientOptions := piperhttp.ClientOptions{
CookieJar: cookieJar,
}
if config.GithubPersonalAccessToken != "" {
clientOptions.Token = "Bearer " + config.GithubPersonalAccessToken
} else {
log.Entry().Warning("no GitHub personal access token was provided")
}
httpClient.SetOptions(clientOptions)
for _, commit := range commitList {
url := githubURL.Scheme + "://api." + githubURL.Host + "/repos" + githubURL.Path + "/commits/" + commit + "/status"
resp, httpErr := httpClient.SendRequest("GET", url, nil, nil, nil)
defer func() {
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
}()
if httpErr != nil {
return "", httpErr
} else if resp == nil {
return "", errors.New("did not retrieve a HTTP response")
}
bodyText, readErr := ioutil.ReadAll(resp.Body)
if readErr != nil {
return "", errors.Wrapf(readErr, "HTTP response body could not be read")
}
response, parsingErr := gabs.ParseJSON([]byte(bodyText))
if parsingErr != nil {
return "", errors.Wrapf(parsingErr, "HTTP response body could not be parsed as JSON: %v", string(bodyText))
}
if status, ok := response.Path("state").Data().(string); ok && status == "success" {
log.Entry().
WithField("repository", config.Repository).
Infof("last successful commit was determined to be %v", commit)
return commit, nil
}
}
return "", errors.Errorf("no commit with status 'success' could be found")
}
func getCommits(config *gctsRollbackOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender) ([]string, error) {
url := config.Host +
"/sap/bc/cts_abapvcs/repository/" + config.Repository +
"/getCommit?sap-client=" + config.Client
type commitsResponseBody struct {
Commits []struct {
ID string `json:"id"`
Author string `json:"author"`
AuthorMail string `json:"authorMail"`
Message string `json:"message"`
Description string `json:"description"`
Date string `json:"date"`
} `json:"commits"`
}
resp, httpErr := httpClient.SendRequest("GET", url, nil, nil, nil)
defer func() {
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
}()
if httpErr != nil {
return []string{}, httpErr
} else if resp == nil {
return []string{}, errors.New("did not retrieve a HTTP response")
}
var response commitsResponseBody
parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response)
if parsingErr != nil {
return []string{}, parsingErr
}
commitList := []string{}
for _, commit := range response.Commits {
commitList = append(commitList, commit.ID)
}
return commitList, nil
}
func getRepoInfo(config *gctsRollbackOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender) (*getRepoInfoResponseBody, error) {
var response getRepoInfoResponseBody
url := config.Host +
"/sap/bc/cts_abapvcs/repository/" + config.Repository +
"?sap-client=" + config.Client
resp, httpErr := httpClient.SendRequest("GET", url, nil, nil, nil)
defer func() {
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
}()
if httpErr != nil {
return &response, httpErr
} else if resp == nil {
return &response, errors.New("did not retrieve a HTTP response")
}
parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response)
if parsingErr != nil {
return &response, parsingErr
}
return &response, nil
}
type getRepoInfoResponseBody struct {
Result struct {
Rid string `json:"rid"`
Name string `json:"name"`
Role string `json:"role"`
Type string `json:"type"`
Vsid string `json:"vsid"`
Status string `json:"status"`
Branch string `json:"branch"`
URL string `json:"url"`
Version string `json:"version"`
Objects int `json:"objects"`
CurrentCommit string `json:"currentCommit"`
Connection string `json:"connection"`
Config []struct {
Key string `json:"key"`
Value string `json:"value"`
} `json:"config"`
} `json:"result"`
}
func getRepoHistory(config *gctsRollbackOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender) (*getRepoHistoryResponseBody, error) {
var response getRepoHistoryResponseBody
url := config.Host +
"/sap/bc/cts_abapvcs/repository/" + config.Repository +
"/getHistory?sap-client=" + config.Client
resp, httpErr := httpClient.SendRequest("GET", url, nil, nil, nil)
defer func() {
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
}()
if httpErr != nil {
return &response, httpErr
} else if resp == nil {
return &response, errors.New("did not retrieve a HTTP response")
}
parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response)
if parsingErr != nil {
return &response, parsingErr
}
return &response, nil
}
type getRepoHistoryResponseBody struct {
Result []struct {
Rid string `json:"rid"`
CheckoutTime int64 `json:"checkoutTime"`
FromCommit string `json:"fromCommit"`
ToCommit string `json:"toCommit"`
Caller string `json:"caller"`
Request string `json:"request"`
Type string `json:"type"`
} `json:"result"`
}