github.com/xgoffin/jenkins-library@v1.154.0/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 }