Skip to content

Commit f893104

Browse files
authored
Merge branch 'main' into MemorystoreInstanceReplication
2 parents f2b8f98 + 32c4370 commit f893104

File tree

189 files changed

+5656
-946
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

189 files changed

+5656
-946
lines changed

.ci/infra/terraform/main.tf

+17
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ module "project-services" {
321321
"parametermanager.googleapis.com",
322322
"privateca.googleapis.com",
323323
"privilegedaccessmanager.googleapis.com",
324+
"progressiverollout.googleapis.com",
324325
"pubsub.googleapis.com",
325326
"pubsublite.googleapis.com",
326327
"publicca.googleapis.com",
@@ -393,6 +394,22 @@ resource "google_project_service_identity" "sqladmin_sa" {
393394
service = "sqladmin.googleapis.com"
394395
}
395396

397+
resource "google_project_service_identity" "osconfig_sa" {
398+
provider = google-beta
399+
depends_on = [module.project-services]
400+
401+
project = google_project.proj.project_id
402+
service = "osconfig.googleapis.com"
403+
}
404+
405+
resource "google_project_service_identity" "progressiverollout_sa" {
406+
provider = google-beta
407+
depends_on = [module.project-services]
408+
409+
project = google_project.proj.project_id
410+
service = "progressiverollout.googleapis.com"
411+
}
412+
396413
# TestAccComposerEnvironment_fixPyPiPackages
397414
# TestAccComposerEnvironmentComposer2_private
398415
# TestAccComposerEnvironment_withEncryptionConfigComposer1

.ci/magician/github/get.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (gh *Client) GetPullRequest(prNumber string) (PullRequest, error) {
5151

5252
var pullRequest PullRequest
5353

54-
err := utils.RequestCall(url, "GET", gh.token, &pullRequest, nil)
54+
err := utils.RequestCallWithRetry(url, "GET", gh.token, &pullRequest, nil)
5555

5656
return pullRequest, err
5757
}
@@ -61,7 +61,7 @@ func (gh *Client) GetPullRequests(state, base, sort, direction string) ([]PullRe
6161

6262
var pullRequests []PullRequest
6363

64-
err := utils.RequestCall(url, "GET", gh.token, &pullRequests, nil)
64+
err := utils.RequestCallWithRetry(url, "GET", gh.token, &pullRequests, nil)
6565

6666
return pullRequests, err
6767
}
@@ -73,7 +73,7 @@ func (gh *Client) GetPullRequestRequestedReviewers(prNumber string) ([]User, err
7373
Users []User `json:"users"`
7474
}
7575

76-
err := utils.RequestCall(url, "GET", gh.token, &requestedReviewers, nil)
76+
err := utils.RequestCallWithRetry(url, "GET", gh.token, &requestedReviewers, nil)
7777
if err != nil {
7878
return nil, err
7979
}
@@ -88,7 +88,7 @@ func (gh *Client) GetPullRequestPreviousReviewers(prNumber string) ([]User, erro
8888
User User `json:"user"`
8989
}
9090

91-
err := utils.RequestCall(url, "GET", gh.token, &reviews, nil)
91+
err := utils.RequestCallWithRetry(url, "GET", gh.token, &reviews, nil)
9292
if err != nil {
9393
return nil, err
9494
}
@@ -110,7 +110,7 @@ func (gh *Client) GetPullRequestComments(prNumber string) ([]PullRequestComment,
110110
url := fmt.Sprintf("https://api.github.com/repos/GoogleCloudPlatform/magic-modules/issues/%s/comments", prNumber)
111111

112112
var comments []PullRequestComment
113-
err := utils.RequestCall(url, "GET", gh.token, &comments, nil)
113+
err := utils.RequestCallWithRetry(url, "GET", gh.token, &comments, nil)
114114
if err != nil {
115115
return nil, err
116116
}
@@ -121,7 +121,7 @@ func (gh *Client) GetTeamMembers(organization, team string) ([]User, error) {
121121
url := fmt.Sprintf("https://api.github.com/orgs/%s/teams/%s/members", organization, team)
122122

123123
var members []User
124-
err := utils.RequestCall(url, "GET", gh.token, &members, nil)
124+
err := utils.RequestCallWithRetry(url, "GET", gh.token, &members, nil)
125125
if err != nil {
126126
return nil, err
127127
}

.ci/magician/github/membership.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func IsCoreReviewer(user string) bool {
7676

7777
func isOrgMember(author, org, githubToken string) bool {
7878
url := fmt.Sprintf("https://api.github.com/orgs/%s/members/%s", org, author)
79-
err := utils.RequestCall(url, "GET", githubToken, nil, nil)
79+
err := utils.RequestCallWithRetry(url, "GET", githubToken, nil, nil)
8080

8181
return err == nil
8282
}

.ci/magician/github/set.go

+15-9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package github
1818
import (
1919
"fmt"
2020
utils "magician/utility"
21+
"strings"
2122
)
2223

2324
func (gh *Client) PostBuildStatus(prNumber, title, state, targetURL, commitSha string) error {
@@ -29,7 +30,7 @@ func (gh *Client) PostBuildStatus(prNumber, title, state, targetURL, commitSha s
2930
"target_url": targetURL,
3031
}
3132

32-
err := utils.RequestCall(url, "POST", gh.token, nil, postBody)
33+
err := utils.RequestCallWithRetry(url, "POST", gh.token, nil, postBody)
3334
if err != nil {
3435
return err
3536
}
@@ -46,7 +47,7 @@ func (gh *Client) PostComment(prNumber, comment string) error {
4647
"body": comment,
4748
}
4849

49-
err := utils.RequestCall(url, "POST", gh.token, nil, body)
50+
err := utils.RequestCallWithRetry(url, "POST", gh.token, nil, body)
5051
if err != nil {
5152
return err
5253
}
@@ -63,7 +64,7 @@ func (gh *Client) UpdateComment(prNumber, comment string, id int) error {
6364
"body": comment,
6465
}
6566

66-
err := utils.RequestCall(url, "PATCH", gh.token, nil, body)
67+
err := utils.RequestCallWithRetry(url, "PATCH", gh.token, nil, body)
6768
if err != nil {
6869
return err
6970
}
@@ -81,7 +82,7 @@ func (gh *Client) RequestPullRequestReviewers(prNumber string, reviewers []strin
8182
"team_reviewers": {},
8283
}
8384

84-
err := utils.RequestCall(url, "POST", gh.token, nil, body)
85+
err := utils.RequestCallWithRetry(url, "POST", gh.token, nil, body)
8586
if err != nil {
8687
return err
8788
}
@@ -97,7 +98,7 @@ func (gh *Client) AddLabels(prNumber string, labels []string) error {
9798
body := map[string][]string{
9899
"labels": labels,
99100
}
100-
err := utils.RequestCall(url, "POST", gh.token, nil, body)
101+
err := utils.RequestCallWithRetry(url, "POST", gh.token, nil, body)
101102

102103
if err != nil {
103104
return fmt.Errorf("failed to add %q labels: %s", labels, err)
@@ -109,7 +110,7 @@ func (gh *Client) AddLabels(prNumber string, labels []string) error {
109110

110111
func (gh *Client) RemoveLabel(prNumber, label string) error {
111112
url := fmt.Sprintf("https://api.github.com/repos/GoogleCloudPlatform/magic-modules/issues/%s/labels/%s", prNumber, label)
112-
err := utils.RequestCall(url, "DELETE", gh.token, nil, nil)
113+
err := utils.RequestCallWithRetry(url, "DELETE", gh.token, nil, nil)
113114

114115
if err != nil {
115116
return fmt.Errorf("failed to remove %s label: %s", label, err)
@@ -120,7 +121,7 @@ func (gh *Client) RemoveLabel(prNumber, label string) error {
120121

121122
func (gh *Client) CreateWorkflowDispatchEvent(workflowFileName string, inputs map[string]any) error {
122123
url := fmt.Sprintf("https://api.github.com/repos/GoogleCloudPlatform/magic-modules/actions/workflows/%s/dispatches", workflowFileName)
123-
err := utils.RequestCall(url, "POST", gh.token, nil, map[string]any{
124+
err := utils.RequestCallWithRetry(url, "POST", gh.token, nil, map[string]any{
124125
"ref": "main",
125126
"inputs": inputs,
126127
})
@@ -136,16 +137,21 @@ func (gh *Client) CreateWorkflowDispatchEvent(workflowFileName string, inputs ma
136137

137138
func (gh *Client) MergePullRequest(owner, repo, prNumber, commitSha string) error {
138139
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/pulls/%s/merge", owner, repo, prNumber)
139-
err := utils.RequestCall(url, "PUT", gh.token, nil, map[string]any{
140+
141+
err := utils.RequestCallWithRetry(url, "PUT", gh.token, nil, map[string]any{
140142
"merge_method": "squash",
141143
"sha": commitSha,
142144
})
143145

144146
if err != nil {
147+
// Check if the error is "Merge already in progress" (405)
148+
if strings.Contains(err.Error(), "Merge already in progress") {
149+
fmt.Printf("Pull request %s is already being merged\n", prNumber)
150+
return nil
151+
}
145152
return fmt.Errorf("failed to merge pull request: %s", err)
146153
}
147154

148155
fmt.Printf("Successfully merged pull request %s\n", prNumber)
149-
150156
return nil
151157
}

.ci/magician/utility/utils.go

+122-12
Original file line numberDiff line numberDiff line change
@@ -20,57 +20,167 @@ import (
2020
"encoding/json"
2121
"fmt"
2222
"io"
23+
"math"
2324
"net/http"
2425
"os"
26+
"time"
2527

2628
"golang.org/x/exp/slices"
2729
)
2830

29-
func RequestCall(url, method, credentials string, result any, body any) error {
31+
// retryConfig holds configuration for request retries
32+
type retryConfig struct {
33+
MaxRetries int // Maximum number of retry attempts
34+
InitialBackoff time.Duration // Initial backoff duration
35+
MaxBackoff time.Duration // Maximum backoff duration
36+
BackoffFactor float64 // Factor by which to multiply backoff on each retry
37+
RetryStatusCodes []int // HTTP status codes that should trigger a retry
38+
}
39+
40+
// defaultRetryConfig provides default retry configuration
41+
func defaultRetryConfig() retryConfig {
42+
return retryConfig{
43+
MaxRetries: 3,
44+
InitialBackoff: 5000 * time.Millisecond,
45+
MaxBackoff: 60 * time.Second,
46+
BackoffFactor: 2.0,
47+
RetryStatusCodes: []int{408, 429, 500, 502, 503, 504}, // Common retry status codes
48+
}
49+
}
50+
51+
// makeHTTPRequest performs the actual HTTP request and returns the response
52+
func makeHTTPRequest(url, method, credentials string, body any) (*http.Response, []byte, error) {
3053
client := &http.Client{}
3154
jsonBody, err := json.Marshal(body)
3255
if err != nil {
33-
return fmt.Errorf("error marshaling JSON: %s", err)
56+
return nil, nil, fmt.Errorf("error marshaling JSON: %s", err)
3457
}
3558
req, err := http.NewRequest(method, url, bytes.NewBuffer(jsonBody))
3659
if err != nil {
37-
return fmt.Errorf("error creating request: %s", err)
60+
return nil, nil, fmt.Errorf("error creating request: %s", err)
3861
}
3962
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", credentials))
4063
req.Header.Set("Content-Type", "application/json")
4164
req.Header.Set("Accept", "application/json")
4265
fmt.Println("")
4366
fmt.Println("request url: ", url)
44-
fmt.Println("request body: ", string(jsonBody)) // Convert to string
67+
fmt.Println("request body: ", string(jsonBody))
4568
fmt.Println("")
4669

4770
resp, err := client.Do(req)
4871
if err != nil {
49-
return err
72+
return nil, nil, err
5073
}
5174
defer resp.Body.Close()
5275

5376
respBodyBytes, err := io.ReadAll(resp.Body)
5477
if err != nil {
55-
return err
78+
return nil, nil, err
5679
}
5780

5881
fmt.Println("response status-code: ", resp.StatusCode)
59-
fmt.Println("response body: ", string(respBodyBytes)) // Convert to string
82+
fmt.Println("response body: ", string(respBodyBytes))
6083
fmt.Println("")
6184

62-
// Decode the response, if needed
85+
return resp, respBodyBytes, nil
86+
}
87+
88+
// processResponse handles the response and unmarshals it to the result if provided
89+
func processResponse(resp *http.Response, respBodyBytes []byte, result any) error {
90+
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
91+
var errorResponse struct {
92+
Message string `json:"message"`
93+
Error string `json:"error"`
94+
}
95+
96+
if err := json.Unmarshal(respBodyBytes, &errorResponse); err == nil {
97+
errorMsg := errorResponse.Message
98+
if errorMsg == "" {
99+
errorMsg = errorResponse.Error
100+
}
101+
102+
if errorMsg != "" {
103+
return fmt.Errorf("got code %d from server: %s", resp.StatusCode, errorMsg)
104+
}
105+
}
106+
107+
// Fall back to generic error if we couldn't parse the error message
108+
return fmt.Errorf("got code %d from server", resp.StatusCode)
109+
}
110+
111+
// If no error status code, decode the response if needed
63112
if result != nil {
64-
if err = json.Unmarshal(respBodyBytes, &result); err != nil {
113+
if err := json.Unmarshal(respBodyBytes, &result); err != nil {
65114
return err
66115
}
67116
}
68117

69-
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
70-
return fmt.Errorf("got code %d from server", resp.StatusCode)
118+
return nil
119+
}
120+
121+
// RequestCall makes a single HTTP request without retries
122+
func RequestCall(url, method, credentials string, result any, body any) error {
123+
resp, respBodyBytes, err := makeHTTPRequest(url, method, credentials, body)
124+
if err != nil {
125+
return err
71126
}
72127

73-
return nil
128+
return processResponse(resp, respBodyBytes, result)
129+
}
130+
131+
// shouldRetry determines if a retry should be attempted based on the status code
132+
func shouldRetry(statusCode int, retryConfig retryConfig) bool {
133+
return slices.Contains(retryConfig.RetryStatusCodes, statusCode)
134+
}
135+
136+
// calculateBackoff calculates the backoff duration for the current retry attempt
137+
func calculateBackoff(attempt int, config retryConfig) time.Duration {
138+
backoff := config.InitialBackoff * time.Duration(math.Pow(config.BackoffFactor, float64(attempt)))
139+
if backoff > config.MaxBackoff {
140+
backoff = config.MaxBackoff
141+
}
142+
return backoff
143+
}
144+
145+
// RequestCallWithRetry makes an HTTP request with retry capability
146+
func requestCallWithRetry(url, method, credentials string, result any, body any, config retryConfig) error {
147+
var lastErr error
148+
149+
for attempt := 0; attempt <= config.MaxRetries; attempt++ {
150+
// If this is a retry attempt, wait before trying again
151+
if attempt > 0 {
152+
backoff := calculateBackoff(attempt-1, config)
153+
fmt.Printf("Retry attempt %d after %v\n", attempt, backoff)
154+
time.Sleep(backoff)
155+
}
156+
157+
resp, respBodyBytes, err := makeHTTPRequest(url, method, credentials, body)
158+
if err != nil {
159+
lastErr = err
160+
continue // Network error, retry
161+
}
162+
163+
// Process the response
164+
err = processResponse(resp, respBodyBytes, result)
165+
if err != nil {
166+
lastErr = err
167+
168+
// Check if we should retry based on status code
169+
if shouldRetry(resp.StatusCode, config) {
170+
continue
171+
}
172+
}
173+
174+
// If we got here with no error, return success
175+
return err
176+
}
177+
178+
return fmt.Errorf("max retries exceeded: %w", lastErr)
179+
}
180+
181+
// RequestCallWithRetry is a convenience function that uses default retry settings
182+
func RequestCallWithRetry(url, method, credentials string, result any, body any) error {
183+
return requestCallWithRetry(url, method, credentials, result, body, defaultRetryConfig())
74184
}
75185

76186
func Removes(s1 []string, s2 []string) []string {

0 commit comments

Comments
 (0)