diff --git a/.ci/gcb-vcr-nightly.yml b/.ci/gcb-vcr-nightly.yml index 5906d404d11f..fb3bbf8663c3 100644 --- a/.ci/gcb-vcr-nightly.yml +++ b/.ci/gcb-vcr-nightly.yml @@ -3,7 +3,7 @@ steps: - name: 'gcr.io/graphite-docker-images/go-plus' id: gcb-vcr-nightly entrypoint: '/workspace/.ci/scripts/go-plus/vcr-cassette-update/vcr_cassette_update.sh' - secretEnv: ["GITHUB_TOKEN", "GOOGLE_BILLING_ACCOUNT", "GOOGLE_CUST_ID", "GOOGLE_FIRESTORE_PROJECT", "GOOGLE_IDENTITY_USER", "GOOGLE_MASTER_BILLING_ACCOUNT", "GOOGLE_ORG", "GOOGLE_ORG_2", "GOOGLE_ORG_DOMAIN", "GOOGLE_PROJECT", "GOOGLE_PROJECT_NUMBER", "GOOGLE_SERVICE_ACCOUNT", "SA_KEY", "GOOGLE_PUBLIC_AVERTISED_PREFIX_DESCRIPTION", "GOOGLE_TPU_V2_VM_RUNTIME_VERSION"] + secretEnv: ["GOOGLE_BILLING_ACCOUNT", "GOOGLE_CUST_ID", "GOOGLE_FIRESTORE_PROJECT", "GOOGLE_IDENTITY_USER", "GOOGLE_MASTER_BILLING_ACCOUNT", "GOOGLE_ORG", "GOOGLE_ORG_2", "GOOGLE_ORG_DOMAIN", "GOOGLE_PROJECT", "GOOGLE_PROJECT_NUMBER", "GOOGLE_SERVICE_ACCOUNT", "SA_KEY", "GOOGLE_PUBLIC_AVERTISED_PREFIX_DESCRIPTION", "GOOGLE_TPU_V2_VM_RUNTIME_VERSION"] args: - $BUILD_ID @@ -15,8 +15,6 @@ options: logsBucket: 'gs://cloudbuild-vcr-nightly-logs' availableSecrets: secretManager: - - versionName: projects/673497134629/secrets/github-magician-token/versions/latest - env: GITHUB_TOKEN - versionName: projects/673497134629/secrets/ci-test-billing-account/versions/latest env: GOOGLE_BILLING_ACCOUNT - versionName: projects/673497134629/secrets/ci-test-cust-id/versions/latest diff --git a/.ci/infra/terraform/main.tf b/.ci/infra/terraform/main.tf index 7c1fc897bfa3..a6f8c58a5f69 100644 --- a/.ci/infra/terraform/main.tf +++ b/.ci/infra/terraform/main.tf @@ -196,6 +196,7 @@ module "project-services" { "cloudidentity.googleapis.com", "cloudiot.googleapis.com", "cloudkms.googleapis.com", + "cloudquotas.googleapis.com", "cloudresourcemanager.googleapis.com", "cloudscheduler.googleapis.com", "cloudtasks.googleapis.com", @@ -222,6 +223,7 @@ module "project-services" { "datastream.googleapis.com", "deploymentmanager.googleapis.com", "dialogflow.googleapis.com", + "discoveryengine.googleapis.com", "dlp.googleapis.com", "dns.googleapis.com", "documentai.googleapis.com", diff --git a/.ci/magician/cmd/membership_checker.go b/.ci/magician/cmd/membership_checker.go index bc37d0c7234d..4af9c95b81bc 100644 --- a/.ci/magician/cmd/membership_checker.go +++ b/.ci/magician/cmd/membership_checker.go @@ -97,41 +97,6 @@ func execMembershipChecker(prNumber, commitSha, branchName, headRepoUrl, headBra authorUserType := gh.GetUserType(author) trusted := authorUserType == github.CoreContributorUserType || authorUserType == github.GooglerUserType - if authorUserType != github.CoreContributorUserType { - fmt.Println("Not core contributor - assigning reviewer") - - requestedReviewers, err := gh.GetPullRequestRequestedReviewers(prNumber) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - previousReviewers, err := gh.GetPullRequestPreviousReviewers(prNumber) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - reviewersToRequest, newPrimaryReviewer := github.ChooseCoreReviewers(requestedReviewers, previousReviewers) - - for _, reviewer := range reviewersToRequest { - err = gh.RequestPullRequestReviewer(prNumber, reviewer) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - } - - if newPrimaryReviewer != "" { - comment := github.FormatReviewerComment(newPrimaryReviewer, authorUserType, trusted) - err = gh.PostComment(prNumber, comment) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - } - } - // auto_run(contributor-membership-checker) will be run on every commit or /gcbrun: // only triggers builds for trusted users if trusted { diff --git a/.ci/magician/cmd/membership_checker_test.go b/.ci/magician/cmd/membership_checker_test.go index 93b3805d29f4..a3a40b4b89bd 100644 --- a/.ci/magician/cmd/membership_checker_test.go +++ b/.ci/magician/cmd/membership_checker_test.go @@ -18,7 +18,6 @@ package cmd import ( "magician/github" "reflect" - "regexp" "testing" ) @@ -78,20 +77,7 @@ func TestExecMembershipChecker_GooglerFlow(t *testing.T) { execMembershipChecker("pr1", "sha1", "branch1", "url1", "head1", "base1", gh, cb) - method := "RequestPullRequestReviewer" - if calls, ok := gh.calledMethods[method]; !ok { - t.Fatal("Review wasn't requested for googler") - } else if len(calls) != 1 { - t.Fatalf("Wrong number of calls for %s, got %d, expected 1", method, len(calls)) - } else if params := calls[0]; len(params) != 2 { - t.Fatalf("Wrong number of params for %s, got %d, expected 2", method, len(params)) - } else if param := params[0]; param != "pr1" { - t.Fatalf("Wrong first param for %s, got %v, expected pr1", method, param) - } else if param := params[1]; !github.IsTeamReviewer(param.(string)) { - t.Fatalf("Wrong second param for %s, got %v, expected a team reviewer", method, param) - } - - method = "TriggerMMPresubmitRuns" + method := "TriggerMMPresubmitRuns" expected := [][]any{{"sha1", map[string]string{"BRANCH_NAME": "branch1", "_BASE_BRANCH": "base1", "_HEAD_BRANCH": "head1", "_HEAD_REPO_URL": "url1", "_PR_NUMBER": "pr1"}}} if calls, ok := cb.calledMethods[method]; !ok { t.Fatal("Presubmit runs not triggered for googler") @@ -126,20 +112,7 @@ func TestExecMembershipChecker_AmbiguousUserFlow(t *testing.T) { execMembershipChecker("pr1", "sha1", "branch1", "url1", "head1", "base1", gh, cb) - method := "RequestPullRequestReviewer" - if calls, ok := gh.calledMethods[method]; !ok { - t.Fatal("Review wasn't requested for ambiguous user") - } else if len(calls) != 1 { - t.Fatalf("Wrong number of calls for %s, got %d, expected 1", method, len(calls)) - } else if params := calls[0]; len(params) != 2 { - t.Fatalf("Wrong number of params for %s, got %d, expected 2", method, len(params)) - } else if param := params[0]; param != "pr1" { - t.Fatalf("Wrong first param for %s, got %v, expected pr1", method, param) - } else if param := params[1]; !github.IsTeamReviewer(param.(string)) { - t.Fatalf("Wrong second param for %s, got %v, expected a team reviewer", method, param) - } - - method = "AddLabel" + method := "AddLabel" expected := [][]any{{"pr1", "awaiting-approval"}} if calls, ok := gh.calledMethods[method]; !ok { t.Fatal("Label wasn't posted to pull request") @@ -181,33 +154,4 @@ func TestExecMembershipChecker_CommentForNewPrimaryReviewer(t *testing.T) { } execMembershipChecker("pr1", "sha1", "branch1", "url1", "head1", "base1", gh, cb) - - method := "RequestPullRequestReviewer" - if calls, ok := gh.calledMethods[method]; !ok { - t.Fatal("Review wasn't requested for googler") - } else if len(calls) != 1 { - t.Fatalf("Wrong number of calls for %s, got %d, expected 1", method, len(calls)) - } else if params := calls[0]; len(params) != 2 { - t.Fatalf("Wrong number of params for %s, got %d, expected 2", method, len(params)) - } else if param := params[0]; param != "pr1" { - t.Fatalf("Wrong first param for %s, got %v, expected pr1", method, param) - } else if param := params[1]; !github.IsTeamReviewer(param.(string)) { - t.Fatalf("Wrong second param for %s, got %v, expected a team reviewer", method, param) - } - - method = "PostComment" - reviewerExp := regexp.MustCompile(`@(.*?),`) - if calls, ok := gh.calledMethods[method]; !ok { - t.Fatal("Comment wasn't posted stating user status") - } else if len(calls) != 1 { - t.Fatalf("Wrong number of calls for %s, got %d, expected 1", method, len(calls)) - } else if params := calls[0]; len(params) != 2 { - t.Fatalf("Wrong number of params for %s, got %d, expected 2", method, len(params)) - } else if param := params[0]; param != "pr1" { - t.Fatalf("Wrong first param for %s, got %v, expected pr1", method, param) - } else if param, ok := params[1].(string); !ok { - t.Fatalf("Got non-string second param for %s", method) - } else if submatches := reviewerExp.FindStringSubmatch(param); len(submatches) != 2 || !github.IsTeamReviewer(submatches[1]) { - t.Fatalf("%s called without a team reviewer (found %v) in the comment: %s", method, submatches, param) - } } diff --git a/.ci/magician/cmd/request_reviewer.go b/.ci/magician/cmd/request_reviewer.go new file mode 100644 index 000000000000..b5deed4e828e --- /dev/null +++ b/.ci/magician/cmd/request_reviewer.go @@ -0,0 +1,102 @@ +/* +* 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 ( + "fmt" + "magician/github" + "os" + + "github.com/spf13/cobra" +) + +// requestReviewerCmd represents the requestReviewer command +var requestReviewerCmd = &cobra.Command{ + Use: "request-reviewer", + Short: "Assigns and re-requests reviewers", + Long: `This command automatically requests (or re-requests) core contributor reviews for a PR based on whether the user is a core contributor. + + 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 + 2. If the author is not a core contributor: + a. Identifies the initially requested reviewer and those who previously reviewed this PR. + b. Determines and requests reviewers based on the above. + c. As appropriate, posts a welcome comment on the PR. + `, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + prNumber := args[0] + fmt.Println("PR Number: ", prNumber) + gh := github.NewClient() + execRequestReviewer(prNumber, gh) + }, +} + +func execRequestReviewer(prNumber string, gh GithubClient) { + pullRequest, err := gh.GetPullRequest(prNumber) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + author := pullRequest.User.Login + if !github.IsCoreContributor(author) { + fmt.Println("Not core contributor - assigning reviewer") + + requestedReviewers, err := gh.GetPullRequestRequestedReviewers(prNumber) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + previousReviewers, err := gh.GetPullRequestPreviousReviewers(prNumber) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + reviewersToRequest, newPrimaryReviewer := github.ChooseCoreReviewers(requestedReviewers, previousReviewers) + + for _, reviewer := range reviewersToRequest { + err = gh.RequestPullRequestReviewer(prNumber, reviewer) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + } + + if newPrimaryReviewer != "" { + comment := github.FormatReviewerComment(newPrimaryReviewer) + err = gh.PostComment(prNumber, comment) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + } + } +} + +func init() { + rootCmd.AddCommand(requestReviewerCmd) +} diff --git a/.ci/magician/cmd/request_reviewer_test.go b/.ci/magician/cmd/request_reviewer_test.go new file mode 100644 index 000000000000..d3daa08960f4 --- /dev/null +++ b/.ci/magician/cmd/request_reviewer_test.go @@ -0,0 +1,103 @@ +/* +* 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 ( + "github.com/stretchr/testify/assert" + "magician/github" + "testing" +) + +func TestExecRequestReviewer(t *testing.T) { + availableReviewers := github.AvailableReviewers() + cases := map[string]struct { + pullRequest github.PullRequest + requestedReviewers []string + previousReviewers []string + teamMembers map[string][]string + expectSpecificReviewers []string + expectReviewersFromList []string + }{ + "core contributor author doesn't get a new reviewer, re-request, or comment with no previous reviewers": { + pullRequest: github.PullRequest{ + User: github.User{Login: availableReviewers[0]}, + }, + expectSpecificReviewers: []string{}, + }, + "core contributor author doesn't get a new reviewer, re-request, or comment with previous reviewers": { + pullRequest: github.PullRequest{ + User: github.User{Login: availableReviewers[0]}, + }, + previousReviewers: []string{availableReviewers[1]}, + expectSpecificReviewers: []string{}, + }, + "non-core-contributor author gets a new reviewer with no previous reviewers": { + pullRequest: github.PullRequest{ + User: github.User{Login: "author"}, + }, + expectReviewersFromList: availableReviewers, + }, + "non-core-contributor author doesn't get a new reviewer (but does get re-request) with previous reviewers": { + pullRequest: github.PullRequest{ + User: github.User{Login: "author"}, + }, + previousReviewers: []string{availableReviewers[1], "author2", availableReviewers[2]}, + expectSpecificReviewers: []string{availableReviewers[1], availableReviewers[2]}, + }, + "non-core-contributor author doesn't get a new reviewer or a re-request with already-requested reviewers": { + pullRequest: github.PullRequest{ + User: github.User{Login: "author"}, + }, + requestedReviewers: []string{availableReviewers[1], "author2", availableReviewers[2]}, + expectSpecificReviewers: []string{}, + }, + } + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + requestedReviewers := []github.User{} + for _, login := range tc.requestedReviewers { + requestedReviewers = append(requestedReviewers, github.User{Login: login}) + } + previousReviewers := []github.User{} + for _, login := range tc.previousReviewers { + previousReviewers = append(previousReviewers, github.User{Login: login}) + } + gh := &mockGithub{ + pullRequest: tc.pullRequest, + requestedReviewers: requestedReviewers, + previousReviewers: previousReviewers, + calledMethods: make(map[string][][]any), + } + + execRequestReviewer("1", gh) + + actualReviewers := []string{} + for _, args := range gh.calledMethods["RequestPullRequestReviewer"] { + actualReviewers = append(actualReviewers, args[1].(string)) + } + + if tc.expectSpecificReviewers != nil { + assert.ElementsMatch(t, tc.expectSpecificReviewers, actualReviewers) + } + if tc.expectReviewersFromList != nil { + for _, reviewer := range actualReviewers { + assert.Contains(t, tc.expectReviewersFromList, reviewer) + } + } + }) + } +} diff --git a/.ci/magician/cmd/test_terraform_vcr.go b/.ci/magician/cmd/test_terraform_vcr.go index 99ca6cd74a81..2749ddd586c0 100644 --- a/.ci/magician/cmd/test_terraform_vcr.go +++ b/.ci/magician/cmd/test_terraform_vcr.go @@ -168,6 +168,8 @@ Affected tests: ` + fmt.Sprintf("`%d`", len(replayingResult.FailedTests)) + ` recordingResult, recordingErr := vt.RunParallel(vcr.Recording, provider.Beta, testDirs, replayingResult.FailedTests) if recordingErr != nil { testState = "failure" + } else { + testState = "success" } if err := vt.UploadCassettes("ci-vcr-cassettes", prNumber, provider.Beta); err != nil { @@ -200,7 +202,7 @@ Affected tests: ` + fmt.Sprintf("`%d`", len(replayingResult.FailedTests)) + ` testState = "failure" } - if err := vt.UploadLogs("ci-vcr-logs", prNumber, buildID, true, false, vcr.Recording, provider.Beta); err != nil { + if err := vt.UploadLogs("ci-vcr-logs", prNumber, buildID, true, true, vcr.Replaying, provider.Beta); err != nil { fmt.Println("Error uploading recording logs: ", err) os.Exit(1) } diff --git a/.ci/magician/github/REVIEWER_ASSIGNMENT_COMMENT.md b/.ci/magician/github/REVIEWER_ASSIGNMENT_COMMENT.md index 09d5bfc62936..2678f295947d 100644 --- a/.ci/magician/github/REVIEWER_ASSIGNMENT_COMMENT.md +++ b/.ci/magician/github/REVIEWER_ASSIGNMENT_COMMENT.md @@ -1,4 +1,4 @@ -Hello! I am a robot. It looks like you are a: {{if eq .authorUserType "Community Contributor"}}Community Contributor{{else}}~Community Contributor~{{end}} {{if eq .authorUserType "Googler"}}Googler{{else}}~Googler~{{end}} {{if eq .authorUserType "Core Contributor"}}Core Contributor{{else}}~Core Contributor~{{end}}. {{if .trusted}}Tests will run automatically.{{else}}Tests will require approval to run.{{end}} +Hello! I am a robot. Tests will require approval from a repository maintainer to run. @{{.reviewer}}, a repository maintainer, has been assigned to review your changes. If you have not received review feedback within 2 business days, please leave a comment on this PR asking them to take a look. diff --git a/.ci/magician/github/membership.go b/.ci/magician/github/membership.go index 0f41990a121c..c1f5081de1ca 100644 --- a/.ci/magician/github/membership.go +++ b/.ci/magician/github/membership.go @@ -39,18 +39,16 @@ var ( "trodge", "hao-nan-li", "NickElliot", + "BBBmau", } // This is for new team members who are onboarding - trustedContributors = []string{ - "BBBmau", - } + trustedContributors = []string{} // This is for reviewers who are "on vacation": will not receive new review assignments but will still receive re-requests for assigned PRs. onVacationReviewers = []string{ "zli82016", "NickElliot", - "ScottSuarez", } ) @@ -74,8 +72,8 @@ func (ut UserType) String() string { } func (gh *Client) GetUserType(user string) UserType { - if isTeamMember(user, gh.token) { - fmt.Println("User is a team member") + if IsCoreContributor(user) { + fmt.Println("User is a core contributor") return CoreContributorUserType } @@ -93,11 +91,11 @@ func (gh *Client) GetUserType(user string) UserType { } // Check if a user is team member to not request a random reviewer -func isTeamMember(author, githubToken string) bool { - return slices.Contains(reviewerRotation, author) || slices.Contains(trustedContributors, author) +func IsCoreContributor(user string) bool { + return slices.Contains(reviewerRotation, user) || slices.Contains(trustedContributors, user) } -func IsTeamReviewer(reviewer string) bool { +func IsCoreReviewer(reviewer string) bool { return slices.Contains(reviewerRotation, reviewer) } @@ -112,8 +110,12 @@ func isOrgMember(author, org, githubToken string) bool { } func GetRandomReviewer() string { - availableReviewers := utils.Removes(reviewerRotation, onVacationReviewers) + availableReviewers := AvailableReviewers() rand.Seed(time.Now().UnixNano()) reviewer := availableReviewers[rand.Intn(len(availableReviewers))] return reviewer } + +func AvailableReviewers() []string { + return utils.Removes(reviewerRotation, onVacationReviewers) +} diff --git a/.ci/magician/github/reviewer_assignment.go b/.ci/magician/github/reviewer_assignment.go index 23651a15b1a0..d06415bab810 100644 --- a/.ci/magician/github/reviewer_assignment.go +++ b/.ci/magician/github/reviewer_assignment.go @@ -34,14 +34,14 @@ func ChooseCoreReviewers(requestedReviewers, previousReviewers []User) (reviewer newPrimaryReviewer = "" for _, reviewer := range requestedReviewers { - if IsTeamReviewer(reviewer.Login) { + if IsCoreReviewer(reviewer.Login) { hasPrimaryReviewer = true break } } for _, reviewer := range previousReviewers { - if IsTeamReviewer(reviewer.Login) { + if IsCoreReviewer(reviewer.Login) { hasPrimaryReviewer = true reviewersToRequest = append(reviewersToRequest, reviewer.Login) } @@ -55,16 +55,14 @@ func ChooseCoreReviewers(requestedReviewers, previousReviewers []User) (reviewer return reviewersToRequest, newPrimaryReviewer } -func FormatReviewerComment(newPrimaryReviewer string, authorUserType UserType, trusted bool) string { +func FormatReviewerComment(newPrimaryReviewer string) string { tmpl, err := template.New("REVIEWER_ASSIGNMENT_COMMENT.md").Parse(reviewerAssignmentComment) if err != nil { panic(fmt.Sprintf("Unable to parse REVIEWER_ASSIGNMENT_COMMENT.md: %s", err)) } sb := new(strings.Builder) tmpl.Execute(sb, map[string]any{ - "reviewer": newPrimaryReviewer, - "authorUserType": authorUserType.String(), - "trusted": trusted, + "reviewer": newPrimaryReviewer, }) return sb.String() } diff --git a/.ci/magician/github/reviewer_assignment_test.go b/.ci/magician/github/reviewer_assignment_test.go index 252fc4d61186..6247891fd7db 100644 --- a/.ci/magician/github/reviewer_assignment_test.go +++ b/.ci/magician/github/reviewer_assignment_test.go @@ -127,27 +127,12 @@ func TestFormatReviewerComment(t *testing.T) { tc := tc t.Run(tn, func(t *testing.T) { t.Parallel() - comment := FormatReviewerComment(tc.Reviewer, tc.AuthorUserType, tc.Trusted) + comment := FormatReviewerComment(tc.Reviewer) t.Log(comment) if !strings.Contains(comment, fmt.Sprintf("@%s", tc.Reviewer)) { t.Errorf("wanted comment to contain @%s; does not.", tc.Reviewer) } - if !strings.Contains(comment, tc.AuthorUserType.String()) { - t.Errorf("wanted comment to contain user type (%s); does not.", tc.AuthorUserType.String()) - } - if strings.Contains(comment, fmt.Sprintf("~%s~", tc.AuthorUserType.String())) { - t.Errorf("wanted user type (%s) in comment to not be crossed out, but it is", tc.AuthorUserType.String()) - } - for _, ut := range []UserType{CommunityUserType, GooglerUserType, CoreContributorUserType} { - if ut != tc.AuthorUserType && !strings.Contains(comment, fmt.Sprintf("~%s~", ut.String())) { - t.Errorf("wanted other user type (%s) in comment to be crossed out, but it is not", ut) - } - } - - if tc.Trusted && !strings.Contains(comment, "Tests will run automatically") { - t.Errorf("wanted comment to say tests will run automatically; does not") - } - if !tc.Trusted && !strings.Contains(comment, "Tests will require approval") { + if !strings.Contains(comment, "Tests will require approval") { t.Errorf("wanted comment to say tests will require approval; does not") } }) diff --git a/.github/workflows/request-reviewer.yml b/.github/workflows/request-reviewer.yml new file mode 100644 index 000000000000..2831ec975caa --- /dev/null +++ b/.github/workflows/request-reviewer.yml @@ -0,0 +1,40 @@ +name: request-reviewer + +permissions: read-all + +on: + pull_request_target: + types: + - edited + - opened + - ready_for_review + - reopened + - synchronize + branches: + - 'main' + - 'FEATURE-BRANCH-*' + +jobs: + request-review: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + permissions: + pull-requests: write + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '^1.20' + # Disable caching for now due to issues with large provider dependency caches + cache: false + - name: Build magician + run: | + cd .ci/magician + go build . + - name: Request reviewer + run: .ci/magician/magician request-reviewer ${{ github.event.pull_request.number }} + diff --git a/.github/workflows/teamcity-pr-checks.yml b/.github/workflows/teamcity-pr-checks.yml new file mode 100644 index 000000000000..6a576185e912 --- /dev/null +++ b/.github/workflows/teamcity-pr-checks.yml @@ -0,0 +1,46 @@ +name: TeamCity Configuration Tests +permissions: read-all + +on: + workflow_dispatch: + pull_request: + paths: + - '.github/workflows/teamcity-pr-checks.yml' + - 'mmv1/third_party/terraform/.teamcity/**' + + +jobs: + teamcity-config-tests: + runs-on: ubuntu-22.04 + steps: + - name: Checkout Repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + + - name: Setup Java + uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0 + with: + distribution: zulu + java-version: 17 + java-package: jdk + + - name: Cache Maven files + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Download TeamCity Tools + run : | + cd mmv1/third_party/terraform/.teamcity + make tools + + # Running the tests twice ensures that the tests are discovered and run + - name: Run TeamCity Tests + run : | + cd mmv1/third_party/terraform/.teamcity + make test + make test \ No newline at end of file diff --git a/.github/workflows/teamcity-services-diff-check-weekly.yml b/.github/workflows/teamcity-services-diff-check-weekly.yml new file mode 100644 index 000000000000..49289dc24ea0 --- /dev/null +++ b/.github/workflows/teamcity-services-diff-check-weekly.yml @@ -0,0 +1,71 @@ +name: TeamCity Services Weekly Diff Check +permissions: read-all + +on: + schedule: + # Runs every tuesday morning + - cron: '0 4 * * 2' + +jobs: + terraform-provider-google: + uses: ./.github/workflows/build-downstream.yml + with: + repo: 'terraform-provider-google' + + terraform-provider-google-beta: + uses: ./.github/workflows/build-downstream.yml + with: + repo: 'terraform-provider-google-beta' + + teamcity-services-diff-check: + needs: [terraform-provider-google, terraform-provider-google-beta] + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + + - name: Download built artifacts - GA provider + uses: actions/download-artifact@v2 + with: + name: artifact-terraform-provider-google + path: artifacts + + - name: Unzip the artifacts and delete the zip + run: | + unzip -o artifacts/output.zip -d ./provider + rm artifacts/output.zip + + - name: Download built artifacts - Beta provider + uses: actions/download-artifact@v2 + with: + name: artifact-terraform-provider-google-beta + path: artifacts + + - name: Unzip the artifacts and delete the zip + run: | + unzip -o artifacts/output.zip -d ./provider + rm artifacts/output.zip + + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: '^1.20' + + - name: Cache Go modules and build cache + uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-test-terraform-provider-google-${{hashFiles('go.sum','google-*/transport/**','google-*/tpgresource/**','google-*/acctest/**','google-*/envvar/**','google-*/sweeper/**','google-*/verify/**') }} + restore-keys: | + ${{ runner.os }}-test-terraform-provider-google-${{ hashFiles('go.sum') }} + ${{ runner.os }}-test-terraform-provider-google- + + - name: Diff Check + run: | + ls provider/google/services > tools/teamcity-diff-check/services_ga.txt + ls provider/google-beta/services > tools/teamcity-diff-check/services_beta.txt + cd tools/teamcity-diff-check + go run main.go -service_file=services_ga + go run main.go -service_file=services_beta + \ No newline at end of file diff --git a/.github/workflows/teamcity-services-diff-check.yml b/.github/workflows/teamcity-services-diff-check.yml new file mode 100644 index 000000000000..535ff9fb6293 --- /dev/null +++ b/.github/workflows/teamcity-services-diff-check.yml @@ -0,0 +1,94 @@ +name: TeamCity Services Diff Check +permissions: read-all + +on: + workflow_dispatch: + pull_request: + paths: + - '.github/workflows/teamcity-services-diff-check.yml' + - 'mmv1/third_party/terraform/.teamcity/components/inputs/services_ga.kt' + - 'mmv1/third_party/terraform/.teamcity/components/inputs/services_beta.kt' + - 'mmv1/products/**' +jobs: + check-pr: + runs-on: ubuntu-22.04 + outputs: + services: ${{steps.services.outputs.services}} + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - id: services + name: "Check for New Services" + run: | + newServices=$(($(git diff --name-only --diff-filter=A origin/main HEAD | grep -P "mmv1/products/.*/product.yaml" | wc -l))) + echo "services=$newServices" >> "${GITHUB_OUTPUT}" + if [ "$newServices" = "0" ];then + echo "No new service found." + fi + terraform-provider-google: + if: ${{needs.check-pr.outputs.services != '0'}} + needs: check-pr + uses: ./.github/workflows/build-downstream.yml + with: + repo: 'terraform-provider-google' + + terraform-provider-google-beta: + if: ${{needs.check-pr.outputs.services != '0'}} + needs: check-pr + uses: ./.github/workflows/build-downstream.yml + with: + repo: 'terraform-provider-google-beta' + + teamcity-services-diff-check: + needs: [terraform-provider-google, terraform-provider-google-beta] + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + + - name: Download built artifacts - GA provider + uses: actions/download-artifact@v2 + with: + name: artifact-terraform-provider-google + path: artifacts + + - name: Unzip the artifacts and delete the zip + run: | + unzip -o artifacts/output.zip -d ./provider + rm artifacts/output.zip + + - name: Download built artifacts - Beta provider + uses: actions/download-artifact@v2 + with: + name: artifact-terraform-provider-google-beta + path: artifacts + + - name: Unzip the artifacts and delete the zip + run: | + unzip -o artifacts/output.zip -d ./provider + rm artifacts/output.zip + + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: '^1.20' + + - name: Cache Go modules and build cache + uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-test-terraform-provider-google-${{hashFiles('go.sum','google-*/transport/**','google-*/tpgresource/**','google-*/acctest/**','google-*/envvar/**','google-*/sweeper/**','google-*/verify/**') }} + restore-keys: | + ${{ runner.os }}-test-terraform-provider-google-${{ hashFiles('go.sum') }} + ${{ runner.os }}-test-terraform-provider-google- + + - name: Diff Check + run: | + ls provider/google/services > tools/teamcity-diff-check/services_ga.txt + ls provider/google-beta/services > tools/teamcity-diff-check/services_beta.txt + cd tools/teamcity-diff-check + go run main.go -service_file=services_ga + go run main.go -service_file=services_beta + \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS index 5528c7612e1b..e69de29bb2d1 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +0,0 @@ -* @GoogleCloudPlatform/terraform-team diff --git a/mmv1/products/alloydb/User.yaml b/mmv1/products/alloydb/User.yaml index 1f3ad0f76698..895f7557d924 100644 --- a/mmv1/products/alloydb/User.yaml +++ b/mmv1/products/alloydb/User.yaml @@ -24,6 +24,7 @@ references: !ruby/object:Api::Resource::ReferenceLinks 'AlloyDB': 'https://cloud.google.com/alloydb/docs/' api: 'https://cloud.google.com/alloydb/docs/reference/rest/v1/projects.locations.clusters.users/create' import_format: ['projects/{{project}}/locations/{{location}}/clusters/{{cluster}}/users/{{user_id}}'] +# Skipping the sweeper because instances will be deleted during cluster sweeps skip_sweeper: true autogen_async: true custom_code: !ruby/object:Provider::Terraform::CustomCode diff --git a/mmv1/products/bigquery/Routine.yaml b/mmv1/products/bigquery/Routine.yaml index 513a80b503d4..3dbbfd3b0577 100644 --- a/mmv1/products/bigquery/Routine.yaml +++ b/mmv1/products/bigquery/Routine.yaml @@ -26,7 +26,7 @@ import_format: ['projects/{{project}}/datasets/{{dataset_id}}/routines/{{routine_id}}'] examples: - !ruby/object:Provider::Terraform::Examples - name: 'big_query_routine_basic' + name: 'bigquery_routine_basic' primary_resource_id: 'sproc' primary_resource_name: "fmt.Sprintf(\"tf_test_dataset_id%s\", context[\"random_suffix\"\ @@ -35,7 +35,7 @@ examples: dataset_id: 'dataset_id' routine_id: 'routine_id' - !ruby/object:Provider::Terraform::Examples - name: 'big_query_routine_json' + name: 'bigquery_routine_json' primary_resource_id: 'sproc' primary_resource_name: "fmt.Sprintf(\"tf_test_dataset_id%s\", context[\"random_suffix\"\ @@ -44,7 +44,7 @@ examples: dataset_id: 'dataset_id' routine_id: 'routine_id' - !ruby/object:Provider::Terraform::Examples - name: 'big_query_routine_tvf' + name: 'bigquery_routine_tvf' primary_resource_id: 'sproc' primary_resource_name: "fmt.Sprintf(\"tf_test_dataset_id%s\", context[\"random_suffix\"\ @@ -53,26 +53,34 @@ examples: dataset_id: 'dataset_id' routine_id: 'routine_id' - !ruby/object:Provider::Terraform::Examples - name: 'big_query_routine_pyspark' + name: 'bigquery_routine_pyspark' primary_resource_id: 'pyspark' vars: dataset_id: 'dataset_id' connection_id: 'connection_id' routine_id: 'routine_id' - !ruby/object:Provider::Terraform::Examples - name: 'big_query_routine_pyspark_mainfile' + name: 'bigquery_routine_pyspark_mainfile' primary_resource_id: 'pyspark_mainfile' vars: dataset_id: 'dataset_id' connection_id: 'connection_id' routine_id: 'routine_id' - !ruby/object:Provider::Terraform::Examples - name: 'big_query_routine_spark_jar' + name: 'bigquery_routine_spark_jar' primary_resource_id: 'spark_jar' vars: dataset_id: 'dataset_id' connection_id: 'connection_id' routine_id: 'routine_id' + - !ruby/object:Provider::Terraform::Examples + skip_test: true + name: 'bigquery_routine_remote_function' + primary_resource_id: 'remote_function' + vars: + dataset_id: 'dataset_id' + connection_id: 'connection_id' + routine_id: 'routine_id' properties: - !ruby/object:Api::Type::NestedObject name: routineReference @@ -283,3 +291,33 @@ properties: description: | The fully qualified name of a class in jarUris, for example, com.example.wordcount. Exactly one of mainClass and main_jar_uri field should be set for Java/Scala language type. + - !ruby/object:Api::Type::NestedObject + name: 'remoteFunctionOptions' + description: Remote function specific options. + properties: + - !ruby/object:Api::Type::String + name: 'endpoint' + description: | + Endpoint of the user-provided remote service, e.g. + `https://us-east1-my_gcf_project.cloudfunctions.net/remote_add` + - !ruby/object:Api::Type::String + name: 'connection' + description: | + Fully qualified name of the user-provided connection object which holds + the authentication information to send requests to the remote service. + Format: "projects/{projectId}/locations/{locationId}/connections/{connectionId}" + - !ruby/object:Api::Type::KeyValuePairs + name: 'userDefinedContext' + description: | + User-defined context as a set of key/value pairs, which will be sent as function + invocation context together with batched arguments in the requests to the remote + service. The total number of bytes of keys and values must be less than 8KB. + + An object containing a list of "key": value pairs. Example: + `{ "name": "wrench", "mass": "1.3kg", "count": "3" }`. + default_from_api: true + - !ruby/object:Api::Type::String + name: 'maxBatchingRows' + description: | + Max number of rows in each batch sent to the remote service. If absent or if 0, + BigQuery dynamically decides the number of rows in a batch. diff --git a/mmv1/products/certificatemanager/DnsAuthorization.yaml b/mmv1/products/certificatemanager/DnsAuthorization.yaml index bcb50c60c329..33cd7604ed80 100644 --- a/mmv1/products/certificatemanager/DnsAuthorization.yaml +++ b/mmv1/products/certificatemanager/DnsAuthorization.yaml @@ -13,9 +13,9 @@ --- !ruby/object:Api::Resource name: 'DnsAuthorization' -base_url: 'projects/{{project}}/locations/global/dnsAuthorizations' -create_url: 'projects/{{project}}/locations/global/dnsAuthorizations?dnsAuthorizationId={{name}}' -self_link: 'projects/{{project}}/locations/global/dnsAuthorizations/{{name}}' +base_url: 'projects/{{project}}/locations/{{location}}/dnsAuthorizations' +create_url: 'projects/{{project}}/locations/{{location}}/dnsAuthorizations?dnsAuthorizationId={{name}}' +self_link: 'projects/{{project}}/locations/{{location}}/dnsAuthorizations/{{name}}' update_verb: :PATCH update_mask: true description: | @@ -39,7 +39,9 @@ async: !ruby/object:Api::OpAsync docs: !ruby/object:Provider::Terraform::Docs autogen_async: true import_format: - ['projects/{{project}}/locations/global/dnsAuthorizations/{{name}}'] + ['projects/{{project}}/locations/{{location}}/dnsAuthorizations/{{name}}'] +schema_version: 1 +state_upgraders: true examples: - !ruby/object:Provider::Terraform::Examples name: 'certificate_manager_dns_authorization_basic' @@ -58,6 +60,13 @@ parameters: Name of the resource; provided by the client when the resource is created. The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, and all following characters must be a dash, underscore, letter or digit. + - !ruby/object:Api::Type::String + name: 'location' + description: | + The Certificate Manager location. If not specified, "global" is used. + default_value: global + immutable: true + url_param_only: true properties: - !ruby/object:Api::Type::String name: 'description' diff --git a/mmv1/products/clouddeploy/Automation.yaml b/mmv1/products/clouddeploy/Automation.yaml index 6af88d3d88f4..bb46c92440bb 100644 --- a/mmv1/products/clouddeploy/Automation.yaml +++ b/mmv1/products/clouddeploy/Automation.yaml @@ -21,7 +21,7 @@ references: !ruby/object:Api::Resource::ReferenceLinks api: 'https://cloud.google.com/deploy/docs/api/reference/rest/v1/projects.locations.deliveryPipelines.automations' base_url: 'projects/{{project}}/locations/{{location}}/deliveryPipelines/{{delivery_pipeline}}/automations' self_link: 'projects/{{project}}/locations/{{location}}/deliveryPipelines/{{delivery_pipeline}}/automations/{{name}}' -min_version: beta + create_url: 'projects/{{project}}/locations/{{location}}/deliveryPipelines/{{delivery_pipeline}}/automations?automationId={{name}}' update_verb: :PATCH update_mask: true @@ -51,7 +51,6 @@ autogen_async: true examples: - !ruby/object:Provider::Terraform::Examples name: "clouddeploy_automation_basic" - min_version: beta primary_resource_id: "b-automation" vars: automation: "cd-automation" @@ -60,7 +59,7 @@ examples: service_account: :SERVICE_ACCT - !ruby/object:Provider::Terraform::Examples name: "clouddeploy_automation_full" - min_version: beta + primary_resource_id: "f-automation" vars: automation: "cd-automation" diff --git a/mmv1/products/clouddeploy/Target.yaml b/mmv1/products/clouddeploy/Target.yaml new file mode 100644 index 000000000000..b8ab9d6fd154 --- /dev/null +++ b/mmv1/products/clouddeploy/Target.yaml @@ -0,0 +1,40 @@ +# 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. + +--- !ruby/object:Api::Resource +name: 'Target' +description: | + The Cloud Deploy `Target` resource. +base_url: 'projects/{{project}}/locations/{{location}}/targets' +self_link: 'projects/{{project}}/locations/{{location}}/targets/{{name}}' +exclude_resource: true +id_format: 'projects/{{project}}/locations/{{location}}/targets/{{name}}' +import_format: + - 'projects/{{project}}/locations/{{location}}/targets/{{name}}' +iam_policy: !ruby/object:Api::Resource::IamPolicy + parent_resource_attribute: 'name' + method_name_separator: ':' + base_url: 'projects/{{project}}/locations/{{location}}/targets/{{name}}' + import_format: ['projects/{{project}}/locations/{{location}}/targets/{{name}}', '{{name}}'] +examples: + - !ruby/object:Provider::Terraform::Examples + name: 'clouddeploy_target_basic' + primary_resource_id: 'default' + primary_resource_name: 'fmt.Sprintf("tf-test-cd-target%s", context["random_suffix"])' + vars: + target: 'cd-target' +properties: + - !ruby/object:Api::Type::String + name: "name" + description: "Dummy property." + required: true diff --git a/mmv1/products/cloudfunctions2/Function.yaml b/mmv1/products/cloudfunctions2/Function.yaml index a094196586fc..71fd0ef07e87 100644 --- a/mmv1/products/cloudfunctions2/Function.yaml +++ b/mmv1/products/cloudfunctions2/Function.yaml @@ -594,6 +594,7 @@ properties: events originating in this region. It can be the same region as the function, a different region or multi-region, or the global region. If not provided, defaults to the same region as the function. + default_from_api: true - !ruby/object:Api::Type::String name: 'eventType' description: 'Required. The type of event to observe.' diff --git a/mmv1/products/cloudrunv2/Service.yaml b/mmv1/products/cloudrunv2/Service.yaml index 1d4c3b7a0895..4c7198049bfe 100644 --- a/mmv1/products/cloudrunv2/Service.yaml +++ b/mmv1/products/cloudrunv2/Service.yaml @@ -430,7 +430,8 @@ properties: - !ruby/object:Api::Type::Boolean name: 'cpuIdle' description: |- - Determines whether CPU should be throttled or not outside of requests. + Determines whether CPU is only allocated during requests. True by default if the parent `resources` field is not set. However, if + `resources` is set, this field must be explicitly set to true to preserve the default behavior. - !ruby/object:Api::Type::Boolean name: 'startupCpuBoost' description: |- diff --git a/mmv1/products/compute/InstanceGroup.yaml b/mmv1/products/compute/InstanceGroup.yaml index e9504d021b87..59abe5b63864 100644 --- a/mmv1/products/compute/InstanceGroup.yaml +++ b/mmv1/products/compute/InstanceGroup.yaml @@ -59,7 +59,7 @@ parameters: and will not be deleted. Only the full identifier of the instance will be returned. - **NOTE** If a user will be recreating instances under the same name + !> **WARNING** If a user will be recreating instances under the same name (eg. via `terraform taint`), please consider adding instances to an instance group via the `instance_group_membership` resource, along side the `update_triggered_by` lifecycle method with an instance's ID. diff --git a/mmv1/products/compute/InstanceGroupMembership.yaml b/mmv1/products/compute/InstanceGroupMembership.yaml index 414b0846cb83..f41938f17bb8 100644 --- a/mmv1/products/compute/InstanceGroupMembership.yaml +++ b/mmv1/products/compute/InstanceGroupMembership.yaml @@ -18,11 +18,11 @@ base_url: 'projects/{{project}}/zones/{{zone}}/instanceGroups/{{instance_group}} description: | Represents the Instance membership to the Instance Group. - **NOTE** You can use this resource instead of the `instances` field in the + -> **NOTE** You can use this resource instead of the `instances` field in the `google_compute_instance_group`, however it's not recommended to use it alongside this field. It might cause inconsistencies, as they can end up competing over control. - **NOTE** This resource has been added to avoid a situation, where after + -> **NOTE** This resource has been added to avoid a situation, where after Instance is recreated, it's removed from Instance Group and it's needed to perform `apply` twice. To avoid situations like this, please use this resource with the lifecycle `update_triggered_by` method, with the passed Instance's ID. diff --git a/mmv1/products/compute/RegionAutoscaler.yaml b/mmv1/products/compute/RegionAutoscaler.yaml index 931e07443408..7ef41e624626 100644 --- a/mmv1/products/compute/RegionAutoscaler.yaml +++ b/mmv1/products/compute/RegionAutoscaler.yaml @@ -140,7 +140,6 @@ properties: Defines operating mode for this policy. - !ruby/object:Api::Type::NestedObject name: 'scaleDownControl' - min_version: beta description: | Defines scale down controls to reduce the risk of response latency and outages due to abrupt scale-in events @@ -265,7 +264,6 @@ properties: required: true - !ruby/object:Api::Type::Double name: 'singleInstanceAssignment' - min_version: beta description: | If scaling is based on a per-group metric value that represents the total amount of work to be done or resource usage, set this value to @@ -341,7 +339,6 @@ properties: (if you are using gce_instance resource type). If multiple TimeSeries are returned upon the query execution, the autoscaler will sum their respective values to obtain its scaling value. - min_version: beta - !ruby/object:Api::Type::NestedObject name: 'loadBalancingUtilization' description: | diff --git a/mmv1/products/compute/RegionNetworkEndpoint.yaml b/mmv1/products/compute/RegionNetworkEndpoint.yaml index cf4f49b6c5d2..81d31f7e1f6e 100644 --- a/mmv1/products/compute/RegionNetworkEndpoint.yaml +++ b/mmv1/products/compute/RegionNetworkEndpoint.yaml @@ -19,7 +19,7 @@ description: | A Region network endpoint represents a IP address/FQDN and port combination that is part of a specific network endpoint group (NEG). - **NOTE**: Network endpoints cannot be created outside of a network endpoint group. + ~> **NOTE**: Network endpoints cannot be created outside of a network endpoint group. immutable: true create_verb: :POST create_url: projects/{{project}}/regions/{{region}}/networkEndpointGroups/{{region_network_endpoint_group}}/attachNetworkEndpoints diff --git a/mmv1/products/compute/RegionTargetHttpsProxy.yaml b/mmv1/products/compute/RegionTargetHttpsProxy.yaml index 723d03ad87af..553bd1a5655a 100644 --- a/mmv1/products/compute/RegionTargetHttpsProxy.yaml +++ b/mmv1/products/compute/RegionTargetHttpsProxy.yaml @@ -41,6 +41,9 @@ async: !ruby/object:Api::OpAsync error: !ruby/object:Api::OpAsync::Error path: 'error/errors' message: 'message' +custom_code: !ruby/object:Provider::Terraform::CustomCode + encoder: templates/terraform/encoders/compute_region_target_https_proxy.go.erb + decoder: templates/terraform/decoders/compute_region_target_https_proxy.go.erb examples: - !ruby/object:Provider::Terraform::Examples name: 'region_target_https_proxy_basic' @@ -51,6 +54,14 @@ examples: region_url_map_name: 'url-map' region_backend_service_name: 'backend-service' region_health_check_name: 'http-health-check' + - !ruby/object:Provider::Terraform::Examples + name: 'region_target_https_proxy_certificate_manager_certificate' + primary_resource_id: 'default' + vars: + region_target_https_proxy_name: 'target-http-proxy' + certificate_manager_certificate_name: 'my-certificate' + region_url_map_name: 'url-map' + region_backend_service_name: 'backend-service' parameters: - !ruby/object:Api::Type::ResourceRef name: 'region' @@ -109,13 +120,26 @@ properties: # update_verb: :POST # update_url: # 'projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}/setQuicOverride' + - !ruby/object:Api::Type::Array + name: 'certificateManagerCertificates' + description: | + URLs to certificate manager certificate resources that are used to authenticate connections between users and the load balancer. + Currently, you may specify up to 15 certificates. Certificate manager certificates do not apply when the load balancing scheme is set to INTERNAL_SELF_MANAGED. + sslCertificates and certificateManagerCertificates fields can not be defined together. + Accepted format is `//certificatemanager.googleapis.com/projects/{project}/locations/{location}/certificates/{resourceName}` or just the self_link `projects/{project}/locations/{location}/certificates/{resourceName}` + update_verb: :POST + update_url: 'projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}/setSslCertificates' + item_type: Api::Type::String + custom_expand: 'templates/terraform/custom_expand/certificate_manager_certificate_construct_full_url.go.erb' + diff_suppress_func: 'tpgresource.CompareResourceNames' + conflicts: + - ssl_certificates - !ruby/object:Api::Type::Array name: 'sslCertificates' description: | - A list of RegionSslCertificate resources that are used to authenticate - connections between users and the load balancer. Currently, exactly - one SSL certificate must be specified. - required: true + URLs to SslCertificate resources that are used to authenticate connections between users and the load balancer. + At least one SSL certificate must be specified. Currently, you may specify up to 15 SSL certificates. + sslCertificates do not apply when the load balancing scheme is set to INTERNAL_SELF_MANAGED. update_verb: :POST update_url: 'projects/{{project}}/regions/{{region}}/targetHttpsProxies/{{name}}/setSslCertificates' item_type: !ruby/object:Api::Type::ResourceRef @@ -124,6 +148,8 @@ properties: imports: 'selfLink' description: 'The SSL certificates used by this TargetHttpsProxy' custom_expand: 'templates/terraform/custom_expand/array_resourceref_with_validation.go.erb' + conflicts: + - certificate_manager_certificates - !ruby/object:Api::Type::ResourceRef name: 'sslPolicy' resource: 'RegionSslPolicy' diff --git a/mmv1/products/compute/RouterNat.yaml b/mmv1/products/compute/RouterNat.yaml index 9b0d414391a1..fef5eb3398c2 100644 --- a/mmv1/products/compute/RouterNat.yaml +++ b/mmv1/products/compute/RouterNat.yaml @@ -236,7 +236,8 @@ properties: - !ruby/object:Api::Type::Integer name: minPortsPerVm description: | - Minimum number of ports allocated to a VM from this NAT. + Minimum number of ports allocated to a VM from this NAT. Defaults to 64 for static port allocation and 32 dynamic port allocation if not set. + default_from_api: true - !ruby/object:Api::Type::Integer name: maxPortsPerVm description: | diff --git a/mmv1/products/datacatalog/Taxonomy.yaml b/mmv1/products/datacatalog/Taxonomy.yaml index 8fa5bf8c9b7b..c472fd415119 100644 --- a/mmv1/products/datacatalog/Taxonomy.yaml +++ b/mmv1/products/datacatalog/Taxonomy.yaml @@ -23,6 +23,7 @@ references: !ruby/object:Api::Resource::ReferenceLinks guides: 'Official Documentation': https://cloud.google.com/data-catalog/docs api: https://cloud.google.com/data-catalog/docs/reference/rest/v1/projects.locations.taxonomies +# Skip sweeper as this resource name is autogenerated from the API, thus the sweeper runs on the display_name field. Handwritten sweeper added #9908. skip_sweeper: true iam_policy: !ruby/object:Api::Resource::IamPolicy skip_import_test: true diff --git a/mmv1/products/datapipeline/Pipeline.yaml b/mmv1/products/datapipeline/Pipeline.yaml index c3e24c9fcd9e..1bf821d94bff 100644 --- a/mmv1/products/datapipeline/Pipeline.yaml +++ b/mmv1/products/datapipeline/Pipeline.yaml @@ -31,6 +31,8 @@ examples: primary_resource_name: 'fmt.Sprintf("tf-test-my-pipeline%s", context["random_suffix"])' primary_resource_id: 'primary' + ignore_read_extra: + - 'schedule_info.0.next_job_time' vars: pipeline_name: 'my-pipeline' account_id: 'my-account' diff --git a/mmv1/products/firebase/AndroidApp.yaml b/mmv1/products/firebase/AndroidApp.yaml index 8cd21b2db2ba..3e58993f5e98 100644 --- a/mmv1/products/firebase/AndroidApp.yaml +++ b/mmv1/products/firebase/AndroidApp.yaml @@ -52,6 +52,7 @@ import_format: '{{app_id}}', ] autogen_async: true +# Sweeper skipped as this resource has customized deletion. skip_sweeper: true identity: - appId diff --git a/mmv1/products/firebase/AppleApp.yaml b/mmv1/products/firebase/AppleApp.yaml index 7c9ab93c8258..f40d18733008 100644 --- a/mmv1/products/firebase/AppleApp.yaml +++ b/mmv1/products/firebase/AppleApp.yaml @@ -52,6 +52,7 @@ import_format: '{{app_id}}' ] autogen_async: true +# Sweeper skipped as this resource has customized deletion. skip_sweeper: true identity: - appId diff --git a/mmv1/products/firebase/WebApp.yaml b/mmv1/products/firebase/WebApp.yaml index e6682fdb76df..1ccebb6f418d 100644 --- a/mmv1/products/firebase/WebApp.yaml +++ b/mmv1/products/firebase/WebApp.yaml @@ -52,6 +52,7 @@ import_format: '{{app_id}}', ] autogen_async: true +# Sweeper skipped as this resource has customized deletion. skip_sweeper: true identity: - appId diff --git a/mmv1/products/firebaseappcheck/AppAttestConfig.yaml b/mmv1/products/firebaseappcheck/AppAttestConfig.yaml new file mode 100644 index 000000000000..025e59fbcca9 --- /dev/null +++ b/mmv1/products/firebaseappcheck/AppAttestConfig.yaml @@ -0,0 +1,89 @@ +# 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. + +--- +!ruby/object:Api::Resource +name: "AppAttestConfig" +base_url: projects/{{project}}/apps/{{app_id}}/appAttestConfig +self_link: projects/{{project}}/apps/{{app_id}}/appAttestConfig +create_url: projects/{{project}}/apps/{{app_id}}/appAttestConfig?updateMask=tokenTtl +create_verb: :PATCH +update_verb: :PATCH +update_mask: true +skip_delete: true +description: | + An app's App Attest configuration object. Note that the Team ID registered with your + app is used as part of the validation process. Make sure your `google_firebase_apple_app` has a team_id present. +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + "Official Documentation": "https://firebase.google.com/docs/app-check" + api: "https://firebase.google.com/docs/reference/appcheck/rest/v1/projects.apps.appAttestConfig" +import_format: + [ + "projects/{{project}}/apps/{{app_id}}/appAttestConfig", + "{{project}}/{{app_id}}", + "{{app_id}}", + ] +examples: + - !ruby/object:Provider::Terraform::Examples + name: "firebase_app_check_app_attest_config_minimal" + # Need the time_sleep resource + pull_external: true + primary_resource_id: "default" + vars: + team_id: "9987654321" + bundle_id: "bundle.id.appattest" + test_vars_overrides: + # Don't add random suffix + team_id: '"9987654321"' + test_env_vars: + project_id: :PROJECT_NAME + - !ruby/object:Provider::Terraform::Examples + name: "firebase_app_check_app_attest_config_full" + # Need the time_sleep resource + pull_external: true + primary_resource_id: "default" + vars: + team_id: "9987654321" + bundle_id: "bundle.id.appattest" + token_ttl: "7200s" + test_vars_overrides: + # Don't add random suffix + team_id: '"9987654321"' + token_ttl: '"7200s"' + test_env_vars: + project_id: :PROJECT_NAME +parameters: + - !ruby/object:Api::Type::String + name: app_id + description: | + The ID of an + [Apple App](https://firebase.google.com/docs/reference/firebase-management/rest/v1beta1/projects.iosApps#IosApp.FIELDS.app_id). + required: true + immutable: true + url_param_only: true +properties: + - !ruby/object:Api::Type::String + name: name + description: | + The relative resource name of the App Attest configuration object + output: true + pattern: projects/{{project}}/apps/{{app_id}}/appAttestConfig + - !ruby/object:Api::Type::String + name: tokenTtl + description: | + Specifies the duration for which App Check tokens exchanged from App Attest artifacts will be valid. + If unset, a default value of 1 hour is assumed. Must be between 30 minutes and 7 days, inclusive. + + A duration in seconds with up to nine fractional digits, ending with 's'. Example: "3.5s". + default_from_api: true diff --git a/mmv1/products/firebaseappcheck/PlayIntegrityConfig.yaml b/mmv1/products/firebaseappcheck/PlayIntegrityConfig.yaml new file mode 100644 index 000000000000..7631e64d1a4b --- /dev/null +++ b/mmv1/products/firebaseappcheck/PlayIntegrityConfig.yaml @@ -0,0 +1,83 @@ +# 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. + +--- +!ruby/object:Api::Resource +name: "PlayIntegrityConfig" +base_url: projects/{{project}}/apps/{{app_id}}/playIntegrityConfig +self_link: projects/{{project}}/apps/{{app_id}}/playIntegrityConfig +create_url: projects/{{project}}/apps/{{app_id}}/playIntegrityConfig?updateMask=tokenTtl +create_verb: :PATCH +update_verb: :PATCH +update_mask: true +skip_delete: true +description: | + An app's Play Integrity configuration object. Note that your registered SHA-256 certificate fingerprints are used to validate tokens issued by the Play Integrity API. + Make sure your `google_firebase_android_app` has at least one `sha256_hashes` present. +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + "Official Documentation": "https://firebase.google.com/docs/app-check" + api: "https://firebase.google.com/docs/reference/appcheck/rest/v1/projects.apps.playIntegrityConfig" +import_format: + [ + "projects/{{project}}/apps/{{app_id}}/playIntegrityConfig", + "{{project}}/{{app_id}}", + "{{app_id}}", + ] +examples: + - !ruby/object:Provider::Terraform::Examples + name: "firebase_app_check_play_integrity_config_minimal" + # Need the time_sleep resource + pull_external: true + primary_resource_id: "default" + vars: + package_name: "package.name.playintegrity" + test_env_vars: + project_id: :PROJECT_NAME + - !ruby/object:Provider::Terraform::Examples + name: "firebase_app_check_play_integrity_config_full" + # Need the time_sleep resource + pull_external: true + primary_resource_id: "default" + vars: + package_name: "package.name.playintegrity" + token_ttl: "7200s" + test_vars_overrides: + # Don't add random suffix + token_ttl: '"7200s"' + test_env_vars: + project_id: :PROJECT_NAME +parameters: + - !ruby/object:Api::Type::String + name: app_id + description: | + The ID of an + [Android App](https://firebase.google.com/docs/reference/firebase-management/rest/v1beta1/projects.androidApps#AndroidApp.FIELDS.app_id). + required: true + immutable: true + url_param_only: true +properties: + - !ruby/object:Api::Type::String + name: name + description: | + The relative resource name of the Play Integrity configuration object + output: true + pattern: projects/{{project}}/apps/{{app_id}}/playIntegrityConfig + - !ruby/object:Api::Type::String + name: tokenTtl + description: | + Specifies the duration for which App Check tokens exchanged from Play Integrity artifacts will be valid. + If unset, a default value of 1 hour is assumed. Must be between 30 minutes and 7 days, inclusive. + + A duration in seconds with up to nine fractional digits, ending with 's'. Example: "3.5s". + default_from_api: true diff --git a/mmv1/products/firebaseappcheck/RecaptchaEnterpriseConfig.yaml b/mmv1/products/firebaseappcheck/RecaptchaEnterpriseConfig.yaml new file mode 100644 index 000000000000..a103c5dfb226 --- /dev/null +++ b/mmv1/products/firebaseappcheck/RecaptchaEnterpriseConfig.yaml @@ -0,0 +1,81 @@ +# 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. + +--- +!ruby/object:Api::Resource +name: "RecaptchaEnterpriseConfig" +base_url: projects/{{project}}/apps/{{app_id}}/recaptchaEnterpriseConfig +self_link: projects/{{project}}/apps/{{app_id}}/recaptchaEnterpriseConfig +create_url: projects/{{project}}/apps/{{app_id}}/recaptchaEnterpriseConfig?updateMask=tokenTtl,siteKey +create_verb: :PATCH +update_verb: :PATCH +update_mask: true +skip_delete: true +description: | + An app's reCAPTCHA Enterprise configuration object. +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + "Official Documentation": "https://firebase.google.com/docs/app-check" + api: "https://firebase.google.com/docs/reference/appcheck/rest/v1/projects.apps.recaptchaEnterpriseConfig" +import_format: + [ + "projects/{{project}}/apps/{{app_id}}/recaptchaEnterpriseConfig", + "{{project}}/{{app_id}}", + "{{app_id}}", + ] +examples: + - !ruby/object:Provider::Terraform::Examples + name: "firebase_app_check_recaptcha_enterprise_config_basic" + # Need the time_sleep resource + pull_external: true + primary_resource_id: "default" + vars: + token_ttl: "7200s" + # A valid key, but is actually unusable + site_key: "6LdpMXIpAAAAANkwWQPgEdjEhal7ugkH9RK9ytuw" + test_vars_overrides: + # Don't add random suffix + token_ttl: '"7200s"' + site_key: '"6LdpMXIpAAAAANkwWQPgEdjEhal7ugkH9RK9ytuw"' + test_env_vars: + project_id: :PROJECT_NAME +parameters: + - !ruby/object:Api::Type::String + name: app_id + description: | + The ID of an + [Web App](https://firebase.google.com/docs/reference/firebase-management/rest/v1beta1/projects.webApps#WebApp.FIELDS.app_id). + required: true + immutable: true + url_param_only: true +properties: + - !ruby/object:Api::Type::String + name: name + description: | + The relative resource name of the reCAPTCHA Enterprise configuration object + output: true + - !ruby/object:Api::Type::String + name: tokenTtl + description: | + Specifies the duration for which App Check tokens exchanged from reCAPTCHA Enterprise artifacts will be valid. + If unset, a default value of 1 hour is assumed. Must be between 30 minutes and 7 days, inclusive. + + A duration in seconds with up to nine fractional digits, ending with 's'. Example: "3.5s". + default_from_api: true + - !ruby/object:Api::Type::String + name: siteKey + description: | + The score-based site key created in reCAPTCHA Enterprise used to invoke reCAPTCHA and generate the reCAPTCHA tokens for your application. + + **Important**: This is not the siteSecret (as it is in reCAPTCHA v3), but rather your score-based reCAPTCHA Enterprise site key. + required: true diff --git a/mmv1/products/firebaseappcheck/RecaptchaV3Config.yaml b/mmv1/products/firebaseappcheck/RecaptchaV3Config.yaml new file mode 100644 index 000000000000..1e42c4796246 --- /dev/null +++ b/mmv1/products/firebaseappcheck/RecaptchaV3Config.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. + +--- +!ruby/object:Api::Resource +name: "RecaptchaV3Config" +base_url: projects/{{project}}/apps/{{app_id}}/recaptchaV3Config +self_link: projects/{{project}}/apps/{{app_id}}/recaptchaV3Config +create_url: projects/{{project}}/apps/{{app_id}}/recaptchaV3Config?updateMask=tokenTtl,siteSecret +create_verb: :PATCH +update_verb: :PATCH +update_mask: true +skip_delete: true +description: | + An app's reCAPTCHA V3 configuration object. +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + "Official Documentation": "https://firebase.google.com/docs/app-check" + api: "https://firebase.google.com/docs/reference/appcheck/rest/v1/projects.apps.recaptchaV3Config" +import_format: + [ + "projects/{{project}}/apps/{{app_id}}/recaptchaV3Config", + "{{project}}/{{app_id}}", + "{{app_id}}", + ] +examples: + - !ruby/object:Provider::Terraform::Examples + name: "firebase_app_check_recaptcha_v3_config_basic" + # Need the time_sleep resource + pull_external: true + primary_resource_id: "default" + vars: + token_ttl: "7200s" + # A valid secret, but is actually unusable + site_secret: "6Lf9YnQpAAAAAC3-MHmdAllTbPwTZxpUw5d34YzX" + test_vars_overrides: + # Don't add random suffix + token_ttl: '"7200s"' + site_secret: '"6Lf9YnQpAAAAAC3-MHmdAllTbPwTZxpUw5d34YzX"' + test_env_vars: + project_id: :PROJECT_NAME +parameters: + - !ruby/object:Api::Type::String + name: app_id + description: | + The ID of an + [Web App](https://firebase.google.com/docs/reference/firebase-management/rest/v1beta1/projects.webApps#WebApp.FIELDS.app_id). + required: true + immutable: true + url_param_only: true +properties: + - !ruby/object:Api::Type::String + name: name + description: | + The relative resource name of the reCAPTCHA V3 configuration object + output: true + - !ruby/object:Api::Type::String + name: tokenTtl + description: | + Specifies the duration for which App Check tokens exchanged from reCAPTCHA V3 artifacts will be valid. + If unset, a default value of 1 hour is assumed. Must be between 30 minutes and 7 days, inclusive. + + A duration in seconds with up to nine fractional digits, ending with 's'. Example: "3.5s". + default_from_api: true + - !ruby/object:Api::Type::String + name: siteSecret + description: | + The site secret used to identify your service for reCAPTCHA v3 verification. + For security reasons, this field will never be populated in any response. + required: true + sensitive: true + ignore_read: true + - !ruby/object:Api::Type::Boolean + name: siteSecretSet + description: | + Whether the siteSecret was previously set. Since we will never return the siteSecret field, this field is the only way to find out whether it was previously set. + output: true diff --git a/mmv1/products/gkebackup/BackupPlan.yaml b/mmv1/products/gkebackup/BackupPlan.yaml index f3937b278d0c..26733710637e 100644 --- a/mmv1/products/gkebackup/BackupPlan.yaml +++ b/mmv1/products/gkebackup/BackupPlan.yaml @@ -105,7 +105,6 @@ examples: deletion_protection: 'false' test_env_vars: project: :PROJECT_NAME -skip_sweeper: true parameters: - !ruby/object:Api::Type::String name: 'location' diff --git a/mmv1/products/gkebackup/RestorePlan.yaml b/mmv1/products/gkebackup/RestorePlan.yaml index 479946268fda..38bcb5216b5c 100644 --- a/mmv1/products/gkebackup/RestorePlan.yaml +++ b/mmv1/products/gkebackup/RestorePlan.yaml @@ -134,7 +134,6 @@ examples: subnetwork_name: 'acctest.BootstrapSubnet(t, "gke-cluster", acctest.BootstrapSharedTestNetwork(t, "gke-cluster"))' oics_vars_overrides: deletion_protection: 'false' -skip_sweeper: true parameters: - !ruby/object:Api::Type::String name: 'location' diff --git a/mmv1/products/gkehub2/Scope.yaml b/mmv1/products/gkehub2/Scope.yaml index 7c12b64c03b4..ff73465a443e 100644 --- a/mmv1/products/gkehub2/Scope.yaml +++ b/mmv1/products/gkehub2/Scope.yaml @@ -117,6 +117,14 @@ properties: - :READY - :DELETING - :UPDATING + - !ruby/object:Api::Type::KeyValuePairs + name: 'namespaceLabels' + description: | + Scope-level cluster namespace labels. For the member clusters bound + to the Scope, these labels are applied to each namespace under the + Scope. Scope-level labels take precedence over Namespace-level + labels (`namespace_labels` in the Fleet Namespace resource) if they + share a key. Keys and values must be Kubernetes-conformant. - !ruby/object:Api::Type::KeyValueLabels name: 'labels' description: | diff --git a/mmv1/products/healthcare/Hl7V2Store.yaml b/mmv1/products/healthcare/Hl7V2Store.yaml index 2c395e9a1338..b923d404a489 100644 --- a/mmv1/products/healthcare/Hl7V2Store.yaml +++ b/mmv1/products/healthcare/Hl7V2Store.yaml @@ -73,6 +73,12 @@ properties: ** Changing this property may recreate the Hl7v2 store (removing all data) ** required: true immutable: true + - !ruby/object:Api::Type::Boolean + name: rejectDuplicateMessage + required: false + default_value: false + description: | + Determines whether duplicate messages are allowed. - !ruby/object:Api::Type::NestedObject name: parserConfig required: false diff --git a/mmv1/products/integrationconnectors/Connection.yaml b/mmv1/products/integrationconnectors/Connection.yaml index 51baae956ad3..cde9925b86dd 100644 --- a/mmv1/products/integrationconnectors/Connection.yaml +++ b/mmv1/products/integrationconnectors/Connection.yaml @@ -927,7 +927,7 @@ properties: - !ruby/object:Api::Type::NestedObject name: "connectorVersionInfraConfig" description: | - This cofiguration provides infra configs like rate limit threshold which need to be configurable for every connector version. + This configuration provides infra configs like rate limit threshold which need to be configurable for every connector version. output: true properties: - !ruby/object:Api::Type::String diff --git a/mmv1/products/looker/Instance.yaml b/mmv1/products/looker/Instance.yaml index 38b871deb18e..be77445ddd7a 100644 --- a/mmv1/products/looker/Instance.yaml +++ b/mmv1/products/looker/Instance.yaml @@ -73,6 +73,14 @@ examples: kms_key_name: 'acctest.BootstrapKMSKeyInLocation(t, "us-central1").CryptoKey.Name' network_name: 'acctest.BootstrapSharedServiceNetworkingConnection(t, "looker-vpc-network-1", acctest.ServiceNetworkWithPrefixLength(20))' skip_docs: true + - !ruby/object:Provider::Terraform::Examples + name: 'looker_instance_custom_domain' + primary_resource_id: 'looker-instance' + vars: + instance_name: 'my-instance' + client_id: 'my-client-id' + client_secret: 'my-client-secret' + custom_domain: 'my-custom-domain' parameters: - !ruby/object:Api::Type::String name: 'region' @@ -416,3 +424,19 @@ properties: description: | Number of additional Developer Users to allocate to the Looker Instance. # UserMetadata Object - End + # CustomDomain Object + - !ruby/object:Api::Type::NestedObject + name: customDomain + description: | + Custom domain settings for a Looker instance. + properties: + - !ruby/object:Api::Type::String + name: 'domain' + description: | + Domain name + - !ruby/object:Api::Type::String + name: 'state' + description: | + Status of the custom domain. + output: true + # CustomDomain Object - End diff --git a/mmv1/products/migrationcenter/PreferenceSet.yaml b/mmv1/products/migrationcenter/PreferenceSet.yaml new file mode 100644 index 000000000000..9232294c71e5 --- /dev/null +++ b/mmv1/products/migrationcenter/PreferenceSet.yaml @@ -0,0 +1,206 @@ +# 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. + +--- !ruby/object:Api::Resource +base_url: projects/{{project}}/locations/{{location}}/preferenceSets +create_url: projects/{{project}}/locations/{{location}}/preferenceSets?preferenceSetId={{preference_set_id}} +self_link: projects/{{project}}/locations/{{location}}/preferenceSets/{{preference_set_id}} +id_format: projects/{{project}}/locations/{{location}}/preferenceSets/{{preference_set_id}} +import_format: + - projects/{{project}}/locations/{{location}}/preferenceSets/{{preference_set_id}} +name: PreferenceSet +description: Manages the PreferenceSet resource. +update_verb: :PATCH +update_mask: true +autogen_async: true +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Managing Migration Preferences': 'https://cloud.google.com/migration-center/docs/migration-preferences' + api: 'https://cloud.google.com/migration-center/docs/reference/rest/v1' +examples: + - !ruby/object:Provider::Terraform::Examples + name: 'preference_set_basic' + primary_resource_id: 'default' + vars: + set_name: 'preference-set-test' + - !ruby/object:Provider::Terraform::Examples + name: 'preference_set_full' + primary_resource_id: 'default' + vars: + set_name: 'preference-set-test' +properties: + - !ruby/object:Api::Type::String + name: name + description: 'Output only. Name of the preference set. ' + output: true + - !ruby/object:Api::Type::String + name: createTime + description: 'Output only. The timestamp when the preference set was created. ' + output: true + - !ruby/object:Api::Type::String + name: updateTime + description: 'Output only. The timestamp when the preference set was last updated. ' + output: true + - !ruby/object:Api::Type::String + name: displayName + description: 'User-friendly display name. Maximum length is 63 characters. ' + - !ruby/object:Api::Type::String + name: description + description: 'A description of the preference set. ' + - !ruby/object:Api::Type::NestedObject + name: virtualMachinePreferences + description: 'VirtualMachinePreferences enables you to create sets of assumptions, + for example, a geographical location and pricing track, for your migrated virtual + machines. The set of preferences influence recommendations for migrating virtual + machine assets. ' + properties: + - !ruby/object:Api::Type::String + name: targetProduct + description: "Target product for assets using this preference set. Specify either + target product or business goal, but not both. \n Possible values:\n COMPUTE_MIGRATION_TARGET_PRODUCT_UNSPECIFIED\nCOMPUTE_MIGRATION_TARGET_PRODUCT_COMPUTE_ENGINE\nCOMPUTE_MIGRATION_TARGET_PRODUCT_VMWARE_ENGINE\nCOMPUTE_MIGRATION_TARGET_PRODUCT_SOLE_TENANCY" + - !ruby/object:Api::Type::NestedObject + name: regionPreferences + description: 'The user preferences relating to target regions. ' + properties: + - !ruby/object:Api::Type::Array + name: preferredRegions + item_type: Api::Type::String + description: 'A list of preferred regions, ordered by the most preferred region + first. Set only valid Google Cloud region names. See https://cloud.google.com/compute/docs/regions-zones + for available regions. ' + - !ruby/object:Api::Type::String + name: commitmentPlan + description: "Commitment plan to consider when calculating costs for virtual machine + insights and recommendations. If you are unsure which value to set, a 3 year + commitment plan is often a good value to start with. \n Possible values:\n COMMITMENT_PLAN_UNSPECIFIED\nCOMMITMENT_PLAN_NONE\nCOMMITMENT_PLAN_ONE_YEAR\nCOMMITMENT_PLAN_THREE_YEARS" + - !ruby/object:Api::Type::String + name: sizingOptimizationStrategy + description: "Sizing optimization strategy specifies the preferred strategy used + when extrapolating usage data to calculate insights and recommendations for + a virtual machine. If you are unsure which value to set, a moderate sizing optimization + strategy is often a good value to start with. \n Possible values:\n SIZING_OPTIMIZATION_STRATEGY_UNSPECIFIED\nSIZING_OPTIMIZATION_STRATEGY_SAME_AS_SOURCE\nSIZING_OPTIMIZATION_STRATEGY_MODERATE\nSIZING_OPTIMIZATION_STRATEGY_AGGRESSIVE" + - !ruby/object:Api::Type::NestedObject + name: computeEnginePreferences + description: 'The user preferences relating to Compute Engine target platform. ' + properties: + - !ruby/object:Api::Type::NestedObject + name: machinePreferences + description: 'The type of machines to consider when calculating virtual machine + migration insights and recommendations. Not all machine types are available + in all zones and regions. ' + properties: + - !ruby/object:Api::Type::Array + name: allowedMachineSeries + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: code + description: 'Code to identify a Compute Engine machine series. Consult + https://cloud.google.com/compute/docs/machine-resource#machine_type_comparison + for more details on the available series. ' + description: 'Compute Engine machine series to consider for insights and recommendations. + If empty, no restriction is applied on the machine series. ' + - !ruby/object:Api::Type::String + name: licenseType + description: "License type to consider when calculating costs for virtual machine + insights and recommendations. If unspecified, costs are calculated based on + the default licensing plan. \n Possible values:\n LICENSE_TYPE_UNSPECIFIED\nLICENSE_TYPE_DEFAULT\nLICENSE_TYPE_BRING_YOUR_OWN_LICENSE" + - !ruby/object:Api::Type::NestedObject + name: vmwareEnginePreferences + description: 'The user preferences relating to Google Cloud VMware Engine target + platform. ' + properties: + - !ruby/object:Api::Type::Double + name: cpuOvercommitRatio + description: 'CPU overcommit ratio. Acceptable values are between 1.0 and 8.0, + with 0.1 increment. ' + - !ruby/object:Api::Type::Double + name: memoryOvercommitRatio + description: 'Memory overcommit ratio. Acceptable values are 1.0, 1.25, 1.5, + 1.75 and 2.0. ' + - !ruby/object:Api::Type::Double + name: storageDeduplicationCompressionRatio + description: 'The Deduplication and Compression ratio is based on the logical + (Used Before) space required to store data before applying deduplication and + compression, in relation to the physical (Used After) space required after + applying deduplication and compression. Specifically, the ratio is the Used + Before space divided by the Used After space. For example, if the Used Before + space is 3 GB, but the physical Used After space is 1 GB, the deduplication + and compression ratio is 3x. Acceptable values are between 1.0 and 4.0. ' + - !ruby/object:Api::Type::String + name: commitmentPlan + description: "Commitment plan to consider when calculating costs for virtual + machine insights and recommendations. If you are unsure which value to set, + a 3 year commitment plan is often a good value to start with. \n Possible + values:\n COMMITMENT_PLAN_UNSPECIFIED\nON_DEMAND\nCOMMITMENT_1_YEAR_MONTHLY_PAYMENTS\nCOMMITMENT_3_YEAR_MONTHLY_PAYMENTS\nCOMMITMENT_1_YEAR_UPFRONT_PAYMENT\nCOMMITMENT_3_YEAR_UPFRONT_PAYMENT" + - !ruby/object:Api::Type::NestedObject + name: soleTenancyPreferences + description: 'Preferences concerning Sole Tenancy nodes and VMs. ' + properties: + - !ruby/object:Api::Type::Double + name: cpuOvercommitRatio + description: 'CPU overcommit ratio. Acceptable values are between 1.0 and 2.0 + inclusive. ' + - !ruby/object:Api::Type::String + name: hostMaintenancePolicy + description: "Sole Tenancy nodes maintenance policy. \n Possible values:\n HOST_MAINTENANCE_POLICY_UNSPECIFIED\nHOST_MAINTENANCE_POLICY_DEFAULT\nHOST_MAINTENANCE_POLICY_RESTART_IN_PLACE\nHOST_MAINTENANCE_POLICY_MIGRATE_WITHIN_NODE_GROUP" + - !ruby/object:Api::Type::String + name: commitmentPlan + description: "Commitment plan to consider when calculating costs for virtual + machine insights and recommendations. If you are unsure which value to set, + a 3 year commitment plan is often a good value to start with. \n Possible + values:\n COMMITMENT_PLAN_UNSPECIFIED\nON_DEMAND\nCOMMITMENT_1_YEAR\nCOMMITMENT_3_YEAR" + - !ruby/object:Api::Type::Array + name: nodeTypes + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: nodeName + description: 'Name of the Sole Tenant node. Consult https://cloud.google.com/compute/docs/nodes/sole-tenant-nodes ' + description: 'A list of sole tenant node types. An empty list means that all + possible node types will be considered. ' +parameters: + - !ruby/object:Api::Type::String + name: location + description: 'Part of `parent`. See documentation of `projectsId`. ' + url_param_only: true + required: true + immutable: true + - !ruby/object:Api::Type::String + name: preferenceSetId + description: 'Required. User specified ID for the preference set. It will become + the last component of the preference set name. The ID must be unique within the + project, must conform with RFC-1034, is restricted to lower-cased letters, and + has a maximum length of 63 characters. The ID must match the regular expression + `[a-z]([a-z0-9-]{0,61}[a-z0-9])?`. ' + url_param_only: true + required: true + immutable: true +async: !ruby/object:Api::OpAsync + operation: !ruby/object:Api::OpAsync::Operation + path: name + base_url: "{{op_id}}" + wait_ms: 1000 + timeouts: + result: !ruby/object:Api::OpAsync::Result + path: response + resource_inside_response: true + status: !ruby/object:Api::OpAsync::Status + path: done + complete: true + allowed: + - true + - false + error: !ruby/object:Api::OpAsync::Error + path: error + message: message diff --git a/mmv1/products/monitoring/GenericService.yaml b/mmv1/products/monitoring/GenericService.yaml index be972bbca23d..142af9e431dd 100644 --- a/mmv1/products/monitoring/GenericService.yaml +++ b/mmv1/products/monitoring/GenericService.yaml @@ -20,13 +20,14 @@ update_verb: :PATCH update_mask: true description: | A Service is a discrete, autonomous, and network-accessible unit, - designed to solve an individual concern (Wikipedia). In Cloud Monitoring, + designed to solve an individual concern. In Cloud Monitoring, a Service acts as the root resource under which operational aspects of the service are accessible references: !ruby/object:Api::Resource::ReferenceLinks guides: 'Service Monitoring': 'https://cloud.google.com/monitoring/service-monitoring' 'Monitoring API Documentation': 'https://cloud.google.com/monitoring/api/v3/' + 'Service-orientation on Wikipedia': 'https://en.wikipedia.org/wiki/Service-orientation' api: 'https://cloud.google.com/monitoring/api/ref_v3/rest/v3/services' legacy_name: 'google_monitoring_service' id_format: 'projects/{{project}}/services/{{service_id}}' diff --git a/mmv1/products/monitoring/Service.yaml b/mmv1/products/monitoring/Service.yaml index 4624cafcd416..a204ba56ef68 100644 --- a/mmv1/products/monitoring/Service.yaml +++ b/mmv1/products/monitoring/Service.yaml @@ -20,13 +20,14 @@ update_verb: :PATCH update_mask: true description: | A Service is a discrete, autonomous, and network-accessible unit, - designed to solve an individual concern (Wikipedia). In Cloud Monitoring, + designed to solve an individual concern. In Cloud Monitoring, a Service acts as the root resource under which operational aspects of the service are accessible references: !ruby/object:Api::Resource::ReferenceLinks guides: 'Service Monitoring': 'https://cloud.google.com/monitoring/service-monitoring' 'Monitoring API Documentation': 'https://cloud.google.com/monitoring/api/v3/' + 'Service-orientation on Wikipedia': 'https://en.wikipedia.org/wiki/Service-orientation' api: 'https://cloud.google.com/monitoring/api/ref_v3/rest/v3/services' legacy_name: 'google_monitoring_custom_service' id_format: '{{name}}' diff --git a/mmv1/products/netapp/VolumeReplication.yaml b/mmv1/products/netapp/VolumeReplication.yaml new file mode 100644 index 000000000000..dd7dde9c7f32 --- /dev/null +++ b/mmv1/products/netapp/VolumeReplication.yaml @@ -0,0 +1,289 @@ +# 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. + +--- +!ruby/object:Api::Resource +name: "VolumeReplication" +description: | + Volume replication creates an asynchronous mirror of a volume in a different location. This capability + lets you use the replicated volume for critical application activity in case of a location-wide outage + or disaster. + + A new destination volume is created as part of the replication resource. It's content is updated on a + schedule with content of the source volume. It can be used as a read-only copy while the mirror is + enabled, or as an independent read-write volume while the mirror is stopped. A destination volume will + also contain the snapshots of the source volume. Resuming a mirror will overwrite all changes on the + destination volume with the content of the source volume. While is mirror is enabled, all configuration + changes done to source or destination volumes are automatically done to both. Please note that the + destination volume is not a resource managed by Terraform. + + Reversing the replication direction is not supported through the provider. +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + "Documentation": "https://cloud.google.com/netapp/volumes/docs/protect-data/about-volume-replication" + api: "https://cloud.google.com/netapp/volumes/docs/reference/rest/v1/projects.locations.volumes.replications" +base_url: projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications +self_link: projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}} +create_url: projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications?replicationId={{name}} +update_url: projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}} +update_verb: :PATCH +update_mask: true +autogen_async: true +async: !ruby/object:Api::OpAsync + operation: !ruby/object:Api::OpAsync::Operation + base_url: "{{op_id}}" +id_format: "projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}" +import_format: + [ + "projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}", + ] +examples: + - !ruby/object:Provider::Terraform::Examples + name: "netapp_volume_replication_create" + primary_resource_id: "test_replication" + vars: + source_pool_name: "source-pool" + destination_pool_name: "destination-pool" + volume_name: "source-volume" + replication_name: "test-replication" + destination_volume: "destination-volume" + network_name: "test-network" + ignore_read_extra: + - "delete_destination_volume" + - "replication_enabled" + - "force_stopping" + - "wait_for_mirror" + test_vars_overrides: + network_name: 'acctest.BootstrapSharedServiceNetworkingConnection(t, "gcnv-network-config-1", acctest.ServiceNetworkWithParentService("netapp.servicenetworking.goog"))' +parameters: + - !ruby/object:Api::Type::String + name: "location" + required: true + immutable: true + url_param_only: true + description: | + Name of region for this resource. The resource needs to be created in the region of the destination volume. + - !ruby/object:Api::Type::String + name: "volumeName" + description: The name of the existing source volume. + required: true + immutable: true + url_param_only: true + - !ruby/object:Api::Type::String + name: "name" + description: The name of the replication. Needs to be unique per location. + required: true + immutable: true + url_param_only: true +properties: + - !ruby/object:Api::Type::Enum + name: "state" + description: | + Indicates the state of replication resource. State of the mirror itself is indicated in mirrorState. + values: + - :STATE_UNSPECIFIED + - :CREATING + - :READY + - :UPDATING + - :DELETING + - :ERROR + output: true + - !ruby/object:Api::Type::String + name: "stateDetails" + description: | + State details of the replication resource. + output: true + - !ruby/object:Api::Type::Enum + name: "role" + 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. + values: + - :REPLICATION_ROLE_UNSPECIFIED + - :SOURCE + - :DESTINATION + output: true + - !ruby/object:Api::Type::Enum + name: "replicationSchedule" + description: | + Specifies the replication interval. + values: + - :EVERY_10_MINUTES + - :HOURLY + - :DAILY + required: true + - !ruby/object:Api::Type::Enum + name: "mirrorState" + 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 + transfer ended and destination volume became accessible read-only. TRANSFERRING means a MIRRORED volume + currently receives an update. Updated every 5 minutes. + values: + - :MIRROR_STATE_UNSPECIFIED + - :PREPARING + - :MIRRORED + - :STOPPED + - :TRANSFERRING + custom_flatten: templates/terraform/custom_flatten/netapp_volume_replicaton_mirror_state.go.erb + output: true + - !ruby/object:Api::Type::String + name: "createTime" + description: | + Create time of the active directory. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z". + output: true + - !ruby/object:Api::Type::String + name: "destinationVolume" + description: | + Full resource name of destination volume with format: `projects/{{project}}/locations/{{location}}/volumes/{{volumeId}}` + output: true + - !ruby/object:Api::Type::NestedObject + name: "transferStats" + description: |- + Replication transfer statistics. All statistics are updated every 5 minutes. + output: true + properties: + - !ruby/object:Api::Type::String + name: "transferBytes" + description: | + Number of bytes transferred so far in current transfer. + output: true + - !ruby/object:Api::Type::String + name: "totalTransferDuration" + description: | + Total time taken so far during current transfer. + output: true + - !ruby/object:Api::Type::String + name: "lastTransferBytes" + description: | + Size of last completed transfer in bytes. + output: true + - !ruby/object:Api::Type::String + name: "lastTransferDuration" + description: | + Time taken during last completed transfer. + output: true + - !ruby/object:Api::Type::String + name: "lagDuration" + description: | + The elapsed time since the creation of the snapshot on the source volume that was last replicated + to the destination volume. Lag time represents the difference in age of the destination volume + data in relation to the source volume data. + output: true + - !ruby/object:Api::Type::String + name: "updateTime" + description: | + Time when progress was updated last. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z". + output: true + - !ruby/object:Api::Type::String + name: "lastTransferEndTime" + description: | + Time when last transfer completed. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z". + output: true + - !ruby/object:Api::Type::String + name: "lastTransferError" + description: | + A message describing the cause of the last transfer failure. + output: true + - !ruby/object:Api::Type::KeyValueLabels + name: "labels" + description: | + Labels as key value pairs. Example: `{ "owner": "Bob", "department": "finance", "purpose": "testing" }` + - !ruby/object:Api::Type::NestedObject + name: "destinationVolumeParameters" + description: |- + Destination volume parameters. + # destinationVolumeParameters is only used on CREATE. Will not be returned on READ. + ignore_read: true + properties: + - !ruby/object:Api::Type::String + name: "storagePool" + description: | + Name of an existing storage pool for the destination volume with format: `projects/{{project}}/locations/{{location}}/storagePools/{{poolId}}` + immutable: true + required: true + - !ruby/object:Api::Type::String + name: "volumeId" + description: | + Name for the destination volume to be created. If not specified, the name of the source volume will be used. + immutable: true + default_from_api: true + - !ruby/object:Api::Type::String + name: "shareName" + description: | + Share name for destination volume. If not specified, name of source volume's share name will be used. + immutable: true + default_from_api: true + - !ruby/object:Api::Type::String + name: "description" + description: | + Description for the destination volume. + immutable: true + - !ruby/object:Api::Type::String + name: "sourceVolume" + description: | + Full resource name of source volume with format: `projects/{{project}}/locations/{{location}}/volumes/{{volumeId}}` + output: true + - !ruby/object:Api::Type::Boolean + name: "healthy" + description: | + Condition of the relationship. Can be one of the following: + - true: The replication relationship is healthy. It has not missed the most recent scheduled transfer. + - false: The replication relationship is not healthy. It has missed the most recent scheduled transfer. + output: true + - !ruby/object:Api::Type::String + name: "description" + description: | + An description of this resource. +virtual_fields: + - !ruby/object:Api::Type::Boolean + name: "delete_destination_volume" + description: | + A destination volume is created as part of replication creation. The destination volume will not became + under Terraform management unless you import it manually. If you delete the replication, this volume + will remain. + Setting this parameter to true will delete the *current* destination volume when destroying the + replication. If you reversed the replication direction, this will be your former source volume! + For production use, it is recommended to keep this parameter false to avoid accidental volume + deletion. Handle with care. Default is false. + default_value: false + - !ruby/object:Api::Type::Boolean + name: "replication_enabled" + description: | + Set to false to stop/break the mirror. Stopping the mirror makes the destination volume read-write + and act independently from the source volume. + Set to true to enable/resume the mirror. WARNING: Resuming a mirror overwrites any changes + done to the destination volume with the content of the source volume. + default_value: true + - !ruby/object:Api::Type::Boolean + name: "force_stopping" + description: | + Only replications with mirror_state=MIRRORED can be stopped. A replication in mirror_state=TRANSFERRING + currently receives an update and stopping the update might be undesirable. Set this parameter to true + to stop anyway. All data transferred to the destination will be discarded and content of destination + volume will remain at the state of the last successful update. Default is false. + default_value: false + - !ruby/object:Api::Type::Boolean + name: "wait_for_mirror" + description: | + Replication resource state is independent of mirror_state. With enough data, it can take many hours + for mirror_state to reach MIRRORED. If you want Terraform to wait for the mirror to finish on + create/stop/resume operations, set this parameter to true. Default is false. + default_value: false +custom_code: !ruby/object:Provider::Terraform::CustomCode + constants: templates/terraform/constants/netapp_volume_replication.go.erb + post_create: templates/terraform/post_create/netapp_volume_replication_post_create.go.erb + pre_delete: templates/terraform/pre_delete/netapp_volume_replication_stop_before_delete.go.erb + post_delete: templates/terraform/post_delete/netapp_volume_replication_delete_destination_volume.go.erb + post_update: templates/terraform/post_update/netapp_volume_replication_mirror_state.go.erb diff --git a/mmv1/products/netapp/volume.yaml b/mmv1/products/netapp/volume.yaml index 4f617b3ab27c..698f8ab4906b 100644 --- a/mmv1/products/netapp/volume.yaml +++ b/mmv1/products/netapp/volume.yaml @@ -53,7 +53,7 @@ parameters: url_param_only: true examples: - !ruby/object:Provider::Terraform::Examples - name: 'volume_basic' + name: 'netapp_volume_basic' primary_resource_id: 'test_volume' vars: volume_name: 'test-volume' @@ -64,6 +64,30 @@ examples: test_vars_overrides: network_name: 'acctest.BootstrapSharedServiceNetworkingConnection(t, "gcnv-network-config-1", acctest.ServiceNetworkWithParentService("netapp.servicenetworking.goog"))' properties: + - !ruby/object:Api::Type::Enum + name: 'state' + description: | + State of the volume. + values: + - STATE_UNSPECIFIED + - READY + - CREATING + - DELETING + - UPDATING + - RESTORING + - DISABLED + - ERROR + output: true + - !ruby/object:Api::Type::String + name: 'stateDetails' + description: | + State details of the volume. + output: true + - !ruby/object:Api::Type::String + name: 'createTime' + description: | + Create time of the volume. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z". + output: true - !ruby/object:Api::Type::String name: 'shareName' description: | @@ -205,11 +229,11 @@ properties: name: 'description' description: | An optional description of this resource. - # Use of snapReserve is depricated. We don't expose it intentially. + # Use of snapReserve is deprecated. Here as a comment to express intention. # - !ruby/object:Api::Type::Integer # name: 'snapReserve' # description: | - # Snap_reserve specifies percentage of volume storage reserved for snapshot storage. Default is 0 percent. Use is deprecated. + # `snap_reserve` specifies percentage of volume storage reserved for snapshot storage. Default is 0 percent. Use is deprecated. - !ruby/object:Api::Type::Boolean name: 'snapshotDirectory' description: | @@ -245,6 +269,34 @@ properties: description: | Reports the resource name of the Active Directory policy being used. Inherited from storage pool. output: true + - !ruby/object:Api::Type::NestedObject + name: 'restoreParameters' + description: |- + Used to create this volume from a snapshot (= cloning) or an backup. + immutable: true + # This parameter is only used at CREATE. READs will omit it. + ignore_read: true + properties: + - !ruby/object:Api::Type::String + name: 'sourceSnapshot' + description: |- + Full name of the snapshot to use for creating this volume. + `source_snapshot` and `source_backup` cannot be used simultaneously. + Format: `projects/{{project}}/locations/{{location}}/volumes/{{volume}}/snapshots/{{snapshot}}`. + exactly_one_of: + - restore_parameters.0.source_backup + - restore_parameters.0.source_snapshot + immutable: true + - !ruby/object:Api::Type::String + name: 'sourceBackup' + description: |- + Full name of the snapshot to use for creating this volume. + `source_snapshot` and `source_backup` cannot be used simultaneously. + Format: `projects/{{project}}/locations/{{location}}/backupVaults/{{backupVaultId}}/backups/{{backup}}`. + exactly_one_of: + - restore_parameters.0.source_backup + - restore_parameters.0.source_snapshot + immutable: true - !ruby/object:Api::Type::String name: 'kmsConfig' description: | @@ -405,17 +457,6 @@ properties: description: |- Set the day or days of the month to make a snapshot (1-31). Accepts a comma separated number of days. Defaults to '1'. default_value: '1' -# This is disabled until we have support for backup resource and can test it. -# - !ruby/object:Api::Type::NestedObject -# name: restoreParameters -# description: Specifies the source information to create a volume from. -# immutable: true -# properties: -# - !ruby/object:Api::Type::String -# name: 'sourceSnapshot' -# description: |- -# Full name of the snapshot resource. Format: `projects/{{project}}/locations/{{location}}/volumes/{{volume}}/snapshots/{{snapshot}}`. -# required: true virtual_fields: - !ruby/object:Api::Type::Enum name: 'deletion_policy' diff --git a/mmv1/products/notebooks/Instance.yaml b/mmv1/products/notebooks/Instance.yaml index b770e0489040..b5fb92b0c1b1 100644 --- a/mmv1/products/notebooks/Instance.yaml +++ b/mmv1/products/notebooks/Instance.yaml @@ -55,6 +55,17 @@ examples: region_override: 'us-west1-a' vars: instance_name: 'notebooks-instance' + - !ruby/object:Provider::Terraform::Examples + name: 'notebook_instance_basic_stopped' + primary_resource_id: 'instance' + primary_resource_name: "fmt.Sprintf(\"tf-test-notebooks-instance%s\", + context[\"\ + random_suffix\"])" + region_override: 'us-west1-a' + vars: + instance_name: 'notebooks-instance' + ignore_read_extra: + - 'desired_state' - !ruby/object:Provider::Terraform::Examples name: 'notebook_instance_basic_container' primary_resource_id: 'instance' @@ -87,9 +98,20 @@ examples: key_name: 'acctest.BootstrapKMSKeyInLocation(t, "global").CryptoKey.Name' test_env_vars: service_account: :SERVICE_ACCT +virtual_fields: + - !ruby/object:Api::Type::Enum + name: desired_state + description: | + Desired state of the Notebook Instance. Set this field to `ACTIVE` to start the Instance, and `STOPPED` to stop the Instance. + values: + - :ACTIVE + - :STOPPED + default_value: :ACTIVE custom_code: !ruby/object:Provider::Terraform::CustomCode constants: templates/terraform/constants/notebooks_instance.go update_encoder: templates/terraform/update_encoder/notebooks_instance.go + post_create: templates/terraform/post_create/notebooks_instance.go.erb + post_update: templates/terraform/post_update/notebooks_instance.go.erb state_upgraders: true schema_version: 1 parameters: @@ -370,6 +392,7 @@ properties: description: | The Compute Engine tags to add to instance. item_type: Api::Type::String + default_from_api: true - !ruby/object:Api::Type::KeyValuePairs name: 'metadata' description: | diff --git a/mmv1/products/secretmanager/SecretVersion.yaml b/mmv1/products/secretmanager/SecretVersion.yaml index 0161b2e6d0d3..9db3d9e31bcb 100644 --- a/mmv1/products/secretmanager/SecretVersion.yaml +++ b/mmv1/products/secretmanager/SecretVersion.yaml @@ -20,6 +20,7 @@ delete_url: '{{name}}:destroy' delete_verb: :POST description: | A secret version resource. +# Sweeper skipped as this resource has customized deletion. skip_sweeper: true examples: - !ruby/object:Provider::Terraform::Examples diff --git a/mmv1/products/securityposture/posture.yaml b/mmv1/products/securityposture/posture.yaml index 993ada901fd7..cc1882c4d055 100644 --- a/mmv1/products/securityposture/posture.yaml +++ b/mmv1/products/securityposture/posture.yaml @@ -120,6 +120,7 @@ properties: output: true - !ruby/object:Api::Type::Array name: 'policySets' + required: true description: | List of policy sets for the posture. item_type: !ruby/object:Api::Type::NestedObject @@ -141,6 +142,7 @@ properties: name: 'policies' description: | List of security policy + required: true item_type: !ruby/object:Api::Type::NestedObject name: 'Policy' description: | @@ -231,7 +233,7 @@ properties: If `true`, then the policy is enforced. If `false`, then any configuration is acceptable. This field can be set only in policies for boolean constraints. - !ruby/object:Api::Type::NestedObject - name: 'expr' + name: 'condition' description: | Represents a textual expression in the Common Expression Language (CEL) syntax. CEL is a C-like expression language. This page details the objects and attributes that are used to the build the CEL expressions for @@ -348,7 +350,7 @@ properties: If `true`, then the policy is enforced. If `false`, then any configuration is acceptable. This field can be set only in policies for boolean constraints. - !ruby/object:Api::Type::NestedObject - name: 'expr' + name: 'condition' description: | Represents a textual expression in the Common Expression Language (CEL) syntax. CEL is a C-like expression language. This page details the objects and attributes that are used to the build the CEL expressions for diff --git a/mmv1/products/spanner/Database.yaml b/mmv1/products/spanner/Database.yaml index 9feb815c7cb9..ea33c9ad211c 100644 --- a/mmv1/products/spanner/Database.yaml +++ b/mmv1/products/spanner/Database.yaml @@ -41,6 +41,7 @@ async: !ruby/object:Api::OpAsync path: 'error' message: 'message' autogen_async: true +# Sweeper skipped as this resource has customized deletion. skip_sweeper: true id_format: '{{instance}}/{{name}}' import_format: diff --git a/mmv1/products/sql/Database.yaml b/mmv1/products/sql/Database.yaml index 1c6c56a9ed14..eca5f0a5c533 100644 --- a/mmv1/products/sql/Database.yaml +++ b/mmv1/products/sql/Database.yaml @@ -30,6 +30,7 @@ import_format: '{{instance}}/{{name}}', '{{name}}', ] +# Sweeper skipped as this resource has customized deletion. skip_sweeper: true examples: - !ruby/object:Provider::Terraform::Examples diff --git a/mmv1/products/vertexai/FeatureOnlineStoreFeatureview.yaml b/mmv1/products/vertexai/FeatureOnlineStoreFeatureview.yaml index de319a0b83f4..ef350a823964 100644 --- a/mmv1/products/vertexai/FeatureOnlineStoreFeatureview.yaml +++ b/mmv1/products/vertexai/FeatureOnlineStoreFeatureview.yaml @@ -55,6 +55,11 @@ examples: primary_resource_id: 'featureview' vars: name: 'example_feature_view' + - !ruby/object:Provider::Terraform::Examples + name: 'vertex_ai_featureonlinestore_featureview_feature_registry' + primary_resource_id: 'featureview_featureregistry' + vars: + name: 'example_feature_view_feature_registry' - !ruby/object:Provider::Terraform::Examples name: 'vertex_ai_featureonlinestore_featureview_with_vector_search' primary_resource_id: 'featureview_vector_search' @@ -112,6 +117,9 @@ properties: name: 'bigQuerySource' description: | Configures how data is supposed to be extracted from a BigQuery source to be loaded onto the FeatureOnlineStore. + exactly_one_of: + - big_query_source + - feature_registry_source properties: - !ruby/object:Api::Type::String name: 'uri' @@ -124,10 +132,40 @@ properties: description: | Columns to construct entityId / row keys. Start by supporting 1 only. item_type: Api::Type::String + - !ruby/object:Api::Type::NestedObject + name: 'featureRegistrySource' + conflicts: + - vector_search_config + exactly_one_of: + - big_query_source + - feature_registry_source + description: | + Configures the features from a Feature Registry source that need to be loaded onto the FeatureOnlineStore. + properties: + - !ruby/object:Api::Type::Array + name: 'featureGroups' + required: true + description: | + List of features that need to be synced to Online Store. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'featureGroupId' + required: true + description: | + Identifier of the feature group. + - !ruby/object:Api::Type::Array + name: featureIds + required: true + description: | + Identifiers of features under the feature group. + item_type: Api::Type::String - !ruby/object:Api::Type::NestedObject name: 'vectorSearchConfig' description: | Configuration for vector search. It contains the required configurations to create an index from source data, so that approximate nearest neighbor (a.k.a ANN) algorithms search can be performed during online serving. + conflicts: + - feature_registry_source immutable: true min_version: beta properties: diff --git a/mmv1/products/workbench/Instance.yaml b/mmv1/products/workbench/Instance.yaml index b0b2b08e8d43..7b3e0d2b47c0 100644 --- a/mmv1/products/workbench/Instance.yaml +++ b/mmv1/products/workbench/Instance.yaml @@ -43,6 +43,15 @@ examples: region_override: 'us-west1-a' vars: instance_name: 'workbench-instance' + - !ruby/object:Provider::Terraform::Examples + name: 'workbench_instance_basic_container' + primary_resource_id: 'instance' + primary_resource_name: "fmt.Sprintf(\"tf-test-workbench-instance%s\", + context[\"\ + random_suffix\"])" + region_override: 'us-west1-a' + vars: + instance_name: 'workbench-instance' - !ruby/object:Provider::Terraform::Examples name: 'workbench_instance_basic_gpu' primary_resource_id: 'instance' @@ -55,7 +64,7 @@ examples: ignore_read_extra: - 'gce_setup.0.vm_image' - !ruby/object:Provider::Terraform::Examples - name: 'workbench_instance_labels' + name: 'workbench_instance_labels_stopped' primary_resource_id: 'instance' primary_resource_name: "fmt.Sprintf(\"tf-test-workbench-instance%s\", context[\"\ @@ -66,6 +75,8 @@ examples: network_name: 'wbi-test-default' test_env_vars: service_account: :SERVICE_ACCT + ignore_read_extra: + - 'desired_state' - !ruby/object:Provider::Terraform::Examples name: 'workbench_instance_full' primary_resource_id: 'instance' @@ -83,15 +94,20 @@ examples: service_account: :SERVICE_ACCT ignore_read_extra: - 'gce_setup.0.vm_image' - - 'gce_setup.0.boot_disk.0.disk_encryption' - 'gce_setup.0.boot_disk.0.disk_type' - - 'gce_setup.0.boot_disk.0.kms_key' - - 'gce_setup.0.data_disks.0.disk_encryption' - 'gce_setup.0.data_disks.0.disk_type' - - 'gce_setup.0.data_disks.0.kms_key' timeouts: !ruby/object:Api::Timeouts insert_minutes: 10 update_minutes: 20 +virtual_fields: + - !ruby/object:Api::Type::Enum + name: desired_state + description: | + Desired state of the Workbench Instance. Set this field to `ACTIVE` to start the Instance, and `STOPPED` to stop the Instance. + values: + - :ACTIVE + - :STOPPED + default_value: :ACTIVE custom_code: !ruby/object:Provider::Terraform::CustomCode constants: templates/terraform/constants/workbench_instance.go post_create: templates/terraform/post_create/workbench_instance.go.erb @@ -149,6 +165,36 @@ properties: use accelerators, make sure that your configuration has [enough vCPUs and memory to support the `machine_type` you have selected](https://cloud.google.com/compute/docs/gpus/#gpus-list). Currently supports only one accelerator configuration. + - !ruby/object:Api::Type::NestedObject + name: shieldedInstanceConfig + default_from_api: true + allow_empty_object: true + send_empty_value: true + description: | + A set of Shielded Instance options. See [Images using supported Shielded + VM features](https://cloud.google.com/compute/docs/instances/modifying-shielded-vm). + Not all combinations are valid. + properties: + - !ruby/object:Api::Type::Boolean + name: enableSecureBoot + description: | + Optional. Defines whether the VM instance has Secure Boot enabled. + Secure Boot helps ensure that the system only runs authentic software by verifying + the digital signature of all boot components, and halting the boot process + if signature verification fails. Disabled by default. + - !ruby/object:Api::Type::Boolean + name: enableVtpm + description: | + Optional. Defines whether the VM instance has the vTPM enabled. + Enabled by default. + - !ruby/object:Api::Type::Boolean + name: enableIntegrityMonitoring + description: | + Optional. Defines whether the VM instance has integrity monitoring + enabled. Enables monitoring and attestation of the boot integrity of the VM + instance. The attestation is performed against the integrity policy baseline. + This baseline is initially derived from the implicitly trusted boot image + when the VM instance is created. Enabled by default. - !ruby/object:Api::Type::Array name: serviceAccounts description: | @@ -176,6 +222,8 @@ properties: name: vmImage custom_flatten: templates/terraform/custom_flatten/workbench_instance_vm_image_flatten.go.erb default_from_api: true + conflicts: + - gce_setup.0.container_image immutable: true description: | Definition of a custom Compute Engine virtual machine image for starting @@ -203,6 +251,23 @@ properties: exactly_one_of: - vm_image.0.name - vm_image.0.family + - !ruby/object:Api::Type::NestedObject + name: containerImage + conflicts: + - gce_setup.0.vm_image + description: | + Use a container image to start the workbench instance. + properties: + - !ruby/object:Api::Type::String + name: 'repository' + description: | + The path to the container image repository. + For example: gcr.io/{project_id}/{imageName} + required: true + - !ruby/object:Api::Type::String + name: 'tag' + description: | + The tag of the container image. If not specified, this defaults to the latest tag. - !ruby/object:Api::Type::NestedObject name: bootDisk default_from_api: true @@ -231,7 +296,6 @@ properties: - !ruby/object:Api::Type::Enum name: diskEncryption default_from_api: true - custom_flatten: templates/terraform/custom_flatten/workbench_instance_boot_disk_encryption_flatten.go.erb values: - GMEK - CMEK @@ -242,11 +306,11 @@ properties: - !ruby/object:Api::Type::String name: kmsKey description: | - 'Optional. Input only. The KMS key used to encrypt the disks, only + 'Optional. The KMS key used to encrypt the disks, only applicable if disk_encryption is CMEK. Format: `projects/{project_id}/locations/{location}/keyRings/{key_ring_id}/cryptoKeys/{key_id}` Learn more about using your own encryption keys.' immutable: true - custom_flatten: templates/terraform/custom_flatten/workbench_instance_boot_disk_kms_flatten.go.erb + diff_suppress_func: WorkbenchInstanceKmsDiffSuppress - !ruby/object:Api::Type::Array name: dataDisks description: Data disks attached to the VM instance. Currently supports only one data disk. @@ -276,7 +340,6 @@ properties: - !ruby/object:Api::Type::Enum name: diskEncryption default_from_api: true - custom_flatten: templates/terraform/custom_flatten/workbench_instance_data_disk_encryption_flatten.go.erb values: - GMEK - CMEK @@ -287,11 +350,11 @@ properties: - !ruby/object:Api::Type::String name: kmsKey description: | - 'Optional. Input only. The KMS key used to encrypt the disks, + 'Optional. The KMS key used to encrypt the disks, only applicable if disk_encryption is CMEK. Format: `projects/{project_id}/locations/{location}/keyRings/{key_ring_id}/cryptoKeys/{key_id}` Learn more about using your own encryption keys.' immutable: true - custom_flatten: templates/terraform/custom_flatten/workbench_instance_data_disk_kms_flatten.go.erb + diff_suppress_func: WorkbenchInstanceKmsDiffSuppress description: | Optional. Data disks attached to the VM instance. Currently supports only one data disk. diff --git a/mmv1/templates/terraform/constants/netapp_volume_replication.go.erb b/mmv1/templates/terraform/constants/netapp_volume_replication.go.erb new file mode 100644 index 000000000000..10ec7b806253 --- /dev/null +++ b/mmv1/templates/terraform/constants/netapp_volume_replication.go.erb @@ -0,0 +1,51 @@ +// Custom function to wait for mirrorState target states +func NetAppVolumeReplicationWaitForMirror(d *schema.ResourceData, meta interface{}, targetState string) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for volume replication: %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 + } + + for { + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("NetappVolumeReplication %q", d.Id())) + } + + log.Printf("[DEBUG] waiting for mirrorState. actual: %v, target: %v", res["mirrorState"], targetState) + + if res["mirrorState"] == targetState { + break + } + + time.Sleep(30 * time.Second) + // This method can potentially run for days, e.g. when setting up a replication for a source volume + // with dozens of TiB of data. Timeout handling yes/no? + } + + return nil +} \ No newline at end of file diff --git a/mmv1/templates/terraform/constants/notebooks_instance.go b/mmv1/templates/terraform/constants/notebooks_instance.go index 5d376547fe83..230d2aece282 100644 --- a/mmv1/templates/terraform/constants/notebooks_instance.go +++ b/mmv1/templates/terraform/constants/notebooks_instance.go @@ -33,3 +33,56 @@ func NotebooksInstanceKmsDiffSuppress(_, old, new string, _ *schema.ResourceData } return false } + +<% unless compiler == "terraformgoogleconversion-codegen" -%> +// waitForNotebooksInstanceActive waits for an Notebook instance to become "ACTIVE" +func waitForNotebooksInstanceActive(d *schema.ResourceData, config *transport_tpg.Config, timeout time.Duration) error { + return resource.Retry(timeout, func() *resource.RetryError { + if err := resourceNotebooksInstanceRead(d, config); err != nil { + return resource.NonRetryableError(err) + } + + name := d.Get("name").(string) + state := d.Get("state").(string) + if state == "ACTIVE" { + log.Printf("[DEBUG] Notebook Instance %q has state %q.", name, state) + return nil + } else { + return resource.RetryableError(fmt.Errorf("Notebook Instance %q has state %q. Waiting for ACTIVE state", name, state)) + } + + }) +} +<% end -%> + +func modifyNotebooksInstanceState(config *transport_tpg.Config, d *schema.ResourceData, project string, billingProject string, userAgent string, state string) (map[string]interface{}, error) { + url, err := tpgresource.ReplaceVars(d, config, "{{NotebooksBasePath}}projects/{{project}}/locations/{{location}}/instances/{{name}}:"+state) + if err != nil { + return nil, err + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return nil, fmt.Errorf("Unable to %q google_notebooks_instance %q: %s", state, d.Id(), err) + } + return res, nil +} + +<% unless compiler == "terraformgoogleconversion-codegen" -%> +func waitForNotebooksOperation(config *transport_tpg.Config, d *schema.ResourceData, project string, billingProject string, userAgent string, response map[string]interface{}) error { + var opRes map[string]interface{} + err := NotebooksOperationWaitTimeWithResponse( + config, response, &opRes, project, "Modifying Notebook Instance state", userAgent, + d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return err + } + return nil +} +<% end -%> diff --git a/mmv1/templates/terraform/constants/workbench_instance.go b/mmv1/templates/terraform/constants/workbench_instance.go index 6ae6d20dca67..4462c0e5baa7 100644 --- a/mmv1/templates/terraform/constants/workbench_instance.go +++ b/mmv1/templates/terraform/constants/workbench_instance.go @@ -140,3 +140,42 @@ func waitForWorkbenchInstanceActive(d *schema.ResourceData, config *transport_tp }) } <% end -%> + +func modifyWorkbenchInstanceState(config *transport_tpg.Config, d *schema.ResourceData, project string, billingProject string, userAgent string, state string) (map[string]interface{}, error) { + url, err := tpgresource.ReplaceVars(d, config, "{{WorkbenchBasePath}}projects/{{project}}/locations/{{location}}/instances/{{name}}:"+state) + if err != nil { + return nil, err + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return nil, fmt.Errorf("Unable to %q google_workbench_instance %q: %s", state, d.Id(), err) + } + return res, nil +} + +func WorkbenchInstanceKmsDiffSuppress(_, old, new string, _ *schema.ResourceData) bool { + if strings.HasPrefix(old, new) { + return true + } + return false +} + +<% unless compiler == "terraformgoogleconversion-codegen" -%> +func waitForWorkbenchOperation(config *transport_tpg.Config, d *schema.ResourceData, project string, billingProject string, userAgent string, response map[string]interface{}) error { + var opRes map[string]interface{} + err := WorkbenchOperationWaitTimeWithResponse( + config, response, &opRes, project, "Modifying Workbench Instance state", userAgent, + d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return err + } + return nil +} +<% end -%> diff --git a/mmv1/templates/terraform/custom_flatten/netapp_volume_replicaton_mirror_state.go.erb b/mmv1/templates/terraform/custom_flatten/netapp_volume_replicaton_mirror_state.go.erb new file mode 100644 index 000000000000..90881e839b6e --- /dev/null +++ b/mmv1/templates/terraform/custom_flatten/netapp_volume_replicaton_mirror_state.go.erb @@ -0,0 +1,20 @@ +func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Actual state of replication_enabled depends on mirrorState. let's update it. + // This is to pickup manual user STOP/RESUME operations on the replication. + if v == nil { + return v + } + + if v.(string) == "STOPPED" { + if err := d.Set("replication_enabled", false); err != nil { + return fmt.Errorf("Error setting replication_enabled: %s", err) + } + } else { + if err := d.Set("replication_enabled", true); err != nil { + return fmt.Errorf("Error setting replication_enabled: %s", err) + } + } + log.Printf("[DEBUG] value of replication_state : %v", d.Get("replication_enabled")) + + return v +} \ No newline at end of file diff --git a/mmv1/templates/terraform/custom_flatten/workbench_instance_boot_disk_encryption_flatten.go.erb b/mmv1/templates/terraform/custom_flatten/workbench_instance_boot_disk_encryption_flatten.go.erb deleted file mode 100644 index eb44d9657f33..000000000000 --- a/mmv1/templates/terraform/custom_flatten/workbench_instance_boot_disk_encryption_flatten.go.erb +++ /dev/null @@ -1,17 +0,0 @@ -<%# The license inside this block applies to this file. - # 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. --%> -func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { - return d.Get("gce_setup.0.boot_disk.0.disk_encryption") -} diff --git a/mmv1/templates/terraform/custom_flatten/workbench_instance_boot_disk_kms_flatten.go.erb b/mmv1/templates/terraform/custom_flatten/workbench_instance_boot_disk_kms_flatten.go.erb deleted file mode 100644 index 70c27a7661e2..000000000000 --- a/mmv1/templates/terraform/custom_flatten/workbench_instance_boot_disk_kms_flatten.go.erb +++ /dev/null @@ -1,17 +0,0 @@ -<%# The license inside this block applies to this file. - # 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. --%> -func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { - return d.Get("gce_setup.0.boot_disk.0.kms_key") -} diff --git a/mmv1/templates/terraform/custom_flatten/workbench_instance_data_disk_encryption_flatten.go.erb b/mmv1/templates/terraform/custom_flatten/workbench_instance_data_disk_encryption_flatten.go.erb deleted file mode 100644 index d93558a3db76..000000000000 --- a/mmv1/templates/terraform/custom_flatten/workbench_instance_data_disk_encryption_flatten.go.erb +++ /dev/null @@ -1,17 +0,0 @@ -<%# The license inside this block applies to this file. - # 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. --%> -func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { - return d.Get("gce_setup.0.data_disks.0.disk_encryption") -} diff --git a/mmv1/templates/terraform/custom_flatten/workbench_instance_data_disk_kms_flatten.go.erb b/mmv1/templates/terraform/custom_flatten/workbench_instance_data_disk_kms_flatten.go.erb deleted file mode 100644 index b6a7b9649db0..000000000000 --- a/mmv1/templates/terraform/custom_flatten/workbench_instance_data_disk_kms_flatten.go.erb +++ /dev/null @@ -1,17 +0,0 @@ -<%# The license inside this block applies to this file. - # 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. --%> -func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { - return d.Get("gce_setup.0.data_disks.0.kms_key") -} diff --git a/mmv1/templates/terraform/decoders/compute_region_target_https_proxy.go.erb b/mmv1/templates/terraform/decoders/compute_region_target_https_proxy.go.erb new file mode 100644 index 000000000000..0a72fe1c476a --- /dev/null +++ b/mmv1/templates/terraform/decoders/compute_region_target_https_proxy.go.erb @@ -0,0 +1,15 @@ +// Since both sslCertificates and certificateManagerCertificates maps to the same API field (sslCertificates), we need to check the types +// of certificates that exist in the array and decide whether to change the field to certificateManagerCertificate or not. +// The decoder logic depends on the fact that the API does not allow mixed type of certificates and it returns +// certificate manager certificates in the format of //certificatemanager.googleapis.com/projects/*/locations/*/certificates/* +if sslCertificates, ok := res["sslCertificates"].([]interface{}); ok && len(sslCertificates) > 0 { + regPat, _ := regexp.Compile("//certificatemanager.googleapis.com/projects/(.*)/locations/(.*)/certificates/(.*)") + + if regPat.MatchString(sslCertificates[0].(string)) { + // It is enough to check only the type of one of the provided certificates beacuse all the certificates should be the same type. + log.Printf("[DEBUG] The field sslCertificates contains certificateManagerCertificates, the field name will be converted to certificateManagerCertificates") + res["certificateManagerCertificates"] = res["sslCertificates"] + delete(res, "sslCertificates") + } +} +return res, nil \ No newline at end of file diff --git a/mmv1/templates/terraform/encoders/compute_region_target_https_proxy.go.erb b/mmv1/templates/terraform/encoders/compute_region_target_https_proxy.go.erb new file mode 100644 index 000000000000..168d4a65c5ef --- /dev/null +++ b/mmv1/templates/terraform/encoders/compute_region_target_https_proxy.go.erb @@ -0,0 +1,10 @@ + +if _, ok := obj["certificateManagerCertificates"]; ok { + // The field certificateManagerCertificates should not be included in the API request, and it should be renamed to `sslCertificates` + // The API does not allow using both certificate manager certificates and sslCertificates. If that changes + // in the future, the encoder logic should change accordingly because this will mean that both fields are no longer mutual exclusive. + log.Printf("[DEBUG] converting the field CertificateManagerCertificates to sslCertificates before sending the request") + obj["sslCertificates"] = obj["certificateManagerCertificates"] + delete(obj, "certificateManagerCertificates") +} +return obj, nil \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/big_query_routine_basic.tf.erb b/mmv1/templates/terraform/examples/bigquery_routine_basic.tf.erb similarity index 100% rename from mmv1/templates/terraform/examples/big_query_routine_basic.tf.erb rename to mmv1/templates/terraform/examples/bigquery_routine_basic.tf.erb diff --git a/mmv1/templates/terraform/examples/big_query_routine_json.tf.erb b/mmv1/templates/terraform/examples/bigquery_routine_json.tf.erb similarity index 100% rename from mmv1/templates/terraform/examples/big_query_routine_json.tf.erb rename to mmv1/templates/terraform/examples/bigquery_routine_json.tf.erb diff --git a/mmv1/templates/terraform/examples/big_query_routine_pyspark.tf.erb b/mmv1/templates/terraform/examples/bigquery_routine_pyspark.tf.erb similarity index 100% rename from mmv1/templates/terraform/examples/big_query_routine_pyspark.tf.erb rename to mmv1/templates/terraform/examples/bigquery_routine_pyspark.tf.erb diff --git a/mmv1/templates/terraform/examples/big_query_routine_pyspark_mainfile.tf.erb b/mmv1/templates/terraform/examples/bigquery_routine_pyspark_mainfile.tf.erb similarity index 100% rename from mmv1/templates/terraform/examples/big_query_routine_pyspark_mainfile.tf.erb rename to mmv1/templates/terraform/examples/bigquery_routine_pyspark_mainfile.tf.erb diff --git a/mmv1/templates/terraform/examples/bigquery_routine_remote_function.tf.erb b/mmv1/templates/terraform/examples/bigquery_routine_remote_function.tf.erb new file mode 100644 index 000000000000..9ef5b2a0dd2a --- /dev/null +++ b/mmv1/templates/terraform/examples/bigquery_routine_remote_function.tf.erb @@ -0,0 +1,27 @@ +resource "google_bigquery_dataset" "test" { + dataset_id = "<%= ctx[:vars]['dataset_id'] %>" +} + +resource "google_bigquery_connection" "test" { + connection_id = "<%= ctx[:vars]['connection_id'] %>" + location = "US" + cloud_resource { } +} + +resource "google_bigquery_routine" "<%= ctx[:primary_resource_id] %>" { + dataset_id = google_bigquery_dataset.test.dataset_id + routine_id = "<%= ctx[:vars]['routine_id'] %>" + routine_type = "SCALAR_FUNCTION" + definition_body = "" + + return_type = "{\"typeKind\" : \"STRING\"}" + + remote_function_options { + endpoint = "https://us-east1-my_gcf_project.cloudfunctions.net/remote_add" + connection = google_bigquery_connection.test.name + max_batching_rows = "10" + user_defined_context = { + "z": "1.5", + } + } +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/big_query_routine_spark_jar.tf.erb b/mmv1/templates/terraform/examples/bigquery_routine_spark_jar.tf.erb similarity index 100% rename from mmv1/templates/terraform/examples/big_query_routine_spark_jar.tf.erb rename to mmv1/templates/terraform/examples/bigquery_routine_spark_jar.tf.erb diff --git a/mmv1/templates/terraform/examples/big_query_routine_tvf.tf.erb b/mmv1/templates/terraform/examples/bigquery_routine_tvf.tf.erb similarity index 100% rename from mmv1/templates/terraform/examples/big_query_routine_tvf.tf.erb rename to mmv1/templates/terraform/examples/bigquery_routine_tvf.tf.erb diff --git a/mmv1/templates/terraform/examples/certificate_manager_dns_authorization_basic.tf.erb b/mmv1/templates/terraform/examples/certificate_manager_dns_authorization_basic.tf.erb index c5abd47bc47b..a63cbe5392b2 100644 --- a/mmv1/templates/terraform/examples/certificate_manager_dns_authorization_basic.tf.erb +++ b/mmv1/templates/terraform/examples/certificate_manager_dns_authorization_basic.tf.erb @@ -1,6 +1,7 @@ resource "google_certificate_manager_dns_authorization" "<%= ctx[:primary_resource_id] %>" { name = "<%= ctx[:vars]['dns_auth_name'] %>" - description = "The default dnss" + location = "global" + description = "The default dns" domain = "<%= ctx[:vars]['subdomain'] %>.hashicorptest.com" } diff --git a/mmv1/templates/terraform/examples/clouddeploy_automation_basic.tf.erb b/mmv1/templates/terraform/examples/clouddeploy_automation_basic.tf.erb index fa7ad4fb230e..41b460f1aefb 100644 --- a/mmv1/templates/terraform/examples/clouddeploy_automation_basic.tf.erb +++ b/mmv1/templates/terraform/examples/clouddeploy_automation_basic.tf.erb @@ -1,5 +1,4 @@ resource "google_clouddeploy_automation" "<%= ctx[:primary_resource_id] %>" { - provider = google-beta name = "<%= ctx[:vars]['automation'] %>" project = google_clouddeploy_delivery_pipeline.pipeline.project location = google_clouddeploy_delivery_pipeline.pipeline.location @@ -19,7 +18,6 @@ resource "google_clouddeploy_automation" "<%= ctx[:primary_resource_id] %>" { } resource "google_clouddeploy_delivery_pipeline" "pipeline" { - provider = google-beta name = "<%= ctx[:vars]['delivery_pipeline'] %>" location = "us-central1" serial_pipeline { diff --git a/mmv1/templates/terraform/examples/clouddeploy_automation_full.tf.erb b/mmv1/templates/terraform/examples/clouddeploy_automation_full.tf.erb index 8cd21d0e10d3..51bf98d1bca1 100644 --- a/mmv1/templates/terraform/examples/clouddeploy_automation_full.tf.erb +++ b/mmv1/templates/terraform/examples/clouddeploy_automation_full.tf.erb @@ -1,5 +1,4 @@ resource "google_clouddeploy_automation" "<%= ctx[:primary_resource_id] %>" { - provider = google-beta name = "<%= ctx[:vars]['automation'] %>" location = "us-central1" delivery_pipeline = google_clouddeploy_delivery_pipeline.pipeline.name @@ -40,7 +39,6 @@ resource "google_clouddeploy_automation" "<%= ctx[:primary_resource_id] %>" { } resource "google_clouddeploy_delivery_pipeline" "pipeline" { - provider = google-beta name = "<%= ctx[:vars]['delivery_pipeline'] %>" location = "us-central1" serial_pipeline { diff --git a/mmv1/templates/terraform/examples/clouddeploy_target_basic.tf.erb b/mmv1/templates/terraform/examples/clouddeploy_target_basic.tf.erb new file mode 100644 index 000000000000..5fd51566834c --- /dev/null +++ b/mmv1/templates/terraform/examples/clouddeploy_target_basic.tf.erb @@ -0,0 +1,4 @@ +resource "google_clouddeploy_target" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['target'] %>" + location = "us-central1" + } diff --git a/mmv1/templates/terraform/examples/cloudfunctions2_basic_gcs.tf.erb b/mmv1/templates/terraform/examples/cloudfunctions2_basic_gcs.tf.erb index 8dd602fc4c5f..00a11528241d 100644 --- a/mmv1/templates/terraform/examples/cloudfunctions2_basic_gcs.tf.erb +++ b/mmv1/templates/terraform/examples/cloudfunctions2_basic_gcs.tf.erb @@ -91,7 +91,6 @@ resource "google_cloudfunctions2_function" "<%= ctx[:primary_resource_id] %>" { } event_trigger { - trigger_region = "us-central1" # The trigger must be in the same location as the bucket event_type = "google.cloud.storage.object.v1.finalized" retry_policy = "RETRY_POLICY_RETRY" service_account_email = google_service_account.account.email diff --git a/mmv1/templates/terraform/examples/firebase_app_check_app_attest_config_full.tf.erb b/mmv1/templates/terraform/examples/firebase_app_check_app_attest_config_full.tf.erb new file mode 100644 index 000000000000..134ecacfc5f8 --- /dev/null +++ b/mmv1/templates/terraform/examples/firebase_app_check_app_attest_config_full.tf.erb @@ -0,0 +1,28 @@ +resource "google_firebase_apple_app" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + display_name = "Apple app" + bundle_id = "<%= ctx[:vars]['bundle_id'] %>" + team_id = "<%= ctx[:vars]['team_id'] %>" +} + +# It takes a while for App Check to recognize the new app +# If your app already exists, you don't have to wait 30 seconds. +resource "time_sleep" "wait_30s" { + depends_on = [google_firebase_apple_app.default] + create_duration = "30s" +} + +resource "google_firebase_app_check_app_attest_config" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + app_id = google_firebase_apple_app.default.app_id + token_ttl = "<%= ctx[:vars]['token_ttl'] %>" + + depends_on = [time_sleep.wait_30s] + + lifecycle { + precondition { + condition = google_firebase_apple_app.default.team_id != "" + error_message = "Provide a Team ID on the Apple App to use App Check" + } + } +} diff --git a/mmv1/templates/terraform/examples/firebase_app_check_app_attest_config_minimal.tf.erb b/mmv1/templates/terraform/examples/firebase_app_check_app_attest_config_minimal.tf.erb new file mode 100644 index 000000000000..a25a7f2d698f --- /dev/null +++ b/mmv1/templates/terraform/examples/firebase_app_check_app_attest_config_minimal.tf.erb @@ -0,0 +1,27 @@ +resource "google_firebase_apple_app" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + display_name = "Apple app" + bundle_id = "<%= ctx[:vars]['bundle_id'] %>" + team_id = "<%= ctx[:vars]['team_id'] %>" +} + +# It takes a while for App Check to recognize the new app +# If your app already exists, you don't have to wait 30 seconds. +resource "time_sleep" "wait_30s" { + depends_on = [google_firebase_apple_app.default] + create_duration = "30s" +} + +resource "google_firebase_app_check_app_attest_config" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + app_id = google_firebase_apple_app.default.app_id + + depends_on = [time_sleep.wait_30s] + + lifecycle { + precondition { + condition = google_firebase_apple_app.default.team_id != "" + error_message = "Provide a Team ID on the Apple App to use App Check" + } + } +} diff --git a/mmv1/templates/terraform/examples/firebase_app_check_play_integrity_config_full.tf.erb b/mmv1/templates/terraform/examples/firebase_app_check_play_integrity_config_full.tf.erb new file mode 100644 index 000000000000..78559763e319 --- /dev/null +++ b/mmv1/templates/terraform/examples/firebase_app_check_play_integrity_config_full.tf.erb @@ -0,0 +1,29 @@ +resource "google_firebase_android_app" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + display_name = "Play Integrity app" + package_name = "<%= ctx[:vars]['package_name'] %>" + sha1_hashes = ["2145bdf698b8715039bd0e83f2069bed435ac21c"] + sha256_hashes = ["2145bdf698b8715039bd0e83f2069bed435ac21ca1b2c3d4e5f6123456789abc"] +} + +# It takes a while for App Check to recognize the new app +# If your app already exists, you don't have to wait 30 seconds. +resource "time_sleep" "wait_30s" { + depends_on = [google_firebase_android_app.default] + create_duration = "30s" +} + +resource "google_firebase_app_check_play_integrity_config" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + app_id = google_firebase_android_app.default.app_id + token_ttl = "<%= ctx[:vars]['token_ttl'] %>" + + depends_on = [time_sleep.wait_30s] + + lifecycle { + precondition { + condition = length(google_firebase_android_app.default.sha256_hashes) > 0 + error_message = "Provide a SHA-256 certificate on the Android App to use App Check" + } + } +} diff --git a/mmv1/templates/terraform/examples/firebase_app_check_play_integrity_config_minimal.tf.erb b/mmv1/templates/terraform/examples/firebase_app_check_play_integrity_config_minimal.tf.erb new file mode 100644 index 000000000000..4aacde367414 --- /dev/null +++ b/mmv1/templates/terraform/examples/firebase_app_check_play_integrity_config_minimal.tf.erb @@ -0,0 +1,28 @@ +resource "google_firebase_android_app" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + display_name = "Play Integrity app" + package_name = "<%= ctx[:vars]['package_name'] %>" + sha1_hashes = ["2145bdf698b8715039bd0e83f2069bed435ac21c"] + sha256_hashes = ["2145bdf698b8715039bd0e83f2069bed435ac21ca1b2c3d4e5f6123456789abc"] +} + +# It takes a while for App Check to recognize the new app +# If your app already exists, you don't have to wait 30 seconds. +resource "time_sleep" "wait_30s" { + depends_on = [google_firebase_android_app.default] + create_duration = "30s" +} + +resource "google_firebase_app_check_play_integrity_config" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + app_id = google_firebase_android_app.default.app_id + + depends_on = [time_sleep.wait_30s] + + lifecycle { + precondition { + condition = length(google_firebase_android_app.default.sha256_hashes) > 0 + error_message = "Provide a SHA-256 certificate on the Android App to use App Check" + } + } +} diff --git a/mmv1/templates/terraform/examples/firebase_app_check_recaptcha_enterprise_config_basic.tf.erb b/mmv1/templates/terraform/examples/firebase_app_check_recaptcha_enterprise_config_basic.tf.erb new file mode 100644 index 000000000000..806dfe37a16d --- /dev/null +++ b/mmv1/templates/terraform/examples/firebase_app_check_recaptcha_enterprise_config_basic.tf.erb @@ -0,0 +1,20 @@ +resource "google_firebase_web_app" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + display_name = "Web App for reCAPTCHA Enterprise" +} + +# It takes a while for App Check to recognize the new app +# If your app already exists, you don't have to wait 30 seconds. +resource "time_sleep" "wait_30s" { + depends_on = [google_firebase_web_app.default] + create_duration = "30s" +} + +resource "google_firebase_app_check_recaptcha_enterprise_config" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + app_id = google_firebase_web_app.default.app_id + site_key = "<%= ctx[:vars]['site_key'] %>" + token_ttl = "<%= ctx[:vars]['token_ttl'] %>" + + depends_on = [time_sleep.wait_30s] +} diff --git a/mmv1/templates/terraform/examples/firebase_app_check_recaptcha_v3_config_basic.tf.erb b/mmv1/templates/terraform/examples/firebase_app_check_recaptcha_v3_config_basic.tf.erb new file mode 100644 index 000000000000..4b4c88b7ddd7 --- /dev/null +++ b/mmv1/templates/terraform/examples/firebase_app_check_recaptcha_v3_config_basic.tf.erb @@ -0,0 +1,20 @@ +resource "google_firebase_web_app" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + display_name = "Web App for reCAPTCHA V3" +} + +# It takes a while for App Check to recognize the new app +# If your app already exists, you don't have to wait 30 seconds. +resource "time_sleep" "wait_30s" { + depends_on = [google_firebase_web_app.default] + create_duration = "30s" +} + +resource "google_firebase_app_check_recaptcha_v3_config" "default" { + project = "<%= ctx[:test_env_vars]['project_id'] %>" + app_id = google_firebase_web_app.default.app_id + site_secret = "<%= ctx[:vars]['site_secret'] %>" + token_ttl = "<%= ctx[:vars]['token_ttl'] %>" + + depends_on = [time_sleep.wait_30s] +} diff --git a/mmv1/templates/terraform/examples/gkehub_scope_basic.tf.erb b/mmv1/templates/terraform/examples/gkehub_scope_basic.tf.erb index 107fa956c82d..63efb8be3c78 100644 --- a/mmv1/templates/terraform/examples/gkehub_scope_basic.tf.erb +++ b/mmv1/templates/terraform/examples/gkehub_scope_basic.tf.erb @@ -1,5 +1,10 @@ resource "google_gke_hub_scope" "<%= ctx[:primary_resource_id] %>" { scope_id = "<%= ctx[:vars]['resource_name'] %>" + namespace_labels = { + keyb = "valueb" + keya = "valuea" + keyc = "valuec" + } labels = { keyb = "valueb" keya = "valuea" diff --git a/mmv1/templates/terraform/examples/healthcare_hl7_v2_store_basic.tf.erb b/mmv1/templates/terraform/examples/healthcare_hl7_v2_store_basic.tf.erb index 42460e90eec3..a75931fd02ae 100644 --- a/mmv1/templates/terraform/examples/healthcare_hl7_v2_store_basic.tf.erb +++ b/mmv1/templates/terraform/examples/healthcare_hl7_v2_store_basic.tf.erb @@ -1,6 +1,7 @@ resource "google_healthcare_hl7_v2_store" "store" { name = "<%= ctx[:vars]['hl7_v2_store_name'] %>" dataset = google_healthcare_dataset.dataset.id + reject_duplicate_message = true notification_configs { pubsub_topic = google_pubsub_topic.topic.id diff --git a/mmv1/templates/terraform/examples/iap_app_engine_service.tf.erb b/mmv1/templates/terraform/examples/iap_app_engine_service.tf.erb index 6727559e4325..37f79449f02b 100644 --- a/mmv1/templates/terraform/examples/iap_app_engine_service.tf.erb +++ b/mmv1/templates/terraform/examples/iap_app_engine_service.tf.erb @@ -39,6 +39,10 @@ resource "google_app_engine_standard_app_version" "version" { runtime = "nodejs10" noop_on_destroy = true + // TODO: Removed basic scaling once automatic_scaling refresh behavior is fixed. + basic_scaling { + max_instances = 5 + } entrypoint { shell = "node ./app.js" } diff --git a/mmv1/templates/terraform/examples/looker_instance_custom_domain.tf.erb b/mmv1/templates/terraform/examples/looker_instance_custom_domain.tf.erb new file mode 100644 index 000000000000..1a69e027ad91 --- /dev/null +++ b/mmv1/templates/terraform/examples/looker_instance_custom_domain.tf.erb @@ -0,0 +1,12 @@ +resource "google_looker_instance" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]["instance_name"] %>" + platform_edition = "LOOKER_CORE_STANDARD" + region = "us-central1" + oauth_config { + client_id = "<%= ctx[:vars]["client_id"] %>" + client_secret = "<%= ctx[:vars]["client_secret"] %>" + } + custom_domain { + domain = "<%= ctx[:vars]["custom_domain"] %>.com" + } +} diff --git a/mmv1/templates/terraform/examples/volume_basic.tf.erb b/mmv1/templates/terraform/examples/netapp_volume_basic.tf.erb similarity index 100% rename from mmv1/templates/terraform/examples/volume_basic.tf.erb rename to mmv1/templates/terraform/examples/netapp_volume_basic.tf.erb diff --git a/mmv1/templates/terraform/examples/netapp_volume_replication_create.tf.erb b/mmv1/templates/terraform/examples/netapp_volume_replication_create.tf.erb new file mode 100644 index 000000000000..07b69c542b2b --- /dev/null +++ b/mmv1/templates/terraform/examples/netapp_volume_replication_create.tf.erb @@ -0,0 +1,54 @@ + +data "google_compute_network" "default" { + name = "<%= ctx[:vars]['network_name'] %>" +} + +resource "google_netapp_storage_pool" "source_pool" { + name = "<%= ctx[:vars]['source_pool_name'] %>" + location = "us-central1" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_storage_pool" "destination_pool" { + name = "<%= ctx[:vars]['destination_pool_name'] %>" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "source_volume" { + location = google_netapp_storage_pool.source_pool.location + name = "<%= ctx[:vars]['volume_name'] %>" + capacity_gib = 100 + share_name = "<%= ctx[:vars]['volume_name'] %>" + storage_pool = google_netapp_storage_pool.source_pool.name + protocols = [ + "NFSV3" + ] + deletion_policy = "FORCE" +} + +resource "google_netapp_volume_replication" "<%= ctx[:primary_resource_id] %>" { + depends_on = [google_netapp_volume.source_volume] + location = google_netapp_volume.source_volume.location + volume_name = google_netapp_volume.source_volume.name + name = "<%= ctx[:vars]['replication_name'] %>" + replication_schedule = "EVERY_10_MINUTES" + description = "This is a replication resource" + destination_volume_parameters { + storage_pool = google_netapp_storage_pool.destination_pool.id + volume_id = "<%= ctx[:vars]['destination_volume'] %>" + # Keeping the share_name of source and destination the same + # simplifies implementing client failover concepts + share_name = "<%= ctx[:vars]['volume_name'] %>" + description = "This is a replicated volume" + } + # WARNING: Setting delete_destination_volume to true, will delete the + # CURRENT destination volume if the replication is deleted. Omit the field + # or set delete_destination_volume=false to avoid accidental volume deletion. + delete_destination_volume = true + wait_for_mirror = true +} diff --git a/mmv1/templates/terraform/examples/notebook_instance_basic_stopped.tf.erb b/mmv1/templates/terraform/examples/notebook_instance_basic_stopped.tf.erb new file mode 100644 index 000000000000..41130bcca3ef --- /dev/null +++ b/mmv1/templates/terraform/examples/notebook_instance_basic_stopped.tf.erb @@ -0,0 +1,10 @@ +resource "google_notebooks_instance" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]["instance_name"] %>" + location = "us-west1-a" + machine_type = "e2-medium" + vm_image { + project = "deeplearning-platform-release" + image_family = "tf-latest-cpu" + } + desired_state = "STOPPED" +} diff --git a/mmv1/templates/terraform/examples/notebook_instance_full.tf.erb b/mmv1/templates/terraform/examples/notebook_instance_full.tf.erb index 0137529b0d1a..31f2af55ca3c 100644 --- a/mmv1/templates/terraform/examples/notebook_instance_full.tf.erb +++ b/mmv1/templates/terraform/examples/notebook_instance_full.tf.erb @@ -36,6 +36,7 @@ resource "google_notebooks_instance" "<%= ctx[:primary_resource_id] %>" { ] disk_encryption = "CMEK" kms_key = "<%= ctx[:vars]['key_name'] %>" + desired_state = "ACTIVE" } data "google_compute_network" "my_network" { diff --git a/mmv1/templates/terraform/examples/preference_set_basic.tf.erb b/mmv1/templates/terraform/examples/preference_set_basic.tf.erb new file mode 100644 index 000000000000..ebe5c10263fa --- /dev/null +++ b/mmv1/templates/terraform/examples/preference_set_basic.tf.erb @@ -0,0 +1,13 @@ +resource "google_migration_center_preference_set" "<%= ctx[:primary_resource_id] %>" { + location = "us-central1" + preference_set_id = "<%= ctx[:vars]['set_name'] %>" + description = "Terraform integration test description" + display_name = "Terraform integration test display" + virtual_machine_preferences { + vmware_engine_preferences { + cpu_overcommit_ratio = 1.5 + } + sizing_optimization_strategy = "SIZING_OPTIMIZATION_STRATEGY_SAME_AS_SOURCE" + target_product = "COMPUTE_MIGRATION_TARGET_PRODUCT_COMPUTE_ENGINE" + } +} diff --git a/mmv1/templates/terraform/examples/preference_set_full.tf.erb b/mmv1/templates/terraform/examples/preference_set_full.tf.erb new file mode 100644 index 000000000000..61acf00f5540 --- /dev/null +++ b/mmv1/templates/terraform/examples/preference_set_full.tf.erb @@ -0,0 +1,35 @@ +resource "google_migration_center_preference_set" "<%= ctx[:primary_resource_id] %>" { + location = "us-central1" + preference_set_id = "<%= ctx[:vars]['set_name'] %>" + description = "Terraform integration test description" + display_name = "Terraform integration test display" + virtual_machine_preferences { + vmware_engine_preferences { + cpu_overcommit_ratio = 1.5 + storage_deduplication_compression_ratio = 1.3 + commitment_plan = "ON_DEMAND" + } + sizing_optimization_strategy = "SIZING_OPTIMIZATION_STRATEGY_SAME_AS_SOURCE" + target_product = "COMPUTE_MIGRATION_TARGET_PRODUCT_COMPUTE_ENGINE" + commitment_plan = "COMMITMENT_PLAN_ONE_YEAR" + region_preferences { + preferred_regions = ["us-central1"] + } + sole_tenancy_preferences { + commitment_plan = "ON_DEMAND" + cpu_overcommit_ratio = 1.2 + host_maintenance_policy = "HOST_MAINTENANCE_POLICY_DEFAULT" + node_types { + node_name = "tf-test" + } + } + compute_engine_preferences { + license_type = "LICENSE_TYPE_BRING_YOUR_OWN_LICENSE" + machine_preferences { + allowed_machine_series { + code = "C3" + } + } + } + } +} diff --git a/mmv1/templates/terraform/examples/region_target_https_proxy_certificate_manager_certificate.tf.erb b/mmv1/templates/terraform/examples/region_target_https_proxy_certificate_manager_certificate.tf.erb new file mode 100644 index 000000000000..05c8d90b46f9 --- /dev/null +++ b/mmv1/templates/terraform/examples/region_target_https_proxy_certificate_manager_certificate.tf.erb @@ -0,0 +1,28 @@ +resource "google_compute_region_target_https_proxy" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['region_target_https_proxy_name'] %>" + url_map = google_compute_region_url_map.default.id + certificate_manager_certificates = ["//certificatemanager.googleapis.com/${google_certificate_manager_certificate.default.id}"] # [google_certificate_manager_certificate.default.id] is also acceptable +} + +resource "google_certificate_manager_certificate" "default" { + name = "<%= ctx[:vars]['certificate_manager_certificate_name'] %>" + location = "us-central1" + self_managed { + pem_certificate = file("test-fixtures/cert.pem") + pem_private_key = file("test-fixtures/private-key.pem") + } +} + +resource "google_compute_region_url_map" "default" { + name = "<%= ctx[:vars]['region_url_map_name'] %>" + default_service = google_compute_region_backend_service.default.id + region = "us-central1" +} + +resource "google_compute_region_backend_service" "default" { + name = "<%= ctx[:vars]['region_backend_service_name'] %>" + region = "us-central1" + protocol = "HTTPS" + timeout_sec = 30 + load_balancing_scheme = "INTERNAL_MANAGED" +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/scc_event_threat_detection_custom_module.tf.erb b/mmv1/templates/terraform/examples/scc_event_threat_detection_custom_module.tf.erb index 34bb64ac066f..8f7c08b49f8c 100644 --- a/mmv1/templates/terraform/examples/scc_event_threat_detection_custom_module.tf.erb +++ b/mmv1/templates/terraform/examples/scc_event_threat_detection_custom_module.tf.erb @@ -4,7 +4,7 @@ resource "google_scc_event_threat_detection_custom_module" "<%= ctx[:primary_res enablement_state = "ENABLED" type = "<%= ctx[:vars]['type'] %>" description = "My Event Threat Detection Custom Module" - cofig = jsonencode({ + config = jsonencode({ "metadata": { "severity": "LOW", "description": "Flagged by Forcepoint as malicious", diff --git a/mmv1/templates/terraform/examples/securityposture_posture_basic.tf.erb b/mmv1/templates/terraform/examples/securityposture_posture_basic.tf.erb index 5d865222aa4e..9101034c48c2 100644 --- a/mmv1/templates/terraform/examples/securityposture_posture_basic.tf.erb +++ b/mmv1/templates/terraform/examples/securityposture_posture_basic.tf.erb @@ -14,6 +14,11 @@ resource "google_securityposture_posture" "<%= ctx[:primary_resource_id] %>"{ canned_constraint_id = "storage.uniformBucketLevelAccess" policy_rules { enforce = true + condition { + description = "condition description" + expression = "resource.matchTag('org_id/tag_key_short_name,'tag_value_short_name')" + title = "a CEL condition" + } } } } @@ -33,6 +38,11 @@ resource "google_securityposture_posture" "<%= ctx[:primary_resource_id] %>"{ } policy_rules { enforce = true + condition { + description = "condition description" + expression = "resource.matchTagId('tagKeys/key_id','tagValues/value_id')" + title = "a CEL condition" + } } } } diff --git a/mmv1/templates/terraform/examples/vertex_ai_featureonlinestore_featureview_feature_registry.tf.erb b/mmv1/templates/terraform/examples/vertex_ai_featureonlinestore_featureview_feature_registry.tf.erb new file mode 100644 index 000000000000..e82ea2a0c104 --- /dev/null +++ b/mmv1/templates/terraform/examples/vertex_ai_featureonlinestore_featureview_feature_registry.tf.erb @@ -0,0 +1,93 @@ +resource "google_vertex_ai_feature_online_store" "featureonlinestore" { + name = "<%= ctx[:vars]['name'] %>" + labels = { + foo = "bar" + } + region = "us-central1" + bigtable { + auto_scaling { + min_node_count = 1 + max_node_count = 2 + cpu_utilization_target = 80 + } + } +} + +resource "google_bigquery_dataset" "sample_dataset" { + dataset_id = "<%= ctx[:vars]['name'] %>" + friendly_name = "test" + description = "This is a test description" + location = "US" +} + +resource "google_bigquery_table" "sample_table" { + deletion_protection = false + dataset_id = google_bigquery_dataset.sample_dataset.dataset_id + table_id = "<%= ctx[:vars]['name'] %>" + + schema = <", + "type": "STRING", + "mode": "NULLABLE" + }, + { + "name": "feature_timestamp", + "type": "TIMESTAMP", + "mode": "NULLABLE" + } +] +EOF +} + +resource "google_vertex_ai_feature_group" "sample_feature_group" { + name = "<%= ctx[:vars]['name'] %>" + description = "A sample feature group" + region = "us-central1" + labels = { + label-one = "value-one" + } + big_query { + big_query_source { + # The source table must have a column named 'feature_timestamp' of type TIMESTAMP. + input_uri = "bq://${google_bigquery_table.sample_table.project}.${google_bigquery_table.sample_table.dataset_id}.${google_bigquery_table.sample_table.table_id}" + } + entity_id_columns = ["feature_id"] + } +} + + + +resource "google_vertex_ai_feature_group_feature" "sample_feature" { + name = "<%= ctx[:vars]['name'] %>" + region = "us-central1" + feature_group = google_vertex_ai_feature_group.sample_feature_group.name + description = "A sample feature" + labels = { + label-one = "value-one" + } +} + + +resource "google_vertex_ai_feature_online_store_featureview" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['name'] %>" + region = "us-central1" + feature_online_store = google_vertex_ai_feature_online_store.featureonlinestore.name + sync_config { + cron = "0 0 * * *" + } + feature_registry_source { + + feature_groups { + feature_group_id = google_vertex_ai_feature_group.sample_feature_group.name + feature_ids = [google_vertex_ai_feature_group_feature.sample_feature.name] + } + } +} + diff --git a/mmv1/templates/terraform/examples/workbench_instance_basic_container.tf.erb b/mmv1/templates/terraform/examples/workbench_instance_basic_container.tf.erb new file mode 100644 index 000000000000..55ba370ffee1 --- /dev/null +++ b/mmv1/templates/terraform/examples/workbench_instance_basic_container.tf.erb @@ -0,0 +1,11 @@ +resource "google_workbench_instance" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['instance_name'] %>" + location = "us-west1-a" + + gce_setup { + container_image { + repository = "us-docker.pkg.dev/deeplearning-platform-release/gcr.io/base-cu113.py310" + tag = "latest" + } + } +} diff --git a/mmv1/templates/terraform/examples/workbench_instance_full.tf.erb b/mmv1/templates/terraform/examples/workbench_instance_full.tf.erb index 2ec579cba8dc..dd1122901e18 100644 --- a/mmv1/templates/terraform/examples/workbench_instance_full.tf.erb +++ b/mmv1/templates/terraform/examples/workbench_instance_full.tf.erb @@ -21,6 +21,12 @@ resource "google_workbench_instance" "<%= ctx[:primary_resource_id] %>" { core_count = 1 } + shielded_instance_config { + enable_secure_boot = true + enable_vtpm = true + enable_integrity_monitoring = true + } + disable_public_ip = false service_accounts { @@ -30,14 +36,14 @@ resource "google_workbench_instance" "<%= ctx[:primary_resource_id] %>" { boot_disk { disk_size_gb = 310 disk_type = "PD_SSD" - disk_encryption = "GMEK" + disk_encryption = "CMEK" kms_key = "<%= ctx[:vars]['key_name'] %>" } data_disks { disk_size_gb = 330 disk_type = "PD_SSD" - disk_encryption = "GMEK" + disk_encryption = "CMEK" kms_key = "<%= ctx[:vars]['key_name'] %>" } @@ -65,4 +71,6 @@ resource "google_workbench_instance" "<%= ctx[:primary_resource_id] %>" { k = "val" } + desired_state = "ACTIVE" + } diff --git a/mmv1/templates/terraform/examples/workbench_instance_labels.tf.erb b/mmv1/templates/terraform/examples/workbench_instance_labels_stopped.tf.erb similarity index 72% rename from mmv1/templates/terraform/examples/workbench_instance_labels.tf.erb rename to mmv1/templates/terraform/examples/workbench_instance_labels_stopped.tf.erb index 3a0d9afda853..151053d0ef49 100644 --- a/mmv1/templates/terraform/examples/workbench_instance_labels.tf.erb +++ b/mmv1/templates/terraform/examples/workbench_instance_labels_stopped.tf.erb @@ -5,6 +5,12 @@ resource "google_workbench_instance" "<%= ctx[:primary_resource_id] %>" { gce_setup { machine_type = "e2-standard-4" + shielded_instance_config { + enable_secure_boot = false + enable_vtpm = false + enable_integrity_monitoring = false + } + service_accounts { email = "<%= ctx[:test_env_vars]["service_account"] %>" } @@ -21,4 +27,6 @@ resource "google_workbench_instance" "<%= ctx[:primary_resource_id] %>" { k = "val" } + desired_state = "STOPPED" + } diff --git a/mmv1/templates/terraform/post_create/netapp_volume_replication_post_create.go.erb b/mmv1/templates/terraform/post_create/netapp_volume_replication_post_create.go.erb new file mode 100644 index 000000000000..b2d44a9c063e --- /dev/null +++ b/mmv1/templates/terraform/post_create/netapp_volume_replication_post_create.go.erb @@ -0,0 +1,7 @@ +if d.Get("wait_for_mirror").(bool) == true { + // Wait for mirrorState=MIRRORED before treating the resource as created + err = NetAppVolumeReplicationWaitForMirror(d, meta, "MIRRORED") + if err != nil { + return fmt.Errorf("Error waiting for volume replication to reach mirror_state==MIRRORED: %s", err) + } +} \ No newline at end of file diff --git a/mmv1/templates/terraform/post_create/notebooks_instance.go.erb b/mmv1/templates/terraform/post_create/notebooks_instance.go.erb new file mode 100644 index 000000000000..a70d7cb313a5 --- /dev/null +++ b/mmv1/templates/terraform/post_create/notebooks_instance.go.erb @@ -0,0 +1,13 @@ +if err := waitForNotebooksInstanceActive(d, config, d.Timeout(schema.TimeoutCreate) - time.Minute); err != nil { + return fmt.Errorf("Notebook instance %q did not reach ACTIVE state: %q", d.Get("name").(string), err) +} + +if p, ok := d.GetOk("desired_state"); ok && p.(string) == "STOPPED" { + dRes, err := modifyNotebooksInstanceState(config, d, project, billingProject, userAgent, "stop") + if err != nil { + return err + } + if err := waitForNotebooksOperation(config, d, project, billingProject, userAgent, dRes); err != nil { + return fmt.Errorf("Error stopping Notebook Instance: %s", err) + } +} diff --git a/mmv1/templates/terraform/post_create/workbench_instance.go.erb b/mmv1/templates/terraform/post_create/workbench_instance.go.erb index e3a968e4a6b2..25128f95f821 100644 --- a/mmv1/templates/terraform/post_create/workbench_instance.go.erb +++ b/mmv1/templates/terraform/post_create/workbench_instance.go.erb @@ -1,3 +1,13 @@ if err := waitForWorkbenchInstanceActive(d, config, d.Timeout(schema.TimeoutCreate) - time.Minute); err != nil { return fmt.Errorf("Workbench instance %q did not reach ACTIVE state: %q", d.Get("name").(string), err) } + +if p, ok := d.GetOk("desired_state"); ok && p.(string) == "STOPPED" { + dRes, err := modifyWorkbenchInstanceState(config, d, project, billingProject, userAgent, "stop") + if err != nil { + return err + } + if err := waitForWorkbenchOperation(config, d, project, billingProject, userAgent, dRes); err != nil { + return fmt.Errorf("Error stopping Workbench Instance: %s", err) + } +} diff --git a/mmv1/templates/terraform/post_delete/netapp_volume_replication_delete_destination_volume.go.erb b/mmv1/templates/terraform/post_delete/netapp_volume_replication_delete_destination_volume.go.erb new file mode 100644 index 000000000000..5a14f1fb407c --- /dev/null +++ b/mmv1/templates/terraform/post_delete/netapp_volume_replication_delete_destination_volume.go.erb @@ -0,0 +1,34 @@ +// A replication CREATE also created a destination volume +// A user can chooses to delete the destination volume after deleting the replication +if d.Get("delete_destination_volume").(bool) == true { + log.Printf("[DEBUG] delete_destination_volume is true. Deleting destination volume %v", d.Get("destination_volume")) + destination_volume := d.Get("destination_volume").(string) + del_url, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}"+destination_volume+"?force=true") + if err != nil { + return err + } + + var obj map[string]interface{} + res_del, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: billingProject, + RawURL: del_url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutDelete), + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, "Volume") + } + + err = NetappOperationWaitTime( + config, res_del, project, "Deleting destination volume", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting destination Volume %q: %#v", destination_volume, res_del) +} \ No newline at end of file diff --git a/mmv1/templates/terraform/post_update/netapp_volume_replication_mirror_state.go.erb b/mmv1/templates/terraform/post_update/netapp_volume_replication_mirror_state.go.erb new file mode 100644 index 000000000000..57a7146702e5 --- /dev/null +++ b/mmv1/templates/terraform/post_update/netapp_volume_replication_mirror_state.go.erb @@ -0,0 +1,89 @@ +// Manage stopping and resuming a mirror + +var obj2 map[string]interface{} +do_change := false +var action string +var targetState string +// state transitions +// there can be a glitch is a transfer starts/ends between reading mirrorState +// and sending the action. This will be very rare. No workaround. +if d.Get("replication_enabled").(bool) == true { + switch d.Get("mirror_state").(string) { + case "STOPPED": + // replication_enabled==true, mirrorState==STOPPED -> resume + action = "resume" + targetState = "MIRRORED" + do_change = true + default: + // replication_enabled==true, mirrorState!=STOPPED -> NOOP + do_change = false + } +} else { + switch d.Get("mirror_state").(string) { + case "MIRRORED": + // replication_enabled==false, mirrorState==MIRRORED -> stop + action = "stop" + targetState = "STOPPED" + do_change = true + case "TRANSFERRING": + // replication_enabled==false, mirrorState==TRANSFERRING -> force stop + // User needs to add force_stopping = true, otherwise will receive error + action = "stop" + targetState = "STOPPED" + do_change = true + case "PREPARING": + // replication_enabled==false, mirrorState==PREPARING -> stop + // Currently cannot be stopped. User will receive following error: + // Error code 3, message: invalid request error: "Replication in preparing state. Please wait until replication is in 'READY' STATE and try again later.". + // User needs to wait until mirrorState=MIRRORED + action = "stop" + targetState = "STOPPED" + do_change = true + default: + // replication_enabled==false, mirrorState==STOPPED -> NOOP + do_change = false + } + + if do_change == true && d.Get("force_stopping").(bool) == true { + obj2 = map[string]interface{}{ + "force": true, + } + } +} + +if do_change { + // We need to send STOP/RESUME API calls + rawurl, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}:"+action) + if err != nil { + return err + } + + res2, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: rawurl, + UserAgent: userAgent, + Body: obj2, + Timeout: d.Timeout(schema.TimeoutUpdate), + }) + if err != nil { + return fmt.Errorf("Error stopping/resuming replication %q: %s", d.Id(), err) + } + + err = NetappOperationWaitTime( + config, res2, project, "volume replication "+action, userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + // If user specified to wait for mirror operations, wait to reach target state + if d.Get("wait_for_mirror").(bool) == true { + err = NetAppVolumeReplicationWaitForMirror(d, meta, targetState) + if err != nil { + return fmt.Errorf("Error waiting for volume replication to reach mirror_state==%s: %s", targetState, err) + } + } +} diff --git a/mmv1/templates/terraform/post_update/notebooks_instance.go.erb b/mmv1/templates/terraform/post_update/notebooks_instance.go.erb new file mode 100644 index 000000000000..819cff2d1dd2 --- /dev/null +++ b/mmv1/templates/terraform/post_update/notebooks_instance.go.erb @@ -0,0 +1,21 @@ +name := d.Get("name").(string) +state := d.Get("state").(string) +desired_state := d.Get("desired_state").(string) + +if state != desired_state { + verb := "start" + if desired_state == "STOPPED" { + verb = "stop" + } + pRes, err := modifyNotebooksInstanceState(config, d, project, billingProject, userAgent, verb) + if err != nil { + return err + } + + if err := waitForNotebooksOperation(config, d, project, billingProject, userAgent, pRes); err != nil { + return fmt.Errorf("Error waiting to modify Notebook Instance state: %s", err) + } + +} else { + log.Printf("[DEBUG] Notebook Instance %q has state %q.", name, state) +} diff --git a/mmv1/templates/terraform/post_update/workbench_instance.go.erb b/mmv1/templates/terraform/post_update/workbench_instance.go.erb index fa994a3aacb4..cd216f018387 100644 --- a/mmv1/templates/terraform/post_update/workbench_instance.go.erb +++ b/mmv1/templates/terraform/post_update/workbench_instance.go.erb @@ -1,33 +1,18 @@ state := d.Get("state").(string) +desired_state := d.Get("desired_state").(string) -if state != "ACTIVE" { - startURL, err := tpgresource.ReplaceVars(d, config, "{{WorkbenchBasePath}}projects/{{project}}/locations/{{location}}/instances/{{name}}:start") - if err != nil { - return err +if state != desired_state { + verb := "start" + if desired_state == "STOPPED" { + verb = "stop" } - - log.Printf("[DEBUG] Starting Workbench Instance: %q", name) - - emptyReqBody := make(map[string]interface{}) - - pRes, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ - Config: config, - Method: "POST", - Project: billingProject, - RawURL: startURL, - UserAgent: userAgent, - Body: emptyReqBody, - }) + pRes, err := modifyWorkbenchInstanceState(config, d, project, billingProject, userAgent, verb) if err != nil { - return fmt.Errorf("Error Starting Workbench Instance: %s", err) + return err } - var opResp map[string]interface{} - err = WorkbenchOperationWaitTimeWithResponse( - config, pRes, &opResp, project, "Starting Workbench Instance", userAgent, - d.Timeout(schema.TimeoutUpdate)) - if err != nil { - return fmt.Errorf("Error waiting to start Workbench Instance: %s", err) + if err := waitForWorkbenchOperation(config, d, project, billingProject, userAgent, pRes); err != nil { + return fmt.Errorf("Error waiting to modify Workbench Instance state: %s", err) } } else { diff --git a/mmv1/templates/terraform/pre_delete/netapp_volume_replication_stop_before_delete.go.erb b/mmv1/templates/terraform/pre_delete/netapp_volume_replication_stop_before_delete.go.erb new file mode 100644 index 000000000000..4e5c188acf2a --- /dev/null +++ b/mmv1/templates/terraform/pre_delete/netapp_volume_replication_stop_before_delete.go.erb @@ -0,0 +1,33 @@ +// A replication can only be deleted if mirrorState==STOPPED +// We are about to delete the replication and need to stop the mirror before. +// FYI: Stopping a PREPARING mirror currently doesn't work. User have to wait until +// mirror reaches MIRRORED. +if d.Get("mirror_state") != "STOPPED" { + rawurl, err := tpgresource.ReplaceVars(d, config, "{{NetappBasePath}}projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/replications/{{name}}:stop") + if err != nil { + return err + } + + reso, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: rawurl, + UserAgent: userAgent, + // We delete anyway, so lets always use force stop + Body: map[string]interface{}{ + "force": true, + }, + Timeout: d.Timeout(schema.TimeoutUpdate), + }) + if err != nil { + return fmt.Errorf("Error stopping volume replication %q before deleting it: %s", d.Id(), err) + } + + err = NetappOperationWaitTime( + config, reso, project, "Deleting volume replication", userAgent, + d.Timeout(schema.TimeoutDelete)) + if err != nil { + return err + } +} \ No newline at end of file diff --git a/mmv1/templates/terraform/pre_update/workbench_instance.go.erb b/mmv1/templates/terraform/pre_update/workbench_instance.go.erb index 2b33205942a7..847a0bcd1311 100644 --- a/mmv1/templates/terraform/pre_update/workbench_instance.go.erb +++ b/mmv1/templates/terraform/pre_update/workbench_instance.go.erb @@ -1,34 +1,15 @@ name := d.Get("name").(string) -if d.HasChange("gce_setup.0.machine_type") || d.HasChange("gce_setup.0.accelerator_configs") { +if d.HasChange("gce_setup.0.machine_type") || d.HasChange("gce_setup.0.accelerator_configs") || d.HasChange("gce_setup.0.shielded_instance_config"){ state := d.Get("state").(string) - if state != "STOPPED" { - stopURL, err := tpgresource.ReplaceVars(d, config, "{{WorkbenchBasePath}}projects/{{project}}/locations/{{location}}/instances/{{name}}:stop") - if err != nil { - return err - } - - log.Printf("[DEBUG] Stopping Workbench Instance: %q", name) - - emptyReqBody := make(map[string]interface{}) - dRes, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ - Config: config, - Method: "POST", - Project: billingProject, - RawURL: stopURL, - UserAgent: userAgent, - Body: emptyReqBody, - }) + if state != "STOPPED" { + dRes, err := modifyWorkbenchInstanceState(config, d, project, billingProject, userAgent, "stop") if err != nil { - return fmt.Errorf("Error Stopping Workbench Instance: %s", err) + return err } - var opRes map[string]interface{} - err = WorkbenchOperationWaitTimeWithResponse( - config, dRes, &opRes, project, "Stopping Workbench Instance", userAgent, - d.Timeout(schema.TimeoutUpdate)) - if err != nil { - return fmt.Errorf("Error waiting to stop Workbench Instance: %s", err) + if err := waitForWorkbenchOperation(config, d, project, billingProject, userAgent, dRes); err != nil { + return fmt.Errorf("Error stopping Workbench Instance: %s", err) } } else { @@ -47,6 +28,9 @@ if d.HasChange("gce_setup.0.machine_type") { if d.HasChange("gce_setup.0.accelerator_configs") { newUpdateMask = append(newUpdateMask, "gce_setup.accelerator_configs") } +if d.HasChange("gce_setup.0.shielded_instance_config") { + newUpdateMask = append(newUpdateMask, "gce_setup.shielded_instance_config") +} if d.HasChange("gce_setup.0.metadata") { newUpdateMask = append(newUpdateMask, "gceSetup.metadata") } diff --git a/mmv1/templates/terraform/state_migrations/certificate_manager_dns_authorization.go.erb b/mmv1/templates/terraform/state_migrations/certificate_manager_dns_authorization.go.erb new file mode 100644 index 000000000000..b3d29dc64136 --- /dev/null +++ b/mmv1/templates/terraform/state_migrations/certificate_manager_dns_authorization.go.erb @@ -0,0 +1,91 @@ +func ResourceCertificateManagerDnsAuthorizationUpgradeV0(_ context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + log.Printf("[DEBUG] Attributes before migration: %#v", rawState) + // Version 0 didn't support location. Default it to global. + rawState["location"] = "global" + log.Printf("[DEBUG] Attributes after migration: %#v", rawState) + return rawState, nil +} + +func resourceCertificateManagerDnsAuthorizationResourceV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "domain": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `A domain which is being authorized. A DnsAuthorization resource covers a +single domain and its wildcard, e.g. authorization for "example.com" can +be used to issue certificates for "example.com" and "*.example.com".`, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Name of the resource; provided by the client when the resource is created. +The name must be 1-64 characters long, and match the regular expression [a-zA-Z][a-zA-Z0-9_-]* which means the first character must be a letter, +and all following characters must be a dash, underscore, letter or digit.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `A human-readable description of the resource.`, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `Set of label tags associated with the DNS Authorization resource. + +**Note**: This field is non-authoritative, and will only manage the labels present in your configuration. +Please refer to the field 'effective_labels' for all of the labels present on the resource.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "dns_resource_record": { + Type: schema.TypeList, + Computed: true, + Description: `The structure describing the DNS Resource Record that needs to be added +to DNS configuration for the authorization to be usable by +certificate.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data": { + Type: schema.TypeString, + Computed: true, + Description: `Data of the DNS Resource Record.`, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `Fully qualified name of the DNS Resource Record. +E.g. '_acme-challenge.example.com'.`, + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: `Type of the DNS Resource Record.`, + }, + }, + }, + }, + "effective_labels": { + Type: schema.TypeMap, + Computed: true, + Description: `All of labels (key/value pairs) present on the resource in GCP, including the labels configured through Terraform, other clients and services.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "terraform_labels": { + Type: schema.TypeMap, + Computed: true, + Description: `The combination of labels configured directly on the resource + and default labels configured on the provider.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + } + +} \ No newline at end of file diff --git a/mmv1/third_party/terraform/.teamcity/CONTRIBUTION_GUIDE.md b/mmv1/third_party/terraform/.teamcity/CONTRIBUTION_GUIDE.md index 911460cd190c..5d803e699493 100644 --- a/mmv1/third_party/terraform/.teamcity/CONTRIBUTION_GUIDE.md +++ b/mmv1/third_party/terraform/.teamcity/CONTRIBUTION_GUIDE.md @@ -17,6 +17,14 @@ Note: these instructions need to be tested and improved. Please contact @SarahFr You will need to install: * Java 17 * `brew install openjdk@17` +* Maven + * `brew install --ignore-dependencies maven` + +Add the following to `~/.zshrc` and reload your terminal: + +``` +export JAVA_HOME=/usr/local/Cellar/openjdk@17/17.0.9/libexec/openjdk.jdk/Contents/Home +``` ## Getting started @@ -27,7 +35,7 @@ You will need to install: * Run `make validate` to check the code for both: * Errors that prevent the code building * Logical errors in TeamCity-specific logic, e.g. the need for unique identifiers for builds. -* Run `make tests` to run the automated tests defined in `.teamcity/tests` +* Run `make test` to run the automated tests defined in `.teamcity/tests` ## Rough description of the code base diff --git a/mmv1/third_party/terraform/.teamcity/components/builds/build_steps.kt b/mmv1/third_party/terraform/.teamcity/components/builds/build_steps.kt index 4faed1046b65..78f942cf380d 100644 --- a/mmv1/third_party/terraform/.teamcity/components/builds/build_steps.kt +++ b/mmv1/third_party/terraform/.teamcity/components/builds/build_steps.kt @@ -118,7 +118,7 @@ fun BuildSteps.runAcceptanceTests() { exit 0 fi - export TEST_COUNT=${'$'}(./test-binary -test.list=%TEST_PREFIX% | wc -l) + export TEST_COUNT=${'$'}(./test-binary -test.list="%TEST_PREFIX%" | wc -l) echo "Found ${'$'}{TEST_COUNT} tests that match the given test prefix %TEST_PREFIX%" if test ${'$'}TEST_COUNT -le "0"; then echo "Skipping test execution; no tests to run" diff --git a/mmv1/third_party/terraform/.teamcity/components/inputs/packages.kt b/mmv1/third_party/terraform/.teamcity/components/inputs/packages.kt index 19b107e2e72d..5ea3d2a5d48a 100644 --- a/mmv1/third_party/terraform/.teamcity/components/inputs/packages.kt +++ b/mmv1/third_party/terraform/.teamcity/components/inputs/packages.kt @@ -7,7 +7,7 @@ package generated -var PackagesList = mapOf( +var PackagesListGa = mapOf( "envvar" to mapOf( "name" to "envvar", "displayName" to "Environment Variables", @@ -50,7 +50,7 @@ var PackagesList = mapOf( ) ) -var SweepersList = mapOf( +var SweepersListGa = mapOf( "sweeper" to mapOf( "name" to "sweeper", "displayName" to "Sweeper", @@ -58,11 +58,53 @@ var SweepersList = mapOf( ) ) -fun GetPackageNameList(): List { - var packageNameList: ArrayList = arrayListOf() - PackagesList.forEach{ p -> - var packageName = p.value.getValue("name").toString() - packageNameList.add(packageName) - } - return packageNameList -} +var PackagesListBeta = mapOf( + "envvar" to mapOf( + "name" to "envvar", + "displayName" to "Environment Variables", + "path" to "./google-beta/envvar" + ), + "fwmodels" to mapOf( + "name" to "fwmodels", + "displayName" to "Framework Models", + "path" to "./google-beta/fwmodels" + ), + "fwprovider" to mapOf( + "name" to "fwprovider", + "displayName" to "Framework Provider", + "path" to "./google-beta/fwprovider" + ), + "fwresource" to mapOf( + "name" to "fwresource", + "displayName" to "Framework Resource", + "path" to "./google-beta/fwresource" + ), + "fwtransport" to mapOf( + "name" to "fwtransport", + "displayName" to "Framework Transport", + "path" to "./google-beta/fwtransport" + ), + "provider" to mapOf( + "name" to "provider", + "displayName" to "SDK Provider", + "path" to "./google-beta/provider" + ), + "transport" to mapOf( + "name" to "transport", + "displayName" to "Transport", + "path" to "./google-beta/transport" + ), + "google" to mapOf( + "name" to "google", + "displayName" to "Google", + "path" to "./google-beta" + ) +) + +var SweepersListBeta = mapOf( + "sweeper" to mapOf( + "name" to "sweeper", + "displayName" to "Sweeper", + "path" to "./google-beta/sweeper" + ) +) diff --git a/mmv1/third_party/terraform/.teamcity/components/projects/project_sweeper_project.kt b/mmv1/third_party/terraform/.teamcity/components/projects/project_sweeper_project.kt index f8da47ef5290..732822a52ff6 100644 --- a/mmv1/third_party/terraform/.teamcity/components/projects/project_sweeper_project.kt +++ b/mmv1/third_party/terraform/.teamcity/components/projects/project_sweeper_project.kt @@ -12,7 +12,7 @@ import SharedResourceNameBeta import SharedResourceNameGa import SharedResourceNameVcr import builds.* -import generated.SweepersList +import generated.SweepersListGa import jetbrains.buildServer.configs.kotlin.Project import replaceCharsId import vcs_roots.HashiCorpVCSRootGa @@ -31,7 +31,7 @@ fun projectSweeperSubProject(allConfig: AllContextParameters): Project { // Create build config for sweeping project resources // Uses the HashiCorpVCSRootGa VCS Root so that the latest sweepers in hashicorp/terraform-provider-google are used - val serviceSweeperConfig = BuildConfigurationForProjectSweeper("N/A", ProjectSweeperName, SweepersList, projectId, HashiCorpVCSRootGa, sharedResources, gaConfig) + val serviceSweeperConfig = BuildConfigurationForProjectSweeper("N/A", ProjectSweeperName, SweepersListGa, projectId, HashiCorpVCSRootGa, sharedResources, gaConfig) val trigger = NightlyTriggerConfiguration(startHour=12) serviceSweeperConfig.addTrigger(trigger) diff --git a/mmv1/third_party/terraform/.teamcity/components/projects/reused/mm_upstream.kt b/mmv1/third_party/terraform/.teamcity/components/projects/reused/mm_upstream.kt index 3b57d2fc12c4..c26ed498edd4 100644 --- a/mmv1/third_party/terraform/.teamcity/components/projects/reused/mm_upstream.kt +++ b/mmv1/third_party/terraform/.teamcity/components/projects/reused/mm_upstream.kt @@ -13,10 +13,12 @@ import ProviderNameGa import ServiceSweeperName import SharedResourceNameVcr import builds.* -import generated.PackagesList -import generated.ServicesListGa +import generated.PackagesListBeta +import generated.PackagesListGa import generated.ServicesListBeta -import generated.SweepersList +import generated.ServicesListGa +import generated.SweepersListBeta +import generated.SweepersListGa import jetbrains.buildServer.configs.kotlin.BuildType import jetbrains.buildServer.configs.kotlin.Project import jetbrains.buildServer.configs.kotlin.vcs.GitVcsRoot @@ -36,7 +38,13 @@ fun mmUpstream(parentProject: String, providerName: String, vcsRoot: GitVcsRoot, val packageBuildConfigs = BuildConfigurationsForPackages(allPackages, providerName, projectId, vcsRoot, sharedResources, config) // Create build config for sweeping the VCR test project - everything except projects - val serviceSweeperConfig = BuildConfigurationForServiceSweeper(providerName, ServiceSweeperName, SweepersList, projectId, vcsRoot, sharedResources, config) + var sweepersList: Map> + when(providerName) { + ProviderNameGa -> sweepersList = SweepersListGa + ProviderNameBeta -> sweepersList = SweepersListBeta + else -> throw Exception("Provider name not supplied when generating a nightly test subproject") + } + val serviceSweeperConfig = BuildConfigurationForServiceSweeper(providerName, ServiceSweeperName, sweepersList, projectId, vcsRoot, sharedResources, config) val trigger = NightlyTriggerConfiguration(startHour=12) serviceSweeperConfig.addTrigger(trigger) // Only the sweeper is on a schedule in this project @@ -60,10 +68,10 @@ fun mmUpstream(parentProject: String, providerName: String, vcsRoot: GitVcsRoot, fun getAllPackageInProviderVersion(providerName: String): Map> { var allPackages: Map> = mapOf() if (providerName == ProviderNameGa){ - allPackages = PackagesList + ServicesListGa + allPackages = PackagesListGa + ServicesListGa } if (providerName == ProviderNameBeta){ - allPackages = PackagesList + ServicesListBeta + allPackages = PackagesListBeta + ServicesListBeta } return allPackages } \ No newline at end of file diff --git a/mmv1/third_party/terraform/.teamcity/components/projects/reused/nightly_tests.kt b/mmv1/third_party/terraform/.teamcity/components/projects/reused/nightly_tests.kt index 0c531f952e40..6fda2b7b7ab1 100644 --- a/mmv1/third_party/terraform/.teamcity/components/projects/reused/nightly_tests.kt +++ b/mmv1/third_party/terraform/.teamcity/components/projects/reused/nightly_tests.kt @@ -14,8 +14,8 @@ import ServiceSweeperName import SharedResourceNameBeta import SharedResourceNameGa import builds.* -import generated.PackagesList -import generated.SweepersList +import generated.SweepersListBeta +import generated.SweepersListGa import jetbrains.buildServer.configs.kotlin.Project import jetbrains.buildServer.configs.kotlin.vcs.GitVcsRoot import replaceCharsId @@ -44,7 +44,13 @@ fun nightlyTests(parentProject:String, providerName: String, vcsRoot: GitVcsRoot } // Create build config for sweeping the nightly test project - val serviceSweeperConfig = BuildConfigurationForServiceSweeper(providerName, ServiceSweeperName, SweepersList, projectId, vcsRoot, sharedResources, config) + var sweepersList: Map> + when(providerName) { + ProviderNameGa -> sweepersList = SweepersListGa + ProviderNameBeta -> sweepersList = SweepersListBeta + else -> throw Exception("Provider name not supplied when generating a nightly test subproject") + } + val serviceSweeperConfig = BuildConfigurationForServiceSweeper(providerName, ServiceSweeperName, sweepersList, projectId, vcsRoot, sharedResources, config) val sweeperTrigger = NightlyTriggerConfiguration(startHour=12) // Override hour serviceSweeperConfig.addTrigger(sweeperTrigger) diff --git a/mmv1/third_party/terraform/.teamcity/components/projects/root_project.kt b/mmv1/third_party/terraform/.teamcity/components/projects/root_project.kt index c3ffd869756f..0c130da8eca5 100644 --- a/mmv1/third_party/terraform/.teamcity/components/projects/root_project.kt +++ b/mmv1/third_party/terraform/.teamcity/components/projects/root_project.kt @@ -12,7 +12,8 @@ import SharedResourceNameGa import SharedResourceNameVcr import builds.AllContextParameters import builds.readOnlySettings -import generated.GetPackageNameList +import generated.PackagesListBeta +import generated.PackagesListGa import generated.ServicesListBeta import generated.ServicesListGa import jetbrains.buildServer.configs.kotlin.Project @@ -38,21 +39,21 @@ fun googleCloudRootProject(allConfig: AllContextParameters): Project { id = "GA_NIGHTLY_SERVICE_LOCK_SHARED_RESOURCE" name = SharedResourceNameGa enabled = true - resourceType = customValues(getServiceNameList(ServicesListGa) + GetPackageNameList()) + resourceType = customValues(getPackageNameList(ServicesListGa) + getPackageNameList(PackagesListGa)) } // For controlling sweeping of the Beta nightly test project sharedResource { id = "BETA_NIGHTLY_SERVICE_LOCK_SHARED_RESOURCE" name = SharedResourceNameBeta enabled = true - resourceType = customValues(getServiceNameList(ServicesListBeta) + GetPackageNameList()) + resourceType = customValues(getPackageNameList(ServicesListBeta) + getPackageNameList(PackagesListBeta)) } // For controlling sweeping of the PR testing project sharedResource { id = "PR_SERVICE_LOCK_SHARED_RESOURCE" name = SharedResourceNameVcr enabled = true - resourceType = customValues(getServiceNameList(ServicesListBeta) + GetPackageNameList()) // Use Beta list of services here, assuming Beta is a superset of GA + resourceType = customValues(getPackageNameList(ServicesListBeta) + getPackageNameList(PackagesListBeta)) // Use Beta list of services here, assuming Beta is a superset of GA } } @@ -66,11 +67,11 @@ fun googleCloudRootProject(allConfig: AllContextParameters): Project { } } -fun getServiceNameList(servicesList: Map>): List { +fun getPackageNameList(servicesList: Map>): List { var serviceNameList: ArrayList = arrayListOf() servicesList.forEach{ s -> var serviceName = s.value.getValue("name").toString() serviceNameList.add(serviceName) } return serviceNameList -} \ No newline at end of file +} diff --git a/mmv1/third_party/terraform/.teamcity/tests/sweepers.kt b/mmv1/third_party/terraform/.teamcity/tests/sweepers.kt index 27e55f737a1f..1603aaeda776 100644 --- a/mmv1/third_party/terraform/.teamcity/tests/sweepers.kt +++ b/mmv1/third_party/terraform/.teamcity/tests/sweepers.kt @@ -9,6 +9,7 @@ package tests import ServiceSweeperName import jetbrains.buildServer.configs.kotlin.BuildType +import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test import jetbrains.buildServer.configs.kotlin.Project @@ -62,6 +63,31 @@ class SweeperTests { assertTrue("env.SKIP_PROJECT_SWEEPER is set to a non-empty string, so project sweepers are skipped. Value = `${value}` ", value != "") } + @Test + fun gaNightlyProjectServiceSweeperRunsInGoogle() { + val project = googleCloudRootProject(testContextParameters()) + + // Find GA nightly test project + val gaProject: Project? = project.subProjects.find { p-> p.name == gaProjectName} + if (gaProject == null) { + Assert.fail("Could not find the Google (GA) project") + } + val gaNightlyTestProject: Project? = gaProject!!.subProjects.find { p-> p.name == nightlyTestsProjectName} + if (gaNightlyTestProject == null) { + Assert.fail("Could not find the Google (GA) Nightly Test project") + } + + // Find sweeper inside + val sweeper: BuildType? = gaNightlyTestProject!!.buildTypes.find { p-> p.name == ServiceSweeperName} + if (sweeper == null) { + Assert.fail("Could not find the sweeper build in the Google (GA) Nightly Test project") + } + + // Check PACKAGE_PATH is in google (not google-beta) + val value = sweeper!!.params.findRawParam("PACKAGE_PATH")!!.value + assertEquals("./google/sweeper", value) + } + @Test fun betaNightlyProjectServiceSweeperSkipsProjectSweep() { val project = googleCloudRootProject(testContextParameters()) @@ -88,4 +114,29 @@ class SweeperTests { val value = sweeper!!.params.findRawParam("env.SKIP_PROJECT_SWEEPER")!!.value assertTrue("env.SKIP_PROJECT_SWEEPER is set to a non-empty string, so project sweepers are skipped. Value = `${value}` ", value != "") } + + @Test + fun betaNightlyProjectServiceSweeperRunsInGoogleBeta() { + val project = googleCloudRootProject(testContextParameters()) + + // Find Beta nightly test project + val betaProject: Project? = project.subProjects.find { p-> p.name == betaProjectName} + if (betaProject == null) { + Assert.fail("Could not find the Google (GA) project") + } + val betaNightlyTestProject: Project? = betaProject!!.subProjects.find { p-> p.name == nightlyTestsProjectName} + if (betaNightlyTestProject == null) { + Assert.fail("Could not find the Google (GA) Nightly Test project") + } + + // Find sweeper inside + val sweeper: BuildType? = betaNightlyTestProject!!.buildTypes.find { p-> p.name == ServiceSweeperName} + if (sweeper == null) { + Assert.fail("Could not find the sweeper build in the Google (GA) Nightly Test project") + } + + // Check PACKAGE_PATH is in google-beta + val value = sweeper!!.params.findRawParam("PACKAGE_PATH")!!.value + assertEquals("./google-beta/sweeper", value) + } } diff --git a/mmv1/third_party/terraform/go.mod.erb b/mmv1/third_party/terraform/go.mod.erb index b75cb3b55059..2d2a0081a295 100644 --- a/mmv1/third_party/terraform/go.mod.erb +++ b/mmv1/third_party/terraform/go.mod.erb @@ -1,5 +1,6 @@ <% autogen_exception -%> module github.com/hashicorp/terraform-provider-google + go 1.20 require ( @@ -15,37 +16,39 @@ require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.6.0 - github.com/hashicorp/terraform-plugin-framework v1.1.1 + github.com/hashicorp/terraform-plugin-framework v1.5.0 github.com/hashicorp/terraform-plugin-framework-validators v0.9.0 - github.com/hashicorp/terraform-plugin-go v0.14.3 - github.com/hashicorp/terraform-plugin-log v0.7.0 - github.com/hashicorp/terraform-plugin-mux v0.8.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0 + github.com/hashicorp/terraform-plugin-go v0.20.0 + github.com/hashicorp/terraform-plugin-log v0.9.0 + github.com/hashicorp/terraform-plugin-mux v0.13.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/hashstructure v1.1.0 github.com/sirupsen/logrus v1.8.1 - golang.org/x/net v0.20.0 - golang.org/x/oauth2 v0.16.0 - google.golang.org/api v0.156.0 - google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 - google.golang.org/grpc v1.60.1 + golang.org/x/net v0.21.0 + golang.org/x/oauth2 v0.17.0 + google.golang.org/api v0.166.0 + google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 + google.golang.org/grpc v1.61.1 google.golang.org/protobuf v1.32.0 ) require ( bitbucket.org/creachadair/stringset v0.0.8 // indirect - cloud.google.com/go v0.111.0 // indirect - cloud.google.com/go/compute v1.23.3 // indirect + cloud.google.com/go v0.112.0 // indirect + cloud.google.com/go/compute v1.23.4 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.1.5 // indirect - cloud.google.com/go/longrunning v0.5.4 // indirect + cloud.google.com/go/iam v1.1.6 // indirect + cloud.google.com/go/longrunning v0.5.5 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/agext/levenshtein v1.2.2 // indirect - github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cloudflare/circl v1.3.3 // indirect github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect - github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect + github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 // indirect github.com/envoyproxy/go-control-plane v0.11.1 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect github.com/fatih/color v1.13.0 // indirect @@ -59,21 +62,21 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cpy v0.0.0-20211218193943-a9c933c06932 // indirect github.com/google/s2a-go v0.1.7 // indirect - github.com/google/uuid v1.5.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/googleapis/gax-go/v2 v2.12.1 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect - github.com/hashicorp/go-hclog v1.2.1 // indirect - github.com/hashicorp/go-plugin v1.4.8 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/hc-install v0.4.0 // indirect - github.com/hashicorp/hcl/v2 v2.14.1 // indirect + github.com/hashicorp/hc-install v0.6.2 // indirect + github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.17.3 // indirect - github.com/hashicorp/terraform-json v0.14.0 // indirect - github.com/hashicorp/terraform-registry-address v0.1.0 // indirect - github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect - github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect + github.com/hashicorp/terraform-exec v0.19.0 // indirect + github.com/hashicorp/terraform-json v0.18.0 // indirect + github.com/hashicorp/terraform-registry-address v0.2.3 // indirect + github.com/hashicorp/terraform-svchost v0.1.1 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect @@ -84,22 +87,23 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/run v1.0.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect - github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect - github.com/vmihailenco/tagparser v0.1.2 // indirect - github.com/zclconf/go-cty v1.11.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/zclconf/go-cty v1.14.1 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect - go.opentelemetry.io/otel v1.21.0 // indirect - go.opentelemetry.io/otel/metric v1.21.0 // indirect - go.opentelemetry.io/otel/trace v1.21.0 // indirect - golang.org/x/crypto v0.18.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect + go.opentelemetry.io/otel v1.23.0 // indirect + go.opentelemetry.io/otel/metric v1.23.0 // indirect + go.opentelemetry.io/otel/trace v1.23.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect + google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/mmv1/third_party/terraform/go.sum b/mmv1/third_party/terraform/go.sum index ab792bb42cf6..70ac88701502 100644 --- a/mmv1/third_party/terraform/go.sum +++ b/mmv1/third_party/terraform/go.sum @@ -1,40 +1,34 @@ bitbucket.org/creachadair/stringset v0.0.8 h1:gQqe4vs8XWgMyijfyKE6K8o4TcyGGrRXe0JvHgx5H+M= bitbucket.org/creachadair/stringset v0.0.8/go.mod h1:AgthVMyMxC/6FK1KBJ2ALdqkZObGN8hOetgpwXyMn34= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM= -cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/bigtable v1.19.0 h1:wiq9LT0kukfInzvy1joMDijCw/OD1UChpSbORXYn0LI= cloud.google.com/go/bigtable v1.19.0/go.mod h1:xl5kPa8PTkJjdBxg6qdGH88464nNqmbISHSRU+D2yFE= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +cloud.google.com/go/compute v1.23.4 h1:EBT9Nw4q3zyE7G45Wvv3MzolIrCJEuHys5muLY0wvAw= +cloud.google.com/go/compute v1.23.4/go.mod h1:/EJMj55asU6kAFnuZET8zqgwgJ9FvXWXOkkfQZa4ioI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= -cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= -cloud.google.com/go/longrunning v0.5.4 h1:w8xEcbZodnA2BbW6sVirkkoC+1gP8wS57EUUgGS0GVg= -cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= +cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg= +cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/GoogleCloudPlatform/declarative-resource-client-library v1.62.0 h1:s4Y6r6RrYLBnqosGXLwR0h1Gqr0VT3wgd6rqvHsD9OE= github.com/GoogleCloudPlatform/declarative-resource-client-library v1.62.0/go.mod h1:pL2Qt5HT+x6xrTd806oMiM3awW6kNIXB/iiuClz6m6k= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= -github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I= -github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= -github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= -github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= 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= @@ -43,21 +37,22 @@ github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91 github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creachadair/staticfile v0.1.2/go.mod h1:a3qySzCIXEprDGxk6tSxSI+dBBdLzqeBOMhZ+o2d3pM= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 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= @@ -70,20 +65,13 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/gammazero/deque v0.0.0-20180920172122-f6adf94963e4 h1:R+19WKQClnfMXS60cP5BmMe1wjZ4u0evY2p2Ar0ZTXo= github.com/gammazero/deque v0.0.0-20180920172122-f6adf94963e4/go.mod h1:GeIq9qoE43YdGnDXURnmKTnGg15pQz4mYkXSTChbneI= github.com/gammazero/workerpool v0.0.0-20181230203049-86a96b5d5d92 h1:EipXK6U05IQ2wtuFRn4k3h0+2lXypzItoXGVyf4r9Io= github.com/gammazero/workerpool v0.0.0-20181230203049-86a96b5d5d92/go.mod h1:w9RqFVO2BM3xwWEcAB8Fwp0OviTBBEiRmSBDfbXnd3w= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= -github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= -github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= -github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= -github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-git/v5 v5.10.1 h1:tu8/D8i+TWxgKpzQ3Vc43e+kkhXqtsZCKI/egajKnxk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -104,10 +92,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -127,8 +113,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cpy v0.0.0-20211218193943-a9c933c06932 h1:5/4TSDzpDnHQ8rKEEQBjRlYx77mHOvXu08oGchxej7o= @@ -136,12 +120,12 @@ github.com/google/go-cpy v0.0.0-20211218193943-a9c933c06932/go.mod h1:cC6EdPbj/1 github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/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/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/googleapis/gax-go/v2 v2.12.1 h1:9F8GV9r9ztXyAi00gsMQHNoF51xPZm8uj1dpYt2ZETM= +github.com/googleapis/gax-go/v2 v2.12.1/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= @@ -149,75 +133,63 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= -github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= -github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= -github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= +github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.4.0 h1:cZkRFr1WVa0Ty6x5fTvL1TuO1flul231rWkGH92oYYk= -github.com/hashicorp/hc-install v0.4.0/go.mod h1:5d155H8EC5ewegao9A4PUTMNPZaq+TbOzkJJZ4vrXeI= -github.com/hashicorp/hcl/v2 v2.14.1 h1:x0BpjfZ+CYdbiz+8yZTQ+gdLO7IXvOut7Da+XJayx34= -github.com/hashicorp/hcl/v2 v2.14.1/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= +github.com/hashicorp/hc-install v0.6.2 h1:V1k+Vraqz4olgZ9UzKiAcbman9i9scg9GgSt/U3mw/M= +github.com/hashicorp/hc-install v0.6.2/go.mod h1:2JBpd+NCFKiHiu/yYCGaPyPHhZLxXTpz8oreHa/a3Ps= +github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= +github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/terraform-exec v0.17.3 h1:MX14Kvnka/oWGmIkyuyvL6POx25ZmKrjlaclkx3eErU= -github.com/hashicorp/terraform-exec v0.17.3/go.mod h1:+NELG0EqQekJzhvikkeQsOAZpsw0cv/03rbeQJqscAI= -github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s= -github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM= -github.com/hashicorp/terraform-plugin-framework v1.1.1 h1:PbnEKHsIU8KTTzoztHQGgjZUWx7Kk8uGtpGMMc1p+oI= -github.com/hashicorp/terraform-plugin-framework v1.1.1/go.mod h1:DyZPxQA+4OKK5ELxFIIcqggcszqdWWUpTLPHAhS/tkY= +github.com/hashicorp/terraform-exec v0.19.0 h1:FpqZ6n50Tk95mItTSS9BjeOVUb4eg81SpgVtZNNtFSM= +github.com/hashicorp/terraform-exec v0.19.0/go.mod h1:tbxUpe3JKruE9Cuf65mycSIT8KiNPZ0FkuTE3H4urQg= +github.com/hashicorp/terraform-json v0.18.0 h1:pCjgJEqqDESv4y0Tzdqfxr/edOIGkjs8keY42xfNBwU= +github.com/hashicorp/terraform-json v0.18.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= +github.com/hashicorp/terraform-plugin-framework v1.5.0 h1:8kcvqJs/x6QyOFSdeAyEgsenVOUeC/IyKpi2ul4fjTg= +github.com/hashicorp/terraform-plugin-framework v1.5.0/go.mod h1:6waavirukIlFpVpthbGd2PUNYaFedB0RwW3MDzJ/rtc= github.com/hashicorp/terraform-plugin-framework-validators v0.9.0 h1:LYz4bXh3t7bTEydXOmPDPupRRnA480B/9+jV8yZvxBA= github.com/hashicorp/terraform-plugin-framework-validators v0.9.0/go.mod h1:+BVERsnfdlhYR2YkXMBtPnmn9UsL19U3qUtSZ+Y/5MY= -github.com/hashicorp/terraform-plugin-go v0.14.3 h1:nlnJ1GXKdMwsC8g1Nh05tK2wsC3+3BL/DBBxFEki+j0= -github.com/hashicorp/terraform-plugin-go v0.14.3/go.mod h1:7ees7DMZ263q8wQ6E4RdIdR6nHHJtrdt4ogX5lPkX1A= -github.com/hashicorp/terraform-plugin-log v0.7.0 h1:SDxJUyT8TwN4l5b5/VkiTIaQgY6R+Y2BQ0sRZftGKQs= -github.com/hashicorp/terraform-plugin-log v0.7.0/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4= -github.com/hashicorp/terraform-plugin-mux v0.8.0 h1:WCTP66mZ+iIaIrCNJnjPEYnVjawTshnDJu12BcXK1EI= -github.com/hashicorp/terraform-plugin-mux v0.8.0/go.mod h1:vdW0daEi8Kd4RFJmet5Ot+SIVB/B8SwQVJiYKQwdCy8= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0 h1:FtCLTiTcykdsURXPt/ku7fYXm3y19nbzbZcUxHx9RbI= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0/go.mod h1:80wf5oad1tW+oLnbXS4UTYmDCrl7BuN1Q+IA91X1a4Y= -github.com/hashicorp/terraform-registry-address v0.1.0 h1:W6JkV9wbum+m516rCl5/NjKxCyTVaaUBbzYcMzBDO3U= -github.com/hashicorp/terraform-registry-address v0.1.0/go.mod h1:EnyO2jYO6j29DTHbJcm00E5nQTFeTtyZH3H5ycydQ5A= -github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= -github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/hashicorp/terraform-plugin-go v0.20.0 h1:oqvoUlL+2EUbKNsJbIt3zqqZ7wi6lzn4ufkn/UA51xQ= +github.com/hashicorp/terraform-plugin-go v0.20.0/go.mod h1:Rr8LBdMlY53a3Z/HpP+ZU3/xCDqtKNCkeI9qOyT10QE= +github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= +github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= +github.com/hashicorp/terraform-plugin-mux v0.13.0 h1:79U401/3nd8CWwDGtTHc8F3miSCAS9XGtVarxSTDgwA= +github.com/hashicorp/terraform-plugin-mux v0.13.0/go.mod h1:Ndv0FtwDG2ogzH59y64f2NYimFJ6I0smRgFUKfm6dyQ= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 h1:Bl3e2ei2j/Z3Hc2HIS15Gal2KMKyLAZ2om1HCEvK6es= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0/go.mod h1:i2C41tszDjiWfziPQDL5R/f3Zp0gahXe5No/MIO9rCE= +github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= +github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= +github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= +github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -238,30 +210,25 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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= -github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -270,48 +237,40 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= -github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= -github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= -github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= -github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= -github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= -github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0= -github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= -github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= +github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA= +github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= 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.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= -go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 h1:P+/g8GpuJGYbOp2tAdKrIPUX9JO02q8Q0YNlHolpibA= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA= +go.opentelemetry.io/otel v1.23.0 h1:Df0pqjqExIywbMCMTxkAwzjLZtRf+bBKLbUcpxO2C9E= +go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFufObyB0= +go.opentelemetry.io/otel/metric v1.23.0 h1:pazkx7ss4LFVVYSxYew7L5I6qvLXHA0Ap2pwV+9Cnpo= +go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/trace v1.23.0 h1:37Ik5Ib7xfYVb4V1UtnT97T1jI+AoIYkJyPkuL4iJgI= +go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk= 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= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -319,72 +278,72 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.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.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= 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.5/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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= @@ -398,35 +357,36 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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.156.0 h1:yloYcGbBtVYjLKQe4enCunxvwn3s2w/XPrrhVf6MsvQ= -google.golang.org/api v0.156.0/go.mod h1:bUSmn4KFO0Q+69zo9CNIDp4Psi6BqM0np0CbzKRSiSY= +google.golang.org/api v0.166.0 h1:6m4NUwrZYhAaVIHZWxaKjw1L1vNAjtMwORmKRyEEo24= +google.golang.org/api v0.166.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA= 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.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 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-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 h1:s1w3X6gQxwrLEpxnLd/qXTVLgQE2yXwaOaoa6IlY/+o= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 h1:g/4bk7P6TPMkAUbUhquq98xey1slwvuVJPosdBqYJlU= +google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M= +google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 h1:x9PwdEgd11LgK+orcck69WVRo7DezSO4VUMPI4xpc8A= +google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk= 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.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= +google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= 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= @@ -441,16 +401,10 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb b/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb index e37501f6d8bd..33109889347c 100644 --- a/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb +++ b/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb @@ -60,6 +60,7 @@ var handwrittenDatasources = map[string]*schema.Resource{ "google_compute_default_service_account": compute.DataSourceGoogleComputeDefaultServiceAccount(), "google_compute_disk": compute.DataSourceGoogleComputeDisk(), "google_compute_forwarding_rule": compute.DataSourceGoogleComputeForwardingRule(), + "google_compute_forwarding_rules": compute.DataSourceGoogleComputeForwardingRules(), "google_compute_global_address": compute.DataSourceGoogleComputeGlobalAddress(), "google_compute_global_forwarding_rule": compute.DataSourceGoogleComputeGlobalForwardingRule(), "google_compute_ha_vpn_gateway": compute.DataSourceGoogleComputeHaVpnGateway(), diff --git a/mmv1/third_party/terraform/services/bigquery/iam_bigquery_dataset.go b/mmv1/third_party/terraform/services/bigquery/iam_bigquery_dataset.go index f6966ac4d2d2..740da92602ad 100644 --- a/mmv1/third_party/terraform/services/bigquery/iam_bigquery_dataset.go +++ b/mmv1/third_party/terraform/services/bigquery/iam_bigquery_dataset.go @@ -214,7 +214,6 @@ func iamMemberToAccess(member string) (string, string, error) { if strings.HasPrefix(member, "deleted:") { return "", "", fmt.Errorf("BigQuery Dataset IAM member is deleted: %s", member) } - pieces := strings.SplitN(member, ":", 2) if len(pieces) > 1 { switch pieces[0] { @@ -222,19 +221,19 @@ func iamMemberToAccess(member string) (string, string, error) { return "groupByEmail", pieces[1], nil case "domain": return "domain", pieces[1], nil + case "iamMember": + return "iamMember", pieces[1], nil case "user": return "userByEmail", pieces[1], nil case "serviceAccount": return "userByEmail", pieces[1], nil - default: - return "", "", fmt.Errorf("Failed to parse BigQuery Dataset IAM member type: %s", member) } } if member == "projectOwners" || member == "projectReaders" || member == "projectWriters" || member == "allAuthenticatedUsers" { // These are special BigQuery Dataset permissions return "specialGroup", member, nil } - return "iamMember", member, nil + return "", "", fmt.Errorf("Failed to parse BigQuery Dataset IAM member type: %s", member) } func accessToIamMember(access map[string]interface{}) (string, error) { @@ -249,7 +248,7 @@ func accessToIamMember(access map[string]interface{}) (string, error) { return member.(string), nil } if member, ok := access["iamMember"]; ok { - return member.(string), nil + return fmt.Sprintf("iamMember:%s", member.(string)), nil } if _, ok := access["view"]; ok { // view does not map to an IAM member, use access instead diff --git a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_iam_member_test.go b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_iam_member_test.go index d138457f702d..0ab43e371d9d 100644 --- a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_iam_member_test.go +++ b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_dataset_iam_member_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-provider-google/google/envvar" ) -func TestAccBigqueryDatasetIamMember_basic(t *testing.T) { +func TestAccBigqueryDatasetIamMember_serviceAccount(t *testing.T) { t.Parallel() datasetID := fmt.Sprintf("tf_test_%s", acctest.RandString(t, 10)) @@ -25,27 +25,55 @@ func TestAccBigqueryDatasetIamMember_basic(t *testing.T) { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), Steps: []resource.TestStep{ { - Config: testAccBigqueryDatasetIamMember_basic(datasetID, saID), + Config: testAccBigqueryDatasetIamMember_serviceAccount(datasetID, saID), Check: testAccCheckBigQueryDatasetAccessPresent(t, "google_bigquery_dataset.dataset", expected), }, { // Destroy step instead of CheckDestroy so we can check the access is removed without deleting the dataset - Config: testAccBigqueryDatasetIamMember_destroy(datasetID, "dataset"), + Config: testAccBigqueryDatasetIamMember_destroy(datasetID), Check: testAccCheckBigQueryDatasetAccessAbsent(t, "google_bigquery_dataset.dataset", expected), }, }, }) } -func testAccBigqueryDatasetIamMember_destroy(datasetID, rs string) string { +func TestAccBigqueryDatasetIamMember_iamMember(t *testing.T) { + t.Parallel() + + datasetID := fmt.Sprintf("tf_test_%s", acctest.RandString(t, 10)) + wifIDs := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)) + + expected := map[string]interface{}{ + "role": "roles/viewer", + "iamMember": fmt.Sprintf("principal://iam.googleapis.com/projects/%s/locations/global/workloadIdentityPools/%s/subject/test", envvar.GetTestProjectNumberFromEnv(), wifIDs), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccBigqueryDatasetIamMember_iamMember(datasetID, wifIDs), + Check: testAccCheckBigQueryDatasetAccessPresent(t, "google_bigquery_dataset.dataset", expected), + }, + { + // Destroy step instead of CheckDestroy so we can check the access is removed without deleting the dataset + Config: testAccBigqueryDatasetIamMember_destroy(datasetID), + Check: testAccCheckBigQueryDatasetAccessAbsent(t, "google_bigquery_dataset.dataset", expected), + }, + }, + }) +} + +func testAccBigqueryDatasetIamMember_destroy(datasetID string) string { return fmt.Sprintf(` -resource "google_bigquery_dataset" "%s" { +resource "google_bigquery_dataset" "dataset" { dataset_id = "%s" } -`, rs, datasetID) +`, datasetID) } -func testAccBigqueryDatasetIamMember_basic(datasetID, saID string) string { +func testAccBigqueryDatasetIamMember_serviceAccount(datasetID, saID string) string { return fmt.Sprintf(` resource "google_bigquery_dataset_iam_member" "access" { dataset_id = google_bigquery_dataset.dataset.dataset_id @@ -62,3 +90,32 @@ resource "google_service_account" "bqviewer" { } `, datasetID, saID) } + +func testAccBigqueryDatasetIamMember_iamMember(datasetID, wifIDs string) string { + return fmt.Sprintf(` +resource "google_bigquery_dataset_iam_member" "access" { + dataset_id = google_bigquery_dataset.dataset.dataset_id + role = "roles/viewer" + member = "iamMember:principal://iam.googleapis.com/${google_iam_workload_identity_pool.wif_pool.name}/subject/test" +} + +resource "google_bigquery_dataset" "dataset" { + dataset_id = "%s" +} + +resource "google_iam_workload_identity_pool" "wif_pool" { + workload_identity_pool_id = "%s" +} + +resource "google_iam_workload_identity_pool_provider" "wif_provider" { + workload_identity_pool_id = google_iam_workload_identity_pool.wif_pool.workload_identity_pool_id + workload_identity_pool_provider_id = "%s" + attribute_mapping = { + "google.subject" = "assertion.sub" + } + oidc { + issuer_uri = "https://issuer-uri.com" + } +} +`, datasetID, wifIDs, wifIDs) +} diff --git a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_routine_test.go b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_routine_test.go index bd106a6f079b..00f5763b505b 100644 --- a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_routine_test.go +++ b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_routine_test.go @@ -173,3 +173,171 @@ resource "google_bigquery_routine" "spark_jar" { } `, context) } + +func TestAccBigQueryRoutine_bigQueryRoutineRemoteFunction(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + "zip_path": "./test-fixtures/function-source.zip", + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckBigQueryRoutineDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccBigQueryRoutine_bigQueryRoutineRemoteFunction(context), + }, + { + ResourceName: "google_bigquery_routine.remote_function_routine", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccBigQueryRoutine_bigQueryRoutineRemoteFunction_Update(context), + }, + { + ResourceName: "google_bigquery_routine.remote_function_routine", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccBigQueryRoutine_bigQueryRoutineRemoteFunction(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_storage_bucket" "default" { + name = "%{random_suffix}-gcf-source" + location = "US" + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_object" "object" { + name = "function-source.zip" + bucket = google_storage_bucket.default.name + source = "%{zip_path}" +} + +resource "google_cloudfunctions2_function" "default" { + name = "function-v2-0" + location = "us-central1" + description = "a new function" + + build_config { + runtime = "nodejs18" + entry_point = "helloHttp" + source { + storage_source { + bucket = google_storage_bucket.default.name + object = google_storage_bucket_object.object.name + } + } + } + + service_config { + max_instance_count = 1 + available_memory = "256M" + timeout_seconds = 60 + } +} + +resource "google_bigquery_connection" "test" { + connection_id = "tf_test_connection_id%{random_suffix}" + location = "US" + cloud_resource { } +} + +resource "google_bigquery_dataset" "test" { + dataset_id = "tf_test_dataset_id%{random_suffix}" +} + +resource "google_bigquery_routine" "remote_function_routine" { + dataset_id = "${google_bigquery_dataset.test.dataset_id}" + routine_id = "tf_test_routine_id%{random_suffix}" + routine_type = "SCALAR_FUNCTION" + definition_body = "" + + return_type = "{\"typeKind\" : \"STRING\"}" + + remote_function_options { + endpoint = google_cloudfunctions2_function.default.service_config[0].uri + connection = "${google_bigquery_connection.test.name}" + max_batching_rows = "10" + user_defined_context = { + "z": "1.5", + } + } +} +`, context) +} + +func testAccBigQueryRoutine_bigQueryRoutineRemoteFunction_Update(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_storage_bucket" "default" { + name = "%{random_suffix}-gcf-source" + location = "US" + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_object" "object" { + name = "function-source.zip" + bucket = google_storage_bucket.default.name + source = "%{zip_path}" +} + +resource "google_cloudfunctions2_function" "default2" { + name = "function-v2-1" + location = "us-central1" + description = "a new new function" + + build_config { + runtime = "nodejs18" + entry_point = "helloHttp" + source { + storage_source { + bucket = google_storage_bucket.default.name + object = google_storage_bucket_object.object.name + } + } + } + + service_config { + max_instance_count = 1 + available_memory = "256M" + timeout_seconds = 60 + } +} + +resource "google_bigquery_connection" "test2" { + connection_id = "tf_test_connection2_id%{random_suffix}" + location = "US" + cloud_resource { } +} + +resource "google_bigquery_dataset" "test" { + dataset_id = "tf_test_dataset_id%{random_suffix}" +} + +resource "google_bigquery_routine" "remote_function_routine" { + dataset_id = "${google_bigquery_dataset.test.dataset_id}" + routine_id = "tf_test_routine_id%{random_suffix}" + routine_type = "SCALAR_FUNCTION" + definition_body = "" + + return_type = "{\"typeKind\" : \"STRING\"}" + + remote_function_options { + endpoint = google_cloudfunctions2_function.default2.service_config[0].uri + connection = "${google_bigquery_connection.test2.name}" + max_batching_rows = "5" + user_defined_context = { + "z": "1.2", + "w": "test", + } + } +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/bigquery/test-fixtures/function-source.zip b/mmv1/third_party/terraform/services/bigquery/test-fixtures/function-source.zip new file mode 100644 index 000000000000..1cb571888ef5 Binary files /dev/null and b/mmv1/third_party/terraform/services/bigquery/test-fixtures/function-source.zip differ diff --git a/mmv1/third_party/terraform/services/certificatemanager/resource_certificate_manager_dns_authorization_upgrade_test.go b/mmv1/third_party/terraform/services/certificatemanager/resource_certificate_manager_dns_authorization_upgrade_test.go new file mode 100644 index 000000000000..293628ee322b --- /dev/null +++ b/mmv1/third_party/terraform/services/certificatemanager/resource_certificate_manager_dns_authorization_upgrade_test.go @@ -0,0 +1,78 @@ +package certificatemanager_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +// Tests schema version migration by creating a dns authorization with an old version of the provider (5.15.0) +// and then updating it with the current version the provider. +func TestAccCertificateManagerDnsAuthorization_migration(t *testing.T) { + acctest.SkipIfVcr(t) + t.Parallel() + name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t)) + + oldVersion := map[string]resource.ExternalProvider{ + "google": { + VersionConstraint: "5.15.0", // a version that doesn't support location yet. + Source: "registry.terraform.io/hashicorp/google", + }, + } + newVersion := map[string]func() (*schema.Provider, error){ + "mynewprovider": func() (*schema.Provider, error) { return acctest.TestAccProviders["google"], nil }, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + CheckDestroy: testAccCheckCertificateManagerDnsAuthorizationDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: dnsAuthorizationResourceConfig(name), + ExternalProviders: oldVersion, + }, + { + ResourceName: "google_certificate_manager_dns_authorization.default", + ImportState: true, + ImportStateVerifyIgnore: []string{"location"}, + ExternalProviders: oldVersion, + }, + { + Config: dnsAuthorizationResourceConfigUpdated(name), + ProviderFactories: newVersion, + }, + { + ResourceName: "google_certificate_manager_dns_authorization.default", + ImportState: true, + ImportStateVerifyIgnore: []string{"location"}, + ProviderFactories: newVersion, + }, + }, + }) +} + +func dnsAuthorizationResourceConfig(name string) string { + return fmt.Sprintf(` + resource "google_certificate_manager_dns_authorization" "default" { + name = "%s" + description = "The default dns" + domain = "domain.hashicorptest.com" + } + `, name) +} + +func dnsAuthorizationResourceConfigUpdated(name string) string { + return fmt.Sprintf(` + provider "mynewprovider" {} + + resource "google_certificate_manager_dns_authorization" "default" { + provider = mynewprovider + name = "%s" + description = "The migrated default dns" + domain = "domain.hashicorptest.com" + } + `, name) +} diff --git a/mmv1/third_party/terraform/services/clouddeploy/resource_clouddeploy_automation_test.go.erb b/mmv1/third_party/terraform/services/clouddeploy/resource_clouddeploy_automation_test.go similarity index 57% rename from mmv1/third_party/terraform/services/clouddeploy/resource_clouddeploy_automation_test.go.erb rename to mmv1/third_party/terraform/services/clouddeploy/resource_clouddeploy_automation_test.go index 601f55640516..82f1fe3a0e5c 100644 --- a/mmv1/third_party/terraform/services/clouddeploy/resource_clouddeploy_automation_test.go.erb +++ b/mmv1/third_party/terraform/services/clouddeploy/resource_clouddeploy_automation_test.go @@ -1,56 +1,52 @@ -<% autogen_exception -%> package clouddeploy_test - -<% unless version == 'ga' -%> import ( - "testing" + "testing" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-provider-google/google/acctest" - "github.com/hashicorp/terraform-provider-google/google/envvar" + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" ) func TestAccClouddeployAutomation_update(t *testing.T) { - t.Parallel() + t.Parallel() - context := map[string]interface{}{ - "service_account": envvar.GetTestServiceAccountFromEnv(t), - "random_suffix": acctest.RandString(t, 10), - } + context := map[string]interface{}{ + "service_account": envvar.GetTestServiceAccountFromEnv(t), + "random_suffix": acctest.RandString(t, 10), + } - acctest.VcrTest(t, resource.TestCase{ - PreCheck: func() { acctest.AccTestPreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), - CheckDestroy: testAccCheckClouddeployAutomationDestroyProducer(t), - Steps: []resource.TestStep{ - { - Config: testAccClouddeployAutomation_basic(context), - }, - { - ResourceName: "google_clouddeploy_automation.automation", - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"location", "delivery_pipeline", "annotations", "labels", "terraform_labels"}, - }, - { - Config: testAccClouddeployAutomation_update(context), - }, - { - ResourceName: "google_clouddeploy_automation.automation", - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"location", "delivery_pipeline", "annotations", "labels", "terraform_labels"}, - }, - }, - }) + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckClouddeployAutomationDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccClouddeployAutomation_basic(context), + }, + { + ResourceName: "google_clouddeploy_automation.automation", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "delivery_pipeline", "annotations", "labels", "terraform_labels"}, + }, + { + Config: testAccClouddeployAutomation_update(context), + }, + { + ResourceName: "google_clouddeploy_automation.automation", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "delivery_pipeline", "annotations", "labels", "terraform_labels"}, + }, + }, + }) } func testAccClouddeployAutomation_basic(context map[string]interface{}) string { - return acctest.Nprintf(` + return acctest.Nprintf(` resource "google_clouddeploy_automation" "automation" { - provider = google-beta name = "tf-test-cd-automation%{random_suffix}" location = "us-central1" delivery_pipeline = google_clouddeploy_delivery_pipeline.pipeline.name @@ -71,7 +67,6 @@ resource "google_clouddeploy_automation" "automation" { } resource "google_clouddeploy_delivery_pipeline" "pipeline" { - provider = google-beta name = "tf-test-cd-pipeline%{random_suffix}" location = "us-central1" serial_pipeline { @@ -85,10 +80,9 @@ resource "google_clouddeploy_delivery_pipeline" "pipeline" { } func testAccClouddeployAutomation_update(context map[string]interface{}) string { - return acctest.Nprintf(` + return acctest.Nprintf(` resource "google_clouddeploy_automation" "automation" { - provider = google-beta name = "tf-test-cd-automation%{random_suffix}" location = "us-central1" delivery_pipeline = google_clouddeploy_delivery_pipeline.pipeline.name @@ -129,7 +123,6 @@ resource "google_clouddeploy_automation" "automation" { } resource "google_clouddeploy_delivery_pipeline" "pipeline" { - provider = google-beta name = "tf-test-cd-pipeline%{random_suffix}" location = "us-central1" serial_pipeline { @@ -141,5 +134,3 @@ resource "google_clouddeploy_delivery_pipeline" "pipeline" { } `, context) } -<% end -%> - diff --git a/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function.go b/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function.go index ac86bac73dbf..b6d3d77906e2 100644 --- a/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function.go +++ b/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function.go @@ -490,6 +490,11 @@ func ResourceCloudFunctionsFunction() *schema.Resource { Computed: true, Description: `Describes the current stage of a deployment.`, }, + "version_id": { + Type: schema.TypeString, + Computed: true, + Description: `The version identifier of the Cloud Function. Each deployment attempt results in a new version of a function being created.`, + }, }, UseJSONNumber: true, } @@ -786,6 +791,9 @@ func resourceCloudFunctionsRead(d *schema.ResourceData, meta interface{}) error if err := d.Set("project", cloudFuncId.Project); err != nil { return fmt.Errorf("Error setting project: %s", err) } + if err := d.Set("version_id", strconv.FormatInt(function.VersionId, 10)); err != nil { + return fmt.Errorf("Error setting version_id: %s", err) + } return nil } diff --git a/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function_test.go.erb b/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function_test.go.erb index 0c2be3a3a1a3..61a267c0fcd8 100644 --- a/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function_test.go.erb +++ b/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function_test.go.erb @@ -71,6 +71,8 @@ func TestAccCloudFunctionsFunction_basic(t *testing.T) { "entry_point", "helloGET"), resource.TestCheckResourceAttr(funcResourceName, "trigger_http", "true"), + resource.TestCheckResourceAttr(funcResourceName, + "version_id", "1"), testAccCloudFunctionsFunctionHasLabel("my-label", "my-label-value", &function), testAccCloudFunctionsFunctionHasEnvironmentVariable("TEST_ENV_VARIABLE", "test-env-variable-value", &function), @@ -110,6 +112,8 @@ func TestAccCloudFunctionsFunction_update(t *testing.T) { t, funcResourceName, &function), resource.TestCheckResourceAttr(funcResourceName, "available_memory_mb", "128"), + resource.TestCheckResourceAttr(funcResourceName, + "version_id", "1"), testAccCloudFunctionsFunctionHasLabel("my-label", "my-label-value", &function), ), }, @@ -138,6 +142,8 @@ func TestAccCloudFunctionsFunction_update(t *testing.T) { "min_instances", "5"), resource.TestCheckResourceAttr(funcResourceName, "ingress_settings", "ALLOW_ALL"), + resource.TestCheckResourceAttr(funcResourceName, + "version_id", "2"), testAccCloudFunctionsFunctionHasLabel("my-label", "my-updated-label-value", &function), testAccCloudFunctionsFunctionHasLabel("a-new-label", "a-new-label-value", &function), testAccCloudFunctionsFunctionHasEnvironmentVariable("TEST_ENV_VARIABLE", diff --git a/mmv1/third_party/terraform/services/composer/resource_composer_environment.go.erb b/mmv1/third_party/terraform/services/composer/resource_composer_environment.go.erb index 8dae1cc3ab12..e192b510fa65 100644 --- a/mmv1/third_party/terraform/services/composer/resource_composer_environment.go.erb +++ b/mmv1/third_party/terraform/services/composer/resource_composer_environment.go.erb @@ -7,6 +7,7 @@ import ( "regexp" "strings" "time" + "context" "github.com/hashicorp/go-version" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" @@ -27,7 +28,7 @@ import ( const ( composerEnvironmentEnvVariablesRegexp = "[a-zA-Z_][a-zA-Z0-9_]*." composerEnvironmentReservedAirflowEnvVarRegexp = "AIRFLOW__[A-Z0-9_]+__[A-Z0-9_]+" - composerEnvironmentVersionRegexp = `composer-(([0-9]+)(\.[0-9]+\.[0-9]+(-preview\.[0-9]+)?)?|latest)-airflow-(([0-9]+)((\.[0-9]+)(\.[0-9]+)?)?)` + composerEnvironmentVersionRegexp = `composer-(([0-9]+)(\.[0-9]+\.[0-9]+(-preview\.[0-9]+)?)?|latest)-airflow-(([0-9]+)((\.[0-9]+)(\.[0-9]+)?)?(-build\.[0-9]+)?)` ) var composerEnvironmentReservedEnvVar = map[string]struct{}{ @@ -168,6 +169,12 @@ func ResourceComposerEnvironment() *schema.Resource { tpgresource.DefaultProviderProject, tpgresource.DefaultProviderRegion, tpgresource.SetLabelsDiff, +<% unless version == "ga" -%> + customdiff.ForceNewIf("config.0.node_config.0.network", forceNewCustomDiff("config.0.node_config.0.network")), + customdiff.ForceNewIf("config.0.node_config.0.subnetwork", forceNewCustomDiff("config.0.node_config.0.subnetwork")), + customdiff.ValidateChange("config.0.software_config.0.image_version", imageVersionChangeValidationFunc), + versionValidationCustomizeDiffFunc, +<% end -%> ), Schema: map[string]*schema.Schema{ @@ -237,17 +244,37 @@ func ResourceComposerEnvironment() *schema.Resource { Type: schema.TypeString, Computed: true, Optional: true, +<% if version == "ga" -%> ForceNew: true, +<% else -%> + ForceNew: false, + ConflictsWith: []string{"config.0.node_config.0.composer_network_attachment"}, +<% end -%> DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName, Description: `The Compute Engine machine type used for cluster instances, specified as a name or relative resource name. For example: "projects/{project}/zones/{zone}/machineTypes/{machineType}". Must belong to the enclosing environment's project and region/zone. The network must belong to the environment's project. If unspecified, the "default" network ID in the environment's project is used. If a Custom Subnet Network is provided, subnetwork must also be provided.`, }, "subnetwork": { Type: schema.TypeString, Optional: true, +<% if version == "ga" -%> ForceNew: true, +<% else -%> + ForceNew: false, + Computed: true, + ConflictsWith: []string{"config.0.node_config.0.composer_network_attachment"}, +<% end -%> DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName, - Description: `The Compute Engine subnetwork to be used for machine communications, , specified as a self-link, relative resource name (e.g. "projects/{project}/regions/{region}/subnetworks/{subnetwork}"), or by name. If subnetwork is provided, network must also be provided and the subnetwork must belong to the enclosing environment's project and region.`, + Description: `The Compute Engine subnetwork to be used for machine communications, specified as a self-link, relative resource name (e.g. "projects/{project}/regions/{region}/subnetworks/{subnetwork}"), or by name. If subnetwork is provided, network must also be provided and the subnetwork must belong to the enclosing environment's project and region.`, }, +<% unless version == "ga" -%> + "composer_network_attachment": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: false, + Description: `PSC (Private Service Connect) Network entry point. Customers can pre-create the Network Attachment and point Cloud Composer environment to use. It is possible to share network attachment among many environments, provided enough IP addresses are available.`, + }, +<% end -%> "disk_size_gb": { Type: schema.TypeInt, Computed: true, @@ -1190,6 +1217,68 @@ func resourceComposerEnvironmentUpdate(d *schema.ResourceData, meta interface{}) return err } +<% unless version == "ga" -%> + noChangeErrorMessage := "Update request does not result in any change to the environment's configuration" + if d.HasChange("config.0.node_config.0.network") || d.HasChange("config.0.node_config.0.subnetwork"){ + // step 1: update with empty network and subnetwork + patchObjEmpty := &composer.Environment{ + Config: &composer.EnvironmentConfig{ + NodeConfig: &composer.NodeConfig{}, + }, + } + err = resourceComposerEnvironmentPatchField("config.nodeConfig.network,config.nodeConfig.subnetwork", userAgent, patchObjEmpty, d, tfConfig) + if err != nil && !strings.Contains(err.Error(), noChangeErrorMessage){ + return err + } + + // step 2: update with new network and subnetwork, if new values are not empty + if (config.NodeConfig.Network != "" && config.NodeConfig.Subnetwork != ""){ + patchObj := &composer.Environment{ + Config: &composer.EnvironmentConfig{ + NodeConfig: &composer.NodeConfig{}, + }, + } + if config != nil && config.NodeConfig != nil { + patchObj.Config.NodeConfig.Network = config.NodeConfig.Network + patchObj.Config.NodeConfig.Subnetwork = config.NodeConfig.Subnetwork + } + err = resourceComposerEnvironmentPatchField("config.nodeConfig.network,config.nodeConfig.subnetwork", userAgent, patchObj, d, tfConfig) + if err != nil { + return err + } + } + } + + if d.HasChange("config.0.node_config.0.composer_network_attachment") { + // step 1: update with empty composer_network_attachment + patchObjEmpty := &composer.Environment{ + Config: &composer.EnvironmentConfig{ + NodeConfig: &composer.NodeConfig{}, + }, + } + err = resourceComposerEnvironmentPatchField("config.nodeConfig.composerNetworkAttachment", userAgent, patchObjEmpty, d, tfConfig) + if err != nil && !strings.Contains(err.Error(), noChangeErrorMessage){ + return err + } + + // step 2: update with new composer_network_attachment + if (config.NodeConfig.ComposerNetworkAttachment != ""){ + patchObj := &composer.Environment{ + Config: &composer.EnvironmentConfig{ + NodeConfig: &composer.NodeConfig{}, + }, + } + if config != nil && config.NodeConfig != nil { + patchObj.Config.NodeConfig.ComposerNetworkAttachment = config.NodeConfig.ComposerNetworkAttachment + } + err = resourceComposerEnvironmentPatchField("config.nodeConfig.composerNetworkAttachment", userAgent, patchObj, d, tfConfig) + if err != nil { + return err + } + } + } +<% end -%> + <% unless version == "ga" -%> if d.HasChange("config.0.software_config.0.image_version") { patchObj := &composer.Environment{ @@ -1592,10 +1681,15 @@ func flattenComposerEnvironmentConfig(envCfg *composer.EnvironmentConfig) interf transformed["airflow_uri"] = envCfg.AirflowUri transformed["node_config"] = flattenComposerEnvironmentConfigNodeConfig(envCfg.NodeConfig) transformed["software_config"] = flattenComposerEnvironmentConfigSoftwareConfig(envCfg.SoftwareConfig) - transformed["private_environment_config"] = flattenComposerEnvironmentConfigPrivateEnvironmentConfig(envCfg.PrivateEnvironmentConfig) + imageVersion := envCfg.SoftwareConfig.ImageVersion + if !isComposer3(imageVersion){ + transformed["private_environment_config"] = flattenComposerEnvironmentConfigPrivateEnvironmentConfig(envCfg.PrivateEnvironmentConfig) + } <% unless version == "ga" -%> - transformed["enable_private_environment"] = envCfg.PrivateEnvironmentConfig.EnablePrivateEnvironment - transformed["enable_private_builds_only"] = envCfg.PrivateEnvironmentConfig.EnablePrivateBuildsOnly + if isComposer3(imageVersion) && envCfg.PrivateEnvironmentConfig != nil { + transformed["enable_private_environment"] = envCfg.PrivateEnvironmentConfig.EnablePrivateEnvironment + transformed["enable_private_builds_only"] = envCfg.PrivateEnvironmentConfig.EnablePrivateBuildsOnly + } <% end -%> transformed["web_server_network_access_control"] = flattenComposerEnvironmentConfigWebServerNetworkAccessControl(envCfg.WebServerNetworkAccessControl) transformed["database_config"] = flattenComposerEnvironmentConfigDatabaseConfig(envCfg.DatabaseConfig) @@ -1803,7 +1897,9 @@ func flattenComposerEnvironmentConfigWorkloadsConfig(workloadsConfig *composer.W transformed["web_server"] = []interface{}{transformedWebServer} transformed["worker"] = []interface{}{transformedWorker} <% unless version == "ga" -%> - transformed["dag_processor"] = []interface{}{transformedDagProcessor} + if transformedDagProcessor != nil { + transformed["dag_processor"] = []interface{}{transformedDagProcessor} + } <% end -%> @@ -1841,6 +1937,9 @@ func flattenComposerEnvironmentConfigNodeConfig(nodeCfg *composer.NodeConfig) in transformed["machine_type"] = nodeCfg.MachineType transformed["network"] = nodeCfg.Network transformed["subnetwork"] = nodeCfg.Subnetwork +<% unless version == "ga" -%> + transformed["composer_network_attachment"] = nodeCfg.ComposerNetworkAttachment +<% end -%> transformed["disk_size_gb"] = nodeCfg.DiskSizeGb transformed["service_account"] = nodeCfg.ServiceAccount transformed["oauth_scopes"] = flattenComposerEnvironmentConfigNodeConfigOauthScopes(nodeCfg.OauthScopes) @@ -1982,7 +2081,8 @@ func expandComposerEnvironmentConfig(v interface{}, d *schema.ResourceData, conf composer.PrivateEnvironmentConfig.EnablePrivateEnvironment in API. Check image version to avoid overriding EnablePrivateEnvironment in case of other versions. */ - if isComposer3(d, config) { + imageVersion := d.Get("config.0.software_config.0.image_version").(string) + if isComposer3(imageVersion) { transformed.PrivateEnvironmentConfig = &composer.PrivateEnvironmentConfig{} if enablePrivateEnvironmentRaw, ok := original["enable_private_environment"]; ok { transformed.PrivateEnvironmentConfig.EnablePrivateEnvironment = enablePrivateEnvironmentRaw.(bool) @@ -2457,6 +2557,13 @@ func expandComposerEnvironmentConfigNodeConfig(v interface{}, d *schema.Resource } transformed.Subnetwork = transformedSubnetwork } + +<% unless version == "ga" -%> + if v, ok := original["composer_network_attachment"]; ok { + transformed.ComposerNetworkAttachment = v.(string) + } +<% end -%> + transformedIPAllocationPolicy, err := expandComposerEnvironmentIPAllocationPolicy(original["ip_allocation_policy"], d, config) if err != nil { return nil, err @@ -2827,7 +2934,7 @@ func composerImageVersionDiffSuppress(_, old, new string, _ *schema.ResourceData versionRe := regexp.MustCompile(composerEnvironmentVersionRegexp) oldVersions := versionRe.FindStringSubmatch(old) newVersions := versionRe.FindStringSubmatch(new) - if oldVersions == nil || len(oldVersions) < 10 { + if oldVersions == nil || len(oldVersions) < 11 { // Somehow one of the versions didn't match the regexp or didn't // have values in the capturing groups. In that case, fall back to // an equality check. @@ -2836,7 +2943,7 @@ func composerImageVersionDiffSuppress(_, old, new string, _ *schema.ResourceData } return old == new } - if newVersions == nil || len(newVersions) < 10 { + if newVersions == nil || len(newVersions) < 11 { // Somehow one of the versions didn't match the regexp or didn't // have values in the capturing groups. In that case, fall back to // an equality check. @@ -2849,9 +2956,11 @@ func composerImageVersionDiffSuppress(_, old, new string, _ *schema.ResourceData oldAirflow := oldVersions[5] oldAirflowMajor := oldVersions[6] oldAirflowMajorMinor := oldVersions[6] + oldVersions[8] + oldAirflowMajorMinorPatch := oldVersions[6] + oldVersions[8] + oldVersions[9] newAirflow := newVersions[5] newAirflowMajor := newVersions[6] newAirflowMajorMinor := newVersions[6] + newVersions[8] + newAirflowMajorMinorPatch := newVersions[6] + newVersions[8] + newVersions[9] // Check Airflow versions. if oldAirflow == oldAirflowMajor || newAirflow == newAirflowMajor { // If one of the Airflow versions specifies only major version @@ -2873,8 +2982,18 @@ func composerImageVersionDiffSuppress(_, old, new string, _ *schema.ResourceData if !eq { return false } + } else if oldAirflow == oldAirflowMajorMinorPatch || newAirflow == newAirflowMajorMinorPatch { + // If one of the Airflow versions specifies only major, minor and patch version + // (like 1.10.15), we can only compare major, minor and patch versions. + eq, err := versionsEqual(oldAirflowMajorMinorPatch, newAirflowMajorMinorPatch) + if err != nil { + log.Printf("[WARN] Could not parse airflow version, %s", err) + } + if !eq { + return false + } } else { - // Otherwise, we compare the full Airflow versions (like 1.10.15). + // Otherwise, we compare the full Airflow versions (like 1.10.15-build.5). eq, err := versionsEqual(oldAirflow, newAirflow) if err != nil { log.Printf("[WARN] Could not parse airflow version, %s", err) @@ -2922,7 +3041,59 @@ func versionsEqual(old, new string) (bool, error) { return o.Equal(n), nil } -func isComposer3(d *schema.ResourceData, config *transport_tpg.Config) bool { - image_version := d.Get("config.0.software_config.0.image_version").(string) - return strings.Contains(image_version, "composer-3") +func isComposer3(imageVersion string) bool { + return strings.Contains(imageVersion, "composer-3") +} + +func forceNewCustomDiff(key string) customdiff.ResourceConditionFunc { + return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool { + old, new := d.GetChange(key) + imageVersion := d.Get("config.0.software_config.0.image_version").(string) + if isComposer3(imageVersion) || tpgresource.CompareSelfLinkRelativePaths("", old.(string), new.(string), nil) { + return false + } + return true + } +} + +func imageVersionChangeValidationFunc(ctx context.Context, old, new, meta any) error { + if old.(string) != "" && !isComposer3(old.(string)) && isComposer3(new.(string)) { + return fmt.Errorf("upgrade to composer 3 is not yet supported") + } + return nil +} + +func validateComposer3FieldUsage(d *schema.ResourceDiff, key string, requireComposer3 bool) error { + _, ok := d.GetOk(key) + imageVersion := d.Get("config.0.software_config.0.image_version").(string) + if ok && ( isComposer3(imageVersion) != requireComposer3 ) { + if requireComposer3 { + return fmt.Errorf("error in configuration, %s should only be used in Composer 3", key) + } else { + return fmt.Errorf("error in configuration, %s should not be used in Composer 3", key) + } + } + return nil } + +func versionValidationCustomizeDiffFunc(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + composer3FieldUsagePolicy := map[string]bool{ + "config.0.node_config.0.max_pods_per_node": false, // not allowed in composer 3 + "config.0.node_config.0.enable_ip_masq_agent": false, + "config.0.node_config.0.config.0.node_config.0.ip_allocation_policy": false, + "config.0.private_environment_config": false, + "config.0.master_authorized_networks_config": false, + "config.0.node_config.0.composer_network_attachment": true, // allowed only in composer 3 + "config.0.node_config.0.composer_internal_ipv4_cidr_block": true, + "config.0.software_config.0.web_server_plugins_mode": true, + "config.0.enable_private_environment": true, + "config.0.enable_private_builds_only": true, + "config.0.workloads_config.0.dag_processor": true, + } + for key, allowed := range composer3FieldUsagePolicy { + if err := validateComposer3FieldUsage(d, key, allowed); err != nil { + return err + } + } + return nil +} \ No newline at end of file diff --git a/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.erb b/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.erb index 80e870eac07a..bde25f487b1f 100644 --- a/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.erb +++ b/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.erb @@ -21,6 +21,7 @@ import ( const testComposerEnvironmentPrefix = "tf-test-composer-env" const testComposerNetworkPrefix = "tf-test-composer-net" const testComposerBucketPrefix = "tf-test-composer-bucket" +const testComposerNetworkAttachmentPrefix = "tf-test-composer-nta" func allComposerServiceAgents() []string { return []string{ @@ -1186,6 +1187,147 @@ func TestAccComposerEnvironmentComposer3_update(t *testing.T) { }) } +func TestAccComposerEnvironmentComposer3_withNetworkSubnetworkAndAttachment_expectError(t *testing.T) { + t.Parallel() + + envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t)) + network := fmt.Sprintf("%s-%d", testComposerNetworkPrefix, acctest.RandInt(t)) + subnetwork := network + "-1" + networkAttachment := fmt.Sprintf("%s-%d", testComposerNetworkAttachmentPrefix, acctest.RandInt(t)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccComposerEnvironmentDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComposerEnvironmentComposer3_withNetworkSubnetworkAndAttachment_expectError(envName, networkAttachment, network, subnetwork), + ExpectError: regexp.MustCompile("Conflicting configuration arguments"), + }, + // This is a terrible clean-up step in order to get destroy to succeed, + // due to dangling firewall rules left by the Composer Environment blocking network deletion. + // TODO: Remove this check if firewall rules bug gets fixed by Composer. + { + PlanOnly: true, + ExpectNonEmptyPlan: true, + Config: testAccComposerEnvironmentComposer3_basic(envName, network, subnetwork), + Check: testAccCheckClearComposerEnvironmentFirewalls(t, network), + }, + }, + }) +} + +func TestAccComposerEnvironmentComposer3_withNetworkAttachment(t *testing.T) { + t.Parallel() + + envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t)) + network := fmt.Sprintf("%s-%d", testComposerNetworkPrefix, acctest.RandInt(t)) + subnetwork := network + "-1" + networkAttachment := fmt.Sprintf("%s-%d", testComposerNetworkAttachmentPrefix, acctest.RandInt(t)) + fullFormNetworkAttachmentName := fmt.Sprintf("projects/%s/regions/%s/networkAttachments/%s", envvar.GetTestProjectFromEnv(), envvar.GetTestRegionFromEnv(), networkAttachment) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccComposerEnvironmentDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComposerEnvironmentComposer3_withNetworkAttachment(envName, networkAttachment, network, subnetwork), + }, + { + ResourceName: "google_composer_environment.test", + ImportState: true, + ImportStateVerify: true, + }, + // This is a terrible clean-up step in order to get destroy to succeed, + // due to dangling firewall rules left by the Composer Environment blocking network deletion. + // TODO: Remove this check if firewall rules bug gets fixed by Composer. + { + PlanOnly: true, + Config: testAccComposerEnvironmentComposer3_withNetworkAttachment(envName, fullFormNetworkAttachmentName, network, subnetwork), + Check: testAccCheckClearComposerEnvironmentFirewalls(t, network), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccComposerEnvironmentComposer3_updateWithNetworkAttachment(t *testing.T) { + t.Parallel() + + envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t)) + network := fmt.Sprintf("%s-%d", testComposerNetworkPrefix, acctest.RandInt(t)) + subnetwork := network + "-1" + networkAttachment := fmt.Sprintf("%s-%d", testComposerNetworkAttachmentPrefix, acctest.RandInt(t)) + fullFormNetworkAttachmentName := fmt.Sprintf("projects/%s/regions/%s/networkAttachments/%s", envvar.GetTestProjectFromEnv(), envvar.GetTestRegionFromEnv(), networkAttachment) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccComposerEnvironmentDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComposerEnvironmentComposer3_withNetworkAndSubnetwork(envName, networkAttachment, network, subnetwork), + }, + { + Config: testAccComposerEnvironmentComposer3_withNetworkAttachment(envName, networkAttachment, network, subnetwork), + }, + { + ResourceName: "google_composer_environment.test", + ImportState: true, + ImportStateVerify: true, + }, + // This is a terrible clean-up step in order to get destroy to succeed, + // due to dangling firewall rules left by the Composer Environment blocking network deletion. + // TODO: Remove this check if firewall rules bug gets fixed by Composer. + { + PlanOnly: true, + Config: testAccComposerEnvironmentComposer3_withNetworkAttachment(envName, fullFormNetworkAttachmentName, network, subnetwork), + Check: testAccCheckClearComposerEnvironmentFirewalls(t, network), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccComposerEnvironmentComposer3_updateWithNetworkAndSubnetwork(t *testing.T) { + t.Parallel() + + envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t)) + network := fmt.Sprintf("%s-%d", testComposerNetworkPrefix, acctest.RandInt(t)) + subnetwork := network + "-1" + networkAttachment := fmt.Sprintf("%s-%d", testComposerNetworkAttachmentPrefix, acctest.RandInt(t)) + fullFormNetworkAttachmentName := fmt.Sprintf("projects/%s/regions/%s/networkAttachments/%s", envvar.GetTestProjectFromEnv(), envvar.GetTestRegionFromEnv(), networkAttachment) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccComposerEnvironmentDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComposerEnvironmentComposer3_withNetworkAttachment(envName, networkAttachment, network, subnetwork), + }, + { + Config: testAccComposerEnvironmentComposer3_withNetworkAndSubnetwork(envName, networkAttachment, network, subnetwork), + }, + { + ResourceName: "google_composer_environment.test", + ImportState: true, + ImportStateVerify: true, + }, + // This is a terrible clean-up step in order to get destroy to succeed, + // due to dangling firewall rules left by the Composer Environment blocking network deletion. + // TODO: Remove this check if firewall rules bug gets fixed by Composer. + { + PlanOnly: true, + Config: testAccComposerEnvironmentComposer3_withNetworkAttachment(envName, fullFormNetworkAttachmentName, network, subnetwork), + Check: testAccCheckClearComposerEnvironmentFirewalls(t, network), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + // Checks Composer 3 specific updatable fields. func TestAccComposerEnvironmentComposer3_updateToEmpty(t *testing.T) { t.Parallel() @@ -1195,7 +1337,7 @@ func TestAccComposerEnvironmentComposer3_updateToEmpty(t *testing.T) { subnetwork := network + "-1" acctest.VcrTest(t, resource.TestCase{ - PreCheck: func() { acctest.AccTestPreCheck(t) }, + PreCheck: func() { acctest.AccTestPreCheck(t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), CheckDestroy: testAccComposerEnvironmentDestroyProducer(t), Steps: []resource.TestStep{ @@ -1232,7 +1374,7 @@ func TestAccComposerEnvironmentComposer3_updateFromEmpty(t *testing.T) { subnetwork := network + "-1" acctest.VcrTest(t, resource.TestCase{ - PreCheck: func() { acctest.AccTestPreCheck(t) }, + PreCheck: func() { acctest.AccTestPreCheck(t) }, ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), CheckDestroy: testAccComposerEnvironmentDestroyProducer(t), Steps: []resource.TestStep{ @@ -1259,6 +1401,77 @@ func TestAccComposerEnvironmentComposer3_updateFromEmpty(t *testing.T) { }, }) } + +func TestAccComposerEnvironmentComposer3_upgrade_expectError(t *testing.T) { + t.Parallel() + + envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t)) + network := fmt.Sprintf("%s-%d", testComposerNetworkPrefix, acctest.RandInt(t)) + subnetwork := network + "-1" + errorRegExp, _ := regexp.Compile(".*upgrade to composer 3 is not yet supported.*") + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccComposerEnvironmentDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComposerEnvironmentComposer2_empty(envName, network, subnetwork), + }, + { + Config: testAccComposerEnvironmentComposer3_empty(envName, network, subnetwork), + ExpectError: errorRegExp, + }, + // This is a terrible clean-up step in order to get destroy to succeed, + // due to dangling firewall rules left by the Composer Environment blocking network deletion. + // TODO: Remove this check if firewall rules bug gets fixed by Composer. + { + PlanOnly: true, + ExpectNonEmptyPlan: false, + Config: testAccComposerEnvironmentComposer2_empty(envName, network, subnetwork), + Check: testAccCheckClearComposerEnvironmentFirewalls(t, network), + }, + }, + }) +} + +func TestAccComposerEnvironmentComposer2_usesUnsupportedField_expectError(t *testing.T) { + t.Parallel() + + envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t)) + errorRegExp, _ := regexp.Compile(".*error in configuration, .* should only be used in Composer 3.*") + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccComposerEnvironmentDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComposerEnvironmentComposer2_usesUnsupportedField(envName), + ExpectError: errorRegExp, + }, + }, + }) +} + +func TestAccComposerEnvironmentComposer3_usesUnsupportedField_expectError(t *testing.T) { + t.Parallel() + + envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t)) + errorRegExp, _ := regexp.Compile(".*error in configuration, .* should not be used in Composer 3.*") + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccComposerEnvironmentDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComposerEnvironmentComposer3_usesUnsupportedField(envName), + ExpectError: errorRegExp, + }, + }, + }) +} <% end -%> func testAccComposerEnvironment_customBucket(bucketName, envName, network, subnetwork string) string { @@ -2835,6 +3048,34 @@ resource "google_project_iam_member" "composer-worker" { } <% unless version == "ga" -%> +func testAccComposerEnvironmentComposer2_empty(name, network, subnetwork string) string { + return fmt.Sprintf(` +resource "google_composer_environment" "test" { + name = "%s" + region = "us-central1" + config { + software_config { + image_version = "composer-2-airflow-2" + } + } +} + +// use a separate network to avoid conflicts with other tests running in parallel +// that use the default network/subnet +resource "google_compute_network" "test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "test" { + name = "%s" + ip_cidr_range = "10.2.0.0/16" + region = "us-central1" + network = google_compute_network.test.self_link +} +`, name, network, subnetwork) +} + func testAccComposerEnvironmentComposer3_empty(name, network, subnetwork string) string { return fmt.Sprintf(` resource "google_composer_environment" "test" { @@ -2844,6 +3085,10 @@ resource "google_composer_environment" "test" { software_config { image_version = "composer-3-airflow-2" } + node_config { + network = google_compute_network.test.id + subnetwork = google_compute_subnetwork.test.id + } } } @@ -2863,17 +3108,51 @@ resource "google_compute_subnetwork" "test" { `, name, network, subnetwork) } -func testAccComposerEnvironmentComposer3_basic(name, network, subnetwork string) string { - return fmt.Sprintf(` +func testAccComposerEnvironmentComposer2_usesUnsupportedField(name string) string { +return fmt.Sprintf(` resource "google_composer_environment" "test" { name = "%s" region = "us-central1" config { + software_config { + image_version = "composer-2-airflow-2" + web_server_plugins_mode = "ENABLED" + } + } +} +`, name) +} + +func testAccComposerEnvironmentComposer3_usesUnsupportedField(name string) string { +return fmt.Sprintf(` +resource "google_composer_environment" "test" { + name = "%s" + region = "us-central1" + config { + node_config { + enable_ip_masq_agent = true + } software_config { image_version = "composer-3-airflow-2" } + } +} +`, name) +} + +func testAccComposerEnvironmentComposer3_basic(name, network, subnetwork string) string { + return fmt.Sprintf(` +resource "google_composer_environment" "test" { + name = "%s" + region = "us-central1" + config { node_config { composer_internal_ipv4_cidr_block = "100.64.128.0/20" + network = google_compute_network.test.id + subnetwork = google_compute_subnetwork.test.id + } + software_config { + image_version = "composer-3-airflow-2" } workloads_config { dag_processor { @@ -2910,6 +3189,8 @@ resource "google_composer_environment" "test" { region = "us-central1" config { node_config { + network = google_compute_network.test_1.id + subnetwork = google_compute_subnetwork.test_1.id composer_internal_ipv4_cidr_block = "100.64.128.0/20" } software_config { @@ -2941,7 +3222,168 @@ resource "google_compute_subnetwork" "test" { region = "us-central1" network = google_compute_network.test.self_link } -`, name, network, subnetwork) + +resource "google_compute_network" "test_1" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "test_1" { + name = "%s" + ip_cidr_range = "10.3.0.0/16" + region = "us-central1" + network = google_compute_network.test_1.self_link +} +`, name, network, subnetwork, network + "-update", subnetwork + "update") +} + +func testAccComposerEnvironmentComposer3_withNetworkAttachment(name, networkAttachment, network, subnetwork string) string { + return fmt.Sprintf(` +resource "google_composer_environment" "test" { + name = "%s" + region = "us-central1" + config { + node_config { + composer_network_attachment = google_compute_network_attachment.test.id + } + software_config { + image_version = "composer-3-airflow-2" + } + } +} + +resource "google_compute_network_attachment" "test" { + name = "%s" + region = "us-central1" + subnetworks = [ google_compute_subnetwork.test-att.id ] + connection_preference = "ACCEPT_MANUAL" + // Composer 3 is modifying producer_accept_lists outside terraform, ignoring this change for now + lifecycle { + ignore_changes = [producer_accept_lists] + } +} + +resource "google_compute_network" "test-att" { + name = "%s-att" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "test-att" { + name = "%s-att" + ip_cidr_range = "10.3.0.0/16" + region = "us-central1" + network = google_compute_network.test-att.self_link +} + +// use a separate network to avoid conflicts with other tests running in parallel +// that use the default network/subnet +resource "google_compute_network" "test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "test" { + name = "%s" + ip_cidr_range = "10.2.0.0/16" + region = "us-central1" + network = google_compute_network.test.self_link +} +`, name, networkAttachment, network, subnetwork, network, subnetwork) +} + +func testAccComposerEnvironmentComposer3_withNetworkAndSubnetwork(name, networkAttachment, network, subnetwork string) string { + return fmt.Sprintf(` +resource "google_composer_environment" "test" { + name = "%s" + region = "us-central1" + config { + node_config { + network = google_compute_network.test.id + subnetwork = google_compute_subnetwork.test.id + } + software_config { + image_version = "composer-3-airflow-2" + } + } +} + +resource "google_compute_network_attachment" "test" { + name = "%s" + region = "us-central1" + subnetworks = [ google_compute_subnetwork.test-att.id ] + connection_preference = "ACCEPT_MANUAL" + // Composer 3 is modifying producer_accept_lists outside terraform, ignoring this change for now + lifecycle { + ignore_changes = [producer_accept_lists] + } +} + +resource "google_compute_network" "test-att" { + name = "%s-att" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "test-att" { + name = "%s-att" + ip_cidr_range = "10.3.0.0/16" + region = "us-central1" + network = google_compute_network.test-att.self_link +} + +// use a separate network to avoid conflicts with other tests running in parallel +// that use the default network/subnet +resource "google_compute_network" "test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "test" { + name = "%s" + ip_cidr_range = "10.2.0.0/16" + region = "us-central1" + network = google_compute_network.test.self_link +} +`, name, networkAttachment, network, subnetwork, network, subnetwork) +} + +func testAccComposerEnvironmentComposer3_withNetworkSubnetworkAndAttachment_expectError(name, networkAttachment, network, subnetwork string) string { + return fmt.Sprintf(` +resource "google_composer_environment" "test" { + name = "%s" + region = "us-central1" + config { + node_config { + network = google_compute_network.test.id + subnetwork = google_compute_subnetwork.test.id + composer_network_attachment = google_compute_network_attachment.test.id + } + software_config { + image_version = "composer-3-airflow-2" + } + } +} + +resource "google_compute_network_attachment" "test" { + name = "%s" + region = "us-central1" + subnetworks = [ google_compute_subnetwork.test.id ] + connection_preference = "ACCEPT_MANUAL" +} + +// use a separate network to avoid conflicts with other tests running in parallel +// that use the default network/subnet +resource "google_compute_network" "test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "test" { + name = "%s" + ip_cidr_range = "10.2.0.0/16" + region = "us-central1" + network = google_compute_network.test.self_link +} +`, name, networkAttachment, network, subnetwork) } <% end -%> diff --git a/mmv1/third_party/terraform/services/compute/data_source_google_compute_forwarding_rules.go b/mmv1/third_party/terraform/services/compute/data_source_google_compute_forwarding_rules.go new file mode 100644 index 000000000000..80f467829735 --- /dev/null +++ b/mmv1/third_party/terraform/services/compute/data_source_google_compute_forwarding_rules.go @@ -0,0 +1,102 @@ +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" +) + +func DataSourceGoogleComputeForwardingRules() *schema.Resource { + return &schema.Resource{ + Read: dataSourceGoogleComputeForwardingRulesRead, + + Schema: map[string]*schema.Schema{ + + "project": { + Type: schema.TypeString, + Optional: true, + }, + + "region": { + Type: schema.TypeString, + Optional: true, + }, + + "rules": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: tpgresource.DatasourceSchemaFromResourceSchema(ResourceComputeForwardingRule().Schema), + }, + }, + }, + } +} + +func dataSourceGoogleComputeForwardingRulesRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return err + } + + region, err := tpgresource.GetRegion(d, config) + if err != nil { + return err + } + + id := fmt.Sprintf("projects/%s/regions/%s/forwardingRules", project, region) + d.SetId(id) + + forwardingRulesAggregatedList, err := config.NewComputeClient(userAgent).ForwardingRules.List(project, region).Do() + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("Forwarding Rules Not Found : %s", project)) + } + + forwardingRules := make([]map[string]interface{}, 0, len(forwardingRulesAggregatedList.Items)) + + for i := 0; i < len(forwardingRulesAggregatedList.Items); i++ { + rule := forwardingRulesAggregatedList.Items[i] + mappedData := map[string]interface{}{ + "name": rule.Name, + "network": rule.Network, + "subnetwork": rule.Subnetwork, + "backend_service": rule.BackendService, + "ip_address": rule.IPAddress, + "service_name": rule.ServiceName, + "service_label": rule.ServiceLabel, + "description": rule.Description, + "self_link": rule.SelfLink, + "labels": rule.Labels, + "ports": rule.Ports, + "region": rule.Region, + "target": rule.Target, + "ip_version": rule.IpVersion, + "network_tier": rule.NetworkTier, + "base_forwarding_rule": rule.BaseForwardingRule, + "port_range": rule.PortRange, + } + forwardingRules = append(forwardingRules, mappedData) + } + + if err := d.Set("rules", forwardingRules); err != nil { + return fmt.Errorf("Error setting the forwarding rules names: %s", err) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error setting the network names: %s", err) + } + + if err := d.Set("region", region); err != nil { + return fmt.Errorf("Error setting the region: %s", err) + } + + return nil +} diff --git a/mmv1/third_party/terraform/services/compute/data_source_google_compute_forwarding_rules_test.go b/mmv1/third_party/terraform/services/compute/data_source_google_compute_forwarding_rules_test.go new file mode 100644 index 000000000000..c0bc3e9004f9 --- /dev/null +++ b/mmv1/third_party/terraform/services/compute/data_source_google_compute_forwarding_rules_test.go @@ -0,0 +1,53 @@ +package compute_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccDataSourceGoogleForwardingRules(t *testing.T) { + t.Parallel() + + poolName := fmt.Sprintf("tf-%s", acctest.RandString(t, 10)) + ruleName := fmt.Sprintf("tf-%s", acctest.RandString(t, 10)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceGoogleForwardingRuleConfig(poolName, ruleName), + Check: acctest.CheckDataSourceStateMatchesResourceState("data.google_compute_forwarding_rule.my_forwarding_rule", "google_compute_forwarding_rule.foobar-fr"), + }, + }, + }) +} + +func testAccDataSourceGoogleForwardingRulesConfig(poolName, ruleName string) string { + return fmt.Sprintf(` +resource "google_compute_target_pool" "foobar-tp" { + description = "Resource created for Terraform acceptance testing" + instances = ["us-central1-a/foo", "us-central1-b/bar"] + name = "%s" +} + +resource "google_compute_forwarding_rule" "foobar-fr" { + description = "Resource created for Terraform acceptance testing" + ip_protocol = "UDP" + name = "%s" + port_range = "80-81" + target = google_compute_target_pool.foobar-tp.self_link + labels = { + my-label = "my-label-value" + } +} + +data "google_compute_forwarding_rules" "my_forwarding_rule" { + project = google_compute_forwarding_rule.foobar-fr.project + region = google_compute_forwarding_rule.foobar-fr.region +} +`, poolName, ruleName) +} diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.erb index 97069c02ca59..1a6ad62d42cf 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.erb @@ -444,7 +444,7 @@ func ResourceComputeInstance() *schema.Resource { "ipv6_access_config": { Type: schema.TypeList, - Optional: true, + Optional: true, Description: `An array of IPv6 access configurations for this interface. Currently, only one IPv6 access config, DIRECT_IPV6, is supported. If there is no ipv6AccessConfig specified, then this instance will have no external IPv6 Internet access.`, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -651,7 +651,7 @@ func ResourceComputeInstance() *schema.Resource { }, }, - "params": { + "params": { Type: schema.TypeList, MaxItems: 1, Optional: true, @@ -673,7 +673,7 @@ func ResourceComputeInstance() *schema.Resource { Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Description: `A set of key/value label pairs assigned to the instance. - + **Note**: This field is non-authoritative, and will only manage the labels present in your configuration. Please refer to the field 'effective_labels' for all of the labels present on the resource.`, }, @@ -1422,7 +1422,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Error creating instance while setting the security policies: %s", err) } <% end -%> - + err = waitUntilInstanceHasDesiredStatus(config, d) if err != nil { return fmt.Errorf("Error waiting for status: %s", err) @@ -1936,7 +1936,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Instance had unexpected number of network interfaces: %d", len(instance.NetworkInterfaces)) } - <% unless version == 'ga' -%> + <% unless version == 'ga' -%> updateSecurityPolicy := false for i := 0; i < len(instance.NetworkInterfaces); i++ { prefix := fmt.Sprintf("network_interface.%d", i) @@ -2067,6 +2067,23 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } } + if !updateDuringStop && d.HasChange(prefix+".stack_type") { + + networkInterfacePatchObj := &compute.NetworkInterface{ + StackType: d.Get(prefix+".stack_type").(string), + Fingerprint: instNetworkInterface.Fingerprint, + } + updateCall := config.NewComputeClient(userAgent).Instances.UpdateNetworkInterface(project, zone, instance.Name, networkName, networkInterfacePatchObj).Do + op, err := updateCall() + if err != nil { + return errwrap.Wrapf("Error updating network interface: {{err}}", err) + } + opErr := ComputeOperationWaitTime(config, op, project, "network interface to update", userAgent, d.Timeout(schema.TimeoutUpdate)) + if opErr != nil { + return opErr + } + } + if !updateDuringStop && d.HasChange(prefix+".ipv6_address") { networkInterfacePatchObj := &compute.NetworkInterface{ diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager.go.erb index 3dc61dfc1d17..878c8c2a2545 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager.go.erb @@ -316,7 +316,6 @@ func ResourceComputeInstanceGroupManager() *schema.Resource { }, }, - <% unless version == "ga" -%> "all_instances_config": { Type: schema.TypeList, Optional: true, @@ -341,7 +340,6 @@ func ResourceComputeInstanceGroupManager() *schema.Resource { }, }, }, - <% end -%> "wait_for_instances": { Type: schema.TypeBool, Optional: true, @@ -353,12 +351,7 @@ func ResourceComputeInstanceGroupManager() *schema.Resource { Optional: true, Default: "STABLE", ValidateFunc: validation.StringInSlice([]string{"STABLE", "UPDATED"}, false), - - <% if version == "ga" -%> - Description: `When used with wait_for_instances specifies the status to wait for. When STABLE is specified this resource will wait until the instances are stable before returning. When UPDATED is set, it will wait for the version target to be reached and any per instance configs to be effective as well as all instances to be stable before returning.`, - <% else -%> Description: `When used with wait_for_instances specifies the status to wait for. When STABLE is specified this resource will wait until the instances are stable before returning. When UPDATED is set, it will wait for the version target to be reached and any per instance configs to be effective and all instances configs to be effective as well as all instances to be stable before returning.`, - <% end -%> }, "stateful_internal_ip": { Type: schema.TypeList, @@ -454,7 +447,6 @@ func ResourceComputeInstanceGroupManager() *schema.Resource { }, }, }, - <% unless version == "ga" -%> "all_instances_config": { Type: schema.TypeList, Computed: true, @@ -469,7 +461,6 @@ func ResourceComputeInstanceGroupManager() *schema.Resource { }, }, }, - <% end -%> "stateful": { Type: schema.TypeList, Computed: true, @@ -589,9 +580,7 @@ func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta inte Versions: expandVersions(d.Get("version").([]interface{})), UpdatePolicy: expandUpdatePolicy(d.Get("update_policy").([]interface{})), InstanceLifecyclePolicy: expandInstanceLifecyclePolicy(d.Get("instance_lifecycle_policy").([]interface{})), - <% unless version == "ga" -%> AllInstancesConfig: expandAllInstancesConfig(nil, d.Get("all_instances_config").([]interface{})), - <% end -%> StatefulPolicy: expandStatefulPolicy(d), // Force send TargetSize to allow a value of 0. @@ -815,13 +804,11 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf if err = d.Set("instance_lifecycle_policy", flattenInstanceLifecyclePolicy(manager.InstanceLifecyclePolicy)); err != nil { return fmt.Errorf("Error setting instance lifecycle policy in state: %s", err.Error()) } - <% unless version == "ga" -%> if manager.AllInstancesConfig != nil { if err = d.Set("all_instances_config", flattenAllInstancesConfig(manager.AllInstancesConfig)); err != nil { return fmt.Errorf("Error setting all_instances_config in state: %s", err.Error()) } } - <% end -%> if err = d.Set("status", flattenStatus(manager.Status)); err != nil { return fmt.Errorf("Error setting status in state: %s", err.Error()) } @@ -892,7 +879,6 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte change = true } - <% unless version == "ga" -%> if d.HasChange("all_instances_config") { oldAic, newAic := d.GetChange("all_instances_config") if newAic == nil || len(newAic.([]interface{})) == 0 { @@ -902,19 +888,11 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte } change = true } - <% end -%> - <% unless version == "ga" -%> if d.HasChange("stateful_internal_ip") || d.HasChange("stateful_external_ip") || d.HasChange("stateful_disk") { updatedManager.StatefulPolicy = expandStatefulPolicy(d) change = true } - <% else -%> - if d.HasChange("stateful_disk") { - updatedManager.StatefulPolicy = expandStatefulPolicy(d) - change = true - } - <% end -%> if d.HasChange("list_managed_instances_results") { updatedManager.ListManagedInstancesResults = d.Get("list_managed_instances_results").(string) @@ -1052,11 +1030,7 @@ func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta inte func computeIGMWaitForInstanceStatus(d *schema.ResourceData, meta interface{}) error { waitForUpdates := d.Get("wait_for_instances_status").(string) == "UPDATED" conf := resource.StateChangeConf{ - <% if version == "ga" -%> - Pending: []string{"creating", "error", "updating per instance configs", "reaching version target"}, - <% else -%> Pending: []string{"creating", "error", "updating per instance configs", "reaching version target", "updating all instances config"}, - <% end -%> Target: []string{"created"}, Refresh: waitForInstancesRefreshFunc(getManager, waitForUpdates, d, meta), Timeout: d.Timeout(schema.TimeoutCreate), @@ -1407,7 +1381,6 @@ func flattenInstanceLifecyclePolicy(instanceLifecyclePolicy *compute.InstanceGro return results } -<% unless version == "ga" -%> func expandAllInstancesConfig(old []interface{}, new []interface{}) *compute.InstanceGroupManagerAllInstancesConfig { var properties *compute.InstancePropertiesPatch for _, raw := range new { @@ -1463,7 +1436,6 @@ func flattenAllInstancesConfig(allInstancesConfig *compute.InstanceGroupManagerA results = append(results, props) return results } -<% end -%> func flattenStatus(status *compute.InstanceGroupManagerStatus) []map[string]interface{} { results := []map[string]interface{}{} @@ -1472,11 +1444,9 @@ func flattenStatus(status *compute.InstanceGroupManagerStatus) []map[string]inte "stateful": flattenStatusStateful(status.Stateful), "version_target": flattenStatusVersionTarget(status.VersionTarget), } - <% unless version == "ga" -%> if status.AllInstancesConfig != nil { data["all_instances_config"] = flattenStatusAllInstancesConfig(status.AllInstancesConfig) } - <% end -%> results = append(results, data) return results } @@ -1509,7 +1479,6 @@ func flattenStatusVersionTarget(versionTarget *compute.InstanceGroupManagerStatu return results } -<% unless version == "ga" -%> func flattenStatusAllInstancesConfig(allInstancesConfig *compute.InstanceGroupManagerStatusAllInstancesConfig) []map[string]interface{} { results := []map[string]interface{}{} data := map[string]interface{}{ @@ -1518,7 +1487,6 @@ func flattenStatusAllInstancesConfig(allInstancesConfig *compute.InstanceGroupMa results = append(results, data) return results } -<% end -%> func resourceInstanceGroupManagerStateImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { if err := d.Set("wait_for_instances", false); err != nil { diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager_test.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager_test.go.erb index 7c3322419219..c02ed9a13c1b 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager_test.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance_group_manager_test.go.erb @@ -660,7 +660,6 @@ resource "google_compute_instance_group_manager" "igm-update" { name = "customhttp" port = 8080 } -<% unless version == "ga" -%> all_instances_config { metadata = { foo = "bar" @@ -669,7 +668,6 @@ resource "google_compute_instance_group_manager" "igm-update" { doo = "dad" } } -<% end -%> instance_lifecycle_policy { force_update_on_repair = "YES" @@ -766,7 +764,6 @@ resource "google_compute_instance_group_manager" "igm-update" { port = 8443 } -<% unless version == "ga" -%> all_instances_config { metadata = { doo = "dad" @@ -775,7 +772,6 @@ resource "google_compute_instance_group_manager" "igm-update" { foo = "bar" } } -<% end -%> instance_lifecycle_policy { force_update_on_repair = "NO" @@ -1780,7 +1776,6 @@ resource "google_compute_instance_group_manager" "igm-basic" { max_surge_fixed = 0 max_unavailable_percent = 50 } -<% unless version == "ga" -%> all_instances_config { metadata = { doo = "dad" @@ -1789,7 +1784,6 @@ resource "google_compute_instance_group_manager" "igm-basic" { foo = "bar" } } -<% end -%> instance_lifecycle_policy { force_update_on_repair = "YES" } diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.erb index c86796109098..bbed47e387b2 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.erb @@ -3279,6 +3279,33 @@ func TestAccComputeInstance_NetworkAttachmentUpdate(t *testing.T) { } <% end %> +func TestAccComputeInstance_NicStackTypeUpdate(t *testing.T) { + t.Parallel() + suffix := acctest.RandString(t, 10) + envRegion := envvar.GetTestRegionFromEnv() + instanceName := fmt.Sprintf("tf-test-compute-instance-%s", suffix) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_nicStackTypeUpdate(suffix, envRegion, "IPV4_ONLY", instanceName), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"allow_stopping_for_update"}), + { + Config: testAccComputeInstance_nicStackTypeUpdate(suffix, envRegion, "IPV4_IPV6", instanceName), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"allow_stopping_for_update"}), + { + Config: testAccComputeInstance_nicStackTypeUpdate(suffix, envRegion, "IPV4_ONLY", instanceName), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"allow_stopping_for_update"}), + }, + }) +} + func testAccCheckComputeInstanceDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { config := acctest.GoogleProviderConfig(t) @@ -8964,3 +8991,50 @@ resource "google_compute_instance" "foobar" { } <% end %> +func testAccComputeInstance_nicStackTypeUpdate(suffix, region, stack_type, instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-11" + project = "debian-cloud" +} + +resource "google_compute_network" "net" { + name = "tf-test-network-%s" + enable_ula_internal_ipv6 = true + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "subnet-ipv6" { + region = "%s" + name = "tf-test-subnet-ip6-%s" + ip_cidr_range = "10.0.0.0/22" + purpose = "PRIVATE" + stack_type = "IPV4_IPV6" + ipv6_access_type = "INTERNAL" + network = google_compute_network.net.id +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "e2-medium" + zone = "%s-a" + tags = ["foo", "bar"] + + boot_disk { + initialize_params { + image = data.google_compute_image.my_image.self_link + } + } + + network_interface { + network = google_compute_network.net.self_link + subnetwork = google_compute_subnetwork.subnet-ipv6.self_link + stack_type = "%s" + } + + metadata = { + foo = "bar" + } +} +`, suffix, region, suffix, instance, region, stack_type) +} diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_region_autoscaler_test.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_region_autoscaler_test.go.erb index 1d6f65491513..64f15c2fb80e 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_region_autoscaler_test.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_region_autoscaler_test.go.erb @@ -217,14 +217,12 @@ resource "google_compute_region_autoscaler" "foobar" { target = 0.5 predictive_method = "OPTIMIZE_AVAILABILITY" } -<% unless version == 'ga' -%> scale_down_control { max_scaled_down_replicas { percent = 80 } time_window_sec = 300 } -<% end -%> } } `, autoscalerName) @@ -269,14 +267,12 @@ resource "google_compute_region_autoscaler" "foobar" { cpu_utilization { target = 0.5 } -<% unless version == 'ga' -%> scale_down_control { max_scaled_down_replicas { percent = 80 } time_window_sec = 300 } -<% end -%> scaling_schedules { name = "every-weekday-morning" description = "Increase to 2 every weekday at 7AM for 6 hours." diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager.go.erb index 871dd0314275..46cfbffd60e9 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager.go.erb @@ -208,12 +208,7 @@ func ResourceComputeRegionInstanceGroupManager() *schema.Resource { Optional: true, Default: "STABLE", ValidateFunc: validation.StringInSlice([]string{"STABLE", "UPDATED"}, false), - - <% if version == "ga" -%> - Description: `When used with wait_for_instances specifies the status to wait for. When STABLE is specified this resource will wait until the instances are stable before returning. When UPDATED is set, it will wait for the version target to be reached and any per instance configs to be effective as well as all instances to be stable before returning.`, - <% else -%> Description: `When used with wait_for_instances specifies the status to wait for. When STABLE is specified this resource will wait until the instances are stable before returning. When UPDATED is set, it will wait for the version target to be reached and any per instance configs to be effective and all instances configs to be effective as well as all instances to be stable before returning.`, - <% end -%> }, "auto_healing_policies": { @@ -365,7 +360,6 @@ func ResourceComputeRegionInstanceGroupManager() *schema.Resource { }, }, }, - <% unless version == "ga" -%> "all_instances_config": { Type: schema.TypeList, Optional: true, @@ -390,7 +384,6 @@ func ResourceComputeRegionInstanceGroupManager() *schema.Resource { }, }, }, - <% end -%> "stateful_internal_ip": { Type: schema.TypeList, Optional: true, @@ -481,7 +474,6 @@ func ResourceComputeRegionInstanceGroupManager() *schema.Resource { }, }, }, - <% unless version == "ga" -%> "all_instances_config": { Type: schema.TypeList, Computed: true, @@ -496,7 +488,6 @@ func ResourceComputeRegionInstanceGroupManager() *schema.Resource { }, }, }, - <% end -%> "stateful": { Type: schema.TypeList, Computed: true, @@ -562,9 +553,7 @@ func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, met Versions: expandVersions(d.Get("version").([]interface{})), UpdatePolicy: expandRegionUpdatePolicy(d.Get("update_policy").([]interface{})), InstanceLifecyclePolicy: expandInstanceLifecyclePolicy(d.Get("instance_lifecycle_policy").([]interface{})), - <% unless version == "ga" -%> AllInstancesConfig: expandAllInstancesConfig(nil, d.Get("all_instances_config").([]interface{})), - <% end -%> DistributionPolicy: expandDistributionPolicy(d), StatefulPolicy: expandStatefulPolicy(d), // Force send TargetSize to allow size of 0. @@ -602,11 +591,7 @@ func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, met func computeRIGMWaitForInstanceStatus(d *schema.ResourceData, meta interface{}) error { waitForUpdates := d.Get("wait_for_instances_status").(string) == "UPDATED" conf := resource.StateChangeConf{ - <% if version == "ga" -%> - Pending: []string{"creating", "error", "updating per instance configs", "reaching version target"}, - <% else -%> Pending: []string{"creating", "error", "updating per instance configs", "reaching version target", "updating all instances config"}, - <% end -%> Target: []string{"created"}, Refresh: waitForInstancesRefreshFunc(getRegionalManager, waitForUpdates, d, meta), Timeout: d.Timeout(schema.TimeoutCreate), @@ -674,11 +659,9 @@ func waitForInstancesRefreshFunc(f getInstanceManagerFunc, waitForUpdates bool, if !m.Status.VersionTarget.IsReached { return false, "reaching version target", nil } - <% unless version == "ga" -%> if !m.Status.AllInstancesConfig.Effective { return false, "updating all instances config", nil } - <% end -%> } return true, "created", nil } else { @@ -763,13 +746,11 @@ func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta if err = d.Set("instance_lifecycle_policy", flattenInstanceLifecyclePolicy(manager.InstanceLifecyclePolicy)); err != nil { return fmt.Errorf("Error setting instance lifecycle policy in state: %s", err.Error()) } - <% unless version == "ga" -%> if manager.AllInstancesConfig != nil { if err = d.Set("all_instances_config", flattenAllInstancesConfig(manager.AllInstancesConfig)); err != nil { return fmt.Errorf("Error setting all_instances_config in state: %s", err.Error()) } } - <% end -%> if err = d.Set("stateful_disk", flattenStatefulPolicy(manager.StatefulPolicy)); err != nil { return fmt.Errorf("Error setting stateful_disk in state: %s", err.Error()) } @@ -847,19 +828,11 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met change = true } - <% unless version == "ga" -%> if d.HasChange("stateful_internal_ip") || d.HasChange("stateful_external_ip") || d.HasChange("stateful_disk") { updatedManager.StatefulPolicy = expandStatefulPolicy(d) change = true } - <% else -%> - if d.HasChange("stateful_disk") { - updatedManager.StatefulPolicy = expandStatefulPolicy(d) - change = true - } - <% end -%> - <% unless version == "ga" -%> if d.HasChange("all_instances_config") { oldAic, newAic := d.GetChange("all_instances_config") if newAic == nil || len(newAic.([]interface{})) == 0 { @@ -869,7 +842,6 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met } change = true } - <% end -%> if d.HasChange("list_managed_instances_results") { updatedManager.ListManagedInstancesResults = d.Get("list_managed_instances_results").(string) diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager_test.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager_test.go.erb index 85416a6ebdcb..4e4ef9645074 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager_test.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_region_instance_group_manager_test.go.erb @@ -323,8 +323,6 @@ func TestAccRegionInstanceGroupManager_distributionPolicy(t *testing.T) { } func TestAccRegionInstanceGroupManager_stateful(t *testing.T) { - // TODO: Flaky test due to ordering of IPs https://github.com/hashicorp/terraform-provider-google/issues/13430 - t.Skip() t.Parallel() template := fmt.Sprintf("tf-test-rigm-%s", acctest.RandString(t, 10)) @@ -564,7 +562,6 @@ resource "google_compute_region_instance_group_manager" "igm-update" { port = 8080 } -<% unless version == "ga" -%> all_instances_config { metadata = { foo = "bar" @@ -573,7 +570,6 @@ resource "google_compute_region_instance_group_manager" "igm-update" { doo = "dad" } } -<% end -%> instance_lifecycle_policy { force_update_on_repair = "YES" @@ -670,7 +666,6 @@ resource "google_compute_region_instance_group_manager" "igm-update" { port = 8443 } -<% unless version == "ga" -%> all_instances_config { metadata = { doo = "dad" @@ -679,7 +674,6 @@ resource "google_compute_region_instance_group_manager" "igm-update" { foo = "bar" } } -<% end -%> instance_lifecycle_policy { force_update_on_repair = "NO" diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_region_target_tcp_proxy_test.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_region_target_tcp_proxy_test.go.erb index 6b7cfd194215..535218117c07 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_region_target_tcp_proxy_test.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_region_target_tcp_proxy_test.go.erb @@ -13,9 +13,9 @@ import ( func TestAccComputeRegionTargetTcpProxy_update(t *testing.T) { t.Parallel() - target := fmt.Sprintf("trtcp-test-%s", acctest.RandString(t, 10)) - backend := fmt.Sprintf("trtcp-test-%s", acctest.RandString(t, 10)) - hc := fmt.Sprintf("trtcp-test-%s", acctest.RandString(t, 10)) + target := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)) + backend := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)) + hc := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)) acctest.VcrTest(t, resource.TestCase{ PreCheck: func() { acctest.AccTestPreCheck(t) }, diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_security_policy.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_security_policy.go.erb index 59db357b97a6..2222dd74ab0d 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_security_policy.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_security_policy.go.erb @@ -5,9 +5,7 @@ import ( "context" "fmt" "log" -<% unless version == 'ga' -%> "strings" -<% end -%> "time" @@ -466,14 +464,12 @@ func ResourceComputeSecurityPolicy() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{"NORMAL", "VERBOSE"}, false), Description: `Logging level. Supported values include: "NORMAL", "VERBOSE".`, }, - <% unless version == 'ga' -%> "user_ip_request_headers": { Type: schema.TypeSet, Optional: true, Description: `An optional list of case-insensitive request header names to use for resolving the callers client IP address.`, Elem: &schema.Schema{Type: schema.TypeString}, }, - <% end -%> }, }, }, @@ -742,9 +738,7 @@ func resourceComputeSecurityPolicyUpdate(d *schema.ResourceData, meta interface{ Fingerprint: d.Get("fingerprint").(string), } - <% unless version == 'ga' -%> updateMask := []string{} - <% end -%> if d.HasChange("type") { securityPolicy.Type = d.Get("type").(string) @@ -759,13 +753,11 @@ func resourceComputeSecurityPolicyUpdate(d *schema.ResourceData, meta interface{ if d.HasChange("advanced_options_config") { securityPolicy.AdvancedOptionsConfig = expandSecurityPolicyAdvancedOptionsConfig(d.Get("advanced_options_config").([]interface{})) securityPolicy.ForceSendFields = append(securityPolicy.ForceSendFields, "AdvancedOptionsConfig", "advancedOptionsConfig.jsonParsing", "advancedOptionsConfig.jsonCustomConfig", "advancedOptionsConfig.logLevel") - <% unless version == 'ga' -%> securityPolicy.ForceSendFields = append(securityPolicy.ForceSendFields, "advanceOptionConfig.userIpRequestHeaders") if len(securityPolicy.AdvancedOptionsConfig.UserIpRequestHeaders) == 0 { // to clean this list we must send the updateMask of this field on the request. updateMask = append(updateMask, "advanced_options_config.user_ip_request_headers") } - <% end -%> } if d.HasChange("adaptive_protection_config") { @@ -784,11 +776,7 @@ func resourceComputeSecurityPolicyUpdate(d *schema.ResourceData, meta interface{ if len(securityPolicy.ForceSendFields) > 0 { client := config.NewComputeClient(userAgent) - <% if version == 'ga' -%> - op, err := client.SecurityPolicies.Patch(project, sp, securityPolicy).Do() - <% else -%> op, err := client.SecurityPolicies.Patch(project, sp, securityPolicy).UpdateMask(strings.Join(updateMask, ",")).Do() - <% end -%> if err != nil { return errwrap.Wrapf(fmt.Sprintf("Error updating SecurityPolicy %q: {{err}}", sp), err) @@ -1230,9 +1218,7 @@ func expandSecurityPolicyAdvancedOptionsConfig(configured []interface{}) *comput JsonParsing: data["json_parsing"].(string), JsonCustomConfig: expandSecurityPolicyAdvancedOptionsConfigJsonCustomConfig(data["json_custom_config"].([]interface{})), LogLevel: data["log_level"].(string), - <% unless version == 'ga' -%> UserIpRequestHeaders: tpgresource.ConvertStringArr(data["user_ip_request_headers"].(*schema.Set).List()), - <% end %> } } @@ -1245,9 +1231,7 @@ func flattenSecurityPolicyAdvancedOptionsConfig(conf *compute.SecurityPolicyAdva "json_parsing": conf.JsonParsing, "json_custom_config": flattenSecurityPolicyAdvancedOptionsConfigJsonCustomConfig(conf.JsonCustomConfig), "log_level": conf.LogLevel, - <% unless version == 'ga' -%> "user_ip_request_headers": schema.NewSet(schema.HashString, tpgresource.ConvertStringArrToInterface(conf.UserIpRequestHeaders)), - <% end -%> } return []map[string]interface{}{data} diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_security_policy_test.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_security_policy_test.go.erb index b3dcaac75f47..310985e5536e 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_security_policy_test.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_security_policy_test.go.erb @@ -189,7 +189,6 @@ func TestAccComputeSecurityPolicy_withAdvancedOptionsConfig(t *testing.T) { ImportState: true, ImportStateVerify: true, }, - <% unless version == 'ga' -%> { Config: testAccComputeSecurityPolicy_withAdvancedOptionsConfig_update(spName), }, @@ -216,7 +215,6 @@ func TestAccComputeSecurityPolicy_withAdvancedOptionsConfig(t *testing.T) { ImportState: true, ImportStateVerify: true, }, - <% end -%> { Config: testAccComputeSecurityPolicy_basic(spName), }, @@ -1108,19 +1106,15 @@ resource "google_compute_security_policy" "policy" { ] } log_level = "VERBOSE" - <% unless version == 'ga' -%> user_ip_request_headers = [ "True-Client-IP", "x-custom-ip" ] - <% end -%> - } } `, spName) } -<% unless version == 'ga' -%> func testAccComputeSecurityPolicy_withAdvancedOptionsConfig_update(spName string) string { return fmt.Sprintf(` resource "google_compute_security_policy" "policy" { @@ -1189,7 +1183,6 @@ resource "google_compute_security_policy" "policy" { } `, spName) } -<% end -%> func testAccComputeSecurityPolicy_withAdaptiveProtection(spName string) string { return fmt.Sprintf(` diff --git a/mmv1/third_party/terraform/services/container/node_config.go.erb b/mmv1/third_party/terraform/services/container/node_config.go.erb index af3492f29413..089c5943aaea 100644 --- a/mmv1/third_party/terraform/services/container/node_config.go.erb +++ b/mmv1/third_party/terraform/services/container/node_config.go.erb @@ -675,14 +675,17 @@ func schemaNodeConfig() *schema.Schema { }, }, }, - <% unless version == 'ga' -%> + "resource_manager_tags": { + Type: schema.TypeMap, + Optional: true, + Description: `A map of resource manager tags. Resource manager tag keys and values have the same definition as resource manager tags. Keys must be in the format tagKeys/{tag_key_id}, and values are in the format tagValues/456. The field is ignored (both PUT & PATCH) when empty.`, + }, "enable_confidential_storage": { Type: schema.TypeBool, Optional: true, ForceNew: true, Description: `If enabled boot disks are configured with confidential mode.`, }, - <% end -%> }, }, } @@ -884,6 +887,10 @@ func expandNodeConfig(v interface{}) *container.NodeConfig { nc.ResourceLabels = m } + if v, ok := nodeConfig["resource_manager_tags"]; ok && len(v.(map[string]interface{})) > 0 { + nc.ResourceManagerTags = expandResourceManagerTags(v) + } + if v, ok := nodeConfig["tags"]; ok { tagsList := v.([]interface{}) tags := []string{} @@ -969,11 +976,9 @@ func expandNodeConfig(v interface{}) *container.NodeConfig { nc.SoleTenantConfig = expandSoleTenantConfig(v) } - <% unless version == 'ga' -%> if v,ok := nodeConfig["enable_confidential_storage"]; ok { nc.EnableConfidentialStorage = v.(bool) } - <% end -%> <% unless version == "ga" -%> if v, ok := nodeConfig["host_maintenance_policy"]; ok { @@ -988,6 +993,19 @@ func expandNodeConfig(v interface{}) *container.NodeConfig { return nc } +func expandResourceManagerTags(v interface{}) *container.ResourceManagerTags { + rmts := make(map[string]string) + + if v != nil { + rmts = tpgresource.ConvertStringMap(v.(map[string]interface{})) + } + + return &container.ResourceManagerTags{ + Tags: rmts, + ForceSendFields: []string{"Tags"}, + } +} + func expandWorkloadMetadataConfig(v interface{}) *container.WorkloadMetadataConfig { if v == nil { return nil @@ -1213,9 +1231,8 @@ func flattenNodeConfig(c *container.NodeConfig, v interface{}) []map[string]inte "advanced_machine_features": flattenAdvancedMachineFeaturesConfig(c.AdvancedMachineFeatures), "sole_tenant_config": flattenSoleTenantConfig(c.SoleTenantConfig), "fast_socket": flattenFastSocket(c.FastSocket), - <% unless version == 'ga' -%> + "resource_manager_tags": flattenResourceManagerTags(c.ResourceManagerTags), "enable_confidential_storage": c.EnableConfidentialStorage, - <% end -%> }) if len(c.OauthScopes) > 0 { @@ -1225,6 +1242,19 @@ func flattenNodeConfig(c *container.NodeConfig, v interface{}) []map[string]inte return config } +func flattenResourceManagerTags(c *container.ResourceManagerTags) map[string]interface{} { + rmt := make(map[string]interface{}) + + if c != nil { + for k, v := range c.Tags { + rmt[k] = v + } + + } + + return rmt +} + func flattenAdvancedMachineFeaturesConfig(c *container.AdvancedMachineFeatures) []map[string]interface{} { result := []map[string]interface{}{} if c != nil { diff --git a/mmv1/third_party/terraform/services/container/resource_container_cluster.go.erb b/mmv1/third_party/terraform/services/container/resource_container_cluster.go.erb index 356a5b58ec6d..00a3f3aaab84 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_cluster.go.erb +++ b/mmv1/third_party/terraform/services/container/resource_container_cluster.go.erb @@ -97,6 +97,7 @@ var ( forceNewClusterNodeConfigFields = []string{ "labels", "workload_metadata_config", + "resource_manager_tags", } suppressDiffForAutopilot = schema.SchemaDiffSuppressFunc(func(k, oldValue, newValue string, d *schema.ResourceData) bool { @@ -1424,11 +1425,11 @@ func ResourceContainerCluster() *schema.Resource { "node_pool_defaults": clusterSchemaNodePoolDefaults(), "node_pool_auto_config": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - Description: `Node pool configs that apply to all auto-provisioned node pools in autopilot clusters and node auto-provisioning enabled clusters.`, + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Description: `Node pool configs that apply to all auto-provisioned node pools in autopilot clusters and node auto-provisioning enabled clusters.`, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "network_tags": { @@ -2069,16 +2070,26 @@ func ResourceContainerCluster() *schema.Resource { Optional: true, Description: `The Fleet host project of the cluster.`, }, - "membership": { - Type: schema.TypeString, - Computed: true, - Description: `Full resource name of the registered fleet membership of the cluster.`, - }, - "pre_registered": { - Type: schema.TypeBool, - Computed: true, - Description: `Whether the cluster has been registered via the fleet API.`, - }, + "membership": { + Type: schema.TypeString, + Computed: true, + Description: `Full resource name of the registered fleet membership of the cluster.`, + }, + "pre_registered": { + Type: schema.TypeBool, + Computed: true, + Description: `Whether the cluster has been registered via the fleet API.`, + }, + "membership_id": { + Type: schema.TypeString, + Computed: true, + Description: `Short name of the fleet membership, for example "member-1".`, + }, + "membership_location": { + Type: schema.TypeString, + Computed: true, + Description: `Location of the fleet membership, for example "us-central1".`, + }, }, }, }, @@ -5256,7 +5267,7 @@ func expandFleet(configured interface{}) *container.Fleet { return &container.Fleet{ Project: config["project"].(string), } -} +} func expandEnableK8sBetaApis(configured interface{}, enabledAPIs []string) *container.K8sBetaAPIConfig { l := configured.([]interface{}) @@ -5414,6 +5425,7 @@ func expandNodePoolAutoConfig(configured interface{}) *container.NodePoolAutoCon if v, ok := config["network_tags"]; ok && len(v.([]interface{})) > 0 { npac.NetworkTags = expandNodePoolAutoConfigNetworkTags(v) } + return npac } @@ -6108,16 +6120,27 @@ func flattenGatewayApiConfig(c *container.GatewayAPIConfig) []map[string]interfa } func flattenFleet(c *container.Fleet) []map[string]interface{} { - if c == nil { - return nil - } - return []map[string]interface{}{ - { - "project": c.Project, - "membership": c.Membership, - "pre_registered": c.PreRegistered, - }, - } + if c == nil { + return nil + } + + // Parse membership_id and membership_location from full membership name. + var membership_id, membership_location string + membershipRE := regexp.MustCompile(`^(//[a-zA-Z0-9\.\-]+)?/?projects/([^/]+)/locations/([a-zA-Z0-9\-]+)/memberships/([^/]+)$`) + if match := membershipRE.FindStringSubmatch(c.Membership); match != nil { + membership_id = match[4] + membership_location = match[3] + } + + return []map[string]interface{}{ + { + "project": c.Project, + "membership": c.Membership, + "membership_id": membership_id, + "membership_location": membership_location, + "pre_registered": c.PreRegistered, + }, + } } func flattenEnableK8sBetaApis(c *container.K8sBetaAPIConfig) []map[string]interface{} { diff --git a/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.erb b/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.erb index c57254c7e06d..910fba9602b0 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.erb +++ b/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.erb @@ -56,6 +56,43 @@ func TestAccContainerCluster_basic(t *testing.T) { }) } +func TestAccContainerCluster_resourceManagerTags(t *testing.T) { + t.Parallel() + + pid := envvar.GetTestProjectFromEnv() + + randomSuffix := acctest.RandString(t, 10) + clusterName := fmt.Sprintf("tf-test-cluster-%s", randomSuffix) + + 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), + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_resourceManagerTags(pid, clusterName, networkName, subnetworkName, randomSuffix), + 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"}, + }, + }, + }) +} + func TestAccContainerCluster_networkingModeRoutes(t *testing.T) { t.Parallel() @@ -2843,9 +2880,9 @@ func TestAccContainerCluster_withAutopilot(t *testing.T) { clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) 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_withAutopilot(pid, containerNetName, clusterName, "us-central1", true, false, ""), @@ -2856,7 +2893,7 @@ func TestAccContainerCluster_withAutopilot(t *testing.T) { { ResourceName: "google_container_cluster.with_autopilot", ImportState: true, - ImportStateVerify: true, + ImportStateVerify: true, ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"}, }, }, @@ -2872,9 +2909,9 @@ func TestAccContainerClusterCustomServiceAccount_withAutopilot(t *testing.T) { serviceAccountName := fmt.Sprintf("tf-test-sa-%s", acctest.RandString(t, 10)) 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_withAutopilot(pid, containerNetName, clusterName, "us-central1", true, false, serviceAccountName), @@ -2891,7 +2928,7 @@ func TestAccContainerClusterCustomServiceAccount_withAutopilot(t *testing.T) { { ResourceName: "google_container_cluster.with_autopilot", ImportState: true, - ImportStateVerify: true, + ImportStateVerify: true, ImportStateVerifyIgnore: []string{"min_master_version", "deletion_protection"}, }, }, @@ -2906,9 +2943,9 @@ func TestAccContainerCluster_errorAutopilotLocation(t *testing.T) { clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) 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_withAutopilot(pid, containerNetName, clusterName, "us-central1-a", true, false, ""), @@ -2926,9 +2963,9 @@ func TestAccContainerCluster_withAutopilotNetworkTags(t *testing.T) { clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) 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_withAutopilot(pid, containerNetName, clusterName, "us-central1", true, true, ""), @@ -4193,7 +4230,7 @@ func TestAccContainerCluster_withGatewayApiConfig(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccContainerCluster_withGatewayApiConfig(clusterName, "CANARY", networkName, subnetworkName), - ExpectError: regexp.MustCompile(`expected gateway_api_config\.0\.channel to be one of \[CHANNEL_DISABLED CHANNEL_EXPERIMENTAL CHANNEL_STANDARD\], got CANARY`), + ExpectError: regexp.MustCompile(`expected gateway_api_config\.0\.channel to be one of [^,]+, got CANARY`), }, { Config: testAccContainerCluster_withGatewayApiConfig(clusterName, "CHANNEL_DISABLED", networkName, subnetworkName), @@ -4444,11 +4481,11 @@ func testAccContainerCluster_withIncompatibleMasterVersionNodeVersion(name strin resource "google_container_cluster" "gke_cluster" { name = "%s" location = "us-central1" - + min_master_version = "1.10.9-gke.5" node_version = "1.10.6-gke.11" initial_node_count = 1 - + } `, name) } @@ -6674,7 +6711,7 @@ resource "google_container_cluster" "with_autoprovisioning" { min_master_version = data.google_container_engine_versions.central1a.latest_master_version initial_node_count = 1 deletion_protection = false - + network = "%s" subnetwork = "%s" @@ -9250,7 +9287,7 @@ resource "google_compute_resource_policy" "policy" { resource "google_container_cluster" "cluster" { name = "%s" location = "us-central1-a" - + node_pool { name = "%s" initial_node_count = 2 @@ -9285,7 +9322,7 @@ func testAccContainerCluster_additional_pod_ranges_config(name string, nameCount } `, podRangeNamesStr) } - + return fmt.Sprintf(` resource "google_compute_network" "main" { name = "%s" @@ -9352,7 +9389,6 @@ func testAccContainerCluster_additional_pod_ranges_config(name string, nameCount `, name, name, name, aprc) } -<% unless version == 'ga' -%> func TestAccContainerCluster_withConfidentialBootDisk(t *testing.T) { t.Parallel() @@ -9521,7 +9557,6 @@ resource "google_container_cluster" "without_confidential_boot_disk" { } `, clusterName, npName, networkName, subnetworkName) } -<% end -%> <% unless version == 'ga' -%> func testAccContainerCluster_withWorkloadALTSConfig(projectID, name, networkName, subnetworkName string, enable bool) string { @@ -9561,3 +9596,86 @@ func testAccContainerCluster_withWorkloadALTSConfig(projectID, name, networkName `, projectID, networkName, subnetworkName, name, enable) } <% end -%> + +func testAccContainerCluster_resourceManagerTags(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { + return fmt.Sprintf(` +data "google_project" "project" { + project_id = "%[1]s" +} + +resource "google_project_iam_binding" "tagHoldAdmin" { + project = "%[1]s" + role = "roles/resourcemanager.tagHoldAdmin" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + ] +} + +resource "google_project_iam_binding" "tagUser" { + project = "%[1]s" + role = "roles/resourcemanager.tagUser" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com", + ] + + depends_on = [google_project_iam_binding.tagHoldAdmin] +} + +resource "time_sleep" "wait_120_seconds" { + create_duration = "120s" + + depends_on = [ + google_project_iam_binding.tagHoldAdmin, + google_project_iam_binding.tagUser + ] +} + +resource "google_tags_tag_key" "key" { + parent = "projects/%[1]s" + short_name = "foobarbaz-%[2]s" + description = "For foo/bar resources" + 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" + description = "For foo resources" +} + +data "google_container_engine_versions" "uscentral1a" { + location = "us-central1-a" +} + +resource "google_container_cluster" "primary" { + name = "%[3]s" + location = "us-central1-a" + min_master_version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + initial_node_count = 1 + + node_config { + 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.key.name}" = "tagValues/${google_tags_tag_value.value.name}" + } + } + + deletion_protection = false + network = "%[4]s" + subnetwork = "%[5]s" + + 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.erb b/mmv1/third_party/terraform/services/container/resource_container_node_pool.go.erb index a4208ccfd799..025ed420fdde 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_node_pool.go.erb +++ b/mmv1/third_party/terraform/services/container/resource_container_node_pool.go.erb @@ -1605,6 +1605,48 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node log.Printf("[INFO] Updated tags for node pool %s", name) } + if d.HasChange(prefix + "node_config.0.resource_manager_tags") { + req := &container.UpdateNodePoolRequest{ + Name: name, + } + if v, ok := d.GetOk(prefix + "node_config.0.resource_manager_tags"); ok { + req.ResourceManagerTags = expandResourceManagerTags(v) + } + + // sets resource manager 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.ResourceManagerTags == nil { + tags := make(map[string]string) + rmTags := &container.ResourceManagerTags{ + Tags: tags, + } + req.ResourceManagerTags = rmTags + } + + updateF := func() error { + clusterNodePoolsUpdateCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.NodePools.Update(nodePoolInfo.fullyQualifiedName(name), 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 resource manager tags", userAgent, + timeout) + } + + if err := retryWhileIncompatibleOperation(timeout, npLockKey, updateF); err != nil { + return err + } + log.Printf("[INFO] Updated resource manager tags for node pool %s", name) + } + if d.HasChange(prefix + "node_config.0.resource_labels") { req := &container.UpdateNodePoolRequest{ Name: name, diff --git a/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.erb b/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.erb index e06180e6c0e9..ec3e852d8ce6 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.erb +++ b/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.erb @@ -37,6 +37,61 @@ func TestAccContainerNodePool_basic(t *testing.T) { }) } +func TestAccContainerNodePool_resourceManagerTags(t *testing.T) { + t.Parallel() + pid := envvar.GetTestProjectFromEnv() + + randomSuffix := acctest.RandString(t, 10) + clusterName := fmt.Sprintf("tf-test-cluster-%s", randomSuffix) + + 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), + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerNodePool_resourceManagerTags(pid, clusterName, networkName, subnetworkName, randomSuffix), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_container_node_pool.primary_nodes", "node_config.0.resource_manager_tags.%"), + ), + }, + { + ResourceName: "google_container_node_pool.primary_nodes", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "cluster"}, + }, + { + Config: testAccContainerNodePool_resourceManagerTagsUpdate1(pid, clusterName, networkName, subnetworkName, randomSuffix), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_container_node_pool.primary_nodes", "node_config.0.resource_manager_tags.%"), + ), + }, + { + ResourceName: "google_container_node_pool.primary_nodes", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "cluster"}, + }, + { + Config: testAccContainerNodePool_resourceManagerTagsUpdate2(pid, clusterName, networkName, subnetworkName, randomSuffix), + }, + { + ResourceName: "google_container_node_pool.primary_nodes", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"min_master_version", "cluster"}, + }, + }, + }) +} + func TestAccContainerNodePool_basicWithClusterId(t *testing.T) { t.Parallel() @@ -4080,8 +4135,6 @@ resource "google_container_node_pool" "np" { } <% end -%> -<% unless version == 'ga' -%> - func TestAccContainerNodePool_withConfidentialBootDisk(t *testing.T) { t.Parallel() @@ -4132,7 +4185,7 @@ resource "google_container_node_pool" "with_confidential_boot_disk" { name = "%s" location = "us-central1-a" cluster = google_container_cluster.cluster.name - + node_config { image_type = "COS_CONTAINERD" boot_disk_kms_key = "%s" @@ -4149,7 +4202,7 @@ resource "google_container_node_pool" "with_confidential_boot_disk" { } func TestAccContainerNodePool_withoutConfidentialBootDisk(t *testing.T) { - t.Parallel() + t.Parallel() cluster := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) np := fmt.Sprintf("tf-test-np-%s", acctest.RandString(t, 10)) @@ -4193,7 +4246,7 @@ resource "google_container_node_pool" "without_confidential_boot_disk" { name = "%s" location = "us-central1-a" cluster = google_container_cluster.cluster.name - + node_config { image_type = "COS_CONTAINERD" oauth_scopes = [ @@ -4207,4 +4260,348 @@ resource "google_container_node_pool" "without_confidential_boot_disk" { } `, cluster, networkName, subnetworkName, np) } -<% end -%> + +func testAccContainerNodePool_resourceManagerTags(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { + return fmt.Sprintf(` +data "google_project" "project" { + project_id = "%[1]s" +} + +resource "google_project_iam_binding" "tagHoldAdmin" { + project = "%[1]s" + role = "roles/resourcemanager.tagHoldAdmin" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + ] +} + +resource "google_project_iam_binding" "tagUser" { + project = "%[1]s" + role = "roles/resourcemanager.tagUser" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com", + ] + + depends_on = [google_project_iam_binding.tagHoldAdmin] +} + +resource "time_sleep" "wait_120_seconds" { + create_duration = "120s" + + depends_on = [ + google_project_iam_binding.tagHoldAdmin, + google_project_iam_binding.tagUser + ] +} + +resource "google_tags_tag_key" "key1" { + parent = "projects/%[1]s" + short_name = "foobarbaz1-%[2]s" + description = "For foo/bar1 resources" + 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" + description = "For foo1 resources" +} + +resource "google_tags_tag_key" "key2" { + parent = "projects/%[1]s" + short_name = "foobarbaz2-%[2]s" + description = "For foo/bar2 resources" + purpose = "GCE_FIREWALL" + purpose_data = { + network = "%[1]s/%[4]s" + } + + depends_on = [google_tags_tag_key.key1] +} + +resource "google_tags_tag_value" "value2" { + parent = "tagKeys/${google_tags_tag_key.key2.name}" + short_name = "foo2-%[2]s" + description = "For foo2 resources" +} + +data "google_container_engine_versions" "uscentral1a" { + location = "us-central1-a" +} + +resource "google_container_cluster" "primary" { + name = "%[3]s" + location = "us-central1-a" + min_master_version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + + # We can't create a cluster with no node pool defined, but we want to only use + # 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 + + deletion_protection = false + network = "%[4]s" + subnetwork = "%[5]s" + + timeouts { + create = "30m" + update = "40m" + } + + depends_on = [time_sleep.wait_120_seconds] +} + +# 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 + + 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 + + resource_manager_tags = { + "tagKeys/${google_tags_tag_key.key1.name}" = "tagValues/${google_tags_tag_value.value1.name}" + } + } +} +`, projectID, randomSuffix, clusterName, networkName, subnetworkName) +} + +func testAccContainerNodePool_resourceManagerTagsUpdate1(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { + return fmt.Sprintf(` +data "google_project" "project" { + project_id = "%[1]s" +} + +resource "google_project_iam_binding" "tagHoldAdmin" { + project = "%[1]s" + role = "roles/resourcemanager.tagHoldAdmin" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + ] +} + +resource "google_project_iam_binding" "tagUser" { + project = "%[1]s" + role = "roles/resourcemanager.tagUser" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com", + ] + + depends_on = [google_project_iam_binding.tagHoldAdmin] +} + +resource "time_sleep" "wait_120_seconds" { + create_duration = "120s" + + depends_on = [ + google_project_iam_binding.tagHoldAdmin, + google_project_iam_binding.tagUser + ] +} + +resource "google_tags_tag_key" "key1" { + parent = "projects/%[1]s" + short_name = "foobarbaz1-%[2]s" + description = "For foo/bar1 resources" + 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" + description = "For foo1 resources" +} + +resource "google_tags_tag_key" "key2" { + parent = "projects/%[1]s" + short_name = "foobarbaz2-%[2]s" + description = "For foo/bar2 resources" + purpose = "GCE_FIREWALL" + purpose_data = { + network = "%[1]s/%[4]s" + } + + depends_on = [google_tags_tag_key.key1] +} + +resource "google_tags_tag_value" "value2" { + parent = "tagKeys/${google_tags_tag_key.key2.name}" + short_name = "foo2-%[2]s" + description = "For foo2 resources" +} + +data "google_container_engine_versions" "uscentral1a" { + location = "us-central1-a" +} + +resource "google_container_cluster" "primary" { + name = "%[3]s" + location = "us-central1-a" + min_master_version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + + # We can't create a cluster with no node pool defined, but we want to only use + # 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 + + deletion_protection = false + network = "%[4]s" + subnetwork = "%[5]s" + + timeouts { + create = "30m" + update = "40m" + } + + depends_on = [time_sleep.wait_120_seconds] +} + +# 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 + + 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 + + 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}" + } + } +} +`, projectID, randomSuffix, clusterName, networkName, subnetworkName) +} + +func testAccContainerNodePool_resourceManagerTagsUpdate2(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { + return fmt.Sprintf(` +data "google_project" "project" { + project_id = "%[1]s" +} + +resource "google_project_iam_binding" "tagHoldAdmin" { + project = "%[1]s" + role = "roles/resourcemanager.tagHoldAdmin" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + ] +} + +resource "google_project_iam_binding" "tagUser" { + project = "%[1]s" + role = "roles/resourcemanager.tagUser" + members = [ + "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com", + "serviceAccount:${data.google_project.project.number}@cloudservices.gserviceaccount.com", + ] + + depends_on = [google_project_iam_binding.tagHoldAdmin] +} + +resource "time_sleep" "wait_120_seconds" { + create_duration = "120s" + + depends_on = [ + google_project_iam_binding.tagHoldAdmin, + google_project_iam_binding.tagUser + ] +} + +resource "google_tags_tag_key" "key1" { + parent = "projects/%[1]s" + short_name = "foobarbaz1-%[2]s" + description = "For foo/bar1 resources" + 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" + description = "For foo1 resources" +} + +resource "google_tags_tag_key" "key2" { + parent = "projects/%[1]s" + short_name = "foobarbaz2-%[2]s" + description = "For foo/bar2 resources" + purpose = "GCE_FIREWALL" + purpose_data = { + network = "%[1]s/%[4]s" + } + + depends_on = [google_tags_tag_key.key1] +} + +resource "google_tags_tag_value" "value2" { + parent = "tagKeys/${google_tags_tag_key.key2.name}" + short_name = "foo2-%[2]s" + description = "For foo2 resources" +} + +data "google_container_engine_versions" "uscentral1a" { + location = "us-central1-a" +} + +resource "google_container_cluster" "primary" { + name = "%[3]s" + location = "us-central1-a" + min_master_version = data.google_container_engine_versions.uscentral1a.release_channel_latest_version["STABLE"] + + # We can't create a cluster with no node pool defined, but we want to only use + # 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 + + deletion_protection = false + network = "%[4]s" + subnetwork = "%[5]s" + + timeouts { + create = "30m" + update = "40m" + } + + depends_on = [time_sleep.wait_120_seconds] +} + +# 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 + + 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 + } +} +`, projectID, randomSuffix, clusterName, networkName, subnetworkName) +} diff --git a/mmv1/third_party/terraform/services/dataflow/resource_dataflow_job.go.erb b/mmv1/third_party/terraform/services/dataflow/resource_dataflow_job.go.erb index 9ecdbd2443ff..e2a0bb2f1b95 100644 --- a/mmv1/third_party/terraform/services/dataflow/resource_dataflow_job.go.erb +++ b/mmv1/third_party/terraform/services/dataflow/resource_dataflow_job.go.erb @@ -371,6 +371,9 @@ func resourceDataflowJobRead(d *schema.ResourceData, meta interface{}) error { if err := d.Set("effective_labels", job.Labels); err != nil { return fmt.Errorf("Error setting effective_labels: %s", err) } + if job.Environment == nil { + return fmt.Errorf("Error accessing Environment proto: proto is nil") + } if err := d.Set("kms_key_name", job.Environment.ServiceKmsKeyName); err != nil { return fmt.Errorf("Error setting kms_key_name: %s", err) } diff --git a/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_app_attest_config_test.go b/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_app_attest_config_test.go new file mode 100644 index 000000000000..a1b05b852feb --- /dev/null +++ b/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_app_attest_config_test.go @@ -0,0 +1,59 @@ +package firebaseappcheck_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +func TestAccFirebaseAppCheckAppAttestConfig_firebaseAppCheckAppAttestConfigUpdate(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project_id": envvar.GetTestProjectFromEnv(), + "team_id": "9987654321", + "random_suffix": acctest.RandString(t, 10), + "token_ttl": "7200s", + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + "time": {}, + }, + Steps: []resource.TestStep{ + { + Config: testAccFirebaseAppCheckAppAttestConfig_firebaseAppCheckAppAttestConfigMinimalExample(context), + }, + { + ResourceName: "google_firebase_app_check_app_attest_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"app_id"}, + }, + { + Config: testAccFirebaseAppCheckAppAttestConfig_firebaseAppCheckAppAttestConfigFullExample(context), + }, + { + ResourceName: "google_firebase_app_check_app_attest_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"app_id"}, + }, + { + Config: testAccFirebaseAppCheckAppAttestConfig_firebaseAppCheckAppAttestConfigMinimalExample(context), + }, + { + ResourceName: "google_firebase_app_check_app_attest_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"app_id"}, + }, + }, + }) +} diff --git a/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_play_integrity_config_test.go b/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_play_integrity_config_test.go new file mode 100644 index 000000000000..c97df9fd1fab --- /dev/null +++ b/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_play_integrity_config_test.go @@ -0,0 +1,58 @@ +package firebaseappcheck_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +func TestAccFirebaseAppCheckPlayIntegrityConfig_firebaseAppCheckPlayIntegrityConfigUpdate(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project_id": envvar.GetTestProjectFromEnv(), + "random_suffix": acctest.RandString(t, 10), + "token_ttl": "7200s", + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + "time": {}, + }, + Steps: []resource.TestStep{ + { + Config: testAccFirebaseAppCheckPlayIntegrityConfig_firebaseAppCheckPlayIntegrityConfigMinimalExample(context), + }, + { + ResourceName: "google_firebase_app_check_play_integrity_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"app_id"}, + }, + { + Config: testAccFirebaseAppCheckPlayIntegrityConfig_firebaseAppCheckPlayIntegrityConfigFullExample(context), + }, + { + ResourceName: "google_firebase_app_check_play_integrity_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"app_id"}, + }, + { + Config: testAccFirebaseAppCheckPlayIntegrityConfig_firebaseAppCheckPlayIntegrityConfigMinimalExample(context), + }, + { + ResourceName: "google_firebase_app_check_play_integrity_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"app_id"}, + }, + }, + }) +} diff --git a/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_recaptcha_enterprise_config_test.go b/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_recaptcha_enterprise_config_test.go new file mode 100644 index 000000000000..6d0a91601ea7 --- /dev/null +++ b/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_recaptcha_enterprise_config_test.go @@ -0,0 +1,57 @@ +package firebaseappcheck_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +func TestAccFirebaseAppCheckRecaptchaEnterpriseConfig_firebaseAppCheckRecaptchaEnterpriseConfigUpdate(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project_id": envvar.GetTestProjectFromEnv(), + "token_ttl": "7200s", + "site_key": "6LdpMXIpAAAAANkwWQPgEdjEhal7ugkH9RK9ytuw", + "random_suffix": acctest.RandString(t, 10), + } + + contextUpdated := map[string]interface{}{ + "project_id": envvar.GetTestProjectFromEnv(), + "token_ttl": "3800s", + "site_key": "7LdpMXIpAAAAANkwWQPgEdjEhal7ugkH9RK9ytuw", + "random_suffix": context["random_suffix"], + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + "time": {}, + }, + Steps: []resource.TestStep{ + { + Config: testAccFirebaseAppCheckRecaptchaEnterpriseConfig_firebaseAppCheckRecaptchaEnterpriseConfigBasicExample(context), + }, + { + ResourceName: "google_firebase_app_check_recaptcha_enterprise_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"app_id"}, + }, + { + Config: testAccFirebaseAppCheckRecaptchaEnterpriseConfig_firebaseAppCheckRecaptchaEnterpriseConfigBasicExample(contextUpdated), + }, + { + ResourceName: "google_firebase_app_check_recaptcha_enterprise_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"app_id"}, + }, + }, + }) +} diff --git a/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_recaptcha_v3_config_test.go b/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_recaptcha_v3_config_test.go new file mode 100644 index 000000000000..961215b8f63e --- /dev/null +++ b/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_recaptcha_v3_config_test.go @@ -0,0 +1,57 @@ +package firebaseappcheck_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +func TestAccFirebaseAppCheckRecaptchaV3Config_firebaseAppCheckRecaptchaV3ConfigUpdate(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project_id": envvar.GetTestProjectFromEnv(), + "token_ttl": "7200s", + "site_secret": "6Lf9YnQpAAAAAC3-MHmdAllTbPwTZxpUw5d34YzX", + "random_suffix": acctest.RandString(t, 10), + } + + contextUpdated := map[string]interface{}{ + "project_id": envvar.GetTestProjectFromEnv(), + "token_ttl": "3800s", + "site_secret": "7Lf9YnQpAAAAAC3-MHmdAllTbPwTZxpUw5d34YzX", + "random_suffix": context["random_suffix"], + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + "time": {}, + }, + Steps: []resource.TestStep{ + { + Config: testAccFirebaseAppCheckRecaptchaV3Config_firebaseAppCheckRecaptchaV3ConfigBasicExample(context), + }, + { + ResourceName: "google_firebase_app_check_recaptcha_v3_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"site_secret", "app_id"}, + }, + { + Config: testAccFirebaseAppCheckRecaptchaV3Config_firebaseAppCheckRecaptchaV3ConfigBasicExample(contextUpdated), + }, + { + ResourceName: "google_firebase_app_check_recaptcha_v3_config.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"site_secret", "app_id"}, + }, + }, + }) +} diff --git a/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_service_config_test.go b/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_service_config_test.go index 2ea26c572329..aba5a807434f 100644 --- a/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_service_config_test.go +++ b/mmv1/third_party/terraform/services/firebaseappcheck/resource_firebase_app_check_service_config_test.go @@ -14,7 +14,7 @@ func TestAccFirebaseAppCheckServiceConfig_firebaseAppCheckServiceConfigUpdate(t context := map[string]interface{}{ "project_id": envvar.GetTestProjectFromEnv(), - "service_id": "firestore.googleapis.com", + "service_id": "identitytoolkit.googleapis.com", "random_suffix": acctest.RandString(t, 10), } diff --git a/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_membership_binding_test.go b/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_membership_binding_test.go index e70ad26785a7..92cd069eb444 100644 --- a/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_membership_binding_test.go +++ b/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_membership_binding_test.go @@ -49,7 +49,7 @@ func TestAccGKEHub2MembershipBinding_gkehubMembershipBindingBasicExample_update( func testAccGKEHub2MembershipBinding_gkehubMembershipBindingBasicExample_basic(context map[string]interface{}) string { return acctest.Nprintf(` resource "google_container_cluster" "primary" { - name = "basiccluster%{random_suffix}" + name = "tf-test-basic-cluster%{random_suffix}" location = "us-central1-a" initial_node_count = 1 deletion_protection = false @@ -93,7 +93,7 @@ resource "google_gke_hub_membership_binding" "example" { func testAccGKEHub2MembershipBinding_gkehubMembershipBindingBasicExample_update(context map[string]interface{}) string { return acctest.Nprintf(` resource "google_container_cluster" "primary" { - name = "basiccluster%{random_suffix}" + name = "tf-test-basic-cluster%{random_suffix}" location = "us-central1-a" initial_node_count = 1 deletion_protection = false diff --git a/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_scope_test.go b/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_scope_test.go index 967fe08c4bc2..be00b628a7c6 100644 --- a/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_scope_test.go +++ b/mmv1/third_party/terraform/services/gkehub2/resource_gke_hub_scope_test.go @@ -47,6 +47,11 @@ func testAccGKEHub2Scope_gkehubScopeBasicExample_basic(context map[string]interf return acctest.Nprintf(` resource "google_gke_hub_scope" "scope" { scope_id = "tf-test-scope%{random_suffix}" + namespace_labels = { + keyb = "valueb" + keya = "valuea" + keyc = "valuec" + } labels = { keyb = "valueb" keya = "valuea" @@ -60,6 +65,11 @@ func testAccGKEHub2Scope_gkehubScopeBasicExample_update(context map[string]inter return acctest.Nprintf(` resource "google_gke_hub_scope" "scope" { scope_id = "tf-test-scope%{random_suffix}" + namespace_labels = { + updated_keyb = "updated_valueb" + updated_keya = "updated_valuea" + updated_keyc = "updated_valuec" + } labels = { updated_keyb = "updated_valueb" updated_keya = "updated_valuea" diff --git a/mmv1/third_party/terraform/services/healthcare/resource_healthcare_hl7_v2_store_test.go.erb b/mmv1/third_party/terraform/services/healthcare/resource_healthcare_hl7_v2_store_test.go.erb index aec48d059681..4491a090fb29 100644 --- a/mmv1/third_party/terraform/services/healthcare/resource_healthcare_hl7_v2_store_test.go.erb +++ b/mmv1/third_party/terraform/services/healthcare/resource_healthcare_hl7_v2_store_test.go.erb @@ -162,6 +162,7 @@ func testGoogleHealthcareHl7V2Store_basic(hl7_v2StoreName, datasetName string) s resource "google_healthcare_hl7_v2_store" "default" { name = "%s" dataset = google_healthcare_dataset.dataset.id + reject_duplicate_message = true } resource "google_healthcare_dataset" "dataset" { diff --git a/mmv1/third_party/terraform/services/migrationcenter/resource_migration_center_preference_set_test.go b/mmv1/third_party/terraform/services/migrationcenter/resource_migration_center_preference_set_test.go new file mode 100644 index 000000000000..739265f52527 --- /dev/null +++ b/mmv1/third_party/terraform/services/migrationcenter/resource_migration_center_preference_set_test.go @@ -0,0 +1,88 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package migrationcenter_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccMigrationCenterPreferenceSet_preferenceSetUpdate(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: testAccCheckMigrationCenterPreferenceSetDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccMigrationCenterPreferenceSet_preferenceSetStart(context), + }, + { + ResourceName: "google_migration_center_preference_set.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location", "preference_set_id"}, + }, + }, + }) +} + +func testAccMigrationCenterPreferenceSet_preferenceSetStart(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_migration_center_preference_set" "default" { + location = "us-central1" + preference_set_id = "tf-test-preference-set-test%{random_suffix}" + description = "Terraform integration test description" + display_name = "Terraform integration test display" + virtual_machine_preferences { + vmware_engine_preferences { + cpu_overcommit_ratio = 1.5 + memory_overcommit_ratio = 2.0 + } + sizing_optimization_strategy = "SIZING_OPTIMIZATION_STRATEGY_SAME_AS_SOURCE" + } +} +`, context) +} + +func testAccMigrationCenterPreferenceSet_preferenceSetUpdate(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_migration_center_preference_set" "default" { + location = "us-central1" + preference_set_id = "tf-test-preference-set-test%{random_suffix}" + description = "Terraform integration test updated description" + display_name = "Terraform integration test updated display" + virtual_machine_preferences { + vmware_engine_preferences { + cpu_overcommit_ratio = 1.4 + } + sizing_optimization_strategy = "SIZING_OPTIMIZATION_STRATEGY_MODERATE" + commitment_plan = "COMMITMENT_PLAN_ONE_YEAR" + preferred_regions = ["us-central1"] + } +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/netapp/resource_netapp_volume_replication_test.go b/mmv1/third_party/terraform/services/netapp/resource_netapp_volume_replication_test.go new file mode 100644 index 000000000000..e663c41e7274 --- /dev/null +++ b/mmv1/third_party/terraform/services/netapp/resource_netapp_volume_replication_test.go @@ -0,0 +1,306 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package netapp_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccNetappVolumeReplication_netappVolumeReplicationCreateExample_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "network_name": acctest.BootstrapSharedServiceNetworkingConnection(t, "gcnv-network-config-1", acctest.ServiceNetworkWithParentService("netapp.servicenetworking.goog")), + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckNetappVolumeReplicationDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_basic(context), + }, + { + ResourceName: "google_netapp_volume_replication.test_replication", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"destination_volume_parameters", "location", "volume_name", "name", "delete_destination_volume", "replication_enabled", "force_stopping", "wait_for_mirror", "labels", "terraform_labels"}, + }, + { + Config: testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_stop(context), + }, + { + ResourceName: "google_netapp_volume_replication.test_replication", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"destination_volume_parameters", "location", "volume_name", "name", "delete_destination_volume", "replication_enabled", "force_stopping", "wait_for_mirror", "labels", "terraform_labels"}, + }, + { + Config: testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_resume(context), + }, + { + ResourceName: "google_netapp_volume_replication.test_replication", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"destination_volume_parameters", "location", "volume_name", "name", "delete_destination_volume", "replication_enabled", "force_stopping", "wait_for_mirror", "labels", "terraform_labels"}, + }, + { + Config: testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_update(context), + }, + { + ResourceName: "google_netapp_volume_replication.test_replication", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"destination_volume_parameters", "location", "volume_name", "name", "delete_destination_volume", "replication_enabled", "force_stopping", "wait_for_mirror", "labels", "terraform_labels"}, + }, + }, + }) +} + +// Basic replication +func testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_basic(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_network" "default" { + name = "%{network_name}" +} + +resource "google_netapp_storage_pool" "source_pool" { + name = "tf-test-source-pool%{random_suffix}" + location = "us-central1" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_storage_pool" "destination_pool" { + name = "tf-test-destination-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "source_volume" { + location = google_netapp_storage_pool.source_pool.location + name = "tf-test-source-volume%{random_suffix}" + capacity_gib = 100 + share_name = "tf-test-source-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.source_pool.name + protocols = [ + "NFSV3" + ] + deletion_policy = "FORCE" +} + +resource "google_netapp_volume_replication" "test_replication" { + depends_on = [google_netapp_volume.source_volume] + location = google_netapp_volume.source_volume.location + volume_name = google_netapp_volume.source_volume.name + name = "tf-test-test-replication%{random_suffix}" + replication_schedule = "EVERY_10_MINUTES" + destination_volume_parameters { + storage_pool = google_netapp_storage_pool.destination_pool.id + volume_id = "tf-test-destination-volume%{random_suffix}" + # Keeping the share_name of source and destination the same + # simplifies implementing client failover concepts + share_name = "tf-test-source-volume%{random_suffix}" + description = "This is a replicated volume" + } + delete_destination_volume = true + wait_for_mirror = true +} +`, context) +} + +// Update parameters +func testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_update(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_network" "default" { + name = "%{network_name}" +} + +resource "google_netapp_storage_pool" "source_pool" { + name = "tf-test-source-pool%{random_suffix}" + location = "us-central1" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_storage_pool" "destination_pool" { + name = "tf-test-destination-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "source_volume" { + location = google_netapp_storage_pool.source_pool.location + name = "tf-test-source-volume%{random_suffix}" + capacity_gib = 100 + share_name = "tf-test-source-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.source_pool.name + protocols = [ + "NFSV3" + ] + deletion_policy = "FORCE" +} + +resource "google_netapp_volume_replication" "test_replication" { + depends_on = [google_netapp_volume.source_volume] + location = google_netapp_volume.source_volume.location + volume_name = google_netapp_volume.source_volume.name + name = "tf-test-test-replication%{random_suffix}" + replication_schedule = "EVERY_10_MINUTES" + description = "This is a replication resource" + labels = { + key = "test" + value = "replication" + } + destination_volume_parameters { + storage_pool = google_netapp_storage_pool.destination_pool.id + volume_id = "tf-test-destination-volume%{random_suffix}" + # Keeping the share_name of source and destination the same + # simplifies implementing client failover concepts + share_name = "tf-test-source-volume%{random_suffix}" + description = "This is a replicated volume" + } + replication_enabled = true + delete_destination_volume = true + force_stopping = true + wait_for_mirror = true +} +`, context) +} + +// Stop replication +func testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_stop(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_network" "default" { + name = "%{network_name}" +} + +resource "google_netapp_storage_pool" "source_pool" { + name = "tf-test-source-pool%{random_suffix}" + location = "us-central1" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_storage_pool" "destination_pool" { + name = "tf-test-destination-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "source_volume" { + location = google_netapp_storage_pool.source_pool.location + name = "tf-test-source-volume%{random_suffix}" + capacity_gib = 100 + share_name = "tf-test-source-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.source_pool.name + protocols = [ + "NFSV3" + ] + deletion_policy = "FORCE" +} + +resource "google_netapp_volume_replication" "test_replication" { + depends_on = [google_netapp_volume.source_volume] + location = google_netapp_volume.source_volume.location + volume_name = google_netapp_volume.source_volume.name + name = "tf-test-test-replication%{random_suffix}" + replication_schedule = "EVERY_10_MINUTES" + description = "This is a replication resource" + labels = { + key = "test" + value = "replication2" + } + destination_volume_parameters { + storage_pool = google_netapp_storage_pool.destination_pool.id + volume_id = "tf-test-destination-volume%{random_suffix}" + # Keeping the share_name of source and destination the same + # simplifies implementing client failover concepts + share_name = "tf-test-source-volume%{random_suffix}" + description = "This is a replicated volume" + } + replication_enabled = false + delete_destination_volume = true + force_stopping = true + wait_for_mirror = true +} +`, context) +} + +// resume replication +func testAccNetappVolumeReplication_netappVolumeReplicationCreateExample_resume(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_network" "default" { + name = "%{network_name}" +} + +resource "google_netapp_storage_pool" "source_pool" { + name = "tf-test-source-pool%{random_suffix}" + location = "us-central1" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_storage_pool" "destination_pool" { + name = "tf-test-destination-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = 2048 + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "source_volume" { + location = google_netapp_storage_pool.source_pool.location + name = "tf-test-source-volume%{random_suffix}" + capacity_gib = 100 + share_name = "tf-test-source-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.source_pool.name + protocols = [ + "NFSV3" + ] + deletion_policy = "FORCE" +} + +resource "google_netapp_volume_replication" "test_replication" { + depends_on = [google_netapp_volume.source_volume] + location = google_netapp_volume.source_volume.location + volume_name = google_netapp_volume.source_volume.name + name = "tf-test-test-replication%{random_suffix}" + replication_schedule = "HOURLY" + description = "This is a replication resource" + labels = { + key = "test" + value = "replication2" + } + destination_volume_parameters { + storage_pool = google_netapp_storage_pool.destination_pool.id + volume_id = "tf-test-destination-volume%{random_suffix}" + # Keeping the share_name of source and destination the same + # simplifies implementing client failover concepts + share_name = "tf-test-source-volume%{random_suffix}" + description = "This is a replicated volume" + } + replication_enabled = true + delete_destination_volume = true + force_stopping = true + wait_for_mirror = true +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/netapp/resource_netapp_volume_test.go b/mmv1/third_party/terraform/services/netapp/resource_netapp_volume_test.go index 386e29eb7f42..81574ec62d00 100644 --- a/mmv1/third_party/terraform/services/netapp/resource_netapp_volume_test.go +++ b/mmv1/third_party/terraform/services/netapp/resource_netapp_volume_test.go @@ -1,20 +1,6 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -// ---------------------------------------------------------------------------- -// -// *** AUTO GENERATED CODE *** Type: MMv1 *** -// -// ---------------------------------------------------------------------------- -// -// This file is automatically generated by Magic Modules and manual -// changes will be clobbered when the file is regenerated. -// -// Please read more about how to change this file in -// .github/CONTRIBUTING.md. -// -// ---------------------------------------------------------------------------- - package netapp_test import ( @@ -25,7 +11,7 @@ import ( "github.com/hashicorp/terraform-provider-google/google/acctest" ) -func TestAccNetappVolume_volumeBasicExample_update(t *testing.T) { +func TestAccNetappVolume_netappVolumeBasicExample_update(t *testing.T) { t.Parallel() context := map[string]interface{}{ @@ -45,7 +31,7 @@ func TestAccNetappVolume_volumeBasicExample_update(t *testing.T) { ResourceName: "google_netapp_volume.test_volume", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"location", "name", "labels", "terraform_labels", "deletion_policy"}, + ImportStateVerifyIgnore: []string{"restore_parameters", "location", "name", "deletion_policy", "labels", "terraform_labels"}, }, { Config: testAccNetappVolume_volumeBasicExample_full(context), }, @@ -53,7 +39,7 @@ func TestAccNetappVolume_volumeBasicExample_update(t *testing.T) { ResourceName: "google_netapp_volume.test_volume", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"location", "name", "labels", "terraform_labels", "deletion_policy"}, + ImportStateVerifyIgnore: []string{"restore_parameters", "location", "name", "deletion_policy", "labels", "terraform_labels"}, }, { Config: testAccNetappVolume_volumeBasicExample_update(context), @@ -62,7 +48,7 @@ func TestAccNetappVolume_volumeBasicExample_update(t *testing.T) { ResourceName: "google_netapp_volume.test_volume", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"location", "name", "labels", "terraform_labels", "deletion_policy"}, + ImportStateVerifyIgnore: []string{"restore_parameters", "location", "name", "deletion_policy", "labels", "terraform_labels"}, }, { Config: testAccNetappVolume_volumeBasicExample_updatesnapshot(context), @@ -71,7 +57,16 @@ func TestAccNetappVolume_volumeBasicExample_update(t *testing.T) { ResourceName: "google_netapp_volume.test_volume", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"location", "name", "labels", "terraform_labels", "deletion_policy"}, + ImportStateVerifyIgnore: []string{"restore_parameters", "location", "name", "deletion_policy", "labels", "terraform_labels"}, + }, + { + Config: testAccNetappVolume_volumeBasicExample_createclonevolume(context), + }, + { + ResourceName: "google_netapp_volume.test_volume_clone", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"restore_parameters", "location", "name", "deletion_policy", "labels", "terraform_labels"}, }, }, }) @@ -80,24 +75,24 @@ func TestAccNetappVolume_volumeBasicExample_update(t *testing.T) { func testAccNetappVolume_volumeBasicExample_basic(context map[string]interface{}) string { return acctest.Nprintf(` resource "google_netapp_storage_pool" "default" { - name = "tf-test-test-pool%{random_suffix}" - location = "us-west2" - service_level = "PREMIUM" - capacity_gib = "2048" - network = data.google_compute_network.default.id + name = "tf-test-test-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = "2048" + network = data.google_compute_network.default.id } resource "google_netapp_volume" "test_volume" { - location = "us-west2" - name = "tf-test-test-volume%{random_suffix}" - capacity_gib = "100" - share_name = "tf-test-test-volume%{random_suffix}" - storage_pool = google_netapp_storage_pool.default.name - protocols = ["NFSV3"] + location = "us-west2" + name = "tf-test-test-volume%{random_suffix}" + capacity_gib = "100" + share_name = "tf-test-test-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.default.name + protocols = ["NFSV3"] } data "google_compute_network" "default" { - name = "%{network_name}" + name = "%{network_name}" } `, context) } @@ -105,237 +100,298 @@ data "google_compute_network" "default" { func testAccNetappVolume_volumeBasicExample_full(context map[string]interface{}) string { return acctest.Nprintf(` resource "google_netapp_storage_pool" "default" { - name = "tf-test-test-pool%{random_suffix}" - location = "us-west2" - service_level = "PREMIUM" - capacity_gib = "2048" - network = data.google_compute_network.default.id + name = "tf-test-test-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = "2048" + network = data.google_compute_network.default.id } - + resource "google_netapp_storage_pool" "default2" { - name = "tf-test-pool%{random_suffix}" - location = "us-west2" - service_level = "EXTREME" - capacity_gib = "2048" - network = data.google_compute_network.default.id + name = "tf-test-pool%{random_suffix}" + location = "us-west2" + service_level = "EXTREME" + capacity_gib = "2048" + network = data.google_compute_network.default.id } - + resource "google_netapp_volume" "test_volume" { - location = "us-west2" - name = "tf-test-test-volume%{random_suffix}" - capacity_gib = "100" - share_name = "tf-test-test-volume%{random_suffix}" - storage_pool = google_netapp_storage_pool.default.name - protocols = ["NFSV3"] - smb_settings = [] - unix_permissions = "0770" - labels = { - key= "test" - value= "pool" - } - description = "This is a test description" - snapshot_directory = false - security_style = "UNIX" - kerberos_enabled = false - export_policy { - rules { - access_type = "READ_ONLY" - allowed_clients = "0.0.0.0/0" - has_root_access = "false" - kerberos5_read_only = false - kerberos5_read_write = false - kerberos5i_read_only = false - kerberos5i_read_write = false - kerberos5p_read_only = false - kerberos5p_read_write = false - nfsv3 = true - nfsv4 = false - } - rules { - access_type = "READ_WRITE" - allowed_clients = "10.2.3.4,10.2.3.5" - has_root_access = "true" - kerberos5_read_only = false - kerberos5_read_write = false - kerberos5i_read_only = false - kerberos5i_read_write = false - kerberos5p_read_only = false - kerberos5p_read_write = false - nfsv3 = true - nfsv4 = false - } - } - restricted_actions = [] - snapshot_policy { - daily_schedule { - snapshots_to_keep = 2 - } - enabled = true - hourly_schedule { - snapshots_to_keep = 2 - } - monthly_schedule { - snapshots_to_keep = 4 - } - weekly_schedule { - snapshots_to_keep = 2 - } - } + location = "us-west2" + name = "tf-test-test-volume%{random_suffix}" + capacity_gib = "100" + share_name = "tf-test-test-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.default.name + protocols = ["NFSV3"] + smb_settings = [] + unix_permissions = "0770" + labels = { + key= "test" + value= "pool" + } + description = "This is a test description" + snapshot_directory = false + security_style = "UNIX" + kerberos_enabled = false + export_policy { + rules { + access_type = "READ_ONLY" + allowed_clients = "0.0.0.0/0" + has_root_access = "false" + kerberos5_read_only = false + kerberos5_read_write = false + kerberos5i_read_only = false + kerberos5i_read_write = false + kerberos5p_read_only = false + kerberos5p_read_write = false + nfsv3 = true + nfsv4 = false + } + rules { + access_type = "READ_WRITE" + allowed_clients = "10.2.3.4,10.2.3.5" + has_root_access = "true" + kerberos5_read_only = false + kerberos5_read_write = false + kerberos5i_read_only = false + kerberos5i_read_write = false + kerberos5p_read_only = false + kerberos5p_read_write = false + nfsv3 = true + nfsv4 = false + } + } + restricted_actions = [] + snapshot_policy { + daily_schedule { + snapshots_to_keep = 2 + } + enabled = true + hourly_schedule { + snapshots_to_keep = 2 + } + monthly_schedule { + snapshots_to_keep = 4 + } + weekly_schedule { + snapshots_to_keep = 2 + } + } } data "google_compute_network" "default" { - name = "%{network_name}" + name = "%{network_name}" } `, context) } func testAccNetappVolume_volumeBasicExample_update(context map[string]interface{}) string { return acctest.Nprintf(` - resource "google_netapp_storage_pool" "default" { - name = "tf-test-test-pool%{random_suffix}" - location = "us-west2" - service_level = "PREMIUM" - capacity_gib = "2048" - network = data.google_compute_network.default.id + name = "tf-test-test-pool%{random_suffix}" + location = "us-west2" + service_level = "PREMIUM" + capacity_gib = "2048" + network = data.google_compute_network.default.id } - + resource "google_netapp_storage_pool" "default2" { - name = "tf-test-pool%{random_suffix}" - location = "us-west2" - service_level = "EXTREME" - capacity_gib = "2048" - network = data.google_compute_network.default.id + name = "tf-test-pool%{random_suffix}" + location = "us-west2" + service_level = "EXTREME" + capacity_gib = "2048" + network = data.google_compute_network.default.id } resource "google_netapp_volume" "test_volume" { - location = "us-west2" - name = "tf-test-test-volume%{random_suffix}" - capacity_gib = "200" - share_name = "tf-test-test-volume%{random_suffix}" - storage_pool = google_netapp_storage_pool.default2.name - protocols = ["NFSV3"] - smb_settings = [] - unix_permissions = "0740" - labels = {} - description = "" - snapshot_directory = true - security_style = "UNIX" - kerberos_enabled = false - export_policy { - rules { - access_type = "READ_WRITE" - allowed_clients = "0.0.0.0/0" - has_root_access = "true" - kerberos5_read_only = false - kerberos5_read_write = false - kerberos5i_read_only = false - kerberos5i_read_write = false - kerberos5p_read_only = false - kerberos5p_read_write = false - nfsv3 = true - nfsv4 = false - } - } - restricted_actions = ["DELETE"] - snapshot_policy { - enabled = true - daily_schedule { - hour = 1 - minute = 2 - snapshots_to_keep = 1 - } - hourly_schedule { - minute = 10 - snapshots_to_keep = 1 - } - monthly_schedule { - days_of_month = "2" - hour = 3 - minute = 4 - snapshots_to_keep = 1 - } - weekly_schedule { - day = "Monday" - hour = 5 - minute = 6 - snapshots_to_keep = 1 - } - } + location = "us-west2" + name = "tf-test-test-volume%{random_suffix}" + capacity_gib = "200" + share_name = "tf-test-test-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.default2.name + protocols = ["NFSV3"] + smb_settings = [] + unix_permissions = "0740" + labels = {} + description = "" + snapshot_directory = true + security_style = "UNIX" + kerberos_enabled = false + export_policy { + rules { + access_type = "READ_WRITE" + allowed_clients = "0.0.0.0/0" + has_root_access = "true" + kerberos5_read_only = false + kerberos5_read_write = false + kerberos5i_read_only = false + kerberos5i_read_write = false + kerberos5p_read_only = false + kerberos5p_read_write = false + nfsv3 = true + nfsv4 = false + } + } + # Delete protection only gets active after an NFS client mounts. + # Setting it here is save, volume can still be deleted. + deletion_policy = "FORCE" + snapshot_policy { + enabled = true + daily_schedule { + hour = 1 + minute = 2 + snapshots_to_keep = 1 + } + hourly_schedule { + minute = 10 + snapshots_to_keep = 1 + } + monthly_schedule { + days_of_month = "2" + hour = 3 + minute = 4 + snapshots_to_keep = 1 + } + weekly_schedule { + day = "Monday" + hour = 5 + minute = 6 + snapshots_to_keep = 1 + } + } } data "google_compute_network" "default" { - name = "%{network_name}" + name = "%{network_name}" } `, context) } func testAccNetappVolume_volumeBasicExample_updatesnapshot(context map[string]interface{}) string { return acctest.Nprintf(` +resource "google_netapp_storage_pool" "default2" { + name = "tf-test-pool%{random_suffix}" + location = "us-west2" + service_level = "EXTREME" + capacity_gib = "2048" + network = data.google_compute_network.default.id +} + +resource "google_netapp_volume" "test_volume" { + location = "us-west2" + name = "tf-test-test-volume%{random_suffix}" + capacity_gib = "200" + share_name = "tf-test-test-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.default2.name + protocols = ["NFSV3"] + smb_settings = [] + unix_permissions = "0740" + labels = {} + description = "" + snapshot_directory = true + security_style = "UNIX" + kerberos_enabled = false + export_policy { + rules { + access_type = "READ_WRITE" + allowed_clients = "0.0.0.0/0" + has_root_access = "true" + kerberos5_read_only = false + kerberos5_read_write = false + kerberos5i_read_only = false + kerberos5i_read_write = false + kerberos5p_read_only = false + kerberos5p_read_write = false + nfsv3 = true + nfsv4 = false + } + } + # Delete protection only gets active after an NFS client mounts. + # Setting it here is save, volume can still be deleted. + restricted_actions = ["DELETE"] + deletion_policy = "FORCE" +} -resource "google_netapp_storage_pool" "default" { - name = "tf-test-test-pool%{random_suffix}" - location = "us-west2" - service_level = "PREMIUM" - capacity_gib = "2048" - network = data.google_compute_network.default.id +resource "google_netapp_volume_snapshot" "test-snapshot" { + depends_on = [google_netapp_volume.test_volume] + location = google_netapp_volume.test_volume.location + volume_name = google_netapp_volume.test_volume.name + name = "test-snapshot" +} + +data "google_compute_network" "default" { + name = "%{network_name}" +} + `, context) } - + +// Tests creating a new volume (clone) from a snapshot created from existing volume +func testAccNetappVolume_volumeBasicExample_createclonevolume(context map[string]interface{}) string { + return acctest.Nprintf(` resource "google_netapp_storage_pool" "default2" { - name = "tf-test-pool%{random_suffix}" - location = "us-west2" - service_level = "EXTREME" - capacity_gib = "2048" - network = data.google_compute_network.default.id + name = "tf-test-pool%{random_suffix}" + location = "us-west2" + service_level = "EXTREME" + capacity_gib = "2048" + network = data.google_compute_network.default.id } - + resource "google_netapp_volume" "test_volume" { - location = "us-west2" - name = "tf-test-test-volume%{random_suffix}" - capacity_gib = "200" - share_name = "tf-test-test-volume%{random_suffix}" - storage_pool = google_netapp_storage_pool.default2.name - protocols = ["NFSV3"] - smb_settings = [] - unix_permissions = "0740" - labels = {} - description = "" - snapshot_directory = true - security_style = "UNIX" - kerberos_enabled = false - export_policy { - rules { - access_type = "READ_WRITE" - allowed_clients = "0.0.0.0/0" - has_root_access = "true" - kerberos5_read_only = false - kerberos5_read_write = false - kerberos5i_read_only = false - kerberos5i_read_write = false - kerberos5p_read_only = false - kerberos5p_read_write = false - nfsv3 = true - nfsv4 = false - } - } - # Delete protection only gets active after an NFS client mounts. - # Setting it here is save, volume can still be deleted. - restricted_actions = ["DELETE"] - deletion_policy = "FORCE" + location = "us-west2" + name = "tf-test-test-volume%{random_suffix}" + capacity_gib = "200" + share_name = "tf-test-test-volume%{random_suffix}" + storage_pool = google_netapp_storage_pool.default2.name + protocols = ["NFSV3"] + smb_settings = [] + unix_permissions = "0740" + labels = {} + description = "" + snapshot_directory = true + security_style = "UNIX" + kerberos_enabled = false + export_policy { + rules { + access_type = "READ_WRITE" + allowed_clients = "0.0.0.0/0" + has_root_access = "true" + kerberos5_read_only = false + kerberos5_read_write = false + kerberos5i_read_only = false + kerberos5i_read_write = false + kerberos5p_read_only = false + kerberos5p_read_write = false + nfsv3 = true + nfsv4 = false + } + } + # Delete protection only gets active after an NFS client mounts. + # Setting it here is save, volume can still be deleted. + restricted_actions = ["DELETE"] + deletion_policy = "FORCE" } -# Add the following snapshot block to the test as soon as snapshot resoruce -# is added to the provider. It will make the test cleanup require -# deletion_policy = "FORCE" on the volume for successful delete. -# resource "google_netapp_volumesnapshot" "test-snapshot" { -# depends_on = [google_netapp_volume.test_volume] -# location = google_netapp_volume.test_volume.location -# volume_name = google_netapp_volume.test_volume.name -# name = "test-snapshot%{random_suffix}" -# } +resource "google_netapp_volume_snapshot" "test-snapshot" { + depends_on = [google_netapp_volume.test_volume] + location = google_netapp_volume.test_volume.location + volume_name = google_netapp_volume.test_volume.name + name = "test-snapshot" +} + +resource "google_netapp_volume" "test_volume_clone" { + location = "us-west2" + name = "tf-test-test-volume-clone%{random_suffix}" + capacity_gib = "200" + share_name = "tf-test-test-volume-clone%{random_suffix}" + storage_pool = google_netapp_storage_pool.default2.name + protocols = ["NFSV3"] + deletion_policy = "FORCE" + restore_parameters { + source_snapshot = google_netapp_volume_snapshot.test-snapshot.id + } +} data "google_compute_network" "default" { - name = "%{network_name}" + name = "%{network_name}" } `, context) } diff --git a/mmv1/third_party/terraform/services/notebooks/resource_notebooks_instance_state_test.go.erb b/mmv1/third_party/terraform/services/notebooks/resource_notebooks_instance_state_test.go.erb new file mode 100644 index 000000000000..ff99d9c7cc31 --- /dev/null +++ b/mmv1/third_party/terraform/services/notebooks/resource_notebooks_instance_state_test.go.erb @@ -0,0 +1,85 @@ +<% autogen_exception -%> +package notebooks_test + +<% unless version == 'ga' %> +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccNotebooksInstance_state(t *testing.T) { + t.Parallel() + + prefix := fmt.Sprintf("%d", acctest.RandInt(t)) + name := fmt.Sprintf("tf-%s", prefix) + + acctest.VcrTest(t, resource.TestCase{ + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccNotebooksInstance_basic_active(name), + }, + { + ResourceName: "google_notebooks_instance.test", + ImportState: true, + ImportStateVerify: true, + ExpectNonEmptyPlan: true, + ImportStateVerifyIgnore: []string{"container_image", "metadata", "vm_image","desired_state", "update_time"}, + }, + { + Config: testAccNotebooksInstance_basic_stopped(name), + }, + { + ResourceName: "google_notebooks_instance.test", + ImportState: true, + ImportStateVerify: true, + ExpectNonEmptyPlan: true, + ImportStateVerifyIgnore: []string{"container_image", "metadata", "vm_image","desired_state", "update_time"}, + }, + { + Config: testAccNotebooksInstance_basic_active(name), + }, + { + ResourceName: "google_notebooks_instance.test", + ImportState: true, + ImportStateVerify: true, + ExpectNonEmptyPlan: true, + ImportStateVerifyIgnore: []string{"container_image", "metadata", "vm_image","desired_state", "update_time"}, + }, + }, + }) +} + +func testAccNotebooksInstance_basic_active(name string) string { + return fmt.Sprintf(` +resource "google_notebooks_instance" "test" { + name = "%s" + location = "us-west1-a" + machine_type = "e2-medium" + vm_image { + project = "deeplearning-platform-release" + image_family = "tf-latest-cpu" + } + desired_state = "ACTIVE" +} +`, name) +} + +func testAccNotebooksInstance_basic_stopped(name string) string { + return fmt.Sprintf(` +resource "google_notebooks_instance" "test" { + name = "%s" + location = "us-west1-a" + machine_type = "e2-medium" + vm_image { + project = "deeplearning-platform-release" + image_family = "tf-latest-cpu" + } + desired_state = "STOPPED" +} +`, name) +} +<% end -%> diff --git a/mmv1/third_party/terraform/services/resourcemanager/resource_google_project.go b/mmv1/third_party/terraform/services/resourcemanager/resource_google_project.go index acf1db96fa56..1d2c97855fe0 100644 --- a/mmv1/third_party/terraform/services/resourcemanager/resource_google_project.go +++ b/mmv1/third_party/terraform/services/resourcemanager/resource_google_project.go @@ -227,7 +227,13 @@ func resourceGoogleProjectCreate(d *schema.ResourceData, meta interface{}) error return errwrap.Wrapf("Error enabling the Compute Engine API required to delete the default network: {{err}} ", err) } - if err = forceDeleteComputeNetwork(d, config, project.ProjectId, "default"); err != nil { + err = forceDeleteComputeNetwork(d, config, project.ProjectId, "default") + // Retry if API is not yet enabled. + if err != nil && transport_tpg.IsGoogleApiErrorWithCode(err, 403) { + time.Sleep(10 * time.Second) + err = forceDeleteComputeNetwork(d, config, project.ProjectId, "default") + } + if err != nil { if transport_tpg.IsGoogleApiErrorWithCode(err, 404) { log.Printf("[DEBUG] Default network not found for project %q, no need to delete it", project.ProjectId) } else { diff --git a/mmv1/third_party/terraform/services/vertexai/resource_vertex_ai_feature_online_store_featureview_test.go b/mmv1/third_party/terraform/services/vertexai/resource_vertex_ai_feature_online_store_featureview_test.go index 87d69f45c9fb..4c3eb97c736f 100644 --- a/mmv1/third_party/terraform/services/vertexai/resource_vertex_ai_feature_online_store_featureview_test.go +++ b/mmv1/third_party/terraform/services/vertexai/resource_vertex_ai_feature_online_store_featureview_test.go @@ -198,3 +198,291 @@ func testAccVertexAIFeatureOnlineStoreFeatureview_vertexAiFeatureonlinestoreFeat } `, context) } + +func TestAccVertexAIFeatureOnlineStoreFeatureview_vertexAiFeatureonlinestoreFeatureview_featureRegistry_updated(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: testAccCheckVertexAIFeatureOnlineStoreFeatureviewDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccVertexAIFeatureOnlineStoreFeatureview_vertexAiFeatureonlinestoreFeatureview_featureRegistry_basic(context), + }, + { + ResourceName: "google_vertex_ai_feature_online_store_featureview.featureregistry_featureview", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "etag", "feature_online_store", "labels", "terraform_labels"}, + }, + { + Config: testAccVertexAIFeatureOnlineStoreFeatureview_vertexAiFeatureonlinestoreFeatureview_featureRegistry_update(context), + }, + { + ResourceName: "google_vertex_ai_feature_online_store_featureview.featureregistry_featureview", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "feature_online_store", "labels", "terraform_labels"}, + }, + }, + }) +} + +func testAccVertexAIFeatureOnlineStoreFeatureview_vertexAiFeatureonlinestoreFeatureview_featureRegistry_basic(context map[string]interface{}) string { + return acctest.Nprintf(` + resource "google_vertex_ai_feature_online_store" "featureregistry_featureonlinestore" { + name = "tf_test_featureonlinestore%{random_suffix}" + labels = { + foo = "bar" + } + region = "us-central1" + bigtable { + auto_scaling { + min_node_count = 1 + max_node_count = 2 + cpu_utilization_target = 80 + } + } + } + + resource "google_bigquery_dataset" "featureregistry-tf-test-dataset" { + + dataset_id = "tf_test_dataset1_featureview%{random_suffix}" + friendly_name = "test" + description = "This is a test description" + location = "US" + } + + resource "google_bigquery_table" "sample_table" { + deletion_protection = false + + dataset_id = google_bigquery_dataset.featureregistry-tf-test-dataset.dataset_id + table_id = "tf_test_bq_table%{random_suffix}" + schema = < +package workbench_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccWorkbenchInstance_shielded_config_update(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), + Steps: []resource.TestStep{ + { + Config: testAccWorkbenchInstance_shielded_config_false(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels"}, + }, + { + Config: testAccWorkbenchInstance_shielded_config_true(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels"}, + }, + }, + }) +} + +func TestAccWorkbenchInstance_shielded_config_remove(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), + Steps: []resource.TestStep{ + { + Config: testAccWorkbenchInstance_shielded_config_true(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels"}, + }, + { + Config: testAccWorkbenchInstance_shielded_config_none(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels"}, + }, + }, + }) +} + +func TestAccWorkbenchInstance_shielded_config_double_apply(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), + Steps: []resource.TestStep{ + { + Config: testAccWorkbenchInstance_shielded_config_none(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels"}, + }, + { + Config: testAccWorkbenchInstance_shielded_config_none(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels"}, + }, + { + Config: testAccWorkbenchInstance_shielded_config_false(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels"}, + }, + { + Config: testAccWorkbenchInstance_shielded_config_false(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels"}, + }, + { + Config: testAccWorkbenchInstance_shielded_config_true(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels"}, + }, + { + Config: testAccWorkbenchInstance_shielded_config_true(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels"}, + }, + }, + }) +} + +func testAccWorkbenchInstance_shielded_config_true(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_workbench_instance" "instance" { + name = "tf-test-workbench-instance%{random_suffix}" + location = "us-central1-a" + + gce_setup { + shielded_instance_config { + enable_secure_boot = true + enable_vtpm = true + enable_integrity_monitoring = true + } + } +} +`, context) +} + +func testAccWorkbenchInstance_shielded_config_false(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_workbench_instance" "instance" { + name = "tf-test-workbench-instance%{random_suffix}" + location = "us-central1-a" + + gce_setup { + shielded_instance_config { + enable_secure_boot = false + enable_vtpm = false + enable_integrity_monitoring = false + } + } + +} +`, context) +} + +func testAccWorkbenchInstance_shielded_config_none(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_workbench_instance" "instance" { + name = "tf-test-workbench-instance%{random_suffix}" + location = "us-central1-a" +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/workbench/resource_workbench_instance_gpu_test.go.erb b/mmv1/third_party/terraform/services/workbench/resource_workbench_instance_test.go.erb similarity index 77% rename from mmv1/third_party/terraform/services/workbench/resource_workbench_instance_gpu_test.go.erb rename to mmv1/third_party/terraform/services/workbench/resource_workbench_instance_test.go.erb index c1914881b2a6..8fbff0b8f080 100644 --- a/mmv1/third_party/terraform/services/workbench/resource_workbench_instance_gpu_test.go.erb +++ b/mmv1/third_party/terraform/services/workbench/resource_workbench_instance_test.go.erb @@ -65,6 +65,12 @@ resource "google_workbench_instance" "instance" { core_count = 1 } + shielded_instance_config { + enable_secure_boot = false + enable_vtpm = true + enable_integrity_monitoring = false + } + metadata = { terraform = "true" } @@ -143,6 +149,12 @@ resource "google_workbench_instance" "instance" { core_count = 1 } + shielded_instance_config { + enable_secure_boot = false + enable_vtpm = true + enable_integrity_monitoring = false + } + } labels = { @@ -270,3 +282,57 @@ resource "google_workbench_instance" "instance" { } `, context) } + +func TestAccWorkbenchInstance_updateState(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), + Steps: []resource.TestStep{ + { + Config: testAccWorkbenchInstance_basic(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels","desired_state"}, + }, + { + Config: testAccWorkbenchInstance_updateState(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels","desired_state"}, + }, + { + Config: testAccWorkbenchInstance_basic(context), + }, + { + ResourceName: "google_workbench_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "instance_owners", "location", "instance_id", "request_id", "labels", "terraform_labels","desired_state"}, + }, + }, + }) +} + +func testAccWorkbenchInstance_updateState(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_workbench_instance" "instance" { + name = "tf-test-workbench-instance%{random_suffix}" + location = "us-central1-a" + + desired_state = "STOPPED" + +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/workstations/resource_workstations_workstation_config_test.go.erb b/mmv1/third_party/terraform/services/workstations/resource_workstations_workstation_config_test.go.erb index d927fb6b72f5..72565a7f5219 100644 --- a/mmv1/third_party/terraform/services/workstations/resource_workstations_workstation_config_test.go.erb +++ b/mmv1/third_party/terraform/services/workstations/resource_workstations_workstation_config_test.go.erb @@ -675,6 +675,15 @@ resource "google_workstations_workstation_cluster" "default" { location = "us-central1" } +# No longer explicitly used in google_workstations_workstation_config resource block below, but the +# service account needs to keep existing to allow the field to default from the API without error +resource "google_service_account" "default" { + provider = google-beta + + account_id = "tf-test-my-account%{random_suffix}" + display_name = "Service Account" +} + resource "google_workstations_workstation_config" "default" { provider = google-beta workstation_config_id = "tf-test-workstation-config%{random_suffix}" @@ -729,6 +738,15 @@ resource "google_workstations_workstation_cluster" "default" { location = "us-central1" } +# No longer explicitly used in google_workstations_workstation_config resource block below, but the +# service account needs to keep existing to allow the field to default from the API without error +resource "google_service_account" "default" { + provider = google-beta + + account_id = "tf-test-my-account%{random_suffix}" + display_name = "Service Account" +} + resource "google_workstations_workstation_config" "default" { provider = google-beta workstation_config_id = "tf-test-workstation-config%{random_suffix}" diff --git a/mmv1/third_party/terraform/tpgiamresource/iam.go.erb b/mmv1/third_party/terraform/tpgiamresource/iam.go.erb index 6dcb6c1ba8f7..ce9fce404baf 100644 --- a/mmv1/third_party/terraform/tpgiamresource/iam.go.erb +++ b/mmv1/third_party/terraform/tpgiamresource/iam.go.erb @@ -274,6 +274,11 @@ func normalizeIamMemberCasing(member string) string { if len(pieces) > 2 && !iamMemberIsCaseSensitive(strings.TrimPrefix(member, "deleted:")) { pieces[2] = strings.ToLower(pieces[2]) } + } else if strings.HasPrefix(member, "iamMember:") { + pieces = strings.SplitN(member, ":", 3) + if len(pieces) > 2 && !iamMemberIsCaseSensitive(strings.TrimPrefix(member, "iamMember:")) { + pieces[2] = strings.ToLower(pieces[2]) + } } else if !iamMemberIsCaseSensitive(member) { pieces = strings.SplitN(member, ":", 2) if len(pieces) > 1 { diff --git a/mmv1/third_party/terraform/website/docs/d/compute_forwarding_rules.html.markdown b/mmv1/third_party/terraform/website/docs/d/compute_forwarding_rules.html.markdown new file mode 100644 index 000000000000..7c930a68a15d --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/compute_forwarding_rules.html.markdown @@ -0,0 +1,42 @@ +--- +subcategory: "Compute Engine" +description: |- + List forwarding rules in a region of a Google Cloud project. +--- + +# google\_compute\_forwarding\_rules + +List all networks in a specified Google Cloud project. + +## Example Usage + +```tf +data "google_compute_forwarding_rules" "my-forwarding-rules" { + project = "my-cloud-project" + region = "us-central1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `project` - (Optional) The name of the project. + +* `region` - (Optional) The region you want to get the forwarding rules from. + +These arguments must be set in either the provider or the resouce in order for the information to be queried. + +## Attributes Reference + +In addition to the arguments listed above, the following attributes are exported: + +* `id` - an identifier for the resource with format projects/{{project}}/region/{{region}}/forwardingRules + +* `project` - The project name being queried. + +* `region` - The region being queried. + +* `rules` - This is a list of the forwarding rules in the project. Each forwarding rule will list the backend, description, ip address. name, network, self link, service label, service name, and subnet. + +* `self_link` - The URI of the resource. diff --git a/mmv1/third_party/terraform/website/docs/d/compute_machine_types.html.markdown b/mmv1/third_party/terraform/website/docs/d/compute_machine_types.html.markdown index b0cc4784099c..eb42c5054471 100644 --- a/mmv1/third_party/terraform/website/docs/d/compute_machine_types.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/compute_machine_types.html.markdown @@ -20,7 +20,7 @@ Configure a Google Kubernetes Engine (GKE) cluster with node auto-provisioning, ```hcl data "google_compute_machine_types" "example" { - filter = "name = 'n1-standard-1'" + filter = "name = \"n1-standard-1\"" zone = "us-central1-a" } diff --git a/mmv1/third_party/terraform/website/docs/guides/getting_started.html.markdown b/mmv1/third_party/terraform/website/docs/guides/getting_started.html.markdown index 4559a54f224a..a329a71258e7 100644 --- a/mmv1/third_party/terraform/website/docs/guides/getting_started.html.markdown +++ b/mmv1/third_party/terraform/website/docs/guides/getting_started.html.markdown @@ -182,7 +182,7 @@ quota or billing issues which don't seem to apply to you, you may want to set ### Using Terraform Cloud as the Backend You need to use a different [environment variable](https://www.terraform.io/docs/cloud/workspaces/variables.html) name to store your credentials in Terraform Cloud. 1. Create an environment variable called `GOOGLE_CREDENTIALS` in your Terraform Cloud workspace. -2. Remove the newline characters from your JSON key file and then paste the credentials into the environment variable value field. +2. Remove the newline characters from your JSON key file and then paste the credentials into the environment variable value field. (Running `cat CREDENTIALS.json | tr -s '\n' ' '` will remove newline characters from your JSON key file) 3. Mark the variable as **Sensitive** and click **Save variable**. All runs within the workspace will use the `GOOGLE_CREDENTIALS` variable to authenticate with Google Cloud Platform. diff --git a/mmv1/third_party/terraform/website/docs/r/bigquery_dataset_iam.html.markdown b/mmv1/third_party/terraform/website/docs/r/bigquery_dataset_iam.html.markdown index e911137bdfae..4702defcded5 100644 --- a/mmv1/third_party/terraform/website/docs/r/bigquery_dataset_iam.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/bigquery_dataset_iam.html.markdown @@ -86,12 +86,13 @@ The following arguments are supported: * `member/members` - (Required) Identities that will be granted the privilege in `role`. Each entry can have one of the following values: - * **allUsers**: A special identifier that represents anyone who is on the internet; with or without a Google account. * **allAuthenticatedUsers**: A special identifier that represents anyone who is authenticated with a Google account or a service account. - * **user:{emailid}**: An email address that represents a specific Google account. For example, alice@gmail.com or joe@example.com. - * **serviceAccount:{emailid}**: An email address that represents a service account. For example, my-other-app@appspot.gserviceaccount.com. - * **group:{emailid}**: An email address that represents a Google group. For example, admins@example.com. + * **allUsers**: A special identifier that represents anyone who is on the internet; with or without a Google account. * **domain:{domain}**: A G Suite domain (primary, instead of alias) name that represents all the users of that domain. For example, google.com or example.com. + * **group:{emailid}**: An email address that represents a Google group. For example, admins@example.com. + * **iamMember:{principal}**: Some other type of member that appears in the IAM Policy but isn't a user, group, domain, or special group. This is used for example for workload/workforce federated identities (principal, principalSet). + * **serviceAccount:{emailid}**: An email address that represents a service account. For example, my-other-app@appspot.gserviceaccount.com. + * **user:{emailid}**: An email address that represents a specific Google account. For example, alice@gmail.com or joe@example.com. * `role` - (Required) The role that should be applied. Only one `google_bigquery_dataset_iam_binding` can be used per role. Note that custom roles must be of the format diff --git a/mmv1/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown b/mmv1/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown index 8a8b26b87091..5cfb46f700fe 100644 --- a/mmv1/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/cloudfunctions_function.html.markdown @@ -149,6 +149,8 @@ Please refer to the field 'effective_labels' for all of the labels present on th * `build_environment_variables` - (Optional) A set of key/value environment variable pairs available during build time. +* `build_worker_pool` - (Optional) Name of the Cloud Build Custom Worker Pool that should be used to build the function. + * `vpc_connector` - (Optional) The VPC Network Connector that this cloud function can connect to. It should be set up as fully-qualified URI. The format of this field is `projects/*/locations/*/connectors/*`. * `vpc_connector_egress_settings` - (Optional) The egress settings for the connector, controlling what traffic is diverted through it. Allowed values are `ALL_TRAFFIC` and `PRIVATE_RANGES_ONLY`. Defaults to `PRIVATE_RANGES_ONLY`. If unset, this field preserves the previously set value. diff --git a/mmv1/third_party/terraform/website/docs/r/compute_instance_group_manager.html.markdown b/mmv1/third_party/terraform/website/docs/r/compute_instance_group_manager.html.markdown index f6dead3b4382..a79b67e8c81c 100644 --- a/mmv1/third_party/terraform/website/docs/r/compute_instance_group_manager.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/compute_instance_group_manager.html.markdown @@ -153,7 +153,7 @@ The following arguments are supported: * `auto_healing_policies` - (Optional) The autohealing policies for this managed instance group. You can specify only one value. Structure is [documented below](#nested_auto_healing_policies). For more information, see the [official documentation](https://cloud.google.com/compute/docs/instance-groups/creating-groups-of-managed-instances#monitoring_groups). -* `all_instances_config` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) +* `all_instances_config` - (Optional) Properties to set on all instances in the group. After setting allInstancesConfig on the group, you must update the group's instances to apply the configuration. @@ -226,9 +226,9 @@ all_instances_config { } ``` -* `metadata` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)), The metadata key-value pairs that you want to patch onto the instance. For more information, see [Project and instance metadata](https://cloud.google.com/compute/docs/metadata#project_and_instance_metadata). +* `metadata` - (Optional), The metadata key-value pairs that you want to patch onto the instance. For more information, see [Project and instance metadata](https://cloud.google.com/compute/docs/metadata#project_and_instance_metadata). -* `labels` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)), The label key-value pairs that you want to patch onto the instance. +* `labels` - (Optional), The label key-value pairs that you want to patch onto the instance. - - - diff --git a/mmv1/third_party/terraform/website/docs/r/compute_region_instance_group_manager.html.markdown b/mmv1/third_party/terraform/website/docs/r/compute_region_instance_group_manager.html.markdown index 0a16ac30b14f..cad9a1343d81 100644 --- a/mmv1/third_party/terraform/website/docs/r/compute_region_instance_group_manager.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/compute_region_instance_group_manager.html.markdown @@ -155,7 +155,7 @@ The following arguments are supported: * `auto_healing_policies` - (Optional) The autohealing policies for this managed instance group. You can specify only one value. Structure is documented below. For more information, see the [official documentation](https://cloud.google.com/compute/docs/instance-groups/creating-groups-of-managed-instances#monitoring_groups). -* `all_instances_config` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) +* `all_instances_config` - (Optional) Properties to set on all instances in the group. After setting allInstancesConfig on the group, you must update the group's instances to apply the configuration. @@ -235,9 +235,9 @@ all_instances_config { } ``` -* `metadata` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)), The metadata key-value pairs that you want to patch onto the instance. For more information, see [Project and instance metadata](https://cloud.google.com/compute/docs/metadata#project_and_instance_metadata). +* `metadata` - (Optional), The metadata key-value pairs that you want to patch onto the instance. For more information, see [Project and instance metadata](https://cloud.google.com/compute/docs/metadata#project_and_instance_metadata). -* `labels` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)), The label key-value pairs that you want to patch onto the instance. +* `labels` - (Optional), The label key-value pairs that you want to patch onto the instance. - - - diff --git a/mmv1/third_party/terraform/website/docs/r/compute_security_policy.html.markdown b/mmv1/third_party/terraform/website/docs/r/compute_security_policy.html.markdown index 98af03c92186..92d2b5ab5b4f 100644 --- a/mmv1/third_party/terraform/website/docs/r/compute_security_policy.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/compute_security_policy.html.markdown @@ -204,7 +204,7 @@ The following arguments are supported: * `NORMAL` - Normal log level. * `VERBOSE` - Verbose log level. -* `user_ip_request_headers` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) An optional list of case-insensitive request header names to use for resolving the callers client IP address. +* `user_ip_request_headers` - (Optional) An optional list of case-insensitive request header names to use for resolving the callers client IP address. The `json_custom_config` block supports: diff --git a/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown b/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown index d35c3df76919..983dd05befe4 100644 --- a/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown @@ -6,22 +6,21 @@ description: |- # google\_container\_cluster --> Visit the [Provision a GKE Cluster (Google Cloud)](https://learn.hashicorp.com/tutorials/terraform/gke?in=terraform/kubernetes&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) Learn tutorial to learn how to provision and interact -with a GKE cluster. +Manages a Google Kubernetes Engine (GKE) cluster. --> See the [Using GKE with Terraform](/docs/providers/google/guides/using_gke_with_terraform.html) -guide for more information about using GKE with Terraform. +To get more information about GKE clusters, see: + * [The API reference](https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1beta1/projects.locations.clusters) + * How-to guides + * [GKE overview](https://cloud.google.com/kubernetes-engine/docs/concepts/kubernetes-engine-overview) + * [About cluster configuration choices](https://cloud.google.com/kubernetes-engine/docs/concepts/types-of-clusters) + * Terraform guidance + * [Using GKE with Terraform](/docs/providers/google/guides/using_gke_with_terraform.html) + * [Provision a GKE Cluster (Google Cloud) Learn tutorial](https://learn.hashicorp.com/tutorials/terraform/gke?in=terraform/kubernetes&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) -Manages a Google Kubernetes Engine (GKE) cluster. For more information see -[the official documentation](https://cloud.google.com/container-engine/docs/clusters) -and [the API reference](https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1beta1/projects.locations.clusters). +-> On version 5.0.0+ of the provider, you must explicitly set `deletion_protection = false` +and run `terraform apply` to write the field to state in order to destroy a cluster. --> **Note**: On version 5.0.0+ of the provider, you must explicitly set `deletion_protection=false` -(and run `terraform apply` to write the field to state) in order to destroy a cluster. -It is recommended to not set this field (or set it to true) until you're ready to destroy. - -~> **Warning:** All arguments and attributes, including basic auth username and -passwords as well as certificate outputs will be stored in the raw state as +~> All arguments and attributes (including certificate outputs) will be stored in the raw state as plaintext. [Read more about sensitive data in state](https://www.terraform.io/language/state/sensitive-data). ## Example Usage - with a separately managed node pool (recommended) @@ -791,8 +790,7 @@ The `master_authorized_networks_config.cidr_blocks` block supports: * `disk_type` - (Optional) Type of the disk attached to each node (e.g. 'pd-standard', 'pd-balanced' or 'pd-ssd'). If unspecified, the default disk type is 'pd-standard' -* `enable_confidential_storage` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) -Enabling Confidential Storage will create boot disk with confidential mode. It is disabled by default. +* `enable_confidential_storage` - (Optional) Enabling Confidential Storage will create boot disk with confidential mode. It is disabled by default. * `ephemeral_storage_config` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) Parameters for the ephemeral storage filesystem. If unspecified, ephemeral storage is backed by the boot disk. Structure is [documented below](#nested_ephemeral_storage_config). @@ -906,6 +904,8 @@ gvnic { * `tags` - (Optional) The list of instance tags applied to all nodes. Tags are used to identify valid sources or targets for network firewalls. +* `resource_manager_tags` - (Optional) A map of resource manager tag keys and values to be attached to the nodes for managing Compute Engine firewalls using Network Firewall Policies. Tags must be according to specifications found [here](https://cloud.google.com/vpc/docs/tags-firewalls-overview#specifications). A maximum of 5 tag key-value pairs can be specified. Existing tags will be replaced with new values. Tags must be in one of the following formats ([KEY]=[VALUE]) 1. `tagKeys/{tag_key_id}=tagValues/{tag_value_id}` 2. `{org_id}/{tag_key_name}={tag_value_name}` 3. `{project_id}/{tag_key_name}={tag_value_name}`. + * `taint` - (Optional) A list of [Kubernetes taints](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) to apply to nodes. This field will only report drift on taint keys that are @@ -999,7 +999,7 @@ sole_tenant_config { * `count` (Required) - The number of the guest accelerator cards exposed to this instance. * `gpu_driver_installation_config` (Optional) - Configuration for auto installation of GPU driver. Structure is [documented below](#nested_gpu_driver_installation_config). - + * `gpu_partition_size` (Optional) - Size of partitions to create on the GPU. Valid values are described in the NVIDIA mig [user guide](https://docs.nvidia.com/datacenter/tesla/mig-user-guide/#partitioning). * `gpu_sharing_config` (Optional) - Configuration for GPU sharing. Structure is [documented below](#nested_gpu_sharing_config). @@ -1111,6 +1111,8 @@ subnet. See [Private Cluster Limitations](https://cloud.google.com/kubernetes-en for more details. This field only applies to private clusters, when `enable_private_nodes` is `true`. +* `private_endpoint_subnetwork` - (Optional) Subnetwork in cluster's network where master's endpoint will be provisioned. + * `master_global_access_config` (Optional) - Controls cluster master global access settings. If unset, Terraform will no longer manage this field and will not modify the previously-set value. Structure is [documented below](#nested_master_global_access_config). @@ -1121,8 +1123,6 @@ In addition, the `private_cluster_config` allows access to the following read-on * `private_endpoint` - The internal IP address of this cluster's master endpoint. -* `private_endpoint_subnetwork` - Subnetwork in cluster's network where master's endpoint will be provisioned. - * `public_endpoint` - The external IP address of this cluster's master endpoint. !> The Google provider is unable to validate certain configurations of @@ -1346,7 +1346,11 @@ exported: * `node_config.0.effective_taints` - List of kubernetes taints applied to each node. Structure is [documented above](#nested_taint). -* `fleet.0.membership` - The resource name of the fleet Membership resource associated to this cluster with format `//gkehub.googleapis.com/projects/{{project}}/locations/{{location}}/memberships/{{name}}`. See the official doc for [fleet management](https://cloud.google.com/kubernetes-engine/docs/fleets-overview). +* `fleet.0.membership` - The resource name of the fleet Membership resource associated to this cluster with format `//gkehub.googleapis.com/projects/{{project}}/locations/{{location}}/memberships/{{name}}`. See the official doc for [fleet management](https://cloud.google.com/kubernetes-engine/docs/fleets-overview). + +* `fleet.0.membership_id` - The short name of the fleet membership, extracted from `fleet.0.membership`. You can use this field to configure `membership_id` under [google_gkehub_feature_membership](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/gke_hub_feature_membership). + +* `fleet.0.membership_location` - The location of the fleet membership, extracted from `fleet.0.membership`. You can use this field to configure `membership_location` under [google_gkehub_feature_membership](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/gke_hub_feature_membership). ## Timeouts diff --git a/mmv1/third_party/terraform/website/docs/r/google_project_iam.html.markdown b/mmv1/third_party/terraform/website/docs/r/google_project_iam.html.markdown index 419315c503d7..33ee748e341d 100644 --- a/mmv1/third_party/terraform/website/docs/r/google_project_iam.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/google_project_iam.html.markdown @@ -228,7 +228,7 @@ An [`import` block](https://developer.hashicorp.com/terraform/language/import) ( ```tf import { - id = ""{{project_id}} roles/viewer user:foo@example.com"m" + id = "{{project_id}} roles/viewer user:foo@example.com" to = google_project_iam_member.default } ``` diff --git a/mmv1/third_party/terraform/website/docs/r/spanner_database_iam.html.markdown b/mmv1/third_party/terraform/website/docs/r/spanner_database_iam.html.markdown index 1de101713eb3..ef09772c1fd0 100644 --- a/mmv1/third_party/terraform/website/docs/r/spanner_database_iam.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/spanner_database_iam.html.markdown @@ -39,6 +39,32 @@ resource "google_spanner_database_iam_policy" "database" { } ``` +With IAM Conditions: + +```hcl +data "google_iam_policy" "admin" { + binding { + role = "roles/editor" + + members = [ + "user:jane@example.com", + ] + + condition { + title = "My Role" + description = "Grant permissions on my_role" + expression = "(resource.type == \"spanner.googleapis.com/DatabaseRole\" && (resource.name.endsWith(\"/myrole\")))" + } + } +} + +resource "google_spanner_database_iam_policy" "database" { + instance = "your-instance-name" + database = "your-database-name" + policy_data = data.google_iam_policy.admin.policy_data +} +``` + ## google\_spanner\_database\_iam\_binding ```hcl @@ -53,6 +79,26 @@ resource "google_spanner_database_iam_binding" "database" { } ``` +With IAM Conditions: + +```hcl +resource "google_spanner_database_iam_binding" "database" { + instance = "your-instance-name" + database = "your-database-name" + role = "roles/compute.networkUser" + + members = [ + "user:jane@example.com", + ] + + condition { + title = "My Role" + description = "Grant permissions on my_role" + expression = "(resource.type == \"spanner.googleapis.com/DatabaseRole\" && (resource.name.endsWith(\"/myrole\")))" + } +} +``` + ## google\_spanner\_database\_iam\_member ```hcl @@ -64,6 +110,23 @@ resource "google_spanner_database_iam_member" "database" { } ``` +With IAM Conditions: + +```hcl +resource "google_spanner_database_iam_member" "database" { + instance = "your-instance-name" + database = "your-database-name" + role = "roles/compute.networkUser" + member = "user:jane@example.com" + + condition { + title = "My Role" + description = "Grant permissions on my_role" + expression = "(resource.type == \"spanner.googleapis.com/DatabaseRole\" && (resource.name.endsWith(\"/myrole\")))" + } +} +``` + ## Argument Reference The following arguments are supported: @@ -91,6 +154,23 @@ The following arguments are supported: * `project` - (Optional) The ID of the project in which the resource belongs. If it is not provided, the provider project is used. +* `condition` - (Optional) An [IAM Condition](https://cloud.google.com/iam/docs/conditions-overview) for a given binding. + Structure is [documented below](#nested_condition). + +--- + +The `condition` block supports: + +* `expression` - (Required) Textual representation of an expression in Common Expression Language syntax. + +* `title` - (Required) A title for the expression, i.e. a short string describing its purpose. + +* `description` - (Optional) An optional description of the expression. This is a longer text which describes the expression, e.g. when hovered over it in a UI. + +~> **Warning:** Terraform considers the `role` and condition contents (`title`+`description`+`expression`) as the +identifier for the binding. This means that if any part of the condition is changed out-of-band, Terraform will +consider it to be an entirely different resource and will treat it as such. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are diff --git a/tools/teamcity-diff-check/main.go b/tools/teamcity-diff-check/main.go new file mode 100644 index 000000000000..b3a71436e8fa --- /dev/null +++ b/tools/teamcity-diff-check/main.go @@ -0,0 +1,92 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "io" + "os" + "regexp" +) + +var serviceFile = flag.String("service_file", "services_ga", "kotlin service file to be parsed") + +func serviceDifference(gS, tS []string) []string { + t := make(map[string]struct{}, len(tS)) + for _, s := range tS { + t[s] = struct{}{} + } + + var diff []string + for _, s := range gS { + if _, found := t[s]; !found { + diff = append(diff, s) + } + } + + return diff +} + +func main() { + flag.Parse() + + file, err := os.Open(*serviceFile + ".txt") + if err != nil { + fmt.Println(err) + return + } + defer file.Close() + + googleServices := []string{} + scanner := bufio.NewScanner(file) + for scanner.Scan() { + googleServices = append(googleServices, scanner.Text()) + } + + //////////////////////////////////////////////////////////////////////////////// + + f, err := os.Open(fmt.Sprintf("../../mmv1/third_party/terraform/.teamcity/components/inputs/%s", *serviceFile+".kt")) + if err != nil { + panic(err) + } + + // Get the file size + stat, err := f.Stat() + if err != nil { + fmt.Println(err) + return + } + + // Read the file into a byte slice + bs := make([]byte, stat.Size()) + _, err = bufio.NewReader(f).Read(bs) + if err != nil && err != io.EOF { + fmt.Println(err) + return + } + + // Regex pattern captures "services" from *serviceFile. + pattern := regexp.MustCompile(`(?m)"(?P\w+)"\sto\s+mapOf`) + + template := []byte("$service") + + dst := []byte{} + teamcityServices := []string{} + + // For each match of the regex in the content. + for _, submatches := range pattern.FindAllSubmatchIndex(bs, -1) { + service := pattern.Expand(dst, template, bs, submatches) + teamcityServices = append(teamcityServices, string(service)) + } + if len(teamcityServices) == 0 { + fmt.Fprintf(os.Stderr, "teamcityServices error: regex produced no matches.\n") + os.Exit(1) + } + + if diff := serviceDifference(googleServices, teamcityServices); len(diff) != 0 { + fmt.Fprintf(os.Stderr, "error: diff in %s\n", *serviceFile) + fmt.Fprintf(os.Stderr, "Missing Services: %s\n", diff) + os.Exit(1) + } + +}