diff --git a/.ci/containers/build-environment/Dockerfile b/.ci/containers/build-environment/Dockerfile index 7866dc9608cb..0a23367f577b 100644 --- a/.ci/containers/build-environment/Dockerfile +++ b/.ci/containers/build-environment/Dockerfile @@ -39,8 +39,8 @@ RUN git config --global user.email "magic-modules@google.com" RUN go install golang.org/x/tools/cmd/goimports@d088b475e3360caabc032aaee1dc66351d4e729a RUN go install github.com/github/hub@v2.11.2+incompatible -ADD "https://raw.githubusercontent.com/GoogleCloudPlatform/magic-modules/main/mmv1/Gemfile" Gemfile -ADD "https://raw.githubusercontent.com/GoogleCloudPlatform/magic-modules/main/mmv1/Gemfile.lock" Gemfile.lock +ADD "https://raw.githubusercontent.com/GoogleCloudPlatform/magic-modules/refs/heads/legacy-ruby/mmv1/Gemfile" Gemfile +ADD "https://raw.githubusercontent.com/GoogleCloudPlatform/magic-modules/refs/heads/legacy-ruby/mmv1/Gemfile.lock" Gemfile.lock RUN bundle install RUN rm Gemfile Gemfile.lock diff --git a/.ci/infra/terraform/README.md b/.ci/infra/terraform/README.md index 0da75e6d6ee3..6ae724e1bb2c 100644 --- a/.ci/infra/terraform/README.md +++ b/.ci/infra/terraform/README.md @@ -48,6 +48,7 @@ After applying this configuration: - Enroll in Cloud Armor Managed Protection Plus tier - Add Cloud Identity Premium Plan to the Google Workspace domain - Perform the Privileged Access Manager set-up https://pantheon.corp.google.com/iam-admin/pam/setup +- (Org only) Enroll the org in the Premium tier of Security Control Center Quotas that will need to be adjusted to support all tests: - Project quota for the new service account diff --git a/.ci/infra/terraform/main.tf b/.ci/infra/terraform/main.tf index 0b93ae88bb5f..a5f0adf8291c 100644 --- a/.ci/infra/terraform/main.tf +++ b/.ci/infra/terraform/main.tf @@ -159,6 +159,12 @@ resource "google_organization_iam_member" "sa_securitycenter_bigquery_exports_ed member = google_service_account.sa.member } +resource "google_organization_iam_member" "sa_principal_access_boundary_admin" { + org_id = data.google_organization.org.org_id + role = "roles/iam.principalAccessBoundaryAdmin" + member = google_service_account.sa.member +} + resource "google_billing_account_iam_member" "sa_master_billing_admin" { billing_account_id = data.google_billing_account.master_acct.id role = "roles/billing.admin" diff --git a/.ci/magician/cmd/interfaces.go b/.ci/magician/cmd/interfaces.go index ff4b1a2db97c..71f0f05218e8 100644 --- a/.ci/magician/cmd/interfaces.go +++ b/.ci/magician/cmd/interfaces.go @@ -24,11 +24,13 @@ type GithubClient interface { GetPullRequests(state, base, sort, direction string) ([]github.PullRequest, error) GetPullRequestRequestedReviewers(prNumber string) ([]github.User, error) GetPullRequestPreviousReviewers(prNumber string) ([]github.User, error) + GetPullRequestComments(prNumber string) ([]github.PullRequestComment, error) GetUserType(user string) github.UserType GetTeamMembers(organization, team string) ([]github.User, error) MergePullRequest(owner, repo, prNumber, commitSha string) error PostBuildStatus(prNumber, title, state, targetURL, commitSha string) error PostComment(prNumber, comment string) error + UpdateComment(prNumber, comment string, id int) error RequestPullRequestReviewers(prNumber string, reviewers []string) error AddLabels(prNumber string, labels []string) error RemoveLabel(prNumber, label string) error diff --git a/.ci/magician/cmd/mock_github_test.go b/.ci/magician/cmd/mock_github_test.go index 08b71599babb..ac34f36154ef 100644 --- a/.ci/magician/cmd/mock_github_test.go +++ b/.ci/magician/cmd/mock_github_test.go @@ -22,12 +22,13 @@ import ( ) type mockGithub struct { - pullRequest github.PullRequest - userType github.UserType - requestedReviewers []github.User - previousReviewers []github.User - teamMembers map[string][]github.User - calledMethods map[string][][]any + pullRequest github.PullRequest + userType github.UserType + requestedReviewers []github.User + previousReviewers []github.User + pullRequestComments []github.PullRequestComment + teamMembers map[string][]github.User + calledMethods map[string][][]any } func (m *mockGithub) GetPullRequest(prNumber string) (github.PullRequest, error) { @@ -55,6 +56,11 @@ func (m *mockGithub) GetPullRequestPreviousReviewers(prNumber string) ([]github. return m.previousReviewers, nil } +func (m *mockGithub) GetPullRequestComments(prNumber string) ([]github.PullRequestComment, error) { + m.calledMethods["GetPullRequestComments"] = append(m.calledMethods["GetPullRequestComments"], []any{prNumber}) + return m.pullRequestComments, nil +} + func (m *mockGithub) GetTeamMembers(organization, team string) ([]github.User, error) { m.calledMethods["GetTeamMembers"] = append(m.calledMethods["GetTeamMembers"], []any{organization, team}) if team == "" { @@ -73,6 +79,11 @@ func (m *mockGithub) PostComment(prNumber string, comment string) error { return nil } +func (m *mockGithub) UpdateComment(prNumber, comment string, id int) error { + m.calledMethods["UpdateComment"] = append(m.calledMethods["UpdateComment"], []any{prNumber, comment, id}) + return nil +} + func (m *mockGithub) AddLabels(prNumber string, labels []string) error { m.calledMethods["AddLabels"] = append(m.calledMethods["AddLabels"], []any{prNumber, labels}) return nil diff --git a/.ci/magician/cmd/reassign_reviewer.go b/.ci/magician/cmd/reassign_reviewer.go new file mode 100644 index 000000000000..70259d7ebb8b --- /dev/null +++ b/.ci/magician/cmd/reassign_reviewer.go @@ -0,0 +1,126 @@ +/* +* Copyright 2024 Google LLC. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. + */ +package cmd + +import ( + "errors" + "fmt" + "magician/github" + "os" + + "github.com/spf13/cobra" +) + +// reassignReviewerCmd represents the reassignReviewer command +var reassignReviewerCmd = &cobra.Command{ + Use: "reassign-reviewer PR_NUMBER [REVIEWER]", + Short: "Reassigns primary reviewer to the given reviewer or a random reviewer if none given", + Long: `This command reassigns reviewers when invoked via a comment on a pull request. + + The command expects the following PR details as arguments: + 1. PR_NUMBER + 2. REVIEWER (optional) + + + It then performs the following operations: + 1. Updates the reviewer comment to reflect the new primary reviewer. + 2. Requests a review from the new primary reviewer. + `, + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + prNumber := args[0] + fmt.Println("PR Number: ", prNumber) + + githubToken, ok := os.LookupEnv("GITHUB_TOKEN") + if !ok { + return fmt.Errorf("did not provide GITHUB_TOKEN environment variable") + } + gh := github.NewClient(githubToken) + var newPrimaryReviewer string + if len(args) > 1 { + newPrimaryReviewer = args[1] + } + return execReassignReviewer(prNumber, newPrimaryReviewer, gh) + }, +} + +func execReassignReviewer(prNumber, newPrimaryReviewer string, gh GithubClient) error { + comments, err := gh.GetPullRequestComments(prNumber) + if err != nil { + return err + } + + reviewerComment, currentReviewer := github.FindReviewerComment(comments) + + if currentReviewer == "" { + fmt.Println("No reviewer comment found, creating one") + newPrimaryReviewer, err = createReviewComment(prNumber, newPrimaryReviewer, gh) + if err != nil { + return err + } + } else { + fmt.Println("Reassigning to random reviewer") + newPrimaryReviewer, err = updateReviewComment(prNumber, currentReviewer, newPrimaryReviewer, reviewerComment.ID, gh) + if err != nil { + return err + } + } + + fmt.Println("New primary reviewer is ", newPrimaryReviewer) + + err = gh.RequestPullRequestReviewers(prNumber, []string{newPrimaryReviewer}) + if err != nil { + return err + } + + return nil +} + +func createReviewComment(prNumber, newPrimaryReviewer string, gh GithubClient) (string, error) { + if newPrimaryReviewer == "" { + newPrimaryReviewer = github.GetRandomReviewer() + } + + if newPrimaryReviewer == "" { + return "", errors.New("no primary reviewer found") + } + + err := gh.PostComment(prNumber, github.FormatReviewerComment(newPrimaryReviewer)) + if err != nil { + return "", err + } + return newPrimaryReviewer, nil +} + +func updateReviewComment(prNumber, currentReviewer, newPrimaryReviewer string, reviewerCommentID int, gh GithubClient) (string, error) { + if newPrimaryReviewer == "" { + newPrimaryReviewer = github.GetNewRandomReviewer(currentReviewer) + } + + if currentReviewer == newPrimaryReviewer { + return newPrimaryReviewer, fmt.Errorf("primary reviewer is already %s", newPrimaryReviewer) + } + + err := gh.UpdateComment(prNumber, github.FormatReviewerComment(newPrimaryReviewer), reviewerCommentID) + if err != nil { + return "", err + } + return newPrimaryReviewer, nil +} + +func init() { + rootCmd.AddCommand(reassignReviewerCmd) +} diff --git a/.ci/magician/cmd/request_reviewer.go b/.ci/magician/cmd/request_reviewer.go index e3981467535c..f019644bc888 100644 --- a/.ci/magician/cmd/request_reviewer.go +++ b/.ci/magician/cmd/request_reviewer.go @@ -31,11 +31,6 @@ var requestReviewerCmd = &cobra.Command{ The command expects the following pull request details as arguments: 1. PR Number - 2. Commit SHA - 3. Branch Name - 4. Head Repo URL - 5. Head Branch - 6. Base Branch It then performs the following operations: 1. Determines the author of the pull request diff --git a/.ci/magician/cmd/scheduled_pr_reminders_test.go b/.ci/magician/cmd/scheduled_pr_reminders_test.go index 760cf6e9fb60..8b4dfff458a4 100644 --- a/.ci/magician/cmd/scheduled_pr_reminders_test.go +++ b/.ci/magician/cmd/scheduled_pr_reminders_test.go @@ -805,7 +805,7 @@ func TestFormatReminderComment(t *testing.T) { &github.User{Login: github.String("other-reviewer")}, }, }, - state: waitingForMerge, + state: waitingForMerge, sinceDays: 5, expectedStrings: []string{ "waiting for merge for 1 week", diff --git a/.ci/magician/cmd/templates/vcr/record_replay.tmpl b/.ci/magician/cmd/templates/vcr/record_replay.tmpl index 522a56c698a2..9a8b2859ac6a 100644 --- a/.ci/magician/cmd/templates/vcr/record_replay.tmpl +++ b/.ci/magician/cmd/templates/vcr/record_replay.tmpl @@ -1,11 +1,19 @@ {{- if gt (len .RecordingResult.PassedTests) 0 -}} {{color "green" "Tests passed during RECORDING mode:"}} -{{range .RecordingResult.PassedTests}}`{{.}}`[[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-{{$.PRNumber}}/artifacts/{{$.BuildID}}/recording/{{.}}.log)] +{{range .RecordingResult.PassedTests -}} +`{{.}}` {{/* remove trailing whitespace */ -}} + [[Debug log](https://storage.cloud.google.com/{{$.LogBucket}}/{{$.Version}}/refs/heads/{{$.Head}}/artifacts/{{$.BuildID}}/recording/{{.}}.log)] +{{/* remove trailing whitespace */ -}} {{end}} -{{- if gt (len .ReplayingAfterRecordingResult.FailedTests ) 0 -}} +{{- if gt (len .ReplayingAfterRecordingResult.FailedTests ) 0 }} + {{color "red" "Tests failed when rerunning REPLAYING mode:"}} -{{range .ReplayingAfterRecordingResult.FailedTests}}`{{.}}`[[Error message](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-{{$.PRNumber}}/artifacts/{{$.BuildID}}/build-log/replaying_build_after_recording/{{.}}_replaying_test.log)] [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-{{$.PRNumber}}/artifacts/{{$.BuildID}}/replaying_after_recording/{{.}}.log)] +{{range .ReplayingAfterRecordingResult.FailedTests -}} +`{{.}}` {{/* remove trailing whitespace */ -}} + [[Error message](https://storage.cloud.google.com/{{$.LogBucket}}/{{$.Version}}/refs/heads/{{$.Head}}/artifacts/{{$.BuildID}}/build-log/replaying_build_after_recording/{{.}}_replaying_test.log)] {{/* remove trailing whitespace */ -}} + [[Debug log](https://storage.cloud.google.com/{{$.LogBucket}}/{{$.Version}}/refs/heads/{{$.Head}}/artifacts/{{$.BuildID}}/replaying_after_recording/{{.}}.log)] +{{/* remove trailing whitespace */ -}} {{end}} Tests failed due to non-determinism or randomness when the VCR replayed the response after the HTTP request was made. @@ -20,12 +28,19 @@ Please fix these to complete your PR. If you believe these test failures to be i {{if gt (len .RecordingResult.FailedTests) 0 -}} {{color "red" "Tests failed during RECORDING mode:"}} -{{range .RecordingResult.FailedTests}}`{{.}}`[[Error message](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-{{$.PRNumber}}/artifacts/{{$.BuildID}}/build-log/recording_build/{{.}}_recording_test.log)] [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-{{$.PRNumber}}/artifacts/{{$.BuildID}}/recording/{{.}}.log)] +{{range .RecordingResult.FailedTests -}} +`{{.}}` {{/* remove trailing whitespace */ -}} + [[Error message](https://storage.cloud.google.com/{{$.LogBucket}}/{{$.Version}}/refs/heads/{{$.Head}}/artifacts/{{$.BuildID}}/build-log/recording_build/{{.}}_recording_test.log)] {{/* remove trailing whitespace */ -}} + [[Debug log](https://storage.cloud.google.com/{{$.LogBucket}}/{{$.Version}}/refs/heads/{{$.Head}}/artifacts/{{$.BuildID}}/recording/{{.}}.log)] +{{/* remove trailing whitespace */ -}} {{end}} {{end}} {{- /* end of if gt (len .RecordingResult.FailedTests) 0 */ -}} -{{if .HasTerminatedTests}}{{color "red" "Several tests got terminated during RECORDING mode."}}{{end}} +{{if .HasTerminatedTests}}{{color "red" "Several tests terminated during RECORDING mode."}}{{end}} + {{if .RecordingErr}}{{color "red" "Errors occurred during RECORDING mode. Please fix them to complete your PR."}}{{end}} + {{if .AllRecordingPassed}}{{color "green" "All tests passed!"}}{{end}} -View the [build log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-{{.PRNumber}}/artifacts/{{.BuildID}}/build-log/recording_test.log) or the [debug log](https://console.cloud.google.com/storage/browser/ci-vcr-logs/beta/refs/heads/auto-pr-{{.PRNumber}}/artifacts/{{.BuildID}}/recording) for each test +View the [build log](https://storage.cloud.google.com/{{.LogBucket}}/{{.Version}}/refs/heads/{{.Head}}/artifacts/{{.BuildID}}/build-log/recording_test.log) {{/* remove trailing whitespace */ -}} +or the [debug log](https://console.cloud.google.com/storage/browser/{{.LogBucket}}/{{.Version}}/refs/heads/{{.Head}}/artifacts/{{.BuildID}}/recording) for each test diff --git a/.ci/magician/cmd/templates/vcr/vcr_cassettes_update_replaying.tmpl b/.ci/magician/cmd/templates/vcr/vcr_cassettes_update_replaying.tmpl index 5c0f891c3e7c..922d55efed0d 100644 --- a/.ci/magician/cmd/templates/vcr/vcr_cassettes_update_replaying.tmpl +++ b/.ci/magician/cmd/templates/vcr/vcr_cassettes_update_replaying.tmpl @@ -17,7 +17,7 @@ Affected tests list: {{- end}} {{if .ReplayingErr}} ################################# -Errors occurred during REPLAYING mode: {{.ReplayingErr}}. +Errors occurred during REPLAYING mode. ################################# {{- end}} {{if .AllReplayingPassed}} diff --git a/.ci/magician/cmd/templates/vcr/without_replay_failed_tests.tmpl b/.ci/magician/cmd/templates/vcr/without_replay_failed_tests.tmpl index 215c5f3ef5ce..256d1652fc4f 100644 --- a/.ci/magician/cmd/templates/vcr/without_replay_failed_tests.tmpl +++ b/.ci/magician/cmd/templates/vcr/without_replay_failed_tests.tmpl @@ -4,4 +4,4 @@ {{color "green" "All tests passed!"}} {{- end}} -View the [build log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-{{.PRNumber}}/artifacts/{{.BuildID}}/build-log/replaying_test.log) +View the [build log](https://storage.cloud.google.com/{{.LogBucket}}/{{.Version}}/refs/heads/{{.Head}}/artifacts/{{.BuildID}}/build-log/replaying_test.log) diff --git a/.ci/magician/cmd/templates_test.go b/.ci/magician/cmd/templates_test.go index 2de0e832daaf..503eb90fb49e 100644 --- a/.ci/magician/cmd/templates_test.go +++ b/.ci/magician/cmd/templates_test.go @@ -24,16 +24,16 @@ func TestColor(t *testing.T) { want: "🟡 Test text", }, { - name: "green", + name: "green", color: "green", - text: "Test text", - want: "🟢 Test text", + text: "Test text", + want: "🟢 Test text", }, { - name: "unsupported color", + name: "unsupported color", color: "mauve", - text: "Test text", - want: "Test text", + text: "Test text", + want: "Test text", }, { name: "empty color", diff --git a/.ci/magician/cmd/test_terraform_vcr.go b/.ci/magician/cmd/test_terraform_vcr.go index 519d1ef1a39e..47b1a70d3a90 100644 --- a/.ci/magician/cmd/test_terraform_vcr.go +++ b/.ci/magician/cmd/test_terraform_vcr.go @@ -72,7 +72,9 @@ type withReplayFailedTests struct { type withoutReplayFailedTests struct { ReplayingErr error - PRNumber string + LogBucket string + Version string + Head string BuildID string } @@ -82,14 +84,26 @@ type recordReplay struct { HasTerminatedTests bool RecordingErr error AllRecordingPassed bool - PRNumber string + LogBucket string + Version string + Head string BuildID string } var testTerraformVCRCmd = &cobra.Command{ Use: "test-terraform-vcr", Short: "Run vcr tests for affected packages", - Long: `This command runs on new pull requests to replay VCR cassettes and re-record failing cassettes.`, + Long: `This command runs on new pull requests to replay VCR cassettes and re-record failing cassettes. + +It expects the following arguments: + 1. PR number + 2. SHA of the latest magic-modules commit + 3. Build ID + 4. Project ID where Cloud Builds are located + 5. Build step number + +The following environment variables are required: +` + listTTVEnvironmentVariables(), RunE: func(cmd *cobra.Command, args []string) error { env := make(map[string]string, len(ttvEnvironmentVariables)) for _, ev := range ttvEnvironmentVariables { @@ -133,6 +147,14 @@ var testTerraformVCRCmd = &cobra.Command{ }, } +func listTTVEnvironmentVariables() string { + var result string + for i, ev := range ttvEnvironmentVariables { + result += fmt.Sprintf("\t%2d. %s\n", i+1, ev) + } + return result +} + func execTestTerraformVCR(prNumber, mmCommitSha, buildID, projectID, buildStep, baseBranch string, gh GithubClient, rnr ExecRunner, ctlr *source.Controller, vt *vcr.Tester) error { newBranch := "auto-pr-" + prNumber oldBranch := newBranch + "-old" @@ -173,7 +195,7 @@ func execTestTerraformVCR(prNumber, mmCommitSha, buildID, projectID, buildStep, return fmt.Errorf("error changing to tpgbRepo dir: %w", err) } - services, runFullVCR := modifiedPackages(tpgbRepo.ChangedFiles) + services, runFullVCR := modifiedPackages(tpgbRepo.ChangedFiles, provider.Beta) if len(services) == 0 && !runFullVCR { fmt.Println("Skipping tests: No go files or test fixtures changed") return nil @@ -189,7 +211,7 @@ func execTestTerraformVCR(prNumber, mmCommitSha, buildID, projectID, buildStep, return fmt.Errorf("error posting pending status: %w", err) } - replayingResult, testDirs, replayingErr := runReplaying(runFullVCR, services, vt) + replayingResult, testDirs, replayingErr := runReplaying(runFullVCR, provider.Beta, services, vt) testState := "success" if replayingErr != nil { testState = "failure" @@ -316,7 +338,9 @@ func execTestTerraformVCR(prNumber, mmCommitSha, buildID, projectID, buildStep, RecordingErr: recordingErr, HasTerminatedTests: hasTerminatedTests, AllRecordingPassed: allRecordingPassed, - PRNumber: prNumber, + LogBucket: "ci-vcr-logs", + Version: provider.Beta.String(), + Head: newBranch, BuildID: buildID, } recordReplayComment, err := formatRecordReplay(recordReplayData) @@ -330,7 +354,8 @@ func execTestTerraformVCR(prNumber, mmCommitSha, buildID, projectID, buildStep, } else { // len(replayingResult.FailedTests) == 0 withoutReplayFailedTestsData := withoutReplayFailedTests{ ReplayingErr: replayingErr, - PRNumber: prNumber, + Head: newBranch, + LogBucket: "ci-vcr-logs", BuildID: buildID, } withoutReplayFailedTestsComment, err := formatWithoutReplayFailedTests(withoutReplayFailedTestsData) @@ -393,7 +418,7 @@ func notRunTests(gaDiff, betaDiff string, result vcr.Result) ([]string, []string return notRunBeta, notRunGa } -func modifiedPackages(changedFiles []string) (map[string]struct{}, bool) { +func modifiedPackages(changedFiles []string, version provider.Version) (map[string]struct{}, bool) { var goFiles []string for _, line := range changedFiles { if strings.HasSuffix(line, ".go") || strings.Contains(line, "test-fixtures") || strings.HasSuffix(line, "go.mod") || strings.HasSuffix(line, "go.sum") { @@ -403,10 +428,10 @@ func modifiedPackages(changedFiles []string) (map[string]struct{}, bool) { services := make(map[string]struct{}) runFullVCR := false for _, file := range goFiles { - if strings.HasPrefix(file, "google-beta/services/") { + if strings.HasPrefix(file, version.ProviderName()+"/services/") { fileParts := strings.Split(file, "/") services[fileParts[2]] = struct{}{} - } else if file == "google-beta/provider/provider_mmv1_resources.go" || file == "google-beta/provider/provider_dcl_resources.go" { + } else if file == version.ProviderName()+"/provider/provider_mmv1_resources.go" || file == version.ProviderName()+"/provider/provider_dcl_resources.go" { fmt.Println("ignore changes in ", file) } else { fmt.Println("run full tests ", file) @@ -417,7 +442,7 @@ func modifiedPackages(changedFiles []string) (map[string]struct{}, bool) { return services, runFullVCR } -func runReplaying(runFullVCR bool, services map[string]struct{}, vt *vcr.Tester) (vcr.Result, []string, error) { +func runReplaying(runFullVCR bool, version provider.Version, services map[string]struct{}, vt *vcr.Tester) (vcr.Result, []string, error) { result := vcr.Result{} var testDirs []string var replayingErr error @@ -425,17 +450,17 @@ func runReplaying(runFullVCR bool, services map[string]struct{}, vt *vcr.Tester) fmt.Println("runReplaying: full VCR tests") result, replayingErr = vt.Run(vcr.RunOptions{ Mode: vcr.Replaying, - Version: provider.Beta, + Version: version, }) } else if len(services) > 0 { fmt.Printf("runReplaying: %d specific services: %v\n", len(services), services) for service := range services { - servicePath := "./" + filepath.Join("google-beta", "services", service) + servicePath := "./" + filepath.Join(version.ProviderName(), "services", service) testDirs = append(testDirs, servicePath) fmt.Println("run VCR tests in ", service) serviceResult, serviceReplayingErr := vt.Run(vcr.RunOptions{ Mode: vcr.Replaying, - Version: provider.Beta, + Version: version, TestDirs: []string{servicePath}, }) if serviceReplayingErr != nil { @@ -475,8 +500,8 @@ func init() { func formatComment(fileName string, tmplText string, data any) (string, error) { funcs := template.FuncMap{ - "join": strings.Join, - "add": func(i, j int) int { return i + j }, + "join": strings.Join, + "add": func(i, j int) int { return i + j }, "color": color, } tmpl, err := template.New(fileName).Funcs(funcs).Parse(tmplText) diff --git a/.ci/magician/cmd/test_terraform_vcr_test.go b/.ci/magician/cmd/test_terraform_vcr_test.go index 2110da3bbb72..79672641c07f 100644 --- a/.ci/magician/cmd/test_terraform_vcr_test.go +++ b/.ci/magician/cmd/test_terraform_vcr_test.go @@ -9,6 +9,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" + "magician/provider" "magician/vcr" ) @@ -64,7 +65,7 @@ func TestModifiedPackagesFromDiffs(t *testing.T) { all: false, }, } { - if packages, all := modifiedPackages(tc.diffs); !reflect.DeepEqual(packages, tc.packages) { + if packages, all := modifiedPackages(tc.diffs, provider.Beta); !reflect.DeepEqual(packages, tc.packages) { t.Errorf("Unexpected packages found for test %s: %v, expected %v", tc.name, packages, tc.packages) } else if all != tc.all { t.Errorf("Unexpected value for all packages for test %s: %v, expected %v", tc.name, all, tc.all) @@ -467,8 +468,8 @@ func TestWithReplayFailedTests(t *testing.T) { func TestWithoutReplayFailedTests(t *testing.T) { tests := []struct { - name string - data withoutReplayFailedTests + name string + data withoutReplayFailedTests wantContains []string }{ { @@ -476,7 +477,9 @@ func TestWithoutReplayFailedTests(t *testing.T) { data: withoutReplayFailedTests{ ReplayingErr: fmt.Errorf("some error"), BuildID: "build-123", - PRNumber: "123", + Head: "auto-pr-123", + LogBucket: "ci-vcr-logs", + Version: provider.Beta.String(), }, wantContains: []string{ color("red", "Errors occurred during REPLAYING mode. Please fix them to complete your PR."), @@ -486,8 +489,10 @@ func TestWithoutReplayFailedTests(t *testing.T) { { name: "without replay error", data: withoutReplayFailedTests{ - BuildID: "build-123", - PRNumber: "123", + BuildID: "build-123", + Head: "auto-pr-123", + LogBucket: "ci-vcr-logs", + Version: provider.Beta.String(), }, wantContains: []string{ color("green", "All tests passed!"), @@ -512,8 +517,8 @@ func TestWithoutReplayFailedTests(t *testing.T) { func TestRecordReplay(t *testing.T) { tests := []struct { - name string - data recordReplay + name string + data recordReplay wantContains []string }{ { @@ -530,24 +535,27 @@ func TestRecordReplay(t *testing.T) { HasTerminatedTests: true, RecordingErr: fmt.Errorf("some error"), BuildID: "build-123", - PRNumber: "123", + LogBucket: "ci-vcr-logs", + Version: provider.Beta.String(), + Head: "auto-pr-123", }, wantContains: []string{ color("green", "Tests passed during RECORDING mode:"), - "`a`[[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/a.log)]", - "`b`[[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/b.log)]", - "`c`[[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/c.log)]", + "`a` [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/a.log)]", + "`b` [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/b.log)]", + "`c` [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/c.log)]", color("red", "Tests failed when rerunning REPLAYING mode:"), - "`b`[[Error message](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/replaying_build_after_recording/b_replaying_test.log)] [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/replaying_after_recording/b.log)]", - "`c`[[Error message](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/replaying_build_after_recording/c_replaying_test.log)] [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/replaying_after_recording/c.log)]", + "`b` [[Error message](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/replaying_build_after_recording/b_replaying_test.log)] [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/replaying_after_recording/b.log)]", + "`c` [[Error message](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/replaying_build_after_recording/c_replaying_test.log)] [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/replaying_after_recording/c.log)]", "Tests failed due to non-determinism or randomness when the VCR replayed the response after the HTTP request was made.", "Please fix these to complete your PR. If you believe these test failures to be incorrect or unrelated to your change, or if you have any questions, please raise the concern with your reviewer.", color("red", "Tests failed during RECORDING mode:"), - "`d`[[Error message](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/recording_build/d_recording_test.log)] [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/d.log)]", - "`e`[[Error message](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/recording_build/e_recording_test.log)] [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/e.log)]", - color("red", "Several tests got terminated during RECORDING mode."), + "`d` [[Error message](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/recording_build/d_recording_test.log)] [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/d.log)]", + "`e` [[Error message](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/recording_build/e_recording_test.log)] [[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/e.log)]", + color("red", "Several tests terminated during RECORDING mode."), "Errors occurred during RECORDING mode. Please fix them to complete your PR.", - "View the [build log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/recording_test.log) or the [debug log](https://console.cloud.google.com/storage/browser/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording) for each test", + "[build log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/recording_test.log)", + "[debug log](https://console.cloud.google.com/storage/browser/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording)", }, }, { @@ -561,16 +569,22 @@ func TestRecordReplay(t *testing.T) { }, AllRecordingPassed: true, BuildID: "build-123", - PRNumber: "123", + Head: "auto-pr-123", + Version: provider.Beta.String(), + LogBucket: "ci-vcr-logs", }, wantContains: []string{ color("green", "Tests passed during RECORDING mode:"), - "`a`[[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/a.log)]", - "`b`[[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/b.log)]", - "`c`[[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/c.log)]", + "`a`", + "[[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/a.log)]", + "`b`", + "[[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/b.log)]", + "`c`", + "[[Debug log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording/c.log)]", color("green", "No issues found for passed tests after REPLAYING rerun."), color("green", "All tests passed!"), - "View the [build log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/recording_test.log) or the [debug log](https://console.cloud.google.com/storage/browser/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording) for each test", + "[build log](https://storage.cloud.google.com/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/build-log/recording_test.log)", + "[debug log](https://console.cloud.google.com/storage/browser/ci-vcr-logs/beta/refs/heads/auto-pr-123/artifacts/build-123/recording)", }, }, } @@ -582,7 +596,7 @@ func TestRecordReplay(t *testing.T) { } for _, wc := range tc.wantContains { if !strings.Contains(got, wc) { - t.Errorf("formatRecordReplay() returned %q, which does not contain %q", got, wc) + t.Errorf("formatRecordReplay() return value:\n%s\n\ndoes not contain %q", got, wc) } } }) diff --git a/.ci/magician/cmd/test_tgc_test.go b/.ci/magician/cmd/test_tgc_test.go index 5e67dcca8e59..e3f89fb98445 100644 --- a/.ci/magician/cmd/test_tgc_test.go +++ b/.ci/magician/cmd/test_tgc_test.go @@ -28,7 +28,7 @@ func TestExecTestTGC(t *testing.T) { execTestTGC("sha1", "pr1", gh) method := "CreateWorkflowDispatchEvent" - expected := [][]any{{"test-tgc.yml", map[string]any{"tpgb-branch": "auto-pr-pr1", "tgc-branch": "auto-pr-pr1", "pr-number":"pr1", "owner": "modular-magician", "repo": "terraform-google-conversion", "sha": "sha1"}}} + expected := [][]any{{"test-tgc.yml", map[string]any{"tpgb-branch": "auto-pr-pr1", "tgc-branch": "auto-pr-pr1", "pr-number": "pr1", "owner": "modular-magician", "repo": "terraform-google-conversion", "sha": "sha1"}}} if calls, ok := gh.calledMethods[method]; !ok { t.Fatal("Workflow dispatch event not created") } else if !reflect.DeepEqual(calls, expected) { diff --git a/.ci/magician/cmd/test_tpg.go b/.ci/magician/cmd/test_tpg.go index 12c0b12c441c..8468aa490c50 100644 --- a/.ci/magician/cmd/test_tpg.go +++ b/.ci/magician/cmd/test_tpg.go @@ -17,9 +17,9 @@ package cmd import ( "fmt" + "github.com/spf13/cobra" "magician/github" "os" - "github.com/spf13/cobra" ) type ttGithub interface { @@ -73,7 +73,7 @@ func execTestTPG(version, commit, pr string, gh ttGithub) error { commitShaOrBranchUpstream := string(content) - if commitShaOrBranchUpstream == ""{ + if commitShaOrBranchUpstream == "" { // fall back to branch if commit SHA can't be found commitShaOrBranchUpstream = "auto-pr-" + pr } diff --git a/.ci/magician/cmd/vcr_cassette_update_test.go b/.ci/magician/cmd/vcr_cassette_update_test.go index beb96423869f..0d391e0f5d45 100644 --- a/.ci/magician/cmd/vcr_cassette_update_test.go +++ b/.ci/magician/cmd/vcr_cassette_update_test.go @@ -49,7 +49,7 @@ func TestFormatVCRCassettesUpdateReplaying(t *testing.T) { "#################################", "", "#################################", - "Errors occurred during REPLAYING mode: some error.", + "Errors occurred during REPLAYING mode.", "#################################", }, "\n", diff --git a/.ci/magician/exec/runner.go b/.ci/magician/exec/runner.go index c1bdc79525c5..2de978885e98 100644 --- a/.ci/magician/exec/runner.go +++ b/.ci/magician/exec/runner.go @@ -103,6 +103,9 @@ func (ar *Runner) ReadFile(name string) (string, error) { // Note: This is not used yet. func (ar *Runner) AppendFile(name, data string) error { f, err := os.OpenFile(ar.abs(name), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err == os.ErrNotExist { + return ar.WriteFile(name, data) + } if err != nil { return fmt.Errorf("error opening file %s: %v", name, err) } diff --git a/.ci/magician/github/get.go b/.ci/magician/github/get.go index 4b1409460639..d41439d0bf8d 100644 --- a/.ci/magician/github/get.go +++ b/.ci/magician/github/get.go @@ -18,6 +18,7 @@ package github import ( "fmt" utils "magician/utility" + "time" ) type User struct { @@ -38,6 +39,13 @@ type PullRequest struct { MergeCommitSha string `json:"merge_commit_sha"` } +type PullRequestComment struct { + User User `json:"user"` + Body string `json:"body"` + ID int `json:"id"` + CreatedAt time.Time `json:"created_at"` +} + func (gh *Client) GetPullRequest(prNumber string) (PullRequest, error) { url := fmt.Sprintf("https://api.github.com/repos/GoogleCloudPlatform/magic-modules/issues/%s", prNumber) @@ -98,6 +106,17 @@ func (gh *Client) GetPullRequestPreviousReviewers(prNumber string) ([]User, erro return result, nil } +func (gh *Client) GetPullRequestComments(prNumber string) ([]PullRequestComment, error) { + url := fmt.Sprintf("https://api.github.com/repos/GoogleCloudPlatform/magic-modules/issues/%s/comments", prNumber) + + var comments []PullRequestComment + err := utils.RequestCall(url, "GET", gh.token, &comments, nil) + if err != nil { + return nil, err + } + return comments, nil +} + func (gh *Client) GetTeamMembers(organization, team string) ([]User, error) { url := fmt.Sprintf("https://api.github.com/orgs/%s/teams/%s/members", organization, team) diff --git a/.ci/magician/github/membership.go b/.ci/magician/github/membership.go index d3694ccd7255..295e8a909022 100644 --- a/.ci/magician/github/membership.go +++ b/.ci/magician/github/membership.go @@ -108,6 +108,14 @@ func GetRandomReviewer() string { return reviewer } +// Return a random reviewer other than the old reviewer +func GetNewRandomReviewer(oldReviewer string) string { + availableReviewers := AvailableReviewers() + availableReviewers = utils.Removes(availableReviewers, []string{oldReviewer}) + reviewer := availableReviewers[rand.Intn(len(availableReviewers))] + return reviewer +} + func AvailableReviewers() []string { return available(time.Now(), maps.Keys(reviewerRotation), onVacationReviewers) } diff --git a/.ci/magician/github/membership_data.go b/.ci/magician/github/membership_data.go index caca9267c716..09e38fb4e0d9 100644 --- a/.ci/magician/github/membership_data.go +++ b/.ci/magician/github/membership_data.go @@ -18,6 +18,7 @@ var ( "hao-nan-li": {}, "NickElliot": {}, "BBBmau": {}, + "SirGitsalot": {}, } // This is for new team members who are onboarding @@ -87,8 +88,8 @@ var ( }, { id: "trodge", - startDate: newDate(2024, 8, 24, pdtLoc), - endDate: newDate(2024, 9, 2, pdtLoc), + startDate: newDate(2024, 10, 23, pdtLoc), + endDate: newDate(2024, 10, 25, pdtLoc), }, { id: "roaks3", diff --git a/.ci/magician/github/reviewer_assignment.go b/.ci/magician/github/reviewer_assignment.go index d06415bab810..9a36f7a079fc 100644 --- a/.ci/magician/github/reviewer_assignment.go +++ b/.ci/magician/github/reviewer_assignment.go @@ -17,6 +17,7 @@ package github import ( "fmt" + "regexp" "strings" "text/template" @@ -66,3 +67,32 @@ func FormatReviewerComment(newPrimaryReviewer string) string { }) return sb.String() } + +var reviewerCommentRegex = regexp.MustCompile("@(?P[^,]*), a repository maintainer, has been assigned to review your changes.") + +// FindReviewerComment returns the comment which mentions the current primary reviewer and the reviewer's login, +// or an empty comment and empty string if no such comment is found. +// comments should only include comments by the magician in the current PR. +func FindReviewerComment(comments []PullRequestComment) (PullRequestComment, string) { + var newestComment PullRequestComment + var currentReviewer string + for _, comment := range comments { + if !newestComment.CreatedAt.IsZero() && comment.CreatedAt.Before(newestComment.CreatedAt) { + // Skip comments older than the newest comment. + continue + } + names := reviewerCommentRegex.SubexpNames() + matches := reviewerCommentRegex.FindStringSubmatch(comment.Body) + if len(matches) < len(names) { + // Skip comments that don't match regex. + continue + } + for i, name := range names { + if name == "reviewer" { + newestComment = comment + currentReviewer = matches[i] + } + } + } + return newestComment, currentReviewer +} diff --git a/.ci/magician/github/reviewer_assignment_test.go b/.ci/magician/github/reviewer_assignment_test.go index 6a2cb33b6a25..31e57901e0aa 100644 --- a/.ci/magician/github/reviewer_assignment_test.go +++ b/.ci/magician/github/reviewer_assignment_test.go @@ -19,6 +19,7 @@ import ( "fmt" "strings" "testing" + "time" "golang.org/x/exp/slices" ) @@ -143,3 +144,66 @@ func TestFormatReviewerComment(t *testing.T) { } } + +func TestFindReviewerComment(t *testing.T) { + cases := map[string]struct { + Comments []PullRequestComment + ExpectReviewer string + ExpectCommentID int + }{ + "no reviewer comment": { + Comments: []PullRequestComment{ + { + Body: "this is not a reviewer comment", + }, + }, + ExpectReviewer: "", + ExpectCommentID: 0, + }, + "reviewer comment": { + Comments: []PullRequestComment{ + { + Body: FormatReviewerComment("trodge"), + ID: 1234, + }, + }, + ExpectReviewer: "trodge", + ExpectCommentID: 1234, + }, + "multiple reviewer comments": { + Comments: []PullRequestComment{ + { + Body: FormatReviewerComment("trodge"), + ID: 1234, + CreatedAt: time.Date(2023, 12, 1, 0, 0, 0, 0, time.UTC), + }, + { + Body: FormatReviewerComment("c2thorn"), + ID: 5678, + CreatedAt: time.Date(2023, 12, 3, 0, 0, 0, 0, time.UTC), + }, + { + Body: FormatReviewerComment("melinath"), + ID: 91011, + CreatedAt: time.Date(2023, 12, 2, 0, 0, 0, 0, time.UTC), + }, + }, + ExpectReviewer: "c2thorn", + ExpectCommentID: 5678, + }, + } + + for tn, tc := range cases { + tc := tc + t.Run(tn, func(t *testing.T) { + t.Parallel() + comment, reviewer := FindReviewerComment(tc.Comments) + if reviewer != tc.ExpectReviewer { + t.Errorf("wanted reviewer to be %s; got %s", tc.ExpectReviewer, reviewer) + } + if comment.ID != tc.ExpectCommentID { + t.Errorf("wanted comment ID to be %d; got %d", tc.ExpectCommentID, comment.ID) + } + }) + } +} diff --git a/.ci/magician/github/set.go b/.ci/magician/github/set.go index fcc430bb0c11..30a7bc8ed864 100644 --- a/.ci/magician/github/set.go +++ b/.ci/magician/github/set.go @@ -56,6 +56,23 @@ func (gh *Client) PostComment(prNumber, comment string) error { return nil } +func (gh *Client) UpdateComment(prNumber, comment string, id int) error { + url := fmt.Sprintf("https://api.github.com/repos/GoogleCloudPlatform/magic-modules/issues/comments/%d", id) + + body := map[string]string{ + "body": comment, + } + + err := utils.RequestCall(url, "PATCH", gh.token, nil, body) + if err != nil { + return err + } + + fmt.Printf("Successfully updated comment %d in pull request %s\n", id, prNumber) + + return nil +} + func (gh *Client) RequestPullRequestReviewers(prNumber string, reviewers []string) error { url := fmt.Sprintf("https://api.github.com/repos/GoogleCloudPlatform/magic-modules/pulls/%s/requested_reviewers", prNumber) diff --git a/.ci/magician/provider/version.go b/.ci/magician/provider/version.go index 6372ff7fd297..4c914187b464 100644 --- a/.ci/magician/provider/version.go +++ b/.ci/magician/provider/version.go @@ -6,7 +6,7 @@ const ( None Version = iota GA Beta - Alpha + Private ) const NumVersions = 3 @@ -17,12 +17,24 @@ func (v Version) String() string { return "ga" case Beta: return "beta" - case Alpha: + case Private: return "alpha" } return "unknown" } +func (v Version) ProviderName() string { + switch v { + case GA: + return "google" + case Beta: + return "google-beta" + case Private: + return "google-private" + } + return "unknown" +} + func (v Version) BucketPath() string { if v == GA { return "" @@ -36,7 +48,7 @@ func (v Version) RepoName() string { return "terraform-provider-google" case Beta: return "terraform-provider-google-beta" - case Alpha: + case Private: return "terraform-next" } return "unknown" diff --git a/.ci/magician/vcr/tester.go b/.ci/magician/vcr/tester.go index 471f4448bbcd..36bcb6200f0e 100644 --- a/.ci/magician/vcr/tester.go +++ b/.ci/magician/vcr/tester.go @@ -135,8 +135,7 @@ func (vt *Tester) SetRepoPath(version provider.Version, repoPath string) { // Fetch the cassettes for the current version if not already fetched. // Should be run from the base dir. func (vt *Tester) FetchCassettes(version provider.Version, baseBranch, head string) error { - _, ok := vt.cassettePaths[version] - if ok { + if _, cassettesAlreadyFetched := vt.cassettePaths[version]; cassettesAlreadyFetched { return nil } cassettePath := filepath.Join(vt.baseDir, "cassettes", version.String()) @@ -195,7 +194,7 @@ type RunOptions struct { // Run the vcr tests in the given mode and provider version and return the result. // This will overwrite any existing logs for the given mode and version. func (vt *Tester) Run(opt RunOptions) (Result, error) { - logPath, err := vt.getLogPath(opt.Mode, opt.Version) + logPath, err := vt.makeLogPath(opt.Mode, opt.Version) if err != nil { return Result{}, err } @@ -299,7 +298,7 @@ func (vt *Tester) Run(opt RunOptions) (Result, error) { } func (vt *Tester) RunParallel(opt RunOptions) (Result, error) { - logPath, err := vt.getLogPath(opt.Mode, opt.Version) + logPath, err := vt.makeLogPath(opt.Mode, opt.Version) if err != nil { return Result{}, err } @@ -427,7 +426,7 @@ func (vt *Tester) runInParallel(mode Mode, version provider.Version, testDir, te wg.Done() } -func (vt *Tester) getLogPath(mode Mode, version provider.Version) (string, error) { +func (vt *Tester) makeLogPath(mode Mode, version provider.Version) (string, error) { lgky := logKey{mode, version} logPath, ok := vt.logPaths[lgky] if !ok { diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9701442ec327..4b325dbf3699 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,36 +1,16 @@ - - - - **Release Note Template for Downstream PRs (will be copied)** +See [Write release notes](https://googlecloudplatform.github.io/magic-modules/contribute/release-notes/) for guidance. + ```release-note:REPLACEME ``` diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/changelog-checker.yml index de1022f77292..5baa82edfc3c 100644 --- a/.github/workflows/changelog-checker.yml +++ b/.github/workflows/changelog-checker.yml @@ -4,7 +4,7 @@ permissions: read-all on: pull_request: - types: [opened, edited] + types: [opened, edited, synchronize] jobs: check: diff --git a/.github/workflows/gofmt.yml b/.github/workflows/gofmt.yml new file mode 100644 index 000000000000..be8ce984c6c4 --- /dev/null +++ b/.github/workflows/gofmt.yml @@ -0,0 +1,41 @@ +name: gofmt + +permissions: + contents: read + +on: + pull_request: + paths: + - "**/*.go" + +jobs: + run-gofmt: + runs-on: ubuntu-22.04 + steps: + - name: Checkout Repository + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + with: + fetch-depth: 0 + - name: Merge base branch + id: pull_request + run: | + git config user.name "modular-magician" + git config user.email "magic-modules@google.com" + git fetch origin ${{ github.base_ref }} # Fetch the base branch + git merge --no-ff origin/${{ github.base_ref }} # Merge with the base branch + - name: Set up Go + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + cache: false + go-version: '^1.21' + - name: gofmt + run: | + GOFMT_OUTPUT="$(gofmt -l .)" + # Currently no simple way to get a non-0 exit code directly from + # gofmt + if [ -n "$GOFMT_OUTPUT" ]; then + echo "The following files are not formatted properly:" >&2 + echo "$GOFMT_OUTPUT" >&2 + exit 1 + fi + echo "gofmt-output=gofmt success" >> "$GITHUB_OUTPUT" diff --git a/.gitignore b/.gitignore index 4aa28d6971f5..ffda5ea7240f 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,7 @@ tpgtools/serialization.go # ignore magician binary .ci/magician/magician + +# ignore leftover testlogs and cassettes +.ci/magician/testlogs +.ci/magician/cassettes diff --git a/docs/content/develop/permadiff.md b/docs/content/develop/diffs.md similarity index 91% rename from docs/content/develop/permadiff.md rename to docs/content/develop/diffs.md index 2b4bb3b1d8ad..55d9c6e68b8f 100644 --- a/docs/content/develop/permadiff.md +++ b/docs/content/develop/diffs.md @@ -1,15 +1,21 @@ --- -title: "Fix a permadiff" +title: "Fix diffs" weight: 60 +aliases: + - /develop/permadiff --- -# Fix a permadiff +# Fix diffs -Permadiffs are an extremely common class of errors that users experience. They manifest as diffs at plan time on fields that a user has not modified in their configuration. They can also show up as test failures with the error message: "After applying this test step, the plan was not empty." +This page outlines best practices for fixing various kinds of diffs that can show up at plan time. These will often show up as test failures with the text: `After applying this test step, the plan was not empty.`. They can also show up for users at plan time, on fields that a user has not modified in their configuration. If the diff does not go away even after running `terraform apply` more than once with the same configuration, the diff is called a "permadiff". -In a general sense, permadiffs are caused by the API returning a different value for the field than what the user sent, which causes Terraform to try to re-send the same request, which gets the same response, which continues to result in the user seeing a diff. In general, APIs that return exactly what the user sent are more friendly for Terraform or other declarative tooling. However, many GCP APIs normalize inputs, have server-side defaults that are returned to the user, do not return all the fields set on a resource, or return data in a different format in some other way. +In a general sense, diffs appear when the API response is detected by Terraform to be different than what is in the user's configuration. This can happen for a number of reasons, including: -This page outlines best practices for working around various types of permadiffs in the `google` and `google-beta` providers. +- API returns a normalized version of the input +- API returns server-side defaults if the field is unset +- API does not return all the fields set on a resource (for example, secrets) + +The sections below describe in more detail how to address a number of different causes of diffs. ## API returns default value for unset field {#default} diff --git a/docs/content/develop/field-reference.md b/docs/content/develop/field-reference.md index c7e4eab84567..6946af19495f 100644 --- a/docs/content/develop/field-reference.md +++ b/docs/content/develop/field-reference.md @@ -81,7 +81,7 @@ state. See for more information. Sensitive fields are often not returned by the API (because they are sensitive). -In this case, the field will also need to use [`ignore_read` or a `custom_flatten` function]({{< ref "/develop/permadiff#ignore_read" >}}). +In this case, the field will also need to use [`ignore_read` or a `custom_flatten` function]({{< ref "/develop/diffs#ignore_read" >}}). Example: @@ -156,6 +156,10 @@ for an "empty" value vs no value for a particular field - for example, boolean fields that have an API-side default of true. `send_empty_value` and `default_from_api` cannot both be true on the same field. +Due to a [bug](https://github.com/hashicorp/terraform-provider-google/issues/13201), +NestedObject fields will currently be sent as `null` if unset (rather than being +omitted.) + Example: ```yaml @@ -178,6 +182,21 @@ Example: - nested_object.0.nested_field ``` +### `required_with` +Specifies a list of fields (excluding the current field) that must all be specified +if at least one is specified. Must be set separately on all listed fields. Not supported within +[lists of nested objects](https://github.com/hashicorp/terraform-plugin-sdk/issues/470#issue-630928923). + +Example: + +```yaml +- name: 'fieldOne' + type: String + required_with: + - field_two + - nested_object.0.nested_field +``` + ### `exactly_one_of` Specifies a list of fields (including the current field) of which exactly one must be set. Must be set separately on all listed fields. Not supported within @@ -215,7 +234,7 @@ Example: Specifies the name of a [diff suppress function](https://developer.hashicorp.com/terraform/plugin/sdkv2/schemas/schema-behaviors#diffsuppressfunc) to use for this field. In many cases, a [custom flattener](https://googlecloudplatform.github.io/magic-modules/develop/custom-code/#custom_flatten) is preferred because it will allow the user to see a clearer diff when the field actually is being changed. See -[Fix a permadiff]({{< ref "/develop/permadiff.md" >}}) for more information and best practices. +[Fix diffs]({{< ref "/develop/diffs" >}}) for more information and best practices. The function specified can be a [provider-specific function](https://github.com/hashicorp/terraform-provider-google-beta/blob/main/google-beta/tpgresource/common_diff_suppress.go) diff --git a/docs/content/develop/promote-to-ga.md b/docs/content/develop/promote-to-ga.md index 04eaf0bd4f54..91c64fb3252e 100644 --- a/docs/content/develop/promote-to-ga.md +++ b/docs/content/develop/promote-to-ga.md @@ -7,7 +7,7 @@ weight: 50 This document describes how to promote an existing resource or field that uses MMv1 and/or handwritten code from the `google-beta` provider to the `google` (also known as "GA") provider. -Handwritten code (including `custom_code`) commonly uses "version guards" in the form of `{{- if ne $.TargetVersionName "ga" -}}...{{- end }}` to wrap code that is beta-specific, which need to be removed during promotion. +Handwritten code (including `custom_code`) commonly uses "version guards" in the form of `{{- if ne $.TargetVersionName "ga" }}...{{- end }}` to wrap code that is beta-specific, which need to be removed during promotion. For more information about types of resources and the generation process overall, see [How Magic Modules works]({{< ref "/get-started/how-magic-modules-works.md" >}}). @@ -31,7 +31,7 @@ For more information about types of resources and the generation process overall 1. Remove `min_version: 'beta'` from the resource's or field's configuration in `ResourceName.yaml`. 2. If necessary, remove version guards from resource-level `custom_code`. 3. Add `min_version: 'beta'` on any fields or subfields that should not be promoted. -4. If necessary, add `{{- if ne $.TargetVersionName "ga" -}}...{{- end }} ` version guards to resource-level `custom_code` that should not be promoted. +4. If necessary, add `{{- if ne $.TargetVersionName "ga" }}...{{- end }} ` version guards to resource-level `custom_code` that should not be promoted. {{< /tab >}} {{< tab "Handwritten" >}} 1. Remove version guards from the resource's implementation for any functionality being promoted. Be sure to check: @@ -40,7 +40,7 @@ For more information about types of resources and the generation process overall - For top-level fields, the resource's `Create`, `Update`, and `Read` methods - For other fields, expanders and flatteners - Any other resource-specific code -2. Add `{{- if ne $.TargetVersionName "ga" -}}...{{- end }}` version guards to any parts of the resource or field implementation that should not be promoted. Be sure to check: +2. Add `{{- if ne $.TargetVersionName "ga" }}...{{- end }}` version guards to any parts of the resource or field implementation that should not be promoted. Be sure to check: - The resource schema - For top-level fields, the resource's `Create`, `Update`, and `Read` methods - For other fields, expanders and flatteners diff --git a/docs/content/develop/test/run-tests.md b/docs/content/develop/test/run-tests.md index 2b8d50d31803..85ec7e63685e 100644 --- a/docs/content/develop/test/run-tests.md +++ b/docs/content/develop/test/run-tests.md @@ -62,8 +62,7 @@ aliases: make testacc TEST=./google/services/container TESTARGS='-run=TestAccContainerNodePool' ``` -> [!NOTE] -> Acceptance tests create actual infrastructure which can incur costs. Acceptance tests may not clean up after themselves if interrupted, so you may want to check for stray resources and / or billing charges. +> **Note:** Acceptance tests create actual infrastructure which can incur costs. Acceptance tests may not clean up after themselves if interrupted, so you may want to check for stray resources and / or billing charges. 1. Optional: Save verbose test output (including API requests and responses) to a file for analysis. @@ -95,8 +94,7 @@ aliases: ```bash make testacc TEST=./google-beta/services/container TESTARGS='-run=TestAccContainerNodePool' ``` -> [!NOTE] -> Acceptance tests create actual infrastructure which can incur costs. Acceptance tests may not clean up after themselves if interrupted, so you may want to check for stray resources and / or billing charges. +> **Note:** Acceptance tests create actual infrastructure which can incur costs. Acceptance tests may not clean up after themselves if interrupted, so you may want to check for stray resources and / or billing charges. 1. Optional: Save verbose test output to a file for analysis. @@ -115,6 +113,13 @@ aliases: {{< /tabs >}} +### Common errors + +- `After applying this test step, the plan was not empty.` + - See [Fix diffs]({{< ref "/develop/diffs" >}}). +- `Blocks of type "FIELD_NAME" are not expected here` + - The field does not exist; this is either because it has not been implemented or because the test is running for the `google` provider and the field is only implemented in the `google-beta` provider. See [Add resource tests]({{< ref "/develop/test/test" >}}) for information on using version guards to exclude beta-only fields from GA tests, or [Promote from beta to GA]({{< ref "/develop/promote-to-ga" >}}) for information on how to promote fields that were accidentally made beta-only. + ## Optional: Test with different `terraform` versions Tests will use whatever version of the `terraform` binary is found on your `PATH`. If you are testing a change that you know only impacts certain `terraform` versions, follow these steps: diff --git a/docs/content/develop/test/test.md b/docs/content/develop/test/test.md index 69f97e9dd679..4d3bc2c6343b 100644 --- a/docs/content/develop/test/test.md +++ b/docs/content/develop/test/test.md @@ -128,6 +128,7 @@ An update test is a test that creates the target resource and then makes updates - Copy the 2 `TestStep` blocks and paste them immediately after, so that there are 4 total test steps. - Change the suffix of the first `Config` value to `_full` (or `_basic`). - Change the suffix of the second `Config` value to `_update`. + - Add `ConfigPlanChecks` to the update step of the test to ensure the resource is updated in-place. - The resulting test function would look similar to this: ```go func TestAccPubsubTopic_update(t *testing.T) { @@ -143,6 +144,11 @@ An update test is a test that creates the target resource and then makes updates }, { Config: testAccPubsubTopic_update(...), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("google_pubsub_topic.foo", plancheck.ResourceActionUpdate), + }, + }, }, { ... diff --git a/mmv1/.yamllint b/mmv1/.yamllint index 0ef87fa11ac3..e72b465ef819 100644 --- a/mmv1/.yamllint +++ b/mmv1/.yamllint @@ -6,3 +6,6 @@ rules: # because we want indentation to look # proper in unindentented versions comments: disable + indentation: + # Autogen YAML files have different indentation for arrays + indent-sequences: consistent \ No newline at end of file diff --git a/mmv1/api/async.go b/mmv1/api/async.go index a689259ddad5..f947c62741b3 100644 --- a/mmv1/api/async.go +++ b/mmv1/api/async.go @@ -71,7 +71,7 @@ func NewAsync() *Async { type OpAsync struct { Result OpAsyncResult - Status OpAsyncStatus + Status OpAsyncStatus `yaml:"status,omitempty"` Error OpAsyncError @@ -81,42 +81,42 @@ type OpAsync struct { } type OpAsyncOperation struct { - Kind string + Kind string `yaml:"kind,omitempty"` - Path string + Path string `yaml:"path,omitempty"` - BaseUrl string `yaml:"base_url"` + BaseUrl string `yaml:"base_url,omitempty"` - WaitMs int `yaml:"wait_ms"` + WaitMs int `yaml:"wait_ms,omitempty"` // Use this if the resource includes the full operation url. - FullUrl string `yaml:"full_url"` + FullUrl string `yaml:"full_url,omitempty"` } // Represents the results of an Operation request type OpAsyncResult struct { - ResourceInsideResponse bool `yaml:"resource_inside_response"` + ResourceInsideResponse bool `yaml:"resource_inside_response,omitempty"` - Path string + Path string `yaml:"path,omitempty"` } // Provides information to parse the result response to check operation // status type OpAsyncStatus struct { - Path string + Path string `yaml:"path,omitempty"` - Complete bool + Complete bool `yaml:"complete,omitempty"` - Allowed []bool + Allowed []bool `yaml:"allowed,omitempty"` } // Provides information on how to retrieve errors of the executed operations type OpAsyncError struct { - google.YamlValidator + google.YamlValidator `yaml:"-"` - Path string + Path string `yaml:"path,omitempty"` - Message string + Message string `yaml:"message,omitempty"` } // Async implementation for polling in Terraform @@ -125,19 +125,19 @@ type PollAsync struct { // Function to call for checking the Poll response for // creating and updating a resource - CheckResponseFuncExistence string `yaml:"check_response_func_existence"` + CheckResponseFuncExistence string `yaml:"check_response_func_existence,omitempty"` // Function to call for checking the Poll response for // deleting a resource - CheckResponseFuncAbsence string `yaml:"check_response_func_absence"` + CheckResponseFuncAbsence string `yaml:"check_response_func_absence,omitempty"` // If true, will suppress errors from polling and default to the // result of the final Read() - SuppressError bool `yaml:"suppress_error"` + SuppressError bool `yaml:"suppress_error,omitempty"` // Number of times the desired state has to occur continuously // during polling before returning a success - TargetOccurrences int `yaml:"target_occurrences"` + TargetOccurrences int `yaml:"target_occurrences,omitempty"` } func (a *Async) UnmarshalYAML(unmarshal func(any) error) error { diff --git a/mmv1/api/resource.go b/mmv1/api/resource.go index e52477e68788..06778e5b26ee 100644 --- a/mmv1/api/resource.go +++ b/mmv1/api/resource.go @@ -31,7 +31,7 @@ type Resource struct { // original value of :name before the provider override happens // same as :name if not overridden in provider - ApiName string `yaml:"api_name"` + ApiName string `yaml:"api_name,omitempty"` // [Required] A description of the resource that's surfaced in provider // documentation. @@ -45,36 +45,36 @@ type Resource struct { // 'Guide name': 'official_documentation_url' // api: 'rest_api_reference_url/version' // - References resource.ReferenceLinks + References resource.ReferenceLinks `yaml:"references,omitempty"` // [Required] The GCP "relative URI" of a resource, relative to the product // base URL. It can often be inferred from the `create` path. - BaseUrl string `yaml:"base_url"` + BaseUrl string `yaml:"base_url,omitempty"` // ==================== // Common Configuration // ==================== // // [Optional] The minimum API version this resource is in. Defaults to ga. - MinVersion string `yaml:"min_version"` + MinVersion string `yaml:"min_version,omitempty"` // [Optional] If set to true, don't generate the resource. - Exclude bool + Exclude bool `yaml:"exclude,omitempty"` // [Optional] If set to true, the resource is not able to be updated. - Immutable bool + Immutable bool `yaml:"immutable,omitempty"` // [Optional] If set to true, this resource uses an update mask to perform // updates. This is typical of newer GCP APIs. - UpdateMask bool `yaml:"update_mask"` + UpdateMask bool `yaml:"update_mask,omitempty"` // [Optional] If set to true, the object has a `self_link` field. This is // typical of older GCP APIs. - HasSelfLink bool `yaml:"has_self_link"` + HasSelfLink bool `yaml:"has_self_link,omitempty"` // [Optional] The validator "relative URI" of a resource, relative to the product // base URL. Specific to defining the resource as a CAI asset. - CaiBaseUrl string `yaml:"cai_base_url"` + CaiBaseUrl string `yaml:"cai_base_url,omitempty"` // ==================== // URL / HTTP Configuration @@ -83,34 +83,34 @@ type Resource struct { // [Optional] The "identity" URL of the resource. Defaults to: // * base_url when the create_verb is POST // * self_link when the create_verb is PUT or PATCH - SelfLink string `yaml:"self_link"` + SelfLink string `yaml:"self_link,omitempty"` // [Optional] The URL used to creating the resource. Defaults to: // * collection url when the create_verb is POST // * self_link when the create_verb is PUT or PATCH - CreateUrl string `yaml:"create_url"` + CreateUrl string `yaml:"create_url,omitempty"` // [Optional] The URL used to delete the resource. Defaults to the self // link. - DeleteUrl string `yaml:"delete_url"` + DeleteUrl string `yaml:"delete_url,omitempty"` // [Optional] The URL used to update the resource. Defaults to the self // link. - UpdateUrl string `yaml:"update_url"` + UpdateUrl string `yaml:"update_url,omitempty"` // [Optional] The HTTP verb used during create. Defaults to POST. - CreateVerb string `yaml:"create_verb"` + CreateVerb string `yaml:"create_verb,omitempty"` // [Optional] The HTTP verb used during read. Defaults to GET. - ReadVerb string `yaml:"read_verb"` + ReadVerb string `yaml:"read_verb,omitempty"` // [Optional] The HTTP verb used during update. Defaults to PUT. - UpdateVerb string `yaml:"update_verb"` + UpdateVerb string `yaml:"update_verb,omitempty"` // [Optional] The HTTP verb used during delete. Defaults to DELETE. - DeleteVerb string `yaml:"delete_verb"` + DeleteVerb string `yaml:"delete_verb,omitempty"` // [Optional] Additional Query Parameters to append to GET. Defaults to "" - ReadQueryParams string `yaml:"read_query_params"` + ReadQueryParams string `yaml:"read_query_params,omitempty"` // ==================== // Collection / Identity URL Configuration @@ -119,7 +119,7 @@ type Resource struct { // [Optional] This is the name of the list of items // within the collection (list) json. Will default to the // camelcase plural name of the resource. - CollectionUrlKey string `yaml:"collection_url_key"` + CollectionUrlKey string `yaml:"collection_url_key,omitempty"` // [Optional] An ordered list of names of parameters that uniquely identify // the resource. @@ -128,14 +128,14 @@ type Resource struct { // and is identified by some non-name value, such as an ip+port pair. // If you're writing a fine-grained resource (eg with nested_query) a value // must be set. - Identity []string + Identity []string `yaml:"identity,omitempty"` // [Optional] (Api::Resource::NestedQuery) This is useful in case you need // to change the query made for GET requests only. In particular, this is // often used to extract an object from a parent object or a collection. // Note that if both nested_query and custom_code.decoder are provided, // the decoder will be included within the code handling the nested query. - NestedQuery *resource.NestedQuery `yaml:"nested_query"` + NestedQuery *resource.NestedQuery `yaml:"nested_query,omitempty"` // ==================== // IAM Configuration @@ -143,19 +143,19 @@ type Resource struct { // // [Optional] (Api::Resource::IamPolicy) Configuration of a resource's // resource-specific IAM Policy. - IamPolicy *resource.IamPolicy `yaml:"iam_policy"` + IamPolicy *resource.IamPolicy `yaml:"iam_policy,omitempty"` // [Optional] If set to true, don't generate the resource itself; only // generate the IAM policy. // TODO rewrite: rename? - ExcludeResource bool `yaml:"exclude_resource"` + ExcludeResource bool `yaml:"exclude_resource,omitempty"` // [Optional] GCP kind, e.g. `compute//disk` - Kind string + Kind string `yaml:"kind,omitempty"` // [Optional] If set to true, indicates that a resource is not configurable // such as GCP regions. - Readonly bool + Readonly bool `yaml:"readonly,omitempty"` // ==================== // Terraform Overrides @@ -163,7 +163,7 @@ type Resource struct { // [Optional] If non-empty, overrides the full filename prefix // i.e. google/resource_product_{{resource_filename_override}}.go // i.e. google/resource_product_{{resource_filename_override}}_test.go - FilenameOverride string `yaml:"filename_override"` + FilenameOverride string `yaml:"filename_override,omitempty"` // If non-empty, overrides the full given resource name. // i.e. 'google_project' for resourcemanager.Project @@ -173,11 +173,11 @@ type Resource struct { // This was added to handle preexisting handwritten resources that // don't match the natural generated name exactly, and to support // services with a mix of handwritten and generated resources. - LegacyName string `yaml:"legacy_name"` + LegacyName string `yaml:"legacy_name,omitempty"` // The Terraform resource id format used when calling //setId(...). // For instance, `{{name}}` means the id will be the resource name. - IdFormat string `yaml:"id_format"` + IdFormat string `yaml:"id_format,omitempty"` // Override attribute used to handwrite the formats for generating regex strings // that match templated values to a self_link when importing, only necessary when @@ -192,107 +192,107 @@ type Resource struct { // - example_import_one // - example_import_two // - ImportFormat []string `yaml:"import_format"` + ImportFormat []string `yaml:"import_format,omitempty"` - CustomCode resource.CustomCode `yaml:"custom_code"` + CustomCode resource.CustomCode `yaml:"custom_code,omitempty"` - Docs resource.Docs + Docs resource.Docs `yaml:"docs,omitempty"` // This block inserts entries into the customdiff.All() block in the // resource schema -- the code for these custom diff functions must // be included in the resource constants or come from tpgresource - CustomDiff []string `yaml:"custom_diff"` + CustomDiff []string `yaml:"custom_diff,omitempty"` // Lock name for a mutex to prevent concurrent API calls for a given // resource. - Mutex string + Mutex string `yaml:"mutex,omitempty"` // Examples in documentation. Backed by generated tests, and have // corresponding OiCS walkthroughs. Examples []resource.Examples // If true, generates product operation handling logic. - AutogenAsync bool `yaml:"autogen_async"` + AutogenAsync bool `yaml:"autogen_async,omitempty"` // If true, resource is not importable - ExcludeImport bool `yaml:"exclude_import"` + ExcludeImport bool `yaml:"exclude_import,omitempty"` // If true, exclude resource from Terraform Validator // (i.e. terraform-provider-conversion) - ExcludeTgc bool `yaml:"exclude_tgc"` + ExcludeTgc bool `yaml:"exclude_tgc,omitempty"` // If true, skip sweeper generation for this resource - ExcludeSweeper bool `yaml:"exclude_sweeper"` + ExcludeSweeper bool `yaml:"exclude_sweeper,omitempty"` // Override sweeper settings - Sweeper resource.Sweeper + Sweeper resource.Sweeper `yaml:"sweeper,omitempty"` - Timeouts *Timeouts + Timeouts *Timeouts `yaml:"timeouts,omitempty"` // An array of function names that determine whether an error is retryable. - ErrorRetryPredicates []string `yaml:"error_retry_predicates"` + ErrorRetryPredicates []string `yaml:"error_retry_predicates,omitempty"` // An array of function names that determine whether an error is not retryable. - ErrorAbortPredicates []string `yaml:"error_abort_predicates"` + ErrorAbortPredicates []string `yaml:"error_abort_predicates,omitempty"` // Optional attributes for declaring a resource's current version and generating // state_upgrader code to the output .go file from files stored at // mmv1/templates/terraform/state_migrations/ // used for maintaining state stability with resources first provisioned on older api versions. - SchemaVersion int `yaml:"schema_version"` + SchemaVersion int `yaml:"schema_version,omitempty"` // From this schema version on, state_upgrader code is generated for the resource. // When unset, state_upgrade_base_schema_version defauts to 0. // Normally, it is not needed to be set. - StateUpgradeBaseSchemaVersion int `yaml:"state_upgrade_base_schema_version"` + StateUpgradeBaseSchemaVersion int `yaml:"state_upgrade_base_schema_version,omitempty"` - StateUpgraders bool `yaml:"state_upgraders"` + StateUpgraders bool `yaml:"state_upgraders,omitempty"` // Do not apply the default attribution label - ExcludeAttributionLabel bool `yaml:"exclude_attribution_label"` + ExcludeAttributionLabel bool `yaml:"exclude_attribution_label,omitempty"` // This block inserts the named function and its attribute into the // resource schema -- the code for the migrate_state function must // be included in the resource constants or come from tpgresource // included for backwards compatibility as an older state migration method // and should not be used for new resources. - MigrateState string `yaml:"migrate_state"` + MigrateState string `yaml:"migrate_state,omitempty"` // Set to true for resources that are unable to be deleted, such as KMS keyrings or project // level resources such as firebase project - ExcludeDelete bool `yaml:"exclude_delete"` + ExcludeDelete bool `yaml:"exclude_delete,omitempty"` // Set to true for resources that are unable to be read from the API, such as // public ca external account keys - ExcludeRead bool `yaml:"exclude_read"` + ExcludeRead bool `yaml:"exclude_read,omitempty"` // Set to true for resources that wish to disable automatic generation of default provider // value customdiff functions // TODO rewrite: 1 instance used - ExcludeDefaultCdiff bool `yaml:"exclude_default_cdiff"` + ExcludeDefaultCdiff bool `yaml:"exclude_default_cdiff,omitempty"` // This enables resources that get their project via a reference to a different resource // instead of a project field to use User Project Overrides - SupportsIndirectUserProjectOverride bool `yaml:"supports_indirect_user_project_override"` + SupportsIndirectUserProjectOverride bool `yaml:"supports_indirect_user_project_override,omitempty"` // If true, the resource's project field can be specified as either the short form project // id or the long form projects/project-id. The extra projects/ string will be removed from // urls and ids. This should only be used for resources that previously supported long form // project ids for backwards compatibility. - LegacyLongFormProject bool `yaml:"legacy_long_form_project"` + LegacyLongFormProject bool `yaml:"legacy_long_form_project,omitempty"` // Function to transform a read error so that handleNotFound recognises // it as a 404. This should be added as a handwritten fn that takes in // an error and returns one. - ReadErrorTransform string `yaml:"read_error_transform"` + ReadErrorTransform string `yaml:"read_error_transform,omitempty"` // If true, resources that failed creation will be marked as tainted. As a consequence // these resources will be deleted and recreated on the next apply call. This pattern // is preferred over deleting the resource directly in post_create_failure hooks. - TaintResourceOnFailedCreate bool `yaml:"taint_resource_on_failed_create"` + TaintResourceOnFailedCreate bool `yaml:"taint_resource_on_failed_create,omitempty"` // Add a deprecation message for a resource that's been deprecated in the API. - DeprecationMessage string `yaml:"deprecation_message"` + DeprecationMessage string `yaml:"deprecation_message,omitempty"` Async *Async @@ -313,21 +313,23 @@ type Resource struct { // Both are resource level fields and do not make sense, and are also not // supported, for nested fields. Nested fields that shouldn't be included // in API payloads are better handled with custom expand/encoder logic. - VirtualFields []*Type `yaml:"virtual_fields"` + VirtualFields []*Type `yaml:"virtual_fields,omitempty"` Parameters []*Type Properties []*Type - ProductMetadata *Product + ProductMetadata *Product `yaml:"-"` // The version name provided by the user through CI - TargetVersionName string + TargetVersionName string `yaml:"-"` // The compiler to generate the downstream files, for example "terraformgoogleconversion-codegen". - Compiler string + Compiler string `yaml:"-"` - ImportPath string + ApiResourceTypeKind string `yaml:"api_resource_type_kind,omitempty"` + + ImportPath string `yaml:"-"` } func (r *Resource) UnmarshalYAML(unmarshal func(any) error) error { diff --git a/mmv1/api/resource/examples.go b/mmv1/api/resource/examples.go index 52975ea28847..7130c5b0d655 100644 --- a/mmv1/api/resource/examples.go +++ b/mmv1/api/resource/examples.go @@ -47,7 +47,7 @@ type Examples struct { // Optional resource type of the "primary" resource. Used in import tests. // If set, this will override the default resource type implied from the // object parent - PrimaryResourceType string `yaml:"primary_resource_type"` + PrimaryResourceType string `yaml:"primary_resource_type,omitempty"` // Vars is a Hash from template variable names to output variable names. // It will use the provided value as a prefix for generated tests, and @@ -73,7 +73,7 @@ type Examples struct { // - :CUST_ID // - :IDENTITY_USER // This list corresponds to the `get*FromEnv` methods in provider_test.go. - TestEnvVars map[string]string `yaml:"test_env_vars"` + TestEnvVars map[string]string `yaml:"test_env_vars,omitempty"` // Hash to provider custom override values for generating test config // If field my-var is set in this hash, it will replace vars[my-var] in @@ -92,11 +92,11 @@ type Examples struct { // "network": nameOfVpc // ... // } - TestVarsOverrides map[string]string `yaml:"test_vars_overrides"` + TestVarsOverrides map[string]string `yaml:"test_vars_overrides,omitempty"` // Hash to provider custom override values for generating oics config // See test_vars_overrides for more details - OicsVarsOverrides map[string]string `yaml:"oics_vars_overrides"` + OicsVarsOverrides map[string]string `yaml:"oics_vars_overrides,omitempty"` // The version name of of the example's version if it's different than the // resource version, eg. `beta` @@ -115,48 +115,48 @@ type Examples struct { // explicit provider block should be defined. While the tests @ 0.12 will // use `google-beta` automatically, past Terraform versions required an // explicit block. - MinVersion string `yaml:"min_version"` + MinVersion string `yaml:"min_version,omitempty"` // Extra properties to ignore read on during import. // These properties will likely be custom code. - IgnoreReadExtra []string `yaml:"ignore_read_extra"` + IgnoreReadExtra []string `yaml:"ignore_read_extra,omitempty"` // Whether to skip generating tests for this resource - ExcludeTest bool `yaml:"exclude_test"` + ExcludeTest bool `yaml:"exclude_test,omitempty"` // Whether to skip generating docs for this example - ExcludeDocs bool `yaml:"exclude_docs"` + ExcludeDocs bool `yaml:"exclude_docs,omitempty"` // Whether to skip import tests for this example - ExcludeImportTest bool `yaml:"exclude_import_test"` + ExcludeImportTest bool `yaml:"exclude_import_test,omitempty"` // The name of the primary resource for use in IAM tests. IAM tests need // a reference to the primary resource to create IAM policies for - PrimaryResourceName string `yaml:"primary_resource_name"` + PrimaryResourceName string `yaml:"primary_resource_name,omitempty"` // The name of the location/region override for use in IAM tests. IAM // tests may need this if the location is not inherited on the resource // for one reason or another - RegionOverride string `yaml:"region_override"` + RegionOverride string `yaml:"region_override,omitempty"` // The path to this example's Terraform config. // Defaults to `templates/terraform/examples/{{name}}.tf.erb` - ConfigPath string `yaml:"config_path"` + ConfigPath string `yaml:"config_path,omitempty"` // If the example should be skipped during VCR testing. // This is the case when something about the resource or config causes VCR to fail for example // a resource with a unique identifier generated within the resource via id.UniqueId() // Or a config with two fine grained resources that have a race condition during create - SkipVcr bool `yaml:"skip_vcr"` + SkipVcr bool `yaml:"skip_vcr,omitempty"` // Specify which external providers are needed for the testcase. // Think before adding as there is latency and adds an external dependency to // your test so avoid if you can. - ExternalProviders []string `yaml:"external_providers"` + ExternalProviders []string `yaml:"external_providers,omitempty"` - DocumentationHCLText string - TestHCLText string - OicsHCLText string + DocumentationHCLText string `yaml:"-"` + TestHCLText string `yaml:"-"` + OicsHCLText string `yaml:"-"` } // Set default value for fields diff --git a/mmv1/api/type.go b/mmv1/api/type.go index 4d81f796f784..b55b6fc219b0 100644 --- a/mmv1/api/type.go +++ b/mmv1/api/type.go @@ -26,16 +26,16 @@ import ( // Represents a property type type Type struct { - Name string + Name string `yaml:"name,omitempty"` // original value of :name before the provider override happens // same as :name if not overridden in provider - ApiName string `yaml:"api_name"` + ApiName string `yaml:"api_name,omitempty"` // TODO rewrite: improve the parsing of properties based on type in resource yaml files. Type string - DefaultValue interface{} `yaml:"default_value"` + DefaultValue interface{} `yaml:"default_value,omitempty"` // Expected to follow the format as follows: // @@ -43,69 +43,69 @@ type Type struct { // This is a description of a field. // If it comprises multiple lines, it must continue to be indented. // - Description string + Description string `yaml:"description,omitempty"` - Exclude bool + Exclude bool `yaml:"exclude,omitempty"` // Add a deprecation message for a field that's been deprecated in the API // use the YAML chomping folding indicator (>-) if this is a multiline // string, as providers expect a single-line one w/o a newline. - DeprecationMessage string `yaml:"deprecation_message"` + DeprecationMessage string `yaml:"deprecation_message,omitempty"` // Add a removed message for fields no longer supported in the API. This should // be used for fields supported in one version but have been removed from // a different version. - RemovedMessage string `yaml:"removed_message"` + RemovedMessage string `yaml:"removed_message,omitempty"` // If set value will not be sent to server on sync. // For nested fields, this also needs to be set on each descendant (ie. self, // child, etc.). - Output bool + Output bool `yaml:"output,omitempty"` // If set to true, changes in the field's value require recreating the // resource. // For nested fields, this only applies at the current level. This means // it should be explicitly added to each field that needs the ForceNew // behavior. - Immutable bool + Immutable bool `yaml:"immutable,omitempty"` // Indicates that this field is client-side only (aka virtual.) - ClientSide bool `yaml:"client_side"` + ClientSide bool `yaml:"client_side,omitempty"` // url_param_only will not send the field in the resource body and will // not attempt to read the field from the API response. // NOTE - this doesn't work for nested fields - UrlParamOnly bool `yaml:"url_param_only"` + UrlParamOnly bool `yaml:"url_param_only,omitempty"` // For nested fields, this only applies within the parent. // For example, an optional parent can contain a required child. - Required bool + Required bool `yaml:"required,omitempty"` // Additional query Parameters to append to GET calls. - ReadQueryParams string `yaml:"read_query_params"` + ReadQueryParams string `yaml:"read_query_params,omitempty"` - UpdateVerb string `yaml:"update_verb"` + UpdateVerb string `yaml:"update_verb,omitempty"` - UpdateUrl string `yaml:"update_url"` + UpdateUrl string `yaml:"update_url,omitempty"` // Some updates only allow updating certain fields at once (generally each // top-level field can be updated one-at-a-time). If this is set, we group // fields to update by (verb, url, fingerprint, id) instead of just // (verb, url, fingerprint), to allow multiple fields to reuse the same // endpoints. - UpdateId string `yaml:"update_id"` + UpdateId string `yaml:"update_id,omitempty"` // The fingerprint value required to update this field. Downstreams should // GET the resource and parse the fingerprint value while doing each update // call. This ensures we can supply the fingerprint to each distinct // request. - FingerprintName string `yaml:"fingerprint_name"` + FingerprintName string `yaml:"fingerprint_name,omitempty"` // If true, we will include the empty value in requests made including // this attribute (both creates and updates). This rarely needs to be // set to true, and corresponds to both the "NullFields" and // "ForceSendFields" concepts in the autogenerated API clients. - SendEmptyValue bool `yaml:"send_empty_value"` + SendEmptyValue bool `yaml:"send_empty_value,omitempty"` // [Optional] If true, empty nested objects are sent to / read from the // API instead of flattened to null. @@ -115,78 +115,78 @@ type Type struct { // In the case of Terraform, this occurs when a block in config has optional // values, and none of them are used. Terraform returns a nil instead of an // empty map[string]interface{} like we'd expect. - AllowEmptyObject bool `yaml:"allow_empty_object"` + AllowEmptyObject bool `yaml:"allow_empty_object,omitempty"` - MinVersion string `yaml:"min_version"` + MinVersion string `yaml:"min_version,omitempty"` - ExactVersion string `yaml:"exact_version"` + ExactVersion string `yaml:"exact_version,omitempty"` // A list of properties that conflict with this property. Uses the "lineage" // field to identify the property eg: parent.meta.label.foo - Conflicts []string + Conflicts []string `yaml:"conflicts,omitempty"` // A list of properties that at least one of must be set. - AtLeastOneOf []string `yaml:"at_least_one_of"` + AtLeastOneOf []string `yaml:"at_least_one_of,omitempty"` // A list of properties that exactly one of must be set. - ExactlyOneOf []string `yaml:"exactly_one_of"` + ExactlyOneOf []string `yaml:"exactly_one_of,omitempty"` // A list of properties that are required to be set together. - RequiredWith []string `yaml:"required_with"` + RequiredWith []string `yaml:"required_with,omitempty"` // Can only be overridden - we should never set this ourselves. - NewType string + NewType string `yaml:"-"` - Properties []*Type + Properties []*Type `yaml:"properties,omitempty"` - EnumValues []string `yaml:"enum_values"` + EnumValues []string `yaml:"enum_values,omitempty"` - ExcludeDocsValues bool `yaml:"exclude_docs_values"` + ExcludeDocsValues bool `yaml:"exclude_docs_values,omitempty"` // ==================== // Array Fields // ==================== - ItemType *Type `yaml:"item_type"` - MinSize string `yaml:"min_size"` - MaxSize string `yaml:"max_size"` + ItemType *Type `yaml:"item_type,omitempty"` + MinSize string `yaml:"min_size,omitempty"` + MaxSize string `yaml:"max_size,omitempty"` // Adds a ValidateFunc to the item schema - ItemValidation resource.Validation `yaml:"item_validation"` + ItemValidation resource.Validation `yaml:"item_validation,omitempty"` - ParentName string + ParentName string `yaml:"parent_name,omitempty"` // ==================== // ResourceRef Fields // ==================== - Resource string - Imports string + Resource string `yaml:"resource,omitempty"` + Imports string `yaml:"imports,omitempty"` // ==================== // Terraform Overrides // ==================== // Adds a DiffSuppressFunc to the schema - DiffSuppressFunc string `yaml:"diff_suppress_func"` + DiffSuppressFunc string `yaml:"diff_suppress_func,omitempty"` - StateFunc string `yaml:"state_func"` // Adds a StateFunc to the schema + StateFunc string `yaml:"state_func,omitempty"` // Adds a StateFunc to the schema - Sensitive bool // Adds `Sensitive: true` to the schema + Sensitive bool `yaml:"sensitive,omitempty"` // Adds `Sensitive: true` to the schema // Does not set this value to the returned API value. Useful for fields // like secrets where the returned API value is not helpful. - IgnoreRead bool `yaml:"ignore_read"` + IgnoreRead bool `yaml:"ignore_read,omitempty"` // Adds a ValidateFunc to the schema - Validation resource.Validation + Validation resource.Validation `yaml:"validation,omitempty"` // Indicates that this is an Array that should have Set diff semantics. - UnorderedList bool `yaml:"unordered_list"` + UnorderedList bool `yaml:"unordered_list,omitempty"` - IsSet bool `yaml:"is_set"` // Uses a Set instead of an Array + IsSet bool `yaml:"is_set,omitempty"` // Uses a Set instead of an Array // Optional function to determine the unique ID of an item in the set // If not specified, schema.HashString (when elements are string) or // schema.HashSchema are used. - SetHashFunc string `yaml:"set_hash_func"` + SetHashFunc string `yaml:"set_hash_func,omitempty"` // if true, then we get the default value from the Google API if no value // is set in the terraform configuration for this field. @@ -194,44 +194,44 @@ type Type struct { // For nested fields, this only applies at the current level. This means // it should be explicitly added to each field that needs the defaulting // behavior. - DefaultFromApi bool `yaml:"default_from_api"` + DefaultFromApi bool `yaml:"default_from_api,omitempty"` // https://github.com/hashicorp/terraform/pull/20837 // Apply a ConfigMode of SchemaConfigModeAttr to the field. // This should be avoided for new fields, and only used with old ones. - SchemaConfigModeAttr bool `yaml:"schema_config_mode_attr"` + SchemaConfigModeAttr bool `yaml:"schema_config_mode_attr,omitempty"` // Names of fields that should be included in the updateMask. - UpdateMaskFields []string `yaml:"update_mask_fields"` + UpdateMaskFields []string `yaml:"update_mask_fields,omitempty"` // For a TypeMap, the expander function to call on the key. // Defaults to expandString. - KeyExpander string `yaml:"key_expander"` + KeyExpander string `yaml:"key_expander,omitempty"` // For a TypeMap, the DSF to apply to the key. - KeyDiffSuppressFunc string `yaml:"key_diff_suppress_func"` + KeyDiffSuppressFunc string `yaml:"key_diff_suppress_func,omitempty"` // ==================== // Map Fields // ==================== // The type definition of the contents of the map. - ValueType *Type `yaml:"value_type"` + ValueType *Type `yaml:"value_type,omitempty"` // While the API doesn't give keys an explicit name, we specify one // because in Terraform the key has to be a property of the object. // // The name of the key. Used in the Terraform schema as a field name. - KeyName string `yaml:"key_name"` + KeyName string `yaml:"key_name,omitempty"` // A description of the key's format. Used in Terraform to describe // the field in documentation. - KeyDescription string `yaml:"key_description"` + KeyDescription string `yaml:"key_description,omitempty"` // ==================== // KeyValuePairs Fields // ==================== // Ignore writing the "effective_labels" and "effective_annotations" fields to API. - IgnoreWrite bool `yaml:"ignore_write"` + IgnoreWrite bool `yaml:"ignore_write,omitempty"` // ==================== // Schema Modifications @@ -259,7 +259,7 @@ type Type struct { // WARN: only fully flattened properties are currently supported. In the // example above you could not flatten `one.two` without also flattening // all of it's parents such as `one` - FlattenObject bool `yaml:"flatten_object"` + FlattenObject bool `yaml:"flatten_object,omitempty"` // =========== // Custom code @@ -273,7 +273,7 @@ type Type struct { // object.input is false. It can return an object of any type, // so the function header *is* part of the custom code template. // As with flatten, `property` and `prefix` are available. - CustomExpand string `yaml:"custom_expand"` + CustomExpand string `yaml:"custom_expand,omitempty"` // A custom flattener replaces the default flattener for an attribute. // It is called as part of Read. It can return an object of any @@ -282,15 +282,15 @@ type Type struct { // header *is* a part of the custom code template. To help with // creating the function header, `property` and `prefix` are available, // just as they are in the standard flattener template. - CustomFlatten string `yaml:"custom_flatten"` + CustomFlatten string `yaml:"custom_flatten,omitempty"` - ResourceMetadata *Resource + ResourceMetadata *Resource `yaml:"resource_metadata,omitempty"` - ParentMetadata *Type // is nil for top-level properties + ParentMetadata *Type `yaml:"parent_metadata,omitempty"` // is nil for top-level properties // The prefix used as part of the property expand/flatten function name // flatten{{$.GetPrefix}}{{$.TitlelizeProperty}} - Prefix string + Prefix string `yaml:"prefix,omitempty"` } const MAX_NAME = 20 diff --git a/mmv1/openapi_generate/resource_yaml.tmpl b/mmv1/openapi_generate/header.txt similarity index 96% rename from mmv1/openapi_generate/resource_yaml.tmpl rename to mmv1/openapi_generate/header.txt index d6182b0a6233..1f252a57f112 100644 --- a/mmv1/openapi_generate/resource_yaml.tmpl +++ b/mmv1/openapi_generate/header.txt @@ -12,4 +12,3 @@ # limitations under the License. --- -name: '{{$.Name}}' diff --git a/mmv1/openapi_generate/parser.go b/mmv1/openapi_generate/parser.go index 3dbaa1edf634..23a8c91a057a 100644 --- a/mmv1/openapi_generate/parser.go +++ b/mmv1/openapi_generate/parser.go @@ -16,23 +16,22 @@ package openapi_generate import ( - "bytes" "context" "fmt" "os" "path" "path/filepath" + "regexp" + "slices" "strings" "log" - "text/template" - "github.com/GoogleCloudPlatform/magic-modules/mmv1/api" "github.com/GoogleCloudPlatform/magic-modules/mmv1/api/product" + r "github.com/GoogleCloudPlatform/magic-modules/mmv1/api/resource" "github.com/GoogleCloudPlatform/magic-modules/mmv1/google" "github.com/getkin/kin-openapi/openapi3" - "github.com/golang/glog" "gopkg.in/yaml.v2" ) @@ -42,7 +41,6 @@ type Parser struct { } func NewOpenapiParser(folder, output string) Parser { - wd, err := os.Getwd() if err != nil { log.Fatalf(err.Error()) @@ -57,7 +55,6 @@ func NewOpenapiParser(folder, output string) Parser { } func (parser Parser) Run() { - f, err := os.Open(parser.Folder) if err != nil { log.Fatalf(err.Error()) @@ -87,28 +84,42 @@ func (parser Parser) WriteYaml(filePath string) { doc, _ := loader.LoadFromFile(filePath) _ = doc.Validate(ctx) + header, err := os.ReadFile("openapi_generate/header.txt") + if err != nil { + log.Fatalf("error reading header %v", err) + } + resourcePaths := findResources(doc) - productPath := buildProduct(filePath, parser.Output, doc) + productPath := buildProduct(filePath, parser.Output, doc, header) + // Disables line wrap for long strings + yaml.FutureLineWrap() log.Printf("Generated product %+v/product.yaml", productPath) for _, pathArray := range resourcePaths { resource := buildResource(filePath, pathArray[0], pathArray[1], doc) - // template method - resourceOutPathTemplate := filepath.Join(productPath, fmt.Sprintf("%s_template.yaml", resource.Name)) - templatePath := "openapi_generate/resource_yaml.tmpl" - WriteGoTemplate(templatePath, resourceOutPathTemplate, resource) - log.Printf("Generated resource %s", resourceOutPathTemplate) - // marshal method - resourceOutPathMarshal := filepath.Join(productPath, fmt.Sprintf("%s_marshal.yaml", resource.Name)) + resourceOutPathMarshal := filepath.Join(productPath, fmt.Sprintf("%s.yaml", resource.Name)) bytes, err := yaml.Marshal(resource) if err != nil { log.Fatalf("error marshalling yaml %v: %v", resourceOutPathMarshal, err) } - err = os.WriteFile(resourceOutPathMarshal, bytes, 0644) + + f, err := os.Create(resourceOutPathMarshal) + if err != nil { + log.Fatalf("error creating resource file %v", err) + } + _, err = f.Write(header) + if err != nil { + log.Fatalf("error writing resource file header %v", err) + } + _, err = f.Write(bytes) + if err != nil { + log.Fatalf("error writing resource file %v", err) + } + err = f.Close() if err != nil { - log.Fatalf("error writing product to path %v: %v", resourceOutPathMarshal, err) + log.Fatalf("error closing resource file %v", err) } log.Printf("Generated resource %s", resourceOutPathMarshal) } @@ -134,7 +145,7 @@ func findResources(doc *openapi3.T) [][]string { return resourcePaths } -func buildProduct(filePath, output string, root *openapi3.T) string { +func buildProduct(filePath, output string, root *openapi3.T, header []byte) string { version := root.Info.Version server := root.Servers[0].URL @@ -162,13 +173,7 @@ func buildProduct(filePath, output string, root *openapi3.T) string { //Scopes should be added soon to OpenAPI, until then use global scope apiProduct.Scopes = []string{"https://www.googleapis.com/auth/cloud-platform"} - // productOutPath := filepath.Join(output, fmt.Sprintf("/%s/product.yaml", productName)) - templatePath := "openapi_generate/product_yaml.tmpl" - - productOutPathTemplate := filepath.Join(output, fmt.Sprintf("/%s/product_template.yaml", productName)) - WriteGoTemplate(templatePath, productOutPathTemplate, apiProduct) - - productOutPathMarshal := filepath.Join(output, fmt.Sprintf("/%s/product_marshal.yaml", productName)) + productOutPathMarshal := filepath.Join(output, fmt.Sprintf("/%s/product.yaml", productName)) // Default yaml marshaller bytes, err := yaml.Marshal(apiProduct) @@ -176,14 +181,49 @@ func buildProduct(filePath, output string, root *openapi3.T) string { log.Fatalf("error marshalling yaml %v: %v", productOutPathMarshal, err) } - err = os.WriteFile(productOutPathMarshal, bytes, 0644) + f, err := os.Create(productOutPathMarshal) if err != nil { - log.Fatalf("error writing product to path %v: %v", productOutPathMarshal, err) + log.Fatalf("error creating product file %v", err) + } + _, err = f.Write(header) + if err != nil { + log.Fatalf("error writing product file header %v", err) + } + _, err = f.Write(bytes) + if err != nil { + log.Fatalf("error writing product file %v", err) + } + err = f.Close() + if err != nil { + log.Fatalf("error closing product file %v", err) } - return productPath } +func baseUrl(resourcePath string) string { + base := strings.ReplaceAll(resourcePath, "{", "{{") + base = strings.ReplaceAll(base, "}", "}}") + // Some APIs use projectsId and locationsId, but we have standardized on these + base = strings.ReplaceAll(base, "projectsId", "project") + base = strings.ReplaceAll(base, "locationsId", "location") + base = stripVersion(base) + r := regexp.MustCompile(`\{\{(\w+)\}\}`) + matches := r.FindStringSubmatch(base) + for i := 0; i < len(matches); i++ { + match := matches[i] + base = strings.ReplaceAll(base, match, google.Underscore(match)) + } + return base +} + +// OpenAPI paths are prefixed with the version of the API, which already exists +// in the product. Strip it out here +func stripVersion(path string) string { + pattern := `^(/.*v\d[^/]*/)` + re := regexp.MustCompile(pattern) + return re.ReplaceAllString(path, "") +} + func buildResource(filePath, resourcePath, resourceName string, root *openapi3.T) api.Resource { resource := api.Resource{} @@ -193,14 +233,31 @@ func buildResource(filePath, resourcePath, resourceName string, root *openapi3.T properties := parsedObjects[1].([]*api.Type) queryParam := parsedObjects[2].(string) - // TODO base_url(resource_path) - baseUrl := resourcePath - selfLink := fmt.Sprintf("%s/%s", baseUrl, strings.ToLower(queryParam)) + baseUrl := baseUrl(resourcePath) + selfLink := fmt.Sprintf("%s/{{%s}}", baseUrl, google.Underscore(queryParam)) resource.Name = resourceName + resource.BaseUrl = baseUrl resource.Parameters = parameters resource.Properties = properties resource.SelfLink = selfLink + resource.IdFormat = selfLink + resource.ImportFormat = []string{selfLink} + resource.CreateUrl = fmt.Sprintf("%s?%s={{%s}}", baseUrl, queryParam, google.Underscore(queryParam)) + resource.Description = "Description" + + resource.AutogenAsync = true + async := api.NewAsync() + async.Operation.BaseUrl = "{{op_id}}" + async.Result.ResourceInsideResponse = true + resource.Async = async + + example := r.Examples{} + example.Name = "name_of_example_file" + example.PrimaryResourceId = "example" + example.Vars = map[string]string{"resource_name": "test-resource"} + + resource.Examples = []r.Examples{example} return resource } @@ -215,7 +272,12 @@ func parseOpenApi(resourcePath, resourceName string, root *openapi3.T) []any { if strings.Contains(strings.ToLower(param.Value.Name), strings.ToLower(resourceName)) { idParam = param.Value.Name } - paramObj := writeObject(param.Value.Name, param.Value.Schema, *param.Value.Schema.Value.Type, true) + paramObj := writeObject(param.Value.Name, param.Value.Schema, propType(param.Value.Schema), true) + description := param.Value.Description + if strings.TrimSpace(description) == "" { + description = "No description" + } + paramObj.Description = trimSpacesFromDescription(description) if param.Value.Name == "requestId" || param.Value.Name == "validateOnly" || paramObj.Name == "" { continue @@ -226,8 +288,7 @@ func parseOpenApi(resourcePath, resourceName string, root *openapi3.T) []any { parameters = append(parameters, ¶mObj) } - // TODO build_properties - properties := []*api.Type{} + properties := buildProperties(path.Post.RequestBody.Value.Content["application/json"].Schema.Value.Properties, path.Post.RequestBody.Value.Content["application/json"].Schema.Value.Required) returnArray = append(returnArray, parameters) returnArray = append(returnArray, properties) @@ -236,6 +297,14 @@ func parseOpenApi(resourcePath, resourceName string, root *openapi3.T) []any { return returnArray } +func propType(prop *openapi3.SchemaRef) openapi3.Types { + if len(prop.Value.AllOf) > 0 { + return *prop.Value.AllOf[0].Value.Type + } else { + return *prop.Value.Type + } +} + func writeObject(name string, obj *openapi3.SchemaRef, objType openapi3.Types, urlParam bool) api.Type { var field api.Type @@ -249,16 +318,15 @@ func writeObject(name string, obj *openapi3.SchemaRef, objType openapi3.Types, u } additionalDescription := "" - // log.Printf("%s %+v", name, obj.Value.AllOf) - if len(obj.Value.AllOf) > 0 { obj = obj.Value.AllOf[0] objType = *obj.Value.Type } - if objType.Is("string") { - field.Type = "string" - field.Name = name + field.Name = name + switch objType[0] { + case "string": + field.Type = "String" if len(obj.Value.Enum) > 0 { var enums []string for _, enum := range obj.Value.Enum { @@ -266,6 +334,48 @@ func writeObject(name string, obj *openapi3.SchemaRef, objType openapi3.Types, u } additionalDescription = fmt.Sprintf("\n Possible values:\n %s", strings.Join(enums, "\n")) } + case "integer": + field.Type = "Integer" + case "number": + field.Type = "Double" + case "boolean": + field.Type = "Boolean" + case "object": + if field.Name == "labels" { + // Standard labels implementation + field.Type = "KeyValueLabels" + break + } + + if obj.Value.AdditionalProperties.Schema != nil && obj.Value.AdditionalProperties.Schema.Value.Type.Is("string") { + // AdditionalProperties with type string is a string -> string map + field.Type = "KeyValuePairs" + break + } + + field.Type = "NestedObject" + + field.Properties = buildProperties(obj.Value.Properties, obj.Value.Required) + case "array": + field.Type = "Array" + var subField api.Type + typ := *obj.Value.Items.Value.Type + switch typ[0] { + case "string": + subField.Type = "String" + case "integer": + subField.Type = "Integer" + case "number": + subField.Type = "Double" + case "boolean": + subField.Type = "Boolean" + case "object": + subField.Type = "NestedObject" + subField.Properties = buildProperties(obj.Value.Items.Value.Properties, obj.Value.Items.Value.Required) + } + field.ItemType = &subField + default: + panic(fmt.Sprintf("Failed to identify field type for %s %s", field.Name, objType[0])) } description := fmt.Sprintf("%s %s", obj.Value.Description, additionalDescription) @@ -273,6 +383,8 @@ func writeObject(name string, obj *openapi3.SchemaRef, objType openapi3.Types, u description = "No description" } + field.Description = trimSpacesFromDescription(description) + if urlParam { field.UrlParamOnly = true field.Required = true @@ -291,34 +403,32 @@ func writeObject(name string, obj *openapi3.SchemaRef, objType openapi3.Types, u } xGoogleImmutable, err := obj.JSONLookup("x-google-immutable") - if obj.Value.ReadOnly || (err == nil && xGoogleImmutable != nil) { + if err == nil && xGoogleImmutable != nil { field.Immutable = true } return field } -func WriteGoTemplate(templatePath, filePath string, input any) { - contents := bytes.Buffer{} - - templateFileName := filepath.Base(templatePath) - templates := []string{ - templatePath, - } - - tmpl, err := template.New(templateFileName).Funcs(google.TemplateFunctions).ParseFiles(templates...) - if err != nil { - glog.Exit(fmt.Sprintf("error parsing %s for filepath %s ", templatePath, filePath), err) - } - if err = tmpl.ExecuteTemplate(&contents, templateFileName, input); err != nil { - glog.Exit(fmt.Sprintf("error executing %s for filepath %s ", templatePath, filePath), err) +func buildProperties(props openapi3.Schemas, required []string) []*api.Type { + properties := []*api.Type{} + for k, prop := range props { + propObj := writeObject(k, prop, propType(prop), false) + if slices.Contains(required, k) { + propObj.Required = true + } + properties = append(properties, &propObj) } + return properties +} - bytes := contents.Bytes() - - err = os.WriteFile(filePath, bytes, 0644) - if err != nil { - log.Fatalf("error writing product to path %v: %v", filePath, err) +// Trims whitespace from the ends of lines in a description to force multiline +// formatting for strings with newlines present +func trimSpacesFromDescription(description string) string { + lines := strings.Split(description, "\n") + var trimmedDescription []string + for _, line := range lines { + trimmedDescription = append(trimmedDescription, strings.Trim(line, " ")) } - + return strings.Join(trimmedDescription, "\n") } diff --git a/mmv1/openapi_generate/parser.rb b/mmv1/openapi_generate/parser.rb deleted file mode 100644 index 8dc47c0446d7..000000000000 --- a/mmv1/openapi_generate/parser.rb +++ /dev/null @@ -1,302 +0,0 @@ -# Copyright 2023 Google Inc. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -require 'openapi_parser' - -module OpenAPIGenerate - # Parser to convert from OpenAPI spec to MMv1 YAML - class Parser - attr_reader :folder - attr_reader :output - - def initialize(folder, output) - @folder = folder - @output = output - end - - def run - openapi_dir = Dir[@folder] - raise "No OpenAPI files found in #{@folder}" if openapi_dir.empty? - - openapi_dir.each do |openapi_file| - write_yaml(openapi_file, @output) - end - end - - def write_object(name, obj, type, url_param) - field = nil - case name - when 'projectsId', 'project' - # projectsId and project are omitted in MMv1 as they are inferred from - # the presence of {{project}} in the URL - return field - when 'locationsId' - name = 'location' - end - additional_description = '' - - # allOf is a workaround for overriding fields on shared objects - if obj.respond_to?(:all_of) && !obj.all_of&.length().nil? - obj = obj.all_of[0] - type = obj.type - end - - case type - when 'string' - field = Api::Type::String.new(name) - if obj.respond_to?(:enum) && obj.enum - additional_description = "\n Possible values:\n #{obj.enum.join("\n")}" - end - when 'integer' - field = Api::Type::Integer.new - field.instance_variable_set(:@name, name) - when 'number' - field = Api::Type::Double.new - field.instance_variable_set(:@name, name) - when 'boolean' - field = Api::Type::Boolean.new - field.instance_variable_set(:@name, name) - when 'object' - if name == 'labels' - # standard labels field handling - field = Api::Type::KeyValueLabels.new - elsif name == 'annotations' - # standard annotations field handling - field = Api::Type::KeyValueAnnotations.new - elsif obj.respond_to?(:additional_properties) \ - && obj.additional_properties.respond_to?(:type) - # additionalProperties.type signifies a string -> string map - field = Api::Type::KeyValuePairs.new - else - field = Api::Type::NestedObject.new - required_props = obj.required || [] - - properties = [] - obj.properties&.each do |prop, i| - prop = write_object(prop, i, i.type, false) - prop.instance_variable_set(:@required, true) if required_props.include?(prop.name) - required_props.delete(prop.name) - properties.push(prop) - end - raise "Unknown required properties #{required_props}" unless required_props.empty? - - field.instance_variable_set(:@properties, properties) - end - field.instance_variable_set(:@name, name) - - when 'array' - field = Api::Type::Array.new - field.instance_variable_set(:@name, name) - case obj.items.type - when 'string' - field.instance_variable_set(:@item_type, 'Api::Type::String') - when 'number' - field.instance_variable_set(:@item_type, 'Api::Type::Double') - when 'boolean' - field.instance_variable_set(:@item_type, 'Api::Type::Boolean') - else - nested_object = Api::Type::NestedObject.new - object_properties = build_properties( - obj.items.properties, - obj.items.required || [] - ) - nested_object.instance_variable_set(:@properties, object_properties) - field.instance_variable_set(:@item_type, nested_object) - end - else - raise "Failed to identify field type #{type} #{name}" - end - - field.instance_variable_set( - :@description, - "#{obj.description} #{additional_description}" || 'No description' - ) - if url_param - field.instance_variable_set(:@url_param_only, true) - field.instance_variable_set(:@required, true) if obj.required - end - - # These methods are only available when the field is set - if obj.respond_to?(:read_only) && obj.read_only - field.instance_variable_set(:@output, obj.read_only) - end - - # x-google-identifier fields are described by AIP 203 and are represented - # as output only in Terraform. - if obj.instance_variable_get(:@raw_schema)['x-google-identifier'] - field.instance_variable_set(:@output, true) - end - - if (obj.respond_to?(:write_only) && obj.write_only) \ - || obj.instance_variable_get(:@raw_schema)['x-google-immutable'] - field.instance_variable_set(:@immutable, true) - end - - field - end - - def find_resources(spec_path) - resource_paths = [] - root = OpenAPIParser.parse(YAML.load_file(spec_path)) - root.paths.path.each do |path| - next unless path[1].post - - # Not very clever way of identifying create resource methods - if path[1].post.operation_id.start_with?('Create') - resource_paths.push([path[0], path[1].post.operation_id.gsub('Create', '')]) - end - end - resource_paths - end - - def parse_openapi(spec_path, resource_path, resource_name) - # Write YAML - root = OpenAPIParser.parse(YAML.load_file(spec_path)) - path = root.paths.path[resource_path] - parameters = [] - path.post.parameters.each do |param| - parameter_object = write_object(param.name, param, param.schema.type, true) - # Ignore standard requestId and validateOnly params - next if param.name == 'requestId' || param.name == 'validateOnly' - next if parameter_object.nil? - - # All parameters are immutable - parameter_object.instance_variable_set(:@immutable, true) - parameters.push(parameter_object) - end - properties = build_properties( - path.post.request_body.content['application/json'].schema.properties, - path.post.request_body.content['application/json'].schema.required || [] - ) - - id_param = path.post.parameters.select do |p| - p.name.downcase.include?(resource_name.downcase) - end.last - raise 'did not find ID param' unless id_param - - [properties, parameters, id_param.name] - end - - def build_properties(properties, required) - prop_objects = [] - properties&.each do |prop, i| - prop_object = write_object(prop, i, i.type, false) - prop_object.instance_variable_set(:@required, true) if required.include?(prop) - - required.delete(prop) - prop_objects.push(prop_object) - end - raise "Unknown required properties in object #{required}" unless required.empty? - - prop_objects - end - - def base_url(resource_path) - base = resource_path.gsub('{', '{{').gsub('}', '}}') - - base = base.gsub('projectsId', 'project') - base = base.gsub('locationsId', 'location') - field_names = base.scan(/(?<=\{\{)\w+(?=\}\})/) - field_names.each do |field_name| - field_name_in_snake_case = field_name.underscore - base = base.gsub("{{#{field_name}}}", "{{#{field_name_in_snake_case}}}") - end - base = base.gsub('/v1/', '') - base.gsub('/v1alpha/', '') - end - - def build_resource(spec_path, resource_path, resource_name) - properties, parameters, query_param = parse_openapi(spec_path, resource_path, resource_name) - - resource = Api::Resource.new - base_url = base_url(resource_path) - resource.base_url = base_url - resource.create_url = "#{base_url}?#{query_param}={{#{query_param.underscore}}}" - self_link = "#{base_url}/{{#{query_param.underscore}}}" - resource.self_link = self_link - resource.id_format = self_link - resource.import_format = [self_link] - - # Name is on the Api::NamedObject parent resource, lets not modify that - resource.instance_variable_set(:@name, resource_name) - # TODO(slevenick): Get resource description published in OpenAPI spec - resource.description = 'Description' - if update?(spec_path, resource_name) - resource.update_verb = :PATCH - resource.update_mask = true - else - resource.immutable = true - end - - resource.autogen_async = true - resource.properties = properties - resource.parameters = parameters - - # Default operation handling - op = Api::OpAsync::Operation.new('name', '{{op_id}}', 1000, nil) - result = Api::OpAsync::Result.new('response', true) - status = Api::OpAsync::Status.new('done', true, [true, false]) - error = Api::OpAsync::Error.new('error', 'message') - async = Api::OpAsync.new(op, result, status, error) - resource.async = async - resource - end - - def update?(spec_path, resource_name) - root = OpenAPIParser.parse(YAML.load_file(spec_path)) - root.paths.path.each do |path| - # PATCH is the standard update method - next unless path[1].patch - - return true if path[1].patch.operation_id.start_with?("Update#{resource_name}") - end - false - end - - def build_product(spec_path, output) - root = OpenAPIParser.parse(YAML.load_file(spec_path)) - version = root.raw_schema['info']['version'] - server = root.raw_schema['servers'][0]['url'] - product_name = spec_path.split('/').last.split('_').first - product_path = File.join(output, product_name) - FileUtils.mkdir_p(product_path) - product = Api::Product.new - api_version = Api::Product::Version.new - api_version.base_url = "#{server}/#{version}/" - # TODO(slevenick) figure out how to tell the API version - api_version.name = 'ga' - product.versions = [api_version] - # Standard titling is "Service Name API" - display_name = root.raw_schema['info']['title'].sub(' API', '') - # Name is on the Api::NamedObject parent resource, lets not modify that - product.instance_variable_set(:@name, display_name.gsub(' ', '')) - product.display_name = display_name - # Scopes should be added soon to OpenAPI, until then use global scope - product.scopes = ['https://www.googleapis.com/auth/cloud-platform'] - File.write(File.join(output, "/#{product_name}/product.yaml"), product.to_yaml) - product_path - end - - def write_yaml(spec_path, output) - resource_paths = find_resources(spec_path) - product_path = build_product(spec_path, output) - Google::LOGGER.info "Generated product '#{product_path}/product.yaml'" - resource_paths.each do |path_array| - resource = build_resource(spec_path, path_array[0], path_array[1]) - file_path = File.join(product_path, "#{resource.name}.yaml") - File.write(file_path, resource.to_yaml) - Google::LOGGER.info "Generated resource '#{file_path}'" - end - end - end -end diff --git a/mmv1/openapi_generate/product_yaml.tmpl b/mmv1/openapi_generate/product_yaml.tmpl deleted file mode 100644 index 38bd016db19c..000000000000 --- a/mmv1/openapi_generate/product_yaml.tmpl +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2024 Google Inc. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - ---- -name: '{{$.Name}}' -display_name: '{{$.DisplayName}}' -versions: -{{- range $version := $.Versions }} - - name: '{{$version.Name}}' - base_url: {{$version.BaseUrl}}' -{{- end }} -scopes: -{{- range $scope := $.Scopes }} - - '{{$scope}}' -{{- end }} diff --git a/mmv1/products/accessapproval/FolderSettings.yaml b/mmv1/products/accessapproval/FolderSettings.yaml index a1dd54828251..87c1976307f3 100644 --- a/mmv1/products/accessapproval/FolderSettings.yaml +++ b/mmv1/products/accessapproval/FolderSettings.yaml @@ -13,6 +13,7 @@ --- name: 'FolderSettings' +api_resource_type_kind: AccessApprovalSettings legacy_name: 'google_folder_access_approval_settings' description: | Access Approval enables you to require your explicit approval whenever Google support and engineering need to access your customer content. diff --git a/mmv1/products/accessapproval/OrganizationSettings.yaml b/mmv1/products/accessapproval/OrganizationSettings.yaml index 5503efb7d19e..1be242422397 100644 --- a/mmv1/products/accessapproval/OrganizationSettings.yaml +++ b/mmv1/products/accessapproval/OrganizationSettings.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationSettings' +api_resource_type_kind: AccessApprovalSettings legacy_name: 'google_organization_access_approval_settings' description: | Access Approval enables you to require your explicit approval whenever Google support and engineering need to access your customer content. diff --git a/mmv1/products/accessapproval/ProjectSettings.yaml b/mmv1/products/accessapproval/ProjectSettings.yaml index adaa591ac172..3bc75c92da90 100644 --- a/mmv1/products/accessapproval/ProjectSettings.yaml +++ b/mmv1/products/accessapproval/ProjectSettings.yaml @@ -13,6 +13,7 @@ --- name: 'ProjectSettings' +api_resource_type_kind: AccessApprovalSettings legacy_name: 'google_project_access_approval_settings' description: | Access Approval enables you to require your explicit approval whenever Google support and engineering need to access your customer content. diff --git a/mmv1/products/accesscontextmanager/AccessLevelCondition.yaml b/mmv1/products/accesscontextmanager/AccessLevelCondition.yaml index 8fc07a0f06a8..af0a6b2b7d20 100644 --- a/mmv1/products/accesscontextmanager/AccessLevelCondition.yaml +++ b/mmv1/products/accesscontextmanager/AccessLevelCondition.yaml @@ -13,6 +13,7 @@ --- name: 'AccessLevelCondition' +api_resource_type_kind: AccessLevel description: | Allows configuring a single access level condition to be appended to an access level's conditions. This resource is intended to be used in cases where it is not possible to compile a full list diff --git a/mmv1/products/accesscontextmanager/AccessLevels.yaml b/mmv1/products/accesscontextmanager/AccessLevels.yaml index 6db368f12d28..594588b2f1e1 100644 --- a/mmv1/products/accesscontextmanager/AccessLevels.yaml +++ b/mmv1/products/accesscontextmanager/AccessLevels.yaml @@ -14,6 +14,7 @@ --- # This is the plural of `AccessLevel`, any changes here should be made to `AccessLevel` as well name: 'AccessLevels' +api_resource_type_kind: AccessLevel description: | Replace all existing Access Levels in an Access Policy with the Access Levels provided. This is done atomically. This is a bulk edit of all Access Levels and may override existing Access Levels created by `google_access_context_manager_access_level`, diff --git a/mmv1/products/accesscontextmanager/EgressPolicy.yaml b/mmv1/products/accesscontextmanager/EgressPolicy.yaml index 26f89b895f1a..5e60dd9e25ab 100644 --- a/mmv1/products/accesscontextmanager/EgressPolicy.yaml +++ b/mmv1/products/accesscontextmanager/EgressPolicy.yaml @@ -13,6 +13,7 @@ --- name: 'EgressPolicy' +api_resource_type_kind: ServicePerimeter description: | This resource has been deprecated, please refer to ServicePerimeterEgressPolicy. references: diff --git a/mmv1/products/accesscontextmanager/IngressPolicy.yaml b/mmv1/products/accesscontextmanager/IngressPolicy.yaml index 63e6e947ec27..8e8415be80ca 100644 --- a/mmv1/products/accesscontextmanager/IngressPolicy.yaml +++ b/mmv1/products/accesscontextmanager/IngressPolicy.yaml @@ -13,6 +13,7 @@ --- name: 'IngressPolicy' +api_resource_type_kind: ServicePerimeter description: | This resource has been deprecated, please refer to ServicePerimeterIngressPolicy. references: diff --git a/mmv1/products/accesscontextmanager/ServicePerimeterDryRunEgressPolicy.yaml b/mmv1/products/accesscontextmanager/ServicePerimeterDryRunEgressPolicy.yaml index 7c3499d9adc2..547a4f1210ef 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeterDryRunEgressPolicy.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeterDryRunEgressPolicy.yaml @@ -13,6 +13,7 @@ --- name: 'ServicePerimeterDryRunEgressPolicy' +api_resource_type_kind: ServicePerimeter description: | Manage a single EgressPolicy in the spec (dry-run) configuration for a service perimeter. EgressPolicies match requests based on egressFrom and egressTo stanzas. @@ -71,6 +72,7 @@ nested_query: is_list_of_ids: false modify_by_patch: true custom_code: + constants: 'templates/terraform/constants/access_context_manager.go.tmpl' pre_create: 'templates/terraform/pre_create/access_context_manager_dry_run_resource.go.tmpl' pre_update: 'templates/terraform/pre_create/access_context_manager_dry_run_resource.go.tmpl' pre_delete: 'templates/terraform/pre_create/access_context_manager_dry_run_resource.go.tmpl' @@ -143,6 +145,8 @@ properties: if it contains a resource in this list. If * is specified for resources, then this `EgressTo` rule will authorize access to all resources outside the perimeter. + custom_flatten: templates/terraform/custom_flatten/accesscontextmanager_egress_policy_resources_custom_flatten.go.tmpl + diff_suppress_func: AccessContextManagerServicePerimeterDryRunEgressPolicyEgressToResourcesDiffSupressFunc item_type: type: String - name: 'externalResources' diff --git a/mmv1/products/accesscontextmanager/ServicePerimeterDryRunIngressPolicy.yaml b/mmv1/products/accesscontextmanager/ServicePerimeterDryRunIngressPolicy.yaml index dbc364519793..c947bc53299f 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeterDryRunIngressPolicy.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeterDryRunIngressPolicy.yaml @@ -13,6 +13,7 @@ --- name: 'ServicePerimeterDryRunIngressPolicy' +api_resource_type_kind: ServicePerimeter description: | Manage a single IngressPolicy in the spec (dry-run) configuration for a service perimeter. IngressPolicies match requests based on ingressFrom and ingressTo stanzas. For an ingress policy to match, @@ -72,6 +73,7 @@ nested_query: is_list_of_ids: false modify_by_patch: true custom_code: + constants: 'templates/terraform/constants/access_context_manager.go.tmpl' pre_create: 'templates/terraform/pre_create/access_context_manager_dry_run_resource.go.tmpl' pre_update: 'templates/terraform/pre_create/access_context_manager_dry_run_resource.go.tmpl' pre_delete: 'templates/terraform/pre_create/access_context_manager_dry_run_resource.go.tmpl' @@ -160,6 +162,8 @@ properties: then this `IngressTo` rule will authorize access to all resources inside the perimeter, provided that the request also matches the `operations` field. + custom_flatten: templates/terraform/custom_flatten/accesscontextmanager_ingress_policy_resources_custom_flatten.go.tmpl + diff_suppress_func: AccessContextManagerServicePerimeterDryRunIngressPolicyIngressToResourcesDiffSupressFunc item_type: type: String - name: 'operations' diff --git a/mmv1/products/accesscontextmanager/ServicePerimeterDryRunResource.yaml b/mmv1/products/accesscontextmanager/ServicePerimeterDryRunResource.yaml index 3672777787a0..8a92f31380b0 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeterDryRunResource.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeterDryRunResource.yaml @@ -13,6 +13,7 @@ --- name: 'ServicePerimeterDryRunResource' +api_resource_type_kind: ServicePerimeter description: | Allows configuring a single GCP resource that should be inside of the `spec` block of a dry run service perimeter. This resource is intended to be used in cases where it is not possible to compile a full list diff --git a/mmv1/products/accesscontextmanager/ServicePerimeterEgressPolicy.yaml b/mmv1/products/accesscontextmanager/ServicePerimeterEgressPolicy.yaml index d78fd6ebac72..ba3f48750add 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeterEgressPolicy.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeterEgressPolicy.yaml @@ -13,6 +13,7 @@ --- name: 'ServicePerimeterEgressPolicy' +api_resource_type_kind: ServicePerimeter description: | Manage a single EgressPolicy in the status (enforced) configuration for a service perimeter. EgressPolicies match requests based on egressFrom and egressTo stanzas. @@ -71,7 +72,8 @@ nested_query: is_list_of_ids: false modify_by_patch: true custom_code: - custom_import: 'templates/terraform/custom_import/access_context_manager_service_perimeter_ingress_policy.go.tmpl' + constants: 'templates/terraform/constants/access_context_manager.go.tmpl' + custom_import: 'templates/terraform/custom_import/access_context_manager_service_perimeter_egress_policy.go.tmpl' exclude_tgc: true # Skipping the sweeper due to the non-standard base_url and because this is fine-grained under ServicePerimeter exclude_sweeper: true @@ -141,6 +143,8 @@ properties: if it contains a resource in this list. If * is specified for resources, then this `EgressTo` rule will authorize access to all resources outside the perimeter. + custom_flatten: templates/terraform/custom_flatten/accesscontextmanager_egress_policy_resources_custom_flatten.go.tmpl + diff_suppress_func: AccessContextManagerServicePerimeterEgressPolicyEgressToResourcesDiffSupressFunc item_type: type: String - name: 'externalResources' diff --git a/mmv1/products/accesscontextmanager/ServicePerimeterIngressPolicy.yaml b/mmv1/products/accesscontextmanager/ServicePerimeterIngressPolicy.yaml index 6e601c76918e..d9b370dced59 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeterIngressPolicy.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeterIngressPolicy.yaml @@ -13,6 +13,7 @@ --- name: 'ServicePerimeterIngressPolicy' +api_resource_type_kind: ServicePerimeter description: | Manage a single IngressPolicy in the status (enforced) configuration for a service perimeter. IngressPolicies match requests based on ingressFrom and ingressTo stanzas. For an ingress policy to match, @@ -72,6 +73,7 @@ nested_query: is_list_of_ids: false modify_by_patch: true custom_code: + constants: 'templates/terraform/constants/access_context_manager.go.tmpl' custom_import: 'templates/terraform/custom_import/access_context_manager_service_perimeter_ingress_policy.go.tmpl' exclude_tgc: true # Skipping the sweeper due to the non-standard base_url and because this is fine-grained under ServicePerimeter @@ -160,6 +162,8 @@ properties: then this `IngressTo` rule will authorize access to all resources inside the perimeter, provided that the request also matches the `operations` field. + custom_flatten: templates/terraform/custom_flatten/accesscontextmanager_ingress_policy_resources_custom_flatten.go.tmpl + diff_suppress_func: AccessContextManagerServicePerimeterIngressPolicyIngressToResourcesDiffSupressFunc item_type: type: String - name: 'operations' diff --git a/mmv1/products/accesscontextmanager/ServicePerimeterResource.yaml b/mmv1/products/accesscontextmanager/ServicePerimeterResource.yaml index 1716c9cdfa2e..a748daef0d74 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeterResource.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeterResource.yaml @@ -13,6 +13,7 @@ --- name: 'ServicePerimeterResource' +api_resource_type_kind: ServicePerimeter description: | Allows configuring a single GCP resource that should be inside the `status` block of a service perimeter. This resource is intended to be used in cases where it is not possible to compile a full list diff --git a/mmv1/products/accesscontextmanager/ServicePerimeters.yaml b/mmv1/products/accesscontextmanager/ServicePerimeters.yaml index 16bac6deb731..f7a4d16b79b7 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeters.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeters.yaml @@ -14,6 +14,7 @@ --- # This is the plural of `ServicePerimeter`, any changes here should be made to `ServicePerimeter` as well name: 'ServicePerimeters' +api_resource_type_kind: ServicePerimeter description: | Replace all existing Service Perimeters in an Access Policy with the Service Perimeters provided. This is done atomically. This is a bulk edit of all Service Perimeters and may override existing Service Perimeters created by `google_access_context_manager_service_perimeter`, diff --git a/mmv1/products/activedirectory/DomainTrust.yaml b/mmv1/products/activedirectory/DomainTrust.yaml index 3c61ee6c44c7..45a20a175ce8 100644 --- a/mmv1/products/activedirectory/DomainTrust.yaml +++ b/mmv1/products/activedirectory/DomainTrust.yaml @@ -13,6 +13,7 @@ --- name: 'DomainTrust' +api_resource_type_kind: Domain kind: 'activedirectory#trust' description: Adds a trust between Active Directory domains references: diff --git a/mmv1/products/apigee/AppGroup.yaml b/mmv1/products/apigee/AppGroup.yaml new file mode 100644 index 000000000000..afaf6cee14c1 --- /dev/null +++ b/mmv1/products/apigee/AppGroup.yaml @@ -0,0 +1,123 @@ +# Copyright 2024 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +name: "AppGroup" +description: | + An `AppGroup` in Apigee. +references: + guides: + "Organizing client app ownership": "https://cloud.google.com/apigee/docs/api-platform/publish/organizing-client-app-ownership" + api: "https://cloud.google.com/apigee/docs/reference/apis/apigee/rest/v1/organizations.appgroups" +docs: +base_url: "appgroups" +self_link: "{{org_id}}/appgroups/{{name}}" +create_url: "{{org_id}}/appgroups" +import_format: + - "{{org_id}}/appgroups/{{name}}" + - "{{org_id}}/{{name}}" +custom_code: + custom_import: "templates/terraform/custom_import/apigee_app_group.go.tmpl" +examples: + - name: "apigee_app_group_basic" + vars: + app_group_name: "my-app-group" + exclude_test: true + - name: "apigee_app_group_basic_test" + primary_resource_id: "apigee_app_group" + test_env_vars: + org_id: "ORG_ID" + billing_account: "BILLING_ACCT" + exclude_docs: true + skip_vcr: true + - name: "apigee_app_group_with_attributes" + vars: + app_group_name: "my-app-group" + exclude_test: true + - name: "apigee_app_group_with_attributes_test" + primary_resource_id: "apigee_app_group" + test_env_vars: + org_id: "ORG_ID" + billing_account: "BILLING_ACCT" + exclude_docs: true + skip_vcr: true +parameters: + - name: "orgId" + type: String + description: | + The Apigee Organization associated with the Apigee app group, + in the format `organizations/{{org_name}}`. + url_param_only: true + required: true + immutable: true +properties: + - name: "appGroupId" + type: String + description: | + Internal identifier that cannot be edited + output: true + - name: "name" + type: String + description: | + Name of the AppGroup. Characters you can use in the name are restricted to: A-Z0-9._-$ %. + required: true + immutable: true + - name: "channelUri" + type: String + description: | + A reference to the associated storefront/marketplace. + - name: "channelId" + type: String + description: | + Channel identifier identifies the owner maintaing this grouping. + - name: "displayName" + type: String + description: | + App group name displayed in the UI + - name: "organization" + type: String + description: | + App group name displayed in the UI + output: true + - name: "status" + type: Enum + description: | + Valid values are active or inactive. Note that the status of the AppGroup should be updated via UpdateAppGroupRequest by setting the action as active or inactive. + enum_values: + - active + - inactive + - name: "attributes" + type: Array + description: | + A list of attributes + item_type: + type: NestedObject + properties: + - name: "name" + type: String + description: | + Key of the attribute + - name: "value" + type: String + description: | + Value of the attribute + - name: "createdAt" + type: String + description: | + Created time as milliseconds since epoch. + output: true + - name: "lastModifiedAt" + type: String + description: | + Modified time as milliseconds since epoch. + output: true diff --git a/mmv1/products/apigee/Developer.yaml b/mmv1/products/apigee/Developer.yaml new file mode 100644 index 000000000000..858a7f9cf884 --- /dev/null +++ b/mmv1/products/apigee/Developer.yaml @@ -0,0 +1,124 @@ +# Copyright 2024 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +name: "Developer" +description: | + A `Developer` is an API consumer that can have apps registered in Apigee. +references: + guides: + "Creating a developer": "https://cloud.google.com/apigee/docs/api-platform/publish/adding-developers-your-api-product" + api: "https://cloud.google.com/apigee/docs/reference/apis/apigee/rest/v1/organizations.developers" +docs: +base_url: "developers" +self_link: "{{org_id}}/developers/{{email}}" +create_url: "{{org_id}}/developers" +delete_url: "{{org_id}}/developers/{{email}}" +update_mask: true +immutable: false +import_format: + - "{{org_id}}/developers/{{email}}" + - "{{org_id}}/{{email}}" +custom_code: + custom_import: "templates/terraform/custom_import/apigee_developer.go.tmpl" +examples: + - name: "apigee_developer_basic" + vars: + instance_name: "my-instance" + developer_email: "john.doe@acme.com" + exclude_test: true + - name: "apigee_developer_basic_test" + primary_resource_id: "apigee_developer" + test_env_vars: + org_id: "ORG_ID" + billing_account: "BILLING_ACCT" + exclude_docs: true + skip_vcr: true + - name: "apigee_developer_with_attributes" + vars: + instance_name: "my-instance" + developer_email: "john.doe@acme.com" + exclude_test: true + - name: "apigee_developer_with_attributes_test" + primary_resource_id: "apigee_developer" + test_env_vars: + org_id: "ORG_ID" + billing_account: "BILLING_ACCT" + exclude_docs: true + skip_vcr: true +parameters: + - name: "orgId" + type: String + description: | + The Apigee Organization associated with the Apigee instance, + in the format `organizations/{{org_name}}`. + url_param_only: true + required: true + immutable: true +properties: + - name: "email" + type: String + description: | + Email address of the developer. This value is used to uniquely identify the developer in Apigee hybrid. Note that the email address has to be in lowercase only.. + required: true + immutable: true + - name: "firstName" + type: String + description: | + First name of the developer. + required: true + - name: "lastName" + type: String + description: | + Last name of the developer. + required: true + - name: "userName" + type: String + description: | + User name of the developer. Not used by Apigee hybrid. + required: true + - name: "attributes" + type: Array + description: | + Developer attributes (name/value pairs). The custom attribute limit is 18. + item_type: + type: NestedObject + properties: + - name: "name" + type: String + description: | + Key of the attribute + - name: "value" + type: String + description: | + Value of the attribute + - name: "organizatioName" + type: String + description: | + Name of the Apigee organization in which the developer resides. + output: true + - name: "status" + type: String + description: | + Status of the developer. Valid values are active and inactive. + output: true + - name: "createdAt" + type: String + description: | + Time at which the developer was created in milliseconds since epoch. + output: true + - name: "lastModifiedAt" + type: String + description: | + Time at which the developer was last modified in milliseconds since epoch. + output: true diff --git a/mmv1/products/apigee/EnvKeystore.yaml b/mmv1/products/apigee/EnvKeystore.yaml index 5af92f2ee88a..622f539bc2f3 100644 --- a/mmv1/products/apigee/EnvKeystore.yaml +++ b/mmv1/products/apigee/EnvKeystore.yaml @@ -13,6 +13,7 @@ --- name: 'EnvKeystore' +api_resource_type_kind: Keystore description: | An `Environment KeyStore` in Apigee. references: diff --git a/mmv1/products/apigee/EnvReferences.yaml b/mmv1/products/apigee/EnvReferences.yaml index 27150d075542..a190e0ced73b 100644 --- a/mmv1/products/apigee/EnvReferences.yaml +++ b/mmv1/products/apigee/EnvReferences.yaml @@ -13,6 +13,7 @@ --- name: 'EnvReferences' +api_resource_type_kind: Reference description: | An `Environment Reference` in Apigee. references: diff --git a/mmv1/products/apigee/Envgroup.yaml b/mmv1/products/apigee/Envgroup.yaml index e10d9362f2ec..c913be9f4b87 100644 --- a/mmv1/products/apigee/Envgroup.yaml +++ b/mmv1/products/apigee/Envgroup.yaml @@ -13,6 +13,7 @@ --- name: 'Envgroup' +api_resource_type_kind: EnvironmentGroup description: | An `Environment group` in Apigee. references: diff --git a/mmv1/products/apigee/EnvgroupAttachment.yaml b/mmv1/products/apigee/EnvgroupAttachment.yaml index 1771abe12de2..1934a6ef58fe 100644 --- a/mmv1/products/apigee/EnvgroupAttachment.yaml +++ b/mmv1/products/apigee/EnvgroupAttachment.yaml @@ -13,6 +13,7 @@ --- name: 'EnvgroupAttachment' +api_resource_type_kind: EnvironmentGroupAttachment description: | An `Environment Group attachment` in Apigee. references: diff --git a/mmv1/products/apigee/EnvironmentKeyvaluemaps.yaml b/mmv1/products/apigee/EnvironmentKeyvaluemaps.yaml index 6c09624ea467..0b852d814245 100644 --- a/mmv1/products/apigee/EnvironmentKeyvaluemaps.yaml +++ b/mmv1/products/apigee/EnvironmentKeyvaluemaps.yaml @@ -13,6 +13,7 @@ --- name: 'EnvironmentKeyvaluemaps' +api_resource_type_kind: KeyValueMap description: | Collection of key/value string pairs. references: diff --git a/mmv1/products/apigee/EnvironmentKeyvaluemapsEntries.yaml b/mmv1/products/apigee/EnvironmentKeyvaluemapsEntries.yaml index 10ebb0d21f46..ce9876a9e56e 100644 --- a/mmv1/products/apigee/EnvironmentKeyvaluemapsEntries.yaml +++ b/mmv1/products/apigee/EnvironmentKeyvaluemapsEntries.yaml @@ -13,6 +13,7 @@ --- name: 'EnvironmentKeyvaluemapsEntries' +api_resource_type_kind: KeyValueEntry description: | Creates key value entries in a key value map scoped to an environment. references: diff --git a/mmv1/products/apigee/KeystoresAliasesSelfSignedCert.yaml b/mmv1/products/apigee/KeystoresAliasesSelfSignedCert.yaml index 5e90f0ff30ea..90a3cbc68d7f 100644 --- a/mmv1/products/apigee/KeystoresAliasesSelfSignedCert.yaml +++ b/mmv1/products/apigee/KeystoresAliasesSelfSignedCert.yaml @@ -13,6 +13,7 @@ --- name: 'KeystoresAliasesSelfSignedCert' +api_resource_type_kind: Alias description: | An Environment Keystore Alias for Self Signed Certificate Format in Apigee references: diff --git a/mmv1/products/apigee/SyncAuthorization.yaml b/mmv1/products/apigee/SyncAuthorization.yaml index 2d0d86f1597b..c62495db149f 100644 --- a/mmv1/products/apigee/SyncAuthorization.yaml +++ b/mmv1/products/apigee/SyncAuthorization.yaml @@ -13,6 +13,7 @@ --- name: 'SyncAuthorization' +api_resource_type_kind: Organization description: | Authorize the Synchronizer to download environment data from the control plane. references: diff --git a/mmv1/products/appengine/ApplicationUrlDispatchRules.yaml b/mmv1/products/appengine/ApplicationUrlDispatchRules.yaml index 3659a5d231a9..a797c2619853 100644 --- a/mmv1/products/appengine/ApplicationUrlDispatchRules.yaml +++ b/mmv1/products/appengine/ApplicationUrlDispatchRules.yaml @@ -13,6 +13,7 @@ --- name: 'ApplicationUrlDispatchRules' +api_resource_type_kind: Application description: | Rules to match an HTTP request and dispatch that request to a service. references: diff --git a/mmv1/products/appengine/FirewallRule.yaml b/mmv1/products/appengine/FirewallRule.yaml index dae0616b3127..19791e425642 100644 --- a/mmv1/products/appengine/FirewallRule.yaml +++ b/mmv1/products/appengine/FirewallRule.yaml @@ -13,6 +13,7 @@ --- name: 'FirewallRule' +api_resource_type_kind: Firewall description: | A single firewall rule that is evaluated against incoming traffic and provides an action to take on matched requests. diff --git a/mmv1/products/appengine/FlexibleAppVersion.yaml b/mmv1/products/appengine/FlexibleAppVersion.yaml index 610cdd074733..a930b9f85296 100644 --- a/mmv1/products/appengine/FlexibleAppVersion.yaml +++ b/mmv1/products/appengine/FlexibleAppVersion.yaml @@ -13,6 +13,7 @@ --- name: 'FlexibleAppVersion' +api_resource_type_kind: Version description: | Flexible App Version resource to create a new version of flexible GAE Application. Based on Google Compute Engine, the App Engine flexible environment automatically scales your app up and down while also balancing the load. diff --git a/mmv1/products/appengine/ServiceNetworkSettings.yaml b/mmv1/products/appengine/ServiceNetworkSettings.yaml index 47ae15061a52..1ee47e8fb9be 100644 --- a/mmv1/products/appengine/ServiceNetworkSettings.yaml +++ b/mmv1/products/appengine/ServiceNetworkSettings.yaml @@ -13,6 +13,7 @@ --- name: 'ServiceNetworkSettings' +api_resource_type_kind: Service description: | A NetworkSettings resource is a container for ingress settings for a version or service. references: diff --git a/mmv1/products/appengine/ServiceSplitTraffic.yaml b/mmv1/products/appengine/ServiceSplitTraffic.yaml index acf21cd99ef1..9ad2d5ca4566 100644 --- a/mmv1/products/appengine/ServiceSplitTraffic.yaml +++ b/mmv1/products/appengine/ServiceSplitTraffic.yaml @@ -13,6 +13,7 @@ --- name: 'ServiceSplitTraffic' +api_resource_type_kind: Service description: | Traffic routing configuration for versions within a single service. Traffic splits define how traffic directed to the service is assigned to versions. references: diff --git a/mmv1/products/appengine/StandardAppVersion.yaml b/mmv1/products/appengine/StandardAppVersion.yaml index 9c2e02fb945c..ebc4b99ac52a 100644 --- a/mmv1/products/appengine/StandardAppVersion.yaml +++ b/mmv1/products/appengine/StandardAppVersion.yaml @@ -17,6 +17,7 @@ # other fields may have different defaults. However, some fields are the same. If fixing a bug # in one, please check the other for the same fix. name: 'StandardAppVersion' +api_resource_type_kind: Version description: | Standard App Version resource to create a new version of standard GAE Application. Learn about the differences between the standard environment and the flexible environment diff --git a/mmv1/products/apphub/Application.yaml b/mmv1/products/apphub/Application.yaml index 1abcc520e527..dc932932af70 100644 --- a/mmv1/products/apphub/Application.yaml +++ b/mmv1/products/apphub/Application.yaml @@ -43,14 +43,30 @@ async: path: 'error' message: 'message' custom_code: + constants: 'templates/terraform/constants/apphub_application.go.tmpl' +custom_diff: + - 'apphubApplicationCustomizeDiff' examples: - - name: 'application_basic' + - name: 'apphub_application_basic' + primary_resource_id: 'example' + vars: + application_id: 'example-application' + location: 'us-east1' + scope_type: 'REGIONAL' + test_vars_overrides: + 'location': '"us-east1"' + 'scope_type': '"REGIONAL"' + - name: 'apphub_application_global_basic' config_path: 'templates/terraform/examples/apphub_application_basic.tf.tmpl' primary_resource_id: 'example' vars: application_id: 'example-application' - - name: 'application_full' - config_path: 'templates/terraform/examples/apphub_application_full.tf.tmpl' + location: 'global' + scope_type: 'GLOBAL' + test_vars_overrides: + 'location': '"global"' + 'scope_type': '"GLOBAL"' + - name: 'apphub_application_full' primary_resource_id: 'example2' vars: application_id: 'example-application' @@ -171,10 +187,11 @@ properties: properties: - name: 'type' type: Enum - description: "Required. Scope Type. \n Possible values:\nREGIONAL" + description: "Required. Scope Type. \n Possible values:\nREGIONAL\nGLOBAL" required: true enum_values: - 'REGIONAL' + - 'GLOBAL' - name: 'uid' type: String description: 'Output only. A universally unique identifier (in UUID4 format) for diff --git a/mmv1/products/artifactregistry/VPCSCConfig.yaml b/mmv1/products/artifactregistry/VPCSCConfig.yaml index 01bde3cfff05..d04a75ea7324 100644 --- a/mmv1/products/artifactregistry/VPCSCConfig.yaml +++ b/mmv1/products/artifactregistry/VPCSCConfig.yaml @@ -13,6 +13,7 @@ --- name: 'VPCSCConfig' +api_resource_type_kind: VpcscConfig description: |- The Artifact Registry VPC SC config that applies to a Project. min_version: 'beta' diff --git a/mmv1/products/bigquery/DatasetAccess.yaml b/mmv1/products/bigquery/DatasetAccess.yaml index 8f1437ae9225..d984f0d5d989 100644 --- a/mmv1/products/bigquery/DatasetAccess.yaml +++ b/mmv1/products/bigquery/DatasetAccess.yaml @@ -13,6 +13,7 @@ --- name: 'DatasetAccess' +api_resource_type_kind: Dataset description: | Gives dataset access for a single entity. This resource is intended to be used in cases where it is not possible to compile a full list of access blocks to include in a diff --git a/mmv1/products/bigqueryconnection/Connection.yaml b/mmv1/products/bigqueryconnection/Connection.yaml index ab1781551eac..f212a1359483 100644 --- a/mmv1/products/bigqueryconnection/Connection.yaml +++ b/mmv1/products/bigqueryconnection/Connection.yaml @@ -117,7 +117,7 @@ examples: region_override: 'US' vars: connection_id: 'my-connection' - - name: 'bigquery_connection_kms' + - name: 'bigquery_connection_sql_with_cmek' primary_resource_id: 'bq-connection-cmek' vars: database_instance_name: 'my-database-instance' @@ -127,7 +127,6 @@ examples: test_vars_overrides: 'deletion_protection': 'false' 'kms_key_name': 'acctest.BootstrapKMSKey(t).CryptoKey.Name' - 'policyChanged': 'acctest.BootstrapPSARole(t, "bq-", "bigquery-encryption", "roles/cloudkms.cryptoKeyEncrypterDecrypter")' oics_vars_overrides: 'deletion_protection': 'false' ignore_read_extra: diff --git a/mmv1/products/bigquerydatatransfer/Config.yaml b/mmv1/products/bigquerydatatransfer/Config.yaml index 093bc363eac7..5c6b2bbff035 100644 --- a/mmv1/products/bigquerydatatransfer/Config.yaml +++ b/mmv1/products/bigquerydatatransfer/Config.yaml @@ -13,6 +13,7 @@ --- name: 'Config' +api_resource_type_kind: TransferConfig description: | Represents a data transfer configuration. A transfer configuration contains all metadata needed to perform a data transfer. diff --git a/mmv1/products/billing/ProjectInfo.yaml b/mmv1/products/billing/ProjectInfo.yaml index b33a18ec292a..2c861183eb23 100644 --- a/mmv1/products/billing/ProjectInfo.yaml +++ b/mmv1/products/billing/ProjectInfo.yaml @@ -13,6 +13,7 @@ --- name: 'ProjectInfo' +api_resource_type_kind: ProjectBillingInfo description: | Billing information for a project. references: diff --git a/mmv1/products/blockchainnodeengine/BlockchainNodes.yaml b/mmv1/products/blockchainnodeengine/BlockchainNodes.yaml index 02692648783f..1ce584deb9aa 100644 --- a/mmv1/products/blockchainnodeengine/BlockchainNodes.yaml +++ b/mmv1/products/blockchainnodeengine/BlockchainNodes.yaml @@ -13,6 +13,7 @@ --- name: 'BlockchainNodes' +api_resource_type_kind: BlockchainNode description: | A representation of a blockchain node. references: diff --git a/mmv1/products/cloudasset/FolderFeed.yaml b/mmv1/products/cloudasset/FolderFeed.yaml index 6e544f09493e..0f0377c38f88 100644 --- a/mmv1/products/cloudasset/FolderFeed.yaml +++ b/mmv1/products/cloudasset/FolderFeed.yaml @@ -13,6 +13,7 @@ --- name: 'FolderFeed' +api_resource_type_kind: Feed description: | Describes a Cloud Asset Inventory feed used to to listen to asset updates. references: diff --git a/mmv1/products/cloudasset/OrganizationFeed.yaml b/mmv1/products/cloudasset/OrganizationFeed.yaml index 611b22c4287e..233efd48ca4f 100644 --- a/mmv1/products/cloudasset/OrganizationFeed.yaml +++ b/mmv1/products/cloudasset/OrganizationFeed.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationFeed' +api_resource_type_kind: Feed description: | Describes a Cloud Asset Inventory feed used to to listen to asset updates. references: diff --git a/mmv1/products/cloudasset/ProjectFeed.yaml b/mmv1/products/cloudasset/ProjectFeed.yaml index 33c1362a14d1..84345f827519 100644 --- a/mmv1/products/cloudasset/ProjectFeed.yaml +++ b/mmv1/products/cloudasset/ProjectFeed.yaml @@ -13,6 +13,7 @@ --- name: 'ProjectFeed' +api_resource_type_kind: Feed description: | Describes a Cloud Asset Inventory feed used to to listen to asset updates. references: diff --git a/mmv1/products/cloudbuild/Trigger.yaml b/mmv1/products/cloudbuild/Trigger.yaml index 9297f0831d34..68350ea563dd 100644 --- a/mmv1/products/cloudbuild/Trigger.yaml +++ b/mmv1/products/cloudbuild/Trigger.yaml @@ -13,6 +13,7 @@ --- name: 'Trigger' +api_resource_type_kind: BuildTrigger description: | Configuration for an automated build in response to source repository changes. references: diff --git a/mmv1/products/cloudfunctions2/Function.yaml b/mmv1/products/cloudfunctions2/Function.yaml index c5dadbe2b121..bc32e810632d 100644 --- a/mmv1/products/cloudfunctions2/Function.yaml +++ b/mmv1/products/cloudfunctions2/Function.yaml @@ -13,6 +13,7 @@ --- name: 'function' +api_resource_type_kind: Function description: | A Cloud Function that contains user computation executed in response to an event. references: diff --git a/mmv1/products/cloudidentity/GroupMembership.yaml b/mmv1/products/cloudidentity/GroupMembership.yaml index c89d41f2607a..9b0376a33642 100644 --- a/mmv1/products/cloudidentity/GroupMembership.yaml +++ b/mmv1/products/cloudidentity/GroupMembership.yaml @@ -13,6 +13,7 @@ --- name: 'GroupMembership' +api_resource_type_kind: Membership description: | A Membership defines a relationship between a Group and an entity belonging to that Group, referred to as a "member". references: diff --git a/mmv1/products/compute/BackendBucketSignedUrlKey.yaml b/mmv1/products/compute/BackendBucketSignedUrlKey.yaml index 860df8a14ba9..72408dcecb83 100644 --- a/mmv1/products/compute/BackendBucketSignedUrlKey.yaml +++ b/mmv1/products/compute/BackendBucketSignedUrlKey.yaml @@ -13,6 +13,7 @@ --- name: 'BackendBucketSignedUrlKey' +api_resource_type_kind: BackendBucket kind: 'compute#BackendBucketSignedUrlKey' description: | A key for signing Cloud CDN signed URLs for BackendBuckets. diff --git a/mmv1/products/compute/BackendServiceSignedUrlKey.yaml b/mmv1/products/compute/BackendServiceSignedUrlKey.yaml index b19dc02e4b55..95097bc60922 100644 --- a/mmv1/products/compute/BackendServiceSignedUrlKey.yaml +++ b/mmv1/products/compute/BackendServiceSignedUrlKey.yaml @@ -13,6 +13,7 @@ --- name: 'BackendServiceSignedUrlKey' +api_resource_type_kind: BackendService kind: 'compute#BackendServiceSignedUrlKey' description: | A key for signing Cloud CDN signed URLs for Backend Services. diff --git a/mmv1/products/compute/DiskResourcePolicyAttachment.yaml b/mmv1/products/compute/DiskResourcePolicyAttachment.yaml index 8f088c140896..b301a4debb44 100644 --- a/mmv1/products/compute/DiskResourcePolicyAttachment.yaml +++ b/mmv1/products/compute/DiskResourcePolicyAttachment.yaml @@ -13,6 +13,7 @@ --- name: 'DiskResourcePolicyAttachment' +api_resource_type_kind: Disk description: | Adds existing resource policies to a disk. You can only add one policy which will be applied to this disk for scheduling snapshot creation. diff --git a/mmv1/products/compute/FirewallPolicyWithRules.yaml b/mmv1/products/compute/FirewallPolicyWithRules.yaml index 2af81138de81..e9153e8a3bc7 100644 --- a/mmv1/products/compute/FirewallPolicyWithRules.yaml +++ b/mmv1/products/compute/FirewallPolicyWithRules.yaml @@ -13,6 +13,7 @@ --- name: 'FirewallPolicyWithRules' +api_resource_type_kind: FirewallPolicy description: | The Compute FirewallPolicy with rules resource. It declaratively manges all rules in the firewall policy. diff --git a/mmv1/products/compute/GlobalForwardingRule.yaml b/mmv1/products/compute/GlobalForwardingRule.yaml index 3c816753c7e2..760bc472528f 100644 --- a/mmv1/products/compute/GlobalForwardingRule.yaml +++ b/mmv1/products/compute/GlobalForwardingRule.yaml @@ -13,6 +13,7 @@ --- name: 'GlobalForwardingRule' +api_resource_type_kind: ForwardingRule kind: 'compute#forwardingRule' description: | Represents a GlobalForwardingRule resource. Global forwarding rules are diff --git a/mmv1/products/compute/GlobalNetworkEndpoint.yaml b/mmv1/products/compute/GlobalNetworkEndpoint.yaml index 2f267d0fd921..4439f55b071f 100644 --- a/mmv1/products/compute/GlobalNetworkEndpoint.yaml +++ b/mmv1/products/compute/GlobalNetworkEndpoint.yaml @@ -13,6 +13,7 @@ --- name: 'GlobalNetworkEndpoint' +api_resource_type_kind: NetworkEndpointGroup kind: 'compute#networkEndpoint' description: | A Global Network endpoint represents a IP address and port combination that exists outside of GCP. diff --git a/mmv1/products/compute/GlobalNetworkEndpointGroup.yaml b/mmv1/products/compute/GlobalNetworkEndpointGroup.yaml index d3d7af7da059..ea990438f560 100644 --- a/mmv1/products/compute/GlobalNetworkEndpointGroup.yaml +++ b/mmv1/products/compute/GlobalNetworkEndpointGroup.yaml @@ -13,6 +13,7 @@ --- name: 'GlobalNetworkEndpointGroup' +api_resource_type_kind: NetworkEndpointGroup kind: 'compute#networkEndpointGroup' description: | A global network endpoint group contains endpoints that reside outside of Google Cloud. diff --git a/mmv1/products/compute/HaVpnGateway.yaml b/mmv1/products/compute/HaVpnGateway.yaml index 1c12ffc5d8f1..2526a4ab6478 100644 --- a/mmv1/products/compute/HaVpnGateway.yaml +++ b/mmv1/products/compute/HaVpnGateway.yaml @@ -13,6 +13,7 @@ --- name: 'HaVpnGateway' +api_resource_type_kind: VpnGateway kind: 'compute#vpnGateway' description: | Represents a VPN gateway running in GCP. This virtual device is managed diff --git a/mmv1/products/compute/Image.yaml b/mmv1/products/compute/Image.yaml index 46036e51e7ff..bdaffea3863d 100644 --- a/mmv1/products/compute/Image.yaml +++ b/mmv1/products/compute/Image.yaml @@ -69,14 +69,17 @@ examples: primary_resource_id: 'example' primary_resource_name: 'fmt.Sprintf("tf-test-example-image%s", context["random_suffix"])' vars: + disk_name: 'example-disk' image_name: 'example-image' - name: 'image_guest_os' primary_resource_id: 'example' vars: + disk_name: 'example-disk' image_name: 'example-image' - name: 'image_basic_storage_location' primary_resource_id: 'example' vars: + disk_name: 'example-disk' image_name: 'example-sl-image' primary_resource_name: 'fmt.Sprintf("tf-test-sl-example-image%s", context["random_suffix"])' parameters: diff --git a/mmv1/products/compute/InstanceGroupMembership.yaml b/mmv1/products/compute/InstanceGroupMembership.yaml index 0236c91b9ec6..9b8ad1f91220 100644 --- a/mmv1/products/compute/InstanceGroupMembership.yaml +++ b/mmv1/products/compute/InstanceGroupMembership.yaml @@ -13,6 +13,7 @@ --- name: 'InstanceGroupMembership' +api_resource_type_kind: InstanceGroup kind: 'compute#instanceGroup' description: | Represents the Instance membership to the Instance Group. diff --git a/mmv1/products/compute/InstanceGroupNamedPort.yaml b/mmv1/products/compute/InstanceGroupNamedPort.yaml index 73def1eff8cd..83cfb674421e 100644 --- a/mmv1/products/compute/InstanceGroupNamedPort.yaml +++ b/mmv1/products/compute/InstanceGroupNamedPort.yaml @@ -13,6 +13,7 @@ --- name: 'InstanceGroupNamedPort' +api_resource_type_kind: InstanceGroup description: | Mange the named ports setting for a managed instance group without managing the group as whole. This resource is primarily intended for use diff --git a/mmv1/products/compute/ManagedSslCertificate.yaml b/mmv1/products/compute/ManagedSslCertificate.yaml index 20403b646bd0..3e3c222fb0ec 100644 --- a/mmv1/products/compute/ManagedSslCertificate.yaml +++ b/mmv1/products/compute/ManagedSslCertificate.yaml @@ -13,6 +13,7 @@ --- name: 'ManagedSslCertificate' +api_resource_type_kind: SslCertificate kind: 'compute#sslCertificate' description: | An SslCertificate resource, used for HTTPS load balancing. This resource diff --git a/mmv1/products/compute/NetworkEndpoint.yaml b/mmv1/products/compute/NetworkEndpoint.yaml index 3cdd5904786d..69a43ca3b49d 100644 --- a/mmv1/products/compute/NetworkEndpoint.yaml +++ b/mmv1/products/compute/NetworkEndpoint.yaml @@ -13,6 +13,7 @@ --- name: 'NetworkEndpoint' +api_resource_type_kind: NetworkEndpointGroup kind: 'compute#networkEndpoint' description: | A Network endpoint represents a IP address and port combination that is diff --git a/mmv1/products/compute/NetworkEndpoints.yaml b/mmv1/products/compute/NetworkEndpoints.yaml index 5fce56f2aeec..e1f8d296721d 100644 --- a/mmv1/products/compute/NetworkEndpoints.yaml +++ b/mmv1/products/compute/NetworkEndpoints.yaml @@ -13,6 +13,7 @@ --- name: 'NetworkEndpoints' +api_resource_type_kind: NetworkEndpointGroup kind: 'compute#networkEndpoints' description: | A set of network endpoints belonging to a network endpoint group (NEG). A diff --git a/mmv1/products/compute/NetworkFirewallPolicy.yaml b/mmv1/products/compute/NetworkFirewallPolicy.yaml index 1792ee6bc09c..5aaa01d52946 100644 --- a/mmv1/products/compute/NetworkFirewallPolicy.yaml +++ b/mmv1/products/compute/NetworkFirewallPolicy.yaml @@ -13,6 +13,7 @@ --- name: 'NetworkFirewallPolicy' +api_resource_type_kind: FirewallPolicy description: "The Compute NetworkFirewallPolicy resource" docs: base_url: 'projects/{{project}}/global/firewallPolicies' diff --git a/mmv1/products/compute/NetworkFirewallPolicyAssociation.yaml b/mmv1/products/compute/NetworkFirewallPolicyAssociation.yaml new file mode 100644 index 000000000000..c24049cdcc80 --- /dev/null +++ b/mmv1/products/compute/NetworkFirewallPolicyAssociation.yaml @@ -0,0 +1,87 @@ +# Copyright 2024 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +name: 'NetworkFirewallPolicyAssociation' +kind: 'compute#firewallPolicyAssociation' +description: | + The Compute NetworkFirewallPolicyAssociation resource +references: + guides: + api: 'https://cloud.google.com/compute/docs/reference/rest/v1/networkFirewallPolicies/addAssociation' +docs: +id_format: 'projects/{{project}}/global/firewallPolicies/{{firewall_policy}}/associations/{{name}}' +base_url: 'projects/{{project}}/global/firewallPolicies/{{firewall_policy}}' +self_link: 'projects/{{project}}/global/firewallPolicies/{{firewall_policy}}/getAssociation?name={{name}}' +create_url: 'projects/{{project}}/global/firewallPolicies/{{firewall_policy}}/addAssociation' +delete_url: 'projects/{{project}}/global/firewallPolicies/{{firewall_policy}}/removeAssociation?name={{name}}' +delete_verb: 'POST' +immutable: true +legacy_long_form_project: true +import_format: + - 'projects/{{project}}/global/firewallPolicies/{{firewall_policy}}/associations/{{name}}' + - '{{project}}/{{firewall_policy}}/{{name}}' +timeouts: + insert_minutes: 20 + update_minutes: 20 + delete_minutes: 20 +async: + actions: ['create', 'delete', 'update'] + type: 'OpAsync' + operation: + base_url: '{{op_id}}' + kind: 'compute#operation' + path: 'name' + wait_ms: 1000 + result: + path: 'targetLink' + resource_inside_response: false + error: + path: 'error/errors' + message: 'message' +examples: + - name: 'network_firewall_policy_association' + primary_resource_id: 'default' + vars: + policy_name: 'my-policy' + association_name: 'my-association' + network_name: 'my-network' + test_env_vars: + project_name: 'PROJECT_NAME' +parameters: + - name: 'firewallPolicy' + type: ResourceRef + description: | + The firewall policy of the resource. + url_param_only: true + required: true + diff_suppress_func: 'tpgresource.CompareSelfLinkOrResourceName' + resource: 'NetworkFirewallPolicy' + imports: 'name' +properties: + - name: 'name' + type: String + description: | + The name for an association. + required: true + - name: 'attachmentTarget' + type: String + description: | + The target that the firewall policy is attached to. + required: true + diff_suppress_func: 'tpgresource.CompareSelfLinkOrResourceName' + - name: 'shortName' + type: String + description: | + The short name of the firewall policy of the association. + output: true diff --git a/mmv1/products/compute/NetworkFirewallPolicyRule.yaml b/mmv1/products/compute/NetworkFirewallPolicyRule.yaml index 75455c4d3b33..5e7afe656eb3 100644 --- a/mmv1/products/compute/NetworkFirewallPolicyRule.yaml +++ b/mmv1/products/compute/NetworkFirewallPolicyRule.yaml @@ -13,6 +13,7 @@ --- name: 'NetworkFirewallPolicyRule' +api_resource_type_kind: FirewallPolicy kind: 'compute#firewallPolicyRule' description: | Represents a rule that describes one or more match conditions along with the action to be taken when traffic matches this condition (allow or deny). diff --git a/mmv1/products/compute/NetworkFirewallPolicyWithRules.yaml b/mmv1/products/compute/NetworkFirewallPolicyWithRules.yaml index 2c89a894586d..a73e10365949 100644 --- a/mmv1/products/compute/NetworkFirewallPolicyWithRules.yaml +++ b/mmv1/products/compute/NetworkFirewallPolicyWithRules.yaml @@ -13,6 +13,7 @@ --- name: 'NetworkFirewallPolicyWithRules' +api_resource_type_kind: FirewallPolicy description: "The Compute NetworkFirewallPolicy with rules resource" min_version: 'beta' docs: diff --git a/mmv1/products/compute/NetworkPeeringRoutesConfig.yaml b/mmv1/products/compute/NetworkPeeringRoutesConfig.yaml index e294562057b6..e3247eda280e 100644 --- a/mmv1/products/compute/NetworkPeeringRoutesConfig.yaml +++ b/mmv1/products/compute/NetworkPeeringRoutesConfig.yaml @@ -13,6 +13,7 @@ --- name: 'NetworkPeeringRoutesConfig' +api_resource_type_kind: Network description: | Manage a network peering's route settings without managing the peering as a whole. This resource is primarily intended for use with GCP-generated diff --git a/mmv1/products/compute/OrganizationSecurityPolicy.yaml b/mmv1/products/compute/OrganizationSecurityPolicy.yaml index d30931f598ce..966188adf897 100644 --- a/mmv1/products/compute/OrganizationSecurityPolicy.yaml +++ b/mmv1/products/compute/OrganizationSecurityPolicy.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationSecurityPolicy' +api_resource_type_kind: SecurityPolicy description: | Organization security policies are used to control incoming/outgoing traffic. min_version: 'beta' diff --git a/mmv1/products/compute/OrganizationSecurityPolicyAssociation.yaml b/mmv1/products/compute/OrganizationSecurityPolicyAssociation.yaml index 5f1a56b57d41..26487f435321 100644 --- a/mmv1/products/compute/OrganizationSecurityPolicyAssociation.yaml +++ b/mmv1/products/compute/OrganizationSecurityPolicyAssociation.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationSecurityPolicyAssociation' +api_resource_type_kind: SecurityPolicy description: | An association for the OrganizationSecurityPolicy. min_version: 'beta' diff --git a/mmv1/products/compute/OrganizationSecurityPolicyRule.yaml b/mmv1/products/compute/OrganizationSecurityPolicyRule.yaml index db79e3d9b1d7..4f5e748eced0 100644 --- a/mmv1/products/compute/OrganizationSecurityPolicyRule.yaml +++ b/mmv1/products/compute/OrganizationSecurityPolicyRule.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationSecurityPolicyRule' +api_resource_type_kind: SecurityPolicy description: | A rule for the OrganizationSecurityPolicy. min_version: 'beta' diff --git a/mmv1/products/compute/PerInstanceConfig.yaml b/mmv1/products/compute/PerInstanceConfig.yaml index a4aed0002834..4d577afe3ffc 100644 --- a/mmv1/products/compute/PerInstanceConfig.yaml +++ b/mmv1/products/compute/PerInstanceConfig.yaml @@ -13,6 +13,7 @@ --- name: 'PerInstanceConfig' +api_resource_type_kind: InstanceGroupManager description: | A config defined for a single managed instance that belongs to an instance group manager. It preserves the instance name across instance group manager operations and can define stateful disks or metadata that are unique to the instance. diff --git a/mmv1/products/compute/ProjectCloudArmorTier.yaml b/mmv1/products/compute/ProjectCloudArmorTier.yaml index 2eeb6d5d7fc3..5c8ebe33fd73 100644 --- a/mmv1/products/compute/ProjectCloudArmorTier.yaml +++ b/mmv1/products/compute/ProjectCloudArmorTier.yaml @@ -13,6 +13,7 @@ --- name: 'ProjectCloudArmorTier' +api_resource_type_kind: Project description: | Sets the Cloud Armor tier of the project. references: diff --git a/mmv1/products/compute/RegionAutoscaler.yaml b/mmv1/products/compute/RegionAutoscaler.yaml index 74463542f425..064189025d4f 100644 --- a/mmv1/products/compute/RegionAutoscaler.yaml +++ b/mmv1/products/compute/RegionAutoscaler.yaml @@ -13,6 +13,7 @@ --- name: 'RegionAutoscaler' +api_resource_type_kind: Autoscaler kind: 'compute#autoscaler' description: | Represents an Autoscaler resource. diff --git a/mmv1/products/compute/RegionBackendService.yaml b/mmv1/products/compute/RegionBackendService.yaml index 4ce7fdf5be6b..ce3cc796194f 100644 --- a/mmv1/products/compute/RegionBackendService.yaml +++ b/mmv1/products/compute/RegionBackendService.yaml @@ -13,6 +13,7 @@ --- name: 'RegionBackendService' +api_resource_type_kind: BackendService kind: 'compute#backendService' description: | A Region Backend Service defines a regionally-scoped group of virtual diff --git a/mmv1/products/compute/RegionCommitment.yaml b/mmv1/products/compute/RegionCommitment.yaml index 38f332a23341..177ac627c8a4 100644 --- a/mmv1/products/compute/RegionCommitment.yaml +++ b/mmv1/products/compute/RegionCommitment.yaml @@ -13,6 +13,7 @@ --- name: 'RegionCommitment' +api_resource_type_kind: Commitment kind: 'compute#commitment' description: | Represents a regional Commitment resource. diff --git a/mmv1/products/compute/RegionDiskResourcePolicyAttachment.yaml b/mmv1/products/compute/RegionDiskResourcePolicyAttachment.yaml index c2eeedbb584e..2aba34f08240 100644 --- a/mmv1/products/compute/RegionDiskResourcePolicyAttachment.yaml +++ b/mmv1/products/compute/RegionDiskResourcePolicyAttachment.yaml @@ -13,6 +13,7 @@ --- name: 'RegionDiskResourcePolicyAttachment' +api_resource_type_kind: Disk description: | Adds existing resource policies to a disk. You can only add one policy which will be applied to this disk for scheduling snapshot creation. diff --git a/mmv1/products/compute/RegionHealthCheck.yaml b/mmv1/products/compute/RegionHealthCheck.yaml index 42419bc26890..616491ff6558 100644 --- a/mmv1/products/compute/RegionHealthCheck.yaml +++ b/mmv1/products/compute/RegionHealthCheck.yaml @@ -13,6 +13,7 @@ --- name: 'RegionHealthCheck' +api_resource_type_kind: HealthCheck kind: 'compute#healthCheck' description: | Health Checks determine whether instances are responsive and able to do work. diff --git a/mmv1/products/compute/RegionNetworkEndpoint.yaml b/mmv1/products/compute/RegionNetworkEndpoint.yaml index eaa29f307f11..044805b852bb 100644 --- a/mmv1/products/compute/RegionNetworkEndpoint.yaml +++ b/mmv1/products/compute/RegionNetworkEndpoint.yaml @@ -13,6 +13,7 @@ --- name: 'RegionNetworkEndpoint' +api_resource_type_kind: NetworkEndpointGroup kind: 'compute#networkEndpoint' description: | A Region network endpoint represents a IP address/FQDN and port combination that is diff --git a/mmv1/products/compute/RegionNetworkEndpointGroup.yaml b/mmv1/products/compute/RegionNetworkEndpointGroup.yaml index bbff58495400..ef911ae8f502 100644 --- a/mmv1/products/compute/RegionNetworkEndpointGroup.yaml +++ b/mmv1/products/compute/RegionNetworkEndpointGroup.yaml @@ -13,6 +13,7 @@ --- name: 'RegionNetworkEndpointGroup' +api_resource_type_kind: NetworkEndpointGroup kind: 'compute#networkEndpointGroup' description: | A regional NEG that can support Serverless Products, proxying traffic to diff --git a/mmv1/products/compute/RegionNetworkFirewallPolicy.yaml b/mmv1/products/compute/RegionNetworkFirewallPolicy.yaml index 13dfcebdcaeb..51dbe294156f 100644 --- a/mmv1/products/compute/RegionNetworkFirewallPolicy.yaml +++ b/mmv1/products/compute/RegionNetworkFirewallPolicy.yaml @@ -13,6 +13,7 @@ --- name: 'RegionNetworkFirewallPolicy' +api_resource_type_kind: FirewallPolicy description: "The Compute NetworkFirewallPolicy resource" docs: base_url: 'projects/{{project}}/regions/{{region}}/firewallPolicies' diff --git a/mmv1/products/compute/RegionNetworkFirewallPolicyAssociation.yaml b/mmv1/products/compute/RegionNetworkFirewallPolicyAssociation.yaml new file mode 100644 index 000000000000..a30ff8a73493 --- /dev/null +++ b/mmv1/products/compute/RegionNetworkFirewallPolicyAssociation.yaml @@ -0,0 +1,93 @@ +# Copyright 2024 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +name: 'RegionNetworkFirewallPolicyAssociation' +kind: 'compute#firewallPolicyAssociation' +description: | + The Compute NetworkFirewallPolicyAssociation resource +references: + guides: + api: 'https://cloud.google.com/compute/docs/reference/rest/v1/regionNetworkFirewallPolicies/addAssociation' +docs: +id_format: 'projects/{{project}}/regions/{{region}}/firewallPolicies/{{firewall_policy}}/associations/{{name}}' +base_url: 'projects/{{project}}/regions/{{region}}/firewallPolicies/{{firewall_policy}}' +self_link: 'projects/{{project}}/regions/{{region}}/firewallPolicies/{{firewall_policy}}/getAssociation?name={{name}}' +create_url: 'projects/{{project}}/regions/{{region}}/firewallPolicies/{{firewall_policy}}/addAssociation' +delete_url: 'projects/{{project}}/regions/{{region}}/firewallPolicies/{{firewall_policy}}/removeAssociation?name={{name}}' +delete_verb: 'POST' +immutable: true +legacy_long_form_project: true +import_format: + - 'projects/{{project}}/regions/{{region}}/firewallPolicies/{{firewall_policy}}/associations/{{name}}' + - '{{project}}/{{firewall_policy}}/{{name}}' +timeouts: + insert_minutes: 20 + update_minutes: 20 + delete_minutes: 20 +async: + actions: ['create', 'delete', 'update'] + type: 'OpAsync' + operation: + base_url: '{{op_id}}' + kind: 'compute#operation' + path: 'name' + wait_ms: 1000 + result: + path: 'targetLink' + resource_inside_response: false + error: + path: 'error/errors' + message: 'message' +examples: + - name: 'region_network_firewall_policy_association' + primary_resource_id: 'default' + vars: + policy_name: 'my-policy' + association_name: 'my-association' + network_name: 'my-network' + test_env_vars: + region: 'REGION' + project_name: 'PROJECT_NAME' +parameters: + - name: 'firewallPolicy' + type: ResourceRef + description: | + The firewall policy of the resource. + url_param_only: true + required: true + diff_suppress_func: 'tpgresource.CompareSelfLinkOrResourceName' + resource: 'RegionNetworkFirewallPolicy' + imports: 'name' + - name: 'region' + type: String + description: 'The location of this resource.' + url_param_only: true + default_from_api: true +properties: + - name: 'name' + type: String + description: | + The name for an association. + required: true + - name: 'attachmentTarget' + type: String + description: | + The target that the firewall policy is attached to. + required: true + diff_suppress_func: 'tpgresource.CompareSelfLinkOrResourceName' + - name: 'shortName' + type: String + description: | + The short name of the firewall policy of the association. + output: true diff --git a/mmv1/products/compute/RegionNetworkFirewallPolicyRule.yaml b/mmv1/products/compute/RegionNetworkFirewallPolicyRule.yaml index 06bf26c8ed40..d62b527a63d4 100644 --- a/mmv1/products/compute/RegionNetworkFirewallPolicyRule.yaml +++ b/mmv1/products/compute/RegionNetworkFirewallPolicyRule.yaml @@ -13,6 +13,7 @@ --- name: 'RegionNetworkFirewallPolicyRule' +api_resource_type_kind: FirewallPolicy kind: 'compute#firewallPolicyRule' description: | Represents a rule that describes one or more match conditions along with the action to be taken when traffic matches this condition (allow or deny). diff --git a/mmv1/products/compute/RegionNetworkFirewallPolicyWithRules.yaml b/mmv1/products/compute/RegionNetworkFirewallPolicyWithRules.yaml index f9a8100f09e1..7277398ab659 100644 --- a/mmv1/products/compute/RegionNetworkFirewallPolicyWithRules.yaml +++ b/mmv1/products/compute/RegionNetworkFirewallPolicyWithRules.yaml @@ -13,6 +13,7 @@ --- name: 'RegionNetworkFirewallPolicyWithRules' +api_resource_type_kind: FirewallPolicy description: "The Compute NetworkFirewallPolicy with rules resource" min_version: 'beta' docs: diff --git a/mmv1/products/compute/RegionPerInstanceConfig.yaml b/mmv1/products/compute/RegionPerInstanceConfig.yaml index 9d0a6340a5ab..67089ff07079 100644 --- a/mmv1/products/compute/RegionPerInstanceConfig.yaml +++ b/mmv1/products/compute/RegionPerInstanceConfig.yaml @@ -13,6 +13,7 @@ --- name: 'RegionPerInstanceConfig' +api_resource_type_kind: InstanceGroupManager description: | A config defined for a single managed instance that belongs to an instance group manager. It preserves the instance name across instance group manager operations and can define stateful disks or metadata that are unique to the instance. diff --git a/mmv1/products/compute/RegionSecurityPolicy.yaml b/mmv1/products/compute/RegionSecurityPolicy.yaml index b41615004665..3876b2baf76e 100644 --- a/mmv1/products/compute/RegionSecurityPolicy.yaml +++ b/mmv1/products/compute/RegionSecurityPolicy.yaml @@ -13,6 +13,7 @@ --- name: 'RegionSecurityPolicy' +api_resource_type_kind: SecurityPolicy description: | Represents a Region Cloud Armor Security Policy resource. min_version: 'beta' diff --git a/mmv1/products/compute/RegionSecurityPolicyRule.yaml b/mmv1/products/compute/RegionSecurityPolicyRule.yaml index 765935d2fe9c..c70a20ddcb83 100644 --- a/mmv1/products/compute/RegionSecurityPolicyRule.yaml +++ b/mmv1/products/compute/RegionSecurityPolicyRule.yaml @@ -13,6 +13,7 @@ --- name: 'RegionSecurityPolicyRule' +api_resource_type_kind: SecurityPolicy description: | A rule for the RegionSecurityPolicy. min_version: 'beta' diff --git a/mmv1/products/compute/RegionSslCertificate.yaml b/mmv1/products/compute/RegionSslCertificate.yaml index 1bb481b10e65..e5b796d9d04a 100644 --- a/mmv1/products/compute/RegionSslCertificate.yaml +++ b/mmv1/products/compute/RegionSslCertificate.yaml @@ -13,6 +13,7 @@ --- name: 'RegionSslCertificate' +api_resource_type_kind: SslCertificate kind: 'compute#sslCertificate' description: | A RegionSslCertificate resource, used for HTTPS load balancing. This resource diff --git a/mmv1/products/compute/RegionSslPolicy.yaml b/mmv1/products/compute/RegionSslPolicy.yaml index f09fc0432096..ea216cd7ce9a 100644 --- a/mmv1/products/compute/RegionSslPolicy.yaml +++ b/mmv1/products/compute/RegionSslPolicy.yaml @@ -13,6 +13,7 @@ --- name: 'RegionSslPolicy' +api_resource_type_kind: SslPolicy kind: 'compute#sslPolicy' description: | Represents a Regional SSL policy. SSL policies give you the ability to control the diff --git a/mmv1/products/compute/RegionTargetHttpProxy.yaml b/mmv1/products/compute/RegionTargetHttpProxy.yaml index 9e31fb6966e0..5d7168ba5338 100644 --- a/mmv1/products/compute/RegionTargetHttpProxy.yaml +++ b/mmv1/products/compute/RegionTargetHttpProxy.yaml @@ -13,6 +13,7 @@ --- name: 'RegionTargetHttpProxy' +api_resource_type_kind: TargetHttpProxy description: | Represents a RegionTargetHttpProxy resource, which is used by one or more forwarding rules to route incoming HTTP requests to a URL map. diff --git a/mmv1/products/compute/RegionTargetHttpsProxy.yaml b/mmv1/products/compute/RegionTargetHttpsProxy.yaml index 9348a3799b67..68405584ee97 100644 --- a/mmv1/products/compute/RegionTargetHttpsProxy.yaml +++ b/mmv1/products/compute/RegionTargetHttpsProxy.yaml @@ -13,6 +13,7 @@ --- name: 'RegionTargetHttpsProxy' +api_resource_type_kind: TargetHttpsProxy description: | Represents a RegionTargetHttpsProxy resource, which is used by one or more forwarding rules to route incoming HTTPS requests to a URL map. diff --git a/mmv1/products/compute/RegionTargetTcpProxy.yaml b/mmv1/products/compute/RegionTargetTcpProxy.yaml index a6b97416621f..2d7a9473a218 100644 --- a/mmv1/products/compute/RegionTargetTcpProxy.yaml +++ b/mmv1/products/compute/RegionTargetTcpProxy.yaml @@ -13,6 +13,7 @@ --- name: 'RegionTargetTcpProxy' +api_resource_type_kind: TargetTcpProxy description: | Represents a RegionTargetTcpProxy resource, which is used by one or more forwarding rules to route incoming TCP requests to a regional TCP proxy load diff --git a/mmv1/products/compute/RegionUrlMap.yaml b/mmv1/products/compute/RegionUrlMap.yaml index 093a6b0ce6e2..ecd19552159a 100644 --- a/mmv1/products/compute/RegionUrlMap.yaml +++ b/mmv1/products/compute/RegionUrlMap.yaml @@ -13,6 +13,7 @@ --- name: 'RegionUrlMap' +api_resource_type_kind: UrlMap kind: 'compute#urlMap' description: | UrlMaps are used to route requests to a backend service based on rules diff --git a/mmv1/products/compute/ResizeRequest.yaml b/mmv1/products/compute/ResizeRequest.yaml index 80fc2e19e459..4540cf55dbc3 100644 --- a/mmv1/products/compute/ResizeRequest.yaml +++ b/mmv1/products/compute/ResizeRequest.yaml @@ -13,6 +13,7 @@ --- name: 'ResizeRequest' +api_resource_type_kind: InstanceGroupManagerResizeRequest kind: 'compute#instanceGroupManagerResizeRequest' description: | Represents a Managed Instance Group Resize Request diff --git a/mmv1/products/compute/RouterNat.yaml b/mmv1/products/compute/RouterNat.yaml index 08eff670dde3..426194faeef2 100644 --- a/mmv1/products/compute/RouterNat.yaml +++ b/mmv1/products/compute/RouterNat.yaml @@ -13,6 +13,7 @@ --- name: 'RouterNat' +api_resource_type_kind: Router description: | A NAT service created in a router. diff --git a/mmv1/products/compute/RouterNatAddress.yaml b/mmv1/products/compute/RouterNatAddress.yaml index 844896518a5e..c1347ec622d8 100644 --- a/mmv1/products/compute/RouterNatAddress.yaml +++ b/mmv1/products/compute/RouterNatAddress.yaml @@ -13,6 +13,7 @@ --- name: 'RouterNatAddress' +api_resource_type_kind: Router description: | A resource used to set the list of IP addresses to be used in a NAT service and manage the draining of destroyed IPs. diff --git a/mmv1/products/compute/RouterRoutePolicy.yaml b/mmv1/products/compute/RouterRoutePolicy.yaml index d3c15564138f..b0b98bb74152 100644 --- a/mmv1/products/compute/RouterRoutePolicy.yaml +++ b/mmv1/products/compute/RouterRoutePolicy.yaml @@ -13,6 +13,7 @@ --- name: 'RouterRoutePolicy' +api_resource_type_kind: Router description: A route policy created in a router min_version: 'beta' references: diff --git a/mmv1/products/compute/SecurityPolicyRule.yaml b/mmv1/products/compute/SecurityPolicyRule.yaml index 85d3067fd82c..1f843b126c43 100644 --- a/mmv1/products/compute/SecurityPolicyRule.yaml +++ b/mmv1/products/compute/SecurityPolicyRule.yaml @@ -13,6 +13,7 @@ --- name: 'SecurityPolicyRule' +api_resource_type_kind: SecurityPolicy description: | A rule for the SecurityPolicy. references: diff --git a/mmv1/products/compute/ServiceAttachment.yaml b/mmv1/products/compute/ServiceAttachment.yaml index 8d6650c63704..5a40d5130f19 100644 --- a/mmv1/products/compute/ServiceAttachment.yaml +++ b/mmv1/products/compute/ServiceAttachment.yaml @@ -169,7 +169,6 @@ properties: type: Integer description: | The number of consumer Network Connectivity Center spokes that the connected Private Service Connect endpoint has propagated to. - min_version: 'beta' output: true - name: 'targetService' type: String @@ -266,5 +265,4 @@ properties: If the connection preference of the service attachment is ACCEPT_AUTOMATIC, the limit applies to each project that contains a connected endpoint. If unspecified, the default propagated connection limit is 250. - min_version: 'beta' default_from_api: true diff --git a/mmv1/products/containerattached/Cluster.yaml b/mmv1/products/containerattached/Cluster.yaml index d5d9f173e7fb..6cb479944e3f 100644 --- a/mmv1/products/containerattached/Cluster.yaml +++ b/mmv1/products/containerattached/Cluster.yaml @@ -13,6 +13,7 @@ --- name: 'Cluster' +api_resource_type_kind: AttachedCluster description: | An Anthos cluster running on customer owned infrastructure. references: @@ -359,6 +360,7 @@ properties: description: | Enable/Disable Security Posture API features for the cluster. default_from_api: true + deprecation_message: '`security_posture_config` is deprecated and will be removed in a future major release.' properties: - name: 'vulnerabilityMode' type: Enum diff --git a/mmv1/products/dataform/RepositoryReleaseConfig.yaml b/mmv1/products/dataform/RepositoryReleaseConfig.yaml index 05804fb325ac..0b31ab83a014 100644 --- a/mmv1/products/dataform/RepositoryReleaseConfig.yaml +++ b/mmv1/products/dataform/RepositoryReleaseConfig.yaml @@ -13,6 +13,7 @@ --- name: 'RepositoryReleaseConfig' +api_resource_type_kind: ReleaseConfig description: |- A resource represents a Dataform release configuration min_version: 'beta' diff --git a/mmv1/products/dataform/RepositoryWorkflowConfig.yaml b/mmv1/products/dataform/RepositoryWorkflowConfig.yaml index cab9d34108db..293bd811996c 100644 --- a/mmv1/products/dataform/RepositoryWorkflowConfig.yaml +++ b/mmv1/products/dataform/RepositoryWorkflowConfig.yaml @@ -13,6 +13,7 @@ --- name: 'RepositoryWorkflowConfig' +api_resource_type_kind: WorkflowConfig description: |- A resource represents a Dataform workflow configuration min_version: 'beta' diff --git a/mmv1/products/dataplex/Datascan.yaml b/mmv1/products/dataplex/Datascan.yaml index f3878e2a2839..c7d1e43e9fd6 100644 --- a/mmv1/products/dataplex/Datascan.yaml +++ b/mmv1/products/dataplex/Datascan.yaml @@ -13,6 +13,7 @@ --- name: 'Datascan' +api_resource_type_kind: DataScan description: | Represents a user-visible job which provides the insights for the related data source. # User-provided label cannot start with goog- diff --git a/mmv1/products/discoveryengine/ChatEngine.yaml b/mmv1/products/discoveryengine/ChatEngine.yaml index d91385c8ba6e..c10cd28ca71c 100644 --- a/mmv1/products/discoveryengine/ChatEngine.yaml +++ b/mmv1/products/discoveryengine/ChatEngine.yaml @@ -13,6 +13,7 @@ --- name: 'ChatEngine' +api_resource_type_kind: Engine description: | Vertex chat and Conversation Engine Chat type references: diff --git a/mmv1/products/discoveryengine/SearchEngine.yaml b/mmv1/products/discoveryengine/SearchEngine.yaml index d33e7c12cbba..9ae92d9ae537 100644 --- a/mmv1/products/discoveryengine/SearchEngine.yaml +++ b/mmv1/products/discoveryengine/SearchEngine.yaml @@ -13,6 +13,7 @@ --- name: 'SearchEngine' +api_resource_type_kind: Engine description: | Vertex AI Search and Conversation can be used to create a search engine or a chat application by connecting it with a datastore references: diff --git a/mmv1/products/documentai/ProcessorDefaultVersion.yaml b/mmv1/products/documentai/ProcessorDefaultVersion.yaml index 059bf49d42bc..b0538d0b9ccd 100644 --- a/mmv1/products/documentai/ProcessorDefaultVersion.yaml +++ b/mmv1/products/documentai/ProcessorDefaultVersion.yaml @@ -13,6 +13,7 @@ --- name: 'ProcessorDefaultVersion' +api_resource_type_kind: Processor description: | The default version for the processor. Deleting this resource is a no-op, and does not unset the default version. docs: diff --git a/mmv1/products/filestore/Instance.yaml b/mmv1/products/filestore/Instance.yaml index 63b7a76b9433..9470ebcdf9b0 100644 --- a/mmv1/products/filestore/Instance.yaml +++ b/mmv1/products/filestore/Instance.yaml @@ -63,7 +63,6 @@ examples: instance_name: 'test-instance' - name: 'filestore_instance_protocol' primary_resource_id: 'instance' - min_version: 'beta' vars: instance_name: 'test-instance' - name: 'filestore_instance_enterprise' @@ -126,7 +125,6 @@ properties: or NFSv4.1, for using NFS version 4.1 as file sharing protocol. NFSv4.1 can be used with HIGH_SCALE_SSD, ZONAL, REGIONAL and ENTERPRISE. The default is NFSv3. - min_version: 'beta' immutable: true custom_flatten: 'templates/terraform/custom_flatten/default_if_empty.tmpl' default_value: "NFS_V3" diff --git a/mmv1/products/firebase/AppleApp.yaml b/mmv1/products/firebase/AppleApp.yaml index 9da65a1da6ae..f0fc923504a7 100644 --- a/mmv1/products/firebase/AppleApp.yaml +++ b/mmv1/products/firebase/AppleApp.yaml @@ -13,6 +13,7 @@ --- name: 'AppleApp' +api_resource_type_kind: IosApp description: | A Google Cloud Firebase Apple application instance min_version: 'beta' diff --git a/mmv1/products/firebaseappcheck/RecaptchaV3Config.yaml b/mmv1/products/firebaseappcheck/RecaptchaV3Config.yaml index ebe6c6d1de8f..573a3043e15f 100644 --- a/mmv1/products/firebaseappcheck/RecaptchaV3Config.yaml +++ b/mmv1/products/firebaseappcheck/RecaptchaV3Config.yaml @@ -13,6 +13,7 @@ --- name: 'RecaptchaV3Config' +api_resource_type_kind: RecaptchaConfig description: | An app's reCAPTCHA V3 configuration object. references: diff --git a/mmv1/products/firebaseappcheck/ServiceConfig.yaml b/mmv1/products/firebaseappcheck/ServiceConfig.yaml index a43725e92d66..f2551cb08c62 100644 --- a/mmv1/products/firebaseappcheck/ServiceConfig.yaml +++ b/mmv1/products/firebaseappcheck/ServiceConfig.yaml @@ -13,6 +13,7 @@ --- name: 'ServiceConfig' +api_resource_type_kind: Service description: The enforcement configuration for a service supported by App Check. references: guides: diff --git a/mmv1/products/firebaseextensions/Instance.yaml b/mmv1/products/firebaseextensions/Instance.yaml index c1a8bcb32c23..957b9f35dbea 100644 --- a/mmv1/products/firebaseextensions/Instance.yaml +++ b/mmv1/products/firebaseextensions/Instance.yaml @@ -13,6 +13,7 @@ --- name: 'Instance' +api_resource_type_kind: ExtensionInstance description: An Instance is an installation of an Extension into a user's project. min_version: 'beta' references: diff --git a/mmv1/products/gkebackup/RestorePlan.yaml b/mmv1/products/gkebackup/RestorePlan.yaml index 87d675bec905..9d19cc03ea2f 100644 --- a/mmv1/products/gkebackup/RestorePlan.yaml +++ b/mmv1/products/gkebackup/RestorePlan.yaml @@ -59,9 +59,9 @@ examples: name: 'restore-all-ns' network_name: 'default' subnetwork_name: 'default' + deletion_protection: 'true' test_env_vars: project: 'PROJECT_NAME' - deletion_protection: 'true' test_vars_overrides: 'deletion_protection': 'false' 'network_name': 'acctest.BootstrapSharedTestNetwork(t, "gke-cluster")' @@ -74,9 +74,9 @@ examples: name: 'rollback-ns' network_name: 'default' subnetwork_name: 'default' + deletion_protection: 'true' test_env_vars: project: 'PROJECT_NAME' - deletion_protection: 'true' test_vars_overrides: 'deletion_protection': 'false' 'network_name': 'acctest.BootstrapSharedTestNetwork(t, "gke-cluster")' @@ -89,9 +89,9 @@ examples: name: 'rollback-app' network_name: 'default' subnetwork_name: 'default' + deletion_protection: 'true' test_env_vars: project: 'PROJECT_NAME' - deletion_protection: 'true' test_vars_overrides: 'deletion_protection': 'false' 'network_name': 'acctest.BootstrapSharedTestNetwork(t, "gke-cluster")' @@ -104,9 +104,9 @@ examples: name: 'all-groupkinds' network_name: 'default' subnetwork_name: 'default' + deletion_protection: 'true' test_env_vars: project: 'PROJECT_NAME' - deletion_protection: 'true' test_vars_overrides: 'deletion_protection': 'false' 'network_name': 'acctest.BootstrapSharedTestNetwork(t, "gke-cluster")' @@ -119,9 +119,9 @@ examples: name: 'rename-ns' network_name: 'default' subnetwork_name: 'default' + deletion_protection: 'true' test_env_vars: project: 'PROJECT_NAME' - deletion_protection: 'true' test_vars_overrides: 'deletion_protection': 'false' 'network_name': 'acctest.BootstrapSharedTestNetwork(t, "gke-cluster")' @@ -134,9 +134,9 @@ examples: name: 'transform-rule' network_name: 'default' subnetwork_name: 'default' + deletion_protection: 'true' test_env_vars: project: 'PROJECT_NAME' - deletion_protection: 'true' test_vars_overrides: 'deletion_protection': 'false' 'network_name': 'acctest.BootstrapSharedTestNetwork(t, "gke-cluster")' @@ -149,9 +149,9 @@ examples: name: 'gitops-mode' network_name: 'default' subnetwork_name: 'default' + deletion_protection: 'true' test_env_vars: project: 'PROJECT_NAME' - deletion_protection: 'true' test_vars_overrides: 'deletion_protection': 'false' 'network_name': 'acctest.BootstrapSharedTestNetwork(t, "gke-cluster")' @@ -164,9 +164,9 @@ examples: name: 'restore-order' network_name: 'default' subnetwork_name: 'default' + deletion_protection: 'true' test_env_vars: project: 'PROJECT_NAME' - deletion_protection: 'true' test_vars_overrides: 'deletion_protection': 'false' 'network_name': 'acctest.BootstrapSharedTestNetwork(t, "gke-cluster")' @@ -179,9 +179,9 @@ examples: name: 'volume-res' network_name: 'default' subnetwork_name: 'default' + deletion_protection: 'true' test_env_vars: project: 'PROJECT_NAME' - deletion_protection: 'true' test_vars_overrides: 'deletion_protection': 'false' 'network_name': 'acctest.BootstrapSharedTestNetwork(t, "gke-cluster")' diff --git a/mmv1/products/gkehub2/MembershipRBACRoleBinding.yaml b/mmv1/products/gkehub2/MembershipRBACRoleBinding.yaml index 43e9f193ce0a..2c9548bb7e80 100644 --- a/mmv1/products/gkehub2/MembershipRBACRoleBinding.yaml +++ b/mmv1/products/gkehub2/MembershipRBACRoleBinding.yaml @@ -13,6 +13,7 @@ --- name: 'MembershipRBACRoleBinding' +api_resource_type_kind: RBACRoleBinding description: | RBACRoleBinding represents a rbacrolebinding across the Fleet. min_version: 'beta' diff --git a/mmv1/products/gkehub2/ScopeRBACRoleBinding.yaml b/mmv1/products/gkehub2/ScopeRBACRoleBinding.yaml index 912b5c109d6a..d51fa9845a1e 100644 --- a/mmv1/products/gkehub2/ScopeRBACRoleBinding.yaml +++ b/mmv1/products/gkehub2/ScopeRBACRoleBinding.yaml @@ -13,6 +13,7 @@ --- name: 'ScopeRBACRoleBinding' +api_resource_type_kind: RBACRoleBinding description: | RBACRoleBinding represents a rbacrolebinding across the Fleet. references: diff --git a/mmv1/products/healthcare/Workspace.yaml b/mmv1/products/healthcare/Workspace.yaml index d2f4277efb36..d5ce18d792a5 100644 --- a/mmv1/products/healthcare/Workspace.yaml +++ b/mmv1/products/healthcare/Workspace.yaml @@ -13,6 +13,7 @@ --- name: 'Workspace' +api_resource_type_kind: DataMapperWorkspace description: | A Data Mapper workspace is used to configure Data Mapper access, permissions and data sources for mapping clinical patient data to the FHIR standard. references: diff --git a/mmv1/products/iap/Client.yaml b/mmv1/products/iap/Client.yaml index fc53cc14164d..3aae432eee14 100644 --- a/mmv1/products/iap/Client.yaml +++ b/mmv1/products/iap/Client.yaml @@ -13,6 +13,7 @@ --- name: 'Client' +api_resource_type_kind: IdentityAwareProxyClient description: | Contains the data that describes an Identity Aware Proxy owned client. diff --git a/mmv1/products/identityplatform/TenantDefaultSupportedIdpConfig.yaml b/mmv1/products/identityplatform/TenantDefaultSupportedIdpConfig.yaml index 2aa6bb9d1487..96e4f25cf08a 100644 --- a/mmv1/products/identityplatform/TenantDefaultSupportedIdpConfig.yaml +++ b/mmv1/products/identityplatform/TenantDefaultSupportedIdpConfig.yaml @@ -13,6 +13,7 @@ --- name: 'TenantDefaultSupportedIdpConfig' +api_resource_type_kind: DefaultSupportedIdpConfig description: | Configurations options for the tenant for authenticating with a the standard set of Identity Toolkit-trusted IDPs. diff --git a/mmv1/products/identityplatform/TenantInboundSamlConfig.yaml b/mmv1/products/identityplatform/TenantInboundSamlConfig.yaml index 9607d690d12e..3309b4c38ef7 100644 --- a/mmv1/products/identityplatform/TenantInboundSamlConfig.yaml +++ b/mmv1/products/identityplatform/TenantInboundSamlConfig.yaml @@ -13,6 +13,7 @@ --- name: 'TenantInboundSamlConfig' +api_resource_type_kind: InboundSamlConfig description: | Inbound SAML configuration for a Identity Toolkit tenant. diff --git a/mmv1/products/identityplatform/TenantOauthIdpConfig.yaml b/mmv1/products/identityplatform/TenantOauthIdpConfig.yaml index 646986badfea..a5fc5e604207 100644 --- a/mmv1/products/identityplatform/TenantOauthIdpConfig.yaml +++ b/mmv1/products/identityplatform/TenantOauthIdpConfig.yaml @@ -13,6 +13,7 @@ --- name: 'TenantOauthIdpConfig' +api_resource_type_kind: OauthIdpConfig description: | OIDC IdP configuration for a Identity Toolkit project within a tenant. diff --git a/mmv1/products/kms/KeyRingImportJob.yaml b/mmv1/products/kms/KeyRingImportJob.yaml index 8c099928946c..87d2fa727126 100644 --- a/mmv1/products/kms/KeyRingImportJob.yaml +++ b/mmv1/products/kms/KeyRingImportJob.yaml @@ -13,6 +13,7 @@ --- name: 'KeyRingImportJob' +api_resource_type_kind: ImportJob description: | A `KeyRingImportJob` can be used to create `CryptoKeys` and `CryptoKeyVersions` using pre-existing key material, generated outside of Cloud KMS. A `KeyRingImportJob` expires 3 days after it is created. diff --git a/mmv1/products/kms/SecretCiphertext.yaml b/mmv1/products/kms/SecretCiphertext.yaml index ed64c6ec9a53..592b75de4dee 100644 --- a/mmv1/products/kms/SecretCiphertext.yaml +++ b/mmv1/products/kms/SecretCiphertext.yaml @@ -13,6 +13,7 @@ --- name: 'SecretCiphertext' +api_resource_type_kind: CryptoKey description: | Encrypts secret data with Google Cloud KMS and provides access to the ciphertext. diff --git a/mmv1/products/logging/FolderSettings.yaml b/mmv1/products/logging/FolderSettings.yaml index 863137ff7889..e7cf8cc96d4c 100644 --- a/mmv1/products/logging/FolderSettings.yaml +++ b/mmv1/products/logging/FolderSettings.yaml @@ -13,6 +13,7 @@ --- name: 'FolderSettings' +api_resource_type_kind: Settings description: | Default resource settings control whether CMEK is required for new log buckets. These settings also determine the storage location for the _Default and _Required log buckets, and whether the _Default sink is enabled or disabled. references: diff --git a/mmv1/products/logging/LinkedDataset.yaml b/mmv1/products/logging/LinkedDataset.yaml index 904ef5319722..03d3e6fb2b08 100644 --- a/mmv1/products/logging/LinkedDataset.yaml +++ b/mmv1/products/logging/LinkedDataset.yaml @@ -13,6 +13,7 @@ --- name: 'LinkedDataset' +api_resource_type_kind: Link description: | Describes a BigQuery linked dataset references: diff --git a/mmv1/products/logging/Metric.yaml b/mmv1/products/logging/Metric.yaml index 21f9375e6189..6689245b2927 100644 --- a/mmv1/products/logging/Metric.yaml +++ b/mmv1/products/logging/Metric.yaml @@ -13,6 +13,7 @@ --- name: 'Metric' +api_resource_type_kind: LogMetric description: | Logs-based metric can also be used to extract values from logs and create a a distribution of the values. The distribution records the statistics of the extracted values along with diff --git a/mmv1/products/logging/OrganizationSettings.yaml b/mmv1/products/logging/OrganizationSettings.yaml index 363e059abe53..49ef28f107f8 100644 --- a/mmv1/products/logging/OrganizationSettings.yaml +++ b/mmv1/products/logging/OrganizationSettings.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationSettings' +api_resource_type_kind: Settings description: | Default resource settings control whether CMEK is required for new log buckets. These settings also determine the storage location for the _Default and _Required log buckets, and whether the _Default sink is enabled or disabled. references: diff --git a/mmv1/products/looker/Instance.yaml b/mmv1/products/looker/Instance.yaml index d15a6b584733..35f569e30cfa 100644 --- a/mmv1/products/looker/Instance.yaml +++ b/mmv1/products/looker/Instance.yaml @@ -43,7 +43,6 @@ async: error: path: 'error' message: 'message' -custom_code: exclude_sweeper: true error_abort_predicates: @@ -106,6 +105,25 @@ examples: instance_name: 'my-instance' client_id: 'my-client-id' client_secret: 'my-client-secret' + - name: 'looker_instance_force_delete' + primary_resource_id: 'looker-instance' + vars: + instance_name: 'my-instance' + client_id: 'my-client-id' + client_secret: 'my-client-secret' + ignore_read_extra: + - 'deletion_policy' +virtual_fields: + - name: 'deletion_policy' + default_value: "DEFAULT" + type: String + description: | + Policy to determine if the cluster should be deleted forcefully. + If setting deletion_policy = "FORCE", the Looker instance will be deleted regardless + of its nested resources. If set to "DEFAULT", Looker instances that still have + nested resources will return an error. Possible values: DEFAULT, FORCE +custom_code: + pre_delete: templates/terraform/pre_delete/looker_instance.go.tmpl parameters: - name: 'region' type: String diff --git a/mmv1/products/memorystore/Instance.yaml b/mmv1/products/memorystore/Instance.yaml index facf54bc16d7..7b71ba707a3e 100644 --- a/mmv1/products/memorystore/Instance.yaml +++ b/mmv1/products/memorystore/Instance.yaml @@ -342,6 +342,89 @@ properties: description: "Optional. If set to true deletion of the instance will fail. " min_version: 'beta' default_value: true + - name: 'endpoints' + type: Array + description: "Endpoints for the instance." + min_version: 'beta' + output: true + item_type: + type: Array + description: "A group of PSC connections. They are created in the same VPC network, one for each service attachment in the cluster." + item_type: + type: NestedObject + properties: + - name: 'pscConnectionId' + type: String + description: + "Output only. The PSC connection id of the forwarding rule connected + to the\nservice attachment. " + min_version: 'beta' + output: true + - name: 'ipAddress' + type: String + description: + "Output only. The IP allocated on the consumer network for the + PSC forwarding rule. " + min_version: 'beta' + output: true + - name: 'forwardingRule' + type: String + description: "Output only. The URI of the consumer side forwarding rule.\nFormat:\nprojects/{project}/regions/{region}/forwardingRules/{forwarding_rule} " + min_version: 'beta' + output: true + - name: 'projectId' + type: String + description: + "Output only. The consumer project_id where the forwarding rule is + created from. " + min_version: 'beta' + output: true + - name: 'network' + type: String + description: + "Output only. The consumer network where the IP address resides, in + the form of\nprojects/{project_id}/global/networks/{network_id}. " + min_version: 'beta' + output: true + - name: 'serviceAttachment' + type: String + description: + "Output only. The service attachment which is the target of the PSC connection, in the form of + projects/{project-id}/regions/{region}/serviceAttachments/{service-attachment-id}." + min_version: 'beta' + output: true + - name: 'pscConnectionStatus' + type: Enum + description: + "Output Only. The status of the PSC connection: whether a connection exists and ACTIVE or it no longer exists. + \n Possible values:\n ACTIVE \n NOT_FOUND" + min_version: 'beta' + output: true + enum_values: + - 'ACTIVE' + - 'NOT_FOUND' + - name: 'connectionType' + type: Enum + description: + "Output Only. Type of a PSC Connection. + \n Possible values:\n CONNECTION_TYPE_DISCOVERY \n CONNECTION_TYPE_PRIMARY \n CONNECTION_TYPE_READER" + min_version: 'beta' + output: true + enum_values: + - 'CONNECTION_TYPE_READER' + - 'CONNECTION_TYPE_PRIMARY' + - 'CONNECTION_TYPE_DISCOVERY' + - name: 'mode' + type: Enum + description: + "Optional. Standalone or cluster. + \n Possible values:\n CLUSTER\nSTANDALONE" + min_version: 'beta' + default_from_api: true + immutable: true + enum_values: + - 'CLUSTER' + - 'STANDALONE' - name: 'pscAutoConnections' type: Array description: @@ -385,3 +468,37 @@ properties: the form of\nprojects/{project_id}/global/networks/{network_id}. " min_version: 'beta' output: true + - name: 'serviceAttachment' + type: String + description: + "Output only. The service attachment which is the target of the PSC connection, in the form of + projects/{project-id}/regions/{region}/serviceAttachments/{service-attachment-id}." + min_version: 'beta' + output: true + - name: 'pscConnectionStatus' + type: Enum + description: + "Output Only. The status of the PSC connection: whether a connection exists and ACTIVE or it no longer exists. + \n Possible values:\n ACTIVE \n NOT_FOUND" + min_version: 'beta' + output: true + enum_values: + - 'ACTIVE' + - 'NOT_FOUND' + - name: 'connectionType' + type: Enum + description: + "Output Only. Type of a PSC Connection. + \n Possible values:\n CONNECTION_TYPE_DISCOVERY \n CONNECTION_TYPE_PRIMARY \n CONNECTION_TYPE_READER" + min_version: 'beta' + output: true + enum_values: + - 'CONNECTION_TYPE_READER' + - 'CONNECTION_TYPE_PRIMARY' + - 'CONNECTION_TYPE_DISCOVERY' + - name: 'port' + type: Integer + description: + "Output only. Ports of the exposed endpoint." + output: true + min_version: 'beta' diff --git a/mmv1/products/metastore/Service.yaml b/mmv1/products/metastore/Service.yaml index cadf3ef80c5b..4b5367a38ee8 100644 --- a/mmv1/products/metastore/Service.yaml +++ b/mmv1/products/metastore/Service.yaml @@ -142,14 +142,17 @@ examples: metastore_service_name: 'backup' - name: 'dataproc_metastore_service_autoscaling_max_scaling_factor' primary_resource_id: 'test_resource' + min_version: 'beta' vars: metastore_service_name: 'test-service' - name: 'dataproc_metastore_service_autoscaling_min_and_max_scaling_factor' primary_resource_id: 'test_resource' + min_version: 'beta' vars: metastore_service_name: 'test-service' - name: 'dataproc_metastore_service_autoscaling_min_scaling_factor' primary_resource_id: 'test_resource' + min_version: 'beta' vars: metastore_service_name: 'test-service' - name: 'dataproc_metastore_service_autoscaling_no_limit_config' diff --git a/mmv1/products/monitoring/AlertPolicy.yaml b/mmv1/products/monitoring/AlertPolicy.yaml index 875e933955da..fdb9889e73a5 100644 --- a/mmv1/products/monitoring/AlertPolicy.yaml +++ b/mmv1/products/monitoring/AlertPolicy.yaml @@ -945,6 +945,16 @@ properties: type: String description: | If an alert policy that was active has no data for this long, any open incidents will close. + - name: 'notificationPrompts' + type: Array + description: | + Control when notifications will be sent out. + item_type: + type: Enum + enum_values: + - 'NOTIFICATION_PROMPT_UNSPECIFIED' + - 'OPENED' + - 'CLOSED' - name: 'notificationChannelStrategy' type: Array description: | diff --git a/mmv1/products/monitoring/GenericService.yaml b/mmv1/products/monitoring/GenericService.yaml index 91421c906008..fb78668b3aad 100644 --- a/mmv1/products/monitoring/GenericService.yaml +++ b/mmv1/products/monitoring/GenericService.yaml @@ -13,6 +13,7 @@ --- name: 'GenericService' +api_resource_type_kind: Service legacy_name: 'google_monitoring_service' description: | A Service is a discrete, autonomous, and network-accessible unit, diff --git a/mmv1/products/monitoring/Slo.yaml b/mmv1/products/monitoring/Slo.yaml index 102f405afc55..34822d3b3210 100644 --- a/mmv1/products/monitoring/Slo.yaml +++ b/mmv1/products/monitoring/Slo.yaml @@ -13,6 +13,7 @@ --- name: 'Slo' +api_resource_type_kind: ServiceLevelObjective description: | A Service-Level Objective (SLO) describes the level of desired good service. It consists of a service-level indicator (SLI), a performance diff --git a/mmv1/products/netapp/ActiveDirectory.yaml b/mmv1/products/netapp/ActiveDirectory.yaml index cfbb8081b3c8..6db6cd7d5ec4 100644 --- a/mmv1/products/netapp/ActiveDirectory.yaml +++ b/mmv1/products/netapp/ActiveDirectory.yaml @@ -73,18 +73,10 @@ properties: Create time of the active directory. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z". output: true - name: 'state' - type: Enum + type: String description: | The state of the Active Directory policy (not the Active Directory itself). output: true - enum_values: - - 'STATE_UNSPECIFIED' - - 'CREATING' - - 'READY' - - 'UPDATING' - - 'DELETING' - - 'IN_USE' - - 'ERROR' - name: 'domain' type: String description: | diff --git a/mmv1/products/netapp/BackupPolicy.yaml b/mmv1/products/netapp/BackupPolicy.yaml index 3ae2f2560b1a..89570a547934 100644 --- a/mmv1/products/netapp/BackupPolicy.yaml +++ b/mmv1/products/netapp/BackupPolicy.yaml @@ -78,17 +78,10 @@ properties: description: | Labels as key value pairs. Example: `{ "owner": "Bob", "department": "finance", "purpose": "testing" }`. - name: 'state' - type: Enum + type: String description: | The state of the backup policy. output: true - enum_values: - - 'STATE_UNSPECIFIED' - - 'CREATING' - - 'READY' - - 'UPDATING' - - 'DELETING' - - 'ERROR' - name: 'dailyBackupLimit' type: Integer description: | diff --git a/mmv1/products/netapp/BackupVault.yaml b/mmv1/products/netapp/BackupVault.yaml index 8e31fa1303d1..d5b9b2d9dd3d 100644 --- a/mmv1/products/netapp/BackupVault.yaml +++ b/mmv1/products/netapp/BackupVault.yaml @@ -69,17 +69,10 @@ parameters: immutable: true properties: - name: 'state' - type: Enum + type: String description: | The state of the Backup Vault. output: true - enum_values: - - 'STATE_UNSPECIFIED' - - 'CREATING' - - 'READY' - - 'UPDATING' - - 'DELETING' - - 'ERROR' - name: 'createTime' type: String description: | diff --git a/mmv1/products/netapp/StoragePool.yaml b/mmv1/products/netapp/StoragePool.yaml index 030d2eb74213..7e6061babea7 100644 --- a/mmv1/products/netapp/StoragePool.yaml +++ b/mmv1/products/netapp/StoragePool.yaml @@ -35,7 +35,6 @@ description: | the next apply. You can trigger a manual [zone switch](https://cloud.google.com/netapp/volumes/docs/configure-and-use/storage-pools/edit-or-delete-storage-pool#switch_active_and_replica_zones) via Terraform by swapping the value of the `zone` and `replica_zone` parameters in your HCL code. - Note : Regional FLEX storage pool are supported in beta provider currently. references: guides: @@ -151,27 +150,21 @@ properties: using security identifiers for NFSv4.1 or principal names for kerberized NFSv4.1. immutable: true - name: 'encryptionType' - type: Enum + type: String description: | Reports if volumes in the pool are encrypted using a Google-managed encryption key or CMEK. output: true - enum_values: - - 'ENCRYPTION_TYPE_UNSPECIFIED' - - 'SERVICE_MANAGED' - - 'CLOUD_KMS' - name: 'zone' type: String description: | Specifies the active zone for regional Flex pools. `zone` and `replica_zone` values can be swapped to initiate a [zone switch](https://cloud.google.com/netapp/volumes/docs/configure-and-use/storage-pools/edit-or-delete-storage-pool#switch_active_and_replica_zones). If you want to create a zonal Flex pool, specify a zone name for `location` and omit `zone`. - min_version: 'beta' - name: 'replicaZone' type: String description: | Specifies the replica zone for regional Flex pools. `zone` and `replica_zone` values can be swapped to initiate a [zone switch](https://cloud.google.com/netapp/volumes/docs/configure-and-use/storage-pools/edit-or-delete-storage-pool#switch_active_and_replica_zones). - min_version: 'beta' - name: 'allowAutoTiering' type: Boolean description: | diff --git a/mmv1/products/netapp/Volume.yaml b/mmv1/products/netapp/Volume.yaml index f800b2c50eaa..8a7a1bf1dc60 100644 --- a/mmv1/products/netapp/Volume.yaml +++ b/mmv1/products/netapp/Volume.yaml @@ -85,19 +85,10 @@ parameters: immutable: true properties: - name: 'state' - type: Enum + type: String description: | State of the volume. output: true - enum_values: - - 'STATE_UNSPECIFIED' - - 'READY' - - 'CREATING' - - 'DELETING' - - 'UPDATING' - - 'RESTORING' - - 'DISABLED' - - 'ERROR' - name: 'stateDetails' type: String description: | @@ -315,14 +306,10 @@ properties: Reports the CMEK policy resurce name being used for volume encryption. Inherited from storage pool. output: true - name: 'encryptionType' - type: Enum + type: String description: | Reports the data-at-rest encryption type of the volume. Inherited from storage pool. output: true - enum_values: - - 'ENCRYPTION_TYPE_UNSPECIFIED' - - 'SERVICE_MANAGED' - - 'CLOUD_KMS' - name: 'hasReplication' type: Boolean description: | @@ -363,15 +350,10 @@ properties: description: Human-readable mount instructions. output: true - name: 'protocol' - type: Enum + type: String description: | Protocol to mount with. output: true - enum_values: - - 'PROTOCOLS_UNSPECIFIED' - - 'NFSV3' - - 'NFSV4' - - 'SMB' - name: 'snapshotPolicy' type: NestedObject description: |- @@ -498,13 +480,11 @@ properties: type: String description: | Specifies the active zone for regional volume. - min_version: 'beta' output: true - name: 'replicaZone' type: String description: | Specifies the replica zone for regional volume. - min_version: 'beta' output: true - name: 'largeCapacity' type: Boolean diff --git a/mmv1/products/netapp/VolumeReplication.yaml b/mmv1/products/netapp/VolumeReplication.yaml index b6c7e64a85ca..6f8f8bc0dfbc 100644 --- a/mmv1/products/netapp/VolumeReplication.yaml +++ b/mmv1/products/netapp/VolumeReplication.yaml @@ -133,33 +133,22 @@ parameters: immutable: true properties: - name: 'state' - type: Enum + type: String description: | Indicates the state of replication resource. State of the mirror itself is indicated in mirrorState. output: true - enum_values: - - 'STATE_UNSPECIFIED' - - 'CREATING' - - 'READY' - - 'UPDATING' - - 'DELETING' - - 'ERROR' - name: 'stateDetails' type: String description: | State details of the replication resource. output: true - name: 'role' - type: Enum + type: String description: | Reverting a replication can swap source and destination volume roles. This field indicates if the `location` hosts the source or destination volume. For resume and revert and resume operations it is critical to understand which volume is the source volume, since it will overwrite changes done to the destination volume. output: true - enum_values: - - 'REPLICATION_ROLE_UNSPECIFIED' - - 'SOURCE' - - 'DESTINATION' - name: 'replicationSchedule' type: Enum description: | @@ -170,7 +159,7 @@ properties: - 'HOURLY' - 'DAILY' - name: 'mirrorState' - type: Enum + type: String description: | Indicates the state of the mirror between source and destination volumes. Depending on the amount of data in your source volume, PREPARING phase can take hours or days. mirrorState = MIRRORED indicates your baseline @@ -178,12 +167,6 @@ properties: currently receives an update. Updated every 5 minutes. output: true custom_flatten: 'templates/terraform/custom_flatten/netapp_volume_replicaton_mirror_state.go.tmpl' - enum_values: - - 'MIRROR_STATE_UNSPECIFIED' - - 'PREPARING' - - 'MIRRORED' - - 'STOPPED' - - 'TRANSFERRING' - name: 'createTime' type: String description: | diff --git a/mmv1/products/netapp/VolumeSnapshot.yaml b/mmv1/products/netapp/VolumeSnapshot.yaml index a4584d4bff60..1180e3898898 100644 --- a/mmv1/products/netapp/VolumeSnapshot.yaml +++ b/mmv1/products/netapp/VolumeSnapshot.yaml @@ -13,6 +13,7 @@ --- name: 'VolumeSnapshot' +api_resource_type_kind: Snapshot description: | NetApp Volumes helps you manage your data usage with snapshots that can quickly restore lost data. Snapshots are point-in-time versions of your volume's content. They are resources of volumes and are diff --git a/mmv1/products/netapp/kmsconfig.yaml b/mmv1/products/netapp/kmsconfig.yaml index 728fdece4525..fa2709203bc0 100644 --- a/mmv1/products/netapp/kmsconfig.yaml +++ b/mmv1/products/netapp/kmsconfig.yaml @@ -14,6 +14,7 @@ --- # API resource name name: 'kmsconfig' +api_resource_type_kind: KmsConfig description: | NetApp Volumes always encrypts your data at rest using volume-specific keys. diff --git a/mmv1/products/networkconnectivity/Group.yaml b/mmv1/products/networkconnectivity/Group.yaml new file mode 100644 index 000000000000..00db44c3de50 --- /dev/null +++ b/mmv1/products/networkconnectivity/Group.yaml @@ -0,0 +1,109 @@ +# Copyright 2024 Google Inc. +# Licensed under the Apache License, Version 2.0 (the License); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Group +legacy_long_form_project: true +base_url: projects/{{project}}/locations/global/hubs/{{hub}}/groups +create_url: projects/{{project}}/locations/global/hubs/{{hub}}/groups/{{name}}?updateMask=autoAccept.autoAcceptProjects,labels,description +update_url: projects/{{project}}/locations/global/hubs/{{hub}}/groups/{{name}}?updateMask=autoAccept.autoAcceptProjects,labels,description +self_link: projects/{{project}}/locations/global/hubs/{{hub}}/groups/{{name}} +create_verb: 'PATCH' +update_verb: 'PATCH' +references: + guides: + 'Official Documentation': 'https://cloud.google.com/network-connectivity/docs/network-connectivity-center/concepts/overview' + api: 'https://cloud.google.com/network-connectivity/docs/reference/networkconnectivity/rest/v1beta/projects.locations.global.hubs.groups' +async: + type: 'OpAsync' + operation: + path: 'name' + base_url: '{{op_id}}' + wait_ms: 1000 + result: + path: 'targetLink' + error: + path: 'error/errors' + message: 'message' +# A handwritten sweeper is needed as the resource name can only be `default`, `center`, and `edge`. +exclude_sweeper: true +examples: + - name: 'network_connectivity_group_basic' + primary_resource_id: 'primary' + vars: + hub_name: "network-connectivity-hub1" + auto_accept_project_1_name: "foo" + auto_accept_project_2_name: "bar" +description: The NetworkConnectivity Group resource +parameters: + - name: 'hub' + type: ResourceRef + resource: 'Hub' + imports: 'id' + description: | + The name of the hub. Hub names must be unique. They use the following form: projects/{projectNumber}/locations/global/hubs/{hubId} + immutable: true + required: true + url_param_only: true + custom_flatten: templates/terraform/custom_flatten/name_from_self_link.erb +properties: + - name: name + type: Enum + description: 'The name of the group. Group names must be unique.' + diff_suppress_func: 'tpgresource.CompareSelfLinkOrResourceName' + required: true + immutable: true + custom_flatten: 'templates/terraform/custom_flatten/name_from_self_link.tmpl' + enum_values: + - 'default' + - 'center' + - 'edge' + - name: createTime + type: String + description: Output only. The time the hub was created. + output: true + - name: updateTime + type: String + description: Output only. The time the hub was last updated. + output: true + - name: labels + type: KeyValueLabels + description: Optional labels in key:value format. For more information about labels, see [Requirements for labels](https://cloud.google.com/resource-manager/docs/creating-managing-labels#requirements). + - name: description + type: String + description: An optional description of the group. + - name: uid + type: String + description: Output only. The Google-generated UUID for the group. This value is unique across all group resources. If a group is deleted and another with the same name is created, the new route table is assigned a different uniqueId. + output: true + - name: state + type: String + description: Output only. The current lifecycle state of this hub. + output: true + exactly_one_of: + - CREATING + - ACTIVE + - DELETING + - name: autoAccept + type: NestedObject + description: Optional. The auto-accept setting for this group. + properties: + - name: autoAcceptProjects + type: Array + description: 'A list of project ids or project numbers for which you want to enable auto-accept. The auto-accept setting is applied to spokes being created or updated in these projects.' + required: true + item_type: + type: String + - name: routeTable + type: String + description: 'Output only. The name of the route table that corresponds to this group. They use the following form: `projects/{projectNumber}/locations/global/hubs/{hubId}/routeTables/{route_table_id}`' + output: true diff --git a/mmv1/products/networksecurity/GatewaySecurityPolicy.yaml b/mmv1/products/networksecurity/GatewaySecurityPolicy.yaml index baa470eca860..34cb3b04e40a 100644 --- a/mmv1/products/networksecurity/GatewaySecurityPolicy.yaml +++ b/mmv1/products/networksecurity/GatewaySecurityPolicy.yaml @@ -105,5 +105,5 @@ properties: type: String description: | Name of a TlsInspectionPolicy resource that defines how TLS inspection is performed for any rule that enables it. - min_version: 'beta' + Note: google_network_security_tls_inspection_policy resource is still in [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html) therefore it will need to import the provider. ignore_read: true diff --git a/mmv1/products/networksecurity/UrlLists.yaml b/mmv1/products/networksecurity/UrlLists.yaml index 68c0dd7de955..bbf9e866040b 100644 --- a/mmv1/products/networksecurity/UrlLists.yaml +++ b/mmv1/products/networksecurity/UrlLists.yaml @@ -13,6 +13,7 @@ --- name: 'UrlLists' +api_resource_type_kind: UrlList description: | UrlList proto helps users to set reusable, independently manageable lists of hosts, host patterns, URLs, URL patterns. references: diff --git a/mmv1/products/networkservices/ServiceLbPolicies.yaml b/mmv1/products/networkservices/ServiceLbPolicies.yaml index e6f4fdd0fc3f..dd57f5520bb2 100644 --- a/mmv1/products/networkservices/ServiceLbPolicies.yaml +++ b/mmv1/products/networkservices/ServiceLbPolicies.yaml @@ -14,6 +14,7 @@ # Warning: This is a temporary file, and should not be edited directly --- name: 'ServiceLbPolicies' +api_resource_type_kind: ServiceLbPolicy description: | ServiceLbPolicy holds global load balancing and traffic distribution configuration that can be applied to a BackendService. min_version: 'beta' diff --git a/mmv1/products/oracledatabase/product.yaml b/mmv1/products/oracledatabase/product.yaml index 9356d79a4a2e..659ceb318f49 100644 --- a/mmv1/products/oracledatabase/product.yaml +++ b/mmv1/products/oracledatabase/product.yaml @@ -12,7 +12,7 @@ # limitations under the License. --- name: 'OracleDatabase' -display_name: 'OracleDatabase' +display_name: 'Oracle Database' versions: - name: 'ga' base_url: 'https://oracledatabase.googleapis.com/v1/' diff --git a/mmv1/products/osconfig/GuestPolicies.yaml b/mmv1/products/osconfig/GuestPolicies.yaml index 8eec88632c96..33084ea58ed9 100644 --- a/mmv1/products/osconfig/GuestPolicies.yaml +++ b/mmv1/products/osconfig/GuestPolicies.yaml @@ -13,6 +13,7 @@ --- name: 'GuestPolicies' +api_resource_type_kind: GuestPolicy description: | An OS Config resource representing a guest configuration policy. These policies represent the desired state for VM instance guest environments including packages to install or remove, diff --git a/mmv1/products/oslogin/SSHPublicKey.yaml b/mmv1/products/oslogin/SSHPublicKey.yaml index 14c3e47fe72a..e2f76ecd4f56 100644 --- a/mmv1/products/oslogin/SSHPublicKey.yaml +++ b/mmv1/products/oslogin/SSHPublicKey.yaml @@ -13,6 +13,7 @@ --- name: 'SSHPublicKey' +api_resource_type_kind: SshPublicKey kind: 'user#sshPublicKeys' description: | The SSH public key information associated with a Google account. diff --git a/mmv1/products/pubsub/Subscription.yaml b/mmv1/products/pubsub/Subscription.yaml index 637be094da13..8fdec6c05331 100644 --- a/mmv1/products/pubsub/Subscription.yaml +++ b/mmv1/products/pubsub/Subscription.yaml @@ -72,6 +72,8 @@ examples: subscription_name: 'example-subscription' dataset_id: 'example_dataset' table_id: 'example_table' + test_vars_overrides: + policy_changed: 'acctest.BootstrapPSARoles(t, "service-", "gcp-sa-pubsub", []string{"roles/bigquery.dataEditor", "roles/bigquery.metadataViewer"})' - name: 'pubsub_subscription_push_bq_table_schema' primary_resource_id: 'example' vars: @@ -79,6 +81,8 @@ examples: subscription_name: 'example-subscription' dataset_id: 'example_dataset' table_id: 'example_table' + test_vars_overrides: + policy_changed: 'acctest.BootstrapPSARoles(t, "service-", "gcp-sa-pubsub", []string{"roles/bigquery.dataEditor", "roles/bigquery.metadataViewer"})' - name: 'pubsub_subscription_push_bq_service_account' primary_resource_id: 'example' vars: diff --git a/mmv1/products/secretmanagerregional/RegionalSecret.yaml b/mmv1/products/secretmanagerregional/RegionalSecret.yaml index 805182ab5d12..bf86f615345d 100644 --- a/mmv1/products/secretmanagerregional/RegionalSecret.yaml +++ b/mmv1/products/secretmanagerregional/RegionalSecret.yaml @@ -13,6 +13,7 @@ --- name: 'RegionalSecret' +api_resource_type_kind: Secret description: | A Regional Secret is a logical secret whose value and versions can be created and accessed within a region only. references: diff --git a/mmv1/products/secretmanagerregional/RegionalSecretVersion.yaml b/mmv1/products/secretmanagerregional/RegionalSecretVersion.yaml index 831bc13632f0..d2b001b4e796 100644 --- a/mmv1/products/secretmanagerregional/RegionalSecretVersion.yaml +++ b/mmv1/products/secretmanagerregional/RegionalSecretVersion.yaml @@ -13,6 +13,7 @@ --- name: 'RegionalSecretVersion' +api_resource_type_kind: SecretVersion description: | A regional secret version resource. docs: diff --git a/mmv1/products/securitycenter/FolderCustomModule.yaml b/mmv1/products/securitycenter/FolderCustomModule.yaml index 0222889977f5..57d8e062dfb4 100644 --- a/mmv1/products/securitycenter/FolderCustomModule.yaml +++ b/mmv1/products/securitycenter/FolderCustomModule.yaml @@ -13,6 +13,7 @@ --- name: 'FolderCustomModule' +api_resource_type_kind: SecurityHealthAnalyticsCustomModule description: | Represents an instance of a Security Health Analytics custom module, including its full module name, display name, enablement state, and last updated time. diff --git a/mmv1/products/securitycenter/FolderNotificationConfig.yaml b/mmv1/products/securitycenter/FolderNotificationConfig.yaml index 3cd524ac3cce..ebacac13ef2e 100644 --- a/mmv1/products/securitycenter/FolderNotificationConfig.yaml +++ b/mmv1/products/securitycenter/FolderNotificationConfig.yaml @@ -13,6 +13,7 @@ --- name: 'FolderNotificationConfig' +api_resource_type_kind: NotificationConfig description: | A Cloud Security Command Center (Cloud SCC) notification configs. A notification config is a Cloud SCC resource that contains the diff --git a/mmv1/products/securitycenter/FolderSccBigQueryExport.yaml b/mmv1/products/securitycenter/FolderSccBigQueryExport.yaml index 6038324aaf16..f0debc66d300 100644 --- a/mmv1/products/securitycenter/FolderSccBigQueryExport.yaml +++ b/mmv1/products/securitycenter/FolderSccBigQueryExport.yaml @@ -13,6 +13,7 @@ --- name: 'FolderSccBigQueryExport' +api_resource_type_kind: BigQueryExport description: | A Cloud Security Command Center (Cloud SCC) Big Query Export Config. It represents exporting Security Command Center data, including assets, findings, and security marks diff --git a/mmv1/products/securitycenter/OrganizationCustomModule.yaml b/mmv1/products/securitycenter/OrganizationCustomModule.yaml index 660be2e9a181..297d2267f880 100644 --- a/mmv1/products/securitycenter/OrganizationCustomModule.yaml +++ b/mmv1/products/securitycenter/OrganizationCustomModule.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationCustomModule' +api_resource_type_kind: SecurityHealthAnalyticsCustomModule description: | Represents an instance of a Security Health Analytics custom module, including its full module name, display name, enablement state, and last updated time. diff --git a/mmv1/products/securitycenter/OrganizationSccBigQueryExport.yaml b/mmv1/products/securitycenter/OrganizationSccBigQueryExport.yaml index 0fd09fc0019b..67dea99db642 100644 --- a/mmv1/products/securitycenter/OrganizationSccBigQueryExport.yaml +++ b/mmv1/products/securitycenter/OrganizationSccBigQueryExport.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationSccBigQueryExport' +api_resource_type_kind: BigQueryExport description: | A Cloud Security Command Center (Cloud SCC) Big Query Export Config. It represents exporting Security Command Center data, including assets, findings, and security marks diff --git a/mmv1/products/securitycenter/ProjectCustomModule.yaml b/mmv1/products/securitycenter/ProjectCustomModule.yaml index 7c30f2f32b6d..782f8f394480 100644 --- a/mmv1/products/securitycenter/ProjectCustomModule.yaml +++ b/mmv1/products/securitycenter/ProjectCustomModule.yaml @@ -13,6 +13,7 @@ --- name: 'ProjectCustomModule' +api_resource_type_kind: SecurityHealthAnalyticsCustomModule description: | Represents an instance of a Security Health Analytics custom module, including its full module name, display name, enablement state, and last updated time. diff --git a/mmv1/products/securitycenter/ProjectNotificationConfig.yaml b/mmv1/products/securitycenter/ProjectNotificationConfig.yaml index d61c57468dca..380fe1a27068 100644 --- a/mmv1/products/securitycenter/ProjectNotificationConfig.yaml +++ b/mmv1/products/securitycenter/ProjectNotificationConfig.yaml @@ -13,6 +13,7 @@ --- name: 'ProjectNotificationConfig' +api_resource_type_kind: NotificationConfig description: | A Cloud Security Command Center (Cloud SCC) notification configs. A notification config is a Cloud SCC resource that contains the diff --git a/mmv1/products/securitycenter/ProjectSccBigQueryExport.yaml b/mmv1/products/securitycenter/ProjectSccBigQueryExport.yaml index d0fe37e5d87d..b860af55386b 100644 --- a/mmv1/products/securitycenter/ProjectSccBigQueryExport.yaml +++ b/mmv1/products/securitycenter/ProjectSccBigQueryExport.yaml @@ -13,6 +13,7 @@ --- name: 'ProjectSccBigQueryExport' +api_resource_type_kind: BigQueryExport description: | A Cloud Security Command Center (Cloud SCC) Big Query Export Config. It represents exporting Security Command Center data, including assets, findings, and security marks diff --git a/mmv1/products/securitycentermanagement/FolderSecurityHealthAnalyticsCustomModule.yaml b/mmv1/products/securitycentermanagement/FolderSecurityHealthAnalyticsCustomModule.yaml index 281cdaba5fae..98de77790355 100644 --- a/mmv1/products/securitycentermanagement/FolderSecurityHealthAnalyticsCustomModule.yaml +++ b/mmv1/products/securitycentermanagement/FolderSecurityHealthAnalyticsCustomModule.yaml @@ -13,6 +13,7 @@ --- name: 'FolderSecurityHealthAnalyticsCustomModule' +api_resource_type_kind: SecurityHealthAnalyticsCustomModule description: | Represents an instance of a Security Health Analytics custom module, including its full module name, display name, enablement state, and last updated time. diff --git a/mmv1/products/securitycentermanagement/OrganizationEventThreatDetectionCustomModule.yaml b/mmv1/products/securitycentermanagement/OrganizationEventThreatDetectionCustomModule.yaml index b637b18f8ed2..e133bef3419b 100644 --- a/mmv1/products/securitycentermanagement/OrganizationEventThreatDetectionCustomModule.yaml +++ b/mmv1/products/securitycentermanagement/OrganizationEventThreatDetectionCustomModule.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationEventThreatDetectionCustomModule' +api_resource_type_kind: EventThreatDetectionCustomModule description: | Represents an instance of an Event Threat Detection custom module, including its full module name, display name, enablement state, and last updated time. diff --git a/mmv1/products/securitycentermanagement/OrganizationSecurityHealthAnalyticsCustomModule.yaml b/mmv1/products/securitycentermanagement/OrganizationSecurityHealthAnalyticsCustomModule.yaml index 056644a00faa..c517cfb593d3 100644 --- a/mmv1/products/securitycentermanagement/OrganizationSecurityHealthAnalyticsCustomModule.yaml +++ b/mmv1/products/securitycentermanagement/OrganizationSecurityHealthAnalyticsCustomModule.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationSecurityHealthAnalyticsCustomModule' +api_resource_type_kind: SecurityHealthAnalyticsCustomModule description: | Represents an instance of a Security Health Analytics custom module, including its full module name, display name, enablement state, and last updated time. diff --git a/mmv1/products/securitycentermanagement/ProjectSecurityHealthAnalyticsCustomModule.yaml b/mmv1/products/securitycentermanagement/ProjectSecurityHealthAnalyticsCustomModule.yaml index 10a8d5730420..aac9ce06cd8f 100644 --- a/mmv1/products/securitycentermanagement/ProjectSecurityHealthAnalyticsCustomModule.yaml +++ b/mmv1/products/securitycentermanagement/ProjectSecurityHealthAnalyticsCustomModule.yaml @@ -13,6 +13,7 @@ --- name: 'ProjectSecurityHealthAnalyticsCustomModule' +api_resource_type_kind: SecurityHealthAnalyticsCustomModule description: | Represents an instance of a Security Health Analytics custom module, including its full module name, display name, enablement state, and last updated time. diff --git a/mmv1/products/securitycenterv2/FolderMuteConfig.yaml b/mmv1/products/securitycenterv2/FolderMuteConfig.yaml index ffca2e43d49c..75c5f727a6cf 100644 --- a/mmv1/products/securitycenterv2/FolderMuteConfig.yaml +++ b/mmv1/products/securitycenterv2/FolderMuteConfig.yaml @@ -13,6 +13,7 @@ --- name: 'FolderMuteConfig' +api_resource_type_kind: MuteConfig description: | Mute Findings is a volume management feature in Security Command Center that lets you manually or programmatically hide irrelevant findings, diff --git a/mmv1/products/securitycenterv2/FolderNotificationConfig.yaml b/mmv1/products/securitycenterv2/FolderNotificationConfig.yaml index b8918135e3dc..24757fa040ac 100644 --- a/mmv1/products/securitycenterv2/FolderNotificationConfig.yaml +++ b/mmv1/products/securitycenterv2/FolderNotificationConfig.yaml @@ -13,6 +13,7 @@ --- name: 'FolderNotificationConfig' +api_resource_type_kind: NotificationConfig description: | A Cloud Security Command Center (Cloud SCC) notification configs. A notification config is a Cloud SCC resource that contains the diff --git a/mmv1/products/securitycenterv2/FolderSccBigQueryExport.yaml b/mmv1/products/securitycenterv2/FolderSccBigQueryExport.yaml index 422cb0e273f6..62671466ba0e 100644 --- a/mmv1/products/securitycenterv2/FolderSccBigQueryExport.yaml +++ b/mmv1/products/securitycenterv2/FolderSccBigQueryExport.yaml @@ -13,6 +13,7 @@ --- name: 'FolderSccBigQueryExport' +api_resource_type_kind: BigQueryExport description: | A Cloud Security Command Center (Cloud SCC) Big Query Export Config. It represents exporting Security Command Center data, including assets, findings, and security marks diff --git a/mmv1/products/securitycenterv2/OrganizationMuteConfig.yaml b/mmv1/products/securitycenterv2/OrganizationMuteConfig.yaml index 7bd58ae0d198..0a5fe59f4fa1 100644 --- a/mmv1/products/securitycenterv2/OrganizationMuteConfig.yaml +++ b/mmv1/products/securitycenterv2/OrganizationMuteConfig.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationMuteConfig' +api_resource_type_kind: MuteConfig description: | Mute Findings is a volume management feature in Security Command Center that lets you manually or programmatically hide irrelevant findings, diff --git a/mmv1/products/securitycenterv2/OrganizationNotificationConfig.yaml b/mmv1/products/securitycenterv2/OrganizationNotificationConfig.yaml index 0f708d3f375b..d44e438be6bb 100644 --- a/mmv1/products/securitycenterv2/OrganizationNotificationConfig.yaml +++ b/mmv1/products/securitycenterv2/OrganizationNotificationConfig.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationNotificationConfig' +api_resource_type_kind: NotificationConfig description: | A Cloud Security Command Center (Cloud SCC) notification configs. A notification config is a Cloud SCC resource that contains the diff --git a/mmv1/products/securitycenterv2/OrganizationSccBigQueryExport.yaml b/mmv1/products/securitycenterv2/OrganizationSccBigQueryExport.yaml index 03b5e70a3a42..c4567998c29d 100644 --- a/mmv1/products/securitycenterv2/OrganizationSccBigQueryExport.yaml +++ b/mmv1/products/securitycenterv2/OrganizationSccBigQueryExport.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationSccBigQueryExport' +api_resource_type_kind: BigQueryExport description: | A Cloud Security Command Center (Cloud SCC) Big Query Export Config. It represents exporting Security Command Center data, including assets, findings, and security marks diff --git a/mmv1/products/securitycenterv2/OrganizationSccBigQueryExports.yaml b/mmv1/products/securitycenterv2/OrganizationSccBigQueryExports.yaml index 4888bfe26fed..5d5ee5910ccc 100644 --- a/mmv1/products/securitycenterv2/OrganizationSccBigQueryExports.yaml +++ b/mmv1/products/securitycenterv2/OrganizationSccBigQueryExports.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationSccBigQueryExports' +api_resource_type_kind: BigQueryExport description: | A Cloud Security Command Center (Cloud SCC) Big Query Export Config. It represents exporting Security Command Center data, including assets, findings, and security marks diff --git a/mmv1/products/securitycenterv2/OrganizationSource.yaml b/mmv1/products/securitycenterv2/OrganizationSource.yaml index 08af4416b71b..a54583806a8a 100644 --- a/mmv1/products/securitycenterv2/OrganizationSource.yaml +++ b/mmv1/products/securitycenterv2/OrganizationSource.yaml @@ -13,6 +13,7 @@ --- name: 'OrganizationSource' +api_resource_type_kind: Source description: | A Cloud Security Command Center's (Cloud SCC) finding source. A finding source is an entity or a mechanism that can produce a finding. A source is diff --git a/mmv1/products/securitycenterv2/ProjectMuteConfig.yaml b/mmv1/products/securitycenterv2/ProjectMuteConfig.yaml index a52b19816d5e..4bd083286f5f 100644 --- a/mmv1/products/securitycenterv2/ProjectMuteConfig.yaml +++ b/mmv1/products/securitycenterv2/ProjectMuteConfig.yaml @@ -13,6 +13,7 @@ --- name: 'ProjectMuteConfig' +api_resource_type_kind: MuteConfig description: | Mute Findings is a volume management feature in Security Command Center that lets you manually or programmatically hide irrelevant findings, diff --git a/mmv1/products/securitycenterv2/ProjectNotificationConfig.yaml b/mmv1/products/securitycenterv2/ProjectNotificationConfig.yaml index 8c59d2f88b6d..739b99758600 100644 --- a/mmv1/products/securitycenterv2/ProjectNotificationConfig.yaml +++ b/mmv1/products/securitycenterv2/ProjectNotificationConfig.yaml @@ -13,6 +13,7 @@ --- name: 'ProjectNotificationConfig' +api_resource_type_kind: NotificationConfig description: | A Cloud Security Command Center (Cloud SCC) notification configs. A notification config is a Cloud SCC resource that contains the diff --git a/mmv1/products/securitycenterv2/ProjectSccBigQueryExport.yaml b/mmv1/products/securitycenterv2/ProjectSccBigQueryExport.yaml index 3e955fa4f05a..3f577888b273 100644 --- a/mmv1/products/securitycenterv2/ProjectSccBigQueryExport.yaml +++ b/mmv1/products/securitycenterv2/ProjectSccBigQueryExport.yaml @@ -13,6 +13,7 @@ --- name: 'ProjectSccBigQueryExport' +api_resource_type_kind: BigQueryExport description: | A Cloud Security Command Center (Cloud SCC) Big Query Export Config. It represents exporting Security Command Center data, including assets, findings, and security marks diff --git a/mmv1/products/servicenetworking/VPCServiceControls.yaml b/mmv1/products/servicenetworking/VPCServiceControls.yaml index ad4a00784e00..23a05bf33a50 100644 --- a/mmv1/products/servicenetworking/VPCServiceControls.yaml +++ b/mmv1/products/servicenetworking/VPCServiceControls.yaml @@ -13,6 +13,7 @@ --- name: 'VPCServiceControls' +api_resource_type_kind: VpcServiceControls description: | Manages the VPC Service Controls configuration for a service networking connection diff --git a/mmv1/products/sourcerepo/Repository.yaml b/mmv1/products/sourcerepo/Repository.yaml index 825c7be24376..3298057649f7 100644 --- a/mmv1/products/sourcerepo/Repository.yaml +++ b/mmv1/products/sourcerepo/Repository.yaml @@ -13,6 +13,7 @@ --- name: 'Repository' +api_resource_type_kind: Repo description: | A repository (or repo) is a Git repository storing versioned source content. references: diff --git a/mmv1/products/spanner/Instance.yaml b/mmv1/products/spanner/Instance.yaml index db8b59e74095..cc1258aeebbe 100644 --- a/mmv1/products/spanner/Instance.yaml +++ b/mmv1/products/spanner/Instance.yaml @@ -228,6 +228,40 @@ properties: should be trying to achieve for the instance. This number is on a scale from 0 (no utilization) to 100 (full utilization). + - name: 'asymmetricAutoscalingOptions' + type: Array + description: | + Asymmetric autoscaling options for specific replicas. + item_type: + type: NestedObject + properties: + - name: 'replicaSelection' + type: NestedObject + required: true + properties: + - name: 'location' + type: String + required: true + description: | + The location of the replica to apply asymmetric autoscaling options. + - name: 'overrides' + type: NestedObject + required: true + properties: + - name: 'autoscalingLimits' + type: NestedObject + required: true + properties: + - name: 'minNodes' + type: Integer + required: true + description: | + The minimum number of nodes for this specific replica. + - name: 'maxNodes' + type: Integer + required: true + description: | + The maximum number of nodes for this specific replica. - name: 'edition' type: Enum description: | diff --git a/mmv1/products/sql/Database.yaml b/mmv1/products/sql/Database.yaml index 01f4a62d6247..089cb3384f0c 100644 --- a/mmv1/products/sql/Database.yaml +++ b/mmv1/products/sql/Database.yaml @@ -48,6 +48,7 @@ async: collection_url_key: 'items' custom_code: pre_delete: 'templates/terraform/pre_delete/sql_database_deletion_policy.tmpl' + pre_read: 'templates/terraform/pre_read/sql_database_activation_policy.tmpl' # Sweeper skipped as this resource has customized deletion. exclude_sweeper: true read_error_transform: 'transformSQLDatabaseReadError' diff --git a/mmv1/products/sql/SourceRepresentationInstance.yaml b/mmv1/products/sql/SourceRepresentationInstance.yaml index 6ff4b0acd3b3..dff9a0c49b71 100644 --- a/mmv1/products/sql/SourceRepresentationInstance.yaml +++ b/mmv1/products/sql/SourceRepresentationInstance.yaml @@ -13,6 +13,7 @@ --- name: 'SourceRepresentationInstance' +api_resource_type_kind: Instance kind: 'sql#instance' description: | A source representation instance is a Cloud SQL instance that represents diff --git a/mmv1/products/storage/DefaultObjectAccessControl.yaml b/mmv1/products/storage/DefaultObjectAccessControl.yaml index 010da944f209..2dab568caf5f 100644 --- a/mmv1/products/storage/DefaultObjectAccessControl.yaml +++ b/mmv1/products/storage/DefaultObjectAccessControl.yaml @@ -13,6 +13,7 @@ --- name: 'DefaultObjectAccessControl' +api_resource_type_kind: ObjectAccessControl kind: 'storage#objectAccessControl' description: | The DefaultObjectAccessControls resources represent the Access Control diff --git a/mmv1/products/storage/HmacKey.yaml b/mmv1/products/storage/HmacKey.yaml index 48837b61b225..3515366a4c76 100644 --- a/mmv1/products/storage/HmacKey.yaml +++ b/mmv1/products/storage/HmacKey.yaml @@ -13,6 +13,7 @@ --- name: 'HmacKey' +api_resource_type_kind: HmacKeyMetadata kind: 'storage#hmacKey' description: | The hmacKeys resource represents an HMAC key within Cloud Storage. The resource diff --git a/mmv1/products/storagetransfer/AgentPool.yaml b/mmv1/products/storagetransfer/AgentPool.yaml index e56175b9eee8..68d501a2ce90 100644 --- a/mmv1/products/storagetransfer/AgentPool.yaml +++ b/mmv1/products/storagetransfer/AgentPool.yaml @@ -13,6 +13,7 @@ --- name: 'AgentPool' +api_resource_type_kind: agentPools description: 'Represents an On-Premises Agent pool.' references: guides: diff --git a/mmv1/products/tpuv2/Vm.yaml b/mmv1/products/tpuv2/Vm.yaml index 37718e44cd1c..a81aaa0beec2 100644 --- a/mmv1/products/tpuv2/Vm.yaml +++ b/mmv1/products/tpuv2/Vm.yaml @@ -13,6 +13,7 @@ --- name: 'Vm' +api_resource_type_kind: Node description: | A Cloud TPU VM instance. min_version: 'beta' diff --git a/mmv1/products/vertexai/FeatureGroupFeature.yaml b/mmv1/products/vertexai/FeatureGroupFeature.yaml index c847e310dc33..a542a4ca32d8 100644 --- a/mmv1/products/vertexai/FeatureGroupFeature.yaml +++ b/mmv1/products/vertexai/FeatureGroupFeature.yaml @@ -13,6 +13,7 @@ --- name: 'FeatureGroupFeature' +api_resource_type_kind: Feature description: Vertex AI Feature Group Feature is feature metadata information. references: guides: diff --git a/mmv1/products/vertexai/FeatureOnlineStoreFeatureview.yaml b/mmv1/products/vertexai/FeatureOnlineStoreFeatureview.yaml index e76635634101..597e4b8bd594 100644 --- a/mmv1/products/vertexai/FeatureOnlineStoreFeatureview.yaml +++ b/mmv1/products/vertexai/FeatureOnlineStoreFeatureview.yaml @@ -13,6 +13,7 @@ --- name: 'FeatureOnlineStoreFeatureview' +api_resource_type_kind: FeatureView description: |- FeatureView is representation of values that the FeatureOnlineStore will serve based on its syncConfig. references: diff --git a/mmv1/products/vertexai/FeaturestoreEntitytype.yaml b/mmv1/products/vertexai/FeaturestoreEntitytype.yaml index 992ba1f88622..c847f218e6a4 100644 --- a/mmv1/products/vertexai/FeaturestoreEntitytype.yaml +++ b/mmv1/products/vertexai/FeaturestoreEntitytype.yaml @@ -13,6 +13,7 @@ --- name: 'FeaturestoreEntitytype' +api_resource_type_kind: EntityType description: |- An entity type is a type of object in a system that needs to be modeled and have stored information about. For example, driver is an entity type, and driver0 is an instance of an entity type driver. references: diff --git a/mmv1/products/vertexai/FeaturestoreEntitytypeFeature.yaml b/mmv1/products/vertexai/FeaturestoreEntitytypeFeature.yaml index 5cae6f2178b4..788faa5d127d 100644 --- a/mmv1/products/vertexai/FeaturestoreEntitytypeFeature.yaml +++ b/mmv1/products/vertexai/FeaturestoreEntitytypeFeature.yaml @@ -13,6 +13,7 @@ --- name: 'FeaturestoreEntitytypeFeature' +api_resource_type_kind: Feature description: |- Feature Metadata information that describes an attribute of an entity type. For example, apple is an entity type, and color is a feature that describes apple. references: diff --git a/mmv1/products/vertexai/IndexEndpointDeployedIndex.yaml b/mmv1/products/vertexai/IndexEndpointDeployedIndex.yaml index 7f69d6f0d5f3..719d6ed862e7 100644 --- a/mmv1/products/vertexai/IndexEndpointDeployedIndex.yaml +++ b/mmv1/products/vertexai/IndexEndpointDeployedIndex.yaml @@ -13,6 +13,7 @@ --- name: 'IndexEndpointDeployedIndex' +api_resource_type_kind: IndexEndpoint description: |- An endpoint indexes are deployed into. An index endpoint can have multiple deployed indexes. references: diff --git a/mmv1/products/vmwareengine/Cluster.yaml b/mmv1/products/vmwareengine/Cluster.yaml index ddf7dcdc1f29..9222c8560759 100644 --- a/mmv1/products/vmwareengine/Cluster.yaml +++ b/mmv1/products/vmwareengine/Cluster.yaml @@ -149,3 +149,103 @@ properties: If zero is provided max value from `nodeType.availableCustomCoreCounts` will be used. Once the customer is created then corecount cannot be changed. default_value: 0 + - name: 'autoscalingSettings' + type: NestedObject + description: | + Configuration of the autoscaling applied to this cluster + properties: + - name: 'autoscalingPolicies' + type: Map + required: true + description: | + The map with autoscaling policies applied to the cluster. + The key is the identifier of the policy. + It must meet the following requirements: + * Only contains 1-63 alphanumeric characters and hyphens + * Begins with an alphabetical character + * Ends with a non-hyphen character + * Not formatted as a UUID + * Complies with [RFC 1034](https://datatracker.ietf.org/doc/html/rfc1034) (section 3.5) + + Currently the map must contain only one element + that describes the autoscaling policy for compute nodes. + key_name: 'autoscale_policy_id' + key_description: 'The key is the identifier of the policy.' + value_type: + name: AutoscalingPolicy + type: NestedObject + properties: + - name: 'nodeTypeId' + type: String + required: true + description: | + The canonical identifier of the node type to add or remove. + - name: 'scaleOutSize' + type: Integer + required: true + description: | + Number of nodes to add to a cluster during a scale-out operation. + Must be divisible by 2 for stretched clusters. + - name: 'cpuThresholds' + type: NestedObject + description: | + Utilization thresholds pertaining to CPU utilization. + properties: + - name: 'scaleOut' + type: Integer + required: true + description: | + The utilization triggering the scale-out operation in percent. + - name: 'scaleIn' + type: Integer + required: true + description: | + The utilization triggering the scale-in operation in percent. + - name: 'consumedMemoryThresholds' + type: NestedObject + description: | + Utilization thresholds pertaining to amount of consumed memory. + properties: + - name: 'scaleOut' + type: Integer + required: true + description: | + The utilization triggering the scale-out operation in percent. + - name: 'scaleIn' + type: Integer + required: true + description: | + The utilization triggering the scale-in operation in percent. + - name: 'storageThresholds' + type: NestedObject + description: | + Utilization thresholds pertaining to amount of consumed storage. + properties: + - name: 'scaleOut' + type: Integer + required: true + description: | + The utilization triggering the scale-out operation in percent. + - name: 'scaleIn' + type: Integer + required: true + description: | + The utilization triggering the scale-in operation in percent. + - name: 'minClusterNodeCount' + type: Integer + description: | + Minimum number of nodes of any type in a cluster. + Mandatory for successful addition of autoscaling settings in cluster. + - name: 'maxClusterNodeCount' + type: Integer + description: | + Maximum number of nodes of any type in a cluster. + Mandatory for successful addition of autoscaling settings in cluster. + - name: 'coolDownPeriod' + type: String + description: | + The minimum duration between consecutive autoscale operations. + It starts once addition or removal of nodes is fully completed. + Minimum cool down period is 30m. + Cool down period must be in whole minutes (for example, 30m, 31m, 50m). + Mandatory for successful addition of autoscaling settings in cluster. diff --git a/mmv1/products/vmwareengine/Network.yaml b/mmv1/products/vmwareengine/Network.yaml index aea8fbd8a50c..45a9b8c22696 100644 --- a/mmv1/products/vmwareengine/Network.yaml +++ b/mmv1/products/vmwareengine/Network.yaml @@ -13,6 +13,7 @@ --- name: 'Network' +api_resource_type_kind: VmwareEngineNetwork description: | Provides connectivity for VMware Engine private clouds. references: diff --git a/mmv1/templates/terraform/constants/access_context_manager.go.tmpl b/mmv1/templates/terraform/constants/access_context_manager.go.tmpl new file mode 100644 index 000000000000..476f59f98a52 --- /dev/null +++ b/mmv1/templates/terraform/constants/access_context_manager.go.tmpl @@ -0,0 +1,41 @@ +func {{$.ResourceName}}EgressToResourcesDiffSupressFunc(_, _, _ string, d *schema.ResourceData) bool { + old, new := d.GetChange("egress_to.0.resources") + + oldResources, err := tpgresource.InterfaceSliceToStringSlice(old) + if err != nil { + log.Printf("[ERROR] Failed to convert config value: %s", err) + return false + } + + newResources, err := tpgresource.InterfaceSliceToStringSlice(new) + if err != nil { + log.Printf("[ERROR] Failed to convert config value: %s", err) + return false + } + + sort.Strings(oldResources) + sort.Strings(newResources) + + return slices.Equal(oldResources, newResources) +} + +func {{$.ResourceName}}IngressToResourcesDiffSupressFunc(_, _, _ string, d *schema.ResourceData) bool { + old, new := d.GetChange("ingress_to.0.resources") + + oldResources, err := tpgresource.InterfaceSliceToStringSlice(old) + if err != nil { + log.Printf("[ERROR] Failed to convert config value: %s", err) + return false + } + + newResources, err := tpgresource.InterfaceSliceToStringSlice(new) + if err != nil { + log.Printf("[ERROR] Failed to convert config value: %s", err) + return false + } + + sort.Strings(oldResources) + sort.Strings(newResources) + + return slices.Equal(oldResources, newResources) +} \ No newline at end of file diff --git a/mmv1/templates/terraform/constants/apphub_application.go.tmpl b/mmv1/templates/terraform/constants/apphub_application.go.tmpl new file mode 100644 index 000000000000..be9892151d7e --- /dev/null +++ b/mmv1/templates/terraform/constants/apphub_application.go.tmpl @@ -0,0 +1,17 @@ +func apphubApplicationCustomizeDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { + if diff.HasChange("location") || diff.HasChange("scope.0.type") { + location := diff.Get("location") + scope_type := diff.Get("scope.0.type") + + if scope_type == "GLOBAL" { + if location != "global" { + return fmt.Errorf("Error validating location %s with %s scope type", location, scope_type) + } + } else { + if location == "global" { + return fmt.Errorf("Error validating location %s with %s scope type", location, scope_type) + } + } + } + return nil +} \ No newline at end of file diff --git a/mmv1/templates/terraform/custom_flatten/accesscontextmanager_egress_policy_resources_custom_flatten.go.tmpl b/mmv1/templates/terraform/custom_flatten/accesscontextmanager_egress_policy_resources_custom_flatten.go.tmpl new file mode 100644 index 000000000000..d8508834b044 --- /dev/null +++ b/mmv1/templates/terraform/custom_flatten/accesscontextmanager_egress_policy_resources_custom_flatten.go.tmpl @@ -0,0 +1,25 @@ +func flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + rawConfigValue := d.Get("egress_to.0.resources") + + // Convert config value to []string + configValue, err := tpgresource.InterfaceSliceToStringSlice(rawConfigValue) + if err != nil { + log.Printf("[ERROR] Failed to convert config value: %s", err) + return v + } + + // Convert v to []string + apiStringValue, err := tpgresource.InterfaceSliceToStringSlice(v) + if err != nil { + log.Printf("[ERROR] Failed to convert API value: %s", err) + return v + } + + sortedStrings, err := tpgresource.SortStringsByConfigOrder(configValue, apiStringValue) + if err != nil { + log.Printf("[ERROR] Could not sort API response value: %s", err) + return v + } + + return sortedStrings +} \ No newline at end of file diff --git a/mmv1/templates/terraform/custom_flatten/accesscontextmanager_ingress_policy_resources_custom_flatten.go.tmpl b/mmv1/templates/terraform/custom_flatten/accesscontextmanager_ingress_policy_resources_custom_flatten.go.tmpl new file mode 100644 index 000000000000..b7c1f0f7d499 --- /dev/null +++ b/mmv1/templates/terraform/custom_flatten/accesscontextmanager_ingress_policy_resources_custom_flatten.go.tmpl @@ -0,0 +1,25 @@ +func flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + rawConfigValue := d.Get("ingress_to.0.resources") + + // Convert config value to []string + configValue, err := tpgresource.InterfaceSliceToStringSlice(rawConfigValue) + if err != nil { + log.Printf("[ERROR] Failed to convert config value: %s", err) + return v + } + + // Convert v to []string + apiStringValue, err := tpgresource.InterfaceSliceToStringSlice(v) + if err != nil { + log.Printf("[ERROR] Failed to convert API value: %s", err) + return v + } + + sortedStrings, err := tpgresource.SortStringsByConfigOrder(configValue, apiStringValue) + if err != nil { + log.Printf("[ERROR] Could not sort API response value: %s", err) + return v + } + + return sortedStrings +} \ No newline at end of file diff --git a/mmv1/templates/terraform/custom_import/apigee_app_group.go.tmpl b/mmv1/templates/terraform/custom_import/apigee_app_group.go.tmpl new file mode 100644 index 000000000000..eb81bd27be4c --- /dev/null +++ b/mmv1/templates/terraform/custom_import/apigee_app_group.go.tmpl @@ -0,0 +1,42 @@ +config := meta.(*transport_tpg.Config) + +// current import_formats cannot import fields with forward slashes in their value +if err := tpgresource.ParseImportId([]string{"(?P.+)"}, d, config); err != nil { + return nil, err +} + +nameParts := strings.Split(d.Get("name").(string), "/") +if len(nameParts) == 4 { + // `organizations/{{"{{"}}org_name{{"}}"}}/appgroups/{{"{{"}}name{{"}}"}}` + orgId := fmt.Sprintf("organizations/%s", nameParts[1]) + if err := d.Set("org_id", orgId); err != nil { + return nil, fmt.Errorf("Error setting org_id: %s", err) + } + if err := d.Set("name", nameParts[3]); err != nil { + return nil, fmt.Errorf("Error setting name: %s", err) + } +} else if len(nameParts) == 3 { + // `organizations/{{"{{"}}org_name{{"}}"}}/{{"{{"}}name{{"}}"}}` + orgId := fmt.Sprintf("organizations/%s", nameParts[1]) + if err := d.Set("org_id", orgId); err != nil { + return nil, fmt.Errorf("Error setting org_id: %s", err) + } + if err := d.Set("name", nameParts[2]); err != nil { + return nil, fmt.Errorf("Error setting name: %s", err) + } +} else { + return nil, fmt.Errorf( + "Saw %s when the name is expected to have shape %s or %s", + d.Get("name"), + "organizations/{{"{{"}}org_name{{"}}"}}/appgroups/{{"{{"}}name{{"}}"}}", + "organizations/{{"{{"}}org_name{{"}}"}}/{{"{{"}}name{{"}}"}}") +} + +// Replace import id for the resource id +id, err := tpgresource.ReplaceVars(d, config, "{{"{{"}}org_id{{"}}"}}/appgroups/{{"{{"}}name{{"}}"}}") +if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) +} +d.SetId(id) + +return []*schema.ResourceData{d}, nil \ No newline at end of file diff --git a/mmv1/templates/terraform/custom_import/apigee_developer.go.tmpl b/mmv1/templates/terraform/custom_import/apigee_developer.go.tmpl new file mode 100644 index 000000000000..5eafd0cdb26c --- /dev/null +++ b/mmv1/templates/terraform/custom_import/apigee_developer.go.tmpl @@ -0,0 +1,42 @@ +config := meta.(*transport_tpg.Config) + +// current import_formats cannot import fields with forward slashes in their value +if err := tpgresource.ParseImportId([]string{"(?P.+)"}, d, config); err != nil { + return nil, err +} + +nameParts := strings.Split(d.Get("email").(string), "/") +if len(nameParts) == 4 { + // `organizations/{{"{{"}}org_name{{"}}"}}/developers/{{"{{"}}email{{"}}"}}` + orgId := fmt.Sprintf("organizations/%s", nameParts[1]) + if err := d.Set("org_id", orgId); err != nil { + return nil, fmt.Errorf("Error setting org_id: %s", err) + } + if err := d.Set("email", nameParts[3]); err != nil { + return nil, fmt.Errorf("Error setting email: %s", err) + } +} else if len(nameParts) == 3 { + // `organizations/{{"{{"}}org_name{{"}}"}}/{{"{{"}}email{{"}}"}}` + orgId := fmt.Sprintf("organizations/%s", nameParts[1]) + if err := d.Set("org_id", orgId); err != nil { + return nil, fmt.Errorf("Error setting org_id: %s", err) + } + if err := d.Set("email", nameParts[2]); err != nil { + return nil, fmt.Errorf("Error setting name: %s", err) + } +} else { + return nil, fmt.Errorf( + "Saw %s when the name is expected to have shape %s or %s", + d.Get("name"), + "organizations/{{"{{"}}org_name{{"}}"}}/developers/{{"{{"}}email{{"}}"}}", + "organizations/{{"{{"}}org_name{{"}}"}}/{{"{{"}}email{{"}}"}}") +} + +// Replace import id for the resource id +id, err := tpgresource.ReplaceVars(d, config, "{{"{{"}}org_id{{"}}"}}/developers/{{"{{"}}email{{"}}"}}") +if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) +} +d.SetId(id) + +return []*schema.ResourceData{d}, nil diff --git a/mmv1/templates/terraform/encoders/spanner_instance_update.go.tmpl b/mmv1/templates/terraform/encoders/spanner_instance_update.go.tmpl index 5c07336b6775..50dbdc8e414e 100644 --- a/mmv1/templates/terraform/encoders/spanner_instance_update.go.tmpl +++ b/mmv1/templates/terraform/encoders/spanner_instance_update.go.tmpl @@ -46,6 +46,9 @@ if d.HasChange("autoscaling_config") { if d.HasChange("autoscaling_config.0.autoscaling_targets.0.storage_utilization_percent") { updateMask = append(updateMask, "autoscalingConfig.autoscalingTargets.storageUtilizationPercent") } + if d.HasChange("autoscaling_config.0.asymmetric_autoscaling_options") { + updateMask = append(updateMask, "autoscalingConfig.asymmetricAutoscalingOptions") + } } } newObj["fieldMask"] = strings.Join(updateMask, ",") diff --git a/mmv1/templates/terraform/examples/apigee_app_group_basic.tf.tmpl b/mmv1/templates/terraform/examples/apigee_app_group_basic.tf.tmpl new file mode 100644 index 000000000000..1e11f856a5f9 --- /dev/null +++ b/mmv1/templates/terraform/examples/apigee_app_group_basic.tf.tmpl @@ -0,0 +1,45 @@ +data "google_client_config" "current" {} + +resource "google_compute_network" "apigee_network" { + name = "apigee-network" +} + +resource "google_compute_global_address" "apigee_range" { + name = "apigee-range" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = google_compute_network.apigee_network.id +} + +resource "google_service_networking_connection" "apigee_vpc_connection" { + network = google_compute_network.apigee_network.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.apigee_range.name] +} + +resource "google_apigee_organization" "apigee_org" { + analytics_region = "us-central1" + project_id = data.google_client_config.current.project + authorized_network = google_compute_network.apigee_network.id + depends_on = [google_service_networking_connection.apigee_vpc_connection] +} + +resource "google_apigee_instance" "apigee_instance" { + name = "instance" + location = "us-central1" + org_id = google_apigee_organization.apigee_org.id + peering_cidr_range = "SLASH_22" +} + +resource "google_apigee_app_group" "apigee_app_group" { + name = "{{index $.Vars "app_group_name"}}" + display_name = "Test app group" + channel_id = "storefront" + channel_uri = "https://my-dev-portal.org/groups/my-group" + status = "active" + org_id = google_apigee_organization.apigee_org.id + depends_on = [ + google_apigee_instance.apigee_instance + ] +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/apigee_app_group_basic_test.tf.tmpl b/mmv1/templates/terraform/examples/apigee_app_group_basic_test.tf.tmpl new file mode 100644 index 000000000000..1bc63f303ce1 --- /dev/null +++ b/mmv1/templates/terraform/examples/apigee_app_group_basic_test.tf.tmpl @@ -0,0 +1,73 @@ +resource "google_project" "project" { + project_id = "tf-test%{random_suffix}" + name = "tf-test%{random_suffix}" + org_id = "{{index $.TestEnvVars "org_id"}}" + billing_account = "{{index $.TestEnvVars "billing_account"}}" + deletion_policy = "DELETE" +} + +resource "google_project_service" "apigee" { + project = google_project.project.project_id + service = "apigee.googleapis.com" +} + +resource "google_project_service" "compute" { + project = google_project.project.project_id + service = "compute.googleapis.com" +} + +resource "google_project_service" "servicenetworking" { + project = google_project.project.project_id + service = "servicenetworking.googleapis.com" +} + +resource "google_compute_network" "apigee_network" { + name = "apigee-network" + project = google_project.project.project_id + depends_on = [google_project_service.compute] +} + +resource "google_compute_global_address" "apigee_range" { + name = "apigee-range" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = google_compute_network.apigee_network.id + project = google_project.project.project_id +} + +resource "google_service_networking_connection" "apigee_vpc_connection" { + network = google_compute_network.apigee_network.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.apigee_range.name] + depends_on = [google_project_service.servicenetworking] +} + +resource "google_apigee_organization" "apigee_org" { + analytics_region = "us-central1" + project_id = google_project.project.project_id + authorized_network = google_compute_network.apigee_network.id + depends_on = [ + google_service_networking_connection.apigee_vpc_connection, + google_project_service.apigee, + ] +} + +resource "google_apigee_instance" "apigee_instance" { + name = "tf-test%{random_suffix}" + location = "us-central1" + org_id = google_apigee_organization.apigee_org.id + peering_cidr_range = "SLASH_22" +} + +resource "google_apigee_app_group" "{{$.PrimaryResourceId}}" { + name = "tf-test%{random_suffix}" + display_name = "Test app group" + channel_id = "storefront" + channel_uri = "https://my-dev-portal.org/groups/my-group" + status = "active" + org_id = google_apigee_organization.apigee_org.id + depends_on = [ + google_apigee_instance.apigee_instance + ] +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/apigee_app_group_with_attributes.tf.tmpl b/mmv1/templates/terraform/examples/apigee_app_group_with_attributes.tf.tmpl new file mode 100644 index 000000000000..f862015f6350 --- /dev/null +++ b/mmv1/templates/terraform/examples/apigee_app_group_with_attributes.tf.tmpl @@ -0,0 +1,53 @@ +data "google_client_config" "current" {} + +resource "google_compute_network" "apigee_network" { + name = "apigee-network" +} + +resource "google_compute_global_address" "apigee_range" { + name = "apigee-range" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = google_compute_network.apigee_network.id +} + +resource "google_service_networking_connection" "apigee_vpc_connection" { + network = google_compute_network.apigee_network.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.apigee_range.name] +} + +resource "google_apigee_organization" "apigee_org" { + analytics_region = "us-central1" + project_id = data.google_client_config.current.project + authorized_network = google_compute_network.apigee_network.id + depends_on = [google_service_networking_connection.apigee_vpc_connection] +} + +resource "google_apigee_instance" "apigee_instance" { + name = "instance" + location = "us-central1" + org_id = google_apigee_organization.apigee_org.id + peering_cidr_range = "SLASH_22" +} + +resource "google_apigee_app_group" "apigee_app_group" { + name = "{{index $.Vars "app_group_name"}}" + display_name = "Test app group" + channel_id = "storefront" + channel_uri = "https://my-dev-portal.org/groups/my-group" + status = "active" + org_id = google_apigee_organization.apigee_org.id + attributes { + name = "business_unit" + value = "HR" + } + attributes { + name = "department" + value = "payroll" + } + depends_on = [ + google_apigee_instance.apigee_instance + ] +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/apigee_app_group_with_attributes_test.tf.tmpl b/mmv1/templates/terraform/examples/apigee_app_group_with_attributes_test.tf.tmpl new file mode 100644 index 000000000000..6c68f9cb8374 --- /dev/null +++ b/mmv1/templates/terraform/examples/apigee_app_group_with_attributes_test.tf.tmpl @@ -0,0 +1,81 @@ +resource "google_project" "project" { + project_id = "tf-test%{random_suffix}" + name = "tf-test%{random_suffix}" + org_id = "{{index $.TestEnvVars "org_id"}}" + billing_account = "{{index $.TestEnvVars "billing_account"}}" + deletion_policy = "DELETE" +} + +resource "google_project_service" "apigee" { + project = google_project.project.project_id + service = "apigee.googleapis.com" +} + +resource "google_project_service" "compute" { + project = google_project.project.project_id + service = "compute.googleapis.com" +} + +resource "google_project_service" "servicenetworking" { + project = google_project.project.project_id + service = "servicenetworking.googleapis.com" +} + +resource "google_compute_network" "apigee_network" { + name = "apigee-network" + project = google_project.project.project_id + depends_on = [google_project_service.compute] +} + +resource "google_compute_global_address" "apigee_range" { + name = "apigee-range" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = google_compute_network.apigee_network.id + project = google_project.project.project_id +} + +resource "google_service_networking_connection" "apigee_vpc_connection" { + network = google_compute_network.apigee_network.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.apigee_range.name] + depends_on = [google_project_service.servicenetworking] +} + +resource "google_apigee_organization" "apigee_org" { + analytics_region = "us-central1" + project_id = google_project.project.project_id + authorized_network = google_compute_network.apigee_network.id + depends_on = [ + google_service_networking_connection.apigee_vpc_connection, + google_project_service.apigee, + ] +} + +resource "google_apigee_instance" "apigee_instance" { + name = "tf-test%{random_suffix}" + location = "us-central1" + org_id = google_apigee_organization.apigee_org.id + peering_cidr_range = "SLASH_22" +} + +resource "google_apigee_app_group" "{{$.PrimaryResourceId}}" { + name = "tf-test%{random_suffix}" + display_name = "Test app group" + channel_id = "storefront" + channel_uri = "https://my-dev-portal.org/groups/my-group" + status = "active" + org_id = google_apigee_organization.apigee_org.id + attributes { + name = "business_unit" + value = "HR" + } + attributes { + name = "department" + value = "payroll" + } + depends_on = [ + google_apigee_instance.apigee_instance + ] +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/apigee_developer_basic.tf.tmpl b/mmv1/templates/terraform/examples/apigee_developer_basic.tf.tmpl new file mode 100644 index 000000000000..4e26d7b8c920 --- /dev/null +++ b/mmv1/templates/terraform/examples/apigee_developer_basic.tf.tmpl @@ -0,0 +1,44 @@ +data "google_client_config" "current" {} + +resource "google_compute_network" "apigee_network" { + name = "apigee-network" +} + +resource "google_compute_global_address" "apigee_range" { + name = "apigee-range" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = google_compute_network.apigee_network.id +} + +resource "google_service_networking_connection" "apigee_vpc_connection" { + network = google_compute_network.apigee_network.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.apigee_range.name] +} + +resource "google_apigee_organization" "apigee_org" { + analytics_region = "us-central1" + project_id = data.google_client_config.current.project + authorized_network = google_compute_network.apigee_network.id + depends_on = [google_service_networking_connection.apigee_vpc_connection] +} + +resource "google_apigee_instance" "apigee_instance" { + name = "{{index $.Vars "instance_name"}}" + location = "us-central1" + org_id = google_apigee_organization.apigee_org.id + peering_cidr_range = "SLASH_22" +} + +resource "google_apigee_developer" "apigee_developer" { + email = "{{index $.Vars "developer_email"}}" + first_name = "John" + last_name = "Doe" + user_name = "john.doe" + org_id = google_apigee_organization.apigee_org.id + depends_on = [ + google_apigee_instance.apigee_instance + ] +} diff --git a/mmv1/templates/terraform/examples/apigee_developer_basic_test.tf.tmpl b/mmv1/templates/terraform/examples/apigee_developer_basic_test.tf.tmpl new file mode 100644 index 000000000000..20ef39fa6fae --- /dev/null +++ b/mmv1/templates/terraform/examples/apigee_developer_basic_test.tf.tmpl @@ -0,0 +1,73 @@ +resource "google_project" "project" { + project_id = "tf-test%{random_suffix}" + name = "tf-test%{random_suffix}" + org_id = "{{index $.TestEnvVars "org_id"}}" + billing_account = "{{index $.TestEnvVars "billing_account"}}" + deletion_policy = "DELETE" +} + +resource "google_project_service" "apigee" { + project = google_project.project.project_id + service = "apigee.googleapis.com" +} + +resource "google_project_service" "compute" { + project = google_project.project.project_id + service = "compute.googleapis.com" +} + +resource "google_project_service" "servicenetworking" { + project = google_project.project.project_id + service = "servicenetworking.googleapis.com" +} + +resource "google_compute_network" "apigee_network" { + name = "apigee-network" + project = google_project.project.project_id + depends_on = [google_project_service.compute] +} + +resource "google_compute_global_address" "apigee_range" { + name = "apigee-range" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = google_compute_network.apigee_network.id + project = google_project.project.project_id +} + +resource "google_service_networking_connection" "apigee_vpc_connection" { + network = google_compute_network.apigee_network.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.apigee_range.name] + depends_on = [google_project_service.servicenetworking] +} + +resource "google_apigee_organization" "apigee_org" { + analytics_region = "us-central1" + project_id = google_project.project.project_id + authorized_network = google_compute_network.apigee_network.id + depends_on = [ + google_service_networking_connection.apigee_vpc_connection, + google_project_service.apigee, + ] +} + +resource "google_apigee_instance" "apigee_instance" { + name = "tf-test%{random_suffix}" + location = "us-central1" + org_id = google_apigee_organization.apigee_org.id + peering_cidr_range = "SLASH_22" +} + +resource "google_apigee_developer" "{{$.PrimaryResourceId}}" { + email = "tf-test%{random_suffix}@acme.com" + first_name = "John" + last_name = "Doe" + user_name = "john.doe" + org_id = google_apigee_organization.apigee_org.id + depends_on = [ + google_apigee_instance.apigee_instance + ] +} + diff --git a/mmv1/templates/terraform/examples/apigee_developer_with_attributes.tf.tmpl b/mmv1/templates/terraform/examples/apigee_developer_with_attributes.tf.tmpl new file mode 100644 index 000000000000..58eef2cfc36b --- /dev/null +++ b/mmv1/templates/terraform/examples/apigee_developer_with_attributes.tf.tmpl @@ -0,0 +1,52 @@ +data "google_client_config" "current" {} + +resource "google_compute_network" "apigee_network" { + name = "apigee-network" +} + +resource "google_compute_global_address" "apigee_range" { + name = "apigee-range" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = google_compute_network.apigee_network.id +} + +resource "google_service_networking_connection" "apigee_vpc_connection" { + network = google_compute_network.apigee_network.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.apigee_range.name] +} + +resource "google_apigee_organization" "apigee_org" { + analytics_region = "us-central1" + project_id = data.google_client_config.current.project + authorized_network = google_compute_network.apigee_network.id + depends_on = [google_service_networking_connection.apigee_vpc_connection] +} + +resource "google_apigee_instance" "apigee_instance" { + name = "{{index $.Vars "instance_name"}}" + location = "us-central1" + org_id = google_apigee_organization.apigee_org.id + peering_cidr_range = "SLASH_22" +} + +resource "google_apigee_developer" "apigee_developer" { + email = "{{index $.Vars "developer_email"}}" + first_name = "John" + last_name = "Doe" + user_name = "john.doe" + attributes { + name = "business_unit" + value = "HR" + } + attributes { + name = "department" + value = "payroll" + } + org_id = google_apigee_organization.apigee_org.id + depends_on = [ + google_apigee_instance.apigee_instance + ] +} diff --git a/mmv1/templates/terraform/examples/apigee_developer_with_attributes_test.tf.tmpl b/mmv1/templates/terraform/examples/apigee_developer_with_attributes_test.tf.tmpl new file mode 100644 index 000000000000..3f840d4acf79 --- /dev/null +++ b/mmv1/templates/terraform/examples/apigee_developer_with_attributes_test.tf.tmpl @@ -0,0 +1,81 @@ +resource "google_project" "project" { + project_id = "tf-test%{random_suffix}" + name = "tf-test%{random_suffix}" + org_id = "{{index $.TestEnvVars "org_id"}}" + billing_account = "{{index $.TestEnvVars "billing_account"}}" + deletion_policy = "DELETE" +} + +resource "google_project_service" "apigee" { + project = google_project.project.project_id + service = "apigee.googleapis.com" +} + +resource "google_project_service" "compute" { + project = google_project.project.project_id + service = "compute.googleapis.com" +} + +resource "google_project_service" "servicenetworking" { + project = google_project.project.project_id + service = "servicenetworking.googleapis.com" +} + +resource "google_compute_network" "apigee_network" { + name = "apigee-network" + project = google_project.project.project_id + depends_on = [google_project_service.compute] +} + +resource "google_compute_global_address" "apigee_range" { + name = "apigee-range" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 16 + network = google_compute_network.apigee_network.id + project = google_project.project.project_id +} + +resource "google_service_networking_connection" "apigee_vpc_connection" { + network = google_compute_network.apigee_network.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.apigee_range.name] + depends_on = [google_project_service.servicenetworking] +} + +resource "google_apigee_organization" "apigee_org" { + analytics_region = "us-central1" + project_id = google_project.project.project_id + authorized_network = google_compute_network.apigee_network.id + depends_on = [ + google_service_networking_connection.apigee_vpc_connection, + google_project_service.apigee, + ] +} + +resource "google_apigee_instance" "apigee_instance" { + name = "tf-test%{random_suffix}" + location = "us-central1" + org_id = google_apigee_organization.apigee_org.id + peering_cidr_range = "SLASH_22" +} + +resource "google_apigee_developer" "{{$.PrimaryResourceId}}" { + email = "tf-test%{random_suffix}@acme.com" + first_name = "John" + last_name = "Doe" + user_name = "john.doe" + attributes { + name = "business_unit" + value = "HR" + } + attributes { + name = "department" + value = "payroll" + } + org_id = google_apigee_organization.apigee_org.id + depends_on = [ + google_apigee_instance.apigee_instance + ] +} + diff --git a/mmv1/templates/terraform/examples/apphub_application_basic.tf.tmpl b/mmv1/templates/terraform/examples/apphub_application_basic.tf.tmpl index 1e48d362718a..b40d5a6b1fef 100644 --- a/mmv1/templates/terraform/examples/apphub_application_basic.tf.tmpl +++ b/mmv1/templates/terraform/examples/apphub_application_basic.tf.tmpl @@ -1,7 +1,7 @@ resource "google_apphub_application" "{{$.PrimaryResourceId}}" { - location = "us-east1" + location = "{{index $.Vars "location"}}" application_id = "{{index $.Vars "application_id"}}" scope { - type = "REGIONAL" + type = "{{index $.Vars "scope_type"}}" } } diff --git a/mmv1/templates/terraform/examples/bigquery_connection_basic.tf.tmpl b/mmv1/templates/terraform/examples/bigquery_connection_basic.tf.tmpl index 66ba793e0082..9294ab9c5178 100644 --- a/mmv1/templates/terraform/examples/bigquery_connection_basic.tf.tmpl +++ b/mmv1/templates/terraform/examples/bigquery_connection_basic.tf.tmpl @@ -6,7 +6,7 @@ resource "google_sql_database_instance" "instance" { tier = "db-f1-micro" } - deletion_protection = "{{index $.Vars "deletion_protection"}}" + deletion_protection = {{index $.Vars "deletion_protection"}} } resource "google_sql_database" "db" { diff --git a/mmv1/templates/terraform/examples/bigquery_connection_full.tf.tmpl b/mmv1/templates/terraform/examples/bigquery_connection_full.tf.tmpl index 6e4af5276440..357fd3b46f5c 100644 --- a/mmv1/templates/terraform/examples/bigquery_connection_full.tf.tmpl +++ b/mmv1/templates/terraform/examples/bigquery_connection_full.tf.tmpl @@ -6,7 +6,7 @@ resource "google_sql_database_instance" "instance" { tier = "db-f1-micro" } - deletion_protection = "{{index $.Vars "deletion_protection"}}" + deletion_protection = {{index $.Vars "deletion_protection"}} } resource "google_sql_database" "db" { diff --git a/mmv1/templates/terraform/examples/bigquery_connection_kms.tf.tmpl b/mmv1/templates/terraform/examples/bigquery_connection_kms.tf.tmpl deleted file mode 100644 index 622ce618e863..000000000000 --- a/mmv1/templates/terraform/examples/bigquery_connection_kms.tf.tmpl +++ /dev/null @@ -1,48 +0,0 @@ -resource "google_sql_database_instance" "instance" { - name = "{{index $.Vars "database_instance_name"}}" - database_version = "POSTGRES_11" - region = "us-central1" - settings { - tier = "db-f1-micro" - } - - deletion_protection = "{{index $.Vars "deletion_protection"}}" -} - -resource "google_sql_database" "db" { - instance = google_sql_database_instance.instance.name - name = "db" -} - -resource "google_sql_user" "user" { - name = "{{index $.Vars "username"}}" - instance = google_sql_database_instance.instance.name - password = "tf-test-my-password%{random_suffix}" -} - -data "google_bigquery_default_service_account" "bq_sa" {} - -data "google_project" "project" {} - -resource "google_project_iam_member" "key_sa_user" { - project = data.google_project.project.project_id - role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" - member = "serviceAccount:${data.google_bigquery_default_service_account.bq_sa.email}" -} - -resource "google_bigquery_connection" "{{$.PrimaryResourceId}}" { - friendly_name = "👋" - description = "a riveting description" - location = "US" - kms_key_name = "{{index $.Vars "kms_key_name"}}" - cloud_sql { - instance_id = google_sql_database_instance.instance.connection_name - database = google_sql_database.db.name - type = "POSTGRES" - credential { - username = google_sql_user.user.name - password = google_sql_user.user.password - } - } -} - diff --git a/mmv1/templates/terraform/examples/bigquery_connection_sql_with_cmek.tf.tmpl b/mmv1/templates/terraform/examples/bigquery_connection_sql_with_cmek.tf.tmpl new file mode 100644 index 000000000000..0a2045a22af9 --- /dev/null +++ b/mmv1/templates/terraform/examples/bigquery_connection_sql_with_cmek.tf.tmpl @@ -0,0 +1,49 @@ +resource "google_sql_database_instance" "instance" { + name = "{{index $.Vars "database_instance_name"}}" + region = "us-central1" + + database_version = "POSTGRES_11" + settings { + tier = "db-f1-micro" + } + + deletion_protection = {{index $.Vars "deletion_protection"}} +} + +resource "google_sql_database" "db" { + instance = google_sql_database_instance.instance.name + name = "db" +} + +resource "google_sql_user" "user" { + name = "{{index $.Vars "username"}}" + instance = google_sql_database_instance.instance.name + password = "tf-test-my-password%{random_suffix}" +} + +data "google_bigquery_default_service_account" "bq_sa" {} + +resource "google_kms_crypto_key_iam_member" "key_sa_user" { + crypto_key_id = "{{index $.Vars "kms_key_name"}}" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:${data.google_bigquery_default_service_account.bq_sa.email}" +} + +resource "google_bigquery_connection" "{{$.PrimaryResourceId}}" { + friendly_name = "👋" + description = "a riveting description" + location = "US" + kms_key_name = "{{index $.Vars "kms_key_name"}}" + cloud_sql { + instance_id = google_sql_database_instance.instance.connection_name + database = google_sql_database.db.name + type = "POSTGRES" + credential { + username = google_sql_user.user.name + password = google_sql_user.user.password + } + } + + depends_on = [google_kms_crypto_key_iam_member.key_sa_user] +} + diff --git a/mmv1/templates/terraform/examples/bigquery_job_copy.tf.tmpl b/mmv1/templates/terraform/examples/bigquery_job_copy.tf.tmpl index 88b2e20502ca..662b9a64d779 100644 --- a/mmv1/templates/terraform/examples/bigquery_job_copy.tf.tmpl +++ b/mmv1/templates/terraform/examples/bigquery_job_copy.tf.tmpl @@ -3,12 +3,13 @@ locals { } resource "google_bigquery_table" "source" { - deletion_protection = false count = local.count dataset_id = google_bigquery_dataset.source[count.index].dataset_id table_id = "{{index $.Vars "job_id"}}_${count.index}_table" + deletion_protection = false + schema = < google-account.json @@ -173,4 +173,56 @@ fun BuildSteps.saveArtifactsToGCS() { // parts like ${GIT_HASH_SHORT} without having Kotlin syntax issues. For more info see: // https://youtrack.jetbrains.com/issue/KT-2425/Provide-a-way-for-escaping-the-dollar-sign-symbol-in-multiline-strings-and-string-templates }) -} \ No newline at end of file +} + +// The S3 plugin we use to upload artifacts to S3 (enabling them to be accessed via the TeamCity UI later) has a limit of +// 1000 artifacts to be uploaded at a time. To avoid a situation where no artifacts are uploaded as a result of exceeding this +// limit we archive all the debug logs if they equal or exceed 1000 for a given build. +fun BuildSteps.archiveArtifactsIfOverLimit() { + step(ScriptBuildStep { + name = "Tasks after running nightly tests: archive artifacts(debug logs) if there are >=1000 before S3 upload" + scriptContent = """ + #!/bin/bash + echo "Post-test step - archive artifacts(debug logs) if there are >=1000 before S3 upload" + + # Get number of artifacts created + ARTIFACT_COUNT=$(ls %teamcity.build.checkoutDir%/debug* | wc -l | grep -o -E '[0-9]+') + + if test ${'$'}ARTIFACT_COUNT -lt "1000"; then + echo "Found <1000 debug artifacts; we won't archive them before upload to S3" + exit 0 + fi + + echo "Found >=1000 debug artifacts; archiving before upload to S3" + + # Make tarball of all debug logs + # Name should look similar to: debug-google-d2503f7-253644-TerraformProviders_GoogleCloud_GOOGLE_NIGHTLYTESTS_GOOGLE_PACKAGE_ACCESSAPPROVAL.tar.gz + cd %teamcity.build.checkoutDir% + ARCHIVE_NAME=debug-%PROVIDER_NAME%-%env.BUILD_NUMBER%-%system.teamcity.buildType.id%-archive.tar.gz + tar -cf ${'$'}ARCHIVE_NAME ./debug* + + # Fail loudly if archive not made as expected + if [ ! -f ${'$'}ARCHIVE_NAME ]; then + echo "Archive file ${'$'}ARCHIVE_NAME not found!" + + # Allow sanity checking + echo "Listing contents of %teamcity.build.checkoutDir%" + ls + + exit 1 + fi + + # Remove all debug logs. These are all .txt files due to the effects of TF_LOG_PATH_MASK. + rm ./debug*.txt + + # Allow sanity checking + echo "Listing files matching the artifact rule value %teamcity.build.checkoutDir%/debug*" + ls debug* + + echo "Finished" + """.trimIndent() + // ${'$'} is required to allow creating a script in TeamCity that contains + // parts like ${GIT_HASH_SHORT} without having Kotlin syntax issues. For more info see: + // https://youtrack.jetbrains.com/issue/KT-2425/Provide-a-way-for-escaping-the-dollar-sign-symbol-in-multiline-strings-and-string-templates + }) +} diff --git a/mmv1/third_party/terraform/.teamcity/components/constants.kt b/mmv1/third_party/terraform/.teamcity/components/constants.kt index 8125f910b883..a3a97fcc9f33 100644 --- a/mmv1/third_party/terraform/.teamcity/components/constants.kt +++ b/mmv1/third_party/terraform/.teamcity/components/constants.kt @@ -43,3 +43,8 @@ const val ProjectSweeperName = "Project Sweeper" const val NightlyTestsProjectId = "NightlyTests" const val MMUpstreamProjectId = "MMUpstreamTests" const val VcrRecordingProjectId = "VCRRecording" + +// Artifact rules controls which artifacts are uploaded to S3 +// https://www.jetbrains.com/help/teamcity/2024.07/configuring-general-settings.html#Artifact+Paths +// The value below lacks a file extension, to allow upload of individual .txt files or a single .tar.gz file +const val ArtifactRules = "%teamcity.build.checkoutDir%/debug*" \ No newline at end of file diff --git a/mmv1/third_party/terraform/acctest/bootstrap_test_utils.go b/mmv1/third_party/terraform/acctest/bootstrap_test_utils.go.tmpl similarity index 95% rename from mmv1/third_party/terraform/acctest/bootstrap_test_utils.go rename to mmv1/third_party/terraform/acctest/bootstrap_test_utils.go.tmpl index 1c5d4cc04519..7071bf4aed10 100644 --- a/mmv1/third_party/terraform/acctest/bootstrap_test_utils.go +++ b/mmv1/third_party/terraform/acctest/bootstrap_test_utils.go.tmpl @@ -156,7 +156,7 @@ func getOrCreateServiceAccount(config *transport_tpg.Config, project, serviceAcc sa, err := config.NewIamClient(config.UserAgent).Projects.ServiceAccounts.Get(name).Do() if err != nil && !transport_tpg.IsGoogleApiErrorWithCode(err, 404) { - return nil, err + return nil, fmt.Errorf("encountered a non-404 error when looking for bootstrapped service account %s: %w", name, err) } if sa == nil { @@ -171,7 +171,7 @@ func getOrCreateServiceAccount(config *transport_tpg.Config, project, serviceAcc } sa, err = config.NewIamClient(config.UserAgent).Projects.ServiceAccounts.Create("projects/"+project, r).Do() if err != nil { - return nil, err + return nil, fmt.Errorf("error when creating bootstrapped service account %s: %w", name, err) } } @@ -738,10 +738,11 @@ func BootstrapConfig(t *testing.T) *transport_tpg.Config { } config := &transport_tpg.Config{ - Credentials: envvar.GetTestCredsFromEnv(), - Project: envvar.GetTestProjectFromEnv(), - Region: envvar.GetTestRegionFromEnv(), - Zone: envvar.GetTestZoneFromEnv(), + Credentials: envvar.GetTestCredsFromEnv(), + ImpersonateServiceAccount: envvar.GetTestImpersonateServiceAccountFromEnv(), + Project: envvar.GetTestProjectFromEnv(), + Region: envvar.GetTestRegionFromEnv(), + Zone: envvar.GetTestZoneFromEnv(), } transport_tpg.ConfigureBasePaths(config) @@ -1231,13 +1232,13 @@ func SetupProjectsAndGetAccessToken(org, billing, pid, service string, config *t Timeout: 5 * time.Minute, }) if err != nil { - return "", err + return "", fmt.Errorf("error creating 'project-1' with project id %s: %w", pid, err) } // Wait for the operation to complete opAsMap, err := tpgresource.ConvertToMap(op) if err != nil { - return "", err + return "", fmt.Errorf("error in ConvertToMap while creating 'project-1' with project id %s: %w", pid, err) } waitErr := resourcemanager.ResourceManagerOperationWaitTime(config, opAsMap, "creating project", config.UserAgent, 5*time.Minute) @@ -1250,7 +1251,7 @@ func SetupProjectsAndGetAccessToken(org, billing, pid, service string, config *t } _, err = config.NewBillingClient(config.UserAgent).Projects.UpdateBillingInfo(resourcemanager.PrefixedProject(pid), ba).Do() if err != nil { - return "", err + return "", fmt.Errorf("error updating billing info for 'project-1' with project id %s: %w", pid, err) } p2 := fmt.Sprintf("%s-2", pid) @@ -1265,7 +1266,7 @@ func SetupProjectsAndGetAccessToken(org, billing, pid, service string, config *t Timeout: 5 * time.Minute, }) if err != nil { - return "", err + return "", fmt.Errorf("error creating 'project-2' with project id %s: %w", p2, err) } // Wait for the operation to complete @@ -1281,7 +1282,7 @@ func SetupProjectsAndGetAccessToken(org, billing, pid, service string, config *t _, err = config.NewBillingClient(config.UserAgent).Projects.UpdateBillingInfo(resourcemanager.PrefixedProject(p2), ba).Do() if err != nil { - return "", err + return "", fmt.Errorf("error updating billing info for 'project-2' with project id %s: %w", p2, err) } // Enable the appropriate service in project-2 only @@ -1293,14 +1294,14 @@ func SetupProjectsAndGetAccessToken(org, billing, pid, service string, config *t _, err = suService.Services.BatchEnable(fmt.Sprintf("projects/%s", p2), serviceReq).Do() if err != nil { - return "", err + return "", fmt.Errorf("error batch enabling services in 'project-2' with project id %s: %w", p2, err) } // Enable the test runner to create service accounts and get an access token on behalf of // the project 1 service account curEmail, err := transport_tpg.GetCurrentUserEmail(config, config.UserAgent) if err != nil { - return "", err + return "", fmt.Errorf("error getting current user email: %w", err) } proj1SATokenCreator := &cloudresourcemanager.Binding{ @@ -1322,7 +1323,7 @@ func SetupProjectsAndGetAccessToken(org, billing, pid, service string, config *t }, }).Do() if err != nil { - return "", err + return "", fmt.Errorf("error getting IAM policy for 'project-1' with project id %s: %w", pid, err) } p.Bindings = tpgiamresource.MergeBindings(append(p.Bindings, bindings...)) @@ -1332,15 +1333,18 @@ func SetupProjectsAndGetAccessToken(org, billing, pid, service string, config *t UpdateMask: "bindings,etag,auditConfigs", }).Do() if err != nil { - return "", err + return "", fmt.Errorf("error setting IAM policy for 'project-1' with project id %s: %w", pid, err) } // Create a service account for project-1 serviceAccountEmail := serviceAccountPrefix + service sa1, err := getOrCreateServiceAccount(config, pid, serviceAccountEmail) if err != nil { - return "", err + return "", fmt.Errorf("error creating service account %s in 'project-1' with project id %s: %w", serviceAccountEmail, pid, err) } + // Setting IAM policies sometimes fails due to the service account not being created yet + // Wait a minute to ensure we can use it. + time.Sleep(1 * time.Minute) // Add permissions to service accounts @@ -1368,6 +1372,21 @@ func SetupProjectsAndGetAccessToken(org, billing, pid, service string, config *t bindings = tpgiamresource.MergeBindings(append(bindings, proj2CryptoKeyBinding)) } + +{{ if ne $.TargetVersionName `ga` -}} + // For Firebase test only + if service == "firebase" { + // Additional permissions besides roles/serviceusage.serviceUsageConsumer and roles/firebase.admin are needed + // https://firebase.google.com/docs/reference/firebase-management/rest/v1beta1/projects/addFirebase + proj2ServiceUsageBinding := &cloudresourcemanager.Binding{ + Members: []string{fmt.Sprintf("serviceAccount:%s", sa1.Email)}, + Role: "roles/serviceusage.serviceUsageAdmin", + } + + bindings = tpgiamresource.MergeBindings(append(bindings, proj2ServiceUsageBinding)) + } +{{- end }} + p, err = rmService.Projects.GetIamPolicy(p2, &cloudresourcemanager.GetIamPolicyRequest{ Options: &cloudresourcemanager.GetPolicyOptions{ @@ -1375,7 +1394,7 @@ func SetupProjectsAndGetAccessToken(org, billing, pid, service string, config *t }, }).Do() if err != nil { - return "", err + return "", fmt.Errorf("error getting IAM policy for 'project-2' with project id %s: %w", p2, err) } p.Bindings = tpgiamresource.MergeBindings(append(p.Bindings, bindings...)) @@ -1385,7 +1404,7 @@ func SetupProjectsAndGetAccessToken(org, billing, pid, service string, config *t UpdateMask: "bindings,etag,auditConfigs", }).Do() if err != nil { - return "", err + return "", fmt.Errorf("error setting IAM policy for 'project-2' with project id %s: %w", p2, err) } // The token creator IAM API call returns success long before the policy is @@ -1399,7 +1418,7 @@ func SetupProjectsAndGetAccessToken(org, billing, pid, service string, config *t } atResp, err := iamCredsService.Projects.ServiceAccounts.GenerateAccessToken(fmt.Sprintf("projects/-/serviceAccounts/%s", sa1.Email), tokenRequest).Do() if err != nil { - return "", err + return "", fmt.Errorf("error generating access token for service account %s: %w", sa1.Email, err) } accessToken := atResp.AccessToken diff --git a/mmv1/third_party/terraform/envvar/envvar_utils.go b/mmv1/third_party/terraform/envvar/envvar_utils.go index 90a866e4d941..1dc4eb6e2b33 100644 --- a/mmv1/third_party/terraform/envvar/envvar_utils.go +++ b/mmv1/third_party/terraform/envvar/envvar_utils.go @@ -104,6 +104,10 @@ var PapDescriptionEnvVars = []string{ "GOOGLE_PUBLIC_AVERTISED_PREFIX_DESCRIPTION", } +var ImpersonateServiceAccountEnvVars = []string{ + "GOOGLE_IMPERSONATE_SERVICE_ACCOUNT", +} + // AccTestPreCheck ensures at least one of the project env variables is set. func GetTestProjectNumberFromEnv() string { return transport_tpg.MultiEnvSearch(ProjectNumberEnvVars) @@ -138,6 +142,10 @@ func GetTestZoneFromEnv() string { return transport_tpg.MultiEnvSearch(ZoneEnvVars) } +func GetTestImpersonateServiceAccountFromEnv() string { + return transport_tpg.MultiEnvSearch(ImpersonateServiceAccountEnvVars) +} + func GetTestCustIdFromEnv(t *testing.T) string { SkipIfEnvNotSet(t, CustIdEnvVars...) return transport_tpg.MultiEnvSearch(CustIdEnvVars) diff --git a/mmv1/third_party/terraform/fwmodels/provider_model.go.tmpl b/mmv1/third_party/terraform/fwmodels/provider_model.go.tmpl index 252fccb53d54..61bc8d3fe57e 100644 --- a/mmv1/third_party/terraform/fwmodels/provider_model.go.tmpl +++ b/mmv1/third_party/terraform/fwmodels/provider_model.go.tmpl @@ -5,7 +5,9 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -// ProviderModel describes the provider config data model. +// ProviderModel maps provider schema data to a Go type. +// When the plugin-framework provider is configured, the Configure function receives data about +// the provider block in the configuration. That data is used to populate this struct. type ProviderModel struct { Credentials types.String `tfsdk:"credentials"` AccessToken types.String `tfsdk:"access_token"` diff --git a/mmv1/third_party/terraform/fwprovider/data_source_provider_config_plugin_framework.go b/mmv1/third_party/terraform/fwprovider/data_source_provider_config_plugin_framework.go index 98ec9011e6ed..315d2dd093fc 100644 --- a/mmv1/third_party/terraform/fwprovider/data_source_provider_config_plugin_framework.go +++ b/mmv1/third_party/terraform/fwprovider/data_source_provider_config_plugin_framework.go @@ -215,7 +215,7 @@ func (d *GoogleProviderConfigPluginFrameworkDataSource) Read(ctx context.Context data.Scopes = d.providerConfig.Scopes data.UserProjectOverride = d.providerConfig.UserProjectOverride data.RequestReason = d.providerConfig.RequestReason - // TODO(SarahFrench) - request_timeout + data.RequestTimeout = d.providerConfig.RequestTimeout data.DefaultLabels = d.providerConfig.DefaultLabels data.AddTerraformAttributionLabel = d.providerConfig.AddTerraformAttributionLabel data.TerraformAttributionLabelAdditionStrategy = d.providerConfig.TerraformAttributionLabelAdditionStrategy diff --git a/mmv1/third_party/terraform/fwprovider/framework_provider.go.tmpl b/mmv1/third_party/terraform/fwprovider/framework_provider.go.tmpl index 470a492f3c84..60e7cc1ac23e 100644 --- a/mmv1/third_party/terraform/fwprovider/framework_provider.go.tmpl +++ b/mmv1/third_party/terraform/fwprovider/framework_provider.go.tmpl @@ -28,6 +28,7 @@ import ( // Ensure the implementation satisfies the expected interfaces var ( + _ provider.Provider = &FrameworkProvider{} _ provider.ProviderWithMetaSchema = &FrameworkProvider{} _ provider.ProviderWithFunctions = &FrameworkProvider{} ) @@ -45,7 +46,9 @@ type FrameworkProvider struct { Version string } -// Metadata returns the provider type name. +// Metadata returns +// - the provider type name : this controls how "google" is present at the start of all resource type names +// - the provider version : this is currently unused by Terraform core func (p *FrameworkProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { resp.TypeName = "google" resp.Version = p.Version @@ -243,7 +246,10 @@ func (p *FrameworkProvider) Schema(_ context.Context, _ provider.SchemaRequest, transport_tpg.ConfigureDCLCustomEndpointAttributesFramework(&resp.Schema) } -// Configure prepares an API client for data sources and resources. +// Configure prepares the metadata/'meta' required for data sources and resources to function. +// Configuration logic implemented here should take user inputs and use them to populate a struct +// with that necessary metadata, e.g. default project value, configured client, etc. +// That prepared 'meta' struct is then returned in the response, and that value will later be supplied to all resources/data sources when they need to configure themselves. func (p *FrameworkProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { var data fwmodels.ProviderModel diff --git a/mmv1/third_party/terraform/fwprovider/framework_provider_access_token_test.go.tmpl b/mmv1/third_party/terraform/fwprovider/framework_provider_access_token_test.go.tmpl index c85685f99654..ccc304e8cad1 100644 --- a/mmv1/third_party/terraform/fwprovider/framework_provider_access_token_test.go.tmpl +++ b/mmv1/third_party/terraform/fwprovider/framework_provider_access_token_test.go.tmpl @@ -267,7 +267,7 @@ func testAccFwProvider_access_token_authInUse(t *testing.T) { {{- end }} // testAccFwProvider_access_tokenInProviderBlock allows setting the access_token argument in a provider block. -// This function uses data.google_provider_config_plugin_framework because it is implemented with the SDKv2 +// This function uses data.google_provider_config_plugin_framework because it is implemented with the PF func testAccFwProvider_access_tokenInProviderBlock(context map[string]interface{}) string { return acctest.Nprintf(` provider "google" { diff --git a/mmv1/third_party/terraform/fwprovider/framework_provider_billing_project_test.go.tmpl b/mmv1/third_party/terraform/fwprovider/framework_provider_billing_project_test.go.tmpl new file mode 100644 index 000000000000..37fcb7137fa7 --- /dev/null +++ b/mmv1/third_party/terraform/fwprovider/framework_provider_billing_project_test.go.tmpl @@ -0,0 +1,297 @@ +package fwprovider_test + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +{{- if ne $.TargetVersionName "ga" }} + "github.com/hashicorp/terraform-provider-google/google/envvar" +{{- end }} +) + +// TestAccFwProvider_billing_project is a series of acc tests asserting how the PF provider handles billing_project arguments +// It is PF specific because the HCL used provisions PF-implemented resources +// It is a counterpart to TestAccSdkProvider_billing_project +func TestAccFwProvider_billing_project(t *testing.T) { + testCases := map[string]func(t *testing.T){ + // Configuring the provider using inputs + "config takes precedence over environment variables": testAccFwProvider_billing_project_configPrecedenceOverEnvironmentVariables, + "when billing_project is unset in the config, environment variables are used in a given order": testAccFwProvider_billing_project_precedenceOrderEnvironmentVariables, // GOOGLE_BILLING_PROJECT + + // Schema-level validation + "when billing_project is set to an empty string in the config the value isn't ignored and results in an error": testAccFwProvider_billing_project_emptyStringValidation, + + // Usage +{{- if ne $.TargetVersionName "ga" }} + // TODO: https://github.com/hashicorp/terraform-provider-google/issues/17882 + "GOOGLE_CLOUD_QUOTA_PROJECT environment variable interferes with the billing_account value used": testAccFwProvider_billing_project_affectedByClientLibraryEnv, + // 1) Usage of billing_account alone is insufficient + // 2) Usage in combination with user_project_override changes the project where quota is used + "using billing_account alone doesn't impact provisioning, but using together with user_project_override does": testAccFwProvider_billing_project_useWithAndWithoutUserProjectOverride, +{{- else }} + // Usage test cases are implemented in a Beta-only way +{{- end }} + } + + for name, tc := range testCases { + // shadow the tc variable into scope so that when + // the loop continues, if t.Run hasn't executed tc(t) + // yet, we don't have a race condition + // see https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +func testAccFwProvider_billing_project_configPrecedenceOverEnvironmentVariables(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + billingProject := "my-billing-project-id" + + // ensure all possible billing_project env vars set; show they aren't used instead + t.Setenv("GOOGLE_BILLING_PROJECT", billingProject) + + providerBillingProject := "foobar" + + context := map[string]interface{}{ + "billing_project": providerBillingProject, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccFwProvider_billing_project_inProviderBlock(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "billing_project", providerBillingProject), + )}, + }, + }) +} + +func testAccFwProvider_billing_project_precedenceOrderEnvironmentVariables(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + /* + These are all the ENVs for billing_project + GOOGLE_BILLING_PROJECT + + GOOGLE_CLOUD_QUOTA_PROJECT - NOT used by provider, but is in client libraries we use + */ + + GOOGLE_BILLING_PROJECT := "GOOGLE_BILLING_PROJECT" + GOOGLE_CLOUD_QUOTA_PROJECT := "GOOGLE_CLOUD_QUOTA_PROJECT" + + context := map[string]interface{}{} + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // GOOGLE_BILLING_PROJECT is used if set + PreConfig: func() { + t.Setenv("GOOGLE_BILLING_PROJECT", GOOGLE_BILLING_PROJECT) //used + t.Setenv("GOOGLE_CLOUD_QUOTA_PROJECT", GOOGLE_CLOUD_QUOTA_PROJECT) + }, + Config: testAccFwProvider_billing_project_inEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "billing_project", GOOGLE_BILLING_PROJECT), + ), + }, + { + // GOOGLE_CLOUD_QUOTA_PROJECT is NOT used here + PreConfig: func() { + t.Setenv("GOOGLE_BILLING_PROJECT", "") + t.Setenv("GOOGLE_CLOUD_QUOTA_PROJECT", GOOGLE_CLOUD_QUOTA_PROJECT) // NOT used + }, + Config: testAccFwProvider_billing_project_inEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckNoResourceAttr("data.google_provider_config_plugin_framework.default", "billing_project"), + ), + }, + }, + }) +} + +func testAccFwProvider_billing_project_emptyStringValidation(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + billingProject := "my-billing-project-id" + + // ensure all billing_project env vars set + t.Setenv("GOOGLE_BILLING_PROJECT", billingProject) + + context := map[string]interface{}{ + "billing_project": "", // empty string used + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccFwProvider_billing_project_inProviderBlock(context), + PlanOnly: true, + ExpectError: regexp.MustCompile("expected a non-empty string"), + }, + }, + }) +} + +{{- if ne $.TargetVersionName "ga" }} +func testAccFwProvider_billing_project_useWithAndWithoutUserProjectOverride(t *testing.T) { + acctest.SkipIfVcr(t) // VCR fails to record final interaction that fails with 403 + + randomString := acctest.RandString(t, 10) + contextUserProjectOverrideFalse := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "billing_account": envvar.GetTestBillingAccountFromEnv(t), + "user_project_override": "false", // Used in combo with billing_account + "random_suffix": randomString, + } + + contextUserProjectOverrideTrue := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "billing_account": envvar.GetTestBillingAccountFromEnv(t), + "user_project_override": "true", // Used in combo with billing_account + "random_suffix": randomString, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + Steps: []resource.TestStep{ + { + // Setup resources + // Neither user_project_override nor billing_project value used here + Config: testAccFwProvider_billing_project_useBillingProject_setup(contextUserProjectOverrideFalse), + }, + { + // With user_project_override=true the Firebase data source CANNOT be used because quota is consumed + // from the newly provisioned project, and that project does not have the Firebase Management API enabled. + // The billing_project is used, leading to the error occurring, because user_project_override=true + Config: testAccFwProvider_billing_project_useBillingProject_scenario(contextUserProjectOverrideTrue), + ExpectError: regexp.MustCompile("Error 403: Firebase Management API has not been used in project"), + }, + { + // With user_project_override=false the Firebase data source still cannot be used, but only because we're querying a made up app! + // This is necessary because we cannot provision anything in the newly provisioned project (to be queried with the datasource) + // unless we make it a Firebase-enabled project. This enables the API, and that undermines the premise of this test. + Config: testAccFwProvider_billing_project_useBillingProject_scenario(contextUserProjectOverrideFalse), + ExpectError: regexp.MustCompile("Error 403: The caller does not have permission, forbidden"), + }, + }, + }) +} + +func testAccFwProvider_billing_project_affectedByClientLibraryEnv(t *testing.T) { + acctest.SkipIfVcr(t) // VCR fails to record final interaction that fails with 403 + + randomString := acctest.RandString(t, 10) + context := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "billing_account": envvar.GetTestBillingAccountFromEnv(t), + "user_project_override": "true", // Used in combo with billing_account + "random_suffix": randomString, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + Steps: []resource.TestStep{ + { + // Setup resources + // Neither user_project_override nor billing_project value used here + Config: testAccFwProvider_billing_project_useBillingProject_setup(context), + }, + { + // This ENV interferes with setting the billing_project, + // so we get an error mentioning the value + PreConfig: func() { + t.Setenv("GOOGLE_CLOUD_QUOTA_PROJECT", "foobar") + }, + Config: testAccFwProvider_billing_project_useBillingProject_scenario(context), + ExpectError: regexp.MustCompile("foobar"), + }, + { + // The same config without that ENV present applies without error + PreConfig: func() { + t.Setenv("GOOGLE_CLOUD_QUOTA_PROJECT", "") + }, + Config: testAccFwProvider_billing_project_useBillingProject_scenario(context), + // We know we're using the billing_account project when we hit this error + ExpectError: regexp.MustCompile("Error 403: Firebase Management API has not been used in project"), + }, + }, + }) +} +{{- end }} + +// testAccFwProvider_billing_project_inProviderBlock allows setting the billing_project argument in a provider block. +// This function uses data.google_provider_config_plugin_framework because it is implemented with the plugin-framework +func testAccFwProvider_billing_project_inProviderBlock(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + billing_project = "%{billing_project}" +} + +data "google_provider_config_plugin_framework" "default" {} +`, context) +} + +// testAccFwProvider_billing_project_inEnvsOnly allows testing when the billing_project argument +// is only supplied via ENVs +func testAccFwProvider_billing_project_inEnvsOnly(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_provider_config_plugin_framework" "default" {} +`, context) +} + +{{- if ne $.TargetVersionName "ga" }} + +func testAccFwProvider_billing_project_useBillingProject_setup(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google-beta" {} + +# Create a new project and enable service APIs in those projects +resource "google_project" "project" { + provider = google-beta + project_id = "tf-test-%{random_suffix}" + name = "tf-test-%{random_suffix}" + org_id = "%{org_id}" + billing_account = "%{billing_account}" + deletion_policy = "DELETE" +} +`, context) +} + +func testAccFwProvider_billing_project_useBillingProject_scenario(context map[string]interface{}) string { + + // SECOND APPLY + // This is needed as configuring the provider depends on resources provisioned in the setup step + return testAccFwProvider_billing_project_useBillingProject_setup(context) + acctest.Nprintf(` +# Set up the usage of +# - user_project_override +# - billing_project +provider "google-beta" { + alias = "user_project_override" + user_project_override = %{user_project_override} + billing_project = google_project.project.project_id + project = google_project.project.project_id +} + +# See if the impersonated SA can interact with the Firebase API in a way that uses +# the newly provisioned project as the source of consumed quota +# NOTE: This data source is implemented with plugin-framework so tests our use of user_project_override + billing_project in a PF specific way. +data "google_firebase_apple_app_config" "my_app_config" { + provider = google-beta.user_project_override + app_id = "1:234567891011:ios:1234abcdef1234abcde123" // Made up +} +`, context) +} +{{- end }} \ No newline at end of file diff --git a/mmv1/third_party/terraform/fwprovider/framework_provider_request_timeout_test.go b/mmv1/third_party/terraform/fwprovider/framework_provider_request_timeout_test.go new file mode 100644 index 000000000000..aa22b6a496f5 --- /dev/null +++ b/mmv1/third_party/terraform/fwprovider/framework_provider_request_timeout_test.go @@ -0,0 +1,132 @@ +package fwprovider_test + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +// TestAccFwProvider_request_timeout is a series of acc tests asserting how the PF provider handles request_timeout arguments +// It is PF specific because the HCL used provisions PF-implemented resources +// It is a counterpart to TestAccSdkProvider_request_timeout +func TestAccFwProvider_request_timeout(t *testing.T) { + testCases := map[string]func(t *testing.T){ + // Configuring the provider using inputs + "a default value of 120s is used when there are no user inputs": testAccFwProvider_request_timeout_providerDefault, + "request_timeout can be set in config in different formats, are NOT normalized to full-length format": testAccFwProvider_request_timeout_setInConfig, + //no ENVs to test + + // Schema-level validation + "when request_timeout is set to an empty string in the config the value fails validation, as it is not a duration": testAccFwProvider_request_timeout_emptyStringValidation, + + // Usage + // We cannot test the impact of this field in an acc test until more resources/data sources are implemented with the plugin-framework + } + + for name, tc := range testCases { + // shadow the tc variable into scope so that when + // the loop continues, if t.Run hasn't executed tc(t) + // yet, we don't have a race condition + // see https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +func testAccFwProvider_request_timeout_providerDefault(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + defaultValue := "120s" + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccFwProvider_request_timeout_unset(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "request_timeout", defaultValue), + ), + }, + }, + }) +} + +func testAccFwProvider_request_timeout_setInConfig(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + providerTimeout1 := "3m0s" + providerTimeout2 := "3m" + + // In the SDK version of the test expectedValue = "3m0s" only + expectedValue1 := "3m0s" + expectedValue2 := "3m" + + context1 := map[string]interface{}{ + "request_timeout": providerTimeout1, + } + context2 := map[string]interface{}{ + "request_timeout": providerTimeout2, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccFwProvider_request_timeout_inProviderBlock(context1), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "request_timeout", expectedValue1), + ), + }, + { + Config: testAccFwProvider_request_timeout_inProviderBlock(context2), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "request_timeout", expectedValue2), + ), + }, + }, + }) +} + +func testAccFwProvider_request_timeout_emptyStringValidation(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + context := map[string]interface{}{ + "request_timeout": "", // empty string used + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccFwProvider_request_timeout_inProviderBlock(context), + ExpectError: regexp.MustCompile("invalid duration"), + }, + }, + }) +} + +// testAccFwProvider_request_timeout_inProviderBlock allows setting the request_timeout argument in a provider block. +// This function uses data.google_provider_config_plugin_framework because it is implemented with the PF +func testAccFwProvider_request_timeout_inProviderBlock(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + request_timeout = "%{request_timeout}" +} + +data "google_provider_config_plugin_framework" "default" {} +`, context) +} + +// testAccFwProvider_request_timeout_inEnvsOnly allows testing when the request_timeout argument is not set +func testAccFwProvider_request_timeout_unset() string { + return ` +data "google_provider_config_plugin_framework" "default" {} +` +} diff --git a/mmv1/third_party/terraform/fwprovider/framework_provider_user_project_override_test.go.tmpl b/mmv1/third_party/terraform/fwprovider/framework_provider_user_project_override_test.go.tmpl new file mode 100644 index 000000000000..5576186241a8 --- /dev/null +++ b/mmv1/third_party/terraform/fwprovider/framework_provider_user_project_override_test.go.tmpl @@ -0,0 +1,334 @@ +package fwprovider_test + +import ( + "fmt" +{{ if ne $.TargetVersionName `ga` -}} + "regexp" +{{- end }} + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +{{ if ne $.TargetVersionName `ga` -}} + "github.com/hashicorp/terraform-provider-google/google/envvar" +{{- end }} +) + +// TestAccFwProvider_user_project_override is a series of acc tests asserting how the plugin-framework provider handles credentials arguments +// It is PF specific because the HCL used uses a PF-implemented data source +// It is a counterpart to TestAccSdkProvider_user_project_override +func TestAccFwProvider_user_project_override(t *testing.T) { + testCases := map[string]func(t *testing.T){ + // Configuring the provider using inputs + "config takes precedence over environment variables": testAccFwProvider_user_project_override_configPrecedenceOverEnvironmentVariables, + "when user_project_override is unset in the config, environment variables are used": testAccFwProvider_user_project_override_precedenceOrderEnvironmentVariables, + + // Schema-level validation + "when user_project_override is set in the config the value can be a boolean (true/false) or a string (true/false/1/0)": testAccFwProvider_user_project_override_booleansInConfigOnly, + "when user_project_override is set via environment variables any of these values can be used: true/false/1/0": testAccFwProvider_user_project_override_envStringsAccepted, + + // Usage +{{ if ne $.TargetVersionName `ga` -}} + "user_project_override uses a resource's project argument to control which project is used for quota and billing purposes": testAccFwProvider_UserProjectOverride, + // We cannot currently implement this test case in a PF-specific way: "user_project_override works for resources that don't take a project argument (provider-level default project value is used)" +{{- else }} + // Usage tests cases are currently implemented in a Beta-only way +{{- end }} + } + + for name, tc := range testCases { + // shadow the tc variable into scope so that when + // the loop continues, if t.Run hasn't executed tc(t) + // yet, we don't have a race condition + // see https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +func testAccFwProvider_user_project_override_configPrecedenceOverEnvironmentVariables(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + override := "true" + providerOverride := false + + // ensure all possible region env vars set; show they aren't used + t.Setenv("USER_PROJECT_OVERRIDE", override) + + context := map[string]interface{}{ + "user_project_override": providerOverride, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Apply-time error; bad value in config is used over of good values in ENVs + Config: testAccFwProvider_user_project_overrideInProviderBlock_boolean(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "user_project_override", fmt.Sprintf("%v", providerOverride)), + ), + }, + }, + }) +} + +func testAccFwProvider_user_project_override_precedenceOrderEnvironmentVariables(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + /* + These are all the ENVs for region, and they are in order of precedence. + USER_PROJECT_OVERRIDE + */ + + context := map[string]interface{}{} + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "") // unset + }, + Config: testAccFwProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckNoResourceAttr("data.google_provider_config_plugin_framework.default", "user_project_override"), + ), + }, + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "true") + }, + Config: testAccFwProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "user_project_override", "true"), + ), + }, + }, + }) +} + +func testAccFwProvider_user_project_override_booleansInConfigOnly(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + context_true := map[string]interface{}{ + "user_project_override": true, + } + context_false := map[string]interface{}{ + "user_project_override": false, + } + + context_1 := map[string]interface{}{ + "user_project_override": "1", + } + context_0 := map[string]interface{}{ + "user_project_override": "0", + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccFwProvider_user_project_overrideInProviderBlock_boolean(context_true), + // No error expected + }, + { + Config: testAccFwProvider_user_project_overrideInProviderBlock_boolean(context_false), + // No error expected + }, + { + Config: testAccFwProvider_user_project_overrideInProviderBlock_string(context_true), + // No error expected + }, + { + Config: testAccFwProvider_user_project_overrideInProviderBlock_string(context_false), + // No error expected + }, + { + Config: testAccFwProvider_user_project_overrideInProviderBlock_string(context_1), + // No error expected + }, + { + Config: testAccFwProvider_user_project_overrideInProviderBlock_string(context_0), + // No error expected + }, + }, + }) +} + +func testAccFwProvider_user_project_override_envStringsAccepted(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + context := map[string]interface{}{} + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "true") + }, + Config: testAccFwProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "user_project_override", "true"), + ), + }, + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "1") + }, + Config: testAccFwProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "user_project_override", "true"), + ), + }, + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "false") + }, + Config: testAccFwProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "user_project_override", "false"), + ), + }, + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "0") + }, + Config: testAccFwProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_plugin_framework.default", "user_project_override", "false"), + ), + }, + }, + }) +} + +// testAccFwProvider_user_project_overrideInProviderBlock allows setting the user_project_override argument in a provider block. +// This function uses data.google_provider_config_plugin_framework because it is implemented with the plugin-framework +func testAccFwProvider_user_project_overrideInProviderBlock_boolean(context map[string]interface{}) string { + v := acctest.Nprintf(` +provider "google" { + user_project_override = %{user_project_override} +} + +data "google_provider_config_plugin_framework" "default" {} +`, context) + return v +} + +func testAccFwProvider_user_project_overrideInProviderBlock_string(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + user_project_override = "%{user_project_override}" +} + +data "google_provider_config_plugin_framework" "default" {} +`, context) +} + +// testAccFwProvider_user_project_overrideInEnvsOnly allows testing when the user_project_override argument +// is only supplied via ENVs +func testAccFwProvider_user_project_overrideInEnvsOnly(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_provider_config_plugin_framework" "default" {} +`, context) +} + +{{ if ne $.TargetVersionName `ga` -}} +func testAccFwProvider_UserProjectOverride(t *testing.T) { + // Parallel fine-grained resource creation + acctest.SkipIfVcr(t) + t.Parallel() + + org := envvar.GetTestOrgFromEnv(t) + billing := envvar.GetTestBillingAccountFromEnv(t) + pid := "tf-test-" + acctest.RandString(t, 10) + + config := acctest.BootstrapConfig(t) + accessToken, err := acctest.SetupProjectsAndGetAccessToken(org, billing, pid, "firebase", config) + if err != nil || accessToken == "" { + if err == nil { + t.Fatal("error when setting up projects and retrieving access token: access token is an empty string") + } + t.Fatalf("error when setting up projects and retrieving access token: %s", err) + } + + context := map[string]interface{}{ + "user_project_override": false, // changed in config functions + "access_token": accessToken, + "project_2": pid + "-2", // acctest.SetupProjectsAndGetAccessToken creates project 1 with id = `pid`, and 2 = `pid` + "-2" + "random_suffix": acctest.RandString(t, 5), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + // No TestDestroy since that's not really the point of this test + Steps: []resource.TestStep{ + { + Config: testAccFwProvider_UserProjectOverride_step2(context, false), + ExpectError: regexp.MustCompile("Firebase Management API has not been used"), + }, + { + Config: testAccFwProvider_UserProjectOverride_step2(context, true), + Check: resource.ComposeTestCheckFunc( + // Firebase resources and data sources are linked to project-2, where the API is enabled + resource.TestCheckResourceAttr("google_firebase_apple_app.project_2_app", "project", context["project_2"].(string)), + resource.TestCheckResourceAttr("data.google_firebase_apple_app_config.project_2_app", "project", context["project_2"].(string)), + ), + }, + { + Config: testAccFwProvider_UserProjectOverride_step3(context, true), + }, + }, + }) +} + +func testAccFwProvider_UserProjectOverride_step2(context map[string]interface{}, override bool) string { + return testAccFwProvider_UserProjectOverride_step3(context, override) + + acctest.Nprintf(` +// See step 3 appended above, which is really step 2 minus the pubsub topic. +// Step 3 exists because provider configurations can't be removed while objects +// created by that provider still exist in state. Step 3 will remove the +// pubsub topic so the whole config can be deleted. + +resource "google_firebase_project" "default" { + provider = google.project-1-token + project = "%{project_2}" +} + +resource "google_firebase_apple_app" "project_2_app" { + provider = google.project-1-token + project = google_firebase_project.default.project // add dependency, also uses project 2 + bundle_id = "apple.app.%{random_suffix}" + display_name = "tf-test Display Name AppleAppConfig DataSource" + app_store_id = "12345" + team_id = "1234567890" +} + +// This is implemented with plugin-framework so tests our use of user_project_override in a PF specific way +data "google_firebase_apple_app_config" "project_2_app" { + project = google_firebase_apple_app.project_2_app.project + app_id = google_firebase_apple_app.project_2_app.app_id +} +`, context) +} + +func testAccFwProvider_UserProjectOverride_step3(context map[string]interface{}, override bool) string { + context["user_project_override"] = override + + return acctest.Nprintf(` +provider "google" { + alias = "project-1-token" + access_token = "%{access_token}" + user_project_override = "%{user_project_override}" +} +`, context) +} +{{- end }} \ No newline at end of file diff --git a/mmv1/third_party/terraform/fwtransport/framework_config.go.tmpl b/mmv1/third_party/terraform/fwtransport/framework_config.go.tmpl index 9107259edd08..25e715009913 100644 --- a/mmv1/third_party/terraform/fwtransport/framework_config.go.tmpl +++ b/mmv1/third_party/terraform/fwtransport/framework_config.go.tmpl @@ -38,7 +38,8 @@ type FrameworkProviderConfig struct { AccessToken types.String ImpersonateServiceAccount types.String ImpersonateServiceAccountDelegates types.List - RequestReason types.String + RequestReason types.String + RequestTimeout types.String AddTerraformAttributionLabel types.Bool TerraformAttributionLabelAdditionStrategy types.String // End temporary @@ -113,6 +114,7 @@ func (p *FrameworkProviderConfig) LoadAndValidateFramework(ctx context.Context, p.ImpersonateServiceAccount = data.ImpersonateServiceAccount p.ImpersonateServiceAccountDelegates = data.ImpersonateServiceAccountDelegates p.RequestReason = data.RequestReason + p.RequestTimeout = data.RequestTimeout p.AddTerraformAttributionLabel = data.AddTerraformAttributionLabel p.TerraformAttributionLabelAdditionStrategy = data.TerraformAttributionLabelAdditionStrategy // End temporary diff --git a/mmv1/third_party/terraform/go.mod b/mmv1/third_party/terraform/go.mod index 6f4adfafb706..a1f5c67209c5 100644 --- a/mmv1/third_party/terraform/go.mod +++ b/mmv1/third_party/terraform/go.mod @@ -3,8 +3,8 @@ module github.com/hashicorp/terraform-provider-google go 1.21 require ( - cloud.google.com/go/bigtable v1.30.0 - github.com/GoogleCloudPlatform/declarative-resource-client-library v1.74.0 + cloud.google.com/go/bigtable v1.33.0 + github.com/GoogleCloudPlatform/declarative-resource-client-library v1.75.0 github.com/apparentlymart/go-cidr v1.1.0 github.com/davecgh/go-spew v1.1.1 github.com/dnaeon/go-vcr v1.0.1 @@ -30,24 +30,24 @@ require ( github.com/stretchr/testify v1.9.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 - golang.org/x/net v0.28.0 - golang.org/x/oauth2 v0.22.0 - google.golang.org/api v0.193.0 - google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 - google.golang.org/grpc v1.65.0 - google.golang.org/protobuf v1.34.2 + golang.org/x/net v0.30.0 + golang.org/x/oauth2 v0.23.0 + google.golang.org/api v0.201.0 + google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 + google.golang.org/grpc v1.67.1 + google.golang.org/protobuf v1.35.1 ) require ( bitbucket.org/creachadair/stringset v0.0.8 // indirect - cel.dev/expr v0.15.0 // indirect - cloud.google.com/go v0.115.1 // indirect - cloud.google.com/go/auth v0.9.0 // indirect + cel.dev/expr v0.16.0 // indirect + cloud.google.com/go v0.116.0 // indirect + cloud.google.com/go/auth v0.9.8 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect - cloud.google.com/go/compute/metadata v0.5.0 // indirect - cloud.google.com/go/iam v1.1.13 // indirect - cloud.google.com/go/longrunning v0.5.12 // indirect - cloud.google.com/go/monitoring v1.20.4 // indirect + cloud.google.com/go/compute/metadata v0.5.2 // indirect + cloud.google.com/go/iam v1.2.1 // indirect + cloud.google.com/go/longrunning v0.6.1 // indirect + cloud.google.com/go/monitoring v1.21.1 // indirect github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect github.com/agext/levenshtein v1.2.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect @@ -55,21 +55,21 @@ require ( github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect - github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect - github.com/envoyproxy/go-control-plane v0.12.0 // indirect - github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect + github.com/cncf/xds/go v0.0.0-20240822171458-6449f94b4d59 // indirect + github.com/envoyproxy/go-control-plane v0.13.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gammazero/deque v0.0.0-20180920172122-f6adf94963e4 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/golang/glog v1.2.1 // indirect + github.com/golang/glog v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cpy v0.0.0-20211218193943-a9c933c06932 // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect @@ -91,6 +91,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/run v1.0.0 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect @@ -98,23 +99,23 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.14.4 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/sdk v1.28.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect - golang.org/x/crypto v0.26.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/sdk v1.29.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect + golang.org/x/crypto v0.28.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect - golang.org/x/time v0.6.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/mmv1/third_party/terraform/go.sum b/mmv1/third_party/terraform/go.sum index 8502771a0734..935dcda00748 100644 --- a/mmv1/third_party/terraform/go.sum +++ b/mmv1/third_party/terraform/go.sum @@ -1,29 +1,41 @@ bitbucket.org/creachadair/stringset v0.0.8 h1:gQqe4vs8XWgMyijfyKE6K8o4TcyGGrRXe0JvHgx5H+M= bitbucket.org/creachadair/stringset v0.0.8/go.mod h1:AgthVMyMxC/6FK1KBJ2ALdqkZObGN8hOetgpwXyMn34= -cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w= -cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= +cel.dev/expr v0.16.0 h1:yloc84fytn4zmJX2GU3TkXGsaieaV7dQ057Qs4sIG2Y= +cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= +cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= +cloud.google.com/go/auth v0.9.8 h1:+CSJ0Gw9iVeSENVCKJoLHhdUykDgXSc4Qn+gu2BRtR8= +cloud.google.com/go/auth v0.9.8/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= -cloud.google.com/go/auth v0.9.0 h1:cYhKl1JUhynmxjXfrk4qdPc6Amw7i+GC9VLflgT0p5M= -cloud.google.com/go/auth v0.9.0/go.mod h1:2HsApZBr9zGZhC9QAXsYVYaWk8kNUt37uny+XVKi7wM= +cloud.google.com/go/auth v0.9.5 h1:4CTn43Eynw40aFVr3GpPqsQponx2jv0BQpjvajsbbzw= +cloud.google.com/go/auth v0.9.5/go.mod h1:Xo0n7n66eHyOWWCnitop6870Ilwo3PiZyodVkkH1xWM= cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= -cloud.google.com/go/bigtable v1.30.0 h1:w+N3/WcCDVuKAMvBCD734795ElyjRVaOgOihBRvnWPM= -cloud.google.com/go/bigtable v1.30.0/go.mod h1:VVl6B9pDrmTmSP5KD65KU/tWk3aCHksaNnVt471BN2o= -cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= -cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= -cloud.google.com/go/iam v1.1.13 h1:7zWBXG9ERbMLrzQBRhFliAV+kjcRToDTgQT3CTwYyv4= -cloud.google.com/go/iam v1.1.13/go.mod h1:K8mY0uSXwEXS30KrnVb+j54LB/ntfZu1dr+4zFMNbus= -cloud.google.com/go/longrunning v0.5.12 h1:5LqSIdERr71CqfUsFlJdBpOkBH8FBCFD7P1nTWy3TYE= -cloud.google.com/go/longrunning v0.5.12/go.mod h1:S5hMV8CDJ6r50t2ubVJSKQVv5u0rmik5//KgLO3k4lU= -cloud.google.com/go/monitoring v1.20.4 h1:zwcViK7mT9SV0kzKqLOI3spRadvsmvw/R9z1MHNeC0E= -cloud.google.com/go/monitoring v1.20.4/go.mod h1:v7F/UcLRw15EX7xq565N7Ae5tnYEE28+Cl717aTXG4c= +cloud.google.com/go/bigtable v1.33.0 h1:2BDaWLRAwXO14DJL/u8crbV2oUbMZkIa2eGq8Yao1bk= +cloud.google.com/go/bigtable v1.33.0/go.mod h1:HtpnH4g25VT1pejHRtInlFPnN5sjTxbQlsYBjh9t5l0= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= +cloud.google.com/go/iam v1.2.1 h1:QFct02HRb7H12J/3utj0qf5tobFh9V4vR6h9eX5EBRU= +cloud.google.com/go/iam v1.2.1/go.mod h1:3VUIJDPpwT6p/amXRC5GY8fCCh70lxPygguVtI0Z4/g= +cloud.google.com/go/longrunning v0.6.1 h1:lOLTFxYpr8hcRtcwWir5ITh1PAKUD/sG2lKrTSYjyMc= +cloud.google.com/go/longrunning v0.6.1/go.mod h1:nHISoOZpBcmlwbJmiVk5oDRz0qG/ZxPynEGs1iZ79s0= +cloud.google.com/go/monitoring v1.21.1 h1:zWtbIoBMnU5LP9A/fz8LmWMGHpk4skdfeiaa66QdFGc= +cloud.google.com/go/monitoring v1.21.1/go.mod h1:Rj++LKrlht9uBi8+Eb530dIrzG/cU/lB8mt+lbeFK1c= +cloud.google.com/go/bigtable v1.31.0 h1:/uVLxGVRbK4mxK/iO89VqXcL/zoTSmkltVfIDYVBluQ= +cloud.google.com/go/bigtable v1.31.0/go.mod h1:N/mwZO+4TSHOeyiE1JxO+sRPnW4bnR7WLn9AEaiJqew= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= +cloud.google.com/go/iam v1.2.0 h1:kZKMKVNk/IsSSc/udOb83K0hL/Yh/Gcqpz+oAkoIFN8= +cloud.google.com/go/iam v1.2.0/go.mod h1:zITGuWgsLZxd8OwAlX+eMFgZDXzBm7icj1PVTYG766Q= +cloud.google.com/go/longrunning v0.6.0 h1:mM1ZmaNsQsnb+5n1DNPeL0KwQd9jQRqSqSDEkBZr+aI= +cloud.google.com/go/longrunning v0.6.0/go.mod h1:uHzSZqW89h7/pasCWNYdUpwGz3PcVWhrWupreVPYLts= +cloud.google.com/go/monitoring v1.21.0 h1:EMc0tB+d3lUewT2NzKC/hr8cSR9WsUieVywzIHetGro= +cloud.google.com/go/monitoring v1.21.0/go.mod h1:tuJ+KNDdJbetSsbSGTqnaBvbauS5kr3Q/koy3Up6r+4= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/GoogleCloudPlatform/declarative-resource-client-library v1.72.0 h1:VodSRLhOrb8hhRbPre275EreP4vTiaejdBcvd2MCtX4= -github.com/GoogleCloudPlatform/declarative-resource-client-library v1.72.0/go.mod h1:pL2Qt5HT+x6xrTd806oMiM3awW6kNIXB/iiuClz6m6k= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= @@ -48,8 +60,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= -github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20240822171458-6449f94b4d59 h1:fLZ97KE86ELjEYJCEUVzmbhfzDxHHGwBrDVMd4XL6Bs= +github.com/cncf/xds/go v0.0.0-20240822171458-6449f94b4d59/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/creachadair/staticfile v0.1.2/go.mod h1:a3qySzCIXEprDGxk6tSxSI+dBBdLzqeBOMhZ+o2d3pM= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -63,11 +75,11 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= +github.com/envoyproxy/go-control-plane v0.13.0 h1:HzkeUz1Knt+3bK+8LG1bxOO/jzWZmdxpwC51i202les= +github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= @@ -96,8 +108,8 @@ github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= +github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -117,8 +129,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= -github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -135,8 +147,8 @@ github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= @@ -238,6 +250,8 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -280,20 +294,20 @@ github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY3 github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= -go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= +go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= +go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -303,8 +317,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc= golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= @@ -327,11 +343,13 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -357,19 +375,27 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -387,8 +413,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.193.0 h1:eOGDoJFsLU+HpCBaDJex2fWiYujAw9KbXgpOAMePoUs= -google.golang.org/api v0.193.0/go.mod h1:Po3YMV1XZx+mTku3cfJrlIYR03wiGrCOsdpC67hjZvw= +google.golang.org/api v0.201.0 h1:+7AD9JNM3tREtawRMu8sOjSbb8VYcYXJG/2eEOmfDu0= +google.golang.org/api v0.201.0/go.mod h1:HVY0FCHVs89xIW9fzf/pBvOEm+OolHa86G/txFezyq4= +google.golang.org/api v0.199.0 h1:aWUXClp+VFJmqE0JPvpZOK3LDQMyFKYIow4etYd9qxs= +google.golang.org/api v0.199.0/go.mod h1:ohG4qSztDJmZdjK/Ar6MhbAmb/Rpi4JHOqagsh90K28= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= @@ -397,20 +425,28 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142 h1:oLiyxGgE+rt22duwci1+TG7bg2/L1LQsXwfjPlmuJA0= -google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142/go.mod h1:G11eXq53iI5Q+kyNOmCvnzBaxEA2Q/Ik5Tj7nqBE8j4= -google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= -google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9 h1:nFS3IivktIU5Mk6KQa+v6RKkHUpdQpphqGNLxqNnbEk= +google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:tEzYTYZxbmVNOu0OAFH9HzdJtLn6h4Aj89zzlBCdHms= +google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f h1:jTm13A2itBi3La6yTGqn8bVSrc3ZZ1r8ENHlIXBfnRA= +google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f/go.mod h1:CLGoBuH1VHxAUXVPP8FfPwPEVJB6lz3URE5mY2SuayE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 h1:BulPr26Jqjnd4eYDVe+YvyR7Yc2vJGkO5/0UxD0/jZU= +google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:hL97c3SYopEHblzpxRL4lSs523++l8DYxGM1FQiYmb4= +google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0= +google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= +google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -422,8 +458,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -440,7 +476,5 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -github.com/GoogleCloudPlatform/declarative-resource-client-library v1.73.0 h1:mVsrkdw7rJbmay3EE/KjHx7WbQcrfwLmxmzCFDXIl90= -github.com/GoogleCloudPlatform/declarative-resource-client-library v1.73.0/go.mod h1:pL2Qt5HT+x6xrTd806oMiM3awW6kNIXB/iiuClz6m6k= -github.com/GoogleCloudPlatform/declarative-resource-client-library v1.74.0 h1:YV3eTXgDw3Zp8Mc12WE2Aa3+22twNd07xkFkEODrlOQ= -github.com/GoogleCloudPlatform/declarative-resource-client-library v1.74.0/go.mod h1:pL2Qt5HT+x6xrTd806oMiM3awW6kNIXB/iiuClz6m6k= +github.com/GoogleCloudPlatform/declarative-resource-client-library v1.75.0 h1:7tFkHNjfjm7dYnjqyuzMon+31lPaMTjca3OuamWd0Oo= +github.com/GoogleCloudPlatform/declarative-resource-client-library v1.75.0/go.mod h1:pL2Qt5HT+x6xrTd806oMiM3awW6kNIXB/iiuClz6m6k= diff --git a/mmv1/third_party/terraform/provider/provider_billing_project_test.go b/mmv1/third_party/terraform/provider/provider_billing_project_test.go new file mode 100644 index 000000000000..452ddbfa2b0b --- /dev/null +++ b/mmv1/third_party/terraform/provider/provider_billing_project_test.go @@ -0,0 +1,347 @@ +package provider_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +// TestAccSdkProvider_billing_project is a series of acc tests asserting how the SDK provider handles billing_project arguments +// It is SDK specific because the HCL used provisions SDK-implemented resources +// It is a counterpart to TestAccFwProvider_billing_project +func TestAccSdkProvider_billing_project(t *testing.T) { + testCases := map[string]func(t *testing.T){ + // Configuring the provider using inputs + "config takes precedence over environment variables": testAccSdkProvider_billing_project_configPrecedenceOverEnvironmentVariables, + "when billing_project is unset in the config, environment variables are used in a given order": testAccSdkProvider_billing_project_precedenceOrderEnvironmentVariables, // GOOGLE_BILLING_PROJECT + + // Schema-level validation + "when billing_project is set to an empty string in the config the value isn't ignored and results in an error": testAccSdkProvider_billing_project_emptyStringValidation, + + // Usage + // TODO: https://github.com/hashicorp/terraform-provider-google/issues/17882 + "GOOGLE_CLOUD_QUOTA_PROJECT environment variable interferes with the billing_account value used": testAccSdkProvider_billing_project_affectedByClientLibraryEnv, + // 1) Usage of billing_account alone is insufficient + // 2) Usage in combination with user_project_override changes the project where quota is used + "using billing_account alone doesn't impact provisioning, but using together with user_project_override does": testAccSdkProvider_billing_project_useWithAndWithoutUserProjectOverride, + } + + for name, tc := range testCases { + // shadow the tc variable into scope so that when + // the loop continues, if t.Run hasn't executed tc(t) + // yet, we don't have a race condition + // see https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +func testAccSdkProvider_billing_project_configPrecedenceOverEnvironmentVariables(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + billingProject := "my-billing-project-id" + + // ensure all possible billing_project env vars set; show they aren't used instead + t.Setenv("GOOGLE_BILLING_PROJECT", billingProject) + + providerBillingProject := "foobar" + + context := map[string]interface{}{ + "billing_project": providerBillingProject, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Apply-time error; bad value in config is used over of good values in ENVs + Config: testAccSdkProvider_billing_project_inProviderBlock(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "billing_project", providerBillingProject), + )}, + }, + }) +} + +func testAccSdkProvider_billing_project_precedenceOrderEnvironmentVariables(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + /* + These are all the ENVs for billing_project + GOOGLE_BILLING_PROJECT + + GOOGLE_CLOUD_QUOTA_PROJECT - NOT used by provider, but is in client libraries we use + */ + + GOOGLE_BILLING_PROJECT := "GOOGLE_BILLING_PROJECT" + GOOGLE_CLOUD_QUOTA_PROJECT := "GOOGLE_CLOUD_QUOTA_PROJECT" + + context := map[string]interface{}{} + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // GOOGLE_BILLING_PROJECT is used if set + PreConfig: func() { + t.Setenv("GOOGLE_BILLING_PROJECT", GOOGLE_BILLING_PROJECT) //used + t.Setenv("GOOGLE_CLOUD_QUOTA_PROJECT", GOOGLE_CLOUD_QUOTA_PROJECT) + }, + Config: testAccSdkProvider_billing_project_inEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "billing_project", GOOGLE_BILLING_PROJECT), + ), + }, + { + // GOOGLE_CLOUD_QUOTA_PROJECT is NOT used here + PreConfig: func() { + t.Setenv("GOOGLE_BILLING_PROJECT", "") + t.Setenv("GOOGLE_CLOUD_QUOTA_PROJECT", GOOGLE_CLOUD_QUOTA_PROJECT) // NOT used + }, + Config: testAccSdkProvider_billing_project_inEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "billing_project", ""), + ), + }, + }, + }) +} + +func testAccSdkProvider_billing_project_emptyStringValidation(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + billingProject := "my-billing-project-id" + + // ensure all billing_project env vars set + t.Setenv("GOOGLE_BILLING_PROJECT", billingProject) + + context := map[string]interface{}{ + "billing_project": "", // empty string used + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccSdkProvider_billing_project_inProviderBlock(context), + PlanOnly: true, + ExpectError: regexp.MustCompile("expected a non-empty string"), + }, + }, + }) +} + +func testAccSdkProvider_billing_project_useWithAndWithoutUserProjectOverride(t *testing.T) { + // Test cannot run in VCR mode due to use of aliases + // See: https://github.com/hashicorp/terraform-provider-google/issues/20019 + acctest.SkipIfVcr(t) + + randomString := acctest.RandString(t, 10) + contextUserProjectOverrideFalse := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "billing_account": envvar.GetTestBillingAccountFromEnv(t), + "user_project_override": "false", // Used in combo with billing_account + "random_suffix": randomString, + } + + contextUserProjectOverrideTrue := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "billing_account": envvar.GetTestBillingAccountFromEnv(t), + "user_project_override": "true", // Used in combo with billing_account + "random_suffix": randomString, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Setup resources + // Neither user_project_override nor billing_project value used here + Config: testAccSdkProvider_billing_project_useBillingProject_setup(contextUserProjectOverrideFalse), + }, + { + // With user_project_override=true the PubSub topic CANNOT be provisioned because quota is consumed + // from the newly provisioned project, and that project does not have the PubSub API enabled. + // The billing_project is used, leading to the error occurring, because user_project_override=true + Config: testAccSdkProvider_billing_project_useBillingProject_scenario(contextUserProjectOverrideTrue), + ExpectError: regexp.MustCompile(fmt.Sprintf("Error 403: Cloud Pub/Sub API has not been used in project tf-test-%s", randomString)), + }, + { + // With user_project_override=false the PubSub topic can be provisioned because quota is consumed + // from the project the Terraform identity is in, and that project has PubSub API enabled. + // The billing_project value isn't used, meaning the error doesn't happen, because user_project_override=false + Config: testAccSdkProvider_billing_project_useBillingProject_scenario(contextUserProjectOverrideFalse), + }, + }, + }) +} + +func testAccSdkProvider_billing_project_affectedByClientLibraryEnv(t *testing.T) { + // Test cannot run in VCR mode due to use of aliases + // See: https://github.com/hashicorp/terraform-provider-google/issues/20019 + acctest.SkipIfVcr(t) + + randomString := acctest.RandString(t, 10) + + context := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "billing_account": envvar.GetTestBillingAccountFromEnv(t), + "user_project_override": "true", // Used in combo with billing_account + "random_suffix": randomString, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Setup resources + // Neither user_project_override nor billing_project value used here + Config: testAccSdkProvider_billing_project_useBillingProject_setupWithApiEnabled(context), + }, + { + // This ENV interferes with setting the billing_project, + // so we get an error mentioning the value + PreConfig: func() { + t.Setenv("GOOGLE_CLOUD_QUOTA_PROJECT", "foobar") + }, + Config: testAccSdkProvider_billing_project_useBillingProject_scenarioWithApiEnabled(context), + ExpectError: regexp.MustCompile("foobar"), + }, + { + // The same config without that ENV present applies without error + PreConfig: func() { + t.Setenv("GOOGLE_CLOUD_QUOTA_PROJECT", "") + }, + Config: testAccSdkProvider_billing_project_useBillingProject_scenarioWithApiEnabled(context), + }, + }, + }) +} + +// testAccSdkProvider_billing_project_inProviderBlock allows setting the billing_project argument in a provider block. +// This function uses data.google_provider_config_sdk because it is implemented with the SDKv2 +func testAccSdkProvider_billing_project_inProviderBlock(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + billing_project = "%{billing_project}" +} + +data "google_provider_config_sdk" "default" {} +`, context) +} + +// testAccSdkProvider_billing_project_inEnvsOnly allows testing when the billing_project argument +// is only supplied via ENVs +func testAccSdkProvider_billing_project_inEnvsOnly(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_provider_config_sdk" "default" {} +`, context) +} + +func testAccSdkProvider_billing_project_useBillingProject_setup(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" {} + +# Create a new project and enable service APIs in those projects +resource "google_project" "project" { + provider = google + project_id = "tf-test-%{random_suffix}" + name = "tf-test-%{random_suffix}" + org_id = "%{org_id}" + billing_account = "%{billing_account}" + deletion_policy = "DELETE" +} + +resource "google_project_service" "serviceusage" { + project = google_project.project.project_id + service = "serviceusage.googleapis.com" + + disable_on_destroy = false # Need it enabled in the project when the test disables services in post-test cleanup +} +`, context) +} + +func testAccSdkProvider_billing_project_useBillingProject_scenario(context map[string]interface{}) string { + + // SECOND APPLY + // This is needed as configuring the provider depends on resources provisioned in the setup step + return testAccSdkProvider_billing_project_useBillingProject_setup(context) + acctest.Nprintf(` +# Set up the usage of +# - user_project_override +# - billing_project +provider "google" { + alias = "user_project_override" + user_project_override = %{user_project_override} + billing_project = google_project.project.project_id + project = google_project.project.project_id +} + +# See if the impersonated SA can provision the PubSub resource in a way that uses +# the newly provisioned project as the source of consumed quota +resource "google_pubsub_topic" "example-resource-in" { + provider = google.user_project_override + project = google_project.project.project_id + name = "tf-test-%{random_suffix}" +} +`, context) +} + +// testAccSdkProvider_billing_project_useBillingProject_setupWithApiEnabled is the same setup as above but appends config to activate +// the PubSub API. This allows the second apply step to succeed in a test, if needed. +func testAccSdkProvider_billing_project_useBillingProject_setupWithApiEnabled(context map[string]interface{}) string { + return testAccSdkProvider_billing_project_useBillingProject_setup(context) + acctest.Nprintf(` + +# Needed for test steps to apply without error +resource "google_project_service" "pubsub" { + project = google_project.project.project_id + service = "pubsub.googleapis.com" +} + +resource "google_project_service" "cloudresourcemanager" { + project = google_project.project.project_id + service = "cloudresourcemanager.googleapis.com" + disable_on_destroy = false # Need it enabled in the project when the test deletes the project resource in post-test cleanup +} +`, context) +} + +// testAccSdkProvider_billing_project_useBillingProject_scenarioWithApiEnabled is the same scenario as above but includes config that +// has activated the PubSub API. This allows the scenario to apply successfully in a test, if needed. +func testAccSdkProvider_billing_project_useBillingProject_scenarioWithApiEnabled(context map[string]interface{}) string { + + // SECOND APPLY + // This is needed as configuring the provider depends on resources provisioned in the setup step + return testAccSdkProvider_billing_project_useBillingProject_setupWithApiEnabled(context) + acctest.Nprintf(` +# Set up the usage of +# - user_project_override +# - billing_project +provider "google" { + alias = "user_project_override" + user_project_override = %{user_project_override} + billing_project = google_project.project.project_id + project = google_project.project.project_id +} + +# See if the impersonated SA can provision the PubSub resource in a way that uses +# the newly provisioned project as the source of consumed quota +resource "google_pubsub_topic" "example-resource-in" { + provider = google.user_project_override + project = google_project.project.project_id + name = "tf-test-%{random_suffix}" + + depends_on = [ + google_project_service.pubsub + ] +} +`, context) +} diff --git a/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.tmpl b/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.tmpl index 99fbf843aabd..d8c2a724b2b3 100644 --- a/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.tmpl +++ b/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.tmpl @@ -170,11 +170,14 @@ var handwrittenDatasources = map[string]*schema.Resource{ "google_monitoring_app_engine_service": monitoring.DataSourceMonitoringServiceAppEngine(), "google_monitoring_uptime_check_ips": monitoring.DataSourceGoogleMonitoringUptimeCheckIps(), "google_netblock_ip_ranges": resourcemanager.DataSourceGoogleNetblockIpRanges(), + "google_oracle_database_autonomous_database": oracledatabase.DataSourceOracleDatabaseAutonomousDatabase(), + "google_oracle_database_autonomous_databases": oracledatabase.DataSourceOracleDatabaseAutonomousDatabases(), "google_oracle_database_db_nodes": oracledatabase.DataSourceOracleDatabaseDbNodes(), "google_oracle_database_db_servers": oracledatabase.DataSourceOracleDatabaseDbServers(), "google_oracle_database_cloud_exadata_infrastructures": oracledatabase.DataSourceOracleDatabaseCloudExadataInfrastructures(), "google_oracle_database_cloud_exadata_infrastructure":oracledatabase.DataSourceOracleDatabaseCloudExadataInfrastructure(), - "google_oracle_database_cloud_vm_cluster": oracledatabase.DataSourceOracleDatabaseCloudVmCluster(), + "google_oracle_database_cloud_vm_clusters": oracledatabase.DataSourceOracleDatabaseCloudVmClusters(), + "google_oracle_database_cloud_vm_cluster": oracledatabase.DataSourceOracleDatabaseCloudVmCluster(), "google_organization": resourcemanager.DataSourceGoogleOrganization(), "google_privateca_certificate_authority": privateca.DataSourcePrivatecaCertificateAuthority(), "google_privileged_access_manager_entitlement": privilegedaccessmanager.DataSourceGooglePrivilegedAccessManagerEntitlement(), diff --git a/mmv1/third_party/terraform/provider/provider_request_timeout_test.go b/mmv1/third_party/terraform/provider/provider_request_timeout_test.go new file mode 100644 index 000000000000..cd57877a8bf4 --- /dev/null +++ b/mmv1/third_party/terraform/provider/provider_request_timeout_test.go @@ -0,0 +1,180 @@ +package provider_test + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +// TestAccSdkProvider_request_timeout is a series of acc tests asserting how the SDK provider handles request_timeout arguments +// It is SDK specific because the HCL used provisions SDK-implemented resources +// It is a counterpart to TestAccFwProvider_request_timeout +func TestAccSdkProvider_request_timeout(t *testing.T) { + testCases := map[string]func(t *testing.T){ + // Configuring the provider using inputs + "a default value of 0s is used when there are no user inputs (it is overridden downstream)": testAccSdkProvider_request_timeout_providerDefault, + "request_timeout can be set in config in different formats, are normalized to full-length format": testAccSdkProvider_request_timeout_setInConfig, + //no ENVs to test + + // Schema-level validation + "when request_timeout is set to an empty string in the config the value fails validation, as it is not a duration": testAccSdkProvider_request_timeout_emptyStringValidation, + + // Usage + "short timeouts impact provisioning resources": testAccSdkProvider_request_timeout_usage, + } + + for name, tc := range testCases { + // shadow the tc variable into scope so that when + // the loop continues, if t.Run hasn't executed tc(t) + // yet, we don't have a race condition + // see https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +// In the SDK version of the provider config code request_timeout has a zero value of "0s" and no default. +// The final 'effective' value is "120s", matching the default used in the plugin-framework version of the provider config code. +// See : https://github.com/hashicorp/terraform-provider-google/blob/09cb850ee64bcd78e4457df70905530c1ed75f19/google/transport/config.go#L1228-L1233 +func testAccSdkProvider_request_timeout_providerDefault(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccSdkProvider_request_timeout_unset(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "request_timeout", "0s"), + ), + }, + }, + }) +} + +func testAccSdkProvider_request_timeout_setInConfig(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + providerTimeout1 := "3m0s" + providerTimeout2 := "3m" + expectedValue := "3m0s" + + context1 := map[string]interface{}{ + "request_timeout": providerTimeout1, + } + context2 := map[string]interface{}{ + "request_timeout": providerTimeout2, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccSdkProvider_request_timeout_inProviderBlock(context1), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "request_timeout", expectedValue), + ), + }, + { + Config: testAccSdkProvider_request_timeout_inProviderBlock(context2), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "request_timeout", expectedValue), + ), + }, + }, + }) +} + +func testAccSdkProvider_request_timeout_emptyStringValidation(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + context := map[string]interface{}{ + "request_timeout": "", // empty string used + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccSdkProvider_request_timeout_inProviderBlock(context), + ExpectError: regexp.MustCompile("invalid duration"), + }, + }, + }) +} + +func testAccSdkProvider_request_timeout_usage(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + shortTimeout := "10ms" // short time that will result in an error + longTimeout := "120s" + + randomString := acctest.RandString(t, 10) + context1 := map[string]interface{}{ + "request_timeout": shortTimeout, + "random_suffix": randomString, + } + context2 := map[string]interface{}{ + "request_timeout": longTimeout, + "random_suffix": randomString, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccSdkProvider_request_timeout_provisionWithTimeout(context1), + // Effect of request_timeout value + ExpectError: regexp.MustCompile("context deadline exceeded"), + }, + { + Config: testAccSdkProvider_request_timeout_provisionWithTimeout(context2), + // No error; everything is fine with an appropriate timeout value + }, + }, + }) +} + +// testAccSdkProvider_request_timeout_inProviderBlock allows setting the request_timeout argument in a provider block. +// This function uses data.google_provider_config_sdk because it is implemented with the SDKv2 +func testAccSdkProvider_request_timeout_inProviderBlock(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + request_timeout = "%{request_timeout}" +} + +data "google_provider_config_sdk" "default" {} +`, context) +} + +// testAccSdkProvider_request_timeout_provisionWithTimeout allows testing the effects of request_timeout on +// provisioning a resource. +func testAccSdkProvider_request_timeout_provisionWithTimeout(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + request_timeout = "%{request_timeout}" +} + +data "google_provider_config_sdk" "default" {} + +resource "google_service_account" "default" { + account_id = "tf-test-%{random_suffix}" + display_name = "AccTest Service Account" +} +`, context) +} + +// testAccSdkProvider_request_timeout_inEnvsOnly allows testing when the request_timeout argument is not set +func testAccSdkProvider_request_timeout_unset() string { + return ` +data "google_provider_config_sdk" "default" {} +` +} diff --git a/mmv1/third_party/terraform/provider/provider_test.go.tmpl b/mmv1/third_party/terraform/provider/provider_test.go.tmpl index 046e242ae8e7..1a63bbeb9cee 100644 --- a/mmv1/third_party/terraform/provider/provider_test.go.tmpl +++ b/mmv1/third_party/terraform/provider/provider_test.go.tmpl @@ -7,7 +7,6 @@ import ( "testing" "github.com/hashicorp/terraform-provider-google/google/acctest" - "github.com/hashicorp/terraform-provider-google/google/envvar" "github.com/hashicorp/terraform-provider-google/google/provider" "github.com/hashicorp/terraform-provider-google/google/tpgresource" transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" @@ -98,88 +97,6 @@ func TestAccProviderMeta_setModuleName(t *testing.T) { }) } -func TestAccProviderUserProjectOverride(t *testing.T) { - // Parallel fine-grained resource creation - acctest.SkipIfVcr(t) - t.Parallel() - - org := envvar.GetTestOrgFromEnv(t) - billing := envvar.GetTestBillingAccountFromEnv(t) - pid := "tf-test-" + acctest.RandString(t, 10) - topicName := "tf-test-topic-" + acctest.RandString(t, 10) - - config := acctest.BootstrapConfig(t) - accessToken, err := acctest.SetupProjectsAndGetAccessToken(org, billing, pid, "pubsub", config) - if err != nil { - t.Error(err) - } - - acctest.VcrTest(t, resource.TestCase{ - PreCheck: func() { acctest.AccTestPreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), - // No TestDestroy since that's not really the point of this test - Steps: []resource.TestStep{ - { - Config: testAccProviderUserProjectOverride_step2(accessToken, pid, false, topicName), - ExpectError: regexp.MustCompile("Cloud Pub/Sub API has not been used"), - }, - { - Config: testAccProviderUserProjectOverride_step2(accessToken, pid, true, topicName), - }, - { - ResourceName: "google_pubsub_topic.project-2-topic", - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"labels", "terraform_labels"}, - }, - { - Config: testAccProviderUserProjectOverride_step3(accessToken, true), - }, - }, - }) -} - -// Do the same thing as TestAccProviderUserProjectOverride, but using a resource that gets its project via -// a reference to a different resource instead of a project field. -func TestAccProviderIndirectUserProjectOverride(t *testing.T) { - // Parallel fine-grained resource creation - acctest.SkipIfVcr(t) - t.Parallel() - - org := envvar.GetTestOrgFromEnv(t) - billing := envvar.GetTestBillingAccountFromEnv(t) - pid := "tf-test-" + acctest.RandString(t, 10) - - config := acctest.BootstrapConfig(t) - accessToken, err := acctest.SetupProjectsAndGetAccessToken(org, billing, pid, "cloudkms", config) - if err != nil { - t.Error(err) - } - - acctest.VcrTest(t, resource.TestCase{ - PreCheck: func() { acctest.AccTestPreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), - // No TestDestroy since that's not really the point of this test - Steps: []resource.TestStep{ - { - Config: testAccProviderIndirectUserProjectOverride_step2(pid, accessToken, false), - ExpectError: regexp.MustCompile(`Cloud Key Management Service \(KMS\) API has not been used`), - }, - { - Config: testAccProviderIndirectUserProjectOverride_step2(pid, accessToken, true), - }, - { - ResourceName: "google_kms_crypto_key.project-2-key", - ImportState: true, - ImportStateVerify: true, - }, - { - Config: testAccProviderIndirectUserProjectOverride_step3(accessToken, true), - }, - }, - }) -} - func TestAccProviderEmptyStrings(t *testing.T) { t.Parallel() @@ -275,81 +192,6 @@ resource "google_compute_address" "default" { }`, key, name) } -// Set up two projects. Project 1 has a service account that is used to create a -// pubsub topic in project 2. The pubsub API is only enabled in project 2, -// which causes the create to fail unless user_project_override is set to true. - -func testAccProviderUserProjectOverride_step2(accessToken, pid string, override bool, topicName string) string { - return fmt.Sprintf(` -// See step 3 below, which is really step 2 minus the pubsub topic. -// Step 3 exists because provider configurations can't be removed while objects -// created by that provider still exist in state. Step 3 will remove the -// pubsub topic so the whole config can be deleted. -%s - -resource "google_pubsub_topic" "project-2-topic" { - provider = google.project-1-token - project = "%s-2" - - name = "%s" - labels = { - foo = "bar" - } -} -`, testAccProviderUserProjectOverride_step3(accessToken, override), pid, topicName) -} - -func testAccProviderUserProjectOverride_step3(accessToken string, override bool) string { - return fmt.Sprintf(` -provider "google" { - alias = "project-1-token" - access_token = "%s" - user_project_override = %v -} -`, accessToken, override) -} - -func testAccProviderIndirectUserProjectOverride_step2(pid, accessToken string, override bool) string { - return fmt.Sprintf(` -// See step 3 below, which is really step 2 minus the kms resources. -// Step 3 exists because provider configurations can't be removed while objects -// created by that provider still exist in state. Step 3 will remove the -// kms resources so the whole config can be deleted. -%s - -resource "google_kms_key_ring" "project-2-keyring" { - provider = google.project-1-token - project = "%s-2" - - name = "%s" - location = "us-central1" -} - -resource "google_kms_crypto_key" "project-2-key" { - provider = google.project-1-token - name = "%s" - key_ring = google_kms_key_ring.project-2-keyring.id -} - -data "google_kms_secret_ciphertext" "project-2-ciphertext" { - provider = google.project-1-token - crypto_key = google_kms_crypto_key.project-2-key.id - plaintext = "my-secret" -} -`, testAccProviderIndirectUserProjectOverride_step3(accessToken, override), pid, pid, pid) -} - -func testAccProviderIndirectUserProjectOverride_step3(accessToken string, override bool) string { - return fmt.Sprintf(` -provider "google" { - alias = "project-1-token" - - access_token = "%s" - user_project_override = %v -} -`, accessToken, override) -} - // Copy the Mmv1 generated function testAccCheckComputeAddressDestroyProducer from the compute_test package to here, // as that function is in the _test.go file and not importable. func testAccCheckComputeAddressDestroyProducer(t *testing.T) func(s *terraform.State) error { diff --git a/mmv1/third_party/terraform/provider/provider_user_project_override_test.go b/mmv1/third_party/terraform/provider/provider_user_project_override_test.go new file mode 100644 index 000000000000..f6d32fa8b314 --- /dev/null +++ b/mmv1/third_party/terraform/provider/provider_user_project_override_test.go @@ -0,0 +1,403 @@ +package provider_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +// TestAccSdkProvider_user_project_override is a series of acc tests asserting how the plugin-framework provider handles credentials arguments +// It is PF specific because the HCL used uses a PF-implemented data source +// It is a counterpart to TestAccFwProvider_user_project_override +func TestAccSdkProvider_user_project_override(t *testing.T) { + testCases := map[string]func(t *testing.T){ + // Configuring the provider using inputs + "config takes precedence over environment variables": testAccSdkProvider_user_project_override_configPrecedenceOverEnvironmentVariables, + "when user_project_override is unset in the config, environment variables are used": testAccSdkProvider_user_project_override_precedenceOrderEnvironmentVariables, + + // Schema-level validation + "when user_project_override is set in the config the value can be a boolean (true/false) or a string (true/false/1/0)": testAccSdkProvider_user_project_override_booleansInConfigOnly, + "when user_project_override is set via environment variables any of these values can be used: true/false/1/0": testAccSdkProvider_user_project_override_envStringsAccepted, + + // Usage + "user_project_override uses a resource's project argument to control which project is used for quota and billing purposes": testAccProviderUserProjectOverride, + "user_project_override works for resources that don't take a project argument (provider-level default project value is used)": testAccProviderIndirectUserProjectOverride, + } + + for name, tc := range testCases { + // shadow the tc variable into scope so that when + // the loop continues, if t.Run hasn't executed tc(t) + // yet, we don't have a race condition + // see https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +func testAccSdkProvider_user_project_override_configPrecedenceOverEnvironmentVariables(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + override := "true" + providerOverride := false + + // ensure all possible region env vars set; show they aren't used + t.Setenv("USER_PROJECT_OVERRIDE", override) + + context := map[string]interface{}{ + "user_project_override": providerOverride, + } + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Apply-time error; bad value in config is used over of good values in ENVs + Config: testAccSdkProvider_user_project_overrideInProviderBlock_boolean(context), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "user_project_override", fmt.Sprintf("%v", providerOverride)), + ), + }, + }, + }) +} + +func testAccSdkProvider_user_project_override_precedenceOrderEnvironmentVariables(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + /* + These are all the ENVs for region, and they are in order of precedence. + USER_PROJECT_OVERRIDE + */ + + context := map[string]interface{}{} + + acctest.VcrTest(t, resource.TestCase{ + // No PreCheck for checking ENVs + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "") // unset + }, + Config: testAccSdkProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + // defaults to false when not set via config or ENVs + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "user_project_override", "false"), + ), + }, + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "true") + }, + Config: testAccSdkProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "user_project_override", "true"), + ), + }, + }, + }) +} + +func testAccSdkProvider_user_project_override_booleansInConfigOnly(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + context_true := map[string]interface{}{ + "user_project_override": true, + } + context_false := map[string]interface{}{ + "user_project_override": false, + } + + context_1 := map[string]interface{}{ + "user_project_override": "1", + } + context_0 := map[string]interface{}{ + "user_project_override": "0", + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccSdkProvider_user_project_overrideInProviderBlock_boolean(context_true), + // No error expected + }, + { + Config: testAccSdkProvider_user_project_overrideInProviderBlock_boolean(context_false), + // No error expected + }, + { + Config: testAccSdkProvider_user_project_overrideInProviderBlock_string(context_true), + // No error expected + }, + { + Config: testAccSdkProvider_user_project_overrideInProviderBlock_string(context_false), + // No error expected + }, + { + Config: testAccSdkProvider_user_project_overrideInProviderBlock_string(context_1), + // No error expected + }, + { + Config: testAccSdkProvider_user_project_overrideInProviderBlock_string(context_0), + // No error expected + }, + }, + }) +} + +func testAccSdkProvider_user_project_override_envStringsAccepted(t *testing.T) { + acctest.SkipIfVcr(t) // Test doesn't interact with API + + context := map[string]interface{}{} + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "true") + }, + Config: testAccSdkProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "user_project_override", "true"), + ), + }, + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "1") + }, + Config: testAccSdkProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "user_project_override", "true"), + ), + }, + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "false") + }, + Config: testAccSdkProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "user_project_override", "false"), + ), + }, + { + PreConfig: func() { + t.Setenv("USER_PROJECT_OVERRIDE", "0") + }, + Config: testAccSdkProvider_user_project_overrideInEnvsOnly(context), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.google_provider_config_sdk.default", "user_project_override", "false"), + ), + }, + }, + }) +} + +// TestAccSdkProvider_user_project_overrideInProviderBlock allows setting the user_project_override argument in a provider block. +// This function uses data.google_provider_config_sdk because it is implemented with the plugin-framework +func testAccSdkProvider_user_project_overrideInProviderBlock_boolean(context map[string]interface{}) string { + v := acctest.Nprintf(` +provider "google" { + user_project_override = %{user_project_override} +} + +data "google_provider_config_sdk" "default" {} +`, context) + return v +} + +func testAccSdkProvider_user_project_overrideInProviderBlock_string(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + user_project_override = "%{user_project_override}" +} + +data "google_provider_config_sdk" "default" {} +`, context) +} + +// testAccSdkProvider_user_project_overrideInEnvsOnly allows testing when the user_project_override argument +// is only supplied via ENVs +func testAccSdkProvider_user_project_overrideInEnvsOnly(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_provider_config_sdk" "default" {} +`, context) +} + +// Set up two projects. Project 1 has a service account that is used to create a +// pubsub topic in project 2. The pubsub API is only enabled in project 2, +// which causes the create to fail unless user_project_override is set to true. +// The test demonstrates how: +// - If user_project_override = false : the apply fails as the API is disabled in project-1 +// - If user_project_override = true : the apply succeeds as X-Goog-User-Project will reference project-2, where API is enabled +func testAccProviderUserProjectOverride(t *testing.T) { + // Test cannot run in VCR mode due to use of aliases + // See: https://github.com/hashicorp/terraform-provider-google/issues/20019 + // And also due to the resources made out of band in acctest.SetupProjectsAndGetAccessToken + acctest.SkipIfVcr(t) + t.Parallel() + + org := envvar.GetTestOrgFromEnv(t) + billing := envvar.GetTestBillingAccountFromEnv(t) + pid := "tf-test-" + acctest.RandString(t, 10) + topicName := "tf-test-topic-" + acctest.RandString(t, 10) + + config := acctest.BootstrapConfig(t) + accessToken, err := acctest.SetupProjectsAndGetAccessToken(org, billing, pid, "pubsub", config) + if err != nil || accessToken == "" { + if err == nil { + t.Fatal("error when setting up projects and retrieving access token: access token is an empty string") + } + t.Fatalf("error when setting up projects and retrieving access token: %s", err) + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + // No TestDestroy since that's not really the point of this test + Steps: []resource.TestStep{ + { + Config: testAccProviderUserProjectOverride_step2(accessToken, pid, false, topicName), + ExpectError: regexp.MustCompile("Cloud Pub/Sub API has not been used"), + }, + { + Config: testAccProviderUserProjectOverride_step2(accessToken, pid, true, topicName), + }, + { + ResourceName: "google_pubsub_topic.project-2-topic", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"labels", "terraform_labels"}, + }, + { + Config: testAccProviderUserProjectOverride_step3(accessToken, true), + }, + }, + }) +} + +// Do the same thing as TestAccProviderUserProjectOverride, but using a resource that gets its project via +// a reference to a different resource instead of a project field. +func testAccProviderIndirectUserProjectOverride(t *testing.T) { + // Test cannot run in VCR mode due to use of aliases + // See: https://github.com/hashicorp/terraform-provider-google/issues/20019 + // And also due to the resources made out of band in acctest.SetupProjectsAndGetAccessToken + acctest.SkipIfVcr(t) + t.Parallel() + + org := envvar.GetTestOrgFromEnv(t) + billing := envvar.GetTestBillingAccountFromEnv(t) + pid := "tf-test-" + acctest.RandString(t, 10) + + config := acctest.BootstrapConfig(t) + accessToken, err := acctest.SetupProjectsAndGetAccessToken(org, billing, pid, "cloudkms", config) + if err != nil || accessToken == "" { + if err == nil { + t.Fatal("error when setting up projects and retrieving access token: access token is an empty string") + } + t.Fatalf("error when setting up projects and retrieving access token: %s", err) + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + // No TestDestroy since that's not really the point of this test + Steps: []resource.TestStep{ + { + Config: testAccProviderIndirectUserProjectOverride_step2(pid, accessToken, false), + ExpectError: regexp.MustCompile(`Cloud Key Management Service \(KMS\) API has not been used`), + }, + { + Config: testAccProviderIndirectUserProjectOverride_step2(pid, accessToken, true), + }, + { + ResourceName: "google_kms_crypto_key.project-2-key", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccProviderIndirectUserProjectOverride_step3(accessToken, true), + }, + }, + }) +} + +func testAccProviderUserProjectOverride_step2(accessToken, pid string, override bool, topicName string) string { + return fmt.Sprintf(` +// See step 3 below, which is really step 2 minus the pubsub topic. +// Step 3 exists because provider configurations can't be removed while objects +// created by that provider still exist in state. Step 3 will remove the +// pubsub topic so the whole config can be deleted. +%s + +resource "google_pubsub_topic" "project-2-topic" { + provider = google.project-1-token + project = "%s-2" + + name = "%s" + labels = { + foo = "bar" + } +} +`, testAccProviderUserProjectOverride_step3(accessToken, override), pid, topicName) +} + +func testAccProviderUserProjectOverride_step3(accessToken string, override bool) string { + return fmt.Sprintf(` +provider "google" { + alias = "project-1-token" + access_token = "%s" + user_project_override = %v +} +`, accessToken, override) +} + +func testAccProviderIndirectUserProjectOverride_step2(pid, accessToken string, override bool) string { + return fmt.Sprintf(` +// See step 3 below, which is really step 2 minus the kms resources. +// Step 3 exists because provider configurations can't be removed while objects +// created by that provider still exist in state. Step 3 will remove the +// kms resources so the whole config can be deleted. +%s + +resource "google_kms_key_ring" "project-2-keyring" { + provider = google.project-1-token + project = "%s-2" + + name = "%s" + location = "us-central1" +} + +resource "google_kms_crypto_key" "project-2-key" { + provider = google.project-1-token + name = "%s" + key_ring = google_kms_key_ring.project-2-keyring.id +} + +data "google_kms_secret_ciphertext" "project-2-ciphertext" { + provider = google.project-1-token + crypto_key = google_kms_crypto_key.project-2-key.id + plaintext = "my-secret" +} +`, testAccProviderIndirectUserProjectOverride_step3(accessToken, override), pid, pid, pid) +} + +func testAccProviderIndirectUserProjectOverride_step3(accessToken string, override bool) string { + return fmt.Sprintf(` +provider "google" { + alias = "project-1-token" + + access_token = "%s" + user_project_override = %v +} +`, accessToken, override) +} diff --git a/mmv1/third_party/terraform/services/apphub/resource_apphub_application_test.go b/mmv1/third_party/terraform/services/apphub/resource_apphub_application_test.go index d2ab0bae46c2..8a59682937fa 100644 --- a/mmv1/third_party/terraform/services/apphub/resource_apphub_application_test.go +++ b/mmv1/third_party/terraform/services/apphub/resource_apphub_application_test.go @@ -1,6 +1,7 @@ package apphub_test import ( + "regexp" "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -21,7 +22,7 @@ func TestAccApphubApplication_applicationUpdateFull(t *testing.T) { CheckDestroy: testAccCheckApphubApplicationDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccApphubApplication_applicationFullExample(context), + Config: testAccApphubApplication_apphubApplicationFullExample(context), }, { ResourceName: "google_apphub_application.example2", @@ -208,3 +209,53 @@ resource "google_apphub_application" "example2" { } `, context) } + +func TestAccApphubApplication_invalidConfigFails(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckApphubApplicationDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccApphubApplication_applicationInvalidConfig1(context), + ExpectError: regexp.MustCompile("Error validating location global with REGIONAL scope type"), + }, + { + Config: testAccApphubApplication_applicationInvalidConfig2(context), + ExpectError: regexp.MustCompile("Error validating location us-east1 with GLOBAL scope type"), + }, + }, + }) +} + +func testAccApphubApplication_applicationInvalidConfig1(context map[string]interface{}) string { + return acctest.Nprintf(` + +resource "google_apphub_application" "invalid_example" { + location = "global" + application_id = "tf-test-invalid-example-application%{random_suffix}" + scope { + type = "REGIONAL" + } +} +`, context) +} + +func testAccApphubApplication_applicationInvalidConfig2(context map[string]interface{}) string { + return acctest.Nprintf(` + +resource "google_apphub_application" "invalid_example" { + location = "us-east1" + application_id = "tf-test-invalid-example-application%{random_suffix}" + scope { + type = "GLOBAL" + } +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_test.go b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_test.go index 876291d15ef3..29478f407afc 100644 --- a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_test.go +++ b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_test.go @@ -782,22 +782,22 @@ data "google_project" "project" { } resource "google_tags_tag_key" "tag_key1" { - parent = "projects/${data.google_project.project.number}" + parent = data.google_project.project.id short_name = "tf_test_tag_key1%{random_suffix}" } resource "google_tags_tag_value" "tag_value1" { - parent = "tagKeys/${google_tags_tag_key.tag_key1.name}" + parent = google_tags_tag_key.tag_key1.id short_name = "tf_test_tag_value1%{random_suffix}" } resource "google_tags_tag_key" "tag_key2" { - parent = "projects/${data.google_project.project.number}" + parent = data.google_project.project.id short_name = "tf_test_tag_key2%{random_suffix}" } resource "google_tags_tag_value" "tag_value2" { - parent = "tagKeys/${google_tags_tag_key.tag_key2.name}" + parent = google_tags_tag_key.tag_key2.id short_name = "tf_test_tag_value2%{random_suffix}" } @@ -808,8 +808,8 @@ resource "google_bigquery_dataset" "dataset" { location = "EU" resource_tags = { - "${data.google_project.project.project_id}/${google_tags_tag_key.tag_key1.short_name}" = "${google_tags_tag_value.tag_value1.short_name}" - "${data.google_project.project.project_id}/${google_tags_tag_key.tag_key2.short_name}" = "${google_tags_tag_value.tag_value2.short_name}" + (google_tags_tag_key.tag_key1.namespaced_name) = google_tags_tag_value.tag_value1.short_name + (google_tags_tag_key.tag_key2.namespaced_name) = google_tags_tag_value.tag_value2.short_name } } `, context) @@ -821,22 +821,22 @@ data "google_project" "project" { } resource "google_tags_tag_key" "tag_key1" { - parent = "projects/${data.google_project.project.number}" + parent = data.google_project.project.id short_name = "tf_test_tag_key1%{random_suffix}" } resource "google_tags_tag_value" "tag_value1" { - parent = "tagKeys/${google_tags_tag_key.tag_key1.name}" + parent = google_tags_tag_key.tag_key1.id short_name = "tf_test_tag_value1%{random_suffix}" } resource "google_tags_tag_key" "tag_key2" { - parent = "projects/${data.google_project.project.number}" + parent = data.google_project.project.id short_name = "tf_test_tag_key2%{random_suffix}" } resource "google_tags_tag_value" "tag_value2" { - parent = "tagKeys/${google_tags_tag_key.tag_key2.name}" + parent = google_tags_tag_key.tag_key2.id short_name = "tf_test_tag_value2%{random_suffix}" } diff --git a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go index a6e11aad0386..81e638036609 100644 --- a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go +++ b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go @@ -4274,7 +4274,7 @@ resource "google_tags_tag_key" "key1" { } resource "google_tags_tag_value" "value1" { - parent = "tagKeys/${google_tags_tag_key.key1.name}" + parent = google_tags_tag_key.key1.id short_name = "%{tag_value_name1}" } @@ -4287,7 +4287,7 @@ resource "google_bigquery_table" "test" { dataset_id = "${google_bigquery_dataset.test.dataset_id}" table_id = "%{table_id}" resource_tags = { - "%{project_id}/${google_tags_tag_key.key1.short_name}" = "${google_tags_tag_value.value1.short_name}" + (google_tags_tag_key.key1.namespaced_name) = google_tags_tag_value.value1.short_name } } `, context) @@ -4301,7 +4301,7 @@ resource "google_tags_tag_key" "key1" { } resource "google_tags_tag_value" "value1" { - parent = "tagKeys/${google_tags_tag_key.key1.name}" + parent = google_tags_tag_key.key1.id short_name = "%{tag_value_name1}" } @@ -4311,7 +4311,7 @@ resource "google_tags_tag_key" "key2" { } resource "google_tags_tag_value" "value2" { - parent = "tagKeys/${google_tags_tag_key.key2.name}" + parent = google_tags_tag_key.key2.id short_name = "%{tag_value_name2}" } @@ -4324,8 +4324,8 @@ resource "google_bigquery_table" "test" { dataset_id = "${google_bigquery_dataset.test.dataset_id}" table_id = "%{table_id}" resource_tags = { - "%{project_id}/${google_tags_tag_key.key1.short_name}" = "${google_tags_tag_value.value1.short_name}" - "%{project_id}/${google_tags_tag_key.key2.short_name}" = "${google_tags_tag_value.value2.short_name}" + (google_tags_tag_key.key1.namespaced_name) = google_tags_tag_value.value1.short_name + (google_tags_tag_key.key2.namespaced_name) = google_tags_tag_value.value2.short_name } } `, context) @@ -4339,7 +4339,7 @@ resource "google_tags_tag_key" "key1" { } resource "google_tags_tag_value" "value1" { - parent = "tagKeys/${google_tags_tag_key.key1.name}" + parent = google_tags_tag_key.key1.id short_name = "%{tag_value_name1}" } @@ -4349,7 +4349,7 @@ resource "google_tags_tag_key" "key2" { } resource "google_tags_tag_value" "value2" { - parent = "tagKeys/${google_tags_tag_key.key2.name}" + parent = google_tags_tag_key.key2.id short_name = "%{tag_value_name2}" } diff --git a/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.tmpl b/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.tmpl index c4da960cb631..8fc8e27024b7 100644 --- a/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.tmpl +++ b/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.tmpl @@ -323,7 +323,7 @@ func TestAccComposerEnvironment_withWebServerConfig(t *testing.T) { func TestAccComposerEnvironment_withEncryptionConfigComposer1(t *testing.T) { t.Parallel() - kms := acctest.BootstrapKMSKeyInLocation(t, "us-central1") + kms := acctest.BootstrapKMSKeyWithPurposeInLocationAndName(t, "ENCRYPT_DECRYPT", "us-central1", "tf-bootstrap-composer1-key1") pid := envvar.GetTestProjectFromEnv() grantServiceAgentsRole(t, "service-", allComposerServiceAgents(), "roles/cloudkms.cryptoKeyEncrypterDecrypter") envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t)) @@ -360,7 +360,7 @@ func TestAccComposerEnvironment_withEncryptionConfigComposer2(t *testing.T) { acctest.SkipIfVcr(t) t.Parallel() - kms := acctest.BootstrapKMSKeyInLocation(t, "us-central1") + kms := acctest.BootstrapKMSKeyWithPurposeInLocationAndName(t, "ENCRYPT_DECRYPT", "us-central1", "tf-bootstrap-composer2-key1") pid := envvar.GetTestProjectFromEnv() grantServiceAgentsRole(t, "service-", allComposerServiceAgents(), "roles/cloudkms.cryptoKeyEncrypterDecrypter") envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t)) diff --git a/mmv1/third_party/terraform/services/compute/data_source_compute_secutity_policy.go b/mmv1/third_party/terraform/services/compute/data_source_compute_security_policy.go similarity index 92% rename from mmv1/third_party/terraform/services/compute/data_source_compute_secutity_policy.go rename to mmv1/third_party/terraform/services/compute/data_source_compute_security_policy.go index d0cba3c3287b..0e08ec840b99 100644 --- a/mmv1/third_party/terraform/services/compute/data_source_compute_secutity_policy.go +++ b/mmv1/third_party/terraform/services/compute/data_source_compute_security_policy.go @@ -19,12 +19,12 @@ func DataSourceGoogleComputeSecurityPolicy() *schema.Resource { tpgresource.AddOptionalFieldsToSchema(dsSchema, "self_link") return &schema.Resource{ - Read: dataSourceComputSecurityPolicyRead, + Read: dataSourceComputeSecurityPolicyRead, Schema: dsSchema, } } -func dataSourceComputSecurityPolicyRead(d *schema.ResourceData, meta interface{}) error { +func dataSourceComputeSecurityPolicyRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*transport_tpg.Config) id := "" diff --git a/mmv1/third_party/terraform/services/compute/data_source_compute_secutity_policy_test.go b/mmv1/third_party/terraform/services/compute/data_source_compute_security_policy_test.go similarity index 100% rename from mmv1/third_party/terraform/services/compute/data_source_compute_secutity_policy_test.go rename to mmv1/third_party/terraform/services/compute/data_source_compute_security_policy_test.go diff --git a/mmv1/third_party/terraform/services/compute/data_source_google_compute_instance.go.tmpl b/mmv1/third_party/terraform/services/compute/data_source_google_compute_instance.go.tmpl index 9b614a71e19e..6c7cd5b37862 100644 --- a/mmv1/third_party/terraform/services/compute/data_source_google_compute_instance.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/data_source_google_compute_instance.go.tmpl @@ -3,9 +3,9 @@ package compute import ( "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-google/google/tpgresource" transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func DataSourceGoogleComputeInstance() *schema.Resource { @@ -23,7 +23,7 @@ func DataSourceGoogleComputeInstance() *schema.Resource { func dataSourceGoogleComputeInstanceRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*transport_tpg.Config) - userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) if err != nil { return err } @@ -202,6 +202,13 @@ func dataSourceGoogleComputeInstanceRead(d *schema.ResourceData, meta interface{ if err := d.Set("name", instance.Name); err != nil { return fmt.Errorf("Error setting name: %s", err) } + if err := d.Set("key_revocation_action_type", instance.KeyRevocationActionType); err != nil { + return fmt.Errorf("Error setting key_revocation_action_type: %s", err) + } + if err := d.Set("creation_timestamp", instance.CreationTimestamp); err != nil { + return fmt.Errorf("Error setting creation_timestamp: %s", err) + } + d.SetId(fmt.Sprintf("projects/%s/zones/%s/instances/%s", project, tpgresource.GetResourceNameFromSelfLink(instance.Zone), instance.Name)) return nil } diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_image_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_image_test.go.tmpl index 727304f7a94f..02087011e908 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_image_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_image_test.go.tmpl @@ -44,7 +44,13 @@ func TestAccComputeImage_update(t *testing.T) { var image compute.Image - name := "image-test-" + acctest.RandString(t, 10) + context := map[string]interface{}{ + "name": "image-test-" + acctest.RandString(t, 10), + "disk_image_path": "./test-fixtures/raw-disk-image.tar.gz", + "bucket_one": "tf-test-compute-image-bucket-" + acctest.RandString(t, 10), + "bucket_two": "tf-test-compute-image-bucket-" + acctest.RandString(t, 10), + } + // Only labels supports an update acctest.VcrTest(t, resource.TestCase{ PreCheck: func() { acctest.AccTestPreCheck(t) }, @@ -52,7 +58,7 @@ func TestAccComputeImage_update(t *testing.T) { CheckDestroy: testAccCheckComputeImageDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccComputeImage_basic(name), + Config: testAccComputeImage_basic(context), Check: resource.ComposeTestCheckFunc( testAccCheckComputeImageExists( t, "google_compute_image.foobar", &image), @@ -60,7 +66,7 @@ func TestAccComputeImage_update(t *testing.T) { ), }, { - Config: testAccComputeImage_update(name), + Config: testAccComputeImage_update(context), Check: resource.ComposeTestCheckFunc( testAccCheckComputeImageExists( t, "google_compute_image.foobar", &image), @@ -358,20 +364,32 @@ resource "google_compute_image" "foobar" { `, name, name, family) } -func testAccComputeImage_basic(name string) string { - return fmt.Sprintf(` +func testAccComputeImage_basic(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_storage_bucket" "bucket_one" { + name = "%{bucket_one}" + location = "US" + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_object" "object" { + name = "raw-disk-image.tar.gz" + bucket = google_storage_bucket.bucket_one.name + source = "%{disk_image_path}" +} + resource "google_compute_image" "foobar" { - name = "%s" + name = "%{name}" description = "description-test" family = "family-test" raw_disk { - source = "https://storage.googleapis.com/bosh-gce-raw-stemcells/bosh-stemcell-97.98-google-kvm-ubuntu-xenial-go_agent-raw-1557960142.tar.gz" + source = "https://${google_storage_bucket.bucket_one.name}.storage.googleapis.com/${google_storage_bucket_object.object.name}" } labels = { my-label = "my-label-value" } } -`, name) +`, context) } func testAccComputeImage_license(name string) string { @@ -402,21 +420,33 @@ resource "google_compute_image" "foobar" { `, name, name) } -func testAccComputeImage_update(name string) string { - return fmt.Sprintf(` +func testAccComputeImage_update(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_storage_bucket" "bucket_two" { + name = "%{bucket_two}" + location = "US" + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_object" "object" { + name = "raw-disk-image.tar.gz" + bucket = google_storage_bucket.bucket_two.name + source = "%{disk_image_path}" +} + resource "google_compute_image" "foobar" { - name = "%s" + name = "%{name}" description = "description-test" family = "family-test" raw_disk { - source = "https://storage.googleapis.com/bosh-gce-raw-stemcells/bosh-stemcell-97.98-google-kvm-ubuntu-xenial-go_agent-raw-1557960142.tar.gz" + source = "https://${google_storage_bucket.bucket_two.name}.storage.googleapis.com/${google_storage_bucket_object.object.name}" } labels = { empty-label = "oh-look-theres-a-label-now" new-field = "only-shows-up-when-updated" } } -`, name) +`, context) } func testAccComputeImage_basedondisk(diskName, imageName string) string { diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl index 299363f66c60..3b1ff46a2250 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/terraform-provider-google/google/tpgresource" transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" + "github.com/hashicorp/terraform-provider-google/google/verify" {{ if eq $.TargetVersionName `ga` }} "google.golang.org/api/compute/v1" @@ -396,6 +397,7 @@ func ResourceComputeInstance() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + ValidateFunc: verify.ValidateRFC1035Name(1, 63), Description: `The name of the instance. One of name or self_link must be provided.`, }, @@ -1177,6 +1179,12 @@ be from 0 to 999,999,999 inclusive.`, Description: `The server-assigned unique identifier of this instance.`, }, + "creation_timestamp": { + Type: schema.TypeString, + Computed: true, + Description: `Creation timestamp in RFC3339 text format.`, + }, + "label_fingerprint": { Type: schema.TypeString, Computed: true, @@ -1262,6 +1270,14 @@ be from 0 to 999,999,999 inclusive.`, }, }, }, + + "key_revocation_action_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"STOP", "NONE", ""}, false), + Description: `Action to be taken when a customer's encryption key is revoked. Supports "STOP" and "NONE", with "NONE" being the default.`, + }, }, CustomizeDiff: customdiff.All( tpgresource.DefaultProviderProject, @@ -1272,7 +1288,6 @@ be from 0 to 999,999,999 inclusive.`, }, suppressEmptyGuestAcceleratorDiff, ), - desiredStatusDiff, validateSubnetworkProject, forceNewIfNetworkIPNotUpdatable, tpgresource.SetLabelsDiff, @@ -1437,6 +1452,7 @@ func expandComputeInstance(project string, d *schema.ResourceData, config *trans DisplayDevice: expandDisplayDevice(d), ResourcePolicies: tpgresource.ConvertStringArr(d.Get("resource_policies").([]interface{})), ReservationAffinity: reservationAffinity, + KeyRevocationActionType: d.Get("key_revocation_action_type").(string), }, nil } @@ -1462,10 +1478,34 @@ func getAllStatusBut(status string) []string { return computeInstanceStatus } -func waitUntilInstanceHasDesiredStatus(config *transport_tpg.Config, d *schema.ResourceData) error { - desiredStatus := d.Get("desired_status").(string) +func changeInstanceStatusOnCreation(config *transport_tpg.Config, d *schema.ResourceData, project, zone, status, userAgent string) error { + var op *compute.Operation + var err error + if status == "TERMINATED" { + op, err = config.NewComputeClient(userAgent).Instances.Stop(project, zone, d.Get("name").(string)).Do() + } else if status == "SUSPENDED" { + op, err = config.NewComputeClient(userAgent).Instances.Suspend(project, zone, d.Get("name").(string)).Do() + } + if err != nil { + return fmt.Errorf("Error changing instance status after creation: %s", err) + } + + waitErr := ComputeOperationWaitTime(config, op, project, "changing instance status", userAgent, d.Timeout(schema.TimeoutCreate)) + if waitErr != nil { + d.SetId("") + return waitErr + } + + err = waitUntilInstanceHasDesiredStatus(config, d, status) + if err != nil { + return fmt.Errorf("Error waiting for status: %s", err) + } - if desiredStatus != "" { + return nil +} + +func waitUntilInstanceHasDesiredStatus(config *transport_tpg.Config, d *schema.ResourceData, status string) error { + if status != "" { stateRefreshFunc := func() (interface{}, string, error) { instance, err := getInstance(config, d) if err != nil || instance == nil { @@ -1476,9 +1516,9 @@ func waitUntilInstanceHasDesiredStatus(config *transport_tpg.Config, d *schema.R } stateChangeConf := retry.StateChangeConf{ Delay: 5 * time.Second, - Pending: getAllStatusBut(desiredStatus), + Pending: getAllStatusBut(status), Refresh: stateRefreshFunc, - Target: []string{desiredStatus}, + Target: []string{status}, Timeout: d.Timeout(schema.TimeoutUpdate), MinTimeout: 2 * time.Second, } @@ -1486,7 +1526,7 @@ func waitUntilInstanceHasDesiredStatus(config *transport_tpg.Config, d *schema.R if err != nil { return fmt.Errorf( - "Error waiting for instance to reach desired status %s: %s", desiredStatus, err) + "Error waiting for instance to reach desired status %s: %s", status, err) } } @@ -1553,11 +1593,20 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err } {{- end }} - err = waitUntilInstanceHasDesiredStatus(config, d) + err = waitUntilInstanceHasDesiredStatus(config, d, "RUNNING") if err != nil { return fmt.Errorf("Error waiting for status: %s", err) } + if val, ok := d.GetOk("desired_status"); ok { + if val.(string) != "RUNNING" { + err = changeInstanceStatusOnCreation(config, d, project, zone.Name, val.(string), userAgent) + if err != nil { + return fmt.Errorf("Error changing instance status after creation: %s", err) + } + } + } + return resourceComputeInstanceRead(d, meta) } @@ -1803,6 +1852,9 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error if err := d.Set("instance_id", fmt.Sprintf("%d", instance.Id)); err != nil { return fmt.Errorf("Error setting instance_id: %s", err) } + if err := d.Set("creation_timestamp", instance.CreationTimestamp); err != nil { + return fmt.Errorf("Error setting creation_timestamp: %s", err) + } if err := d.Set("project", project); err != nil { return fmt.Errorf("Error setting project: %s", err) } @@ -1835,6 +1887,9 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error if err := d.Set("reservation_affinity", flattenReservationAffinity(instance.ReservationAffinity)); err != nil { return fmt.Errorf("Error setting reservation_affinity: %s", err) } + if err := d.Set("key_revocation_action_type", instance.KeyRevocationActionType); err != nil { + return fmt.Errorf("Error setting key_revocation_action_type: %s", err) + } d.SetId(fmt.Sprintf("projects/%s/zones/%s/instances/%s", project, zone, instance.Name)) @@ -2895,25 +2950,6 @@ func suppressEmptyGuestAcceleratorDiff(_ context.Context, d *schema.ResourceDiff return nil } -// return an error if the desired_status field is set to a value other than RUNNING on Create. -func desiredStatusDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { - // when creating an instance, name is not set - oldName, _ := diff.GetChange("name") - - if oldName == nil || oldName == "" { - _, newDesiredStatus := diff.GetChange("desired_status") - - if newDesiredStatus == nil || newDesiredStatus == "" { - return nil - } else if newDesiredStatus != "RUNNING" { - return fmt.Errorf("When creating an instance, desired_status can only accept RUNNING value") - } - return nil - } - - return nil -} - func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*transport_tpg.Config) userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager_test.go.tmpl index 5c7ad1c0f191..029ed68659b9 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager_test.go.tmpl @@ -2007,8 +2007,8 @@ resource "google_tags_tag_key" "igm-key" { resource "google_tags_tag_value" "igm-value" { description = "Terraform test tag value." - parent = "tagKeys/${google_tags_tag_key.igm-key.name}" - short_name = "%s" + parent = google_tags_tag_key.igm-key.id + short_name = "%s" } resource "google_compute_instance_group_manager" "igm-tags" { @@ -2025,7 +2025,7 @@ resource "google_compute_instance_group_manager" "igm-tags" { params { resource_manager_tags = { - "tagKeys/${google_tags_tag_key.igm-key.name}" = "tagValues/${google_tags_tag_value.igm-value.name}" + (google_tags_tag_key.igm-key.id) = google_tags_tag_value.igm-value.id } } } diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance_template.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_instance_template.go.tmpl index e16551759592..43a2063de52e 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance_template.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance_template.go.tmpl @@ -831,6 +831,13 @@ be from 0 to 999,999,999 inclusive.`, Description: `A special URI of the created resource that uniquely identifies this instance template.`, }, + "creation_timestamp": { + Type: schema.TypeString, + ForceNew: true, + Computed: true, + Description: `Creation timestamp in RFC3339 text format.`, + }, + "service_account": { Type: schema.TypeList, MaxItems: 1, @@ -1097,6 +1104,14 @@ be from 0 to 999,999,999 inclusive.`, }, }, }, + + "key_revocation_action_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"NONE", "STOP", ""}, false), + Description: `Action to be taken when a customer's encryption key is revoked. Supports "STOP" and "NONE", with "NONE" being the default.`, + }, }, UseJSONNumber: true, } @@ -1438,6 +1453,7 @@ func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interfac {{- end }} ResourcePolicies: resourcePolicies, ReservationAffinity: reservationAffinity, + KeyRevocationActionType: d.Get("key_revocation_action_type").(string), } if _, ok := d.GetOk("effective_labels"); ok { @@ -1819,6 +1835,9 @@ func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{ if err = d.Set("self_link_unique", fmt.Sprintf("%v?uniqueId=%v", instanceTemplate.SelfLink, instanceTemplate.Id)); err != nil { return fmt.Errorf("Error setting self_link_unique: %s", err) } + if err = d.Set("creation_timestamp", instanceTemplate.CreationTimestamp); err != nil { + return fmt.Errorf("Error setting creation_timestamp: %s", err) + } if err = d.Set("name", instanceTemplate.Name); err != nil { return fmt.Errorf("Error setting name: %s", err) } @@ -1848,6 +1867,9 @@ func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{ if err = d.Set("instance_description", instanceTemplate.Properties.Description); err != nil { return fmt.Errorf("Error setting instance_description: %s", err) } + if err = d.Set("key_revocation_action_type", instanceTemplate.Properties.KeyRevocationActionType); err != nil { + return fmt.Errorf("Error setting key_revocation_action_type: %s", err) + } if err = d.Set("project", project); err != nil { return fmt.Errorf("Error setting project: %s", err) } diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance_template_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_instance_template_test.go.tmpl index df076213b9e0..905dde01495b 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance_template_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance_template_test.go.tmpl @@ -52,6 +52,7 @@ func TestAccComputeInstanceTemplate_basic(t *testing.T) { testAccCheckComputeInstanceTemplateMetadata(&instanceTemplate, "foo", "bar"), testAccCheckComputeInstanceTemplateContainsLabel(&instanceTemplate, "my_label", "foobar"), testAccCheckComputeInstanceTemplateLacksShieldedVmConfig(&instanceTemplate), + resource.TestCheckResourceAttrSet("google_compute_instance_template.foobar", "creation_timestamp"), ), }, { @@ -69,13 +70,20 @@ func TestAccComputeInstanceTemplate_imageShorthand(t *testing.T) { var instanceTemplate compute.InstanceTemplate + context := map[string]interface{}{ + "template": "tf-test-instance-template-" + acctest.RandString(t, 10), + "image": "tf-test-compute-image-" + acctest.RandString(t, 10), + "bucket": "tf-test-compute-image-bucket-" + acctest.RandString(t, 10), + "disk_image_path": "./test-fixtures/raw-disk-image.tar.gz", + } + acctest.VcrTest(t, resource.TestCase{ PreCheck: func() { acctest.AccTestPreCheck(t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), CheckDestroy: testAccCheckComputeInstanceTemplateDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccComputeInstanceTemplate_imageShorthand(acctest.RandString(t, 10)), + Config: testAccComputeInstanceTemplate_imageShorthand(context), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceTemplateExists( t, "google_compute_instance_template.foobar", &instanceTemplate), @@ -1088,14 +1096,14 @@ func TestAccComputeInstanceTemplate_nictype_update(t *testing.T) { CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccComputeInstanceTemplate_nictype(instanceTemplateName, instanceTemplateName, "GVNIC"), + Config: testAccComputeInstanceTemplate_nictype(instanceTemplateName, "GVNIC"), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceTemplateExists( t, "google_compute_instance_template.foobar", &instanceTemplate), ), }, { - Config: testAccComputeInstanceTemplate_nictype(instanceTemplateName, instanceTemplateName, "VIRTIO_NET"), + Config: testAccComputeInstanceTemplate_nictype(instanceTemplateName, "VIRTIO_NET"), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceTemplateExists( t, "google_compute_instance_template.foobar", &instanceTemplate), @@ -1633,6 +1641,56 @@ func TestAccComputeInstanceTemplate_resourceManagerTags(t *testing.T) { }) } +func TestAccComputeInstanceTemplate_keyRevocationActionType(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + context_1 := map[string]interface{}{ + "instance_name": fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)), + "key_revocation_action_type": `"NONE"`, + } + context_2 := map[string]interface{}{ + "instance_name": context_1["instance_name"].(string), + "key_revocation_action_type": `"STOP"`, + } + context_3 := map[string]interface{}{ + "instance_name": context_1["instance_name"].(string), + "key_revocation_action_type": `""`, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeInstanceTemplateDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeInstanceTemplate_keyRevocationActionType(context_1), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + t, "google_compute_instance_template.foobar", &instanceTemplate), + resource.TestCheckResourceAttr("google_compute_instance_template.foobar", "key_revocation_action_type", "NONE"), + ), + }, + { + Config: testAccComputeInstanceTemplate_keyRevocationActionType(context_2), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + t, "google_compute_instance_template.foobar", &instanceTemplate), + resource.TestCheckResourceAttr("google_compute_instance_template.foobar", "key_revocation_action_type", "STOP"), + ), + }, + { + Config: testAccComputeInstanceTemplate_keyRevocationActionType(context_3), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + t, "google_compute_instance_template.foobar", &instanceTemplate), + resource.TestCheckResourceAttr("google_compute_instance_template.foobar", "key_revocation_action_type", ""), + ), + }, + }, + }) +} + func TestUnitComputeInstanceTemplate_IpCidrRangeDiffSuppress(t *testing.T) { cases := map[string]struct { Old, New string @@ -2310,14 +2368,26 @@ resource "google_compute_instance_template" "foobar" { `, suffix) } -func testAccComputeInstanceTemplate_imageShorthand(suffix string) string { - return fmt.Sprintf(` +func testAccComputeInstanceTemplate_imageShorthand(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_storage_bucket" "bucket" { + name = "%{bucket}" + location = "US" + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_object" "object" { + name = "raw-disk-image.tar.gz" + bucket = google_storage_bucket.bucket.name + source = "%{disk_image_path}" +} + resource "google_compute_image" "foobar" { - name = "tf-test-%s" + name = "%{image}" description = "description-test" family = "family-test" raw_disk { - source = "https://storage.googleapis.com/bosh-gce-raw-stemcells/bosh-stemcell-97.98-google-kvm-ubuntu-xenial-go_agent-raw-1557960142.tar.gz" + source = "https://${google_storage_bucket.bucket.name}.storage.googleapis.com/${google_storage_bucket_object.object.name}" } labels = { my-label = "my-label-value" @@ -2328,7 +2398,7 @@ resource "google_compute_image" "foobar" { } resource "google_compute_instance_template" "foobar" { - name = "tf-test-instance-template-%s" + name = "%{template}" machine_type = "e2-medium" can_ip_forward = false tags = ["foo", "bar"] @@ -2360,7 +2430,7 @@ resource "google_compute_instance_template" "foobar" { my_label = "foobar" } } -`, suffix, suffix) +`, context) } func testAccComputeInstanceTemplate_preemptible(suffix string) string { @@ -3848,25 +3918,11 @@ resource "google_compute_resource_policy" "foo" { `, suffix, policyName) } -func testAccComputeInstanceTemplate_nictype(image, instance, nictype string) string { +func testAccComputeInstanceTemplate_nictype(instance, nictype string) string { return fmt.Sprintf(` -resource "google_compute_image" "example" { - name = "%s" - raw_disk { - source = "https://storage.googleapis.com/bosh-gce-raw-stemcells/bosh-stemcell-97.98-google-kvm-ubuntu-xenial-go_agent-raw-1557960142.tar.gz" - } - - guest_os_features { - type = "SECURE_BOOT" - } - - guest_os_features { - type = "MULTI_IP_SUBNET" - } - - guest_os_features { - type = "GVNIC" - } +data "google_compute_image" "example" { + family = "debian-12" + project = "debian-cloud" } resource "google_compute_instance_template" "foobar" { @@ -3876,7 +3932,7 @@ resource "google_compute_instance_template" "foobar" { tags = ["foo", "bar"] disk { - source_image = google_compute_image.example.name + source_image = data.google_compute_image.example.self_link auto_delete = true boot = true } @@ -3903,7 +3959,7 @@ resource "google_compute_instance_template" "foobar" { my_label = "foobar" } } -`, image, instance, nictype) +`, instance, nictype) } func testAccComputeInstanceTemplate_queueCount(instanceTemplateName string) string { @@ -4381,8 +4437,8 @@ resource "google_tags_tag_key" "key" { } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "foo%{random_suffix}" + parent = google_tags_tag_key.key.id + short_name = "foo%{random_suffix}" description = "For foo resources." } @@ -4402,12 +4458,12 @@ resource "google_compute_instance_template" "foobar" { boot = true resource_manager_tags = { - "tagKeys/${google_tags_tag_key.key.name}" = "tagValues/${google_tags_tag_value.value.name}" + (google_tags_tag_key.key.id) = google_tags_tag_value.value.id } } resource_manager_tags = { - "tagKeys/${google_tags_tag_key.key.name}" = "tagValues/${google_tags_tag_value.value.name}" + (google_tags_tag_key.key.id) = google_tags_tag_value.value.id } network_interface { @@ -4452,3 +4508,30 @@ resource "google_compute_instance_template" "foobar" { `, context) } {{- end }} + +func testAccComputeInstanceTemplate_keyRevocationActionType(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_image" "my_image" { + family = "debian-11" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "foobar" { + name = "%{instance_name}" + machine_type = "e2-medium" + + disk { + source_image = data.google_compute_image.my_image.self_link + auto_delete = true + disk_size_gb = 10 + boot = true + } + + network_interface { + network = "default" + } + + key_revocation_action_type = %{key_revocation_action_type} +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.tmpl index 7832b3f5206d..640450bc7915 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.tmpl @@ -192,6 +192,7 @@ func TestAccComputeInstance_basic1(t *testing.T) { testAccCheckComputeInstanceMetadata(&instance, "baz", "qux"), testAccCheckComputeInstanceDisk(&instance, instanceName, true, true), resource.TestCheckResourceAttr("google_compute_instance.foobar", "current_status", "RUNNING"), + resource.TestCheckResourceAttrSet("google_compute_instance.foobar", "creation_timestamp"), // by default, DeletionProtection is implicitly false. This should be false on any // instance resource without an explicit deletion_protection = true declaration. @@ -1843,14 +1844,14 @@ func TestAccComputeInstance_nictype_update(t *testing.T) { CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccComputeInstance_nictype(instanceName, instanceName, "GVNIC"), + Config: testAccComputeInstance_nictype(instanceName, "GVNIC"), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( t, "google_compute_instance.foobar", &instance), ), }, { - Config: testAccComputeInstance_nictype(instanceName, instanceName, "VIRTIO_NET"), + Config: testAccComputeInstance_nictype(instanceName, "VIRTIO_NET"), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( t, "google_compute_instance.foobar", &instance), @@ -2287,27 +2288,80 @@ func TestAccComputeInstance_enableDisplay(t *testing.T) { }) } -func TestAccComputeInstance_desiredStatusOnCreation(t *testing.T) { +func TestAccComputeInstance_desiredStatusTerminatedOnCreation(t *testing.T) { t.Parallel() var instance compute.Instance - var instanceName = fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)) + + context_1 := map[string]interface{}{ + "instance_name": fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)), + "zone": "us-central1-a", + "desired_status": "TERMINATED", + } + + context_2 := map[string]interface{}{ + "instance_name": context_1["instance_name"], + "zone": context_1["zone"], + "desired_status": "RUNNING", + } acctest.VcrTest(t, resource.TestCase{ - PreCheck: func() { acctest.AccTestPreCheck(t) }, + PreCheck: func() { acctest.AccTestPreCheck(t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), - CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t), + CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "e2-medium", "TERMINATED", false), - ExpectError: regexp.MustCompile("When creating an instance, desired_status can only accept RUNNING value"), + Config: testAccComputeInstance_desiredStatusOnCreation(context_1), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists(t, "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatus(&instance, context_1["desired_status"].(string)), + ), }, { - Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "e2-medium", "RUNNING", false), + Config: testAccComputeInstance_desiredStatusOnCreation(context_2), Check: resource.ComposeTestCheckFunc( - testAccCheckComputeInstanceExists( - t, "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), + testAccCheckComputeInstanceExists(t, "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatus(&instance, context_2["desired_status"].(string)), + ), + }, + }, + }) +} + +func TestAccComputeInstance_desiredStatusSuspendedOnCreation(t *testing.T) { + t.Parallel() + + var instance compute.Instance + + context_1 := map[string]interface{}{ + "instance_name": fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)), + "zone": "us-central1-a", + "desired_status": "SUSPENDED", + } + + context_2 := map[string]interface{}{ + "instance_name": context_1["instance_name"], + "zone": context_1["zone"], + "desired_status": "RUNNING", + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusOnCreation(context_1), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists(t, "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatus(&instance, context_1["desired_status"].(string)), + ), + }, + { + Config: testAccComputeInstance_desiredStatusOnCreation(context_2), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists(t, "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatus(&instance, context_2["desired_status"].(string)), ), }, }, @@ -3531,6 +3585,56 @@ func TestAccComputeInstance_proactiveAttributionLabel(t *testing.T) { }) } +func TestAccComputeInstance_keyRevocationActionType(t *testing.T) { + t.Parallel() + + var instance compute.Instance + context_1 := map[string]interface{}{ + "instance_name": fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)), + "key_revocation_action_type": `"NONE"`, + } + context_2 := map[string]interface{}{ + "instance_name": context_1["instance_name"].(string), + "key_revocation_action_type": `"STOP"`, + } + context_3 := map[string]interface{}{ + "instance_name": context_1["instance_name"].(string), + "key_revocation_action_type": `""`, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_keyRevocationActionType(context_1), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + t, "google_compute_instance.foobar", &instance), + resource.TestCheckResourceAttr("google_compute_instance.foobar", "key_revocation_action_type", "NONE"), + ), + }, + { + Config: testAccComputeInstance_keyRevocationActionType(context_2), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + t, "google_compute_instance.foobar", &instance), + resource.TestCheckResourceAttr("google_compute_instance.foobar", "key_revocation_action_type", "STOP"), + ), + }, + { + Config: testAccComputeInstance_keyRevocationActionType(context_3), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + t, "google_compute_instance.foobar", &instance), + resource.TestCheckResourceAttr("google_compute_instance.foobar", "key_revocation_action_type", ""), + ), + }, + }, + }) +} + {{ if ne $.TargetVersionName `ga` -}} const errorDeleteAccessConfigWithSecPolicy = "Cannot delete an access config with a security policy set. Please remove the security policy first" @@ -5086,8 +5190,8 @@ resource "google_tags_tag_key" "key" { } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "foo%{random_suffix}" + parent = google_tags_tag_key.key.id + short_name = "foo%{random_suffix}" description = "For foo resources." } @@ -5105,14 +5209,14 @@ resource "google_compute_instance" "foobar" { initialize_params { image = data.google_compute_image.my_image.self_link resource_manager_tags = { - "tagKeys/${google_tags_tag_key.key.name}" = "tagValues/${google_tags_tag_value.value.name}" + (google_tags_tag_key.key.id) = google_tags_tag_value.value.id } } } params { resource_manager_tags = { - "tagKeys/${google_tags_tag_key.key.name}" = "tagValues/${google_tags_tag_value.value.name}" + (google_tags_tag_key.key.id) = google_tags_tag_value.value.id } } @@ -5132,8 +5236,8 @@ resource "google_tags_tag_key" "key" { } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "foo%{random_suffix}" + parent = google_tags_tag_key.key.id + short_name = "foo%{random_suffix}" description = "For foo resources." } @@ -5144,8 +5248,8 @@ resource "google_tags_tag_key" "key_new" { } resource "google_tags_tag_value" "value_new" { - parent = "tagKeys/${google_tags_tag_key.key_new.name}" - short_name = "foonew%{random_suffix}" + parent = google_tags_tag_key.key_new.id + short_name = "foonew%{random_suffix}" description = "New value for foo resources." } @@ -5163,15 +5267,15 @@ resource "google_compute_instance" "foobar" { initialize_params { image = data.google_compute_image.my_image.self_link resource_manager_tags = { - "tagKeys/${google_tags_tag_key.key.name}" = "tagValues/${google_tags_tag_value.value.name}" + (google_tags_tag_key.key.id) = google_tags_tag_value.value.id } } } params { resource_manager_tags = { - "tagKeys/${google_tags_tag_key.key.name}" = "tagValues/${google_tags_tag_value.value.name}" - "tagKeys/${google_tags_tag_key.key_new.name}" = "tagValues/${google_tags_tag_value.value_new.name}" + (google_tags_tag_key.key.id) = google_tags_tag_value.value.id + (google_tags_tag_key.key_new.id) = google_tags_tag_value.value_new.id } } @@ -7471,25 +7575,11 @@ resource "google_compute_subnetwork" "inst-test-subnetwork" { `, instance, network, subnetwork) } -func testAccComputeInstance_nictype(image, instance, nictype string) string { +func testAccComputeInstance_nictype(instance, nictype string) string { return fmt.Sprintf(` -resource "google_compute_image" "example" { - name = "%s" - raw_disk { - source = "https://storage.googleapis.com/bosh-gce-raw-stemcells/bosh-stemcell-97.98-google-kvm-ubuntu-xenial-go_agent-raw-1557960142.tar.gz" - } - - guest_os_features { - type = "SECURE_BOOT" - } - - guest_os_features { - type = "MULTI_IP_SUBNET" - } - - guest_os_features { - type = "GVNIC" - } +data "google_compute_image" "example" { + family = "debian-12" + project = "debian-cloud" } resource "google_compute_instance" "foobar" { @@ -7503,7 +7593,7 @@ resource "google_compute_instance" "foobar" { boot_disk { initialize_params { - image = google_compute_image.example.id + image = data.google_compute_image.example.self_link } } @@ -7523,7 +7613,7 @@ resource "google_compute_instance" "foobar" { my_other_key = "my_other_value" } } -`, image, instance, nictype) +`, instance, nictype) } func testAccComputeInstance_guestAccelerator(instance string, count uint8) string { @@ -8937,6 +9027,33 @@ resource "google_compute_instance" "foobar" { `, instance) } +func testAccComputeInstance_desiredStatusOnCreation(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_image" "my_image" { + family = "debian-11" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%{instance_name}" + machine_type = "e2-medium" + zone = "%{zone}" + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + desired_status = "%{desired_status}" +} +`, context) +} + func testAccComputeInstance_resourcePolicyCollocate(instance, suffix string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { @@ -10837,3 +10954,30 @@ resource "google_compute_instance" "foobar" { } `, diskName, instanceName, machineType, zone, bootDiskInterface, allowStoppingForUpdate) } + +func testAccComputeInstance_keyRevocationActionType(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_image" "my_image" { + family = "debian-11" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%{instance_name}" + machine_type = "e2-medium" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = data.google_compute_image.my_image.self_link + } + } + + network_interface { + network = "default" + } + + key_revocation_action_type = %{key_revocation_action_type} +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_network_firewall_policy_rule_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_network_firewall_policy_rule_test.go.tmpl index ed0ddf7f3525..4d4656289939 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_network_firewall_policy_rule_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_network_firewall_policy_rule_test.go.tmpl @@ -86,6 +86,7 @@ func TestAccComputeNetworkFirewallPolicyRule_multipleRules(t *testing.T) { context := map[string]interface{}{ "random_suffix": acctest.RandString(t, 10), + "project_name": envvar.GetTestProjectFromEnv(), "org_name": fmt.Sprintf("organizations/%s", envvar.GetTestOrgFromEnv(t)), } @@ -261,7 +262,7 @@ resource "google_compute_network_firewall_policy_rule" "primary" { src_threat_intelligences = ["iplist-known-malicious-ips"] src_secure_tags { - name = "tagValues/${google_tags_tag_value.basic_value.name}" + name = google_tags_tag_value.basic_value.id } layer4_configs { @@ -289,7 +290,7 @@ resource "google_tags_tag_key" "basic_key" { resource "google_tags_tag_value" "basic_value" { description = "For valuename resources." - parent = "tagKeys/${google_tags_tag_key.basic_key.name}" + parent = google_tags_tag_key.basic_key.id short_name = "tf-test-tagvalue-%{random_suffix}" } `, context) @@ -339,7 +340,7 @@ resource "google_compute_network_firewall_policy_rule" "primary" { } target_secure_tags { - name = "tagValues/${google_tags_tag_value.basic_value.name}" + name = google_tags_tag_value.basic_value.id } } @@ -360,7 +361,7 @@ resource "google_tags_tag_key" "basic_key" { resource "google_tags_tag_value" "basic_value" { description = "For valuename resources." - parent = "tagKeys/${google_tags_tag_key.basic_key.name}" + parent = google_tags_tag_key.basic_key.id short_name = "tf-test-tagvalue-%{random_suffix}" } `, context) @@ -731,11 +732,23 @@ resource "google_compute_network_firewall_policy_rule" "fw_policy_rule2" { func testAccComputeNetworkFirewallPolicyRule_multipleAdd(context map[string]interface{}) string { return acctest.Nprintf(` +resource "google_compute_network" "network1" { + name = "tf-test-%{random_suffix}" + auto_create_subnetworks = false +} + resource "google_compute_network_firewall_policy" "fw_policy" { name = "tf-test-policy-%{random_suffix}" description = "Resource created for Terraform acceptance testing" } +resource "google_compute_network_firewall_policy_association" "fw_policy_a" { + name = "tf-test-policy-a-%{random_suffix}" + project = "projects/%{project_name}" + attachment_target = google_compute_network.network1.id + firewall_policy = google_compute_network_firewall_policy.fw_policy.id +} + resource "google_network_security_address_group" "address_group" { name = "tf-test-policy%{random_suffix}" parent = "%{org_name}" @@ -815,11 +828,23 @@ resource "google_compute_network_firewall_policy_rule" "fw_policy_rule3" { func testAccComputeNetworkFirewallPolicyRule_multipleRemove(context map[string]interface{}) string { return acctest.Nprintf(` +resource "google_compute_network" "network1" { + name = "tf-test-%{random_suffix}" + auto_create_subnetworks = false +} + resource "google_compute_network_firewall_policy" "fw_policy" { name = "tf-test-policy-%{random_suffix}" description = "Resource created for Terraform acceptance testing" } +resource "google_compute_network_firewall_policy_association" "fw_policy_a" { + name = "tf-test-policy-a-%{random_suffix}" + project = "%{project_name}" + attachment_target = google_compute_network.network1.id + firewall_policy = google_compute_network_firewall_policy.fw_policy.id +} + resource "google_network_security_address_group" "address_group" { name = "tf-test-policy%{random_suffix}" parent = "%{org_name}" diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_network_firewall_policy_with_rules_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_network_firewall_policy_with_rules_test.go.tmpl index 35bf3fae4357..0fddf58aad55 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_network_firewall_policy_with_rules_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_network_firewall_policy_with_rules_test.go.tmpl @@ -71,7 +71,7 @@ resource "google_compute_network_firewall_policy_with_rules" "network-firewall-p dest_address_groups = [google_network_security_address_group.address_group_1.id] } target_secure_tag { - name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + name = google_tags_tag_value.secure_tag_value_1.id } } rule { @@ -90,7 +90,7 @@ resource "google_compute_network_firewall_policy_with_rules" "network-firewall-p src_threat_intelligences = ["iplist-known-malicious-ips", "iplist-public-clouds"] src_address_groups = [google_network_security_address_group.address_group_1.id] src_secure_tag { - name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + name = google_tags_tag_value.secure_tag_value_1.id } } disabled = true @@ -139,7 +139,7 @@ resource "google_tags_tag_key" "secure_tag_key_1" { resource "google_tags_tag_value" "secure_tag_value_1" { provider = google-beta description = "Tag value" - parent = "tagKeys/${google_tags_tag_key.secure_tag_key_1.name}" + parent = google_tags_tag_key.secure_tag_key_1.id short_name = "tf-test-tf-tag-value%{random_suffix}" } @@ -205,7 +205,7 @@ resource "google_compute_network_firewall_policy_with_rules" "network-firewall-p src_threat_intelligences = ["iplist-public-clouds"] src_address_groups = [google_network_security_address_group.address_group_1.id] src_secure_tag { - name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + name = google_tags_tag_value.secure_tag_value_1.id } } disabled = false @@ -237,7 +237,7 @@ resource "google_tags_tag_key" "secure_tag_key_1" { resource "google_tags_tag_value" "secure_tag_value_1" { provider = google-beta description = "Tag value" - parent = "tagKeys/${google_tags_tag_key.secure_tag_key_1.name}" + parent = google_tags_tag_key.secure_tag_key_1.id short_name = "tf-test-tf-tag-value%{random_suffix}" } diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager.go.tmpl index 1889ca104bfb..3c036f3a1e86 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager.go.tmpl @@ -633,7 +633,7 @@ func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, met UpdatePolicy: expandRegionUpdatePolicy(d.Get("update_policy").([]interface{})), InstanceLifecyclePolicy: expandInstanceLifecyclePolicy(d.Get("instance_lifecycle_policy").([]interface{})), AllInstancesConfig: expandAllInstancesConfig(nil, d.Get("all_instances_config").([]interface{})), - DistributionPolicy: expandDistributionPolicy(d), + DistributionPolicy: expandDistributionPolicyForCreate(d), StatefulPolicy: expandStatefulPolicy(d), {{- if ne $.TargetVersionName "ga" }} Params: expandInstanceGroupManagerParams(d), @@ -907,7 +907,7 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met } if d.HasChange("distribution_policy_target_shape") { - updatedManager.DistributionPolicy = expandDistributionPolicy(d) + updatedManager.DistributionPolicy = expandDistributionPolicyForUpdate(d) change = true } @@ -1145,24 +1145,39 @@ func flattenRegionUpdatePolicy(updatePolicy *compute.InstanceGroupManagerUpdateP return results } -func expandDistributionPolicy(d *schema.ResourceData) *compute.DistributionPolicy { - dpz := d.Get("distribution_policy_zones").(*schema.Set) +func expandDistributionPolicyForUpdate(d *schema.ResourceData) *compute.DistributionPolicy { dpts := d.Get("distribution_policy_target_shape").(string) - if dpz.Len() == 0 && dpts == "" { + if dpts == "" { return nil } + // distributionPolicy.Zones is NOT updateable. + return &compute.DistributionPolicy{TargetShape: dpts} +} - distributionPolicyZoneConfigs := make([]*compute.DistributionPolicyZoneConfiguration, 0, dpz.Len()) - for _, raw := range dpz.List() { - data := raw.(string) - distributionPolicyZoneConfig := compute.DistributionPolicyZoneConfiguration{ - Zone: "zones/" + data, - } - - distributionPolicyZoneConfigs = append(distributionPolicyZoneConfigs, &distributionPolicyZoneConfig) +func expandDistributionPolicyForCreate(d *schema.ResourceData) *compute.DistributionPolicy { + dpz := d.Get("distribution_policy_zones").(*schema.Set) + dpts := d.Get("distribution_policy_target_shape").(string) + if dpz.Len() == 0 && dpts == "" { + return nil } - - return &compute.DistributionPolicy{Zones: distributionPolicyZoneConfigs, TargetShape: dpts} + distributionPolicy := &compute.DistributionPolicy{} + + if dpz.Len() > 0 { + distributionPolicyZoneConfigs := make([]*compute.DistributionPolicyZoneConfiguration, 0, dpz.Len()) + for _, raw := range dpz.List() { + data := raw.(string) + distributionPolicyZoneConfig := compute.DistributionPolicyZoneConfiguration{ + Zone: "zones/" + data, + } + + distributionPolicyZoneConfigs = append(distributionPolicyZoneConfigs, &distributionPolicyZoneConfig) + } + distributionPolicy.Zones=distributionPolicyZoneConfigs + } + if dpts != "" { + distributionPolicy.TargetShape = dpts + } + return distributionPolicy } func flattenDistributionPolicy(distributionPolicy *compute.DistributionPolicy) []string { diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager_test.go.tmpl index 680a4a629675..2b7571e2c915 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager_test.go.tmpl @@ -312,7 +312,16 @@ func TestAccRegionInstanceGroupManager_distributionPolicy(t *testing.T) { CheckDestroy: testAccCheckRegionInstanceGroupManagerDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccRegionInstanceGroupManager_distributionPolicy(template, igm, zones), + Config: testAccRegionInstanceGroupManager_distributionPolicyEmpty(template, igm), + }, + { + ResourceName: "google_compute_region_instance_group_manager.igm-basic", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"status"}, + }, + { + Config: testAccRegionInstanceGroupManager_distributionPolicy(template, igm), }, { ResourceName: "google_compute_region_instance_group_manager.igm-basic", @@ -329,6 +338,15 @@ func TestAccRegionInstanceGroupManager_distributionPolicy(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{"status"}, }, + { + Config: testAccRegionInstanceGroupManager_distributionPolicyEmpty(template, igm), + }, + { + ResourceName: "google_compute_region_instance_group_manager.igm-basic", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"status"}, + }, }, }) } @@ -1135,7 +1153,7 @@ resource "google_compute_region_instance_group_manager" "igm-basic" { `, primaryTemplate, canaryTemplate, igm) } -func testAccRegionInstanceGroupManager_distributionPolicy(template, igm string, zones []string) string { +func testAccRegionInstanceGroupManager_distributionPolicyEmpty(template, igm string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { family = "debian-11" @@ -1169,8 +1187,6 @@ resource "google_compute_region_instance_group_manager" "igm-basic" { base_instance_name = "tf-test-igm-basic" region = "us-central1" target_size = 2 - distribution_policy_zones = ["%s"] - distribution_policy_target_shape = "ANY" update_policy { instance_redistribution_type = "NONE" @@ -1180,7 +1196,54 @@ resource "google_compute_region_instance_group_manager" "igm-basic" { max_unavailable_fixed = 6 } } -`, template, igm, strings.Join(zones, "\",\"")) +`, template, igm) +} + +func testAccRegionInstanceGroupManager_distributionPolicy(template, igm string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-11" + project = "debian-cloud" +} + +resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "e2-medium" + can_ip_forward = false + tags = ["foo", "bar"] + disk { + source_image = data.google_compute_image.my_image.self_link + auto_delete = true + boot = true + } + network_interface { + network = "default" + } +} + +resource "google_compute_region_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + + version { + instance_template = google_compute_instance_template.igm-basic.self_link + name = "primary" + } + + base_instance_name = "tf-test-igm-basic" + region = "us-central1" + target_size = 2 + distribution_policy_target_shape = "BALANCED" + + update_policy { + instance_redistribution_type = "NONE" + type = "OPPORTUNISTIC" + minimal_action = "REPLACE" + max_surge_fixed = 0 + max_unavailable_fixed = 6 + } +} +`, template, igm) } func testAccRegionInstanceGroupManager_distributionPolicyUpdate(template, igm string, zones []string) string { @@ -1892,8 +1955,8 @@ resource "google_tags_tag_key" "rigm-key" { resource "google_tags_tag_value" "rigm-value" { description = "Terraform test tag value." - parent = "tagKeys/${google_tags_tag_key.rigm-key.name}" - short_name = "%s" + parent = google_tags_tag_key.rigm-key.id + short_name = "%s" } resource "google_compute_region_instance_group_manager" "rigm-tags" { @@ -1910,7 +1973,7 @@ resource "google_compute_region_instance_group_manager" "rigm-tags" { params { resource_manager_tags = { - "tagKeys/${google_tags_tag_key.rigm-key.name}" = "tagValues/${google_tags_tag_value.rigm-value.name}" + (google_tags_tag_key.rigm-key.id) = google_tags_tag_value.rigm-value.id } } } diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_template.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_template.go.tmpl index 0dbcead3a2d7..b59f4c5f2a80 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_template.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_template.go.tmpl @@ -783,6 +783,13 @@ be from 0 to 999,999,999 inclusive.`, Description: `The URI of the created resource.`, }, + "creation_timestamp": { + Type: schema.TypeString, + Computed: true, + ForceNew: true, + Description: `The time at which the instance was created in RFC 3339 format.`, + }, + "service_account": { Type: schema.TypeList, MaxItems: 1, @@ -1049,6 +1056,14 @@ be from 0 to 999,999,999 inclusive.`, }, }, }, + + "key_revocation_action_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"NONE", "STOP", ""}, false), + Description: `Action to be taken when a customer's encryption key is revoked. Supports "STOP" and "NONE", with "NONE" being the default.`, + }, }, UseJSONNumber: true, } @@ -1131,6 +1146,7 @@ func resourceComputeRegionInstanceTemplateCreate(d *schema.ResourceData, meta in {{- end }} ResourcePolicies: resourcePolicies, ReservationAffinity: reservationAffinity, + KeyRevocationActionType: d.Get("key_revocation_action_type").(string), } if _, ok := d.GetOk("effective_labels"); ok { @@ -1306,6 +1322,9 @@ func resourceComputeRegionInstanceTemplateRead(d *schema.ResourceData, meta inte if err = d.Set("self_link", instanceTemplate["selfLink"]); err != nil { return fmt.Errorf("Error setting self_link: %s", err) } + if err := d.Set("creation_timestamp", instanceTemplate["creationTimestamp"]); err != nil { + return fmt.Errorf("Error setting creation_timestamp: %s", err) + } if err = d.Set("name", instanceTemplate["name"]); err != nil { return fmt.Errorf("Error setting name: %s", err) } @@ -1335,6 +1354,9 @@ func resourceComputeRegionInstanceTemplateRead(d *schema.ResourceData, meta inte if err = d.Set("instance_description", instanceProperties.Description); err != nil { return fmt.Errorf("Error setting instance_description: %s", err) } + if err = d.Set("key_revocation_action_type", instanceProperties.KeyRevocationActionType); err != nil { + return fmt.Errorf("Error setting key_revocation_action_type: %s", err) + } if err = d.Set("project", project); err != nil { return fmt.Errorf("Error setting project: %s", err) } diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_template_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_template_test.go.tmpl index b6c7e832b2ae..f8cd2bd6c7d9 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_template_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_template_test.go.tmpl @@ -47,6 +47,7 @@ func TestAccComputeRegionInstanceTemplate_basic(t *testing.T) { testAccCheckComputeRegionInstanceTemplateMetadata(&instanceTemplate, "foo", "bar"), testAccCheckComputeRegionInstanceTemplateContainsLabel(&instanceTemplate, "my_label", "foobar"), testAccCheckComputeRegionInstanceTemplateLacksShieldedVmConfig(&instanceTemplate), + resource.TestCheckResourceAttrSet("google_compute_region_instance_template.foobar", "creation_timestamp"), ), }, { @@ -64,13 +65,20 @@ func TestAccComputeRegionInstanceTemplate_imageShorthand(t *testing.T) { var instanceTemplate compute.InstanceTemplate + context := map[string]interface{}{ + "template": "tf-test-instance-template-" + acctest.RandString(t, 10), + "image": "tf-test-compute-image-" + acctest.RandString(t, 10), + "bucket": "tf-test-compute-image-bucket-" + acctest.RandString(t, 10), + "disk_image_path": "./test-fixtures/raw-disk-image.tar.gz", + } + acctest.VcrTest(t, resource.TestCase{ PreCheck: func() { acctest.AccTestPreCheck(t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), CheckDestroy: testAccCheckComputeRegionInstanceTemplateDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccComputeRegionInstanceTemplate_imageShorthand(acctest.RandString(t, 10)), + Config: testAccComputeRegionInstanceTemplate_imageShorthand(context), Check: resource.ComposeTestCheckFunc( testAccCheckComputeRegionInstanceTemplateExists( t, "google_compute_region_instance_template.foobar", &instanceTemplate), @@ -957,14 +965,14 @@ func TestAccComputeRegionInstanceTemplate_nictype_update(t *testing.T) { CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccComputeRegionInstanceTemplate_nictype(instanceTemplateName, instanceTemplateName, "GVNIC"), + Config: testAccComputeRegionInstanceTemplate_nictype(instanceTemplateName, "GVNIC"), Check: resource.ComposeTestCheckFunc( testAccCheckComputeRegionInstanceTemplateExists( t, "google_compute_region_instance_template.foobar", &instanceTemplate), ), }, { - Config: testAccComputeRegionInstanceTemplate_nictype(instanceTemplateName, instanceTemplateName, "VIRTIO_NET"), + Config: testAccComputeRegionInstanceTemplate_nictype(instanceTemplateName, "VIRTIO_NET"), Check: resource.ComposeTestCheckFunc( testAccCheckComputeRegionInstanceTemplateExists( t, "google_compute_region_instance_template.foobar", &instanceTemplate), @@ -1281,6 +1289,56 @@ func TestAccComputeRegionInstanceTemplate_resourceManagerTags(t *testing.T) { }) } +func TestAccComputeRegionInstanceTemplate_keyRevocationActionType(t *testing.T) { + t.Parallel() + + var instanceTemplate compute.InstanceTemplate + context_1 := map[string]interface{}{ + "instance_name": fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)), + "key_revocation_action_type": `"NONE"`, + } + context_2 := map[string]interface{}{ + "instance_name": context_1["instance_name"].(string), + "key_revocation_action_type": `"STOP"`, + } + context_3 := map[string]interface{}{ + "instance_name": context_1["instance_name"].(string), + "key_revocation_action_type": `""`, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeRegionInstanceTemplateDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionInstanceTemplate_keyRevocationActionType(context_1), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionInstanceTemplateExists( + t, "google_compute_region_instance_template.foobar", &instanceTemplate), + resource.TestCheckResourceAttr("google_compute_region_instance_template.foobar", "key_revocation_action_type", "NONE"), + ), + }, + { + Config: testAccComputeRegionInstanceTemplate_keyRevocationActionType(context_2), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionInstanceTemplateExists( + t, "google_compute_region_instance_template.foobar", &instanceTemplate), + resource.TestCheckResourceAttr("google_compute_region_instance_template.foobar", "key_revocation_action_type", "STOP"), + ), + }, + { + Config: testAccComputeRegionInstanceTemplate_keyRevocationActionType(context_3), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeRegionInstanceTemplateExists( + t, "google_compute_region_instance_template.foobar", &instanceTemplate), + resource.TestCheckResourceAttr("google_compute_region_instance_template.foobar", "key_revocation_action_type", ""), + ), + }, + }, + }) +} + func testAccCheckComputeRegionInstanceTemplateDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { config := acctest.GoogleProviderConfig(t) @@ -1823,14 +1881,26 @@ resource "google_compute_region_instance_template" "foobar" { `, suffix) } -func testAccComputeRegionInstanceTemplate_imageShorthand(suffix string) string { - return fmt.Sprintf(` +func testAccComputeRegionInstanceTemplate_imageShorthand(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_storage_bucket" "bucket" { + name = "%{bucket}" + location = "US" + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_object" "object" { + name = "raw-disk-image.tar.gz" + bucket = google_storage_bucket.bucket.name + source = "%{disk_image_path}" +} + resource "google_compute_image" "foobar" { - name = "tf-test-%s" + name = "%{image}" description = "description-test" family = "family-test" raw_disk { - source = "https://storage.googleapis.com/bosh-gce-raw-stemcells/bosh-stemcell-97.98-google-kvm-ubuntu-xenial-go_agent-raw-1557960142.tar.gz" + source = "https://${google_storage_bucket.bucket.name}.storage.googleapis.com/${google_storage_bucket_object.object.name}" } labels = { my-label = "my-label-value" @@ -1841,7 +1911,7 @@ resource "google_compute_image" "foobar" { } resource "google_compute_region_instance_template" "foobar" { - name = "tf-test-instance-template-%s" + name = "%{template}" region = "us-central1" machine_type = "e2-medium" can_ip_forward = false @@ -1874,7 +1944,7 @@ resource "google_compute_region_instance_template" "foobar" { my_label = "foobar" } } -`, suffix, suffix) +`, context) } func testAccComputeRegionInstanceTemplate_preemptible(suffix string) string { @@ -3239,25 +3309,11 @@ resource "google_compute_resource_policy" "foo" { `, suffix, policyName) } -func testAccComputeRegionInstanceTemplate_nictype(image, instance, nictype string) string { +func testAccComputeRegionInstanceTemplate_nictype(instance, nictype string) string { return fmt.Sprintf(` -resource "google_compute_image" "example" { - name = "%s" - raw_disk { - source = "https://storage.googleapis.com/bosh-gce-raw-stemcells/bosh-stemcell-97.98-google-kvm-ubuntu-xenial-go_agent-raw-1557960142.tar.gz" - } - - guest_os_features { - type = "SECURE_BOOT" - } - - guest_os_features { - type = "MULTI_IP_SUBNET" - } - - guest_os_features { - type = "GVNIC" - } +data "google_compute_image" "example" { + family = "debian-12" + project = "debian-cloud" } resource "google_compute_region_instance_template" "foobar" { @@ -3268,7 +3324,7 @@ resource "google_compute_region_instance_template" "foobar" { tags = ["foo", "bar"] disk { - source_image = google_compute_image.example.name + source_image = data.google_compute_image.example.self_link auto_delete = true boot = true } @@ -3295,7 +3351,7 @@ resource "google_compute_region_instance_template" "foobar" { my_label = "foobar" } } -`, image, instance, nictype) +`, instance, nictype) } func testAccComputeRegionInstanceTemplate_queueCount(instanceTemplateName string) string { @@ -3781,7 +3837,7 @@ resource "google_tags_tag_key" "key" { } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" + parent = google_tags_tag_key.key.id short_name = "foo%{random_suffix}" description = "For foo resources." } @@ -3817,3 +3873,31 @@ resource "google_compute_region_instance_template" "foobar" { } `, context) } + +func testAccComputeRegionInstanceTemplate_keyRevocationActionType(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_image" "my_image" { + family = "debian-11" + project = "debian-cloud" +} + +resource "google_compute_region_instance_template" "foobar" { + name = "%{instance_name}" + machine_type = "e2-medium" + region = "us-central1" + + disk { + source_image = data.google_compute_image.my_image.self_link + auto_delete = true + disk_size_gb = 10 + boot = true + } + + network_interface { + network = "default" + } + + key_revocation_action_type = %{key_revocation_action_type} +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_region_network_firewall_policy_rule_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_region_network_firewall_policy_rule_test.go.tmpl index c6edf183b756..3e157ac6b7e3 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_region_network_firewall_policy_rule_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_region_network_firewall_policy_rule_test.go.tmpl @@ -231,7 +231,7 @@ resource "google_compute_region_network_firewall_policy_rule" "primary" { } src_secure_tags { - name = "tagValues/${google_tags_tag_value.basic_value.name}" + name = google_tags_tag_value.basic_value.id } src_address_groups = [google_network_security_address_group.basic_regional_networksecurity_address_group.id] @@ -256,7 +256,7 @@ resource "google_tags_tag_key" "basic_key" { resource "google_tags_tag_value" "basic_value" { description = "For valuename resources." - parent = "tagKeys/${google_tags_tag_key.basic_key.name}" + parent = google_tags_tag_key.basic_key.id short_name = "tf-test-tagvalue-%{random_suffix}" } @@ -309,7 +309,7 @@ resource "google_compute_region_network_firewall_policy_rule" "primary" { } target_secure_tags { - name = "tagValues/${google_tags_tag_value.basic_value.name}" + name = google_tags_tag_value.basic_value.id } } @@ -331,7 +331,7 @@ resource "google_tags_tag_key" "basic_key" { resource "google_tags_tag_value" "basic_value" { description = "For valuename resources." - parent = "tagKeys/${google_tags_tag_key.basic_key.name}" + parent = google_tags_tag_key.basic_key.id short_name = "tf-test-tagvalue-%{random_suffix}" } diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_region_network_firewall_policy_with_rules_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_region_network_firewall_policy_with_rules_test.go.tmpl index 28eb6d172d99..9adc5d300f29 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_region_network_firewall_policy_with_rules_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_region_network_firewall_policy_with_rules_test.go.tmpl @@ -72,7 +72,7 @@ resource "google_compute_region_network_firewall_policy_with_rules" "region-netw dest_address_groups = [google_network_security_address_group.address_group_1.id] } target_secure_tag { - name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + name = google_tags_tag_value.secure_tag_value_1.id } } rule { @@ -92,7 +92,7 @@ resource "google_compute_region_network_firewall_policy_with_rules" "region-netw src_threat_intelligences = ["iplist-known-malicious-ips", "iplist-public-clouds"] src_address_groups = [google_network_security_address_group.address_group_1.id] src_secure_tag { - name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + name = google_tags_tag_value.secure_tag_value_1.id } } disabled = true @@ -124,7 +124,7 @@ resource "google_tags_tag_key" "secure_tag_key_1" { resource "google_tags_tag_value" "secure_tag_value_1" { provider = google-beta description = "Tag value" - parent = "tagKeys/${google_tags_tag_key.secure_tag_key_1.name}" + parent = google_tags_tag_key.secure_tag_key_1.id short_name = "tf-test-tf-tag-value%{random_suffix}" } `, context) @@ -172,7 +172,7 @@ resource "google_compute_region_network_firewall_policy_with_rules" "region-netw src_threat_intelligences = ["iplist-public-clouds"] src_address_groups = [google_network_security_address_group.address_group_1.id] src_secure_tag { - name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + name = google_tags_tag_value.secure_tag_value_1.id } } disabled = false @@ -204,7 +204,7 @@ resource "google_tags_tag_key" "secure_tag_key_1" { resource "google_tags_tag_value" "secure_tag_value_1" { provider = google-beta description = "Tag value" - parent = "tagKeys/${google_tags_tag_key.secure_tag_key_1.name}" + parent = google_tags_tag_key.secure_tag_key_1.id short_name = "tf-test-tf-tag-value%{random_suffix}" } `, context) diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_service_attachment_test.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_service_attachment_test.go.tmpl index b8dd4990c9d7..a1d62492a635 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_service_attachment_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_service_attachment_test.go.tmpl @@ -50,7 +50,6 @@ func TestAccComputeServiceAttachment_serviceAttachmentBasicExampleUpdate(t *test }) } -{{ if ne $.TargetVersionName `ga` -}} func TestAccComputeServiceAttachment_serviceAttachmentConnectedEndpointsOutput(t *testing.T) { t.Parallel() @@ -60,7 +59,7 @@ func TestAccComputeServiceAttachment_serviceAttachmentConnectedEndpointsOutput(t acctest.VcrTest(t, resource.TestCase{ PreCheck: func() { acctest.AccTestPreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), CheckDestroy: testAccCheckComputeServiceAttachmentDestroyProducer(t), Steps: []resource.TestStep{ { @@ -96,7 +95,6 @@ func TestAccComputeServiceAttachment_serviceAttachmentConnectedEndpointsOutput(t }, }) } -{{- end }} func testAccComputeServiceAttachment_serviceAttachmentBasicExampleFork(context map[string]interface{}) string { return acctest.Nprintf(` @@ -280,7 +278,6 @@ resource "google_compute_subnetwork" "psc_ilb_nat" { `, context) } -{{ if ne $.TargetVersionName `ga` -}} func testAccComputeServiceAttachment_serviceAttachmentBasicExampleConnectedEndpointsOutput(context map[string]interface{}, preventDestroy bool) string { context["lifecycle_block"] = "" if preventDestroy { @@ -292,7 +289,6 @@ func testAccComputeServiceAttachment_serviceAttachmentBasicExampleConnectedEndpo return acctest.Nprintf(` resource "google_compute_service_attachment" "psc_ilb_service_attachment" { - provider = google-beta name = "tf-test-my-psc-ilb%{random_suffix}" region = "us-west2" description = "A service attachment configured with Terraforms" @@ -313,7 +309,6 @@ resource "google_compute_service_attachment" "psc_ilb_service_attachment" { } resource "google_compute_address" "psc_ilb_consumer_address" { - provider = google-beta name = "tf-test-psc-ilb-consumer-address%{random_suffix}" region = "us-west2" @@ -322,7 +317,6 @@ resource "google_compute_address" "psc_ilb_consumer_address" { } resource "google_compute_forwarding_rule" "psc_ilb_consumer" { - provider = google-beta name = "tf-test-psc-ilb-consumer-forwarding-rule%{random_suffix}" region = "us-west2" @@ -333,7 +327,6 @@ resource "google_compute_forwarding_rule" "psc_ilb_consumer" { } resource "google_compute_forwarding_rule" "psc_ilb_target_service" { - provider = google-beta name = "tf-test-producer-forwarding-rule%{random_suffix}" region = "us-west2" @@ -346,7 +339,6 @@ resource "google_compute_forwarding_rule" "psc_ilb_target_service" { } resource "google_compute_region_backend_service" "producer_service_backend" { - provider = google-beta name = "tf-test-producer-service%{random_suffix}" region = "us-west2" @@ -354,7 +346,6 @@ resource "google_compute_region_backend_service" "producer_service_backend" { } resource "google_compute_health_check" "producer_service_health_check" { - provider = google-beta name = "tf-test-producer-service-health-check%{random_suffix}" check_interval_sec = 1 @@ -365,13 +356,11 @@ resource "google_compute_health_check" "producer_service_health_check" { } resource "google_compute_network" "psc_ilb_network" { - provider = google-beta name = "tf-test-psc-ilb-network%{random_suffix}" auto_create_subnetworks = false } resource "google_compute_subnetwork" "psc_ilb_producer_subnetwork" { - provider = google-beta name = "tf-test-psc-ilb-producer-subnetwork%{random_suffix}" region = "us-west2" @@ -380,7 +369,6 @@ resource "google_compute_subnetwork" "psc_ilb_producer_subnetwork" { } resource "google_compute_subnetwork" "psc_ilb_nat" { - provider = google-beta name = "tf-test-psc-ilb-nat%{random_suffix}" region = "us-west2" @@ -390,7 +378,6 @@ resource "google_compute_subnetwork" "psc_ilb_nat" { } `, context) } -{{- end }} func TestAccComputeServiceAttachment_serviceAttachmentBasicExampleGateway(t *testing.T) { t.Parallel() diff --git a/mmv1/third_party/terraform/services/compute/test-fixtures/raw-disk-image.tar.gz b/mmv1/third_party/terraform/services/compute/test-fixtures/raw-disk-image.tar.gz new file mode 100644 index 000000000000..706e863d3993 Binary files /dev/null and b/mmv1/third_party/terraform/services/compute/test-fixtures/raw-disk-image.tar.gz differ diff --git a/mmv1/third_party/terraform/services/container/node_config.go.tmpl b/mmv1/third_party/terraform/services/container/node_config.go.tmpl index b5cbc90fef22..b98582509fde 100644 --- a/mmv1/third_party/terraform/services/container/node_config.go.tmpl +++ b/mmv1/third_party/terraform/services/container/node_config.go.tmpl @@ -2161,7 +2161,7 @@ func nodePoolNodeConfigUpdate(d *schema.ResourceData, config *transport_tpg.Conf req.Tags = ntags } - // sets tags to the empty list when user removes a previously defined list of tags entriely + // sets tags to the empty list when user removes a previously defined list of tags entriely // aka the node pool goes from having tags to no longer having any if req.Tags == nil { tags := []string{} diff --git a/mmv1/third_party/terraform/services/container/resource_container_cluster.go.tmpl b/mmv1/third_party/terraform/services/container/resource_container_cluster.go.tmpl index f4864da23dc6..eebf01f54c98 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_cluster.go.tmpl +++ b/mmv1/third_party/terraform/services/container/resource_container_cluster.go.tmpl @@ -104,12 +104,6 @@ var ( "private_cluster_config.0.master_global_access_config", } - forceNewClusterNodeConfigFields = []string{ - "labels", - "workload_metadata_config", - "resource_manager_tags", - } - suppressDiffForAutopilot = schema.SchemaDiffSuppressFunc(func(k, oldValue, newValue string, d *schema.ResourceData) bool { if v, _ := d.Get("enable_autopilot").(bool); v { return true @@ -126,19 +120,6 @@ var ( }) ) -// This uses the node pool nodeConfig schema but sets -// node-pool-only updatable fields to ForceNew -func clusterSchemaNodeConfig() *schema.Schema { - nodeConfigSch := schemaNodeConfig() - schemaMap := nodeConfigSch.Elem.(*schema.Resource).Schema - for _, k := range forceNewClusterNodeConfigFields { - if sch, ok := schemaMap[k]; ok { - tpgresource.ChangeFieldSchemaToForceNew(sch) - } - } - return nodeConfigSch -} - // Defines default nodel pool settings for the entire cluster. These settings are // overridden if specified on the specific NodePool object. func clusterSchemaNodePoolDefaults() *schema.Schema { @@ -1477,7 +1458,7 @@ func ResourceContainerCluster() *schema.Resource { }, }, - "node_config": clusterSchemaNodeConfig(), + "node_config": schemaNodeConfig(), "node_pool": { Type: schema.TypeList, @@ -2236,12 +2217,18 @@ func ResourceContainerCluster() *schema.Resource { // by only comparing the blocks with a positive count and ignoring those with count=0 // // One quirk with this approach is that configs with mixed count=0 and count>0 accelerator blocks will -// show a confusing diff if one of there are config changes that result in a legitimate diff as the count=0 +// show a confusing diff if there are config changes that result in a legitimate diff as the count=0 // blocks will not be in state. -func resourceNodeConfigEmptyGuestAccelerator(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { +func resourceNodeConfigEmptyGuestAccelerator(_ context.Context, diff *schema.ResourceDiff, meta any) error { old, new := diff.GetChange("node_config.0.guest_accelerator") - oList := old.([]interface{}) - nList := new.([]interface{}) + oList, ok := old.([]any) + if !ok { + return fmt.Errorf("type assertion failed, expected []any, got %T", old) + } + nList, ok := new.([]any) + if !ok { + return fmt.Errorf("type assertion failed, expected []any, got %T", new) + } if len(nList) == len(oList) || len(nList) == 0 { return nil @@ -2251,9 +2238,12 @@ func resourceNodeConfigEmptyGuestAccelerator(_ context.Context, diff *schema.Res // will be longer than the current state. // this index tracks the location of positive count accelerator blocks index := 0 - for i, item := range nList { - accel := item.(map[string]interface{}) - if accel["count"].(int) == 0 { + for _, item := range nList { + nAccel, ok := item.(map[string]any) + if !ok { + return fmt.Errorf("type assertion failed, expected []any, got %T", item) + } + if nAccel["count"].(int) == 0 { hasAcceleratorWithEmptyCount = true // Ignore any 'empty' accelerators because they aren't sent to the API continue @@ -2264,7 +2254,14 @@ func resourceNodeConfigEmptyGuestAccelerator(_ context.Context, diff *schema.Res // This will prevent array index overruns return nil } - if !reflect.DeepEqual(nList[i], oList[index]) { + // Delete Optional + Computed field from old and new map. + oAccel, ok := oList[index].(map[string]any) + if !ok { + return fmt.Errorf("type assertion failed, expected []any, got %T", oList[index]) + } + delete(nAccel, "gpu_driver_installation_config") + delete(oAccel, "gpu_driver_installation_config") + if !reflect.DeepEqual(oAccel, nAccel) { return nil } index += 1 @@ -3796,133 +3793,15 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er } if d.HasChange("node_config") { - if d.HasChange("node_config.0.image_type") { - it := d.Get("node_config.0.image_type").(string) - req := &container.UpdateClusterRequest{ - Update: &container.ClusterUpdate{ - DesiredImageType: it, - }, - } - - updateF := func() error { - name := containerClusterFullName(project, location, clusterName) - clusterUpdateCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.Update(name, req) - if config.UserProjectOverride { - clusterUpdateCall.Header().Add("X-Goog-User-Project", project) - } - op, err := clusterUpdateCall.Do() - if err != nil { - return err - } - - // Wait until it's updated - return ContainerOperationWait(config, op, project, location, "updating GKE image type", userAgent, d.Timeout(schema.TimeoutUpdate)) - } - - // Call update serially. - if err := transport_tpg.LockedCall(lockKey, updateF); err != nil { - return err - } - - log.Printf("[INFO] GKE cluster %s: image type has been updated to %s", d.Id(), it) - } - - if d.HasChange("node_config.0.kubelet_config") { - - defaultPool := "default-pool" - - timeout := d.Timeout(schema.TimeoutCreate) - - nodePoolInfo, err := extractNodePoolInformationFromCluster(d, config, clusterName) - if err != nil { - return err - } - - // Acquire write-lock on nodepool. - npLockKey := nodePoolInfo.nodePoolLockKey(defaultPool) - // Still should be further consolidated / DRYed up - // See b/361634104 - it := d.Get("node_config.0.kubelet_config") + defaultPool := "default-pool" - // While we're getting the value from fields in - // node_config.kubelet_config, the actual setting that needs to be - // updated is on the default nodepool. - req := &container.UpdateNodePoolRequest{ - Name: defaultPool, - KubeletConfig: expandKubeletConfig(it), - } - - updateF := func() error { - clusterNodePoolsUpdateCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.NodePools.Update(nodePoolInfo.fullyQualifiedName(defaultPool), req) - if config.UserProjectOverride { - clusterNodePoolsUpdateCall.Header().Add("X-Goog-User-Project", nodePoolInfo.project) - } - op, err := clusterNodePoolsUpdateCall.Do() - if err != nil { - return err - } - - // Wait until it's updated - return ContainerOperationWait(config, op, nodePoolInfo.project, nodePoolInfo.location, - "updating GKE node pool kubelet_config", userAgent, timeout) - } - - if err := retryWhileIncompatibleOperation(timeout, npLockKey, updateF); err != nil { - return err - } - - log.Printf("[INFO] GKE cluster %s: kubelet_config updated", d.Id()) - } - - if d.HasChange("node_config.0.gcfs_config") { - - defaultPool := "default-pool" - - timeout := d.Timeout(schema.TimeoutCreate) - - nodePoolInfo, err := extractNodePoolInformationFromCluster(d, config, clusterName) - if err != nil { - return err - } - - // Acquire write-lock on nodepool. - npLockKey := nodePoolInfo.nodePoolLockKey(defaultPool) - - gcfsEnabled := d.Get("node_config.0.gcfs_config.0.enabled").(bool) - - // While we're getting the value from the drepcated field in - // node_config.kubelet_config, the actual setting that needs to be updated - // is on the default nodepool. - req := &container.UpdateNodePoolRequest{ - Name: defaultPool, - GcfsConfig: &container.GcfsConfig{ - Enabled: gcfsEnabled, - }, - } - - updateF := func() error { - clusterNodePoolsUpdateCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.NodePools.Update(nodePoolInfo.fullyQualifiedName(defaultPool), req) - if config.UserProjectOverride { - clusterNodePoolsUpdateCall.Header().Add("X-Goog-User-Project", nodePoolInfo.project) - } - op, err := clusterNodePoolsUpdateCall.Do() - if err != nil { - return err - } - - // Wait until it's updated - return ContainerOperationWait(config, op, nodePoolInfo.project, nodePoolInfo.location, - "updating GKE node pool gcfs_config", userAgent, timeout) - } - - if err := retryWhileIncompatibleOperation(timeout, npLockKey, updateF); err != nil { - return err - } - - log.Printf("[INFO] GKE cluster %s: %s setting for gcfs_config updated to %t", d.Id(), defaultPool, gcfsEnabled) + nodePoolInfo, err := extractNodePoolInformationFromCluster(d, config, clusterName) + if err != nil { + return err } + nodePoolNodeConfigUpdate(d, config, nodePoolInfo, "", defaultPool, d.Timeout(schema.TimeoutUpdate)) } if d.HasChange("notification_config") { diff --git a/mmv1/third_party/terraform/services/container/resource_container_cluster_migratev1.go.tmpl b/mmv1/third_party/terraform/services/container/resource_container_cluster_migratev1.go.tmpl index 4a4731962b8b..6763e5be156e 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_cluster_migratev1.go.tmpl +++ b/mmv1/third_party/terraform/services/container/resource_container_cluster_migratev1.go.tmpl @@ -1198,7 +1198,7 @@ func resourceContainerClusterResourceV1() *schema.Resource { }, }, - "node_config": clusterSchemaNodeConfig(), + "node_config": schemaNodeConfig(), "node_pool": { Type: schema.TypeList, diff --git a/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.tmpl b/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.tmpl index 739a31c8c773..aa2456fe0746 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.tmpl +++ b/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.tmpl @@ -68,6 +68,10 @@ func TestAccContainerCluster_resourceManagerTags(t *testing.T) { networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) + if acctest.BootstrapPSARole(t, "service-", "container-engine-robot", "roles/resourcemanager.tagHoldAdmin") { + t.Fatal("Stopping the test because a role was added to the policy.") + } + acctest.VcrTest(t, resource.TestCase{ PreCheck: func() { acctest.AccTestPreCheck(t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), @@ -77,7 +81,31 @@ func TestAccContainerCluster_resourceManagerTags(t *testing.T) { CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccContainerCluster_resourceManagerTags(pid, clusterName, networkName, subnetworkName, randomSuffix), + Config: testAccContainerCluster_resourceManagerTags(pid, clusterName, networkName, subnetworkName, randomSuffix, 1), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_container_cluster.primary", "self_link"), + resource.TestCheckResourceAttrSet("google_container_cluster.primary", "node_config.0.resource_manager_tags.%"), + ), + }, + { + ResourceName: "google_container_cluster.primary", + ImportStateId: fmt.Sprintf("us-central1-a/%s", clusterName), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"}, + }, + { + Config: testAccContainerCluster_resourceManagerTags(pid, clusterName, networkName, subnetworkName, randomSuffix, 2), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("google_container_cluster.primary", "self_link"), resource.TestCheckResourceAttrSet("google_container_cluster.primary", "node_config.0.resource_manager_tags.%"), @@ -1475,26 +1503,26 @@ func TestAccContainerCluster_updateVersion(t *testing.T) { subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) acctest.VcrTest(t, resource.TestCase{ - PreCheck: func() { acctest.AccTestPreCheck(t) }, + PreCheck: func() { acctest.AccTestPreCheck(t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), - CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), Steps: []resource.TestStep{ { Config: testAccContainerCluster_withLowerVersion(clusterName, networkName, subnetworkName), }, { - ResourceName: "google_container_cluster.with_version", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_container_cluster.with_version", + ImportState: true, + ImportStateVerify: true, ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"}, }, { Config: testAccContainerCluster_updateVersion(clusterName, networkName, subnetworkName), }, { - ResourceName: "google_container_cluster.with_version", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_container_cluster.with_version", + ImportState: true, + ImportStateVerify: true, ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"}, }, }, @@ -1509,32 +1537,169 @@ func TestAccContainerCluster_withNodeConfig(t *testing.T) { subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) acctest.VcrTest(t, resource.TestCase{ - PreCheck: func() { acctest.AccTestPreCheck(t) }, + PreCheck: func() { acctest.AccTestPreCheck(t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), - CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), Steps: []resource.TestStep{ { Config: testAccContainerCluster_withNodeConfig(clusterName, networkName, subnetworkName), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, }, { - ResourceName: "google_container_cluster.with_node_config", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_container_cluster.with_node_config", + ImportState: true, + ImportStateVerify: true, ImportStateVerifyIgnore: []string{"node_config.0.taint", "deletion_protection"}, }, { Config: testAccContainerCluster_withNodeConfigUpdate(clusterName, networkName, subnetworkName), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, }, { - ResourceName: "google_container_cluster.with_node_config", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_container_cluster.with_node_config", + ImportState: true, + ImportStateVerify: true, ImportStateVerifyIgnore: []string{"node_config.0.taint", "deletion_protection"}, }, }, }) } +func TestAccContainerCluster_withNodeConfigLinuxNodeConfig(t *testing.T) { + t.Parallel() + + clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) + networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") + subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + // First test with empty `node_config.linux_node_config` (should result in "CGROUP_MODE_UNSPECIFIED") + { + Config: testAccContainerCluster_withNodeConfigLinuxNodeConfig(clusterName, networkName, subnetworkName, ""), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, + }, + { + ResourceName: "google_container_cluster.with_linux_node_config", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + // Then add a config and make sure it updates. + { + Config: testAccContainerCluster_withNodeConfigLinuxNodeConfig(clusterName, networkName, subnetworkName, "CGROUP_MODE_V2"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "google_container_cluster.with_linux_node_config", + "node_config.0.linux_node_config.0.cgroup_mode", "CGROUP_MODE_V2", + ), + ), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, + }, + { + ResourceName: "google_container_cluster.with_linux_node_config", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + // Lastly, update the setting in-place. V1 since UNSPECIFIED is default + { + Config: testAccContainerCluster_withNodeConfigLinuxNodeConfig(clusterName, networkName, subnetworkName, "CGROUP_MODE_V1"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "google_container_cluster.with_linux_node_config", + "node_config.0.linux_node_config.0.cgroup_mode", "CGROUP_MODE_V1", + ), + ), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, + }, + { + ResourceName: "google_container_cluster.with_linux_node_config", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + }, + }) +} + +func TestAccContainerCluster_withNodeConfigFastSocket(t *testing.T) { + t.Parallel() + + clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) + networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") + subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withNodeConfigFastSocket(clusterName, networkName, subnetworkName, false), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_container_cluster.with_fast_socket", + "node_config.0.fast_socket.0.enabled", "false"), + resource.TestCheckResourceAttr("google_container_cluster.with_fast_socket", + "node_config.0.gvnic.0.enabled", "true"), + ), + }, + { + ResourceName: "google_container_cluster.with_fast_socket", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + { + Config: testAccContainerCluster_withNodeConfigFastSocket(clusterName, networkName, subnetworkName, true), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_container_cluster.with_fast_socket", + "node_config.0.fast_socket.0.enabled", "true"), + ), + }, + { + ResourceName: "google_container_cluster.with_fast_socket", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + }, + }) +} + func TestAccContainerCluster_withNodeConfigGcfsConfig(t *testing.T) { t.Parallel() clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) @@ -2036,21 +2201,44 @@ func TestAccContainerCluster_withWorkloadMetadataConfig(t *testing.T) { subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) acctest.VcrTest(t, resource.TestCase{ - PreCheck: func() { acctest.AccTestPreCheck(t) }, + PreCheck: func() { acctest.AccTestPreCheck(t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), - CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccContainerCluster_withWorkloadMetadataConfig(clusterName, networkName, subnetworkName), + Config: testAccContainerCluster_withWorkloadMetadataConfig(clusterName, "GCE_METADATA", networkName, subnetworkName), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("google_container_cluster.with_workload_metadata_config", "node_config.0.workload_metadata_config.0.mode", "GCE_METADATA"), ), }, { - ResourceName: "google_container_cluster.with_workload_metadata_config", - ImportState: true, - ImportStateVerify: true, + ResourceName: "google_container_cluster.with_workload_metadata_config", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"}, + }, + { + Config: testAccContainerCluster_withWorkloadMetadataConfig(clusterName, "GKE_METADATA", networkName, subnetworkName), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_container_cluster.with_workload_metadata_config", + "node_config.0.workload_metadata_config.0.mode", "GKE_METADATA"), + ), + }, + { + ResourceName: "google_container_cluster.with_workload_metadata_config", + ImportState: true, + ImportStateVerify: true, ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"}, }, }, @@ -6710,7 +6898,7 @@ resource "google_container_cluster" "with_node_config" { initial_node_count = 1 node_config { - machine_type = "n1-standard-1" // can't be e2 because of local-ssd + machine_type = "n1-standard-1" // can't be e2 because of local-ssd disk_size_gb = 15 disk_type = "pd-ssd" local_ssd_count = 1 @@ -6748,12 +6936,35 @@ resource "google_container_cluster" "with_node_config" { image_type = "COS_CONTAINERD" } deletion_protection = false - network = "%s" - subnetwork = "%s" + network = "%s" + subnetwork = "%s" } `, clusterName, networkName, subnetworkName) } +func testAccContainerCluster_withNodeConfigFastSocket(clusterName, networkName, subnetworkName string, fastSocket bool) string { + return fmt.Sprintf(` +resource "google_container_cluster" "with_fast_socket" { + name = "%s" + location = "us-central1-f" + initial_node_count = 1 + + node_config { + gvnic { + enabled = true + } + fast_socket { + enabled = %t + } + } + + deletion_protection = false + network = "%s" + subnetwork = "%s" +} +`, clusterName, fastSocket, networkName, subnetworkName) +} + func testAccContainerCluster_withNodeConfigGcfsConfig(clusterName, networkName, subnetworkName string, enabled bool) string { return fmt.Sprintf(` resource "google_container_cluster" "with_node_config_gcfs_config" { @@ -6981,9 +7192,11 @@ resource "google_container_cluster" "with_node_config" { initial_node_count = 1 node_config { - machine_type = "n1-standard-1" // can't be e2 because of local-ssd - disk_size_gb = 15 - disk_type = "pd-ssd" + // Test updates for these fields as well + machine_type = "n1-standard-2" // can't be e2 because of local-ssd + disk_size_gb = 20 + disk_type = "pd-balanced" + local_ssd_count = 1 oauth_scopes = [ "https://www.googleapis.com/auth/monitoring", @@ -6996,35 +7209,68 @@ resource "google_container_cluster" "with_node_config" { foo = "bar" disable-legacy-endpoints = "true" } + labels = { - foo = "bar" + foo = "baz" + qux = "zzz" } - tags = ["foo", "bar"] + tags = ["baz", "qux"] preemptible = true min_cpu_platform = "Intel Broadwell" taint { key = "taint_key" value = "taint_value" - effect = "PREFER_NO_SCHEDULE" + effect = "NO_SCHEDULE" } taint { key = "taint_key2" - value = "taint_value2" + value = "taint_value2_updated" effect = "NO_EXECUTE" } - // Updatable fields image_type = "UBUNTU_CONTAINERD" } deletion_protection = false - network = "%s" - subnetwork = "%s" + network = "%s" + subnetwork = "%s" } `, clusterName, networkName, subnetworkName) } +func testAccContainerCluster_withNodeConfigLinuxNodeConfig(clusterName, networkName, subnetworkName, cgroupMode string) string { + // Empty block inside node_config if cgroupMode is empty + linuxNodeConfig := "" + + if cgroupMode != "" { + linuxNodeConfig = fmt.Sprintf(` + linux_node_config { + cgroup_mode = "%s" + } +`, cgroupMode) + } + + return fmt.Sprintf(` +resource "google_container_cluster" "with_linux_node_config" { + name = "%s" + location = "us-central1-f" + initial_node_count = 1 + + node_config { + disk_size_gb = 15 + + %s + } + + network = "%s" + subnetwork = "%s" + + deletion_protection = false +} +`, clusterName, linuxNodeConfig, networkName, subnetworkName) +} + func testAccContainerCluster_withNodeConfigScopeAlias(clusterName, networkName, subnetworkName string) string { return fmt.Sprintf(` resource "google_container_cluster" "with_node_config_scope_alias" { @@ -7203,18 +7449,24 @@ resource "google_container_cluster" "with_node_config" { `, reservation, clusterName, networkName, subnetworkName) } -func testAccContainerCluster_withWorkloadMetadataConfig(clusterName, networkName, subnetworkName string) string { +func testAccContainerCluster_withWorkloadMetadataConfig(clusterName, workloadMetadataConfigMode, networkName, subnetworkName string) string { return fmt.Sprintf(` data "google_container_engine_versions" "central1a" { location = "us-central1-a" } +data "google_project" "project" {} + resource "google_container_cluster" "with_workload_metadata_config" { name = "%s" location = "us-central1-a" initial_node_count = 1 min_master_version = data.google_container_engine_versions.central1a.latest_master_version + workload_identity_config { + workload_pool = "${data.google_project.project.project_id}.svc.id.goog" + } + node_config { oauth_scopes = [ "https://www.googleapis.com/auth/logging.write", @@ -7222,14 +7474,14 @@ resource "google_container_cluster" "with_workload_metadata_config" { ] workload_metadata_config { - mode = "GCE_METADATA" + mode = "%s" } } deletion_protection = false - network = "%s" - subnetwork = "%s" + network = "%s" + subnetwork = "%s" } -`, clusterName, networkName, subnetworkName) +`, clusterName, workloadMetadataConfigMode, networkName, subnetworkName) } {{ if not (or (eq $.TargetVersionName ``) (eq $.TargetVersionName `ga`)) }} @@ -8667,7 +8919,6 @@ resource "google_container_cluster" "with_pco_disabled" { network = google_compute_network.container_network.name subnetwork = google_compute_subnetwork.container_subnetwork.name - min_master_version = "1.27" initial_node_count = 1 datapath_provider = "ADVANCED_DATAPATH" @@ -9470,11 +9721,6 @@ resource "google_container_cluster" "with_autopilot" { datapath_provider = "ADVANCED_DATAPATH" deletion_protection = false - - timeouts { - create = "30m" - update = "40m" - } } `, clusterName, networkName, subnetworkName) } @@ -9534,11 +9780,6 @@ resource "google_container_cluster" "with_autopilot" { enable_cilium_clusterwide_network_policy = true deletion_protection = false - - timeouts { - create = "30m" - update = "40m" - } } `, clusterName, networkName, subnetworkName) } @@ -10299,14 +10540,16 @@ func testAccContainerCluster_autopilot_net_admin(name, networkName, subnetworkNa resource "google_container_cluster" "primary" { name = "%s" location = "us-central1" + + network = "%s" + subnetwork = "%s" + enable_autopilot = true allow_net_admin = %t - min_master_version = 1.27 + deletion_protection = false - network = "%s" - subnetwork = "%s" } -`, name, enabled, networkName, subnetworkName) +`, name, networkName, subnetworkName, enabled) } @@ -10746,7 +10989,7 @@ resource "google_container_cluster" "primary" { `, name, networkName, subnetworkName) } -func testAccContainerCluster_resourceManagerTags(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { +func testAccContainerCluster_resourceManagerTags(projectID, clusterName, networkName, subnetworkName, randomSuffix string, tagResourceNumber int) string { return fmt.Sprintf(` data "google_project" "project" { project_id = "%[1]s" @@ -10755,13 +10998,13 @@ data "google_project" "project" { resource "google_project_iam_member" "tagHoldAdmin" { project = "%[1]s" role = "roles/resourcemanager.tagHoldAdmin" - member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" + member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" } resource "google_project_iam_member" "tagUser1" { project = "%[1]s" role = "roles/resourcemanager.tagUser" - member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" + member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" depends_on = [google_project_iam_member.tagHoldAdmin] } @@ -10769,7 +11012,7 @@ resource "google_project_iam_member" "tagUser1" { resource "google_project_iam_member" "tagUser2" { project = "%[1]s" role = "roles/resourcemanager.tagUser" - member = "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com" + member = "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com" depends_on = [google_project_iam_member.tagHoldAdmin] } @@ -10784,22 +11027,39 @@ resource "time_sleep" "wait_120_seconds" { ] } -resource "google_tags_tag_key" "key" { - parent = "projects/%[1]s" - short_name = "foobarbaz-%[2]s" +resource "google_tags_tag_key" "key1" { + parent = data.google_project.project.id + short_name = "foobarbaz-%[2]s" description = "For foo/bar resources" - purpose = "GCE_FIREWALL" + purpose = "GCE_FIREWALL" purpose_data = { network = "%[1]s/%[4]s" } } -resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "foo-%[2]s" +resource "google_tags_tag_value" "value1" { + parent = google_tags_tag_key.key1.id + short_name = "foo-%[2]s" description = "For foo resources" } +# To test updates: create two key / value sets, and swap them for the update +resource "google_tags_tag_key" "key2" { + parent = data.google_project.project.id + short_name = "qux-%[2]s" + description = "For qux resources" + purpose = "GCE_FIREWALL" + purpose_data = { + network = "%[1]s/%[4]s" + } +} + +resource "google_tags_tag_value" "value2" { + parent = google_tags_tag_key.key2.id + short_name = "qux-%[2]s" + description = "For qux resources" +} + data "google_container_engine_versions" "uscentral1a" { location = "us-central1-a" } @@ -10811,26 +11071,20 @@ resource "google_container_cluster" "primary" { initial_node_count = 1 node_config { - machine_type = "n1-standard-1" // can't be e2 because of local-ssd - disk_size_gb = 15 + disk_size_gb = 15 resource_manager_tags = { - "tagKeys/${google_tags_tag_key.key.name}" = "tagValues/${google_tags_tag_value.value.name}" + (google_tags_tag_key.key%[6]d.id) = google_tags_tag_value.value%[6]d.id } } deletion_protection = false - network = "%[4]s" - subnetwork = "%[5]s" - - timeouts { - create = "30m" - update = "40m" - } + network = "%[4]s" + subnetwork = "%[5]s" depends_on = [time_sleep.wait_120_seconds] } -`, projectID, randomSuffix, clusterName, networkName, subnetworkName) +`, projectID, randomSuffix, clusterName, networkName, subnetworkName, tagResourceNumber) } func testAccContainerCluster_withAutopilotResourceManagerTags(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { @@ -10966,11 +11220,6 @@ resource "google_container_cluster" "with_autopilot" { enabled = true } - timeouts { - create = "30m" - update = "40m" - } - depends_on = [time_sleep.wait_120_seconds] } `, projectID, randomSuffix, clusterName, networkName, subnetworkName) @@ -11110,11 +11359,6 @@ resource "google_container_cluster" "with_autopilot" { enabled = true } - timeouts { - create = "30m" - update = "40m" - } - depends_on = [time_sleep.wait_120_seconds] } `, projectID, randomSuffix, clusterName, networkName, subnetworkName) @@ -11247,11 +11491,6 @@ resource "google_container_cluster" "with_autopilot" { enabled = true } - timeouts { - create = "30m" - update = "40m" - } - depends_on = [time_sleep.wait_120_seconds] } `, projectID, randomSuffix, clusterName, networkName, subnetworkName) diff --git a/mmv1/third_party/terraform/services/container/resource_container_node_pool.go.tmpl b/mmv1/third_party/terraform/services/container/resource_container_node_pool.go.tmpl index 4b463e56ab3b..ccda8717798e 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_node_pool.go.tmpl +++ b/mmv1/third_party/terraform/services/container/resource_container_node_pool.go.tmpl @@ -1198,11 +1198,13 @@ func flattenNodePool(d *schema.ResourceData, config *transport_tpg.Config, np *c nodePool["max_pods_per_node"] = np.MaxPodsConstraint.MaxPodsPerNode } - nodePool["management"] = []map[string]interface{}{ - { - "auto_repair": np.Management.AutoRepair, - "auto_upgrade": np.Management.AutoUpgrade, - }, + if np.Management != nil { + nodePool["management"] = []map[string]interface{}{ + { + "auto_repair": np.Management.AutoRepair, + "auto_upgrade": np.Management.AutoUpgrade, + }, + } } if np.UpgradeSettings != nil { diff --git a/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.tmpl b/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.tmpl index 31345acb5ace..544af3b762be 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.tmpl +++ b/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.tmpl @@ -838,7 +838,6 @@ resource "google_compute_subnetwork" "container_subnetwork" { resource "google_container_cluster" "cluster" { name = "%s" location = "us-central1-a" - min_master_version = "1.27" initial_node_count = 1 network = google_compute_network.container_network.name @@ -2561,10 +2560,11 @@ resource "google_container_cluster" "cluster" { name = "%s" location = "us-central1" initial_node_count = 3 - min_master_version = "1.27" - deletion_protection = false - network = "%s" + + network = "%s" subnetwork = "%s" + + deletion_protection = false } resource "google_container_node_pool" "np" { @@ -2587,10 +2587,11 @@ resource "google_container_cluster" "cluster" { name = "%s" location = "us-central1" initial_node_count = 3 - min_master_version = "1.27" - deletion_protection = false - network = "%s" + + network = "%s" subnetwork = "%s" + + deletion_protection = false } resource "google_container_node_pool" "np" { @@ -2618,11 +2619,12 @@ resource "google_container_cluster" "cluster" { provider = google.user-project-override name = "%s" location = "us-central1" + + network = "%s" + subnetwork = "%s" + initial_node_count = 3 - min_master_version = "1.27" deletion_protection = false - network = "%s" - subnetwork = "%s" } resource "google_container_node_pool" "np" { @@ -3674,7 +3676,7 @@ resource "google_container_cluster" "cluster" { resource "google_container_node_pool" "with_upgrade_settings" { name = "%s" location = "us-central1" - cluster = "${google_container_cluster.cluster.name}" + cluster = google_container_cluster.cluster.name initial_node_count = 1 %s } @@ -3688,13 +3690,13 @@ data "google_container_engine_versions" "central1c" { } resource "google_container_cluster" "cluster" { - name = "%s" - location = "us-central1-c" - initial_node_count = 1 - min_master_version = data.google_container_engine_versions.central1c.latest_master_version + name = "%s" + location = "us-central1-c" + initial_node_count = 1 + min_master_version = data.google_container_engine_versions.central1c.latest_master_version deletion_protection = false - network = "%s" - subnetwork = "%s" + network = "%s" + subnetwork = "%s" } resource "google_container_node_pool" "np_with_gpu" { @@ -3705,7 +3707,7 @@ resource "google_container_node_pool" "np_with_gpu" { initial_node_count = 1 node_config { - machine_type = "a2-highgpu-1g" // can't be e2 because of accelerator + machine_type = "a2-highgpu-1g" // can't be e2 because of accelerator disk_size_gb = 32 oauth_scopes = [ @@ -3722,14 +3724,14 @@ resource "google_container_node_pool" "np_with_gpu" { image_type = "COS_CONTAINERD" guest_accelerator { - type = "nvidia-tesla-a100" + type = "nvidia-tesla-a100" gpu_partition_size = "1g.5gb" - count = 1 - gpu_driver_installation_config { - gpu_driver_version = "LATEST" - } + count = 1 + gpu_driver_installation_config { + gpu_driver_version = "LATEST" + } gpu_sharing_config { - gpu_sharing_strategy = "TIME_SHARING" + gpu_sharing_strategy = "TIME_SHARING" max_shared_clients_per_gpu = 2 } } @@ -3839,7 +3841,7 @@ resource "google_container_node_pool" "np" { count = 0 type = "nvidia-tesla-p100" } - machine_type = "n1-highmem-4" + machine_type = "n1-highmem-4" } } `, cluster, networkName, subnetworkName, np) @@ -3872,7 +3874,7 @@ resource "google_container_node_pool" "np" { count = %d type = "nvidia-tesla-p100" } - machine_type = "n1-highmem-4" + machine_type = "n1-highmem-4" } } `, cluster, networkName, subnetworkName, np, count) @@ -3945,12 +3947,12 @@ resource "google_container_node_pool" "np" { func testAccContainerNodePool_concurrentCreate(cluster, np1, np2, networkName, subnetworkName string) string { return fmt.Sprintf(` resource "google_container_cluster" "cluster" { - name = "%s" - location = "us-central1-a" - initial_node_count = 3 + name = "%s" + location = "us-central1-a" + initial_node_count = 3 deletion_protection = false - network = "%s" - subnetwork = "%s" + network = "%s" + subnetwork = "%s" } resource "google_container_node_pool" "np1" { @@ -3961,11 +3963,11 @@ resource "google_container_node_pool" "np1" { } resource "google_container_node_pool" "np2" { - name = "%s" - location = "us-central1-a" - cluster = google_container_cluster.cluster.name - initial_node_count = 2 - } + name = "%s" + location = "us-central1-a" + cluster = google_container_cluster.cluster.name + initial_node_count = 2 +} `, cluster, networkName, subnetworkName, np1, np2) } @@ -4011,20 +4013,20 @@ resource "google_compute_node_template" "soletenant-tmpl" { } resource "google_compute_node_group" "nodes" { - name = "tf-test-soletenant-group" - zone = "us-central1-a" - initial_size = 1 + name = "tf-test-soletenant-group" + zone = "us-central1-a" + initial_size = 1 node_template = google_compute_node_template.soletenant-tmpl.id } resource "google_container_cluster" "cluster" { - name = "%s" - location = "us-central1-a" - initial_node_count = 1 - min_master_version = data.google_container_engine_versions.central1a.latest_master_version + name = "%s" + location = "us-central1-a" + initial_node_count = 1 + min_master_version = data.google_container_engine_versions.central1a.latest_master_version deletion_protection = false - network = "%s" - subnetwork = "%s" + network = "%s" + subnetwork = "%s" } resource "google_container_node_pool" "with_sole_tenant_config" { @@ -4033,14 +4035,14 @@ resource "google_container_node_pool" "with_sole_tenant_config" { cluster = google_container_cluster.cluster.name initial_node_count = 1 node_config { - machine_type = "n1-standard-2" - sole_tenant_config { - node_affinity { - key = "compute.googleapis.com/node-group-name" - operator = "IN" - values = [google_compute_node_group.nodes.name] - } - } + machine_type = "n1-standard-2" + sole_tenant_config { + node_affinity { + key = "compute.googleapis.com/node-group-name" + operator = "IN" + values = [google_compute_node_group.nodes.name] + } + } oauth_scopes = [ "https://www.googleapis.com/auth/logging.write", "https://www.googleapis.com/auth/monitoring", @@ -4260,8 +4262,8 @@ resource "google_container_cluster" "cluster" { initial_node_count = 1 node_config { host_maintenance_policy { - maintenance_interval = "PERIODIC" - } + maintenance_interval = "PERIODIC" + } machine_type = "n2-standard-2" } deletion_protection = false @@ -4274,8 +4276,8 @@ resource "google_container_node_pool" "np" { initial_node_count = 1 node_config { host_maintenance_policy { - maintenance_interval = "PERIODIC" - } + maintenance_interval = "PERIODIC" + } machine_type = "n2-standard-2" } } @@ -4421,13 +4423,13 @@ data "google_project" "project" { resource "google_project_iam_member" "tagHoldAdmin" { project = "%[1]s" role = "roles/resourcemanager.tagHoldAdmin" - member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" + member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" } resource "google_project_iam_member" "tagUser1" { project = "%[1]s" role = "roles/resourcemanager.tagUser" - member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" + member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" depends_on = [google_project_iam_member.tagHoldAdmin] } @@ -4435,7 +4437,7 @@ resource "google_project_iam_member" "tagUser1" { resource "google_project_iam_member" "tagUser2" { project = "%[1]s" role = "roles/resourcemanager.tagUser" - member = "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com" + member = "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com" depends_on = [google_project_iam_member.tagHoldAdmin] } @@ -4451,26 +4453,26 @@ resource "time_sleep" "wait_120_seconds" { } resource "google_tags_tag_key" "key1" { - parent = "projects/%[1]s" - short_name = "foobarbaz1-%[2]s" + parent = "projects/%[1]s" + short_name = "foobarbaz1-%[2]s" description = "For foo/bar1 resources" - purpose = "GCE_FIREWALL" + purpose = "GCE_FIREWALL" purpose_data = { network = "%[1]s/%[4]s" } } resource "google_tags_tag_value" "value1" { - parent = "tagKeys/${google_tags_tag_key.key1.name}" - short_name = "foo1-%[2]s" + parent = google_tags_tag_key.key1.id + short_name = "foo1-%[2]s" description = "For foo1 resources" } resource "google_tags_tag_key" "key2" { - parent = "projects/%[1]s" - short_name = "foobarbaz2-%[2]s" + parent = "projects/%[1]s" + short_name = "foobarbaz2-%[2]s" description = "For foo/bar2 resources" - purpose = "GCE_FIREWALL" + purpose = "GCE_FIREWALL" purpose_data = { network = "%[1]s/%[4]s" } @@ -4479,8 +4481,8 @@ resource "google_tags_tag_key" "key2" { } resource "google_tags_tag_value" "value2" { - parent = "tagKeys/${google_tags_tag_key.key2.name}" - short_name = "foo2-%[2]s" + parent = google_tags_tag_key.key2.id + short_name = "foo2-%[2]s" description = "For foo2 resources" } @@ -4497,7 +4499,7 @@ resource "google_container_cluster" "primary" { # separately managed node pools. So we create the smallest possible default # node pool and immediately delete it. remove_default_node_pool = true - initial_node_count = 1 + initial_node_count = 1 deletion_protection = false network = "%[4]s" @@ -4513,19 +4515,19 @@ resource "google_container_cluster" "primary" { # Separately Managed Node Pool resource "google_container_node_pool" "primary_nodes" { - name = google_container_cluster.primary.name - location = "us-central1-a" - cluster = google_container_cluster.primary.name + name = google_container_cluster.primary.name + location = "us-central1-a" + cluster = google_container_cluster.primary.name - version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] node_count = 1 node_config { - machine_type = "n1-standard-1" // can't be e2 because of local-ssd - disk_size_gb = 15 + machine_type = "n1-standard-1" // can't be e2 because of local-ssd + disk_size_gb = 15 resource_manager_tags = { - "tagKeys/${google_tags_tag_key.key1.name}" = "tagValues/${google_tags_tag_value.value1.name}" + (google_tags_tag_key.key1.id) = google_tags_tag_value.value1.id } } } @@ -4541,13 +4543,13 @@ data "google_project" "project" { resource "google_project_iam_member" "tagHoldAdmin" { project = "%[1]s" role = "roles/resourcemanager.tagHoldAdmin" - member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" + member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" } resource "google_project_iam_member" "tagUser1" { project = "%[1]s" role = "roles/resourcemanager.tagUser" - member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" + member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" depends_on = [google_project_iam_member.tagHoldAdmin] } @@ -4555,7 +4557,7 @@ resource "google_project_iam_member" "tagUser1" { resource "google_project_iam_member" "tagUser2" { project = "%[1]s" role = "roles/resourcemanager.tagUser" - member = "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com" + member = "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com" depends_on = [google_project_iam_member.tagHoldAdmin] } @@ -4571,26 +4573,26 @@ resource "time_sleep" "wait_120_seconds" { } resource "google_tags_tag_key" "key1" { - parent = "projects/%[1]s" - short_name = "foobarbaz1-%[2]s" + parent = "projects/%[1]s" + short_name = "foobarbaz1-%[2]s" description = "For foo/bar1 resources" - purpose = "GCE_FIREWALL" + purpose = "GCE_FIREWALL" purpose_data = { network = "%[1]s/%[4]s" } } resource "google_tags_tag_value" "value1" { - parent = "tagKeys/${google_tags_tag_key.key1.name}" - short_name = "foo1-%[2]s" + parent = google_tags_tag_key.key1.id + short_name = "foo1-%[2]s" description = "For foo1 resources" } resource "google_tags_tag_key" "key2" { - parent = "projects/%[1]s" - short_name = "foobarbaz2-%[2]s" + parent = "projects/%[1]s" + short_name = "foobarbaz2-%[2]s" description = "For foo/bar2 resources" - purpose = "GCE_FIREWALL" + purpose = "GCE_FIREWALL" purpose_data = { network = "%[1]s/%[4]s" } @@ -4599,8 +4601,8 @@ resource "google_tags_tag_key" "key2" { } resource "google_tags_tag_value" "value2" { - parent = "tagKeys/${google_tags_tag_key.key2.name}" - short_name = "foo2-%[2]s" + parent = google_tags_tag_key.key2.id + short_name = "foo2-%[2]s" description = "For foo2 resources" } @@ -4617,7 +4619,7 @@ resource "google_container_cluster" "primary" { # separately managed node pools. So we create the smallest possible default # node pool and immediately delete it. remove_default_node_pool = true - initial_node_count = 1 + initial_node_count = 1 deletion_protection = false network = "%[4]s" @@ -4633,20 +4635,20 @@ resource "google_container_cluster" "primary" { # Separately Managed Node Pool resource "google_container_node_pool" "primary_nodes" { - name = google_container_cluster.primary.name - location = "us-central1-a" - cluster = google_container_cluster.primary.name + name = google_container_cluster.primary.name + location = "us-central1-a" + cluster = google_container_cluster.primary.name - version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] node_count = 1 node_config { - machine_type = "n1-standard-1" // can't be e2 because of local-ssd - disk_size_gb = 15 + machine_type = "n1-standard-1" // can't be e2 because of local-ssd + disk_size_gb = 15 resource_manager_tags = { - "tagKeys/${google_tags_tag_key.key1.name}" = "tagValues/${google_tags_tag_value.value1.name}" - "tagKeys/${google_tags_tag_key.key2.name}" = "tagValues/${google_tags_tag_value.value2.name}" + (google_tags_tag_key.key1.id) = google_tags_tag_value.value1.id + (google_tags_tag_key.key2.id) = google_tags_tag_value.value2.id } } } @@ -4662,13 +4664,13 @@ data "google_project" "project" { resource "google_project_iam_member" "tagHoldAdmin" { project = "%[1]s" role = "roles/resourcemanager.tagHoldAdmin" - member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" + member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" } resource "google_project_iam_member" "tagUser1" { project = "%[1]s" role = "roles/resourcemanager.tagUser" - member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" + member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" depends_on = [google_project_iam_member.tagHoldAdmin] } @@ -4676,7 +4678,7 @@ resource "google_project_iam_member" "tagUser1" { resource "google_project_iam_member" "tagUser2" { project = "%[1]s" role = "roles/resourcemanager.tagUser" - member = "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com" + member = "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com" depends_on = [google_project_iam_member.tagHoldAdmin] } @@ -4692,26 +4694,26 @@ resource "time_sleep" "wait_120_seconds" { } resource "google_tags_tag_key" "key1" { - parent = "projects/%[1]s" - short_name = "foobarbaz1-%[2]s" + parent = "projects/%[1]s" + short_name = "foobarbaz1-%[2]s" description = "For foo/bar1 resources" - purpose = "GCE_FIREWALL" + purpose = "GCE_FIREWALL" purpose_data = { network = "%[1]s/%[4]s" } } resource "google_tags_tag_value" "value1" { - parent = "tagKeys/${google_tags_tag_key.key1.name}" - short_name = "foo1-%[2]s" + parent = google_tags_tag_key.key1.id + short_name = "foo1-%[2]s" description = "For foo1 resources" } resource "google_tags_tag_key" "key2" { - parent = "projects/%[1]s" - short_name = "foobarbaz2-%[2]s" + parent = "projects/%[1]s" + short_name = "foobarbaz2-%[2]s" description = "For foo/bar2 resources" - purpose = "GCE_FIREWALL" + purpose = "GCE_FIREWALL" purpose_data = { network = "%[1]s/%[4]s" } @@ -4720,8 +4722,8 @@ resource "google_tags_tag_key" "key2" { } resource "google_tags_tag_value" "value2" { - parent = "tagKeys/${google_tags_tag_key.key2.name}" - short_name = "foo2-%[2]s" + parent = google_tags_tag_key.key2.id + short_name = "foo2-%[2]s" description = "For foo2 resources" } @@ -4738,7 +4740,7 @@ resource "google_container_cluster" "primary" { # separately managed node pools. So we create the smallest possible default # node pool and immediately delete it. remove_default_node_pool = true - initial_node_count = 1 + initial_node_count = 1 deletion_protection = false network = "%[4]s" @@ -4754,16 +4756,16 @@ resource "google_container_cluster" "primary" { # Separately Managed Node Pool resource "google_container_node_pool" "primary_nodes" { - name = google_container_cluster.primary.name - location = "us-central1-a" - cluster = google_container_cluster.primary.name + name = google_container_cluster.primary.name + location = "us-central1-a" + cluster = google_container_cluster.primary.name - version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] node_count = 1 node_config { - machine_type = "n1-standard-1" // can't be e2 because of local-ssd - disk_size_gb = 15 + machine_type = "n1-standard-1" // can't be e2 because of local-ssd + disk_size_gb = 15 } } `, projectID, randomSuffix, clusterName, networkName, subnetworkName) @@ -4816,39 +4818,38 @@ func TestAccContainerNodePool_privateRegistry(t *testing.T) { func testAccContainerNodePool_privateRegistryEnabled(secretID, cluster, nodepool, network, subnetwork string) string { return fmt.Sprintf(` -data "google_project" "test_project" { - } +data "google_project" "test_project" {} resource "google_secret_manager_secret" "secret-basic" { - secret_id = "%s" - replication { - user_managed { - replicas { - location = "us-central1" - } - } - } + secret_id = "%s" + replication { + user_managed { + replicas { + location = "us-central1" + } + } + } } resource "google_secret_manager_secret_version" "secret-version-basic" { - secret = google_secret_manager_secret.secret-basic.id - secret_data = "dummypassword" - } + secret = google_secret_manager_secret.secret-basic.id + secret_data = "dummypassword" +} resource "google_secret_manager_secret_iam_member" "secret_iam" { - secret_id = google_secret_manager_secret.secret-basic.id - role = "roles/secretmanager.admin" - member = "serviceAccount:${data.google_project.test_project.number}-compute@developer.gserviceaccount.com" - depends_on = [google_secret_manager_secret_version.secret-version-basic] - } + secret_id = google_secret_manager_secret.secret-basic.id + role = "roles/secretmanager.admin" + member = "serviceAccount:${data.google_project.test_project.number}-compute@developer.gserviceaccount.com" + depends_on = [google_secret_manager_secret_version.secret-version-basic] +} resource "google_container_cluster" "cluster" { - name = "%s" - location = "us-central1-a" - initial_node_count = 1 + name = "%s" + location = "us-central1-a" + initial_node_count = 1 deletion_protection = false - network = "%s" - subnetwork = "%s" + network = "%s" + subnetwork = "%s" } resource "google_container_node_pool" "np" { @@ -4858,22 +4859,22 @@ resource "google_container_node_pool" "np" { initial_node_count = 1 node_config { - oauth_scopes = [ + oauth_scopes = [ "https://www.googleapis.com/auth/cloud-platform", ] machine_type = "n1-standard-8" - image_type = "COS_CONTAINERD" + image_type = "COS_CONTAINERD" containerd_config { private_registry_access_config { enabled = true certificate_authority_domain_config { - fqdns = [ "my.custom.domain", "10.0.0.127:8888" ] + fqdns = ["my.custom.domain", "10.0.0.127:8888"] gcp_secret_manager_certificate_config { secret_uri = google_secret_manager_secret_version.secret-version-basic.name } } certificate_authority_domain_config { - fqdns = [ "10.1.2.32" ] + fqdns = ["10.1.2.32"] gcp_secret_manager_certificate_config { secret_uri = google_secret_manager_secret_version.secret-version-basic.name } @@ -4916,9 +4917,9 @@ data "google_container_engine_versions" "central1a" { } resource "google_container_cluster" "cluster" { - name = "%s" - location = "us-central1-a" - initial_node_count = 3 + name = "%s" + location = "us-central1-a" + initial_node_count = 3 deletion_protection = false min_master_version = data.google_container_engine_versions.central1a.release_channel_latest_version["RAPID"] @@ -4936,14 +4937,14 @@ resource "google_container_node_pool" "np" { node_config { service_account = "default" - machine_type = "n1-standard-8" + machine_type = "n1-standard-8" guest_accelerator { type = "nvidia-tesla-t4" count = 1 gpu_sharing_config { - gpu_sharing_strategy = "TIME_SHARING" - max_shared_clients_per_gpu = 3 + gpu_sharing_strategy = "TIME_SHARING" + max_shared_clients_per_gpu = 3 } } } @@ -4986,12 +4987,12 @@ func TestAccContainerNodePool_storagePools(t *testing.T) { func testAccContainerNodePool_storagePools(cluster, np, networkName, subnetworkName, storagePoolResourceName, location string) string { return fmt.Sprintf(` resource "google_container_cluster" "cluster" { - name = "%[1]s" - location = "%[6]s" - initial_node_count = 1 + name = "%[1]s" + location = "%[6]s" + initial_node_count = 1 deletion_protection = false - network = "%[3]s" - subnetwork = "%[4]s" + network = "%[3]s" + subnetwork = "%[4]s" } resource "google_container_node_pool" "np" { @@ -5001,10 +5002,10 @@ resource "google_container_node_pool" "np" { initial_node_count = 1 node_config { - machine_type = "c3-standard-4" - image_type = "COS_CONTAINERD" + machine_type = "c3-standard-4" + image_type = "COS_CONTAINERD" storage_pools = ["%[5]s"] - disk_type = "hyperdisk-balanced" + disk_type = "hyperdisk-balanced" } } `, cluster, np, networkName, subnetworkName, storagePoolResourceName, location) @@ -5059,14 +5060,15 @@ provider "google" { alias = "user-project-override" user_project_override = true } + resource "google_container_cluster" "cluster" { - provider = google.user-project-override - name = "%[1]s" - location = "%[6]s" - initial_node_count = 3 + provider = google.user-project-override + name = "%[1]s" + location = "%[6]s" + initial_node_count = 3 deletion_protection = false - network = "%[3]s" - subnetwork = "%[4]s" + network = "%[3]s" + subnetwork = "%[4]s" } resource "google_container_node_pool" "np" { diff --git a/mmv1/third_party/terraform/services/containerattached/resource_container_attached_cluster_update_test.go b/mmv1/third_party/terraform/services/containerattached/resource_container_attached_cluster_update_test.go index 7c199b496517..e4de49f585d9 100644 --- a/mmv1/third_party/terraform/services/containerattached/resource_container_attached_cluster_update_test.go +++ b/mmv1/third_party/terraform/services/containerattached/resource_container_attached_cluster_update_test.go @@ -118,9 +118,6 @@ resource "google_container_attached_cluster" "primary" { namespace = "default" } } - security_posture_config { - vulnerability_mode = "VULNERABILITY_ENTERPRISE" - } } `, context) } @@ -169,9 +166,6 @@ resource "google_container_attached_cluster" "primary" { namespace = "custom-ns" } } - security_posture_config { - vulnerability_mode = "VULNERABILITY_DISABLED" - } lifecycle { prevent_destroy = true } @@ -318,9 +312,6 @@ resource "google_container_attached_cluster" "primary" { namespace = "custom-ns" } } - security_posture_config { - vulnerability_mode = "VULNERABILITY_DISABLED" - } } `, context) } diff --git a/mmv1/third_party/terraform/services/dataproc/resource_dataproc_cluster_test.go.tmpl b/mmv1/third_party/terraform/services/dataproc/resource_dataproc_cluster_test.go.tmpl index 7cb8c98bd466..1bc233926119 100644 --- a/mmv1/third_party/terraform/services/dataproc/resource_dataproc_cluster_test.go.tmpl +++ b/mmv1/third_party/terraform/services/dataproc/resource_dataproc_cluster_test.go.tmpl @@ -73,8 +73,8 @@ func TestAccDataprocCluster_basic(t *testing.T) { // Default behaviour is for Dataproc to autogen or autodiscover a config bucket resource.TestCheckResourceAttrSet("google_dataproc_cluster.basic", "cluster_config.0.bucket"), - // Default behavior is for Dataproc to not use only internal IP addresses - resource.TestCheckResourceAttr("google_dataproc_cluster.basic", "cluster_config.0.gce_cluster_config.0.internal_ip_only", "false"), + // Default behavior as of 2.2+ is for clusters to disallow external IPs by default + resource.TestCheckResourceAttr("google_dataproc_cluster.basic", "cluster_config.0.gce_cluster_config.0.internal_ip_only", "true"), // Expect 1 master instances with computed values resource.TestCheckResourceAttr("google_dataproc_cluster.basic", "cluster_config.0.master_config.#", "1"), @@ -168,6 +168,7 @@ func TestAccDataprocCluster_withAccelerators(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckDataprocClusterExists(t, "google_dataproc_cluster.accelerated_cluster", &cluster), testAccCheckDataprocClusterAccelerator(&cluster, project, 1, 1), + resource.TestCheckResourceAttr("google_dataproc_cluster.accelerated_cluster", "cluster_config.0.gce_cluster_config.0.internal_ip_only", "false"), ), }, }, @@ -1419,6 +1420,7 @@ resource "google_dataproc_cluster" "accelerated_cluster" { } gce_cluster_config { + internal_ip_only = false subnetwork = "%s" zone = "%s" } diff --git a/mmv1/third_party/terraform/services/datastream/resource_datastream_stream_test.go b/mmv1/third_party/terraform/services/datastream/resource_datastream_stream_test.go index c90c0270ede2..a89e78388e77 100644 --- a/mmv1/third_party/terraform/services/datastream/resource_datastream_stream_test.go +++ b/mmv1/third_party/terraform/services/datastream/resource_datastream_stream_test.go @@ -33,7 +33,7 @@ func TestAccDatastreamStream_update(t *testing.T) { ResourceName: "google_datastream_stream.default", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"stream_id", "location", "desired_state", "labels", "terraform_labels"}, + ImportStateVerifyIgnore: []string{"create_without_validation", "stream_id", "location", "desired_state", "labels", "terraform_labels"}, }, { Config: testAccDatastreamStream_datastreamStreamBasicUpdate(context, "RUNNING", true), @@ -43,7 +43,7 @@ func TestAccDatastreamStream_update(t *testing.T) { ResourceName: "google_datastream_stream.default", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"stream_id", "location", "desired_state", "labels", "terraform_labels"}, + ImportStateVerifyIgnore: []string{"create_without_validation", "stream_id", "location", "desired_state", "labels", "terraform_labels"}, }, { Config: testAccDatastreamStream_datastreamStreamBasicUpdate(context, "PAUSED", true), @@ -53,7 +53,7 @@ func TestAccDatastreamStream_update(t *testing.T) { ResourceName: "google_datastream_stream.default", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"stream_id", "location", "desired_state", "labels", "terraform_labels"}, + ImportStateVerifyIgnore: []string{"create_without_validation", "stream_id", "location", "desired_state", "labels", "terraform_labels"}, }, { Config: testAccDatastreamStream_datastreamStreamBasicUpdate(context, "RUNNING", true), @@ -63,7 +63,7 @@ func TestAccDatastreamStream_update(t *testing.T) { ResourceName: "google_datastream_stream.default", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"stream_id", "location", "desired_state", "labels", "terraform_labels"}, + ImportStateVerifyIgnore: []string{"create_without_validation", "stream_id", "location", "desired_state", "labels", "terraform_labels"}, }, { // Disable prevent_destroy diff --git a/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_feature_test.go.tmpl b/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_feature_test.go.tmpl index 310b805dcaa2..ab02acdeec48 100644 --- a/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_feature_test.go.tmpl +++ b/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_feature_test.go.tmpl @@ -546,7 +546,7 @@ resource "google_gke_hub_feature" "feature" { location = "global" fleet_default_member_config { configmanagement { - version = "1.16.0" + version = "1.19.1" config_sync { source_format = "hierarchy" git { @@ -573,7 +573,7 @@ resource "google_gke_hub_feature" "feature" { location = "global" fleet_default_member_config { configmanagement { - version = "1.16.1" + version = "1.19.2" management = "MANAGEMENT_MANUAL" config_sync { enabled = true @@ -602,7 +602,7 @@ resource "google_gke_hub_feature" "feature" { location = "global" fleet_default_member_config { configmanagement { - version = "1.16.1" + version = "1.19.2" management = "MANAGEMENT_AUTOMATIC" config_sync { prevent_drift = true diff --git a/mmv1/third_party/terraform/services/monitoring/resource_monitoring_alert_policy_test.go b/mmv1/third_party/terraform/services/monitoring/resource_monitoring_alert_policy_test.go index a37a982acb05..e3ec6583d1d8 100644 --- a/mmv1/third_party/terraform/services/monitoring/resource_monitoring_alert_policy_test.go +++ b/mmv1/third_party/terraform/services/monitoring/resource_monitoring_alert_policy_test.go @@ -399,7 +399,8 @@ resource "google_monitoring_alert_policy" "log" { notification_rate_limit { period = "300s" } - auto_close = "2000s" + auto_close = "2000s" + notification_prompts = ["OPENED"] } severity = "WARNING" diff --git a/mmv1/third_party/terraform/services/netapp/resource_netapp_storage_pool_test.go.tmpl b/mmv1/third_party/terraform/services/netapp/resource_netapp_storage_pool_test.go.tmpl index c3b6ecd51d4b..9d3aab1f435d 100644 --- a/mmv1/third_party/terraform/services/netapp/resource_netapp_storage_pool_test.go.tmpl +++ b/mmv1/third_party/terraform/services/netapp/resource_netapp_storage_pool_test.go.tmpl @@ -2,11 +2,8 @@ package netapp_test import ( "testing" -{{- if ne $.TargetVersionName "ga" }} "time" "github.com/hashicorp/terraform-plugin-testing/terraform" -{{- end }} - "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-google/google/acctest" ) @@ -197,7 +194,6 @@ resource "google_netapp_storage_pool" "test_pool" { `, context) } -{{ if ne $.TargetVersionName `ga` -}} func TestAccNetappStoragePool_FlexRegionalStoragePoolCreateExample_update(t *testing.T) { context := map[string]interface{}{ "network_name": acctest.BootstrapSharedServiceNetworkingConnection(t, "gcnv-network-config-1", acctest.ServiceNetworkWithParentService("netapp.servicenetworking.goog")), @@ -206,7 +202,7 @@ func TestAccNetappStoragePool_FlexRegionalStoragePoolCreateExample_update(t *tes acctest.VcrTest(t, resource.TestCase{ PreCheck: func() { acctest.AccTestPreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), CheckDestroy: testAccCheckNetappStoragePoolDestroyProducer(t), ExternalProviders: map[string]resource.ExternalProvider{ "time": {}, @@ -247,7 +243,6 @@ func TestAccNetappStoragePool_FlexRegionalStoragePoolCreateExample_update(t *tes func testAccNetappStoragePool_FlexRegionalStoragePoolCreateExample_full(context map[string]interface{}) string { return acctest.Nprintf(` resource "google_netapp_storage_pool" "test_pool" { - provider = google-beta name = "tf-test-pool%{random_suffix}" location = "us-east1" service_level = "FLEX" @@ -263,7 +258,6 @@ resource "time_sleep" "wait_5_minutes" { } data "google_compute_network" "default" { - provider = google-beta name = "%{network_name}" } `, context) @@ -272,7 +266,6 @@ data "google_compute_network" "default" { func testAccNetappStoragePool_FlexRegionalStoragePoolCreateExample_switchZone(context map[string]interface{}) string { return acctest.Nprintf(` resource "google_netapp_storage_pool" "test_pool" { - provider = google-beta name = "tf-test-pool%{random_suffix}" location = "us-east1" service_level = "FLEX" @@ -288,7 +281,6 @@ resource "time_sleep" "wait_5_minutes" { } data "google_compute_network" "default" { - provider = google-beta name = "%{network_name}" } `, context) @@ -305,7 +297,6 @@ func testAccNetappStoragePool_FlexRegionalStoragePoolCreateExample_sleep_5_mins( func testAccNetappStoragePool_FlexRegionalStoragePoolCreateExample_switchBackZone(context map[string]interface{}) string { return acctest.Nprintf(` resource "google_netapp_storage_pool" "test_pool" { - provider = google-beta name = "tf-test-pool%{random_suffix}" location = "us-east1" service_level = "FLEX" @@ -321,10 +312,7 @@ resource "time_sleep" "wait_5_minutes" { } data "google_compute_network" "default" { - provider = google-beta name = "%{network_name}" } `, context) } - -{{ end }} diff --git a/mmv1/third_party/terraform/services/networkconnectivity/resource_network_connectivity_group_test.go b/mmv1/third_party/terraform/services/networkconnectivity/resource_network_connectivity_group_test.go new file mode 100644 index 000000000000..68b531193d0c --- /dev/null +++ b/mmv1/third_party/terraform/services/networkconnectivity/resource_network_connectivity_group_test.go @@ -0,0 +1,95 @@ +package networkconnectivity_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +func TestAccNetworkConnectivityGroup_BasicGroup(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project_name": envvar.GetTestProjectFromEnv(), + "hub_random_suffix": acctest.RandString(t, 10), + "project_random_suffix_1": acctest.RandString(t, 10), + "project_random_suffix_2": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckNetworkConnectivityGroupDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetworkConnectivityGroup_BasicGroup(context), + }, + { + ResourceName: "google_network_connectivity_group.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"hub", "labels", "terraform_labels"}, + }, + { + Config: testAccNetworkConnectivityGroup_BasicGroupUpdate0(context), + }, + { + ResourceName: "google_network_connectivity_group.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"hub", "labels", "terraform_labels"}, + }, + }, + }) +} + +func testAccNetworkConnectivityGroup_BasicGroup(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_network_connectivity_hub" "basic_hub" { + name = "tf-test-hub%{hub_random_suffix}" + description = "A sample hub" + project = "%{project_name}" + labels = { + label-one = "value-one" + } +} +resource "google_network_connectivity_group" "primary" { + hub = google_network_connectivity_hub.basic_hub.id + name = "default" + labels = { + label-one = "value-one" + } + description = "A sample group" + auto_accept { + auto_accept_projects = ["tf-test-name%{project_random_suffix_1}"] + } +} +`, context) +} + +func testAccNetworkConnectivityGroup_BasicGroupUpdate0(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_network_connectivity_hub" "basic_hub" { + name = "tf-test-hub%{hub_random_suffix}" + description = "A sample hub" + project = "%{project_name}" + labels = { + label-one = "value-one" + } +} +resource "google_network_connectivity_group" "primary" { + hub = google_network_connectivity_hub.basic_hub.id + name = "default" + labels = { + label-two = "value-one" + } + description = "An updated sample group" + auto_accept { + auto_accept_projects = ["tf-test-name%{project_random_suffix_1}", "tf-test-name%{project_random_suffix_2}"] + } +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_database.go b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_database.go new file mode 100644 index 000000000000..30e49b7e1c2f --- /dev/null +++ b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_database.go @@ -0,0 +1,41 @@ +package oracledatabase + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func DataSourceOracleDatabaseAutonomousDatabase() *schema.Resource { + dsSchema := tpgresource.DatasourceSchemaFromResourceSchema(ResourceOracleDatabaseAutonomousDatabase().Schema) + tpgresource.AddRequiredFieldsToSchema(dsSchema, "location", "autonomous_database_id") + tpgresource.AddOptionalFieldsToSchema(dsSchema, "project") + return &schema.Resource{ + Read: dataSourceOracleDatabaseAutonomousDatabaseRead, + Schema: dsSchema, + } + +} + +func dataSourceOracleDatabaseAutonomousDatabaseRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/autonomousDatabases/{{autonomous_database_id}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + + d.SetId(id) + + err = resourceOracleDatabaseAutonomousDatabaseRead(d, meta) + if err != nil { + return err + } + + if d.Id() == "" { + return fmt.Errorf("%s not found", id) + } + return nil +} diff --git a/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_database_test.go b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_database_test.go new file mode 100644 index 000000000000..fce8fe50cf32 --- /dev/null +++ b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_database_test.go @@ -0,0 +1,40 @@ +package oracledatabase_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccOracleDatabaseAutonomousDatabase_basic(t *testing.T) { + t.Parallel() + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccOracleDatabaseAutonomousDatabase_basic(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_database.my-adb", "display_name"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_database.my-adb", "database"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_database.my-adb", "cidr"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_database.my-adb", "network"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_database.my-adb", "properties.#"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_database.my-adb", "properties.0.character_set"), + ), + }, + }, + }) +} + +func testAccOracleDatabaseAutonomousDatabase_basic() string { + return fmt.Sprintf(` +data "google_oracle_database_autonomous_database" "my-adb"{ + autonomous_database_id = "do-not-delete-tf-adb" + location = "us-east4" + project = "oci-terraform-testing" +} +`) +} diff --git a/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_databases.go b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_databases.go new file mode 100644 index 000000000000..08441d6be785 --- /dev/null +++ b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_databases.go @@ -0,0 +1,112 @@ +package oracledatabase + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func DataSourceOracleDatabaseAutonomousDatabases() *schema.Resource { + dsSchema := map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Optional: true, + Description: "The ID of the project in which the dataset is located. If it is not provided, the provider project is used.", + }, + "location": { + Type: schema.TypeString, + Required: true, + Description: "location", + }, + "autonomous_databases": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: tpgresource.DatasourceSchemaFromResourceSchema(ResourceOracleDatabaseAutonomousDatabase().Schema), + }, + }, + } + return &schema.Resource{ + Read: dataSourceOracleDatabaseAutonomousDatabasesRead, + Schema: dsSchema, + } + +} + +func dataSourceOracleDatabaseAutonomousDatabasesRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{OracleDatabaseBasePath}}projects/{{project}}/locations/{{location}}/autonomousDatabases") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + + billingProject := "" + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for autonomousDatabases: %s", err) + } + billingProject = project + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + + if err != nil { + return fmt.Errorf("Error reading autonomousDatabases: %s", err) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error setting autonomousDatabases project: %s", err) + } + + if err := d.Set("autonomous_databases", flattenOracleDatabaseautonomousDatabases(res["autonomousDatabases"], d, config)); err != nil { + return fmt.Errorf("Error setting autonomousDatabases: %s", err) + } + + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/autonomousDatabases") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return nil +} + +func flattenOracleDatabaseautonomousDatabases(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) []map[string]interface{} { + if v == nil { + return nil + } + l := v.([]interface{}) + transformed := make([]map[string]interface{}, 0) + for _, raw := range l { + original := raw.(map[string]interface{}) + transformed = append(transformed, map[string]interface{}{ + "name": flattenOracleDatabaseAutonomousDatabaseName(original["name"], d, config), + "database": flattenOracleDatabaseAutonomousDatabaseDatabase(original["database"], d, config), + "display_name": flattenOracleDatabaseAutonomousDatabaseDisplayName(original["displayName"], d, config), + "entitlement_id": flattenOracleDatabaseAutonomousDatabaseEntitlementId(original["entitlementId"], d, config), + "properties": flattenOracleDatabaseAutonomousDatabaseProperties(original["properties"], d, config), + "labels": flattenOracleDatabaseAutonomousDatabaseLabels(original["labels"], d, config), + "network": flattenOracleDatabaseAutonomousDatabaseNetwork(original["network"], d, config), + "cidr": flattenOracleDatabaseAutonomousDatabaseCidr(original["cidr"], d, config), + "create_time": flattenOracleDatabaseAutonomousDatabaseCreateTime(original["createTime"], d, config), + "terraform_labels": flattenOracleDatabaseAutonomousDatabaseTerraformLabels(original["labels"], d, config), + "effective_labels": flattenOracleDatabaseAutonomousDatabaseEffectiveLabels(original["labels"], d, config), + }) + } + return transformed +} diff --git a/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_databases_test.go b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_databases_test.go new file mode 100644 index 000000000000..5048247fe538 --- /dev/null +++ b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_autonomous_databases_test.go @@ -0,0 +1,41 @@ +package oracledatabase_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccOracleDatabaseAutonomousDatabases_basic(t *testing.T) { + t.Parallel() + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccOracleDatabaseAutonomousDatabases_basic(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_databases.my-adbs", "autonomous_databases.#"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_databases.my-adbs", "autonomous_databases.0.display_name"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_databases.my-adbs", "autonomous_databases.0.cidr"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_databases.my-adbs", "autonomous_databases.0.network"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_databases.my-adbs", "autonomous_databases.0.entitlement_id"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_databases.my-adbs", "autonomous_databases.0.database"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_databases.my-adbs", "autonomous_databases.0.properties.#"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_autonomous_databases.my-adbs", "autonomous_databases.0.properties.0.state"), + ), + }, + }, + }) +} + +func testAccOracleDatabaseAutonomousDatabases_basic() string { + return fmt.Sprintf(` +data "google_oracle_database_autonomous_databases" "my-adbs"{ + location = "us-east4" + project = "oci-terraform-testing" +} +`) +} diff --git a/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_cloud_vm_clusters.go b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_cloud_vm_clusters.go new file mode 100644 index 000000000000..739b09212745 --- /dev/null +++ b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_cloud_vm_clusters.go @@ -0,0 +1,113 @@ +package oracledatabase + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func DataSourceOracleDatabaseCloudVmClusters() *schema.Resource { + dsSchema := map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Optional: true, + Description: "The ID of the project in which the dataset is located. If it is not provided, the provider project is used.", + }, + "location": { + Type: schema.TypeString, + Required: true, + Description: "location", + }, + "cloud_vm_clusters": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: tpgresource.DatasourceSchemaFromResourceSchema(ResourceOracleDatabaseCloudVmCluster().Schema), + }, + }, + } + return &schema.Resource{ + Read: dataSourceOracleDatabaseCloudVmClustersRead, + Schema: dsSchema, + } + +} + +func dataSourceOracleDatabaseCloudVmClustersRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{OracleDatabaseBasePath}}projects/{{project}}/locations/{{location}}/cloudVmClusters") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/cloudVmClusters") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + billingProject := "" + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for cloudVmClusters: %s", err) + } + billingProject = project + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + + if err != nil { + return fmt.Errorf("Error reading cloudVmClusters: %s", err) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error setting cloudVmClusters project: %s", err) + } + + if err := d.Set("cloud_vm_clusters", flattenOracleDatabaseCloudVmClusters(res["cloudVmClusters"], d, config)); err != nil { + return fmt.Errorf("Error setting cloudVmClusters: %s", err) + } + + return nil +} + +func flattenOracleDatabaseCloudVmClusters(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) []map[string]interface{} { + if v == nil { + return nil + } + l := v.([]interface{}) + transformed := make([]map[string]interface{}, 0) + for _, raw := range l { + original := raw.(map[string]interface{}) + transformed = append(transformed, map[string]interface{}{ + "name": flattenOracleDatabaseCloudVmClusterName(original["name"], d, config), + "exadata_infrastructure": flattenOracleDatabaseCloudVmClusterExadataInfrastructure(original["exadataInfrastructure"], d, config), + "display_name": flattenOracleDatabaseCloudVmClusterDisplayName(original["displayName"], d, config), + "gcp_oracle_zone": flattenOracleDatabaseCloudVmClusterGcpOracleZone(original["gcpOracleZone"], d, config), + "properties": flattenOracleDatabaseCloudVmClusterProperties(original["properties"], d, config), + "labels": flattenOracleDatabaseCloudVmClusterLabels(original["labels"], d, config), + "create_time": flattenOracleDatabaseCloudVmClusterCreateTime(original["createTime"], d, config), + "cidr": flattenOracleDatabaseCloudVmClusterCidr(original["cidr"], d, config), + "backup_subnet_cidr": flattenOracleDatabaseCloudVmClusterBackupSubnetCidr(original["backupSubnetCidr"], d, config), + "network": flattenOracleDatabaseCloudVmClusterNetwork(original["network"], d, config), + "terraform_labels": flattenOracleDatabaseCloudVmClusterTerraformLabels(original["labels"], d, config), + "effective_labels": flattenOracleDatabaseCloudVmClusterEffectiveLabels(original["labels"], d, config), + }) + } + return transformed +} diff --git a/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_cloud_vm_clusters_test.go b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_cloud_vm_clusters_test.go new file mode 100644 index 000000000000..4a38d480cbbf --- /dev/null +++ b/mmv1/third_party/terraform/services/oracledatabase/data_source_oracle_database_cloud_vm_clusters_test.go @@ -0,0 +1,36 @@ +package oracledatabase_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccOracleDatabaseCloudVmClusters_basic(t *testing.T) { + t.Parallel() + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccOracleDatabaseCloudVmClusters_basic(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.google_oracle_database_cloud_vm_clusters.my_vmclusters", "cloud_vm_clusters.#"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_cloud_vm_clusters.my_vmclusters", "cloud_vm_clusters.0.display_name"), + resource.TestCheckResourceAttrSet("data.google_oracle_database_cloud_vm_clusters.my_vmclusters", "cloud_vm_clusters.0.properties.#"), + ), + }, + }, + }) +} + +func testAccOracleDatabaseCloudVmClusters_basic() string { + return fmt.Sprintf(` +data "google_oracle_database_cloud_vm_clusters" "my_vmclusters"{ + location = "us-east4" + project = "oci-terraform-testing" +} +`) +} diff --git a/mmv1/third_party/terraform/services/pubsub/resource_pubsub_subscription_test.go b/mmv1/third_party/terraform/services/pubsub/resource_pubsub_subscription_test.go index 403a732ded4d..9be5b447b8e4 100644 --- a/mmv1/third_party/terraform/services/pubsub/resource_pubsub_subscription_test.go +++ b/mmv1/third_party/terraform/services/pubsub/resource_pubsub_subscription_test.go @@ -207,6 +207,9 @@ func TestAccPubsubSubscriptionBigQuery_update(t *testing.T) { PreCheck: func() { acctest.AccTestPreCheck(t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), CheckDestroy: testAccCheckPubsubSubscriptionDestroyProducer(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, Steps: []resource.TestStep{ { Config: testAccPubsubSubscriptionBigQuery_basic(dataset, table, topic, subscriptionShort, false, ""), @@ -238,10 +241,17 @@ func TestAccPubsubSubscriptionBigQuery_serviceAccount(t *testing.T) { topic := fmt.Sprintf("tf-test-topic-%s", acctest.RandString(t, 10)) subscriptionShort := fmt.Sprintf("tf-test-sub-%s", acctest.RandString(t, 10)) + if acctest.BootstrapPSARoles(t, "service-", "gcp-sa-pubsub", []string{"roles/bigquery.dataEditor", "roles/bigquery.metadataViewer"}) { + t.Fatal("Stopping the test because roles were added to IAM policy.") + } + acctest.VcrTest(t, resource.TestCase{ PreCheck: func() { acctest.AccTestPreCheck(t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), CheckDestroy: testAccCheckPubsubSubscriptionDestroyProducer(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, Steps: []resource.TestStep{ { Config: testAccPubsubSubscriptionBigQuery_basic(dataset, table, topic, subscriptionShort, false, "bq-test-sa"), @@ -681,40 +691,44 @@ resource "google_service_account" "bq_write_service_account" { } resource "google_project_iam_member" "viewer" { - project = data.google_project.project.project_id - role = "roles/bigquery.metadataViewer" - member = "serviceAccount:${google_service_account.bq_write_service_account.email}" + project = data.google_project.project.project_id + role = "roles/bigquery.metadataViewer" + member = "serviceAccount:${google_service_account.bq_write_service_account.email}" } resource "google_project_iam_member" "editor" { - project = data.google_project.project.project_id - role = "roles/bigquery.dataEditor" - member = "serviceAccount:${google_service_account.bq_write_service_account.email}" + project = data.google_project.project.project_id + role = "roles/bigquery.dataEditor" + member = "serviceAccount:${google_service_account.bq_write_service_account.email}" }`, serviceAccountId) serviceAccountEmailField = "service_account_email = google_service_account.bq_write_service_account.email" } else { serviceAccountResource = fmt.Sprintf(` resource "google_project_iam_member" "viewer" { - project = data.google_project.project.project_id - role = "roles/bigquery.metadataViewer" - member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-pubsub.iam.gserviceaccount.com" + project = data.google_project.project.project_id + role = "roles/bigquery.metadataViewer" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-pubsub.iam.gserviceaccount.com" } resource "google_project_iam_member" "editor" { - project = data.google_project.project.project_id - role = "roles/bigquery.dataEditor" - member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-pubsub.iam.gserviceaccount.com" + project = data.google_project.project.project_id + role = "roles/bigquery.dataEditor" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-pubsub.iam.gserviceaccount.com" } `) } return fmt.Sprintf(` -data "google_project" "project" { } +data "google_project" "project" {} + +resource "time_sleep" "wait_30_seconds" { + create_duration = "30s" +} %s resource "google_bigquery_dataset" "test" { - dataset_id = "%s" + dataset_id = "%s" } resource "google_bigquery_table" "test" { @@ -745,12 +759,13 @@ resource "google_pubsub_subscription" "foo" { bigquery_config { table = "${google_bigquery_table.test.project}.${google_bigquery_table.test.dataset_id}.${google_bigquery_table.test.table_id}" use_table_schema = %t - %s + %s } depends_on = [ google_project_iam_member.viewer, - google_project_iam_member.editor + google_project_iam_member.editor, + time_sleep.wait_30_seconds, ] } `, serviceAccountResource, dataset, table, topic, subscription, useTableSchema, serviceAccountEmailField) diff --git a/mmv1/third_party/terraform/services/resourcemanager/data_source_google_service_account.go b/mmv1/third_party/terraform/services/resourcemanager/data_source_google_service_account.go index 15b075a1e763..de7cb897cebe 100644 --- a/mmv1/third_party/terraform/services/resourcemanager/data_source_google_service_account.go +++ b/mmv1/third_party/terraform/services/resourcemanager/data_source_google_service_account.go @@ -41,6 +41,10 @@ func DataSourceGoogleServiceAccount() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "disabled": { + Type: schema.TypeBool, + Computed: true, + }, }, } } @@ -84,6 +88,9 @@ func dataSourceGoogleServiceAccountRead(d *schema.ResourceData, meta interface{} if err := d.Set("member", "serviceAccount:"+sa.Email); err != nil { return fmt.Errorf("Error setting member: %s", err) } + if err := d.Set("disabled", sa.Disabled); err != nil { + return fmt.Errorf("Error setting disabled: %s", err) + } return nil } diff --git a/mmv1/third_party/terraform/services/resourcemanager/data_source_google_service_account_test.go b/mmv1/third_party/terraform/services/resourcemanager/data_source_google_service_account_test.go index a3f35308929f..ffb22244691f 100644 --- a/mmv1/third_party/terraform/services/resourcemanager/data_source_google_service_account_test.go +++ b/mmv1/third_party/terraform/services/resourcemanager/data_source_google_service_account_test.go @@ -29,6 +29,7 @@ func TestAccDatasourceGoogleServiceAccount_basic(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "name"), resource.TestCheckResourceAttrSet(resourceName, "display_name"), resource.TestCheckResourceAttrSet(resourceName, "member"), + resource.TestCheckResourceAttrSet(resourceName, "disabled"), ), }, }, diff --git a/mmv1/third_party/terraform/services/securitycenterv2/resource_scc_v2_organization_big_query_export_config_test.go b/mmv1/third_party/terraform/services/securitycenterv2/resource_scc_v2_organization_big_query_export_config_test.go index 96a49e6bf9b6..679d25f325a0 100644 --- a/mmv1/third_party/terraform/services/securitycenterv2/resource_scc_v2_organization_big_query_export_config_test.go +++ b/mmv1/third_party/terraform/services/securitycenterv2/resource_scc_v2_organization_big_query_export_config_test.go @@ -65,6 +65,7 @@ resource "google_bigquery_dataset" "default" { location = "US" default_table_expiration_ms = 3600000 default_partition_expiration_ms = null + delete_contents_on_destroy = true labels = { env = "default" @@ -77,7 +78,7 @@ resource "google_bigquery_dataset" "default" { resource "time_sleep" "wait_1_minute" { depends_on = [google_bigquery_dataset.default] - create_duration = "3m" + create_duration = "6m" } resource "google_scc_v2_organization_scc_big_query_export" "default" { @@ -109,6 +110,7 @@ resource "google_bigquery_dataset" "default" { location = "US" default_table_expiration_ms = 3600000 default_partition_expiration_ms = null + delete_contents_on_destroy = true labels = { env = "default" diff --git a/mmv1/third_party/terraform/services/securitycenterv2/resource_scc_v2_organization_big_query_exports_config_test.go b/mmv1/third_party/terraform/services/securitycenterv2/resource_scc_v2_organization_big_query_exports_config_test.go index 932394d641c1..aa003308d943 100644 --- a/mmv1/third_party/terraform/services/securitycenterv2/resource_scc_v2_organization_big_query_exports_config_test.go +++ b/mmv1/third_party/terraform/services/securitycenterv2/resource_scc_v2_organization_big_query_exports_config_test.go @@ -65,6 +65,7 @@ resource "google_bigquery_dataset" "default" { location = "US" default_table_expiration_ms = 3600000 default_partition_expiration_ms = null + delete_contents_on_destroy = true labels = { env = "default" @@ -77,7 +78,7 @@ resource "google_bigquery_dataset" "default" { resource "time_sleep" "wait_1_minute" { depends_on = [google_bigquery_dataset.default] - create_duration = "3m" + create_duration = "6m" } resource "google_scc_v2_organization_scc_big_query_exports" "default" { @@ -109,6 +110,7 @@ resource "google_bigquery_dataset" "default" { location = "US" default_table_expiration_ms = 3600000 default_partition_expiration_ms = null + delete_contents_on_destroy = true labels = { env = "default" diff --git a/mmv1/third_party/terraform/services/spanner/resource_spanner_instance_test.go b/mmv1/third_party/terraform/services/spanner/resource_spanner_instance_test.go index fbc32c3efaed..2343a09815c3 100644 --- a/mmv1/third_party/terraform/services/spanner/resource_spanner_instance_test.go +++ b/mmv1/third_party/terraform/services/spanner/resource_spanner_instance_test.go @@ -312,6 +312,53 @@ func TestAccSpannerInstance_basicWithAutoscalingUsingNodeConfigUpdate(t *testing }) } +func TestAccSpannerInstance_basicWithAsymmetricAutoscalingConfigsUpdate(t *testing.T) { + displayName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)) + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckSpannerInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccSpannerInstance_main(displayName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_spanner_instance.main", "state"), + ), + }, + { + ResourceName: "google_spanner_instance.main", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"labels", "terraform_labels"}, + }, + { + Config: testAccSpannerInstance_basicWithAsymmetricAutoscalingConfigsUpdate(displayName, 1, 10), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_spanner_instance.main", "state"), + ), + }, + { + ResourceName: "google_spanner_instance.main", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"labels", "terraform_labels"}, + }, + { + Config: testAccSpannerInstance_basicWithAsymmetricAutoscalingConfigsUpdate(displayName, 3, 5), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_spanner_instance.main", "state"), + ), + }, + { + ResourceName: "google_spanner_instance.main", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"labels", "terraform_labels"}, + }, + }, + }) +} + func testAccSpannerInstance_basic(name string) string { return fmt.Sprintf(` resource "google_spanner_instance" "basic" { @@ -319,7 +366,7 @@ resource "google_spanner_instance" "basic" { config = "regional-us-central1" display_name = "%s-dname" num_nodes = 1 - edition = "ENTERPRISE" + edition = "ENTERPRISE" } `, name, name) } @@ -458,3 +505,63 @@ resource "google_spanner_instance" "basic" { } `, name, name, maxNodes, minNodes, cupUtilizationPercent, storageUtilizationPercent) } + +func testAccSpannerInstance_main(name string) string { + return fmt.Sprintf(` +resource "google_spanner_instance" "main" { + name = "%s" + config = "nam-eur-asia3" + display_name = "%s" + num_nodes = 1 + edition = "ENTERPRISE_PLUS" +} +`, name, name) +} + +func testAccSpannerInstance_basicWithAsymmetricAutoscalingConfigsUpdate(name string, minNodes, maxNodes int) string { + return fmt.Sprintf(` +provider "google" { + alias = "user-project-override" + user_project_override = true +} + +resource "google_spanner_instance" "main" { + provider = google.user-project-override + name = "%s" + config = "nam-eur-asia3" + display_name = "%s" + autoscaling_config { + autoscaling_limits { + max_nodes = 3 + min_nodes = 1 + } + autoscaling_targets { + high_priority_cpu_utilization_percent = 75 + storage_utilization_percent = 90 + } + asymmetric_autoscaling_options { + replica_selection { + location = "europe-west1" + } + overrides { + autoscaling_limits { + min_nodes = 3 + max_nodes = 30 + } + } + } + asymmetric_autoscaling_options { + replica_selection { + location = "asia-east1" + } + overrides { + autoscaling_limits { + min_nodes = %d + max_nodes = %d + } + } + } + } + edition = "ENTERPRISE_PLUS" +}`, name, name, minNodes, maxNodes) +} diff --git a/mmv1/third_party/terraform/services/sql/resource_sql_database_instance.go.tmpl b/mmv1/third_party/terraform/services/sql/resource_sql_database_instance.go.tmpl index 160f138a7519..08da2c84defc 100644 --- a/mmv1/third_party/terraform/services/sql/resource_sql_database_instance.go.tmpl +++ b/mmv1/third_party/terraform/services/sql/resource_sql_database_instance.go.tmpl @@ -177,7 +177,7 @@ func ResourceSqlDatabaseInstance() *schema.Resource { "edition": { Type: schema.TypeString, Optional: true, - Default: "ENTERPRISE", + Computed: true, ValidateFunc: validation.StringInSlice([]string{"ENTERPRISE", "ENTERPRISE_PLUS"}, false), Description: `The edition of the instance, can be ENTERPRISE or ENTERPRISE_PLUS.`, }, @@ -198,6 +198,7 @@ func ResourceSqlDatabaseInstance() *schema.Resource { "data_cache_config": { Type: schema.TypeList, Optional: true, + Computed: true, MaxItems: 1, Description: `Data cache configurations.`, Elem: &schema.Resource{ diff --git a/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_test.go b/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_test.go index 40cd23f6eec8..0cf6813466d7 100644 --- a/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_test.go +++ b/mmv1/third_party/terraform/services/sql/resource_sql_database_instance_test.go @@ -1596,6 +1596,32 @@ func TestAccSQLDatabaseInstance_DenyMaintenancePeriod(t *testing.T) { }) } +func TestAccSQLDatabaseInstance_DefaultEdition(t *testing.T) { + t.Parallel() + databaseName := "tf-test-" + acctest.RandString(t, 10) + databaseVersion := "POSTGRES_16" + enterprisePlusTier := "db-perf-optimized-N-2" + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testGoogleSqlDatabaseInstance_DefaultEdition(databaseName, databaseVersion, enterprisePlusTier), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("google_sql_database_instance.instance", "settings.0.edition", "ENTERPRISE_PLUS"), + ), + }, + { + ResourceName: "google_sql_database_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + }, + }) +} + func TestAccSqlDatabaseInstance_Edition(t *testing.T) { t.Parallel() enterprisePlusName := "tf-test-enterprise-plus" + acctest.RandString(t, 10) @@ -1755,7 +1781,7 @@ func TestAccSqlDatabaseInstance_Postgres_Edition_Upgrade(t *testing.T) { CheckDestroy: testAccSqlDatabaseInstanceDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testGoogleSqlDatabaseInstance_EditionConfig_noEdition(editionUpgrade, enterpriseTier), + Config: testGoogleSqlDatabaseInstance_EditionConfig(editionUpgrade, enterpriseTier, "ENTERPRISE"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("google_sql_database_instance.instance", "settings.0.edition", "ENTERPRISE"), ), @@ -1783,6 +1809,7 @@ func TestAccSqlDatabaseInstance_Postgres_Edition_Upgrade(t *testing.T) { } func TestAccSqlDatabaseInstance_Edition_Downgrade(t *testing.T) { + t.Skip("https://github.com/hashicorp/terraform-provider-google/issues/20010") t.Parallel() enterprisePlusTier := "db-perf-optimized-N-2" enterpriseTier := "db-custom-2-13312" @@ -1805,7 +1832,7 @@ func TestAccSqlDatabaseInstance_Edition_Downgrade(t *testing.T) { ImportStateVerifyIgnore: []string{"deletion_protection"}, }, { - Config: testGoogleSqlDatabaseInstance_EditionConfig_noEdition(editionDowngrade, enterpriseTier), + Config: testGoogleSqlDatabaseInstance_EditionConfig(editionDowngrade, enterpriseTier, "ENTERPRISE"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("google_sql_database_instance.instance", "settings.0.edition", "ENTERPRISE"), ), @@ -2681,6 +2708,19 @@ resource "google_sql_database_instance" "instance" { }`, databaseName, endDate, startDate, time) } +func testGoogleSqlDatabaseInstance_DefaultEdition(databaseName, databaseVersion, tier string) string { + return fmt.Sprintf(` +resource "google_sql_database_instance" "instance" { + name = "%s" + region = "us-east1" + database_version = "%s" + deletion_protection = false + settings { + tier = "%s" + } +}`, databaseName, databaseVersion, tier) +} + func testGoogleSqlDatabaseInstance_EditionConfig_noEdition(databaseName, tier string) string { return fmt.Sprintf(` @@ -2691,9 +2731,6 @@ resource "google_sql_database_instance" "instance" { deletion_protection = false settings { tier = "%s" - backup_configuration { - transaction_log_retention_days = 7 - } } }`, databaseName, tier) } @@ -2709,6 +2746,9 @@ resource "google_sql_database_instance" "instance" { settings { tier = "%s" edition = "%s" + backup_configuration { + transaction_log_retention_days = 7 + } } }`, databaseName, tier, edition) } diff --git a/mmv1/third_party/terraform/services/sql/resource_sql_database_test.go b/mmv1/third_party/terraform/services/sql/resource_sql_database_test.go index aabe707bc983..44250cab79f0 100644 --- a/mmv1/third_party/terraform/services/sql/resource_sql_database_test.go +++ b/mmv1/third_party/terraform/services/sql/resource_sql_database_test.go @@ -183,6 +183,67 @@ func testAccSqlDatabaseDestroyProducer(t *testing.T) func(s *terraform.State) er } } +func TestAccSqlDatabase_instanceWithActivationPolicy(t *testing.T) { + t.Parallel() + + var database sqladmin.Database + + instance_name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t)) + database_name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccSqlDatabaseDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testGoogleSqlDatabase_instanceWithActivationPolicy(instance_name, database_name, "ALWAYS"), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleSqlDatabaseExists( + t, "google_sql_database.database", &database), + ), + }, + // Step 2: Update activation_policy to NEVER + { + Config: testGoogleSqlDatabase_instanceWithActivationPolicy(instance_name, database_name, "NEVER"), + }, + // Step 3: Refresh to verify no errors + { + Config: testGoogleSqlDatabase_instanceWithActivationPolicy(instance_name, database_name, "NEVER"), + }, + // Step 4: Update activation_policy to ALWAYS so that post-test destroy code is able to delete the google_sql_database resource + { + Config: testGoogleSqlDatabase_instanceWithActivationPolicy(instance_name, database_name, "ALWAYS"), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleSqlDatabaseExists( + t, "google_sql_database.database", &database), + ), + }, + }, + }) +} + +func testGoogleSqlDatabase_instanceWithActivationPolicy(instance_name, database_name, activationPolicy string) string { + return fmt.Sprintf(` +resource "google_sql_database_instance" "instance" { + name = "%s" + database_version = "MYSQL_5_7" + region = "us-central1" + deletion_protection = false + settings { + tier = "db-f1-micro" + availability_type = "ZONAL" + activation_policy = "%s" + } +} + +resource "google_sql_database" "database" { + name = "%s" + instance = google_sql_database_instance.instance.name + } +`, instance_name, activationPolicy, database_name) +} + var testGoogleSqlDatabase_basic = ` resource "google_sql_database_instance" "instance" { name = "%s" diff --git a/mmv1/third_party/terraform/services/sql/resource_sql_user.go b/mmv1/third_party/terraform/services/sql/resource_sql_user.go index c507f883f122..f54e1fdac50a 100644 --- a/mmv1/third_party/terraform/services/sql/resource_sql_user.go +++ b/mmv1/third_party/terraform/services/sql/resource_sql_user.go @@ -326,6 +326,13 @@ func resourceSqlUserRead(d *schema.ResourceData, meta interface{}) error { instance := d.Get("instance").(string) name := d.Get("name").(string) host := d.Get("host").(string) + databaseInstance, err := config.NewSqlAdminClient(userAgent).Instances.Get(project, instance).Do() + if err != nil { + return err + } + if databaseInstance.Settings.ActivationPolicy != "ALWAYS" { + return nil + } var users *sqladmin.UsersListResponse err = nil @@ -342,11 +349,6 @@ func resourceSqlUserRead(d *schema.ResourceData, meta interface{}) error { } var user *sqladmin.User - databaseInstance, err := config.NewSqlAdminClient(userAgent).Instances.Get(project, instance).Do() - if err != nil { - return err - } - for _, currentUser := range users.Items { var username string if !(strings.Contains(databaseInstance.DatabaseVersion, "POSTGRES") || currentUser.Type == "CLOUD_IAM_GROUP") { diff --git a/mmv1/third_party/terraform/services/sql/resource_sql_user_test.go b/mmv1/third_party/terraform/services/sql/resource_sql_user_test.go index 80c50c7363ce..a14ad85a4fe2 100644 --- a/mmv1/third_party/terraform/services/sql/resource_sql_user_test.go +++ b/mmv1/third_party/terraform/services/sql/resource_sql_user_test.go @@ -324,6 +324,65 @@ func TestAccSqlUser_mysqlPasswordPolicy(t *testing.T) { }) } +func TestAccSqlUser_instanceWithActivationPolicy(t *testing.T) { + // Multiple fine-grained resources + acctest.SkipIfVcr(t) + t.Parallel() + + instance := fmt.Sprintf("tf-test-%d", acctest.RandInt(t)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccSqlUserDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testGoogleSqlUser_instanceWithActivationPolicy(instance, "ALWAYS"), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleSqlUserExists(t, "google_sql_user.user"), + ), + }, + // Step 2: Update activation_policy to NEVER + { + Config: testGoogleSqlUser_instanceWithActivationPolicy(instance, "NEVER"), + }, + // Step 3: Refresh to verify no errors + { + Config: testGoogleSqlUser_instanceWithActivationPolicy(instance, "NEVER"), + }, + // Step 4: Update activation_policy to ALWAYS so that post-test destroy code is able to delete the google_sql_user resource + { + Config: testGoogleSqlUser_instanceWithActivationPolicy(instance, "ALWAYS"), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleSqlUserExists(t, "google_sql_user.user"), + ), + }, + }, + }) +} + +func testGoogleSqlUser_instanceWithActivationPolicy(instance, activationPolicy string) string { + return fmt.Sprintf(` +resource "google_sql_database_instance" "instance" { + name = "%s" + database_version = "MYSQL_5_7" + region = "us-central1" + deletion_protection = false + settings { + tier = "db-f1-micro" + availability_type = "ZONAL" + activation_policy = "%s" + } +} + +resource "google_sql_user" "user" { + name = "admin" + instance = google_sql_database_instance.instance.name + password = "password" + } +`, instance, activationPolicy) +} + func testGoogleSqlUser_mysql(instance, password string) string { return fmt.Sprintf(` resource "google_sql_database_instance" "instance" { diff --git a/mmv1/third_party/terraform/services/storage/resource_storage_bucket.go.tmpl b/mmv1/third_party/terraform/services/storage/resource_storage_bucket.go.tmpl index c3e9ee9cc63d..16d54bfcb343 100644 --- a/mmv1/third_party/terraform/services/storage/resource_storage_bucket.go.tmpl +++ b/mmv1/third_party/terraform/services/storage/resource_storage_bucket.go.tmpl @@ -961,18 +961,9 @@ func resourceStorageBucketRead(d *schema.ResourceData, meta interface{}) error { // Get the bucket and acl bucket := d.Get("name").(string) - var res *storage.Bucket // There seems to be some eventual consistency errors in some cases, so we want to check a few times // to make sure it exists before moving on - err = transport_tpg.Retry(transport_tpg.RetryOptions{ - RetryFunc: func() (operr error) { - var retryErr error - res, retryErr = config.NewStorageClient(userAgent).Buckets.Get(bucket).Do() - return retryErr - }, - Timeout: d.Timeout(schema.TimeoutRead), - ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.IsNotFoundRetryableError("bucket read")}, - }) + res, err := config.NewStorageClient(userAgent).Buckets.Get(bucket).Do() if err != nil { return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("Storage Bucket %q", d.Get("name").(string))) diff --git a/mmv1/third_party/terraform/services/tags/resource_tags_test.go b/mmv1/third_party/terraform/services/tags/resource_tags_test.go index f883b401ed7e..a81bb3a1fa96 100644 --- a/mmv1/third_party/terraform/services/tags/resource_tags_test.go +++ b/mmv1/third_party/terraform/services/tags/resource_tags_test.go @@ -235,8 +235,8 @@ resource "google_tags_tag_key" "key" { resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "foo%{random_suffix}" + parent = google_tags_tag_key.key.id + short_name = "foo%{random_suffix}" description = "For foo resources." } `, context) @@ -284,8 +284,8 @@ resource "google_tags_tag_key" "key" { resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "foo%{random_suffix}" + parent = google_tags_tag_key.key.id + short_name = "foo%{random_suffix}" description = "For foo resources." } `, context) @@ -302,8 +302,8 @@ resource "google_tags_tag_key" "key" { resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "foo%{random_suffix}" + parent = google_tags_tag_key.key.id + short_name = "foo%{random_suffix}" description = "For any foo resources." } `, context) @@ -388,14 +388,14 @@ resource "google_tags_tag_key" "key" { } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "foo%{random_suffix}" + parent = google_tags_tag_key.key.id + short_name = "foo%{random_suffix}" description = "For foo%{random_suffix} resources." } resource "google_tags_tag_binding" "binding" { - parent = "//cloudresourcemanager.googleapis.com/projects/${google_project.project.number}" - tag_value = "tagValues/${google_tags_tag_value.value.name}" + parent = "//cloudresourcemanager.googleapis.com/projects/${google_project.project.number}" + tag_value = google_tags_tag_value.value.id } `, context) } @@ -692,8 +692,8 @@ resource "google_tags_tag_key" "key" { } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "%{value_short_name}" + parent = google_tags_tag_key.key.id + short_name = "%{value_short_name}" description = "For %{value_short_name} resources." } @@ -714,8 +714,8 @@ resource "google_tags_tag_key" "key" { } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "%{value_short_name}" + parent = google_tags_tag_key.key.id + short_name = "%{value_short_name}" description = "For %{value_short_name} resources." } @@ -742,8 +742,8 @@ resource "google_tags_tag_key" "key" { } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "%{value_short_name}" + parent = google_tags_tag_key.key.id + short_name = "%{value_short_name}" description = "For %{value_short_name} resources." } @@ -766,8 +766,8 @@ resource "google_tags_tag_key" "key" { } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "%{value_short_name}" + parent = google_tags_tag_key.key.id + short_name = "%{value_short_name}" description = "For %{value_short_name} resources." } @@ -788,8 +788,8 @@ resource "google_tags_tag_key" "key" { } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "%{value_short_name}" + parent = google_tags_tag_key.key.id + short_name = "%{value_short_name}" description = "For %{value_short_name} resources." } @@ -842,8 +842,8 @@ resource "google_tags_tag_key" "key" { } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "foo%{random_suffix}" + parent = google_tags_tag_key.key.id + short_name = "foo%{random_suffix}" description = "For foo%{random_suffix} resources." } @@ -866,9 +866,9 @@ resource "google_cloud_run_service" "default" { } resource "google_tags_location_tag_binding" "binding" { - parent = "//run.googleapis.com/projects/${data.google_project.project.number}/locations/${google_cloud_run_service.default.location}/services/${google_cloud_run_service.default.name}" - tag_value = "tagValues/${google_tags_tag_value.value.name}" - location = "us-central1" + parent = "//run.googleapis.com/projects/${data.google_project.project.number}/locations/${google_cloud_run_service.default.location}/services/${google_cloud_run_service.default.name}" + tag_value = google_tags_tag_value.value.id + location = "us-central1" } `, context) } @@ -949,27 +949,27 @@ resource "google_tags_tag_key" "key" { description = "For a certain set of resources." } resource "google_tags_tag_value" "value" { - parent = "tagKeys/${google_tags_tag_key.key.name}" - short_name = "foo%{random_suffix}" + parent = google_tags_tag_key.key.id + short_name = "foo%{random_suffix}" description = "For foo%{random_suffix} resources." } resource "google_compute_instance" "default" { name = "test-%{random_suffix}" machine_type = "e2-medium" zone = "us-central1-a" - boot_disk { - initialize_params { - image = "debian-cloud/debian-11" - } - } - network_interface { - network = "default" - } + boot_disk { + initialize_params { + image = "debian-cloud/debian-11" + } + } + network_interface { + network = "default" + } } resource "google_tags_location_tag_binding" "binding" { - parent = "//compute.googleapis.com/projects/${data.google_project.project.number}/zones/us-central1-a/instances/${google_compute_instance.default.instance_id}" - tag_value = "tagValues/${google_tags_tag_value.value.name}" - location = "us-central1-a" + parent = "//compute.googleapis.com/projects/${data.google_project.project.number}/zones/us-central1-a/instances/${google_compute_instance.default.instance_id}" + tag_value = google_tags_tag_value.value.id + location = "us-central1-a" } `, context) } diff --git a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_cluster_test.go b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_cluster_test.go index 312781a70fdf..7cd1ab5213eb 100644 --- a/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_cluster_test.go +++ b/mmv1/third_party/terraform/services/vmwareengine/resource_vmwareengine_cluster_test.go @@ -46,7 +46,7 @@ func TestAccVmwareengineCluster_vmwareEngineClusterUpdate(t *testing.T) { ImportStateVerifyIgnore: []string{"parent", "name"}, }, { - Config: testVmwareEngineClusterConfig(context, 4), // expand the cluster + Config: testVmwareEngineClusterUpdateConfig(context, 4), // expand the cluster }, { ResourceName: "google_vmwareengine_cluster.vmw-engine-ext-cluster", @@ -104,6 +104,88 @@ resource "google_vmwareengine_cluster" "vmw-engine-ext-cluster" { node_count = %{node_count} custom_core_count = 32 } + autoscaling_settings { + autoscaling_policies { + autoscale_policy_id = "autoscaling-policy" + node_type_id = "standard-72" + scale_out_size = 1 + consumed_memory_thresholds { + scale_out = 75 + scale_in = 20 + } + storage_thresholds { + scale_out = 80 + scale_in = 20 + } + } + min_cluster_node_count = 3 + max_cluster_node_count = 8 + cool_down_period = "1800s" + } +} + +data "google_vmwareengine_cluster" "ds" { + name = google_vmwareengine_cluster.vmw-engine-ext-cluster.name + parent = google_vmwareengine_private_cloud.cluster-pc.id +} +`, context) +} + +func testVmwareEngineClusterUpdateConfig(context map[string]interface{}, nodeCount int) string { + context["node_count"] = nodeCount + return acctest.Nprintf(` +resource "google_vmwareengine_network" "cluster-nw" { + name = "tf-test-cluster-nw%{random_suffix}" + location = "global" + type = "STANDARD" + description = "PC network description." +} + +resource "google_vmwareengine_private_cloud" "cluster-pc" { + location = "%{region}-b" + name = "tf-test-cluster-pc%{random_suffix}" + description = "Sample test PC." + deletion_delay_hours = 0 + send_deletion_delay_hours_if_zero = true + network_config { + management_cidr = "192.168.10.0/24" + vmware_engine_network = google_vmwareengine_network.cluster-nw.id + } + management_cluster { + cluster_id = "tf-test-mgmt-cluster%{random_suffix}" + node_type_configs { + node_type_id = "standard-72" + node_count = 3 + } + } +} + +resource "google_vmwareengine_cluster" "vmw-engine-ext-cluster" { + name = "tf-test-ext-cluster%{random_suffix}" + parent = google_vmwareengine_private_cloud.cluster-pc.id + node_type_configs { + node_type_id = "standard-72" + node_count = %{node_count} + custom_core_count = 32 + } + autoscaling_settings { + autoscaling_policies { + autoscale_policy_id = "autoscaling-policy" + node_type_id = "standard-72" + scale_out_size = 2 + cpu_thresholds { + scale_out = 80 + scale_in = 15 + } + storage_thresholds { + scale_out = 79 + scale_in = 15 + } + } + min_cluster_node_count = 3 + max_cluster_node_count = 10 + cool_down_period = "3600s" + } } data "google_vmwareengine_cluster" "ds" { diff --git a/mmv1/third_party/terraform/services/workstations/resource_workstations_workstation_config_test.go.tmpl b/mmv1/third_party/terraform/services/workstations/resource_workstations_workstation_config_test.go.tmpl index ee56c4ee692c..b9bfb4e4321f 100644 --- a/mmv1/third_party/terraform/services/workstations/resource_workstations_workstation_config_test.go.tmpl +++ b/mmv1/third_party/terraform/services/workstations/resource_workstations_workstation_config_test.go.tmpl @@ -762,8 +762,8 @@ resource "google_tags_tag_key" "tag_key1" { } resource "google_tags_tag_value" "tag_value1" { - provider = google-beta - parent = "tagKeys/${google_tags_tag_key.tag_key1.name}" + provider = google-beta + parent = google_tags_tag_key.tag_key1.id short_name = "%{value_short_name}" } @@ -1346,13 +1346,13 @@ data "google_project" "project" { resource "google_tags_tag_key" "tag_key1" { provider = google-beta - parent = "projects/${data.google_project.project.number}" + parent = data.google_project.project.id short_name = "tf_test_tag_key1%{random_suffix}" } resource "google_tags_tag_value" "tag_value1" { provider = google-beta - parent = "tagKeys/${google_tags_tag_key.tag_key1.name}" + parent = google_tags_tag_key.tag_key1.id short_name = "tf_test_tag_value1%{random_suffix}" } @@ -1390,7 +1390,7 @@ resource "google_workstations_workstation_config" "default" { boot_disk_size_gb = 35 disable_public_ip_addresses = true vm_tags = { - "tagKeys/${google_tags_tag_key.tag_key1.name}" = "tagValues/${google_tags_tag_value.tag_value1.name}" + (google_tags_tag_key.tag_key1.id) = google_tags_tag_value.tag_value1.id } } } diff --git a/mmv1/third_party/terraform/verify/validation.go b/mmv1/third_party/terraform/verify/validation.go index 2e9da45a5cf2..b3a0152e7436 100644 --- a/mmv1/third_party/terraform/verify/validation.go +++ b/mmv1/third_party/terraform/verify/validation.go @@ -22,7 +22,7 @@ const ( SubnetworkLinkRegex = "projects/(" + ProjectRegex + ")/regions/(" + RegionRegex + ")/subnetworks/(" + SubnetworkRegex + ")$" - RFC1035NameTemplate = "[a-z](?:[-a-z0-9]{%d,%d}[a-z0-9])" + RFC1035NameTemplate = "[a-z]([-a-z0-9]%v[a-z0-9])?" CloudIoTIdRegex = "^[a-zA-Z][-a-zA-Z0-9._+~%]{2,254}$" // Format of default Compute service accounts created by Google @@ -41,7 +41,7 @@ var ( // The first and last characters have different restrictions, than // the middle characters. The middle characters length must be between // 4 and 28 since the first and last character are excluded. - ServiceAccountNameRegex = fmt.Sprintf(RFC1035NameTemplate, 4, 28) + ServiceAccountNameRegex = fmt.Sprintf(RFC1035NameTemplate, "{4,28}") ServiceAccountLinkRegexPrefix = "projects/" + ProjectRegexWildCard + "/serviceAccounts/" PossibleServiceAccountNames = []string{ @@ -54,7 +54,7 @@ var ( ServiceAccountKeyNameRegex = ServiceAccountLinkRegexPrefix + "(.+)/keys/(.+)" // Format of service accounts created through the API - CreatedServiceAccountNameRegex = fmt.Sprintf(RFC1035NameTemplate, 4, 28) + "@" + ProjectNameInDNSFormRegex + "\\.iam\\.gserviceaccount\\.com$" + CreatedServiceAccountNameRegex = fmt.Sprintf(RFC1035NameTemplate, "{4,28}") + "@" + ProjectNameInDNSFormRegex + "\\.iam\\.gserviceaccount\\.com$" // Format of service-created service account // examples are: @@ -194,19 +194,26 @@ func ValidateRFC3339Time(v interface{}, k string) (warnings []string, errors []e } func ValidateRFC1035Name(min, max int) schema.SchemaValidateFunc { - if min < 2 || max < min { - return func(i interface{}, k string) (s []string, errors []error) { - if min < 2 { - errors = append(errors, fmt.Errorf("min must be at least 2. Got: %d", min)) - } - if max < min { - errors = append(errors, fmt.Errorf("max must greater than min. Got [%d, %d]", min, max)) - } - return + return func(i interface{}, k string) (s []string, errors []error) { + value := i.(string) + re := fmt.Sprintf("^"+RFC1035NameTemplate+"$", "*") + if min < 1 { + errors = append(errors, fmt.Errorf("min must be at least 1. Got: %d", min)) + } + if max < min { + errors = append(errors, fmt.Errorf("max must greater than min. Got [%d, %d]", min, max)) + } + + if len(value) < min || len(value) > max { + errors = append(errors, fmt.Errorf("%q (%q) must be between %d and %d characters long", k, value, min, max)) + } + + if !regexp.MustCompile(re).MatchString(value) { + errors = append(errors, fmt.Errorf("%q (%q) must match regex %q", k, value, re)) } - } - return ValidateRegexp(fmt.Sprintf("^"+RFC1035NameTemplate+"$", min-2, max-2)) + return + } } func ValidateIpCidrRange(v interface{}, k string) (warnings []string, errors []error) { diff --git a/mmv1/third_party/terraform/verify/validation_test.go b/mmv1/third_party/terraform/verify/validation_test.go index b03fac489df7..19555861fb64 100644 --- a/mmv1/third_party/terraform/verify/validation_test.go +++ b/mmv1/third_party/terraform/verify/validation_test.go @@ -170,12 +170,18 @@ func TestValidateRFC1035Name(t *testing.T) { {TestName: "valid lower bound", Min: 12, Max: 30, Value: "a-valid-name"}, {TestName: "valid upper bound", Min: 6, Max: 12, Value: "a-valid-name"}, {TestName: "valid with numbers", Min: 6, Max: 30, Value: "valid000-name"}, + {TestName: "valid shortest", Min: 1, Max: 63, Value: "a"}, + {TestName: "valid longest", Min: 1, Max: 63, Value: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, {TestName: "must start with a letter", Min: 6, Max: 10, Value: "0invalid", ExpectError: true}, {TestName: "cannot end with a dash", Min: 6, Max: 10, Value: "invalid-", ExpectError: true}, {TestName: "too short", Min: 6, Max: 10, Value: "short", ExpectError: true}, {TestName: "too long", Min: 6, Max: 10, Value: "toolooooong", ExpectError: true}, - {TestName: "min too small", Min: 1, Max: 10, Value: "", ExpectError: true}, + {TestName: "min too small", Min: 0, Max: 10, Value: "", ExpectError: true}, {TestName: "min < max", Min: 6, Max: 5, Value: "", ExpectError: true}, + {TestName: "min < max", Min: 6, Max: 5, Value: "", ExpectError: true}, + {TestName: "invalid smallest possible w/ higher limit", Min: 2, Max: 63, Value: "a", ExpectError: true}, + {TestName: "invalid smallest possible hyphen", Min: 1, Max: 1, Value: "-", ExpectError: true}, + {TestName: "invalid smallest possible ends with hyphen", Min: 2, Max: 63, Value: "a-", ExpectError: true}, } for _, c := range cases { diff --git a/mmv1/third_party/terraform/website/docs/d/artifact_registry_docker_image.html.markdown b/mmv1/third_party/terraform/website/docs/d/artifact_registry_docker_image.html.markdown index 730c7706972c..c32be1c98a94 100644 --- a/mmv1/third_party/terraform/website/docs/d/artifact_registry_docker_image.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/artifact_registry_docker_image.html.markdown @@ -23,7 +23,7 @@ resource "google_artifact_registry_repository" "my_repo" { data "google_artifact_registry_docker_image" "my_image" { location = google_artifact_registry_repository.my_repo.location repository_id = google_artifact_registry_repository.my_repo.repository_id - image = "my-image:my-tag" + image_name = "my-image:my-tag" } resource "google_cloud_run_v2_service" "default" { diff --git a/mmv1/third_party/terraform/website/docs/d/compute_instance.html.markdown b/mmv1/third_party/terraform/website/docs/d/compute_instance.html.markdown index b6c32b263ff2..b2a64d881f19 100644 --- a/mmv1/third_party/terraform/website/docs/d/compute_instance.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/compute_instance.html.markdown @@ -73,6 +73,8 @@ The following arguments are supported: * `instance_id` - The server-assigned unique identifier of this instance. +* `creation_timestamp` - Creation timestamp in RFC3339 text format. + * `metadata_fingerprint` - The unique fingerprint of the metadata. * `self_link` - The URI of the created resource. @@ -107,6 +109,8 @@ The following arguments are supported: encoded SHA-256 hash of the [customer-supplied encryption key] (https://cloud.google.com/compute/docs/disks/customer-supplied-encryption) that protects this resource. +* `key_revocation_action_type` - Action to be taken when a customer's encryption key is revoked. + --- The `boot_disk` block supports: diff --git a/mmv1/third_party/terraform/website/docs/d/compute_instance_template.html.markdown b/mmv1/third_party/terraform/website/docs/d/compute_instance_template.html.markdown index 08be6b15f0f8..7d6ced023906 100644 --- a/mmv1/third_party/terraform/website/docs/d/compute_instance_template.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/compute_instance_template.html.markdown @@ -125,6 +125,8 @@ The following arguments are supported: * `confidential_instance_config` - Enable [Confidential Mode](https://cloud.google.com/compute/confidential-vm/docs/about-cvm) on this VM. Structure is [documented below](#nested_confidential_instance_config) +* `key_revocation_action_type` - Action to be taken when a customer's encryption key is revoked. + The `disk` block supports: * `auto_delete` - Whether or not the disk should be auto-deleted. @@ -318,6 +320,8 @@ The `disk_encryption_key` block supports: * `id` - an identifier for the resource with format `projects/{{project}}/global/instanceTemplates/{{name}}` +* `creation_timestamp` - Creation timestamp in RFC3339 text format. + * `metadata_fingerprint` - The unique fingerprint of the metadata. * `self_link` - The URI of the created resource. diff --git a/mmv1/third_party/terraform/website/docs/d/compute_region_instance_template.html.markdown b/mmv1/third_party/terraform/website/docs/d/compute_region_instance_template.html.markdown index e6f4f548ea6a..38ba97359625 100644 --- a/mmv1/third_party/terraform/website/docs/d/compute_region_instance_template.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/compute_region_instance_template.html.markdown @@ -111,6 +111,8 @@ The following arguments are supported: * `confidential_instance_config` - Enable [Confidential Mode](https://cloud.google.com/compute/confidential-vm/docs/about-cvm) on this VM. Structure is [documented below](#nested_confidential_instance_config) +* `key_revocation_action_type` - Action to be taken when a customer's encryption key is revoked. + The `disk` block supports: * `auto_delete` - Whether or not the disk should be auto-deleted. @@ -302,6 +304,8 @@ The `disk_encryption_key` block supports: * `id` - an identifier for the resource with format `projects/{{project}}/regions/{{region}}/instanceTemplates/{{name}}` +* `creation_timestamp` - Creation timestamp in RFC3339 text format. + * `metadata_fingerprint` - The unique fingerprint of the metadata. * `self_link` - The URI of the created resource. diff --git a/mmv1/third_party/terraform/website/docs/d/oracle_database_autonomous_database.html.markdown b/mmv1/third_party/terraform/website/docs/d/oracle_database_autonomous_database.html.markdown new file mode 100644 index 000000000000..91207bbe0864 --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/oracle_database_autonomous_database.html.markdown @@ -0,0 +1,37 @@ +--- +subcategory: "Oracle Database" +description: |- + Get information about an AutonomousDatabase. +--- + +# google_oracle_database_autonomous_database + +Get information about an AutonomousDatabase. + +For more information see the +[API](https://cloud.google.com/oracle/database/docs/reference/rest/v1/projects.locations.autonomousDatabases). + +## Example Usage + +```hcl +data "google_oracle_database_autonomous_database" "my-instance"{ + location = "us-east4" + autonomous_database_id = "autonomous_database_id" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `autonomous_database_id` - (Required) The ID of the AutonomousDatabase. + +* `location` - (Required) The location of the resource. + +- - - +* `project` - (Optional) The project to which the resource belongs. If it + is not provided, the provider project is used. + +## Attributes Reference + +See [google_oracle_database_autonomous_database](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/oracle_database_autonomous_database#argument-reference) resource for details of the available attributes. \ No newline at end of file diff --git a/mmv1/third_party/terraform/website/docs/d/oracle_database_autonomous_databases.html.markdown b/mmv1/third_party/terraform/website/docs/d/oracle_database_autonomous_databases.html.markdown new file mode 100644 index 000000000000..46da4266ea41 --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/oracle_database_autonomous_databases.html.markdown @@ -0,0 +1,38 @@ +--- +subcategory: "Oracle Database" +description: |- + List all AutonomousDatabases. +--- + +# google_oracle_database_autonomous_databases + +List all AutonomousDatabases. + +For more information see the +[API](https://cloud.google.com/oracle/database/docs/reference/rest/v1/projects.locations.autonomousDatabases). + +## Example Usage + +```hcl +data "google_oracle_database_autonomous_databases" "my-adbs"{ + location = "us-east4" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `location` - (Required) The location of the resource. + +- - - +* `project` - (Optional) The project to which the resource belongs. If it + is not provided, the provider project is used. + +## Attributes Reference + +The following attributes are exported: + +* `AutonomousDatabases` - A list of AutonomousDatabases. + +See [google_oracle_database_autonomous_database](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/oracle_database_autonomous_database#argument-reference) resource for details of the available attributes. \ No newline at end of file diff --git a/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_exadata_infrastructure.html.markdown b/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_exadata_infrastructure.html.markdown index ee3b2dc894ed..e06ca361ff71 100644 --- a/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_exadata_infrastructure.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_exadata_infrastructure.html.markdown @@ -34,4 +34,4 @@ The following arguments are supported: ## Attributes Reference -See [google_oracle_database_cloud_exadata_infrastructure](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_oracle_database_cloud_exadata_infrastructure#argument-reference) resource for details of the available attributes. \ No newline at end of file +See [google_oracle_database_cloud_exadata_infrastructure](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/oracle_database_cloud_exadata_infrastructure#argument-reference) resource for details of the available attributes. \ No newline at end of file diff --git a/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_exadata_infrastructures.html.markdown b/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_exadata_infrastructures.html.markdown index 95852a8389ec..bff0fff370bc 100644 --- a/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_exadata_infrastructures.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_exadata_infrastructures.html.markdown @@ -35,4 +35,4 @@ The following attributes are exported: * `CloudExadataInfrastructures` - A list of ExadataInfrastructures. -See [google_oracle_database_cloud_exadata_infrastructure](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_oracle_database_cloud_exadata_infrastructure#argument-reference) resource for details of the available attributes. +See [google_oracle_database_cloud_exadata_infrastructure](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/oracle_database_cloud_exadata_infrastructure#argument-reference) resource for details of the available attributes. diff --git a/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_vm_cluster.html.markdown b/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_vm_cluster.html.markdown index 60ff8e0bae1e..e8516500f6d8 100644 --- a/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_vm_cluster.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_vm_cluster.html.markdown @@ -34,4 +34,4 @@ The following arguments are supported: ## Attributes Reference -See [google_oracle_database_cloud_vm_cluster](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_oracle_database_cloud_vm_cluster#argument-reference) resource for details of the available attributes. \ No newline at end of file +See [google_oracle_database_cloud_vm_cluster](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/oracle_database_cloud_vm_cluster#argument-reference) resource for details of the available attributes. \ No newline at end of file diff --git a/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_vm_clusters.html.markdown b/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_vm_clusters.html.markdown new file mode 100644 index 000000000000..4c5db27e34cd --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/oracle_database_cloud_vm_clusters.html.markdown @@ -0,0 +1,38 @@ +--- +subcategory: "Oracle Database" +description: |- + List all CloudVmClusters. +--- + +# google_oracle_database_cloud_vm_clusters + +List all CloudVmClusters. + +For more information see the +[API](https://cloud.google.com/oracle/database/docs/reference/rest/v1/projects.locations.cloudVmClusters). + +## Example Usage + +```hcl +data "google_oracle_database_cloud_vm_clusters" "my_vmclusters"{ + location = "us-east4" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `location` - (Required) The location of the resource. + +- - - +* `project` - (Optional) The project to which the resource belongs. If it + is not provided, the provider project is used. + +## Attributes Reference + +The following attributes are exported: + +* `CloudVmClusters` - A list of CloudVmClusters. + +See [google_oracle_database_cloud_vm_cluster](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/oracle_database_cloud_vm_cluster#argument-reference) resource for details of the available attributes. diff --git a/mmv1/third_party/terraform/website/docs/d/service_account.html.markdown b/mmv1/third_party/terraform/website/docs/d/service_account.html.markdown index dc4c4aacf51b..b4a2ae2f6c57 100644 --- a/mmv1/third_party/terraform/website/docs/d/service_account.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/service_account.html.markdown @@ -70,3 +70,5 @@ exported: * `display_name` - The display name for the service account. * `member` - The Identity of the service account in the form `serviceAccount:{email}`. This value is often used to refer to the service account in order to grant IAM permissions. + +* `disabled` - Whether a service account is disabled or not. diff --git a/mmv1/third_party/terraform/website/docs/r/composer_environment.html.markdown b/mmv1/third_party/terraform/website/docs/r/composer_environment.html.markdown index 0e743759157d..e82b80921d4e 100644 --- a/mmv1/third_party/terraform/website/docs/r/composer_environment.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/composer_environment.html.markdown @@ -1638,9 +1638,9 @@ In addition to the arguments listed above, the following computed attributes are This resource provides the following [Timeouts](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/retries-and-customizable-timeouts) configuration options: configuration options: -- `create` - Default is 60 minutes. -- `update` - Default is 60 minutes. -- `delete` - Default is 6 minutes. +- `create` - Default is 120 minutes. +- `update` - Default is 120 minutes. +- `delete` - Default is 30 minutes. ## Import diff --git a/mmv1/third_party/terraform/website/docs/r/compute_instance.html.markdown b/mmv1/third_party/terraform/website/docs/r/compute_instance.html.markdown index b18f1594ce09..369f11e5b04a 100644 --- a/mmv1/third_party/terraform/website/docs/r/compute_instance.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/compute_instance.html.markdown @@ -251,6 +251,8 @@ is desired, you will need to modify your state file manually using * `partner_metadata` - (optional) [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html) key/value pair represents partner metadata assigned to instance where key represent a defined namespace and value is a json string represent the entries associted with the namespace. +* `key_revocation_action_type` - (optional) Action to be taken when a customer's encryption key is revoked. Supports `STOP` and `NONE`, with `NONE` being the default. + --- The `boot_disk` block supports: @@ -590,6 +592,8 @@ exported: * `id` - an identifier for the resource with format `projects/{{project}}/zones/{{zone}}/instances/{{name}}` +* `creation_timestamp` - Creation timestamp in RFC3339 text format. + * `instance_id` - The server-assigned unique identifier of this instance. * `metadata_fingerprint` - The unique fingerprint of the metadata. diff --git a/mmv1/third_party/terraform/website/docs/r/compute_instance_template.html.markdown b/mmv1/third_party/terraform/website/docs/r/compute_instance_template.html.markdown index afe75854b4e5..1d65214ae30a 100644 --- a/mmv1/third_party/terraform/website/docs/r/compute_instance_template.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/compute_instance_template.html.markdown @@ -425,6 +425,8 @@ The following arguments are supported: * `partner_metadata` - (optional) [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html) key/value pair represents partner metadata assigned to instance template where key represent a defined namespace and value is a json string represent the entries associted with the namespace. +* `key_revocation_action_type` - (optional) Action to be taken when a customer's encryption key is revoked. Supports `STOP` and `NONE`, with `NONE` being the default. + The `disk` block supports: * `auto_delete` - (Optional) Whether or not the disk should be auto-deleted. @@ -733,6 +735,8 @@ exported: * `id` - an identifier for the resource with format `projects/{{project}}/global/instanceTemplates/{{name}}` +* `creation_timestamp` - Creation timestamp in RFC3339 text format. + * `metadata_fingerprint` - The unique fingerprint of the metadata. * `self_link` - The URI of the created resource. diff --git a/mmv1/third_party/terraform/website/docs/r/compute_region_instance_template.html.markdown b/mmv1/third_party/terraform/website/docs/r/compute_region_instance_template.html.markdown index facff74df4b6..a80285494177 100644 --- a/mmv1/third_party/terraform/website/docs/r/compute_region_instance_template.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/compute_region_instance_template.html.markdown @@ -393,6 +393,8 @@ The following arguments are supported: * `partner_metadata` - (optional) [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html) key/value pair represents partner metadata assigned to instance template where key represent a defined namespace and value is a json string represent the entries associted with the namespace. +* `key_revocation_action_type` - (optional) Action to be taken when a customer's encryption key is revoked. Supports `STOP` and `NONE`, with `NONE` being the default. + The `disk` block supports: * `auto_delete` - (Optional) Whether or not the disk should be auto-deleted. @@ -693,6 +695,8 @@ exported: * `id` - an identifier for the resource with format `projects/{{project}}/regions/{{region}}/instanceTemplates/{{name}}` +* `creation_timestamp` - Creation timestamp in RFC3339 text format. + * `metadata_fingerprint` - The unique fingerprint of the metadata. * `self_link` - The URI of the created resource. diff --git a/tools/diff-processor/breaking_changes/resource_config_diff.go b/tools/diff-processor/breaking_changes/resource_config_diff.go index 95eb5526c754..9edea8de0a48 100644 --- a/tools/diff-processor/breaking_changes/resource_config_diff.go +++ b/tools/diff-processor/breaking_changes/resource_config_diff.go @@ -10,7 +10,7 @@ import ( // structure for rules regarding resource config changes type ResourceConfigDiffRule struct { Identifier string - Messages func(resource string, resourceConfigDiff diff.ResourceConfigDiff) []string + Messages func(resource string, resourceConfigDiff diff.ResourceConfigDiff) []string } // ResourceConfigDiffRules is a list of ResourceConfigDiffRule diff --git a/tools/diff-processor/breaking_changes/resource_config_diff_test.go b/tools/diff-processor/breaking_changes/resource_config_diff_test.go index 6a8e25607a05..83d278b50c52 100644 --- a/tools/diff-processor/breaking_changes/resource_config_diff_test.go +++ b/tools/diff-processor/breaking_changes/resource_config_diff_test.go @@ -44,4 +44,4 @@ var resourceConfigRemovingAResourceTestCases = []resourceInventoryTestCase{ new: nil, wantViolations: true, }, -} \ No newline at end of file +} diff --git a/tools/diff-processor/cmd/breaking_changes.go b/tools/diff-processor/cmd/breaking_changes.go index 72d14cdb95bb..bd16d705f67c 100644 --- a/tools/diff-processor/cmd/breaking_changes.go +++ b/tools/diff-processor/cmd/breaking_changes.go @@ -10,8 +10,8 @@ import ( "os" "sort" - "github.com/GoogleCloudPlatform/magic-modules/tools/diff-processor/diff" "github.com/GoogleCloudPlatform/magic-modules/tools/diff-processor/breaking_changes" + "github.com/GoogleCloudPlatform/magic-modules/tools/diff-processor/diff" "github.com/spf13/cobra" ) diff --git a/tools/diff-processor/cmd/breaking_changes_test.go b/tools/diff-processor/cmd/breaking_changes_test.go index a4c7f37a8f57..e9e7ac20a64e 100644 --- a/tools/diff-processor/cmd/breaking_changes_test.go +++ b/tools/diff-processor/cmd/breaking_changes_test.go @@ -5,8 +5,8 @@ import ( "encoding/json" "testing" - "github.com/GoogleCloudPlatform/magic-modules/tools/diff-processor/diff" "github.com/GoogleCloudPlatform/magic-modules/tools/diff-processor/breaking_changes" + "github.com/GoogleCloudPlatform/magic-modules/tools/diff-processor/diff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) diff --git a/tpgtools/api/assuredworkloads/samples/basic.workload.json b/tpgtools/api/assuredworkloads/samples/basic.workload.json index c3d1aa445c1e..eacbc711d185 100755 --- a/tpgtools/api/assuredworkloads/samples/basic.workload.json +++ b/tpgtools/api/assuredworkloads/samples/basic.workload.json @@ -13,6 +13,9 @@ "nextRotationTime": "9999-10-02T15:01:23Z", "rotationPeriod": "10368000s" }, + "workloadOptions": { + "kajEnrollmentType": "KEY_ACCESS_TRANSPARENCY_OFF" + }, "resourceSettings": [ { "resourceType": "CONSUMER_FOLDER", diff --git a/tpgtools/go.mod b/tpgtools/go.mod index 7a6a1b4204f9..ba4a797b4f7f 100644 --- a/tpgtools/go.mod +++ b/tpgtools/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( bitbucket.org/creachadair/stringset v0.0.11 - github.com/GoogleCloudPlatform/declarative-resource-client-library v1.74.0 + github.com/GoogleCloudPlatform/declarative-resource-client-library v1.75.0 github.com/golang/glog v1.1.2 github.com/hashicorp/hcl v1.0.0 github.com/kylelemons/godebug v1.1.0 diff --git a/tpgtools/go.sum b/tpgtools/go.sum index 95306e1d1396..d3144c1199b7 100644 --- a/tpgtools/go.sum +++ b/tpgtools/go.sum @@ -6,8 +6,8 @@ cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdi cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/GoogleCloudPlatform/declarative-resource-client-library v1.74.0 h1:YV3eTXgDw3Zp8Mc12WE2Aa3+22twNd07xkFkEODrlOQ= -github.com/GoogleCloudPlatform/declarative-resource-client-library v1.74.0/go.mod h1:pL2Qt5HT+x6xrTd806oMiM3awW6kNIXB/iiuClz6m6k= +github.com/GoogleCloudPlatform/declarative-resource-client-library v1.75.0 h1:7tFkHNjfjm7dYnjqyuzMon+31lPaMTjca3OuamWd0Oo= +github.com/GoogleCloudPlatform/declarative-resource-client-library v1.75.0/go.mod h1:pL2Qt5HT+x6xrTd806oMiM3awW6kNIXB/iiuClz6m6k= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/tpgtools/overrides/assuredworkloads/samples/workload/basic.tf.tmpl b/tpgtools/overrides/assuredworkloads/samples/workload/basic.tf.tmpl index 51db686c8b2f..8a426b088674 100644 --- a/tpgtools/overrides/assuredworkloads/samples/workload/basic.tf.tmpl +++ b/tpgtools/overrides/assuredworkloads/samples/workload/basic.tf.tmpl @@ -8,6 +8,9 @@ resource "google_assured_workloads_workload" "primary" { provisioned_resources_parent = google_folder.folder1.name organization = "{{org_id}}" location = "us-central1" + workload_options { + kaj_enrollment_type = "KEY_ACCESS_TRANSPARENCY_OFF" + } resource_settings { resource_type = "CONSUMER_FOLDER" display_name = "folder-display-name" diff --git a/tpgtools/overrides/assuredworkloads/samples/workload/meta.yaml b/tpgtools/overrides/assuredworkloads/samples/workload/meta.yaml index 7911ffc518bf..cae74f97ca5a 100644 --- a/tpgtools/overrides/assuredworkloads/samples/workload/meta.yaml +++ b/tpgtools/overrides/assuredworkloads/samples/workload/meta.yaml @@ -5,6 +5,7 @@ ignore_read: - "billing_account" - "kms_settings" - "resource_settings" + - "workload_options" - "provisioned_resources_parent" - "partner_services_billing_account" doc_hide: diff --git a/tpgtools/overrides/compute/beta/network_firewall_policy_association.yaml b/tpgtools/overrides/compute/beta/network_firewall_policy_association.yaml deleted file mode 100644 index 3260bc51a574..000000000000 --- a/tpgtools/overrides/compute/beta/network_firewall_policy_association.yaml +++ /dev/null @@ -1,38 +0,0 @@ -- type: CUSTOM_RESOURCE_NAME - details: - title: region_network_firewall_policy_association - location: region -- type: EXCLUDE - field: location - location: global -- type: EXCLUDE - field: region -- type: CUSTOM_NAME - details: - name: region - field: location -- type: CUSTOM_ID - details: - id: "projects/{{project}}/regions/{{region}}/firewallPolicies/{{firewall_policy}}/associations/{{name}}" - location: region -- type: CUSTOM_ID - details: - id: "projects/{{project}}/global/firewallPolicies/{{firewall_policy}}/associations/{{name}}" - location: global -- type: IMPORT_FORMAT - details: - formats: - - "projects/{{project}}/regions/{{region}}/firewallPolicies/{{firewall_policy}}/associations/{{name}}" - - "{{project}}/{{region}}/{{firewall_policy}}/{{name}}" - location: region -- type: IMPORT_FORMAT - details: - formats: - - "projects/{{project}}/global/firewallPolicies/{{firewall_policy}}/associations/{{name}}" - - "{{project}}/{{firewall_policy}}/{{name}}" - location: global -- type: CUSTOMIZE_DIFF - details: - functions: - - tpgresource.DefaultProviderProject - - tpgresource.DefaultProviderRegion diff --git a/tpgtools/overrides/compute/network_firewall_policy_association.yaml b/tpgtools/overrides/compute/network_firewall_policy_association.yaml deleted file mode 100644 index 3260bc51a574..000000000000 --- a/tpgtools/overrides/compute/network_firewall_policy_association.yaml +++ /dev/null @@ -1,38 +0,0 @@ -- type: CUSTOM_RESOURCE_NAME - details: - title: region_network_firewall_policy_association - location: region -- type: EXCLUDE - field: location - location: global -- type: EXCLUDE - field: region -- type: CUSTOM_NAME - details: - name: region - field: location -- type: CUSTOM_ID - details: - id: "projects/{{project}}/regions/{{region}}/firewallPolicies/{{firewall_policy}}/associations/{{name}}" - location: region -- type: CUSTOM_ID - details: - id: "projects/{{project}}/global/firewallPolicies/{{firewall_policy}}/associations/{{name}}" - location: global -- type: IMPORT_FORMAT - details: - formats: - - "projects/{{project}}/regions/{{region}}/firewallPolicies/{{firewall_policy}}/associations/{{name}}" - - "{{project}}/{{region}}/{{firewall_policy}}/{{name}}" - location: region -- type: IMPORT_FORMAT - details: - formats: - - "projects/{{project}}/global/firewallPolicies/{{firewall_policy}}/associations/{{name}}" - - "{{project}}/{{firewall_policy}}/{{name}}" - location: global -- type: CUSTOMIZE_DIFF - details: - functions: - - tpgresource.DefaultProviderProject - - tpgresource.DefaultProviderRegion diff --git a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/global.tf.tmpl b/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/global.tf.tmpl deleted file mode 100644 index f02ab0b76ed9..000000000000 --- a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/global.tf.tmpl +++ /dev/null @@ -1,16 +0,0 @@ -resource "google_compute_network_firewall_policy" "network_firewall_policy" { - name = "{{policy}}" - project = "{{project}}" - description = "Sample global network firewall policy" -} - -resource "google_compute_network" "network" { - name = "{{network}}" -} - -resource "google_compute_network_firewall_policy_association" "primary" { - name = "{{association}}" - attachment_target = google_compute_network.network.id - firewall_policy = google_compute_network_firewall_policy.network_firewall_policy.name - project = "{{project}}" -} diff --git a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/global.yaml b/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/global.yaml deleted file mode 100644 index d9d94648aae7..000000000000 --- a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/global.yaml +++ /dev/null @@ -1,11 +0,0 @@ -updates: -- resource: ./global_update.tf.tmpl -variables: -- name: association - type: resource_name -- name: policy - type: resource_name -- name: network - type: resource_name -- name: project - type: project diff --git a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/global_update.tf.tmpl b/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/global_update.tf.tmpl deleted file mode 100644 index 05cb145b2f33..000000000000 --- a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/global_update.tf.tmpl +++ /dev/null @@ -1,20 +0,0 @@ -resource "google_compute_network_firewall_policy" "network_firewall_policy" { - name = "{{policy}}" - project = "{{project}}" - description = "Sample global network firewall policy" -} - -resource "google_compute_network" "network" { - name = "{{network}}" -} - -resource "google_compute_network" "network2" { - name = "update-{{network}}" -} - -resource "google_compute_network_firewall_policy_association" "primary" { - name = "{{association}}" - attachment_target = google_compute_network.network2.id - firewall_policy = google_compute_network_firewall_policy.network_firewall_policy.name - project = "{{project}}" -} diff --git a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/meta.yaml b/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/meta.yaml deleted file mode 100644 index cd9c3249496e..000000000000 --- a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/meta.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# meta.yaml -# this is a shared config file that all the tests merge with -# -doc_hide: - - global_network_firewall_policy_association.yaml - - basic_regional_network_firewall_policy_association.yaml - -doc_hide_conditional: - - location: global - file_name: regional.tf.tmpl - - location: region - file_name: global.tf.tmpl - -test_hide: - - global_network_firewall_policy_association.yaml - - basic_regional_network_firewall_policy_association.yaml - -test_hide_conditional: - - location: global - file_name: regional.tf.tmpl - - location: region - file_name: global.tf.tmpl diff --git a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/regional.tf.tmpl b/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/regional.tf.tmpl deleted file mode 100644 index 29548370ab59..000000000000 --- a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/regional.tf.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -resource "google_compute_region_network_firewall_policy" "basic_regional_network_firewall_policy" { - name = "{{policy}}" - project = "{{project}}" - description = "Sample global network firewall policy" - region = "{{region}}" -} - -resource "google_compute_network" "basic_network" { - name = "{{network}}" -} - -resource "google_compute_region_network_firewall_policy_association" "primary" { - name = "{{association}}" - attachment_target = google_compute_network.basic_network.id - firewall_policy = google_compute_region_network_firewall_policy.basic_regional_network_firewall_policy.name - project = "{{project}}" - region = "{{region}}" -} diff --git a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/regional.yaml b/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/regional.yaml deleted file mode 100644 index c97bc51b9b0d..000000000000 --- a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/regional.yaml +++ /dev/null @@ -1,13 +0,0 @@ -updates: -- resource: ./regional_update.tf.tmpl -variables: -- name: association - type: resource_name -- name: policy - type: resource_name -- name: network - type: resource_name -- name: project - type: project -- name: region - type: region diff --git a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/regional_update.tf.tmpl b/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/regional_update.tf.tmpl deleted file mode 100644 index 8a9f5a746a2a..000000000000 --- a/tpgtools/overrides/compute/samples/networkfirewallpolicyassociation/regional_update.tf.tmpl +++ /dev/null @@ -1,22 +0,0 @@ -resource "google_compute_region_network_firewall_policy" "basic_regional_network_firewall_policy" { - name = "{{policy}}" - project = "{{project}}" - description = "Sample global network firewall policy" - region = "{{region}}" -} - -resource "google_compute_network" "basic_network" { - name = "{{network}}" -} - -resource "google_compute_network" "basic_network2" { - name = "update-{{network}}" -} - -resource "google_compute_region_network_firewall_policy_association" "primary" { - name = "{{association}}" - attachment_target = google_compute_network.basic_network2.id - firewall_policy = google_compute_region_network_firewall_policy.basic_regional_network_firewall_policy.name - project = "{{project}}" - region = "{{region}}" -} diff --git a/tpgtools/property_helpers.go b/tpgtools/property_helpers.go index 459d815ff509..bb81101fe0f5 100644 --- a/tpgtools/property_helpers.go +++ b/tpgtools/property_helpers.go @@ -1,11 +1,11 @@ // Copyright 2021 Google LLC. All Rights Reserved. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/tpgtools/serializable/serializable.go b/tpgtools/serializable/serializable.go index 43ef251d219a..01b29133090f 100644 --- a/tpgtools/serializable/serializable.go +++ b/tpgtools/serializable/serializable.go @@ -1,11 +1,11 @@ // Copyright 2021 Google LLC. All Rights Reserved. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/tpgtools/serializable/serializable_test.go b/tpgtools/serializable/serializable_test.go index cb9000c0a0ea..73fa51816be7 100644 --- a/tpgtools/serializable/serializable_test.go +++ b/tpgtools/serializable/serializable_test.go @@ -1,11 +1,11 @@ // Copyright 2021 Google LLC. All Rights Reserved. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,7 +18,7 @@ import ( "testing" ) -func TestListOfResources(t *testing.T) { +func TestListOfResources(t *testing.T) { services, err := ListOfResources("test_specs") if err != nil { t.Errorf("received error: %v", err) diff --git a/tpgtools/serialization_helpers.go b/tpgtools/serialization_helpers.go index 4be1b3800d45..e362def5319d 100644 --- a/tpgtools/serialization_helpers.go +++ b/tpgtools/serialization_helpers.go @@ -7,8 +7,8 @@ import ( "strings" cloudresourcemanager "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/cloudresourcemanager" - cloudresourcemanagerBeta "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/cloudresourcemanager/beta" cloudresourcemanagerAlpha "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/cloudresourcemanager/alpha" + cloudresourcemanagerBeta "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/cloudresourcemanager/beta" ) func serializeAlphaProjectToHCL(r cloudresourcemanagerAlpha.Project, hasGAEquivalent bool) (string, error) { diff --git a/tpgtools/type.go b/tpgtools/type.go index cb31f0a7c562..0e1a43a4342c 100644 --- a/tpgtools/type.go +++ b/tpgtools/type.go @@ -1,11 +1,11 @@ // Copyright 2021 Google LLC. All Rights Reserved. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.