Skip to content

Commit 94f500f

Browse files
committed
Merge branch 'main' of github.com:GoogleCloudPlatform/magic-modules into cluster-director-terraform
2 parents fc4cb0b + f746dc6 commit 94f500f

File tree

564 files changed

+24687
-2273
lines changed

Some content is hidden

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

564 files changed

+24687
-2273
lines changed

.ci/gcb-ingest-test-data.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
steps:
3+
- name: 'gcr.io/graphite-docker-images/go-plus'
4+
id: collect-nightly-test-status
5+
entrypoint: '/workspace/.ci/scripts/go-plus/magician/exec.sh'
6+
secretEnv: ["TEAMCITY_TOKEN"]
7+
args:
8+
- 'collect-nightly-test-status'
9+
- $_CUSTOM_DATE
10+
11+
timeout: 3600s
12+
options:
13+
machineType: 'N1_HIGHCPU_32'
14+
15+
logsBucket: 'gs://cloudbuild-ingest-test-data-logs'
16+
availableSecrets:
17+
secretManager:
18+
- versionName: projects/673497134629/secrets/teamcity-token/versions/latest
19+
env: TEAMCITY_TOKEN

.ci/gcb-test-failure-ticket.yaml

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
11
---
22
steps:
3-
- name: 'gcr.io/graphite-docker-images/go-plus'
4-
id: collect-nightly-test-status
5-
entrypoint: '/workspace/.ci/scripts/go-plus/magician/exec.sh'
6-
secretEnv: ["TEAMCITY_TOKEN"]
7-
args:
8-
- 'collect-nightly-test-status'
9-
- $_CUSTOM_DATE
103
- name: 'gcr.io/graphite-docker-images/go-plus'
114
id: create-test-failure-ticket
125
entrypoint: '/workspace/.ci/scripts/go-plus/magician/exec.sh'
136
secretEnv: ["GITHUB_TOKEN"]
14-
waitFor: ["collect-nightly-test-status"]
157
args:
168
- 'create-test-failure-ticket'
17-
- name: 'ubuntu'
18-
args: ['sleep', '120']
199
- name: 'gcr.io/graphite-docker-images/go-plus'
2010
id: manage-test-failure-ticket
2111
entrypoint: '/workspace/.ci/scripts/go-plus/magician/exec.sh'
@@ -31,7 +21,5 @@ options:
3121
logsBucket: 'gs://cloudbuild-test-failure-ticket-logs'
3222
availableSecrets:
3323
secretManager:
34-
- versionName: projects/673497134629/secrets/teamcity-token/versions/latest
35-
env: TEAMCITY_TOKEN
3624
- versionName: projects/673497134629/secrets/github-classic--repo-workflow/versions/latest
3725
env: GITHUB_TOKEN

.ci/infra/terraform/main.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ module "project-services" {
295295
"firebaseremoteconfig.googleapis.com",
296296
"firebaserules.googleapis.com",
297297
"firebasestorage.googleapis.com",
298+
"firebasevertexai.googleapis.com",
298299
"firestore.googleapis.com",
299300
"firestorekeyvisualizer.googleapis.com",
300301
"gkebackup.googleapis.com",

.ci/magician/cmd/collect_nightly_test_status.go

Lines changed: 185 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import (
2121
"magician/provider"
2222
"magician/teamcity"
2323
utils "magician/utility"
24+
"net/url"
2425
"os"
26+
"regexp"
2527
"strconv"
2628
"strings"
2729
"time"
@@ -45,6 +47,7 @@ type TestInfo struct {
4547
Resource string `json:"resource"`
4648
CommitSha string `json:"commit_sha"`
4749
ErrorMessage string `json:"error_message"`
50+
ErrorType string `json:"error_type"`
4851
LogLink string `json:"log_link"`
4952
ProviderVersion string `json:"provider_version"`
5053
QueuedDate time.Time `json:"queued_date"`
@@ -84,24 +87,27 @@ var collectNightlyTestStatusCmd = &cobra.Command{
8487
tc := teamcity.NewClient(env["TEAMCITY_TOKEN"])
8588
gcs := cloudstorage.NewClient()
8689

87-
now := time.Now()
88-
8990
loc, err := time.LoadLocation("America/Los_Angeles")
9091
if err != nil {
9192
return fmt.Errorf("Error loading location: %s", err)
9293
}
93-
date := now.In(loc)
94+
95+
now := time.Now().In(loc)
96+
year, month, day := now.Date()
97+
9498
customDate := args[0]
9599
// check if a specific date is provided
96100
if customDate != "" {
97101
parsedDate, err := time.Parse("2006-01-02", customDate) // input format YYYY-MM-DD
98-
// Set the time to 7pm PT
99-
date = time.Date(parsedDate.Year(), parsedDate.Month(), parsedDate.Day(), 19, 0, 0, 0, loc)
100102
if err != nil {
101103
return fmt.Errorf("invalid input time format: %w", err)
102104
}
105+
year, month, day = parsedDate.Date()
103106
}
104107

108+
// Set the time to 7pm PT
109+
date := time.Date(year, month, day, 19, 0, 0, 0, loc)
110+
105111
return execCollectNightlyTestStatus(date, tc, gcs)
106112
},
107113
}
@@ -134,10 +140,40 @@ func execCollectNightlyTestStatus(now time.Time, tc TeamcityClient, gcs Cloudsto
134140
}
135141

136142
func createTestReport(pVersion provider.Version, tc TeamcityClient, gcs CloudstorageClient, formattedStartCut, formattedFinishCut, date string) error {
143+
144+
baseLocator := fmt.Sprintf("count:500,project:%s,branch:refs/heads/nightly-test,queuedDate:(date:%s,condition:before),queuedDate:(date:%s,condition:after)", pVersion.TeamCityNightlyProjectName(), formattedFinishCut, formattedStartCut)
145+
fields := "build(id,buildTypeId,buildConfName,webUrl,number,queuedDate,startDate,finishDate)"
146+
params := url.Values{}
147+
148+
// Check Queued Builds
149+
params.Set("locator", fmt.Sprintf("%s,state:queued", baseLocator))
150+
queuedBuilds, err := tc.GetBuilds(params)
151+
if err != nil {
152+
return fmt.Errorf("failed to get queued builds: %w", err)
153+
}
154+
if len(queuedBuilds.Builds) > 0 {
155+
fmt.Printf("%s Test unfinished: there are still %d builds queued.\n", strings.ToUpper(pVersion.String()), len(queuedBuilds.Builds))
156+
return nil
157+
}
158+
159+
// Check Running Builds
160+
params.Set("locator", fmt.Sprintf("%s,state:running,tag:cron-trigger", baseLocator))
161+
params.Set("fields", fields)
162+
runningBuilds, err := tc.GetBuilds(params)
163+
if err != nil {
164+
return fmt.Errorf("failed to get running builds: %w", err)
165+
}
166+
if len(runningBuilds.Builds) > 0 {
167+
fmt.Printf("%s Test unfinished: there are still %d builds running.\n", strings.ToUpper(pVersion.String()), len(runningBuilds.Builds))
168+
return nil
169+
}
170+
137171
// Get all service test builds
138-
builds, err := tc.GetBuilds(pVersion.TeamCityNightlyProjectName(), formattedFinishCut, formattedStartCut)
172+
params.Set("locator", fmt.Sprintf("%s,state:finished,tag:cron-trigger", baseLocator))
173+
params.Set("fields", fields)
174+
builds, err := tc.GetBuilds(params)
139175
if err != nil {
140-
return err
176+
return fmt.Errorf("failed to get finished builds: %w", err)
141177
}
142178

143179
var testInfoList []TestInfo
@@ -164,12 +200,14 @@ func createTestReport(pVersion provider.Version, tc TeamcityClient, gcs Cloudsto
164200

165201
for _, testResult := range serviceTestResults.TestResults {
166202
var errorMessage string
203+
var errorType string
167204
// Get test debug log gcs link
168205
logLink := fmt.Sprintf("https://storage.cloud.google.com/teamcity-logs/nightly/%s/%s/%s/debug-%s-%s-%s-%s.txt", pVersion.TeamCityNightlyProjectName(), date, build.Number, pVersion.ProviderName(), build.Number, strconv.Itoa(build.Id), testResult.Name)
169206
// Get concise error message for failed and skipped tests
170207
// Skipped tests have a status of "UNKNOWN" on TC
171208
if testResult.Status == "FAILURE" || testResult.Status == "UNKNOWN" {
172209
errorMessage = convertErrorMessage(testResult.ErrorMessage)
210+
errorType = categorizeError(errorMessage)
173211
}
174212

175213
queuedTime, err := time.Parse(tcTimeFormat, build.QueuedDate)
@@ -192,6 +230,7 @@ func createTestReport(pVersion provider.Version, tc TeamcityClient, gcs Cloudsto
192230
Resource: convertTestNameToResource(testResult.Name),
193231
CommitSha: build.Number,
194232
ErrorMessage: errorMessage,
233+
ErrorType: errorType,
195234
LogLink: logLink,
196235
ProviderVersion: strings.ToUpper(pVersion.String()),
197236
Duration: testResult.Duration,
@@ -253,6 +292,145 @@ func convertErrorMessage(rawErrorMessage string) string {
253292
return strings.TrimSpace(rawErrorMessage[startIndex:endIndex])
254293
}
255294

295+
var (
296+
reSubnetNotReady = regexp.MustCompile(`The resource '[^']+/subnetworks/[^']+' is not ready`)
297+
reApiEnv = regexp.MustCompile(`has not been used in project (ci-test-project-188019|1067888929963|ci-test-project-nightly-ga|594424405950|ci-test-project-nightly-beta|653407317329|tf-vcr-private|808590572184) before or it is disabled`)
298+
reAttrSet = regexp.MustCompile(`Attribute '[^']+' expected to be set`)
299+
reQuotaLimit = regexp.MustCompile(`Quota limit '[^']+' has been exceeded`)
300+
reGoogleApi4xx = regexp.MustCompile(`googleapi: Error 4\d\d`)
301+
reGoogleApi5xx = regexp.MustCompile(`googleapi: Error 5\d\d`)
302+
reGoogleApiGeneric = regexp.MustCompile(`googleapi: Error`)
303+
)
304+
305+
func categorizeError(errMsg string) string {
306+
if strings.Contains(errMsg, "Error code 13") {
307+
return "Error code 13"
308+
}
309+
if strings.Contains(errMsg, "Precondition check failed") {
310+
return "Precondition check failed"
311+
}
312+
313+
// Diff Category
314+
if strings.Contains(errMsg, "After applying this test step, the plan was not empty") ||
315+
strings.Contains(errMsg, "After applying this test step and performing a `terraform refresh`") ||
316+
strings.Contains(errMsg, "Expected a non-empty plan, but got an empty plan") ||
317+
strings.Contains(errMsg, "error: Check failed") {
318+
return "Diff"
319+
}
320+
321+
if strings.Contains(errMsg, "timeout while waiting for state") {
322+
return "Operation timeout"
323+
}
324+
325+
// Regex: Subnetwork not ready
326+
if reSubnetNotReady.MatchString(errMsg) {
327+
return "Subnetwork not ready"
328+
}
329+
330+
// ImportStateVerify Category
331+
if strings.Contains(errMsg, "ImportStateVerify attributes not equivalent") ||
332+
strings.Contains(errMsg, "Cannot import non-existent remote object") ||
333+
strings.Contains(errMsg, "Error: Unexpected Import Identifier") {
334+
return "ImportStateVerify"
335+
}
336+
337+
// Deprecated (Case-insensitive check)
338+
if strings.Contains(strings.ToLower(errMsg), "deprecated") {
339+
return "Deprecated"
340+
}
341+
342+
if strings.Contains(errMsg, "Provider produced inconsistent result after apply") &&
343+
strings.Contains(errMsg, "Root object was present, but now absent") {
344+
return "Root object was present, but now absent"
345+
}
346+
347+
if strings.Contains(errMsg, "Provider produced inconsistent final plan") {
348+
return "Provider produced inconsistent final plan"
349+
}
350+
351+
// API Enablement
352+
if reApiEnv.MatchString(errMsg) {
353+
return "API enablement (Test environment)"
354+
}
355+
if strings.Contains(errMsg, "has not been used in project") && strings.Contains(errMsg, "before or it is disabled") {
356+
return "API enablement (Created project)"
357+
}
358+
359+
if strings.Contains(errMsg, "does not have required permissions") {
360+
return "Permissions"
361+
}
362+
if strings.Contains(errMsg, "bootstrap_iam_test_utils.go") {
363+
return "Bootstrapping"
364+
}
365+
366+
// Bad Config Category
367+
if strings.Contains(errMsg, "Inconsistent dependency lock file") ||
368+
strings.Contains(errMsg, "Invalid resource type") ||
369+
strings.Contains(errMsg, "Blocks of type") && strings.Contains(errMsg, "are not expected here") ||
370+
strings.Contains(errMsg, "Conflicting configuration arguments") ||
371+
reAttrSet.MatchString(errMsg) {
372+
return "Bad config"
373+
}
374+
375+
// Quota Category
376+
if strings.Contains(errMsg, "Quota exhausted") ||
377+
strings.Contains(errMsg, "Quota exceeded") ||
378+
strings.Contains(errMsg, "You do not have quota") ||
379+
reQuotaLimit.MatchString(errMsg) {
380+
return "Quota"
381+
}
382+
383+
if strings.Contains(errMsg, "does not have enough resources available") {
384+
return "Resource availability"
385+
}
386+
387+
// API Create/Read/Update/Delete
388+
if strings.Contains(errMsg, "Error: Error waiting to create") ||
389+
strings.Contains(errMsg, "Error: Error waiting for Create") ||
390+
strings.Contains(errMsg, "Error: Error waiting for creating") ||
391+
strings.Contains(errMsg, "Error: Error creating") ||
392+
strings.Contains(errMsg, "was created in the error state") ||
393+
strings.Contains(errMsg, "Error: Error changing instance status after creation:") {
394+
return "API Create"
395+
}
396+
397+
if strings.Contains(errMsg, "Error: Error reading") {
398+
return "API Read"
399+
}
400+
401+
if strings.Contains(errMsg, "Error setting IAM policy") ||
402+
strings.Contains(errMsg, "Error applying IAM policy") {
403+
return "API IAM"
404+
}
405+
406+
if strings.Contains(errMsg, "Error: Error waiting for Updating") ||
407+
strings.Contains(errMsg, "Error: Error updating") {
408+
return "API Update"
409+
}
410+
411+
if strings.Contains(errMsg, "Error: Error waiting for Deleting") ||
412+
strings.Contains(errMsg, "Error running post-test destroy") {
413+
return "API Delete"
414+
}
415+
416+
// Google API Errors (Order matters: check specific codes before generic)
417+
if reGoogleApi4xx.MatchString(errMsg) {
418+
return "API (4xx)"
419+
}
420+
if reGoogleApi5xx.MatchString(errMsg) {
421+
return "API (5xx)"
422+
}
423+
if reGoogleApiGeneric.MatchString(errMsg) ||
424+
strings.Contains(errMsg, "Error: Error when reading or editing") ||
425+
strings.Contains(errMsg, "Error: Error waiting") ||
426+
strings.Contains(errMsg, "unable to queue the operation") ||
427+
strings.Contains(errMsg, "Error waiting for Switching runtime") {
428+
return "API (Other)"
429+
}
430+
431+
return "Other"
432+
}
433+
256434
func init() {
257435
rootCmd.AddCommand(collectNightlyTestStatusCmd)
258436
}

0 commit comments

Comments
 (0)