Skip to content

Commit afd7dd7

Browse files
Merge branch 'GoogleCloudPlatform:main' into extra-attributes-saml-support
2 parents ed4437f + 57616ae commit afd7dd7

File tree

880 files changed

+36785
-7028
lines changed

Some content is hidden

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

880 files changed

+36785
-7028
lines changed

.ci/containers/build-environment/Dockerfile

-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# Stage 1: Building Go dependencies
21
FROM golang:1.23-bullseye AS builder
32

43
# Set working directory
@@ -11,11 +10,6 @@ ADD "https://raw.githubusercontent.com/GoogleCloudPlatform/magic-modules/main/tp
1110
# Install the go dependencies
1211
RUN go mod download
1312

14-
# Stage 2: Creating the final imag
15-
FROM ruby:3.1-bullseye
16-
17-
# golang
18-
COPY --from=golang:1.23-bullseye /usr/local/go /usr/local/go
1913
ENV GOPATH /go
2014
ENV PATH /usr/local/go/bin:$PATH
2115
ENV PATH $GOPATH/bin:$PATH
@@ -38,12 +32,3 @@ RUN git config --global user.email "[email protected]"
3832

3933
RUN go install golang.org/x/tools/cmd/goimports@d088b475e3360caabc032aaee1dc66351d4e729a
4034
RUN go install github.com/github/[email protected]+incompatible
41-
42-
ADD "https://raw.githubusercontent.com/GoogleCloudPlatform/magic-modules/refs/heads/legacy-ruby/mmv1/Gemfile" Gemfile
43-
ADD "https://raw.githubusercontent.com/GoogleCloudPlatform/magic-modules/refs/heads/legacy-ruby/mmv1/Gemfile.lock" Gemfile.lock
44-
RUN bundle install
45-
RUN rm Gemfile Gemfile.lock
46-
47-
# Copy Go dependencies from builder stage
48-
COPY --from=builder /go/pkg /go/pkg
49-

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

+19
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: gcb-test-failure-ticket
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-test-failure-ticket-logs'
16+
availableSecrets:
17+
secretManager:
18+
- versionName: projects/673497134629/secrets/teamcity-token/versions/latest
19+
env: TEAMCITY_TOKEN

.ci/infra/terraform/main.tf

+9
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ module "project-services" {
298298
"logging.googleapis.com",
299299
"looker.googleapis.com",
300300
"managedidentities.googleapis.com",
301+
"managedkafka.googleapis.com",
301302
"memcache.googleapis.com",
302303
"memorystore.googleapis.com",
303304
"metastore.googleapis.com",
@@ -316,6 +317,7 @@ module "project-services" {
316317
"osconfig.googleapis.com",
317318
"oslogin.googleapis.com",
318319
"parallelstore.googleapis.com",
320+
"parametermanager.googleapis.com",
319321
"privateca.googleapis.com",
320322
"privilegedaccessmanager.googleapis.com",
321323
"pubsub.googleapis.com",
@@ -467,6 +469,13 @@ resource "google_project_iam_member" "compute_agent_encrypter_decrypter" {
467469
member = "serviceAccount:service-${google_project.proj.number}@compute-system.iam.gserviceaccount.com"
468470
}
469471

472+
# TestAccColabRuntime_colabRuntimeBasicExample
473+
# TestAccColabRuntime_colabRuntimeFullExample
474+
resource "google_project_iam_member" "colab_admin_permissions" {
475+
project = google_project.proj.project_id
476+
role = "roles/aiplatform.colabEnterpriseAdmin"
477+
member = "user:[email protected]"
478+
}
470479

471480
data "google_organization" "org2" {
472481
organization = var.org2_id

.ci/magician/cloudstorage/bucket.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2025 Google LLC. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package cloudstorage
17+
18+
import (
19+
"context"
20+
"fmt"
21+
"io"
22+
"os"
23+
"time"
24+
25+
"cloud.google.com/go/storage"
26+
)
27+
28+
func (gcs *Client) WriteToGCSBucket(bucket, object, filePath string) error {
29+
ctx := context.Background()
30+
31+
client, err := storage.NewClient(ctx)
32+
if err != nil {
33+
return fmt.Errorf("storage.NewClient: %v", err)
34+
}
35+
defer client.Close()
36+
37+
file, err := os.Open(filePath)
38+
if err != nil {
39+
return fmt.Errorf("os.Open: %w", err)
40+
}
41+
defer file.Close()
42+
43+
ctx, cancel := context.WithTimeout(ctx, time.Second*50)
44+
defer cancel()
45+
46+
writer := client.Bucket(bucket).Object(object).NewWriter(ctx)
47+
writer.ContentType = "application/json"
48+
49+
if _, err = io.Copy(writer, file); err != nil {
50+
return fmt.Errorf("io.Copy: %w", err)
51+
}
52+
if err := writer.Close(); err != nil {
53+
return fmt.Errorf("Writer.Close: %w", err)
54+
}
55+
56+
fmt.Printf("File uploaded to bucket %s as %s\n", bucket, object)
57+
return nil
58+
}

.ci/magician/cloudstorage/init.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2025 Google LLC. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package cloudstorage
17+
18+
type Client struct {
19+
}
20+
21+
func NewClient() *Client {
22+
return &Client{}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/*
2+
* Copyright 2025 Google LLC. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package cmd
17+
18+
import (
19+
"fmt"
20+
"magician/cloudstorage"
21+
"magician/provider"
22+
"magician/teamcity"
23+
utils "magician/utility"
24+
"os"
25+
"strconv"
26+
"strings"
27+
"time"
28+
29+
"github.com/spf13/cobra"
30+
)
31+
32+
const (
33+
NIGHTLY_DATA_BUCKET = "nightly-test-data"
34+
)
35+
36+
var cntsRequiredEnvironmentVariables = [...]string{
37+
"TEAMCITY_TOKEN",
38+
}
39+
40+
type TestInfo struct {
41+
Name string `json:"name"`
42+
Status string `json:"status"`
43+
Service string `json:"service"`
44+
ErrorMessage string `json:"error_message"`
45+
LogLink string `json"log_link`
46+
}
47+
48+
// collectNightlyTestStatusCmd represents the collectNightlyTestStatus command
49+
var collectNightlyTestStatusCmd = &cobra.Command{
50+
Use: "collect-nightly-test-status",
51+
Short: "Collects and stores nightly test status",
52+
Long: `This command collects nightly test status, stores the data in JSON files and upload the files to GCS.
53+
54+
55+
The command expects the following argument(s):
56+
1. Custom test date in YYYY-MM-DD format. default: ""(current time when the job is executed)
57+
58+
It then performs the following operations:
59+
1. Collects nightly test status of the execution day or the specified test date (if provided)
60+
2. Stores the collected data in JSON files
61+
3. Uploads the JSON files to GCS
62+
63+
The following environment variables are required:
64+
` + listCNTSRequiredEnvironmentVariables(),
65+
Args: cobra.ExactArgs(1),
66+
RunE: func(cmd *cobra.Command, args []string) error {
67+
env := make(map[string]string)
68+
for _, ev := range cntsRequiredEnvironmentVariables {
69+
val, ok := os.LookupEnv(ev)
70+
if !ok {
71+
return fmt.Errorf("did not provide %s environment variable", ev)
72+
}
73+
env[ev] = val
74+
}
75+
76+
tc := teamcity.NewClient(env["TEAMCITY_TOKEN"])
77+
gcs := cloudstorage.NewClient()
78+
79+
now := time.Now()
80+
81+
loc, err := time.LoadLocation("America/Los_Angeles")
82+
if err != nil {
83+
return fmt.Errorf("Error loading location: %s", err)
84+
}
85+
date := now.In(loc)
86+
customDate := args[0]
87+
// check if a specific date is provided
88+
if customDate != "" {
89+
parsedDate, err := time.Parse("2006-01-02", customDate) // input format YYYY-MM-DD
90+
// Set the time to 6pm PT
91+
date = time.Date(parsedDate.Year(), parsedDate.Month(), parsedDate.Day(), 18, 0, 0, 0, loc)
92+
if err != nil {
93+
return fmt.Errorf("invalid input time format: %w", err)
94+
}
95+
}
96+
97+
return execCollectNightlyTestStatus(date, tc, gcs)
98+
},
99+
}
100+
101+
func listCNTSRequiredEnvironmentVariables() string {
102+
var result string
103+
for i, ev := range cntsRequiredEnvironmentVariables {
104+
result += fmt.Sprintf("\t%2d. %s\n", i+1, ev)
105+
}
106+
return result
107+
}
108+
109+
func execCollectNightlyTestStatus(now time.Time, tc TeamcityClient, gcs CloudstorageClient) error {
110+
lastday := now.AddDate(0, 0, -1)
111+
formattedStartCut := lastday.Format(time.RFC3339)
112+
formattedFinishCut := now.Format(time.RFC3339)
113+
date := now.Format("2006-01-02")
114+
115+
err := createTestReport(provider.GA, tc, gcs, formattedStartCut, formattedFinishCut, date)
116+
if err != nil {
117+
return fmt.Errorf("Error getting GA nightly test status: %w", err)
118+
}
119+
120+
err = createTestReport(provider.Beta, tc, gcs, formattedStartCut, formattedFinishCut, date)
121+
if err != nil {
122+
return fmt.Errorf("Error getting Beta nightly test status: %w", err)
123+
}
124+
125+
return nil
126+
}
127+
128+
func createTestReport(pVersion provider.Version, tc TeamcityClient, gcs CloudstorageClient, formattedStartCut, formattedFinishCut, date string) error {
129+
// Get all service test builds
130+
builds, err := tc.GetBuilds(pVersion.TeamCityNightlyProjectName(), formattedFinishCut, formattedStartCut)
131+
if err != nil {
132+
return err
133+
}
134+
135+
var testInfoList []TestInfo
136+
for _, build := range builds.Builds {
137+
// Get service package name
138+
serviceName, err := convertServiceName(build.BuildTypeId)
139+
if err != nil {
140+
return fmt.Errorf("failed to convert test service name for %s: %v", build.BuildTypeId, err)
141+
}
142+
// Skip sweeper package
143+
if serviceName == "sweeper" {
144+
continue
145+
}
146+
147+
// Get test results
148+
serviceTestResults, err := tc.GetTestResults(build)
149+
if err != nil {
150+
return fmt.Errorf("failed to get test results: %v", err)
151+
}
152+
if len(serviceTestResults.TestResults) == 0 {
153+
fmt.Printf("Service %s has no tests\n", serviceName)
154+
continue
155+
}
156+
157+
for _, testResult := range serviceTestResults.TestResults {
158+
var errorMessage string
159+
// Get test debug log gcs link
160+
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)
161+
// Get concise error message
162+
if testResult.Status == "FAILURE" {
163+
errorMessage = convertErrorMessage(testResult.ErrorMessage)
164+
}
165+
testInfoList = append(testInfoList, TestInfo{
166+
Name: testResult.Name,
167+
Status: testResult.Status,
168+
Service: serviceName,
169+
ErrorMessage: errorMessage,
170+
LogLink: logLink,
171+
})
172+
}
173+
}
174+
175+
// Write test status data to a JSON file
176+
fmt.Println("Write test status")
177+
testStatusFileName := fmt.Sprintf("%s-%s.json", date, pVersion.String())
178+
err = utils.WriteToJson(testInfoList, testStatusFileName)
179+
if err != nil {
180+
return err
181+
}
182+
183+
// Upload test status data file to gcs bucket
184+
objectName := pVersion.String() + "/" + testStatusFileName
185+
err = gcs.WriteToGCSBucket(NIGHTLY_DATA_BUCKET, objectName, testStatusFileName)
186+
if err != nil {
187+
return err
188+
}
189+
190+
return nil
191+
}
192+
193+
// convertServiceName extracts service package name from teamcity build type id
194+
// input: TerraformProviders_GoogleCloud_GOOGLE_NIGHTLYTESTS_GOOGLE_PACKAGE_SECRETMANAGER
195+
// output: secretmanager
196+
func convertServiceName(servicePath string) (string, error) {
197+
idx := strings.LastIndex(servicePath, "_")
198+
199+
if idx != -1 {
200+
return strings.ToLower(servicePath[idx+1:]), nil
201+
}
202+
return "", fmt.Errorf("wrong service path format for %s", servicePath)
203+
}
204+
205+
// convertErrorMessage returns concise error message
206+
func convertErrorMessage(rawErrorMessage string) string {
207+
208+
startMarker := "------- Stdout: -------"
209+
endMarker := "------- Stderr: -------"
210+
startIndex := strings.Index(rawErrorMessage, startMarker)
211+
endIndex := strings.Index(rawErrorMessage, endMarker)
212+
213+
if startIndex != -1 {
214+
startIndex += len(startMarker)
215+
} else {
216+
startIndex = 0
217+
}
218+
219+
if endIndex == -1 {
220+
endIndex = len(rawErrorMessage)
221+
}
222+
223+
return strings.TrimSpace(rawErrorMessage[startIndex:endIndex])
224+
}
225+
226+
func init() {
227+
rootCmd.AddCommand(collectNightlyTestStatusCmd)
228+
}

0 commit comments

Comments
 (0)