github.com/jaylevin/jenkins-library@v1.230.4/cmd/gctsRollback.go (about)

     1  package cmd
     2  
     3  import (
     4  	"io/ioutil"
     5  	"net/http/cookiejar"
     6  	"net/url"
     7  
     8  	"github.com/Jeffail/gabs/v2"
     9  	"github.com/SAP/jenkins-library/pkg/command"
    10  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    11  	"github.com/SAP/jenkins-library/pkg/log"
    12  	"github.com/SAP/jenkins-library/pkg/telemetry"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  func gctsRollback(config gctsRollbackOptions, telemetryData *telemetry.CustomData) {
    17  	// for command execution use Command
    18  	c := command.Command{}
    19  	// reroute command output to logging framework
    20  	c.Stdout(log.Entry().Writer())
    21  	c.Stderr(log.Entry().Writer())
    22  
    23  	// for http calls import  piperhttp "github.com/SAP/jenkins-library/pkg/http"
    24  	// and use a  &piperhttp.Client{} in a custom system
    25  	// Example: step checkmarxExecuteScan.go
    26  	httpClient := &piperhttp.Client{}
    27  
    28  	// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end
    29  	err := rollback(&config, telemetryData, &c, httpClient)
    30  	if err != nil {
    31  		log.Entry().WithError(err).Fatal("step execution failed")
    32  	}
    33  }
    34  
    35  func rollback(config *gctsRollbackOptions, telemetryData *telemetry.CustomData, command command.ExecRunner, httpClient piperhttp.Sender) error {
    36  
    37  	cookieJar, cookieErr := cookiejar.New(nil)
    38  	if cookieErr != nil {
    39  		return cookieErr
    40  	}
    41  	clientOptions := piperhttp.ClientOptions{
    42  		CookieJar: cookieJar,
    43  		Username:  config.Username,
    44  		Password:  config.Password,
    45  	}
    46  	httpClient.SetOptions(clientOptions)
    47  
    48  	repoInfo, err := getRepoInfo(config, telemetryData, httpClient)
    49  	if err != nil {
    50  		return errors.Wrap(err, "could not get local repository data")
    51  	}
    52  
    53  	if repoInfo.Result.URL == "" {
    54  		return errors.Errorf("no remote repository URL configured")
    55  	}
    56  
    57  	if err != nil {
    58  		return errors.Wrap(err, "could not parse remote repository URL as valid URL")
    59  	}
    60  
    61  	var deployOptions gctsDeployOptions
    62  
    63  	if config.Commit != "" {
    64  		log.Entry().Infof("rolling back to specified commit %v", config.Commit)
    65  
    66  		deployOptions = gctsDeployOptions{
    67  			Username:   config.Username,
    68  			Password:   config.Password,
    69  			Host:       config.Host,
    70  			Repository: config.Repository,
    71  			Client:     config.Client,
    72  			Commit:     config.Commit,
    73  		}
    74  
    75  	} else {
    76  		repoHistory, err := getRepoHistory(config, telemetryData, httpClient)
    77  		if err != nil {
    78  			return errors.Wrap(err, "could not retrieve repository commit history")
    79  		}
    80  		if repoHistory.Result[0].FromCommit != "" {
    81  
    82  			log.Entry().WithField("repository", config.Repository).Infof("rolling back to last active commit %v", repoHistory.Result[0].FromCommit)
    83  			deployOptions = gctsDeployOptions{
    84  				Username:   config.Username,
    85  				Password:   config.Password,
    86  				Host:       config.Host,
    87  				Repository: config.Repository,
    88  				Client:     config.Client,
    89  				Commit:     repoHistory.Result[0].FromCommit,
    90  			}
    91  
    92  		} else {
    93  			return errors.Errorf("no commit to rollback to (fromCommit) could be identified from the repository commit history")
    94  		}
    95  	}
    96  
    97  	deployErr := pullByCommit(&deployOptions, telemetryData, command, httpClient)
    98  
    99  	if deployErr != nil {
   100  		return errors.Wrap(deployErr, "rollback commit failed")
   101  	}
   102  
   103  	log.Entry().
   104  		WithField("repository", config.Repository).
   105  		Infof("rollback was successful")
   106  	return nil
   107  }
   108  
   109  func getLastSuccessfullCommit(config *gctsRollbackOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender, githubURL *url.URL, commitList []string) (string, error) {
   110  
   111  	cookieJar, cookieErr := cookiejar.New(nil)
   112  	if cookieErr != nil {
   113  		return "", cookieErr
   114  	}
   115  	clientOptions := piperhttp.ClientOptions{
   116  		CookieJar: cookieJar,
   117  	}
   118  
   119  	if config.GithubPersonalAccessToken != "" {
   120  		clientOptions.Token = "Bearer " + config.GithubPersonalAccessToken
   121  	} else {
   122  		log.Entry().Warning("no GitHub personal access token was provided")
   123  	}
   124  
   125  	httpClient.SetOptions(clientOptions)
   126  
   127  	for _, commit := range commitList {
   128  
   129  		url := githubURL.Scheme + "://api." + githubURL.Host + "/repos" + githubURL.Path + "/commits/" + commit + "/status"
   130  
   131  		resp, httpErr := httpClient.SendRequest("GET", url, nil, nil, nil)
   132  
   133  		defer func() {
   134  			if resp != nil && resp.Body != nil {
   135  				resp.Body.Close()
   136  			}
   137  		}()
   138  
   139  		if httpErr != nil {
   140  			return "", httpErr
   141  		} else if resp == nil {
   142  			return "", errors.New("did not retrieve a HTTP response")
   143  		}
   144  
   145  		bodyText, readErr := ioutil.ReadAll(resp.Body)
   146  
   147  		if readErr != nil {
   148  			return "", errors.Wrapf(readErr, "HTTP response body could not be read")
   149  		}
   150  
   151  		response, parsingErr := gabs.ParseJSON([]byte(bodyText))
   152  
   153  		if parsingErr != nil {
   154  			return "", errors.Wrapf(parsingErr, "HTTP response body could not be parsed as JSON: %v", string(bodyText))
   155  		}
   156  
   157  		if status, ok := response.Path("state").Data().(string); ok && status == "success" {
   158  			log.Entry().
   159  				WithField("repository", config.Repository).
   160  				Infof("last successful commit was determined to be %v", commit)
   161  			return commit, nil
   162  		}
   163  	}
   164  
   165  	return "", errors.Errorf("no commit with status 'success' could be found")
   166  }
   167  
   168  func getCommits(config *gctsRollbackOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender) ([]string, error) {
   169  
   170  	url := config.Host +
   171  		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
   172  		"/getCommit?sap-client=" + config.Client
   173  
   174  	type commitsResponseBody struct {
   175  		Commits []struct {
   176  			ID          string `json:"id"`
   177  			Author      string `json:"author"`
   178  			AuthorMail  string `json:"authorMail"`
   179  			Message     string `json:"message"`
   180  			Description string `json:"description"`
   181  			Date        string `json:"date"`
   182  		} `json:"commits"`
   183  	}
   184  
   185  	resp, httpErr := httpClient.SendRequest("GET", url, nil, nil, nil)
   186  
   187  	defer func() {
   188  		if resp != nil && resp.Body != nil {
   189  			resp.Body.Close()
   190  		}
   191  	}()
   192  
   193  	if httpErr != nil {
   194  		return []string{}, httpErr
   195  	} else if resp == nil {
   196  		return []string{}, errors.New("did not retrieve a HTTP response")
   197  	}
   198  
   199  	var response commitsResponseBody
   200  	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response)
   201  	if parsingErr != nil {
   202  		return []string{}, parsingErr
   203  	}
   204  
   205  	commitList := []string{}
   206  	for _, commit := range response.Commits {
   207  		commitList = append(commitList, commit.ID)
   208  	}
   209  
   210  	return commitList, nil
   211  }
   212  
   213  func getRepoInfo(config *gctsRollbackOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender) (*getRepoInfoResponseBody, error) {
   214  
   215  	var response getRepoInfoResponseBody
   216  
   217  	url := config.Host +
   218  		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
   219  		"?sap-client=" + config.Client
   220  
   221  	resp, httpErr := httpClient.SendRequest("GET", url, nil, nil, nil)
   222  
   223  	defer func() {
   224  		if resp != nil && resp.Body != nil {
   225  			resp.Body.Close()
   226  		}
   227  	}()
   228  
   229  	if httpErr != nil {
   230  		return &response, httpErr
   231  	} else if resp == nil {
   232  		return &response, errors.New("did not retrieve a HTTP response")
   233  	}
   234  
   235  	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response)
   236  	if parsingErr != nil {
   237  		return &response, parsingErr
   238  	}
   239  
   240  	return &response, nil
   241  }
   242  
   243  type getRepoInfoResponseBody struct {
   244  	Result struct {
   245  		Rid           string `json:"rid"`
   246  		Name          string `json:"name"`
   247  		Role          string `json:"role"`
   248  		Type          string `json:"type"`
   249  		Vsid          string `json:"vsid"`
   250  		Status        string `json:"status"`
   251  		Branch        string `json:"branch"`
   252  		URL           string `json:"url"`
   253  		Version       string `json:"version"`
   254  		Objects       int    `json:"objects"`
   255  		CurrentCommit string `json:"currentCommit"`
   256  		Connection    string `json:"connection"`
   257  		Config        []struct {
   258  			Key   string `json:"key"`
   259  			Value string `json:"value"`
   260  		} `json:"config"`
   261  	} `json:"result"`
   262  }
   263  
   264  func getRepoHistory(config *gctsRollbackOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender) (*getRepoHistoryResponseBody, error) {
   265  
   266  	var response getRepoHistoryResponseBody
   267  
   268  	url := config.Host +
   269  		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
   270  		"/getHistory?sap-client=" + config.Client
   271  
   272  	resp, httpErr := httpClient.SendRequest("GET", url, nil, nil, nil)
   273  
   274  	defer func() {
   275  		if resp != nil && resp.Body != nil {
   276  			resp.Body.Close()
   277  		}
   278  	}()
   279  
   280  	if httpErr != nil {
   281  		return &response, httpErr
   282  	} else if resp == nil {
   283  		return &response, errors.New("did not retrieve a HTTP response")
   284  	}
   285  
   286  	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &response)
   287  	if parsingErr != nil {
   288  		return &response, parsingErr
   289  	}
   290  
   291  	return &response, nil
   292  }
   293  
   294  type getRepoHistoryResponseBody struct {
   295  	Result []struct {
   296  		Rid          string `json:"rid"`
   297  		CheckoutTime int64  `json:"checkoutTime"`
   298  		FromCommit   string `json:"fromCommit"`
   299  		ToCommit     string `json:"toCommit"`
   300  		Caller       string `json:"caller"`
   301  		Request      string `json:"request"`
   302  		Type         string `json:"type"`
   303  	} `json:"result"`
   304  }