From 6f36892d535d4163aa287137392f4e5256f3272e Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 14:30:29 -0700 Subject: [PATCH 01/56] add terraform-provider-google-beta to ci --- .ci/ci.yml.tmpl | 26 +++++++++++++++++++++++- .ci/magic-modules/generate-terraform.sh | 15 +++++++++----- .ci/magic-modules/generate-terraform.yml | 2 +- .gitmodules | 4 ++++ 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/.ci/ci.yml.tmpl b/.ci/ci.yml.tmpl index d76d8652433d..14c44e5d1b1d 100644 --- a/.ci/ci.yml.tmpl +++ b/.ci/ci.yml.tmpl @@ -11,7 +11,7 @@ build/{{repo}}/{{name}} {% set chef_submodules = names_as_list('chef', chef_modules).split() %} {% set all_submodules = puppet_submodules + chef_submodules + - (['build/terraform'] if terraform_enabled else []) + + (['build/terraform', 'build/terraform-beta'] if terraform_enabled else []) + (['build/ansible'] if ansible_enabled else []) %} {% set all_submodules_yaml_format = '[' + ','.join(all_submodules) + ']' %} @@ -97,6 +97,11 @@ resources: source: uri: git@github.com:((github-account.username))/terraform-provider-google.git private_key: ((repo-key.private_key)) + - name: terraform-beta-intermediate + type: git-branch + source: + uri: git@github.com:((github-account.username))/terraform-provider-google-beta.git + private_key: ((repo-key.private_key)) {% endif %} {% if ansible_enabled %} @@ -214,6 +219,18 @@ jobs: branch_file: magic-modules-branched/branchname only_if_diff: true force: true + - do: + # consumes: magic-modules-branched + # produces: terraform-beta-generated + - task: generate-terraform-beta + file: magic-modules-branched/.ci/magic-modules/generate-terraform-beta.yml + # Puts 'terraform-beta-generated' into the robot's fork. + - put: terraform-beta-intermediate + params: + repository: terraform-beta-generated + branch_file: magic-modules-branched/branchname + only_if_diff: true + force: true {% endif %} {% if ansible_enabled %} - do: @@ -476,6 +493,13 @@ jobs: # not push the update even though the commit hashes are different. only_if_diff: true force: true + - put: terraform-beta-intermediate + params: + repository: magic-modules-with-comment/build/terraform-beta + branch_file: magic-modules-with-comment/original_pr_branch_name + # See comment on terraform-intermediate + only_if_diff: true + force: true {% endif %} {% if ansible_enabled %} - put: ansible-intermediate diff --git a/.ci/magic-modules/generate-terraform.sh b/.ci/magic-modules/generate-terraform.sh index 4df353d477fe..c9d5566b202c 100755 --- a/.ci/magic-modules/generate-terraform.sh +++ b/.ci/magic-modules/generate-terraform.sh @@ -14,11 +14,16 @@ PATCH_DIR="$(pwd)/patches" export GOPATH="${PWD}/go" mkdir -p "${GOPATH}/src/github.com/terraform-providers" +PROVIDER_NAME="terraform-provider-google" +if [ -n "$VERSION" ]; then + PROVIDER_NAME="terraform-provider-google-$VERSION" +fi + pushd magic-modules-branched -ln -s "${PWD}/build/terraform/" "${GOPATH}/src/github.com/terraform-providers/terraform-provider-google" +ln -s "${PWD}/build/terraform/" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" popd -pushd "${GOPATH}/src/github.com/terraform-providers/terraform-provider-google" +pushd "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" go get popd @@ -27,10 +32,10 @@ LAST_COMMIT_AUTHOR="$(git log --pretty="%an <%ae>" -n1 HEAD)" bundle install # Build all terraform products -bundle exec compiler -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/terraform-provider-google/" +bundle exec compiler -v $VERSION -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" # Resources that were already using beta APIs before they started being autogenerated -bundle exec compiler -v beta -p products/compute -t Address,Firewall,ForwardingRule,GlobalAddress,RegionDisk,Subnetwork,VpnTunnel -e terraform -o "${GOPATH}/src/github.com/terraform-providers/terraform-provider-google/" +bundle exec compiler -v beta -p products/compute -t Address,Firewall,ForwardingRule,GlobalAddress,RegionDisk,Subnetwork,VpnTunnel -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" # This command can crash - if that happens, the script should not fail. set +e @@ -50,7 +55,7 @@ git add -A git commit -m "$TERRAFORM_COMMIT_MSG" --author="$LAST_COMMIT_AUTHOR" || true # don't crash if no changes git checkout -B "$(cat ../../branchname)" -apply_patches "$PATCH_DIR/terraform-providers/terraform-provider-google" "$TERRAFORM_COMMIT_MSG" "$LAST_COMMIT_AUTHOR" "master" +apply_patches "$PATCH_DIR/terraform-providers/$PROVIDER_NAME" "$TERRAFORM_COMMIT_MSG" "$LAST_COMMIT_AUTHOR" "master" popd popd diff --git a/.ci/magic-modules/generate-terraform.yml b/.ci/magic-modules/generate-terraform.yml index 24b23d20bf21..ef72a9c238d9 100644 --- a/.ci/magic-modules/generate-terraform.yml +++ b/.ci/magic-modules/generate-terraform.yml @@ -18,4 +18,4 @@ outputs: - name: terraform-generated run: - path: magic-modules-branched/.ci/magic-modules/generate-terraform.sh + path: magic-modules-branched/.ci/magic-modules/generate-terraform.sh \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 515df3d2c747..61e796c0a2a0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -72,6 +72,10 @@ path = build/terraform url = git@github.com:terraform-providers/terraform-provider-google.git branch = master +[submodule "build/terraform-beta"] + path = build/terraform-beta + url = git@github.com:terraform-providers/terraform-provider-google-beta.git + branch = master [submodule "build/puppet/bigquery"] path = build/puppet/bigquery url = git@github.com:GoogleCloudPlatform/puppet-google-bigquery.git From 3ccf86769bcf121f1b8d466ea0af2e4723bab14d Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 14:47:29 -0700 Subject: [PATCH 02/56] properly add beta submodule --- .gitmodules | 7 +++---- terraform-provider-google-beta | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) create mode 160000 terraform-provider-google-beta diff --git a/.gitmodules b/.gitmodules index 61e796c0a2a0..9e938f8c7250 100644 --- a/.gitmodules +++ b/.gitmodules @@ -72,10 +72,6 @@ path = build/terraform url = git@github.com:terraform-providers/terraform-provider-google.git branch = master -[submodule "build/terraform-beta"] - path = build/terraform-beta - url = git@github.com:terraform-providers/terraform-provider-google-beta.git - branch = master [submodule "build/puppet/bigquery"] path = build/puppet/bigquery url = git@github.com:GoogleCloudPlatform/puppet-google-bigquery.git @@ -85,3 +81,6 @@ [submodule "build/chef/iam"] path = build/chef/iam url = git@github.com:GoogleCloudPlatform/chef-google-iam.git +[submodule "terraform-provider-google-beta"] + path = terraform-provider-google-beta + url = https://github.com/terraform-providers/terraform-provider-google-beta diff --git a/terraform-provider-google-beta b/terraform-provider-google-beta new file mode 160000 index 000000000000..8c0bc9377140 --- /dev/null +++ b/terraform-provider-google-beta @@ -0,0 +1 @@ +Subproject commit 8c0bc9377140b8dfd4550102e063ea6b83e54a8f From 73048ebc20b2064ea011425fbb73b8e500c94706 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 14:47:41 -0700 Subject: [PATCH 03/56] add forgotten yml file --- .ci/magic-modules/generate-terraform-beta.yml | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .ci/magic-modules/generate-terraform-beta.yml diff --git a/.ci/magic-modules/generate-terraform-beta.yml b/.ci/magic-modules/generate-terraform-beta.yml new file mode 100644 index 000000000000..3a601fc07029 --- /dev/null +++ b/.ci/magic-modules/generate-terraform-beta.yml @@ -0,0 +1,24 @@ +--- +# This file takes two inputs: magic-modules-branched in detached-HEAD state, and the list of patches. +# It spits out "terraform-generated", a terraform repo on a new branch (named after the +# HEAD commit on the PR), with the new generated code in it. +platform: linux + +image_resource: + type: docker-image + source: + repository: nmckinley/go-ruby-python + tag: '1.11-2.5-2.7' + +inputs: + - name: magic-modules-branched + - name: patches + +outputs: + - name: terraform-beta-generated + +run: + path: magic-modules-branched/.ci/magic-modules/generate-terraform.sh + +params: + VERSION: beta From f4c8a5c2bb438629cd22314bcbc9629746d692b8 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 14:51:52 -0700 Subject: [PATCH 04/56] remove bad submodule --- .gitmodules | 3 --- terraform-provider-google-beta | 1 - 2 files changed, 4 deletions(-) delete mode 160000 terraform-provider-google-beta diff --git a/.gitmodules b/.gitmodules index 9e938f8c7250..515df3d2c747 100644 --- a/.gitmodules +++ b/.gitmodules @@ -81,6 +81,3 @@ [submodule "build/chef/iam"] path = build/chef/iam url = git@github.com:GoogleCloudPlatform/chef-google-iam.git -[submodule "terraform-provider-google-beta"] - path = terraform-provider-google-beta - url = https://github.com/terraform-providers/terraform-provider-google-beta diff --git a/terraform-provider-google-beta b/terraform-provider-google-beta deleted file mode 160000 index 8c0bc9377140..000000000000 --- a/terraform-provider-google-beta +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8c0bc9377140b8dfd4550102e063ea6b83e54a8f From eca1e521ff05763fc226d860b91386f22fce0402 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 14:53:34 -0700 Subject: [PATCH 05/56] actually add submodule --- .gitmodules | 3 +++ build/terraform-provider-google-beta | 1 + 2 files changed, 4 insertions(+) create mode 160000 build/terraform-provider-google-beta diff --git a/.gitmodules b/.gitmodules index 515df3d2c747..db795d14c274 100644 --- a/.gitmodules +++ b/.gitmodules @@ -81,3 +81,6 @@ [submodule "build/chef/iam"] path = build/chef/iam url = git@github.com:GoogleCloudPlatform/chef-google-iam.git +[submodule "build/terraform-provider-google-beta"] + path = build/terraform-provider-google-beta + url = https://github.com/terraform-providers/terraform-provider-google-beta diff --git a/build/terraform-provider-google-beta b/build/terraform-provider-google-beta new file mode 160000 index 000000000000..8c0bc9377140 --- /dev/null +++ b/build/terraform-provider-google-beta @@ -0,0 +1 @@ +Subproject commit 8c0bc9377140b8dfd4550102e063ea6b83e54a8f From 0223d22b3cd6cf8827124dcaf2c7a4ff8e1c8c10 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 14:55:57 -0700 Subject: [PATCH 06/56] remove it again --- .gitmodules | 3 --- build/terraform-provider-google-beta | 1 - 2 files changed, 4 deletions(-) delete mode 160000 build/terraform-provider-google-beta diff --git a/.gitmodules b/.gitmodules index db795d14c274..515df3d2c747 100644 --- a/.gitmodules +++ b/.gitmodules @@ -81,6 +81,3 @@ [submodule "build/chef/iam"] path = build/chef/iam url = git@github.com:GoogleCloudPlatform/chef-google-iam.git -[submodule "build/terraform-provider-google-beta"] - path = build/terraform-provider-google-beta - url = https://github.com/terraform-providers/terraform-provider-google-beta diff --git a/build/terraform-provider-google-beta b/build/terraform-provider-google-beta deleted file mode 160000 index 8c0bc9377140..000000000000 --- a/build/terraform-provider-google-beta +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8c0bc9377140b8dfd4550102e063ea6b83e54a8f From ff1898430daf4ae0f8fe13461dbd14fdc1369dc9 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 14:56:44 -0700 Subject: [PATCH 07/56] actually actually add submodule --- .gitmodules | 3 +++ build/terraform-beta | 1 + 2 files changed, 4 insertions(+) create mode 160000 build/terraform-beta diff --git a/.gitmodules b/.gitmodules index 515df3d2c747..e79df742203a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -81,3 +81,6 @@ [submodule "build/chef/iam"] path = build/chef/iam url = git@github.com:GoogleCloudPlatform/chef-google-iam.git +[submodule "build/terraform-beta"] + path = build/terraform-beta + url = https://github.com/terraform-providers/terraform-provider-google-beta diff --git a/build/terraform-beta b/build/terraform-beta new file mode 160000 index 000000000000..8c0bc9377140 --- /dev/null +++ b/build/terraform-beta @@ -0,0 +1 @@ +Subproject commit 8c0bc9377140b8dfd4550102e063ea6b83e54a8f From 0439c11ef26a9cad9a463a47fe6f8b8dd01b095d Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 15:04:48 -0700 Subject: [PATCH 08/56] update submodule dir based on version --- .ci/magic-modules/generate-terraform.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.ci/magic-modules/generate-terraform.sh b/.ci/magic-modules/generate-terraform.sh index c9d5566b202c..8ecfb957f7ae 100755 --- a/.ci/magic-modules/generate-terraform.sh +++ b/.ci/magic-modules/generate-terraform.sh @@ -15,12 +15,14 @@ export GOPATH="${PWD}/go" mkdir -p "${GOPATH}/src/github.com/terraform-providers" PROVIDER_NAME="terraform-provider-google" +SUBMODULE_DIR="terraform" if [ -n "$VERSION" ]; then PROVIDER_NAME="terraform-provider-google-$VERSION" + SUBMODULE_DIR="terraform-$VERSION" fi pushd magic-modules-branched -ln -s "${PWD}/build/terraform/" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" +ln -s "${PWD}/build/$SUBMODULE_DIR/" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" popd pushd "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" @@ -45,7 +47,7 @@ if [ -z "$TERRAFORM_COMMIT_MSG" ]; then TERRAFORM_COMMIT_MSG="Magic Modules changes." fi -pushd "build/terraform" +pushd "build/$SUBMODULE_DIR" # These config entries will set the "committer". git config --global user.email "magic-modules@google.com" git config --global user.name "Modular Magician" @@ -60,4 +62,4 @@ apply_patches "$PATCH_DIR/terraform-providers/$PROVIDER_NAME" "$TERRAFORM_COMMIT popd popd -git clone magic-modules-branched/build/terraform ./terraform-generated +git clone magic-modules-branched/build/$SUBMODULE_DIR ./terraform-generated From f971c1158222ea314d1b60e53214d6c45b013e32 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 15:23:38 -0700 Subject: [PATCH 09/56] quote version --- .ci/magic-modules/generate-terraform.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/magic-modules/generate-terraform.sh b/.ci/magic-modules/generate-terraform.sh index 8ecfb957f7ae..08393cf0e7a2 100755 --- a/.ci/magic-modules/generate-terraform.sh +++ b/.ci/magic-modules/generate-terraform.sh @@ -34,7 +34,7 @@ LAST_COMMIT_AUTHOR="$(git log --pretty="%an <%ae>" -n1 HEAD)" bundle install # Build all terraform products -bundle exec compiler -v $VERSION -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" +bundle exec compiler -v "$VERSION" -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" # Resources that were already using beta APIs before they started being autogenerated bundle exec compiler -v beta -p products/compute -t Address,Firewall,ForwardingRule,GlobalAddress,RegionDisk,Subnetwork,VpnTunnel -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" From 39fbaec1e9acc9f9c905213a29e0c9bb85df05c8 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 15:23:53 -0700 Subject: [PATCH 10/56] use other way of specifying submodule url --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index e79df742203a..30d25457690a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -83,4 +83,4 @@ url = git@github.com:GoogleCloudPlatform/chef-google-iam.git [submodule "build/terraform-beta"] path = build/terraform-beta - url = https://github.com/terraform-providers/terraform-provider-google-beta + url = git@github.com:terraform-providers/terraform-provider-google-beta.git From 248a4e3631caf46bb525d545c761133ef57701d1 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 17:30:20 -0700 Subject: [PATCH 11/56] skip generation if product doesn't exist at specified version --- api/product.rb | 9 +++++++++ compiler.rb | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/api/product.rb b/api/product.rb index 6e5a8e33e6ea..6606e0c044ff 100644 --- a/api/product.rb +++ b/api/product.rb @@ -91,6 +91,15 @@ def set_properties_based_on_version(version) end # rubocop:enable Naming/AccessorMethodName + def exists_at_version(name) + return true if name.nil? + @versions.each do |v| + return true if v.name == name + end + + false + end + private def check_versions diff --git a/compiler.rb b/compiler.rb index 0f3060b02b8f..ae4f4dbc440b 100755 --- a/compiler.rb +++ b/compiler.rb @@ -108,6 +108,12 @@ product_api.validate pp product_api if ENV['COMPILER_DEBUG'] + unless product_api.exists_at_version(version) + Google::LOGGER.info \ + "'#{product_name}' does not have a '#{version}' version, skipping" + next + end + provider_config = Provider::Config.parse(provider_yaml_path, product_api, version) pp provider_config if ENV['COMPILER_DEBUG'] From 1cc61a6a4fadd2c9e54b4a03a6c0e258c80e840b Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 26 Sep 2018 17:30:40 -0700 Subject: [PATCH 12/56] generate tf at ga first, then at beta --- .ci/magic-modules/generate-terraform.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.ci/magic-modules/generate-terraform.sh b/.ci/magic-modules/generate-terraform.sh index 08393cf0e7a2..0b368165e5f0 100755 --- a/.ci/magic-modules/generate-terraform.sh +++ b/.ci/magic-modules/generate-terraform.sh @@ -17,8 +17,8 @@ mkdir -p "${GOPATH}/src/github.com/terraform-providers" PROVIDER_NAME="terraform-provider-google" SUBMODULE_DIR="terraform" if [ -n "$VERSION" ]; then - PROVIDER_NAME="terraform-provider-google-$VERSION" - SUBMODULE_DIR="terraform-$VERSION" + PROVIDER_NAME="terraform-provider-google-$VERSION" + SUBMODULE_DIR="terraform-$VERSION" fi pushd magic-modules-branched @@ -34,7 +34,13 @@ LAST_COMMIT_AUTHOR="$(git log --pretty="%an <%ae>" -n1 HEAD)" bundle install # Build all terraform products -bundle exec compiler -v "$VERSION" -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" +# Since not all products have a beta version, generate everything at GA, then +# generate the ones that have a beta version at beta. +bundle exec compiler -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" + +if [ "$VERSION" = "beta" ]; then + bundle exec compiler -v beta -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" +fi # Resources that were already using beta APIs before they started being autogenerated bundle exec compiler -v beta -p products/compute -t Address,Firewall,ForwardingRule,GlobalAddress,RegionDisk,Subnetwork,VpnTunnel -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" From 3da348f422dc6143fcb7e75ea1113659d59e4741 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Tue, 2 Oct 2018 15:23:47 -0700 Subject: [PATCH 13/56] copy git dir correctly --- .ci/magic-modules/generate-terraform.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/magic-modules/generate-terraform.sh b/.ci/magic-modules/generate-terraform.sh index 0b368165e5f0..779b0f1eb7d6 100755 --- a/.ci/magic-modules/generate-terraform.sh +++ b/.ci/magic-modules/generate-terraform.sh @@ -68,4 +68,4 @@ apply_patches "$PATCH_DIR/terraform-providers/$PROVIDER_NAME" "$TERRAFORM_COMMIT popd popd -git clone magic-modules-branched/build/$SUBMODULE_DIR ./terraform-generated +git clone magic-modules-branched/build/$SUBMODULE_DIR ./$SUBMODULE_DIR-generated From c0bbeb9fd0b007b4b213e7d1c80f744943794be0 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Tue, 2 Oct 2018 19:10:34 -0700 Subject: [PATCH 14/56] maybe actually set the right version to generate, who really knows bash anyway --- .ci/magic-modules/generate-terraform.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.ci/magic-modules/generate-terraform.sh b/.ci/magic-modules/generate-terraform.sh index 779b0f1eb7d6..8c1d2e92a2f5 100755 --- a/.ci/magic-modules/generate-terraform.sh +++ b/.ci/magic-modules/generate-terraform.sh @@ -16,9 +16,11 @@ mkdir -p "${GOPATH}/src/github.com/terraform-providers" PROVIDER_NAME="terraform-provider-google" SUBMODULE_DIR="terraform" +VERSION_FLAG="" if [ -n "$VERSION" ]; then PROVIDER_NAME="terraform-provider-google-$VERSION" SUBMODULE_DIR="terraform-$VERSION" + VERSION_FLAG="-v $VERSION" fi pushd magic-modules-branched @@ -36,11 +38,7 @@ bundle install # Build all terraform products # Since not all products have a beta version, generate everything at GA, then # generate the ones that have a beta version at beta. -bundle exec compiler -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" - -if [ "$VERSION" = "beta" ]; then - bundle exec compiler -v beta -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" -fi +bundle exec compiler -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" $VERSION_FLAG # Resources that were already using beta APIs before they started being autogenerated bundle exec compiler -v beta -p products/compute -t Address,Firewall,ForwardingRule,GlobalAddress,RegionDisk,Subnetwork,VpnTunnel -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" From 0fde8ad65e63450ad393a63dbc59cb258becebe7 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Tue, 2 Oct 2018 20:17:16 -0700 Subject: [PATCH 15/56] try to run beta provider tests as part of terraform test job --- .ci/ci.yml.tmpl | 13 +++++++++---- .ci/unit-tests/run.sh | 12 ++++++++++-- .ci/unit-tests/task.yml | 4 ++-- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/.ci/ci.yml.tmpl b/.ci/ci.yml.tmpl index a1adef03c121..81470d180826 100644 --- a/.ci/ci.yml.tmpl +++ b/.ci/ci.yml.tmpl @@ -242,11 +242,16 @@ jobs: version: every trigger: true params: - submodules: [build/terraform] + submodules: [build/terraform, build/terraform-beta] passed: [mm-generate] - - task: test - file: magic-modules/.ci/unit-tests/task.yml - timeout: 30m + - aggregate: + {% for version in ['terraform', 'terraform-beta'] %} + - task: test-{{version}} + file: magic-modules/.ci/unit-tests/task.yml + timeout: 30m + params: + VERSION: {{version.partition('-')[2]}} + {% endfor %} on_failure: do: - get: magic-modules-new-prs diff --git a/.ci/unit-tests/run.sh b/.ci/unit-tests/run.sh index dbe47f3ea409..e16b4cd8652e 100755 --- a/.ci/unit-tests/run.sh +++ b/.ci/unit-tests/run.sh @@ -7,10 +7,18 @@ export GOPATH=${PWD}/go set -x +PROVIDER_NAME="terraform-provider-google" +SUBMODULE_DIR="terraform" + +if [ -n "$VERSION" ]; then + PROVIDER_NAME="terraform-provider-google-$VERSION" + SUBMODULE_DIR="terraform-$VERSION" +fi + # Create GOPATH structure mkdir -p "${GOPATH}/src/github.com/terraform-providers" -ln -s "${PWD}/$1" "${GOPATH}/src/github.com/terraform-providers/terraform-provider-google" +ln -s "${PWD}/magic-modules/build/$SUBMODULE_DIR" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" -cd "${GOPATH}/src/github.com/terraform-providers/terraform-provider-google" +cd "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" go test -v ./google -parallel 16 -run '^Test' -timeout 1m diff --git a/.ci/unit-tests/task.yml b/.ci/unit-tests/task.yml index 5ee190c9ceab..37afe47f33b8 100644 --- a/.ci/unit-tests/task.yml +++ b/.ci/unit-tests/task.yml @@ -8,5 +8,5 @@ image_resource: tag: '1.9.3' run: path: magic-modules/.ci/unit-tests/run.sh - args: - - magic-modules/build/terraform/ +params: + VERSION: "" From 80dc82f38cf93fd3a2992699ba800e815b346e11 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Tue, 2 Oct 2018 21:09:22 -0700 Subject: [PATCH 16/56] add beta provider in a few more places --- .ci/magic-modules/create-pr.sh | 47 ++++++++++++++++-------- .ci/magic-modules/generate-terraform.sh | 7 ++-- .ci/magic-modules/point-to-submodules.sh | 19 +++++++--- .ci/unit-tests/run.sh | 5 ++- 4 files changed, 52 insertions(+), 26 deletions(-) diff --git a/.ci/magic-modules/create-pr.sh b/.ci/magic-modules/create-pr.sh index a4c5bae086ae..1fc34b256473 100755 --- a/.ci/magic-modules/create-pr.sh +++ b/.ci/magic-modules/create-pr.sh @@ -31,25 +31,36 @@ git checkout -b "$BRANCH_NAME" if [ "$BRANCH_NAME" = "$ORIGINAL_PR_BRANCH" ]; then DEPENDENCIES="" NEWLINE=$'\n' + # There is no existing PR - this is the first pass through the pipeline and # we will need to create a PR using 'hub'. if [ -n "$TERRAFORM_REPO_USER" ]; then - pushd build/terraform - - git log -1 --pretty=%B > ./downstream_body - echo "" >> ./downstream_body - echo "" >> ./downstream_body - if [ -n "$ORIGINAL_PR_USER" ]; then - echo "/cc @$ORIGINAL_PR_USER" >> ./downstream_body - fi - - git checkout -b "$BRANCH_NAME" - if TF_PR=$(hub pull-request -b "$TERRAFORM_REPO_USER/terraform-provider-google:master" -F ./downstream_body); then - DEPENDENCIES="${DEPENDENCIES}depends: $TF_PR ${NEWLINE}" - else - echo "Terraform - did not generate a PR." - fi - popd + for VERSION in ('' 'beta'); do + if [ -n "$VERSION" ]; then + PROVIDER_NAME="terraform-provider-google-$VERSION" + SUBMODULE_DIR="terraform-$VERSION" + else + PROVIDER_NAME="terraform-provider-google" + SUBMODULE_DIR="terraform" + fi + + pushd build/$SUBMODULE_DIR + + git log -1 --pretty=%B > ./downstream_body + echo "" >> ./downstream_body + echo "" >> ./downstream_body + if [ -n "$ORIGINAL_PR_USER" ]; then + echo "/cc @$ORIGINAL_PR_USER" >> ./downstream_body + fi + + git checkout -b "$BRANCH_NAME" + if TF_PR=$(hub pull-request -b "$TERRAFORM_REPO_USER/$PROVIDER_NAME:master" -F ./downstream_body); then + DEPENDENCIES="${DEPENDENCIES}depends: $TF_PR ${NEWLINE}" + else + echo "$SUBMODULE_DIR - did not generate a PR." + fi + popd + done fi if [ -n "$ANSIBLE_REPO_USER" ]; then @@ -147,6 +158,10 @@ else pushd build/terraform git branch -f "$ORIGINAL_PR_BRANCH" popd + + pushd build/terraform-beta + git branch -f "$ORIGINAL_PR_BRANCH" + popd fi for PRD in "${PUPPET_PRODUCTS[@]}"; do diff --git a/.ci/magic-modules/generate-terraform.sh b/.ci/magic-modules/generate-terraform.sh index 8c1d2e92a2f5..dac6a0314197 100755 --- a/.ci/magic-modules/generate-terraform.sh +++ b/.ci/magic-modules/generate-terraform.sh @@ -14,13 +14,14 @@ PATCH_DIR="$(pwd)/patches" export GOPATH="${PWD}/go" mkdir -p "${GOPATH}/src/github.com/terraform-providers" -PROVIDER_NAME="terraform-provider-google" -SUBMODULE_DIR="terraform" -VERSION_FLAG="" if [ -n "$VERSION" ]; then PROVIDER_NAME="terraform-provider-google-$VERSION" SUBMODULE_DIR="terraform-$VERSION" VERSION_FLAG="-v $VERSION" +else + PROVIDER_NAME="terraform-provider-google" + SUBMODULE_DIR="terraform" + VERSION_FLAG="" fi pushd magic-modules-branched diff --git a/.ci/magic-modules/point-to-submodules.sh b/.ci/magic-modules/point-to-submodules.sh index e852cdb96506..38d709afb244 100755 --- a/.ci/magic-modules/point-to-submodules.sh +++ b/.ci/magic-modules/point-to-submodules.sh @@ -44,11 +44,20 @@ for PRD in "${PRODUCT_ARRAY[@]}"; do done if [ "$TERRAFORM_ENABLED" = "true" ]; then - git config -f .gitmodules submodule.build/terraform.branch "$BRANCH" - git config -f .gitmodules submodule.build/terraform.url "git@github.com:$GH_USERNAME/terraform-provider-google.git" - git submodule sync build/terraform - ssh-agent bash -c "ssh-add ~/github_private_key; git submodule update --remote --init build/terraform" - git add build/terraform + for VERSION in ('' 'beta'); do + if [ -n "$VERSION" ]; then + PROVIDER_NAME="terraform-provider-google-$VERSION" + SUBMODULE_DIR="terraform-$VERSION" + else + PROVIDER_NAME="terraform-provider-google" + SUBMODULE_DIR="terraform" + fi + git config -f .gitmodules "submodule.build/$SUBMODULE_DIR.branch" "$BRANCH" + git config -f .gitmodules "submodule.build/$SUBMODULE_DIR.url" "git@github.com:$GH_USERNAME/$PROVIDER_NAME.git" + git submodule sync "build/$SUBMODULE_DIR" + ssh-agent bash -c "ssh-add ~/github_private_key; git submodule update --remote --init build/$SUBMODULE_DIR" + git add "build/$SUBMODULE_DIR" + done fi if [ "$ANSIBLE_ENABLED" = "true" ]; then diff --git a/.ci/unit-tests/run.sh b/.ci/unit-tests/run.sh index e16b4cd8652e..cf9a7e908be6 100755 --- a/.ci/unit-tests/run.sh +++ b/.ci/unit-tests/run.sh @@ -7,12 +7,13 @@ export GOPATH=${PWD}/go set -x -PROVIDER_NAME="terraform-provider-google" -SUBMODULE_DIR="terraform" if [ -n "$VERSION" ]; then PROVIDER_NAME="terraform-provider-google-$VERSION" SUBMODULE_DIR="terraform-$VERSION" +else + PROVIDER_NAME="terraform-provider-google" + SUBMODULE_DIR="terraform" fi # Create GOPATH structure From ca035fbc4aa28c1c54b27a6b9222dfb6af7a3d93 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Tue, 2 Oct 2018 21:35:23 -0700 Subject: [PATCH 17/56] make arrays correctly --- .ci/magic-modules/create-pr.sh | 3 +-- .ci/magic-modules/point-to-submodules.sh | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.ci/magic-modules/create-pr.sh b/.ci/magic-modules/create-pr.sh index 1fc34b256473..dc01f4931a23 100755 --- a/.ci/magic-modules/create-pr.sh +++ b/.ci/magic-modules/create-pr.sh @@ -31,11 +31,10 @@ git checkout -b "$BRANCH_NAME" if [ "$BRANCH_NAME" = "$ORIGINAL_PR_BRANCH" ]; then DEPENDENCIES="" NEWLINE=$'\n' - # There is no existing PR - this is the first pass through the pipeline and # we will need to create a PR using 'hub'. if [ -n "$TERRAFORM_REPO_USER" ]; then - for VERSION in ('' 'beta'); do + for VERSION in '' 'beta'; do if [ -n "$VERSION" ]; then PROVIDER_NAME="terraform-provider-google-$VERSION" SUBMODULE_DIR="terraform-$VERSION" diff --git a/.ci/magic-modules/point-to-submodules.sh b/.ci/magic-modules/point-to-submodules.sh index 38d709afb244..7df863363240 100755 --- a/.ci/magic-modules/point-to-submodules.sh +++ b/.ci/magic-modules/point-to-submodules.sh @@ -44,7 +44,7 @@ for PRD in "${PRODUCT_ARRAY[@]}"; do done if [ "$TERRAFORM_ENABLED" = "true" ]; then - for VERSION in ('' 'beta'); do + for VERSION in '' 'beta'; do if [ -n "$VERSION" ]; then PROVIDER_NAME="terraform-provider-google-$VERSION" SUBMODULE_DIR="terraform-$VERSION" From e178fe8ed0ddbea5277130c30e21e9a567439764 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 3 Oct 2018 10:04:40 -0700 Subject: [PATCH 18/56] fix issue with empty string becoming the word null --- .ci/ci.yml.tmpl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.ci/ci.yml.tmpl b/.ci/ci.yml.tmpl index 81470d180826..ce4681b4c108 100644 --- a/.ci/ci.yml.tmpl +++ b/.ci/ci.yml.tmpl @@ -245,13 +245,14 @@ jobs: submodules: [build/terraform, build/terraform-beta] passed: [mm-generate] - aggregate: - {% for version in ['terraform', 'terraform-beta'] %} - - task: test-{{version}} + - task: test-terraform + file: magic-modules/.ci/unit-tests/task.yml + timeout: 30m + - task: test-terraform-beta file: magic-modules/.ci/unit-tests/task.yml timeout: 30m params: - VERSION: {{version.partition('-')[2]}} - {% endfor %} + VERSION: beta on_failure: do: - get: magic-modules-new-prs From 3d616b978aa1059226782a3267f9419422b76d9c Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 3 Oct 2018 13:56:24 -0700 Subject: [PATCH 19/56] more loops --- .ci/ci.yml.tmpl | 44 +++----- .ci/magic-modules/create-pr.sh | 20 ++-- .ci/magic-modules/create-pr.yml | 1 + .ci/magic-modules/generate-terraform-beta.yml | 24 ---- .ci/magic-modules/generate-terraform.sh | 103 +++++++++--------- .ci/magic-modules/generate-terraform.yml | 5 +- .ci/magic-modules/point-to-submodules.sh | 3 +- .ci/magic-modules/point-to-submodules.yml | 1 + .ci/vars.tmpl | 1 + .github/PULL_REQUEST_TEMPLATE.md | 1 + 10 files changed, 93 insertions(+), 110 deletions(-) delete mode 100644 .ci/magic-modules/generate-terraform-beta.yml diff --git a/.ci/ci.yml.tmpl b/.ci/ci.yml.tmpl index ce4681b4c108..27881225eef3 100644 --- a/.ci/ci.yml.tmpl +++ b/.ci/ci.yml.tmpl @@ -141,25 +141,18 @@ jobs: # produces: terraform-generated - task: generate-terraform file: magic-modules-branched/.ci/magic-modules/generate-terraform.yml - # Puts 'terraform-generated' into the robot's fork. - - put: terraform-intermediate params: - repository: terraform-generated - branch_file: magic-modules-branched/branchname - only_if_diff: true - force: true - - do: - # consumes: magic-modules-branched - # produces: terraform-beta-generated - - task: generate-terraform-beta - file: magic-modules-branched/.ci/magic-modules/generate-terraform-beta.yml - # Puts 'terraform-beta-generated' into the robot's fork. - - put: terraform-beta-intermediate - params: - repository: terraform-beta-generated - branch_file: magic-modules-branched/branchname - only_if_diff: true - force: true + VERSIONS: "{{','.join(vars.terraform_versions)}}" + # Puts 'terraform-generated' into the robot's fork. + - aggregate: + {% for version in vars.terraform_versions %} + - put: terraform{{ '-'+version if version else '' }}-intermediate + params: + repository: terraform{{ '-'+version if version else '' }}-generated + branch_file: magic-modules-branched/branchname + only_if_diff: true + force: true + {% endfor %} - do: # consumes: magic-modules-branched # produces: ansible-generated @@ -226,6 +219,7 @@ jobs: CREDS: ((repo-key.private_key)) PUPPET_MODULES: {{','.join(vars.puppet_modules)}} CHEF_MODULES: {{','.join(vars.chef_modules)}} + TERRAFORM_VERSIONS: "{{','.join(vars.terraform_versions)}}" TERRAFORM_ENABLED: true ANSIBLE_ENABLED: true @@ -389,6 +383,7 @@ jobs: CHEF_REPO_USER: GoogleCloudPlatform CHEF_MODULES: {{','.join(vars.chef_modules)}} {%- endif %} + TERRAFORM_VERSIONS: "{{','.join(vars.terraform_versions)}}" on_failure: put: magic-modules-new-prs params: @@ -402,9 +397,10 @@ jobs: # the pipeline (when a PR needs to be updated), this does that updating by pushing # the new code to the repository/branch from which a pull request is already open. - aggregate: - - put: terraform-intermediate + {% for version in vars.terraform_versions %} + - put: terraform{{ '-'+version if version else '' }}-intermediate params: - repository: magic-modules-with-comment/build/terraform + repository: magic-modules-with-comment/build/terraform{{ '-'+version if version else '' }} branch_file: magic-modules-with-comment/original_pr_branch_name # Every time a change runs through this pipeline, it will generate a commit with # a different hash - the hash includes timestamps. Therefore, even if there's no @@ -414,13 +410,7 @@ jobs: # not push the update even though the commit hashes are different. only_if_diff: true force: true - - put: terraform-beta-intermediate - params: - repository: magic-modules-with-comment/build/terraform-beta - branch_file: magic-modules-with-comment/original_pr_branch_name - # See comment on terraform-intermediate - only_if_diff: true - force: true + {% endfor %} - put: ansible-intermediate params: repository: magic-modules-with-comment/build/ansible diff --git a/.ci/magic-modules/create-pr.sh b/.ci/magic-modules/create-pr.sh index dc01f4931a23..340e2ce4b4af 100755 --- a/.ci/magic-modules/create-pr.sh +++ b/.ci/magic-modules/create-pr.sh @@ -26,6 +26,8 @@ echo "$ORIGINAL_PR_BRANCH" > ./original_pr_branch_name BRANCH_NAME="$(git config -f .gitmodules --get submodule.build/terraform.branch)" IFS="," read -ra PUPPET_PRODUCTS <<< "$PUPPET_MODULES" IFS="," read -ra CHEF_PRODUCTS <<< "$CHEF_MODULES" +IFS="," read -ra TERRAFORM_VERSIONS <<< "$TERRAFORM_VERSIONS" + git checkout -b "$BRANCH_NAME" if [ "$BRANCH_NAME" = "$ORIGINAL_PR_BRANCH" ]; then @@ -34,7 +36,7 @@ if [ "$BRANCH_NAME" = "$ORIGINAL_PR_BRANCH" ]; then # There is no existing PR - this is the first pass through the pipeline and # we will need to create a PR using 'hub'. if [ -n "$TERRAFORM_REPO_USER" ]; then - for VERSION in '' 'beta'; do + for VERSION in "${TERRAFORM_VERSIONS[@]}"; do if [ -n "$VERSION" ]; then PROVIDER_NAME="terraform-provider-google-$VERSION" SUBMODULE_DIR="terraform-$VERSION" @@ -154,13 +156,15 @@ else git branch -f "$ORIGINAL_PR_BRANCH" if [ -n "$TERRAFORM_REPO_USER" ]; then - pushd build/terraform - git branch -f "$ORIGINAL_PR_BRANCH" - popd - - pushd build/terraform-beta - git branch -f "$ORIGINAL_PR_BRANCH" - popd + for VERSION in "${TERRAFORM_VERSIONS[@]}"; do + if [ -n "$VERSION" ]; then + pushd "build/terraform-$VERSION" + else + pushd build/terraform + fi + git branch -f "$ORIGINAL_PR_BRANCH" + popd + done fi for PRD in "${PUPPET_PRODUCTS[@]}"; do diff --git a/.ci/magic-modules/create-pr.yml b/.ci/magic-modules/create-pr.yml index 859d1a049a91..983feeb7f5d2 100644 --- a/.ci/magic-modules/create-pr.yml +++ b/.ci/magic-modules/create-pr.yml @@ -28,3 +28,4 @@ params: PUPPET_MODULES: "" CHEF_REPO_USER: "" CHEF_MODULES: "" + TERRAFORM_VERSIONS: "" diff --git a/.ci/magic-modules/generate-terraform-beta.yml b/.ci/magic-modules/generate-terraform-beta.yml deleted file mode 100644 index 3a601fc07029..000000000000 --- a/.ci/magic-modules/generate-terraform-beta.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -# This file takes two inputs: magic-modules-branched in detached-HEAD state, and the list of patches. -# It spits out "terraform-generated", a terraform repo on a new branch (named after the -# HEAD commit on the PR), with the new generated code in it. -platform: linux - -image_resource: - type: docker-image - source: - repository: nmckinley/go-ruby-python - tag: '1.11-2.5-2.7' - -inputs: - - name: magic-modules-branched - - name: patches - -outputs: - - name: terraform-beta-generated - -run: - path: magic-modules-branched/.ci/magic-modules/generate-terraform.sh - -params: - VERSION: beta diff --git a/.ci/magic-modules/generate-terraform.sh b/.ci/magic-modules/generate-terraform.sh index dac6a0314197..2deb243b8c8c 100755 --- a/.ci/magic-modules/generate-terraform.sh +++ b/.ci/magic-modules/generate-terraform.sh @@ -14,57 +14,62 @@ PATCH_DIR="$(pwd)/patches" export GOPATH="${PWD}/go" mkdir -p "${GOPATH}/src/github.com/terraform-providers" -if [ -n "$VERSION" ]; then - PROVIDER_NAME="terraform-provider-google-$VERSION" - SUBMODULE_DIR="terraform-$VERSION" - VERSION_FLAG="-v $VERSION" -else - PROVIDER_NAME="terraform-provider-google" - SUBMODULE_DIR="terraform" - VERSION_FLAG="" -fi - -pushd magic-modules-branched -ln -s "${PWD}/build/$SUBMODULE_DIR/" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" -popd - -pushd "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" -go get -popd - -pushd magic-modules-branched -LAST_COMMIT_AUTHOR="$(git log --pretty="%an <%ae>" -n1 HEAD)" -bundle install - -# Build all terraform products -# Since not all products have a beta version, generate everything at GA, then -# generate the ones that have a beta version at beta. -bundle exec compiler -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" $VERSION_FLAG - -# Resources that were already using beta APIs before they started being autogenerated -bundle exec compiler -v beta -p products/compute -t Address,Firewall,ForwardingRule,GlobalAddress,RegionDisk,Subnetwork,VpnTunnel -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" - -# This command can crash - if that happens, the script should not fail. -set +e -TERRAFORM_COMMIT_MSG="$(python .ci/magic-modules/extract_from_pr_description.py --tag terraform < .git/body)" -set -e -if [ -z "$TERRAFORM_COMMIT_MSG" ]; then - TERRAFORM_COMMIT_MSG="Magic Modules changes." -fi +IFS="," read -ra VERSION_ARRAY <<< "$VERSIONS" +for VERSION in "${VERSION_ARRAY[@]}"; do + + if [ -n "$VERSION" ]; then + PROVIDER_NAME="terraform-provider-google-$VERSION" + SUBMODULE_DIR="terraform-$VERSION" + VERSION_FLAG="-v $VERSION" + else + PROVIDER_NAME="terraform-provider-google" + SUBMODULE_DIR="terraform" + VERSION_FLAG="" + fi + + pushd magic-modules-branched + ln -s "${PWD}/build/$SUBMODULE_DIR/" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" + popd + + pushd "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" + go get + popd + + pushd magic-modules-branched + LAST_COMMIT_AUTHOR="$(git log --pretty="%an <%ae>" -n1 HEAD)" + bundle install + + # Build all terraform products + # Since not all products have a beta version, generate everything at GA, then + # generate the ones that have a beta version at beta. + bundle exec compiler -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" $VERSION_FLAG + + # Resources that were already using beta APIs before they started being autogenerated + bundle exec compiler -v beta -p products/compute -t Address,Firewall,ForwardingRule,GlobalAddress,RegionDisk,Subnetwork,VpnTunnel -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" + + # This command can crash - if that happens, the script should not fail. + set +e + TERRAFORM_COMMIT_MSG="$(python .ci/magic-modules/extract_from_pr_description.py --tag $SUBMODULE_DIR < .git/body)" + set -e + if [ -z "$TERRAFORM_COMMIT_MSG" ]; then + TERRAFORM_COMMIT_MSG="Magic Modules changes." + fi + + pushd "build/$SUBMODULE_DIR" + # These config entries will set the "committer". + git config --global user.email "magic-modules@google.com" + git config --global user.name "Modular Magician" -pushd "build/$SUBMODULE_DIR" -# These config entries will set the "committer". -git config --global user.email "magic-modules@google.com" -git config --global user.name "Modular Magician" + git add -A + # Set the "author" to the commit's real author. + git commit -m "$TERRAFORM_COMMIT_MSG" --author="$LAST_COMMIT_AUTHOR" || true # don't crash if no changes + git checkout -B "$(cat ../../branchname)" -git add -A -# Set the "author" to the commit's real author. -git commit -m "$TERRAFORM_COMMIT_MSG" --author="$LAST_COMMIT_AUTHOR" || true # don't crash if no changes -git checkout -B "$(cat ../../branchname)" + apply_patches "$PATCH_DIR/terraform-providers/$PROVIDER_NAME" "$TERRAFORM_COMMIT_MSG" "$LAST_COMMIT_AUTHOR" "master" -apply_patches "$PATCH_DIR/terraform-providers/$PROVIDER_NAME" "$TERRAFORM_COMMIT_MSG" "$LAST_COMMIT_AUTHOR" "master" + popd + popd -popd -popd + git clone magic-modules-branched/build/$SUBMODULE_DIR ./$SUBMODULE_DIR-generated -git clone magic-modules-branched/build/$SUBMODULE_DIR ./$SUBMODULE_DIR-generated +done diff --git a/.ci/magic-modules/generate-terraform.yml b/.ci/magic-modules/generate-terraform.yml index ef72a9c238d9..ac7242a184f8 100644 --- a/.ci/magic-modules/generate-terraform.yml +++ b/.ci/magic-modules/generate-terraform.yml @@ -18,4 +18,7 @@ outputs: - name: terraform-generated run: - path: magic-modules-branched/.ci/magic-modules/generate-terraform.sh \ No newline at end of file + path: magic-modules-branched/.ci/magic-modules/generate-terraform.sh + +params: + VERSIONS: "" diff --git a/.ci/magic-modules/point-to-submodules.sh b/.ci/magic-modules/point-to-submodules.sh index 7df863363240..fb737d4af194 100755 --- a/.ci/magic-modules/point-to-submodules.sh +++ b/.ci/magic-modules/point-to-submodules.sh @@ -44,7 +44,8 @@ for PRD in "${PRODUCT_ARRAY[@]}"; do done if [ "$TERRAFORM_ENABLED" = "true" ]; then - for VERSION in '' 'beta'; do + IFS="," read -ra TERRAFORM_VERSIONS <<< "$TERRAFORM_VERSIONS" + for VERSION in "${TERRAFORM_VERSIONS[@]}"; do if [ -n "$VERSION" ]; then PROVIDER_NAME="terraform-provider-google-$VERSION" SUBMODULE_DIR="terraform-$VERSION" diff --git a/.ci/magic-modules/point-to-submodules.yml b/.ci/magic-modules/point-to-submodules.yml index 82f5c5653674..930179dde799 100644 --- a/.ci/magic-modules/point-to-submodules.yml +++ b/.ci/magic-modules/point-to-submodules.yml @@ -23,6 +23,7 @@ params: GH_USERNAME: "" CREDS: "" TERRAFORM_ENABLED: false + TERRAFORM_VERSIONS: "" ANSIBLE_ENABLED: false PUPPET_MODULES: "" CHEF_MODULES: "" diff --git a/.ci/vars.tmpl b/.ci/vars.tmpl index 4fca15784a2b..4f63641c71dd 100644 --- a/.ci/vars.tmpl +++ b/.ci/vars.tmpl @@ -37,3 +37,4 @@ build/{{repo}}/{{name}} ] } %} +{% set terraform_versions = ['', 'beta'] %} diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index be8c9d4a27dc..092544af5db7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -21,6 +21,7 @@ this PR's changes from the commit messages for downstream commits. ----------------------------------------------------------------- # [all] ## [terraform] +### [terraform-beta] ## [puppet] ### [puppet-bigquery] ### [puppet-compute] From 47aada05418ffdeb6034ea084522be942bc33d4c Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 3 Oct 2018 14:32:54 -0700 Subject: [PATCH 20/56] add terraform-beta-generated output back --- .ci/magic-modules/generate-terraform.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/magic-modules/generate-terraform.yml b/.ci/magic-modules/generate-terraform.yml index ac7242a184f8..5b9ef618bac0 100644 --- a/.ci/magic-modules/generate-terraform.yml +++ b/.ci/magic-modules/generate-terraform.yml @@ -16,6 +16,7 @@ inputs: outputs: - name: terraform-generated + - name: terraform-beta-generated run: path: magic-modules-branched/.ci/magic-modules/generate-terraform.sh From 78d03a54051561f8b8988fdc3c6254b3d3671632 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Fri, 5 Oct 2018 21:26:23 -0700 Subject: [PATCH 21/56] put all files in google-beta if the version is beta --- products/binaryauthorization/terraform.yaml | 13 ++------ products/compute/terraform.yaml | 33 ++------------------- products/containeranalysis/terraform.yaml | 11 ++----- products/filestore/terraform.yaml | 8 ++--- products/monitoring/terraform.yaml | 7 +++-- products/redis/terraform.yaml | 12 ++------ products/resourcemanager/terraform.yaml | 7 ++--- provider/terraform.rb | 6 ++-- 8 files changed, 20 insertions(+), 77 deletions(-) diff --git a/products/binaryauthorization/terraform.yaml b/products/binaryauthorization/terraform.yaml index 108d07da61a6..882e0f654ff2 100644 --- a/products/binaryauthorization/terraform.yaml +++ b/products/binaryauthorization/terraform.yaml @@ -107,17 +107,8 @@ overrides: !ruby/object:Provider::ResourceOverrides files: !ruby/object:Provider::Config::Files # All of these files will be copied verbatim. copy: - 'google/transport.go': 'templates/terraform/transport.go' - 'google/transport_test.go': 'templates/terraform/transport_test.go' - 'google/import.go': 'templates/terraform/import.go' - 'google/import_test.go': 'templates/terraform/import_test.go' - # Handwritten acceptance tests for autogenerated resources. - # Adding them here allows updating the tests as part of a MM pull request. - 'google/resource_binaryauthorization_attestor_test.go': - 'templates/terraform/tests/resource_binaryauthorization_attestor_test.go' - 'google/resource_binaryauthorization_policy_test.go': - 'templates/terraform/tests/resource_binaryauthorization_policy_test.go' +<%= lines(indent(compile('provider/terraform/common~copy.yaml'), 4)) -%> # These files have templating (ERB) code that will be run. # This is usually to add licensing info, autogeneration notices, etc. compile: - 'google/provider_{{product_name}}_gen.go': 'templates/terraform/provider_gen.erb' +<%= lines(indent(compile('provider/terraform/common~compile.yaml'), 4)) -%> diff --git a/products/compute/terraform.yaml b/products/compute/terraform.yaml index bc2a06bff921..82d597996d5c 100644 --- a/products/compute/terraform.yaml +++ b/products/compute/terraform.yaml @@ -861,40 +861,11 @@ examples: !ruby/object:Api::Resource::HashArray files: !ruby/object:Provider::Config::Files # All of these files will be copied verbatim. copy: - 'google/transport.go': 'templates/terraform/transport.go' - 'google/transport_test.go': 'templates/terraform/transport_test.go' - 'google/import.go': 'templates/terraform/import.go' - 'google/import_test.go': 'templates/terraform/import_test.go' - # Handwritten acceptance tests for autogenerated resources. - # Adding them here allows updating the tests as part of a MM pull request. - 'google/resource_compute_address_test.go': - 'templates/terraform/tests/resource_compute_address_test.go' - 'google/resource_compute_autoscaler_test.go': - 'templates/terraform/tests/resource_compute_autoscaler_test.go' - 'google/resource_compute_firewall_test.go': - 'templates/terraform/tests/resource_compute_firewall_test.go' - 'google/resource_compute_global_address_test.go': - 'templates/terraform/tests/resource_compute_global_address_test.go' - 'google/resource_compute_health_check_test.go': - 'templates/terraform/tests/resource_compute_health_check_test.go' - 'google/resource_compute_region_autoscaler_test.go': - 'templates/terraform/tests/resource_compute_region_autoscaler_test.go' - 'google/resource_compute_region_disk_test.go': - 'templates/terraform/tests/resource_compute_region_disk_test.go' - 'google/resource_compute_router_test.go': - 'templates/terraform/tests/resource_compute_router_test.go' - 'google/resource_compute_ssl_certificate_test.go': - 'templates/terraform/tests/resource_compute_ssl_certificate_test.go' - 'google/resource_compute_target_https_proxy_test.go': - 'templates/terraform/tests/resource_compute_target_https_proxy_test.go' - 'google/resource_compute_target_ssl_proxy_test.go': - 'templates/terraform/tests/resource_compute_target_ssl_proxy_test.go' - 'google/resource_compute_vpn_tunnel_test.go': - 'templates/terraform/tests/resource_compute_vpn_tunnel_test.go' +<%= lines(indent(compile('provider/terraform/common~copy.yaml'), 4)) -%> # These files have templating (ERB) code that will be run. # This is usually to add licensing info, autogeneration notices, etc. compile: - 'google/provider_{{product_name}}_gen.go': 'templates/terraform/provider_gen.erb' +<%= lines(indent(compile('provider/terraform/common~compile.yaml'), 4)) -%> # This is for custom testing code. All of our tests follow a specific pattern # that sometimes needs to be deviated from. We're working towards a world where diff --git a/products/containeranalysis/terraform.yaml b/products/containeranalysis/terraform.yaml index b05c6801db54..6a9b7837bd28 100644 --- a/products/containeranalysis/terraform.yaml +++ b/products/containeranalysis/terraform.yaml @@ -35,18 +35,11 @@ examples: !ruby/object:Api::Resource::HashArray files: !ruby/object:Provider::Config::Files # All of these files will be copied verbatim. copy: - 'google/transport.go': 'templates/terraform/transport.go' - 'google/transport_test.go': 'templates/terraform/transport_test.go' - 'google/import.go': 'templates/terraform/import.go' - 'google/import_test.go': 'templates/terraform/import_test.go' - # Handwritten acceptance tests for autogenerated resources. - # Adding them here allows updating the tests as part of a MM pull request. - 'google/resource_containeranalysis_note_test.go': - 'templates/terraform/tests/resource_containeranalysis_note_test.go' +<%= lines(indent(compile('provider/terraform/common~copy.yaml'), 4)) -%> # These files have templating (ERB) code that will be run. # This is usually to add licensing info, autogeneration notices, etc. compile: - 'google/provider_{{product_name}}_gen.go': 'templates/terraform/provider_gen.erb' +<%= lines(indent(compile('provider/terraform/common~compile.yaml'), 4)) -%> # This is for custom testing code. All of our tests follow a specific pattern # that sometimes needs to be deviated from. We're working towards a world where diff --git a/products/filestore/terraform.yaml b/products/filestore/terraform.yaml index 54780439a2a6..a312b214f7cd 100644 --- a/products/filestore/terraform.yaml +++ b/products/filestore/terraform.yaml @@ -41,15 +41,11 @@ examples: !ruby/object:Api::Resource::HashArray files: !ruby/object:Provider::Config::Files # All of these files will be copied verbatim. copy: - 'google/transport.go': 'templates/terraform/transport.go' - 'google/transport_test.go': 'templates/terraform/transport_test.go' - 'google/import.go': 'templates/terraform/import.go' - 'google/import_test.go': 'templates/terraform/import_test.go' - 'google/filestore_operation.go': 'templates/terraform/filestore_operation.go' +<%= lines(indent(compile('provider/terraform/common~copy.yaml'), 4)) -%> # These files have templating (ERB) code that will be run. # This is usually to add licensing info, autogeneration notices, etc. compile: - 'google/provider_{{product_name}}_gen.go': 'templates/terraform/provider_gen.erb' +<%= lines(indent(compile('provider/terraform/common~compile.yaml'), 4)) -%> # This is for custom testing code. All of our tests follow a specific pattern # that sometimes needs to be deviated from. We're working towards a world where diff --git a/products/monitoring/terraform.yaml b/products/monitoring/terraform.yaml index 910f19a1cf85..7cbb514b6af2 100644 --- a/products/monitoring/terraform.yaml +++ b/products/monitoring/terraform.yaml @@ -86,7 +86,8 @@ overrides: !ruby/object:Provider::ResourceOverrides files: !ruby/object:Provider::Config::Files copy: - 'google/resource_monitoring_alert_policy_test.go': - 'templates/terraform/tests/resource_monitoring_alert_policy_test.go' +<%= lines(indent(compile('provider/terraform/common~copy.yaml'), 4)) -%> + # These files have templating (ERB) code that will be run. + # This is usually to add licensing info, autogeneration notices, etc. compile: - 'google/provider_{{product_name}}_gen.go': 'templates/terraform/provider_gen.erb' +<%= lines(indent(compile('provider/terraform/common~compile.yaml'), 4)) -%> diff --git a/products/redis/terraform.yaml b/products/redis/terraform.yaml index a0ac33f757c7..ba05a42707f4 100644 --- a/products/redis/terraform.yaml +++ b/products/redis/terraform.yaml @@ -59,19 +59,11 @@ examples: !ruby/object:Api::Resource::HashArray files: !ruby/object:Provider::Config::Files # All of these files will be copied verbatim. copy: - 'google/transport.go': 'templates/terraform/transport.go' - 'google/transport_test.go': 'templates/terraform/transport_test.go' - 'google/import.go': 'templates/terraform/import.go' - 'google/import_test.go': 'templates/terraform/import_test.go' - 'google/redis_operation.go': 'templates/terraform/redis_operation.go' - # Handwritten acceptance tests for autogenerated resources. - # Adding them here allows updating the tests as part of a MM pull request. - 'google/resource_redis_instance_test.go': - 'templates/terraform/tests/resource_redis_instance_test.go' +<%= lines(indent(compile('provider/terraform/common~copy.yaml'), 4)) -%> # These files have templating (ERB) code that will be run. # This is usually to add licensing info, autogeneration notices, etc. compile: - 'google/provider_{{product_name}}_gen.go': 'templates/terraform/provider_gen.erb' +<%= lines(indent(compile('provider/terraform/common~compile.yaml'), 4)) -%> # This is for custom testing code. All of our tests follow a specific pattern # that sometimes needs to be deviated from. We're working towards a world where diff --git a/products/resourcemanager/terraform.yaml b/products/resourcemanager/terraform.yaml index 631aa5662760..39bec11253e5 100644 --- a/products/resourcemanager/terraform.yaml +++ b/products/resourcemanager/terraform.yaml @@ -38,11 +38,8 @@ overrides: !ruby/object:Provider::ResourceOverrides files: !ruby/object:Provider::Config::Files # All of these files will be copied verbatim. copy: - 'google/transport.go': 'templates/terraform/transport.go' - 'google/transport_test.go': 'templates/terraform/transport_test.go' - 'google/import.go': 'templates/terraform/import.go' - 'google/import_test.go': 'templates/terraform/import_test.go' +<%= lines(indent(compile('provider/terraform/common~copy.yaml'), 4)) -%> # These files have templating (ERB) code that will be run. # This is usually to add licensing info, autogeneration notices, etc. compile: - 'google/provider_{{product_name}}_gen.go': 'templates/terraform/provider_gen.erb' +<%= lines(indent(compile('provider/terraform/common~compile.yaml'), 4)) -%> diff --git a/provider/terraform.rb b/provider/terraform.rb index e761d4a646d9..81b971055a59 100644 --- a/provider/terraform.rb +++ b/provider/terraform.rb @@ -117,7 +117,8 @@ def nested_properties(property) # per resource. The resource.erb template forms the basis of a single # GCP Resource on Terraform. def generate_resource(data) - target_folder = File.join(data[:output_folder], 'google') + dir = data[:version].name == 'beta' ? 'google-beta' : 'google' + target_folder = File.join(data[:output_folder], dir) FileUtils.mkpath target_folder name = data[:object].name.underscore product_name = data[:product_name].underscore @@ -150,7 +151,8 @@ def generate_documentation(data) def generate_resource_tests(data) return if data[:object].example.reject(&:skip_test).empty? - target_folder = File.join(data[:output_folder], 'google') + dir = data[:version].name == 'beta' ? 'google-beta' : 'google' + target_folder = File.join(data[:output_folder], dir) FileUtils.mkpath target_folder name = data[:object].name.underscore product_name = data[:product_name].underscore From 627bd3fee47f7ee927f068e67d9841f6205eb85d Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Fri, 5 Oct 2018 22:55:39 -0700 Subject: [PATCH 22/56] add common files --- provider/terraform/common~compile.yaml | 17 +++++++++++++++ provider/terraform/common~copy.yaml | 29 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 provider/terraform/common~compile.yaml create mode 100644 provider/terraform/common~copy.yaml diff --git a/provider/terraform/common~compile.yaml b/provider/terraform/common~compile.yaml new file mode 100644 index 000000000000..eaa276b7fab9 --- /dev/null +++ b/provider/terraform/common~compile.yaml @@ -0,0 +1,17 @@ +# Copyright 2018 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. +# These files contains code that needs to be compiled before being delivered to +# the final module tree structure: + +<% dir = _version_name == 'beta' ? 'google-beta' : 'google' -%> +'<%= dir -%>/provider_<%= api.prefix[1..-1] -%>_gen.go': 'templates/terraform/provider_gen.erb' diff --git a/provider/terraform/common~copy.yaml b/provider/terraform/common~copy.yaml new file mode 100644 index 000000000000..3cdcc083dfeb --- /dev/null +++ b/provider/terraform/common~copy.yaml @@ -0,0 +1,29 @@ +# Copyright 2018 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. +# These files contains code that needs to be compiled before being delivered to +# the final module tree structure: + +<% + dir = _version_name == 'beta' ? 'google-beta' : 'google' + Dir["templates/terraform/tests/*#{api.prefix[1..-1]}*"].each do |file_path| + fname = file_path.split('/')[-1] +-%> +# Handwritten acceptance tests for autogenerated resources. +# Adding them here allows updating the tests as part of a MM pull request. +'<%= dir -%>/<%= fname -%>': 'templates/terraform/tests/<%= fname -%>' +<% end -%> + +'<%= dir -%>/transport.go': 'templates/terraform/transport.go' +'<%= dir -%>/transport_test.go': 'templates/terraform/transport_test.go' +'<%= dir -%>/import.go': 'templates/terraform/import.go' +'<%= dir -%>/import_test.go': 'templates/terraform/import_test.go' From 3bce028f217f7f420a5bd10bdec81cf686590b5a Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Fri, 5 Oct 2018 23:20:58 -0700 Subject: [PATCH 23/56] fix test target dir --- .ci/unit-tests/run.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.ci/unit-tests/run.sh b/.ci/unit-tests/run.sh index cf9a7e908be6..5cea80e47800 100755 --- a/.ci/unit-tests/run.sh +++ b/.ci/unit-tests/run.sh @@ -11,9 +11,11 @@ set -x if [ -n "$VERSION" ]; then PROVIDER_NAME="terraform-provider-google-$VERSION" SUBMODULE_DIR="terraform-$VERSION" + TEST_DIR="google-$VERSION" else PROVIDER_NAME="terraform-provider-google" SUBMODULE_DIR="terraform" + TEST_DIR="google" fi # Create GOPATH structure @@ -22,4 +24,4 @@ ln -s "${PWD}/magic-modules/build/$SUBMODULE_DIR" "${GOPATH}/src/github.com/terr cd "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" -go test -v ./google -parallel 16 -run '^Test' -timeout 1m +go test -v ./$TEST_DIR -parallel 16 -run '^Test' -timeout 1m From cfdbee60252e67bfd5233e211caa7f4f1fb6abcf Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 8 Oct 2018 11:18:36 -0700 Subject: [PATCH 24/56] mark labelfingerprint beta for globaladdress --- products/compute/api.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/products/compute/api.yaml b/products/compute/api.yaml index a84f97abfe8d..2f80297a24bd 100644 --- a/products/compute/api.yaml +++ b/products/compute/api.yaml @@ -1070,6 +1070,7 @@ objects: internally during updates. update_url: 'projects/{{project}}/global/addresses/{{name}}/setLabels' update_verb: :POST + min_version: beta - !ruby/object:Api::Type::Enum name: 'ipVersion' description: | From 2e54ebbfcf725e2ddebb8569291ff0650e292bb2 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 8 Oct 2018 11:23:09 -0700 Subject: [PATCH 25/56] stop explicitly generating some beta apis --- .ci/magic-modules/generate-terraform.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.ci/magic-modules/generate-terraform.sh b/.ci/magic-modules/generate-terraform.sh index 2deb243b8c8c..815b83fb3f3b 100755 --- a/.ci/magic-modules/generate-terraform.sh +++ b/.ci/magic-modules/generate-terraform.sh @@ -40,13 +40,8 @@ for VERSION in "${VERSION_ARRAY[@]}"; do bundle install # Build all terraform products - # Since not all products have a beta version, generate everything at GA, then - # generate the ones that have a beta version at beta. bundle exec compiler -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" $VERSION_FLAG - # Resources that were already using beta APIs before they started being autogenerated - bundle exec compiler -v beta -p products/compute -t Address,Firewall,ForwardingRule,GlobalAddress,RegionDisk,Subnetwork,VpnTunnel -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" - # This command can crash - if that happens, the script should not fail. set +e TERRAFORM_COMMIT_MSG="$(python .ci/magic-modules/extract_from_pr_description.py --tag $SUBMODULE_DIR < .git/body)" From 7ef2ac59b5c96373b66137197370f8e7ea6282f6 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 8 Oct 2018 15:06:40 -0700 Subject: [PATCH 26/56] generate at lower version if product not at version --- api/product.rb | 12 ++++++++++-- compiler.rb | 2 +- provider/core.rb | 25 +++++++++++++++---------- provider/terraform.rb | 4 ++-- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/api/product.rb b/api/product.rb index 6606e0c044ff..00ae1f287030 100644 --- a/api/product.rb +++ b/api/product.rb @@ -81,7 +81,7 @@ def version_obj(name) end def version_obj_or_default(name) - name.nil? ? default_version : version_obj(name) + exists_at_version(name) ? version_obj(name) : default_version end # Not a conventional setter, so ignore rubocop's warning @@ -91,8 +91,16 @@ def set_properties_based_on_version(version) end # rubocop:enable Naming/AccessorMethodName + def exists_at_version_or_lower(name) + name ||= 'ga' + return false if !Version::ORDER.include?(name) + for i in 0..Version::ORDER.index(name) do + return true if exists_at_version(Version::ORDER[i]) + end + false + end + def exists_at_version(name) - return true if name.nil? @versions.each do |v| return true if v.name == name end diff --git a/compiler.rb b/compiler.rb index 2edc1f8eacee..97dddcbf738a 100755 --- a/compiler.rb +++ b/compiler.rb @@ -116,7 +116,7 @@ product_api.validate pp product_api if ENV['COMPILER_DEBUG'] - unless product_api.exists_at_version(version) + unless product_api.exists_at_version_or_lower(version) Google::LOGGER.info \ "'#{product_name}' does not have a '#{version}' version, skipping" next diff --git a/provider/core.rb b/provider/core.rb index e9fa42a2b615..ebe6fa391cf4 100644 --- a/provider/core.rb +++ b/provider/core.rb @@ -65,8 +65,7 @@ def initialize(config, api) # rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/PerceivedComplexity def generate(output_folder, types, version_name) - version = @api.version_obj_or_default(version_name) - generate_objects(output_folder, types, version) + generate_objects(output_folder, types, version_name) generate_client_functions(output_folder) unless @config.functions.nil? copy_files(output_folder) \ unless @config.files.nil? || @config.files.copy.nil? @@ -81,7 +80,7 @@ def generate(output_folder, types, version_name) compile_files(output_folder) \ unless @config.files.nil? || @config.files.compile.nil? - generate_datasources(output_folder, types, version) \ + generate_datasources(output_folder, types, version_name) \ unless @config.datasources.nil? apply_file_acls(output_folder) \ unless @config.files.nil? || @config.files.permissions.nil? @@ -217,7 +216,8 @@ def compile_file_list(output_folder, files, data = {}) # rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/PerceivedComplexity # rubocop:disable Metrics/AbcSize - def generate_objects(output_folder, types, version) + def generate_objects(output_folder, types, version_name) + version = @api.version_obj_or_default(version_name) @api.set_properties_based_on_version(version) (@api.objects || []).each do |object| if !types.empty? && !types.include?(object.name) @@ -227,7 +227,11 @@ def generate_objects(output_folder, types, version) elsif types.empty? && object.exclude_if_not_in_version(version) Google::LOGGER.info "Excluding #{object.name} per API version" else - generate_object object, output_folder, version + # version_name will differ from version.name if the resource is being + # generated at its default version instead of the one that was passed + # in to the compiler. Terraform needs to know which version was passed + # in so it can name its output directories correctly. + generate_object object, output_folder, version_name end end end @@ -235,8 +239,8 @@ def generate_objects(output_folder, types, version) # rubocop:enable Metrics/PerceivedComplexity # rubocop:enable Metrics/AbcSize - def generate_object(object, output_folder, version) - data = build_object_data(object, output_folder, version) + def generate_object(object, output_folder, version_name) + data = build_object_data(object, output_folder, version_name) generate_resource data generate_resource_tests data @@ -251,6 +255,7 @@ def generate_datasources(output_folder, types, version) # We need to apply overrides for datasources @config.datasources.validate + version = @api.version_obj_or_default(version_name) @api.set_properties_based_on_version(version) @api.objects.each do |object| if !types.empty? && !types.include?(object.name) @@ -266,7 +271,7 @@ def generate_datasources(output_folder, types, version) "Excluding #{object.name} datasource per API version" ) else - generate_datasource object, output_folder, version + generate_datasource object, output_folder, version_name end end end @@ -274,8 +279,8 @@ def generate_datasources(output_folder, types, version) # rubocop:enable Metrics/PerceivedComplexity # rubocop:enable Metrics/AbcSize - def generate_datasource(object, output_folder, version) - data = build_object_data(object, output_folder, version) + def generate_datasource(object, output_folder, versio_name) + data = build_object_data(object, output_folder, version_name) compile_datasource data end diff --git a/provider/terraform.rb b/provider/terraform.rb index 81b971055a59..da1ed37bd54d 100644 --- a/provider/terraform.rb +++ b/provider/terraform.rb @@ -117,7 +117,7 @@ def nested_properties(property) # per resource. The resource.erb template forms the basis of a single # GCP Resource on Terraform. def generate_resource(data) - dir = data[:version].name == 'beta' ? 'google-beta' : 'google' + dir = data[:version] == 'beta' ? 'google-beta' : 'google' target_folder = File.join(data[:output_folder], dir) FileUtils.mkpath target_folder name = data[:object].name.underscore @@ -151,7 +151,7 @@ def generate_documentation(data) def generate_resource_tests(data) return if data[:object].example.reject(&:skip_test).empty? - dir = data[:version].name == 'beta' ? 'google-beta' : 'google' + dir = data[:version] == 'beta' ? 'google-beta' : 'google' target_folder = File.join(data[:output_folder], dir) FileUtils.mkpath target_folder name = data[:object].name.underscore From a68e462a2c4df88c444362ab1b6281f38dbed3c4 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 8 Oct 2018 15:06:48 -0700 Subject: [PATCH 27/56] remove deprecation messages --- templates/terraform/property_documentation.erb | 4 ---- templates/terraform/resource.erb | 5 ----- templates/terraform/resource.html.markdown.erb | 5 ----- templates/terraform/schema_property.erb | 5 ----- 4 files changed, 19 deletions(-) diff --git a/templates/terraform/property_documentation.erb b/templates/terraform/property_documentation.erb index a1eaa2768115..079ec14626eb 100644 --- a/templates/terraform/property_documentation.erb +++ b/templates/terraform/property_documentation.erb @@ -9,7 +9,3 @@ <% if property.is_a?(Api::Type::NestedObject) || string_to_object_map?(property) || (property.is_a?(Api::Type::Array) && property.item_type.is_a?(Api::Type::NestedObject)) -%> Structure is documented below. <% end -%> -<% if property.min_version.name == 'beta' && property.__resource.min_version.name != 'beta' -%> - This property is in beta, and should be used with the terraform-provider-google-beta provider. - See [Provider Versions](https://terraform.io/docs/provider/google/provider_versions.html) for more details on beta fields. -<% end -%> diff --git a/templates/terraform/resource.erb b/templates/terraform/resource.erb index fe9ad44b5f9c..57139507f56b 100644 --- a/templates/terraform/resource.erb +++ b/templates/terraform/resource.erb @@ -49,11 +49,6 @@ func resource<%= resource_name -%>() *schema.Resource { Importer: &schema.ResourceImporter{ State: resource<%= resource_name -%>Import, }, -<% if object.min_version.name == 'beta' -%> - DeprecationMessage: `This resource is in beta and will be removed from this provider. -Use the <%= resource_name -%> resource in the terraform-provider-google-beta provider to continue using it. -See https://terraform.io/docs/provider/google/provider_versions.html for more details on beta resources.`, -<% end -%> <% unless object.async.nil? -%> Timeouts: &schema.ResourceTimeout { diff --git a/templates/terraform/resource.html.markdown.erb b/templates/terraform/resource.html.markdown.erb index 4cbc87e0029c..52d6d1924116 100644 --- a/templates/terraform/resource.html.markdown.erb +++ b/templates/terraform/resource.html.markdown.erb @@ -52,11 +52,6 @@ description: |- <%= lines(object.description) -%> -<% if object.min_version.name == 'beta' -%> -~> **Warning:** This resource is in beta, and should be used with the terraform-provider-google-beta provider. -See [Provider Versions](https://terraform.io/docs/provider/google/provider_versions.html) for more details on beta resources. -<% end -%> - <% if !object.references.nil? -%> To get more information about <%= object.name -%>, see: diff --git a/templates/terraform/schema_property.erb b/templates/terraform/schema_property.erb index e399023aeb4d..7f2a5f15d088 100644 --- a/templates/terraform/schema_property.erb +++ b/templates/terraform/schema_property.erb @@ -32,11 +32,6 @@ <% if force_new?(property, object) -%> ForceNew: true, <% end -%> -<% if property.min_version.name == 'beta' && property.__resource.min_version.name != 'beta' -%> - Deprecated: `This field is in beta and will be removed from this provider. -Use the terraform-provider-google-beta provider to continue using it. -See https://terraform.io/docs/provider/google/provider_versions.html for more details on beta fields.`, -<% end -%> <% unless property.validation.nil? || property.output -%> <% if !property.validation.regex.nil? -%> ValidateFunc: validateRegexp(`<%= property.validation.regex -%>`), From 98b8eb4d35367a57afc7a7850029ca51f5f6ea73 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 8 Oct 2018 15:20:39 -0700 Subject: [PATCH 28/56] return true if no versions for _bundles --- api/product.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/api/product.rb b/api/product.rb index 00ae1f287030..bd7f4af165ec 100644 --- a/api/product.rb +++ b/api/product.rb @@ -101,6 +101,7 @@ def exists_at_version_or_lower(name) end def exists_at_version(name) + return true if @versions.nil? @versions.each do |v| return true if v.name == name end From 1ec9255d815764025687b681f5f9e0d2a48045e0 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 8 Oct 2018 15:26:07 -0700 Subject: [PATCH 29/56] rubocop --- api/product.rb | 4 ++-- provider/core.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/product.rb b/api/product.rb index bd7f4af165ec..d4c315987cdb 100644 --- a/api/product.rb +++ b/api/product.rb @@ -93,8 +93,8 @@ def set_properties_based_on_version(version) def exists_at_version_or_lower(name) name ||= 'ga' - return false if !Version::ORDER.include?(name) - for i in 0..Version::ORDER.index(name) do + return false unless Version::ORDER.include?(name) + (0..Version::ORDER.index(name)).each do |i| return true if exists_at_version(Version::ORDER[i]) end false diff --git a/provider/core.rb b/provider/core.rb index ebe6fa391cf4..9c934674551f 100644 --- a/provider/core.rb +++ b/provider/core.rb @@ -251,7 +251,7 @@ def generate_object(object, output_folder, version_name) # rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/PerceivedComplexity - def generate_datasources(output_folder, types, version) + def generate_datasources(output_folder, types, version_name) # We need to apply overrides for datasources @config.datasources.validate @@ -279,7 +279,7 @@ def generate_datasources(output_folder, types, version) # rubocop:enable Metrics/PerceivedComplexity # rubocop:enable Metrics/AbcSize - def generate_datasource(object, output_folder, versio_name) + def generate_datasource(object, output_folder, version_name) data = build_object_data(object, output_folder, version_name) compile_datasource data From 078fb25dd801933024acbfa866dabc423ead44e1 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 8 Oct 2018 16:12:10 -0700 Subject: [PATCH 30/56] another exists if there are no versions --- api/product.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/api/product.rb b/api/product.rb index d4c315987cdb..c6543a9a5e28 100644 --- a/api/product.rb +++ b/api/product.rb @@ -92,6 +92,7 @@ def set_properties_based_on_version(version) # rubocop:enable Naming/AccessorMethodName def exists_at_version_or_lower(name) + return true if @versions.nil? name ||= 'ga' return false unless Version::ORDER.include?(name) (0..Version::ORDER.index(name)).each do |i| From 9e953ce29db2cd23f8ac8dfe40300b1ff21b412c Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 8 Oct 2018 16:57:08 -0700 Subject: [PATCH 31/56] auth api name should be ga --- products/auth/api.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/products/auth/api.yaml b/products/auth/api.yaml index 34608e33feef..a661b609fcef 100644 --- a/products/auth/api.yaml +++ b/products/auth/api.yaml @@ -16,6 +16,6 @@ name: Google Cloud Auth prefix: gauth versions: - !ruby/object:Api::Product::Version - name: v1 + name: ga base_url: 'dummy_url' default: true From 313af83b049c40f0d47896205448ca1b7df4d059 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Tue, 9 Oct 2018 13:23:42 -0700 Subject: [PATCH 32/56] add 2.0.0 branch to beta submodule and extra newline back to tf docs --- .gitmodules | 1 + templates/terraform/resource.html.markdown.erb | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitmodules b/.gitmodules index cb00a88a2646..94f1fbe6b186 100644 --- a/.gitmodules +++ b/.gitmodules @@ -84,3 +84,4 @@ [submodule "build/terraform-beta"] path = build/terraform-beta url = git@github.com:terraform-providers/terraform-provider-google-beta.git + branch = 2.0.0 diff --git a/templates/terraform/resource.html.markdown.erb b/templates/terraform/resource.html.markdown.erb index 52d6d1924116..dcbbed33dcbb 100644 --- a/templates/terraform/resource.html.markdown.erb +++ b/templates/terraform/resource.html.markdown.erb @@ -52,6 +52,7 @@ description: |- <%= lines(object.description) -%> + <% if !object.references.nil? -%> To get more information about <%= object.name -%>, see: From a39616cf083563b00b09c667502f9e50d5eb4234 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 15 Oct 2018 10:08:16 -0700 Subject: [PATCH 33/56] add another loop --- .ci/ci.yml.tmpl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.ci/ci.yml.tmpl b/.ci/ci.yml.tmpl index 27881225eef3..c614d402c350 100644 --- a/.ci/ci.yml.tmpl +++ b/.ci/ci.yml.tmpl @@ -239,14 +239,15 @@ jobs: submodules: [build/terraform, build/terraform-beta] passed: [mm-generate] - aggregate: - - task: test-terraform - file: magic-modules/.ci/unit-tests/task.yml - timeout: 30m - - task: test-terraform-beta + {% for version in vars.terraform_versions %} + - task: test-terraform{{ '-'+version if version else '' }} file: magic-modules/.ci/unit-tests/task.yml timeout: 30m + {% if version %} params: - VERSION: beta + VERSION: {{ version }} + {% endif %} + {% endfor %} on_failure: do: - get: magic-modules-new-prs From 678361d7af661f2da4359a6d16273fd8553c4304 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 15 Oct 2018 11:05:39 -0700 Subject: [PATCH 34/56] update submodule to track 2.0.0 branch --- build/terraform-beta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/terraform-beta b/build/terraform-beta index 8c0bc9377140..2b5eb99d6b42 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 8c0bc9377140b8dfd4550102e063ea6b83e54a8f +Subproject commit 2b5eb99d6b42129a7f21478ce49aa668db5ebffa From 8a7321f466ca73cfdac8d90ee6b15de7c0fed81a Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 17 Oct 2018 11:30:31 -0700 Subject: [PATCH 35/56] convert files that need to be different between versions to erb --- products/compute/terraform.yaml | 1 + provider/core.rb | 6 +- provider/terraform/common~compile.yaml | 24 ++ provider/terraform/custom_code.rb | 6 +- ...e_google_container_engine_versions.go.erb} | 3 + ...> resource_compute_backend_service.go.erb} | 3 + ...rce_compute_global_forwarding_rule.go.erb} | 3 + ...rce_compute_instance_group_manager.go.erb} | 217 ++++++++++++- ...pute_region_instance_group_manager.go.erb} | 177 ++++++++++- ...r.go => resource_container_cluster.go.erb} | 108 ++++++- ...go => resource_container_node_pool.go.erb} | 5 + ...google_compute_instance_group_test.go.erb} | 8 + ...> resource_compute_autoscaler_test.go.erb} | 8 + ...ource_compute_backend_service_test.go.erb} | 29 ++ ...ompute_instance_group_manager_test.go.erb} | 290 +++++++++++++++++- ...rce_compute_region_autoscaler_test.go.erb} | 15 + ...region_instance_group_manager_test.go.erb} | 187 ++++++++++- ...twork.go => iam_compute_subnetwork.go.erb} | 13 +- .../{node_config.go => node_config.go.erb} | 5 + .../examples/autoscaler_basic.tf.erb | 7 + .../examples/region_autoscaler_basic.tf.erb | 7 + 21 files changed, 1095 insertions(+), 27 deletions(-) rename provider/terraform/data_sources/{data_source_google_container_engine_versions.go => data_source_google_container_engine_versions.go.erb} (96%) rename provider/terraform/resources/{resource_compute_backend_service.go => resource_compute_backend_service.go.erb} (99%) rename provider/terraform/resources/{resource_compute_global_forwarding_rule.go => resource_compute_global_forwarding_rule.go.erb} (99%) rename provider/terraform/resources/{resource_compute_instance_group_manager.go => resource_compute_instance_group_manager.go.erb} (80%) rename provider/terraform/resources/{resource_compute_region_instance_group_manager.go => resource_compute_region_instance_group_manager.go.erb} (81%) rename provider/terraform/resources/{resource_container_cluster.go => resource_container_cluster.go.erb} (94%) rename provider/terraform/resources/{resource_container_node_pool.go => resource_container_node_pool.go.erb} (99%) rename provider/terraform/tests/{data_source_google_compute_instance_group_test.go => data_source_google_compute_instance_group_test.go.erb} (97%) rename provider/terraform/tests/{resource_compute_autoscaler_test.go => resource_compute_autoscaler_test.go.erb} (97%) rename provider/terraform/tests/{resource_compute_backend_service_test.go => resource_compute_backend_service_test.go.erb} (97%) rename provider/terraform/tests/{resource_compute_instance_group_manager_test.go => resource_compute_instance_group_manager_test.go.erb} (77%) rename provider/terraform/tests/{resource_compute_region_autoscaler_test.go => resource_compute_region_autoscaler_test.go.erb} (95%) rename provider/terraform/tests/{resource_compute_region_instance_group_manager_test.go => resource_compute_region_instance_group_manager_test.go.erb} (83%) rename provider/terraform/utils/{iam_compute_subnetwork.go => iam_compute_subnetwork.go.erb} (92%) rename provider/terraform/utils/{node_config.go => node_config.go.erb} (98%) diff --git a/products/compute/terraform.yaml b/products/compute/terraform.yaml index ef7eba64e461..f6bf3923dde0 100644 --- a/products/compute/terraform.yaml +++ b/products/compute/terraform.yaml @@ -69,6 +69,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "autoscaler_basic" primary_resource_id: "foobar" + version: <%= _version_name %> vars: autoscaler_name: "my-autoscaler" instance_template_name: "my-instance-template" diff --git a/provider/core.rb b/provider/core.rb index e75a90be32d2..b392ec305f93 100644 --- a/provider/core.rb +++ b/provider/core.rb @@ -77,7 +77,7 @@ def generate(output_folder, types, version_name) # Compilation has to be the last step, as some files (e.g. # CONTRIBUTING.md) may depend on the list of all files previously copied # or compiled. - compile_files(output_folder) \ + compile_files(output_folder, version_name) \ unless @config.files.nil? || @config.files.compile.nil? generate_datasources(output_folder, types, version_name) \ @@ -101,8 +101,8 @@ def copy_files(output_folder) end end - def compile_files(output_folder) - compile_file_list(output_folder, @config.files.compile) + def compile_files(output_folder, version_name) + compile_file_list(output_folder, @config.files.compile, {version: version_name}) end def compile_examples(output_folder) diff --git a/provider/terraform/common~compile.yaml b/provider/terraform/common~compile.yaml index 63085cbb98a7..56dd7748503f 100644 --- a/provider/terraform/common~compile.yaml +++ b/provider/terraform/common~compile.yaml @@ -14,3 +14,27 @@ # the final module tree structure: <% dir = _version_name == 'beta' ? 'google-beta' : 'google' -%> '<%= dir -%>/provider_<%= api.prefix[1..-1] -%>_gen.go': 'templates/terraform/provider_gen.erb' + +<% Dir["provider/terraform/tests/*.go.erb"].each do |file_path| + fname = file_path.split('/')[-1] +-%> +'<%= dir -%>/<%= fname.split(".erb")[0] -%>': 'provider/terraform/tests/<%= fname -%>' +<% end -%> +<% + Dir["provider/terraform/resources/*.go.erb"].each do |file_path| + fname = file_path.split('/')[-1] +-%> +'<%= dir -%>/<%= fname.split(".erb")[0] -%>': 'provider/terraform/resources/<%= fname -%>' +<% end -%> +<% + Dir["provider/terraform/data_sources/*.go.erb"].each do |file_path| + fname = file_path.split('/')[-1] +-%> +'<%= dir -%>/<%= fname.split(".erb")[0] -%>': 'provider/terraform/data_sources/<%= fname -%>' +<% end -%> +<% + Dir["provider/terraform/utils/*.go.erb"].each do |file_path| + fname = file_path.split('/')[-1] +-%> +'<%= dir -%>/<%= fname.split(".erb")[0] -%>': 'provider/terraform/utils/<%= fname -%>' +<% end -%> diff --git a/provider/terraform/custom_code.rb b/provider/terraform/custom_code.rb index 3a77e7ae8342..7d0cea0e3e16 100644 --- a/provider/terraform/custom_code.rb +++ b/provider/terraform/custom_code.rb @@ -68,6 +68,9 @@ class Examples < Api::Object # vars is a Hash from template variable names to output variable names attr_reader :vars + # the version (ga, beta, etc.) this example is being generated at + attr_reader :version + # Extra properties to ignore read on during import. # These properties will likely be custom code. attr_reader :ignore_read_extra @@ -94,7 +97,8 @@ def config_test body = lines(compile_file( { vars: vars.map { |k, str| [k, "#{str}-%s"] }.to_h, - primary_resource_id: primary_resource_id + primary_resource_id: primary_resource_id, + version: version }, "templates/terraform/examples/#{name}.tf.erb" )) diff --git a/provider/terraform/data_sources/data_source_google_container_engine_versions.go b/provider/terraform/data_sources/data_source_google_container_engine_versions.go.erb similarity index 96% rename from provider/terraform/data_sources/data_source_google_container_engine_versions.go rename to provider/terraform/data_sources/data_source_google_container_engine_versions.go.erb index 5e6e420195fd..14063b548091 100644 --- a/provider/terraform/data_sources/data_source_google_container_engine_versions.go +++ b/provider/terraform/data_sources/data_source_google_container_engine_versions.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -20,7 +21,9 @@ func dataSourceGoogleContainerEngineVersions() *schema.Resource { Optional: true, }, "region": { +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeString, Optional: true, ConflictsWith: []string{"zone"}, diff --git a/provider/terraform/resources/resource_compute_backend_service.go b/provider/terraform/resources/resource_compute_backend_service.go.erb similarity index 99% rename from provider/terraform/resources/resource_compute_backend_service.go rename to provider/terraform/resources/resource_compute_backend_service.go.erb index fd747ed57e58..864f1a1f75a5 100644 --- a/provider/terraform/resources/resource_compute_backend_service.go +++ b/provider/terraform/resources/resource_compute_backend_service.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -159,7 +160,9 @@ func resourceComputeBackendService() *schema.Resource { }, "custom_request_headers": &schema.Schema{ +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, diff --git a/provider/terraform/resources/resource_compute_global_forwarding_rule.go b/provider/terraform/resources/resource_compute_global_forwarding_rule.go.erb similarity index 99% rename from provider/terraform/resources/resource_compute_global_forwarding_rule.go rename to provider/terraform/resources/resource_compute_global_forwarding_rule.go.erb index a775861d2484..b842067dfddd 100644 --- a/provider/terraform/resources/resource_compute_global_forwarding_rule.go +++ b/provider/terraform/resources/resource_compute_global_forwarding_rule.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -56,7 +57,9 @@ func resourceComputeGlobalForwardingRule() *schema.Resource { }, "labels": &schema.Schema{ +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, diff --git a/provider/terraform/resources/resource_compute_instance_group_manager.go b/provider/terraform/resources/resource_compute_instance_group_manager.go.erb similarity index 80% rename from provider/terraform/resources/resource_compute_instance_group_manager.go rename to provider/terraform/resources/resource_compute_instance_group_manager.go.erb index 584316b56d9c..e52fae538a57 100644 --- a/provider/terraform/resources/resource_compute_instance_group_manager.go +++ b/provider/terraform/resources/resource_compute_instance_group_manager.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -37,17 +38,25 @@ func resourceComputeInstanceGroupManager() *schema.Resource { ForceNew: true, }, +<% if version.nil? || version == 'ga' -%> "instance_template": &schema.Schema{ Type: schema.TypeString, Optional: true, DiffSuppressFunc: compareSelfLinkRelativePaths, }, +<% end -%> "version": &schema.Schema{ +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeList, +<% if version.nil? || version == 'ga' -%> Optional: true, Computed: true, +<% else -%> + Required: true, +<% end -%> Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": &schema.Schema{ @@ -114,7 +123,11 @@ func resourceComputeInstanceGroupManager() *schema.Resource { }, "named_port": &schema.Schema{ +<% if version.nil? || version == 'ga' -%> Type: schema.TypeList, +<% else -%> + Type: schema.TypeSet, +<% end -%> Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -143,6 +156,7 @@ func resourceComputeInstanceGroupManager() *schema.Resource { Computed: true, }, +<% if version.nil? || version == 'ga' -%> "update_strategy": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -158,6 +172,7 @@ func resourceComputeInstanceGroupManager() *schema.Resource { return false }, }, +<% end -%> "target_pools": &schema.Schema{ Type: schema.TypeSet, @@ -175,7 +190,9 @@ func resourceComputeInstanceGroupManager() *schema.Resource { }, "auto_healing_policies": &schema.Schema{ +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeList, Optional: true, MaxItems: 1, @@ -196,8 +213,12 @@ func resourceComputeInstanceGroupManager() *schema.Resource { }, }, +<% if version.nil? || version == 'ga' -%> "rolling_update_policy": &schema.Schema{ Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% else -%> + "update_policy": &schema.Schema{ +<% end -%> Type: schema.TypeList, Optional: true, MaxItems: 1, @@ -218,28 +239,46 @@ func resourceComputeInstanceGroupManager() *schema.Resource { "max_surge_fixed": &schema.Schema{ Type: schema.TypeInt, Optional: true, +<% if version.nil? || version == 'ga' -%> Default: 1, ConflictsWith: []string{"rolling_update_policy.0.max_surge_percent"}, +<% else -%> + Computed: true, + ConflictsWith: []string{"update_policy.0.max_surge_percent"}, +<% end -%> }, "max_surge_percent": &schema.Schema{ Type: schema.TypeInt, Optional: true, +<% if version.nil? || version == 'ga' -%> ConflictsWith: []string{"rolling_update_policy.0.max_surge_fixed"}, +<% else -%> + ConflictsWith: []string{"update_policy.0.max_surge_fixed"}, +<% end -%> ValidateFunc: validation.IntBetween(0, 100), }, "max_unavailable_fixed": &schema.Schema{ Type: schema.TypeInt, Optional: true, +<% if version.nil? || version == 'ga' -%> Default: 1, ConflictsWith: []string{"rolling_update_policy.0.max_unavailable_percent"}, +<% else -%> + Computed: true, + ConflictsWith: []string{"update_policy.0.max_unavailable_percent"}, +<% end -%> }, "max_unavailable_percent": &schema.Schema{ Type: schema.TypeInt, Optional: true, +<% if version.nil? || version == 'ga' -%> ConflictsWith: []string{"rolling_update_policy.0.max_unavailable_fixed"}, +<% else -%> + ConflictsWith: []string{"update_policy.0.max_unavailable_fixed"}, +<% end -%> ValidateFunc: validation.IntBetween(0, 100), }, @@ -300,21 +339,32 @@ func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta inte return err } +<% if version.nil? || version == 'ga' -%> if _, ok := d.GetOk("rolling_update_policy"); d.Get("update_strategy") == "ROLLING_UPDATE" && !ok { return fmt.Errorf("[rolling_update_policy] must be set when 'update_strategy' is set to 'ROLLING_UPDATE'") } +<% end -%> // Build the parameter manager := &computeBeta.InstanceGroupManager{ Name: d.Get("name").(string), Description: d.Get("description").(string), BaseInstanceName: d.Get("base_instance_name").(string), +<% if version.nil? || version == 'ga' -%> InstanceTemplate: d.Get("instance_template").(string), +<% end -%> TargetSize: int64(d.Get("target_size").(int)), +<% if version.nil? || version == 'ga' -%> NamedPorts: getNamedPortsBeta(d.Get("named_port").([]interface{})), +<% else -%> + NamedPorts: getNamedPortsBeta(d.Get("named_port").(*schema.Set).List()), +<% end -%> TargetPools: convertStringSet(d.Get("target_pools").(*schema.Set)), AutoHealingPolicies: expandAutoHealingPolicies(d.Get("auto_healing_policies").([]interface{})), Versions: expandVersions(d.Get("version").([]interface{})), +<% unless version.nil? || version == 'ga' -%> + UpdatePolicy: expandUpdatePolicy(d.Get("update_policy").([]interface{})), +<% end -%> // Force send TargetSize to allow a value of 0. ForceSendFields: []string{"TargetSize"}, } @@ -438,12 +488,19 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf } manager, err := getManager(d, meta) - if err != nil || manager == nil { + if err != nil { return err } + if manager == nil { + log.Printf("[WARN] Instance Group Manager %q not found, removing from state.", d.Id()) + d.SetId("") + return nil + } d.Set("base_instance_name", manager.BaseInstanceName) +<% if version.nil? || version == 'ga' -%> d.Set("instance_template", ConvertSelfLinkToV1(manager.InstanceTemplate)) +<% end -%> if err := d.Set("version", flattenVersions(manager.Versions)); err != nil { return err } @@ -452,17 +509,30 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf d.Set("description", manager.Description) d.Set("project", project) d.Set("target_size", manager.TargetSize) - d.Set("target_pools", manager.TargetPools) - d.Set("named_port", flattenNamedPortsBeta(manager.NamedPorts)) + if err = d.Set("target_pools", manager.TargetPools); err != nil { + return fmt.Errorf("Error setting target_pools in state: %s", err.Error()) + } + if err = d.Set("named_port", flattenNamedPortsBeta(manager.NamedPorts)); err != nil { + return fmt.Errorf("Error setting named_port in state: %s", err.Error()) + } d.Set("fingerprint", manager.Fingerprint) d.Set("instance_group", ConvertSelfLinkToV1(manager.InstanceGroup)) d.Set("self_link", ConvertSelfLinkToV1(manager.SelfLink)) + +<% if version.nil? || version == 'ga' -%> update_strategy, ok := d.GetOk("update_strategy") if !ok { update_strategy = "REPLACE" } d.Set("update_strategy", update_strategy.(string)) - d.Set("auto_healing_policies", flattenAutoHealingPolicies(manager.AutoHealingPolicies)) +<% else -%> + if err = d.Set("update_policy", flattenUpdatePolicy(manager.UpdatePolicy)); err != nil { + return fmt.Errorf("Error setting update_policy in state: %s", err.Error()) + } +<% end -%> + if err = d.Set("auto_healing_policies", flattenAutoHealingPolicies(manager.AutoHealingPolicies)); err != nil { + return fmt.Errorf("Error setting auto_healing_policies in state: %s", err.Error()) + } if d.Get("wait_for_instances").(bool) { conf := resource.StateChangeConf{ @@ -480,6 +550,8 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf return nil } + +<% if version.nil? || version == 'ga' -%> // Updates an instance group manager by applying the update strategy (REPLACE, RESTART) // and rolling update policy (PROACTIVE, OPPORTUNISTIC). Updates performed by API // are OPPORTUNISTIC by default. @@ -698,6 +770,103 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte return resourceComputeInstanceGroupManagerRead(d, meta) } +<% else -%> +func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + zone, err := getZone(d, config) + if err != nil { + return err + } + + updatedManager := &computeBeta.InstanceGroupManager{ + Fingerprint: d.Get("fingerprint").(string), + } + var change bool + + if d.HasChange("target_pools") { + updatedManager.TargetPools = convertStringSet(d.Get("target_pools").(*schema.Set)) + change = true + } + + if d.HasChange("auto_healing_policies") { + updatedManager.AutoHealingPolicies = expandAutoHealingPolicies(d.Get("auto_healing_policies").([]interface{})) + updatedManager.ForceSendFields = append(updatedManager.ForceSendFields, "AutoHealingPolicies") + change = true + } + + if d.HasChange("version") { + updatedManager.Versions = expandVersions(d.Get("version").([]interface{})) + change = true + } + + if d.HasChange("update_policy") { + updatedManager.UpdatePolicy = expandUpdatePolicy(d.Get("update_policy").([]interface{})) + change = true + } + + if change { + op, err := config.clientComputeBeta.InstanceGroupManagers.Patch(project, zone, d.Get("name").(string), updatedManager).Do() + if err != nil { + return fmt.Errorf("Error updating managed group instances: %s", err) + } + + err = computeSharedOperationWait(config.clientCompute, op, project, "Updating managed group instances") + if err != nil { + return err + } + } + + // named ports can't be updated through PATCH + // so we call the update method on the instance group, instead of the igm + if d.HasChange("named_port") { + + // Build the parameters for a "SetNamedPorts" request: + namedPorts := getNamedPortsBeta(d.Get("named_port").(*schema.Set).List()) + setNamedPorts := &computeBeta.InstanceGroupsSetNamedPortsRequest{ + NamedPorts: namedPorts, + } + + // Make the request: + op, err := config.clientComputeBeta.InstanceGroups.SetNamedPorts( + project, zone, d.Get("name").(string), setNamedPorts).Do() + + if err != nil { + return fmt.Errorf("Error updating InstanceGroupManager: %s", err) + } + + // Wait for the operation to complete: + err = computeSharedOperationWait(config.clientCompute, op, project, "Updating InstanceGroupManager") + if err != nil { + return err + } + } + + // target_size should be updated through resize + if d.HasChange("target_size") { + targetSize := int64(d.Get("target_size").(int)) + op, err := config.clientComputeBeta.InstanceGroupManagers.Resize( + project, zone, d.Get("name").(string), targetSize).Do() + + if err != nil { + return fmt.Errorf("Error updating InstanceGroupManager: %s", err) + } + + // Wait for the operation to complete + err = computeSharedOperationWait(config.clientCompute, op, project, "Updating InstanceGroupManager") + if err != nil { + return err + } + } + + return resourceComputeInstanceGroupManagerRead(d, meta) +} +<% end -%> func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) @@ -823,24 +992,36 @@ func expandUpdatePolicy(configured []interface{}) *computeBeta.InstanceGroupMana if v := data["max_surge_percent"]; v.(int) > 0 { updatePolicy.MaxSurge = &computeBeta.FixedOrPercent{ Percent: int64(v.(int)), +<% unless version.nil? || version == 'ga' -%> + NullFields: []string{"Fixed"}, +<% end -%> } } else { updatePolicy.MaxSurge = &computeBeta.FixedOrPercent{ Fixed: int64(data["max_surge_fixed"].(int)), // allow setting this value to 0 ForceSendFields: []string{"Fixed"}, +<% unless version.nil? || version == 'ga' -%> + NullFields: []string{"Percent"}, +<% end -%> } } if v := data["max_unavailable_percent"]; v.(int) > 0 { updatePolicy.MaxUnavailable = &computeBeta.FixedOrPercent{ Percent: int64(v.(int)), +<% unless version.nil? || version == 'ga' -%> + NullFields: []string{"Fixed"}, +<% end -%> } } else { updatePolicy.MaxUnavailable = &computeBeta.FixedOrPercent{ Fixed: int64(data["max_unavailable_fixed"].(int)), // allow setting this value to 0 ForceSendFields: []string{"Fixed"}, +<% unless version.nil? || version == 'ga' -%> + NullFields: []string{"Percent"}, +<% end -%> } } @@ -864,6 +1045,34 @@ func flattenAutoHealingPolicies(autoHealingPolicies []*computeBeta.InstanceGroup return autoHealingPoliciesSchema } +<% unless version.nil? || version == 'ga' -%> +func flattenUpdatePolicy(updatePolicy *computeBeta.InstanceGroupManagerUpdatePolicy) []map[string]interface{} { + results := []map[string]interface{}{} + if updatePolicy != nil { + up := map[string]interface{}{} + if updatePolicy.MaxSurge != nil { + up["max_surge_fixed"] = updatePolicy.MaxSurge.Fixed + up["max_surge_percent"] = updatePolicy.MaxSurge.Percent + } else { + up["max_surge_fixed"] = 0 + up["max_surge_percent"] = 0 + } + if updatePolicy.MaxUnavailable != nil { + up["max_unavailable_fixed"] = updatePolicy.MaxUnavailable.Fixed + up["max_unavailable_percent"] = updatePolicy.MaxUnavailable.Percent + } else { + up["max_unavailable_fixed"] = 0 + up["max_unavailable_percent"] = 0 + } + up["min_ready_sec"] = updatePolicy.MinReadySec + up["minimal_action"] = updatePolicy.MinimalAction + up["type"] = updatePolicy.Type + results = append(results, up) + } + return results +} +<% end -%> + func resourceInstanceGroupManagerStateImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { d.Set("wait_for_instances", false) zonalID, err := parseInstanceGroupManagerId(d.Id()) diff --git a/provider/terraform/resources/resource_compute_region_instance_group_manager.go b/provider/terraform/resources/resource_compute_region_instance_group_manager.go.erb similarity index 81% rename from provider/terraform/resources/resource_compute_region_instance_group_manager.go rename to provider/terraform/resources/resource_compute_region_instance_group_manager.go.erb index b11f7dfc29cf..4dc0ca1693da 100644 --- a/provider/terraform/resources/resource_compute_region_instance_group_manager.go +++ b/provider/terraform/resources/resource_compute_region_instance_group_manager.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -42,17 +43,23 @@ func resourceComputeRegionInstanceGroupManager() *schema.Resource { ForceNew: true, }, +<% if version.nil? || version == 'ga' -%> "instance_template": &schema.Schema{ Type: schema.TypeString, Optional: true, DiffSuppressFunc: compareSelfLinkRelativePaths, }, +<% end -%> "version": &schema.Schema{ Type: schema.TypeList, +<% if version.nil? || version == 'ga' -%> Optional: true, Computed: true, Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% else -%> + Required: true, +<% end -%> Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": &schema.Schema{ @@ -118,7 +125,11 @@ func resourceComputeRegionInstanceGroupManager() *schema.Resource { }, "named_port": &schema.Schema{ +<% if version.nil? || version == 'ga' -%> Type: schema.TypeList, +<% else -%> + Type: schema.TypeSet, +<% end -%> Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -147,12 +158,14 @@ func resourceComputeRegionInstanceGroupManager() *schema.Resource { Computed: true, }, +<% if version.nil? || version == 'ga' -%> "update_strategy": &schema.Schema{ Type: schema.TypeString, Optional: true, Default: "NONE", ValidateFunc: validation.StringInSlice([]string{"NONE", "ROLLING_UPDATE"}, false), }, +<% end -%> "target_pools": &schema.Schema{ Type: schema.TypeSet, @@ -181,7 +194,9 @@ func resourceComputeRegionInstanceGroupManager() *schema.Resource { Type: schema.TypeList, Optional: true, MaxItems: 1, +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "health_check": &schema.Schema{ @@ -211,11 +226,16 @@ func resourceComputeRegionInstanceGroupManager() *schema.Resource { }, }, +<% if version.nil? || version == 'ga' -%> "rolling_update_policy": &schema.Schema{ + Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% else -%> + "update_policy": &schema.Schema{ + Computed: true, +<% end -%> Type: schema.TypeList, Optional: true, MaxItems: 1, - Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "minimal_action": &schema.Schema{ @@ -233,28 +253,46 @@ func resourceComputeRegionInstanceGroupManager() *schema.Resource { "max_surge_fixed": &schema.Schema{ Type: schema.TypeInt, Optional: true, - Default: 0, +<% if version.nil? || version == 'ga' -%> + Default: 1, ConflictsWith: []string{"rolling_update_policy.0.max_surge_percent"}, +<% else -%> + Computed: true, + ConflictsWith: []string{"update_policy.0.max_surge_percent"}, +<% end -%> }, "max_surge_percent": &schema.Schema{ Type: schema.TypeInt, Optional: true, +<% if version.nil? || version == 'ga' -%> ConflictsWith: []string{"rolling_update_policy.0.max_surge_fixed"}, +<% else -%> + ConflictsWith: []string{"update_policy.0.max_surge_fixed"}, +<% end -%> ValidateFunc: validation.IntBetween(0, 100), }, "max_unavailable_fixed": &schema.Schema{ Type: schema.TypeInt, Optional: true, - Default: 0, +<% if version.nil? || version == 'ga' -%> + Default: 1, ConflictsWith: []string{"rolling_update_policy.0.max_unavailable_percent"}, +<% else -%> + Computed: true, + ConflictsWith: []string{"update_policy.0.max_unavailable_percent"}, +<% end -%> }, "max_unavailable_percent": &schema.Schema{ Type: schema.TypeInt, Optional: true, +<% if version.nil? || version == 'ga' -%> ConflictsWith: []string{"rolling_update_policy.0.max_unavailable_fixed"}, +<% else -%> + ConflictsWith: []string{"update_policy.0.max_unavailable_fixed"}, +<% end -%> ValidateFunc: validation.IntBetween(0, 100), }, @@ -283,20 +321,31 @@ func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, met return err } +<% if version.nil? || version == 'ga' -%> if _, ok := d.GetOk("rolling_update_policy"); d.Get("update_strategy") == "ROLLING_UPDATE" && !ok { return fmt.Errorf("[rolling_update_policy] must be set when 'update_strategy' is set to 'ROLLING_UPDATE'") } +<% end -%> manager := &computeBeta.InstanceGroupManager{ Name: d.Get("name").(string), Description: d.Get("description").(string), BaseInstanceName: d.Get("base_instance_name").(string), +<% if version.nil? || version == 'ga' -%> InstanceTemplate: d.Get("instance_template").(string), +<% end -%> TargetSize: int64(d.Get("target_size").(int)), +<% if version.nil? || version == 'ga' -%> NamedPorts: getNamedPortsBeta(d.Get("named_port").([]interface{})), +<% else -%> + NamedPorts: getNamedPortsBeta(d.Get("named_port").(*schema.Set).List()), +<% end -%> TargetPools: convertStringSet(d.Get("target_pools").(*schema.Set)), AutoHealingPolicies: expandAutoHealingPolicies(d.Get("auto_healing_policies").([]interface{})), Versions: expandVersions(d.Get("version").([]interface{})), +<% unless version.nil? || version == 'ga' -%> + UpdatePolicy: expandUpdatePolicy(d.Get("update_policy").([]interface{})), +<% end -%> DistributionPolicy: expandDistributionPolicy(d.Get("distribution_policy_zones").(*schema.Set)), // Force send TargetSize to allow size of 0. ForceSendFields: []string{"TargetSize"}, @@ -368,9 +417,14 @@ func waitForInstancesRefreshFunc(f getInstanceManagerFunc, d *schema.ResourceDat func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) manager, err := getRegionalManager(d, meta) - if err != nil || manager == nil { + if err != nil { return err } + if manager == nil { + log.Printf("[WARN] Region Instance Group Manager %q not found, removing from state.", d.Id()) + d.SetId("") + return nil + } regionalID, err := parseRegionInstanceGroupManagerId(d.Id()) if err != nil { @@ -384,7 +438,9 @@ func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta } d.Set("base_instance_name", manager.BaseInstanceName) - d.Set("instance_template", manager.InstanceTemplate) +<% if version.nil? || version == 'ga' -%> + d.Set("instance_template", ConvertSelfLinkToV1(manager.InstanceTemplate)) +<% end -%> if err := d.Set("version", flattenVersions(manager.Versions)); err != nil { return err } @@ -393,20 +449,32 @@ func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta d.Set("description", manager.Description) d.Set("project", regionalID.Project) d.Set("target_size", manager.TargetSize) - d.Set("target_pools", manager.TargetPools) - d.Set("named_port", flattenNamedPortsBeta(manager.NamedPorts)) + if err := d.Set("target_pools", manager.TargetPools); err != nil { + return fmt.Errorf("Error setting target_pools in state: %s", err.Error()) + } + if err := d.Set("named_port", flattenNamedPortsBeta(manager.NamedPorts)); err != nil { + return fmt.Errorf("Error setting named_port in state: %s", err.Error()) + } d.Set("fingerprint", manager.Fingerprint) d.Set("instance_group", ConvertSelfLinkToV1(manager.InstanceGroup)) - d.Set("auto_healing_policies", flattenAutoHealingPolicies(manager.AutoHealingPolicies)) + if err := d.Set("auto_healing_policies", flattenAutoHealingPolicies(manager.AutoHealingPolicies)); err != nil { + return fmt.Errorf("Error setting auto_healing_policies in state: %s", err.Error()) + } if err := d.Set("distribution_policy_zones", flattenDistributionPolicy(manager.DistributionPolicy)); err != nil { return err } d.Set("self_link", ConvertSelfLinkToV1(manager.SelfLink)) +<% if version.nil? || version == 'ga' -%> update_strategy, ok := d.GetOk("update_strategy") if !ok { update_strategy = "NONE" } d.Set("update_strategy", update_strategy.(string)) +<% else -%> + if err := d.Set("update_policy", flattenUpdatePolicy(manager.UpdatePolicy)); err != nil { + return fmt.Errorf("Error setting update_policy in state: %s", err.Error()) + } +<% end -%> if d.Get("wait_for_instances").(bool) { conf := resource.StateChangeConf{ @@ -424,6 +492,7 @@ func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta return nil } +<% if version.nil? || version == 'ga' -%> // Updates an instance group manager by applying the update strategy (REPLACE, RESTART) // and rolling update policy (PROACTIVE, OPPORTUNISTIC). Updates performed by API // are OPPORTUNISTIC by default. @@ -631,6 +700,98 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met return resourceComputeRegionInstanceGroupManagerRead(d, meta) } +<% else -%> +func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + region, err := getRegion(d, config) + if err != nil { + return err + } + + updatedManager := &computeBeta.InstanceGroupManager{ + Fingerprint: d.Get("fingerprint").(string), + } + var change bool + + if d.HasChange("target_pools") { + updatedManager.TargetPools = convertStringSet(d.Get("target_pools").(*schema.Set)) + change = true + } + + if d.HasChange("auto_healing_policies") { + updatedManager.AutoHealingPolicies = expandAutoHealingPolicies(d.Get("auto_healing_policies").([]interface{})) + updatedManager.ForceSendFields = append(updatedManager.ForceSendFields, "AutoHealingPolicies") + change = true + } + + if d.HasChange("version") { + updatedManager.Versions = expandVersions(d.Get("version").([]interface{})) + change = true + } + + if d.HasChange("update_policy") { + updatedManager.UpdatePolicy = expandUpdatePolicy(d.Get("update_policy").([]interface{})) + change = true + } + + if change { + op, err := config.clientComputeBeta.RegionInstanceGroupManagers.Patch(project, region, d.Get("name").(string), updatedManager).Do() + if err != nil { + return fmt.Errorf("Error updating region managed group instances: %s", err) + } + + err = computeSharedOperationWait(config.clientCompute, op, project, "Updating region managed group instances") + if err != nil { + return err + } + } + + // named ports can't be updated through PATCH + // so we call the update method on the region instance group, instead of the rigm + if d.HasChange("named_port") { + namedPorts := getNamedPortsBeta(d.Get("named_port").(*schema.Set).List()) + setNamedPorts := &computeBeta.RegionInstanceGroupsSetNamedPortsRequest{ + NamedPorts: namedPorts, + } + + op, err := config.clientComputeBeta.RegionInstanceGroups.SetNamedPorts( + project, region, d.Get("name").(string), setNamedPorts).Do() + + if err != nil { + return fmt.Errorf("Error updating RegionInstanceGroupManager: %s", err) + } + + err = computeSharedOperationWait(config.clientCompute, op, project, "Updating RegionInstanceGroupManager") + if err != nil { + return err + } + } + + // target size should use resize + if d.HasChange("target_size") { + targetSize := int64(d.Get("target_size").(int)) + op, err := config.clientComputeBeta.RegionInstanceGroupManagers.Resize( + project, region, d.Get("name").(string), targetSize).Do() + + if err != nil { + return fmt.Errorf("Error resizing RegionInstanceGroupManager: %s", err) + } + + err = computeSharedOperationWait(config.clientCompute, op, project, "Resizing RegionInstanceGroupManager") + if err != nil { + return err + } + } + + return resourceComputeRegionInstanceGroupManagerRead(d, meta) +} +<% end -%> func resourceComputeRegionInstanceGroupManagerDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) diff --git a/provider/terraform/resources/resource_container_cluster.go b/provider/terraform/resources/resource_container_cluster.go.erb similarity index 94% rename from provider/terraform/resources/resource_container_cluster.go rename to provider/terraform/resources/resource_container_cluster.go.erb index e686f9e656d0..81f1281d9fb8 100644 --- a/provider/terraform/resources/resource_container_cluster.go +++ b/provider/terraform/resources/resource_container_cluster.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -97,7 +98,9 @@ func resourceContainerCluster() *schema.Resource { }, "region": { +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeString, Optional: true, Computed: true, @@ -202,7 +205,9 @@ func resourceContainerCluster() *schema.Resource { }, "enable_binary_authorization": { +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeBool, Optional: true, Default: false, @@ -216,7 +221,9 @@ func resourceContainerCluster() *schema.Resource { }, "enable_tpu": { +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeBool, Optional: true, ForceNew: true, @@ -395,7 +402,9 @@ func resourceContainerCluster() *schema.Resource { }, "pod_security_policy_config": { +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeList, Optional: true, MaxItems: 1, @@ -505,15 +514,65 @@ func resourceContainerCluster() *schema.Resource { }, "private_cluster": { +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", + Default: false, +<% else -%> + Deprecated: "Use private_cluster_config.enable_private_nodes instead.", + ConflictsWith: []string{"private_cluster_config"}, + Computed: true, +<% end -%> Type: schema.TypeBool, Optional: true, ForceNew: true, - Default: false, }, +<% unless version.nil? || version == 'ga' -%> + "private_cluster_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Computed: true, + ConflictsWith: []string{"private_cluster", "master_ipv4_cidr_block"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable_private_endpoint": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + "enable_private_nodes": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + "master_ipv4_cidr_block": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.CIDRNetwork(28, 28), + }, + "private_endpoint": { + Type: schema.TypeString, + Computed: true, + }, + "public_endpoint": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, +<% end -%> + "master_ipv4_cidr_block": { - Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% if version.nil? || version == 'ga' -%> + Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% else -%> + Deprecated: "Use private_cluster_config.master_ipv4_cidr_block instead.", + ConflictsWith: []string{"private_cluster_config"}, + Computed: true, +<% end -%> Type: schema.TypeString, Optional: true, ForceNew: true, @@ -651,6 +710,12 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er } } +<% unless version.nil? || version == 'ga' -%> + if v, ok := d.GetOk("private_cluster_config"); ok { + cluster.PrivateClusterConfig = expandPrivateClusterConfig(v) + } +<% end -%> + req := &containerBeta.CreateClusterRequest{ Cluster: cluster, } @@ -773,6 +838,12 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro return err } +<% unless version.nil? || version == 'ga' -%> + if err := d.Set("private_cluster_config", flattenPrivateClusterConfig(cluster.PrivateClusterConfig)); err != nil { + return err + } +<% end -%> + igUrls, err := getInstanceGroupUrlsFromManagerUrls(config, cluster.InstanceGroupUrls) if err != nil { return err @@ -1509,6 +1580,22 @@ func expandNetworkPolicy(configured interface{}) *containerBeta.NetworkPolicy { return result } +<% unless version.nil? || version == 'ga' -%> +func expandPrivateClusterConfig(configured interface{}) *containerBeta.PrivateClusterConfig { + l := configured.([]interface{}) + if len(l) == 0 { + return nil + } + config := l[0].(map[string]interface{}) + return &containerBeta.PrivateClusterConfig{ + EnablePrivateEndpoint: config["enable_private_endpoint"].(bool), + EnablePrivateNodes: config["enable_private_nodes"].(bool), + MasterIpv4CidrBlock: config["master_ipv4_cidr_block"].(string), + ForceSendFields: []string{"EnablePrivateEndpoint", "EnablePrivateNodes", "MasterIpv4CidrBlock"}, + } +} +<% end -%> + func expandPodSecurityPolicyConfig(configured interface{}) *containerBeta.PodSecurityPolicyConfig { l := configured.([]interface{}) if len(l) == 0 || l[0] == nil { @@ -1590,6 +1677,23 @@ func flattenClusterNodePools(d *schema.ResourceData, config *Config, c []*contai return nodePools, nil } +<% unless version.nil? || version == 'ga' -%> +func flattenPrivateClusterConfig(c *containerBeta.PrivateClusterConfig) []map[string]interface{} { + if c == nil { + return nil + } + return []map[string]interface{}{ + { + "enable_private_endpoint": c.EnablePrivateEndpoint, + "enable_private_nodes": c.EnablePrivateNodes, + "master_ipv4_cidr_block": c.MasterIpv4CidrBlock, + "private_endpoint": c.PrivateEndpoint, + "public_endpoint": c.PublicEndpoint, + }, + } +} +<% end -%> + func flattenIPAllocationPolicy(c *containerBeta.IPAllocationPolicy) []map[string]interface{} { if c == nil { return nil diff --git a/provider/terraform/resources/resource_container_node_pool.go b/provider/terraform/resources/resource_container_node_pool.go.erb similarity index 99% rename from provider/terraform/resources/resource_container_node_pool.go rename to provider/terraform/resources/resource_container_node_pool.go.erb index 009fc788d822..90d548b80e90 100644 --- a/provider/terraform/resources/resource_container_node_pool.go +++ b/provider/terraform/resources/resource_container_node_pool.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -54,7 +55,9 @@ func resourceContainerNodePool() *schema.Resource { ForceNew: true, }, "region": &schema.Schema{ +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeString, Optional: true, ForceNew: true, @@ -86,7 +89,9 @@ var schemaNodePool = map[string]*schema.Schema{ }, "max_pods_per_node": &schema.Schema{ +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeInt, Optional: true, ForceNew: true, diff --git a/provider/terraform/tests/data_source_google_compute_instance_group_test.go b/provider/terraform/tests/data_source_google_compute_instance_group_test.go.erb similarity index 97% rename from provider/terraform/tests/data_source_google_compute_instance_group_test.go rename to provider/terraform/tests/data_source_google_compute_instance_group_test.go.erb index bc42d7805093..4c4bab6e98bc 100644 --- a/provider/terraform/tests/data_source_google_compute_instance_group_test.go +++ b/provider/terraform/tests/data_source_google_compute_instance_group_test.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -311,7 +312,14 @@ resource "google_compute_instance_template" "igm-basic" { resource "google_compute_instance_group_manager" "igm" { name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + name = "primary" + } +<% end -%> base_instance_name = "igm" zone = "us-central1-a" target_size = 10 diff --git a/provider/terraform/tests/resource_compute_autoscaler_test.go b/provider/terraform/tests/resource_compute_autoscaler_test.go.erb similarity index 97% rename from provider/terraform/tests/resource_compute_autoscaler_test.go rename to provider/terraform/tests/resource_compute_autoscaler_test.go.erb index f68abb49ff7b..3ebbb0d81610 100644 --- a/provider/terraform/tests/resource_compute_autoscaler_test.go +++ b/provider/terraform/tests/resource_compute_autoscaler_test.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -260,7 +261,14 @@ resource "google_compute_target_pool" "foobar" { resource "google_compute_instance_group_manager" "foobar" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.foobar.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" + } +<% end -%> target_pools = ["${google_compute_target_pool.foobar.self_link}"] base_instance_name = "foobar" zone = "us-central1-a" diff --git a/provider/terraform/tests/resource_compute_backend_service_test.go b/provider/terraform/tests/resource_compute_backend_service_test.go.erb similarity index 97% rename from provider/terraform/tests/resource_compute_backend_service_test.go rename to provider/terraform/tests/resource_compute_backend_service_test.go.erb index 19d8116a6ad5..d9c79fafeab2 100644 --- a/provider/terraform/tests/resource_compute_backend_service_test.go +++ b/provider/terraform/tests/resource_compute_backend_service_test.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -699,7 +700,14 @@ resource "google_compute_backend_service" "lipsum" { resource "google_compute_instance_group_manager" "foobar" { name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.foobar.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" + } +<% end -%> base_instance_name = "foobar" zone = "us-central1-f" target_size = 1 @@ -762,7 +770,14 @@ resource "google_compute_backend_service" "lipsum" { resource "google_compute_instance_group_manager" "foobar" { name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.foobar.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" + } +<% end -%> base_instance_name = "foobar" zone = "us-central1-f" target_size = 1 @@ -915,7 +930,14 @@ resource "google_compute_backend_service" "lipsum" { resource "google_compute_instance_group_manager" "foobar" { name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.foobar.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" + } +<% end -%> base_instance_name = "foobar" zone = "us-central1-f" target_size = 1 @@ -973,7 +995,14 @@ resource "google_compute_backend_service" "lipsum" { resource "google_compute_instance_group_manager" "foobar" { name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.foobar.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" + } +<% end -%> base_instance_name = "foobar" zone = "us-central1-f" target_size = 1 diff --git a/provider/terraform/tests/resource_compute_instance_group_manager_test.go b/provider/terraform/tests/resource_compute_instance_group_manager_test.go.erb similarity index 77% rename from provider/terraform/tests/resource_compute_instance_group_manager_test.go rename to provider/terraform/tests/resource_compute_instance_group_manager_test.go.erb index 5326f18622d8..8ab73042783f 100644 --- a/provider/terraform/tests/resource_compute_instance_group_manager_test.go +++ b/provider/terraform/tests/resource_compute_instance_group_manager_test.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -128,6 +129,7 @@ func TestAccInstanceGroupManager_updateLifecycle(t *testing.T) { }) } +<% if version.nil? || version == 'ga' -%> func TestAccInstanceGroupManager_updateStrategy(t *testing.T) { t.Parallel() @@ -149,6 +151,7 @@ func TestAccInstanceGroupManager_updateStrategy(t *testing.T) { }, }) } +<% end -%> func TestAccInstanceGroupManager_rollingUpdatePolicy(t *testing.T) { t.Parallel() @@ -160,14 +163,45 @@ func TestAccInstanceGroupManager_rollingUpdatePolicy(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckInstanceGroupManagerDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccInstanceGroupManager_rollingUpdatePolicy(igm), }, +<% if version.nil? || version == 'ga' -%> // No import step because rolling updates are broken and the field will be removed in 2.0.0. // TODO(danawillow): Remove this test once we've removed the field. - resource.TestStep{ +<% else -%> + { + ResourceName: "google_compute_instance_group_manager.igm-rolling-update-policy", + ImportState: true, + ImportStateVerify: true, + }, +<% end -%> + { Config: testAccInstanceGroupManager_rollingUpdatePolicy2(igm), }, +<% unless version.nil? || version == 'ga' -%> + { + ResourceName: "google_compute_instance_group_manager.igm-rolling-update-policy", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccInstanceGroupManager_rollingUpdatePolicy3(igm), + }, + { + ResourceName: "google_compute_instance_group_manager.igm-rolling-update-policy", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccInstanceGroupManager_rollingUpdatePolicy4(igm), + }, + { + ResourceName: "google_compute_instance_group_manager.igm-rolling-update-policy", + ImportState: true, + ImportStateVerify: true, + }, +<% end -%> }, }) } @@ -245,6 +279,16 @@ func TestAccInstanceGroupManager_autoHealingPolicies(t *testing.T) { ImportState: true, ImportStateVerify: true, }, +<% unless version.nil? || version == 'ga' -%> + { + Config: testAccInstanceGroupManager_autoHealingPoliciesRemoved(template, target, igm, hck), + }, + { + ResourceName: "google_compute_instance_group_manager.igm-basic", + ImportState: true, + ImportStateVerify: true, + }, +<% end -%> }, }) } @@ -347,7 +391,14 @@ func testAccInstanceGroupManager_basic(template, target, igm1, igm2 string) stri resource "google_compute_instance_group_manager" "igm-basic" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + name = "prod" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + } +<% end -%> target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] base_instance_name = "igm-basic" zone = "us-central1-c" @@ -357,7 +408,14 @@ func testAccInstanceGroupManager_basic(template, target, igm1, igm2 string) stri resource "google_compute_instance_group_manager" "igm-no-tp" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + name = "prod" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + } +<% end -%> base_instance_name = "igm-no-tp" zone = "us-central1-c" target_size = 2 @@ -400,7 +458,14 @@ func testAccInstanceGroupManager_targetSizeZero(template, igm string) string { resource "google_compute_instance_group_manager" "igm-basic" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + name = "prod" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + } +<% end -%> base_instance_name = "igm-basic" zone = "us-central1-c" } @@ -448,7 +513,14 @@ func testAccInstanceGroupManager_update(template, target, igm string) string { resource "google_compute_instance_group_manager" "igm-update" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-update.self_link}" +<% else -%> + version { + name = "prod" + instance_template = "${google_compute_instance_template.igm-update.self_link}" + } +<% end -%> target_pools = ["${google_compute_target_pool.igm-update.self_link}"] base_instance_name = "igm-update" zone = "us-central1-c" @@ -533,7 +605,14 @@ func testAccInstanceGroupManager_update2(template1, target1, target2, template2, resource "google_compute_instance_group_manager" "igm-update" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-update2.self_link}" +<% else -%> + version { + name = "prod" + instance_template = "${google_compute_instance_template.igm-update2.self_link}" + } +<% end -%> target_pools = [ "${google_compute_target_pool.igm-update.self_link}", "${google_compute_target_pool.igm-update2.self_link}", @@ -586,7 +665,14 @@ func testAccInstanceGroupManager_updateLifecycle(tag, igm string) string { resource "google_compute_instance_group_manager" "igm-update" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-update.self_link}" +<% else -%> + version { + name = "prod" + instance_template = "${google_compute_instance_template.igm-update.self_link}" + } +<% end -%> base_instance_name = "igm-update" zone = "us-central1-c" target_size = 2 @@ -597,6 +683,7 @@ func testAccInstanceGroupManager_updateLifecycle(tag, igm string) string { }`, tag, igm) } +<% if version.nil? || version == 'ga' -%> func testAccInstanceGroupManager_updateStrategy(igm string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { @@ -642,6 +729,7 @@ func testAccInstanceGroupManager_updateStrategy(igm string) string { } }`, igm) } +<% end -%> func testAccInstanceGroupManager_rollingUpdatePolicy(igm string) string { return fmt.Sprintf(` @@ -677,12 +765,23 @@ resource "google_compute_instance_template" "igm-rolling-update-policy" { resource "google_compute_instance_group_manager" "igm-rolling-update-policy" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" +<% else -%> + version { + name = "prod" + instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" + } +<% end -%> base_instance_name = "igm-rolling-update-policy" zone = "us-central1-c" target_size = 3 +<% if version.nil? || version == 'ga' -%> update_strategy = "ROLLING_UPDATE" rolling_update_policy { +<% else -%> + update_policy { +<% end -%> type = "PROACTIVE" minimal_action = "REPLACE" max_surge_percent = 50 @@ -726,12 +825,23 @@ resource "google_compute_instance_template" "igm-rolling-update-policy" { resource "google_compute_instance_group_manager" "igm-rolling-update-policy" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" +<% else -%> + version { + name = "prod2" + instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" + } +<% end -%> base_instance_name = "igm-rolling-update-policy" zone = "us-central1-c" target_size = 3 +<% if version.nil? || version == 'ga' -%> update_strategy = "ROLLING_UPDATE" rolling_update_policy { +<% else -%> + update_policy { +<% end -%> type = "PROACTIVE" minimal_action = "REPLACE" max_surge_fixed = 2 @@ -745,6 +855,100 @@ resource "google_compute_instance_group_manager" "igm-rolling-update-policy" { }`, igm) } +<% unless version.nil? || version == 'ga' -%> +func testAccInstanceGroupManager_rollingUpdatePolicy3(igm string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} +resource "google_compute_instance_template" "igm-rolling-update-policy" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["terraform-testing"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + lifecycle { + create_before_destroy = true + } +} +resource "google_compute_instance_group_manager" "igm-rolling-update-policy" { + description = "Terraform test instance group manager" + name = "%s" + version { + name = "prod2" + instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" + } + base_instance_name = "igm-rolling-update-policy" + zone = "us-central1-c" + target_size = 3 + update_policy { + type = "PROACTIVE" + minimal_action = "REPLACE" + max_surge_fixed = 0 + max_unavailable_fixed = 2 + min_ready_sec = 20 + } + named_port { + name = "customhttp" + port = 8080 + } +}`, igm) +} + +func testAccInstanceGroupManager_rollingUpdatePolicy4(igm string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} +resource "google_compute_instance_template" "igm-rolling-update-policy" { + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["terraform-testing"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + lifecycle { + create_before_destroy = true + } +} +resource "google_compute_instance_group_manager" "igm-rolling-update-policy" { + description = "Terraform test instance group manager" + name = "%s" + version { + name = "prod2" + instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" + } + base_instance_name = "igm-rolling-update-policy" + zone = "us-central1-c" + target_size = 3 + update_policy { + type = "PROACTIVE" + minimal_action = "REPLACE" + max_surge_fixed = 2 + max_unavailable_fixed = 0 + min_ready_sec = 20 + } + named_port { + name = "customhttp" + port = 8080 + } +}`, igm) +} +<% end -%> + func testAccInstanceGroupManager_separateRegions(igm1, igm2 string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { @@ -779,7 +983,14 @@ func testAccInstanceGroupManager_separateRegions(igm1, igm2 string) string { resource "google_compute_instance_group_manager" "igm-basic" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + name = "prod" + } +<% end -%> base_instance_name = "igm-basic" zone = "us-central1-c" target_size = 2 @@ -788,7 +999,14 @@ func testAccInstanceGroupManager_separateRegions(igm1, igm2 string) string { resource "google_compute_instance_group_manager" "igm-basic-2" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + name = "prod" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + } +<% end -%> base_instance_name = "igm-basic-2" zone = "us-west1-b" target_size = 2 @@ -833,7 +1051,14 @@ resource "google_compute_target_pool" "igm-basic" { resource "google_compute_instance_group_manager" "igm-basic" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + name = "prod" + } +<% end -%> target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] base_instance_name = "igm-basic" zone = "us-central1-c" @@ -853,6 +1078,60 @@ resource "google_compute_http_health_check" "zero" { `, template, target, igm, hck) } +<% unless version.nil? || version == 'ga' -%> +func testAccInstanceGroupManager_autoHealingPoliciesRemoved(template, target, igm, hck string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} +resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + metadata { + foo = "bar" + } + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} +resource "google_compute_target_pool" "igm-basic" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" +} +resource "google_compute_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + version { + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + name = "prod" + } + target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] + base_instance_name = "igm-basic" + zone = "us-central1-c" + target_size = 2 +} +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + `, template, target, igm, hck) +} +<% end -%> + func testAccInstanceGroupManager_versions(primaryTemplate string, canaryTemplate string, igm string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { @@ -966,7 +1245,14 @@ resource "google_compute_target_pool" "igm-basic" { resource "google_compute_instance_group_manager" "igm-basic" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + name = "primary" + } +<% end -%> target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] base_instance_name = "igm-basic" zone = "us-central1-c" diff --git a/provider/terraform/tests/resource_compute_region_autoscaler_test.go b/provider/terraform/tests/resource_compute_region_autoscaler_test.go.erb similarity index 95% rename from provider/terraform/tests/resource_compute_region_autoscaler_test.go rename to provider/terraform/tests/resource_compute_region_autoscaler_test.go.erb index 2336fddc27d1..4c6e4d7efd2a 100644 --- a/provider/terraform/tests/resource_compute_region_autoscaler_test.go +++ b/provider/terraform/tests/resource_compute_region_autoscaler_test.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -191,7 +192,14 @@ resource "google_compute_target_pool" "foobar" { resource "google_compute_region_instance_group_manager" "foobar" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.foobar.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" + } +<% end -%> target_pools = ["${google_compute_target_pool.foobar.self_link}"] base_instance_name = "foobar" region = "us-central1" @@ -256,7 +264,14 @@ resource "google_compute_target_pool" "foobar" { resource "google_compute_region_instance_group_manager" "foobar" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.foobar.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" + } +<% end -%> target_pools = ["${google_compute_target_pool.foobar.self_link}"] base_instance_name = "foobar" region = "us-central1" diff --git a/provider/terraform/tests/resource_compute_region_instance_group_manager_test.go b/provider/terraform/tests/resource_compute_region_instance_group_manager_test.go.erb similarity index 83% rename from provider/terraform/tests/resource_compute_region_instance_group_manager_test.go rename to provider/terraform/tests/resource_compute_region_instance_group_manager_test.go.erb index 83a1eb93d6e4..ede3687b209d 100644 --- a/provider/terraform/tests/resource_compute_region_instance_group_manager_test.go +++ b/provider/terraform/tests/resource_compute_region_instance_group_manager_test.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -129,6 +130,7 @@ func TestAccRegionInstanceGroupManager_updateLifecycle(t *testing.T) { }) } +<% if version.nil? || version == 'ga' -%> func TestAccRegionInstanceGroupManager_updateStrategy(t *testing.T) { t.Parallel() @@ -150,6 +152,7 @@ func TestAccRegionInstanceGroupManager_updateStrategy(t *testing.T) { }, }) } +<% end -%> func TestAccRegionInstanceGroupManager_rollingUpdatePolicy(t *testing.T) { t.Parallel() @@ -161,14 +164,29 @@ func TestAccRegionInstanceGroupManager_rollingUpdatePolicy(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckInstanceGroupManagerDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccRegionInstanceGroupManager_rollingUpdatePolicy(igm), }, +<% if version.nil? || version == 'ga' -%> // No import step because rolling updates are broken and the field will be removed in 2.0.0. // TODO(danawillow): Remove this test once we've removed the field. - resource.TestStep{ +<% else -%> + { + ResourceName: "google_compute_region_instance_group_manager.igm-rolling-update-policy", + ImportState: true, + ImportStateVerify: true, + }, +<% end -%> + { Config: testAccRegionInstanceGroupManager_rollingUpdatePolicy2(igm), }, +<% unless version.nil? || version == 'ga' -%> + { + ResourceName: "google_compute_region_instance_group_manager.igm-rolling-update-policy", + ImportState: true, + ImportStateVerify: true, + }, +<% end -%> }, }) } @@ -245,6 +263,16 @@ func TestAccRegionInstanceGroupManager_autoHealingPolicies(t *testing.T) { ImportState: true, ImportStateVerify: true, }, +<% unless version.nil? || version == 'ga' -%> + { + Config: testAccRegionInstanceGroupManager_autoHealingPoliciesRemoved(template, target, igm, hck), + }, + { + ResourceName: "google_compute_region_instance_group_manager.igm-basic", + ImportState: true, + ImportStateVerify: true, + }, +<% end -%> }, }) } @@ -341,7 +369,14 @@ func testAccRegionInstanceGroupManager_basic(template, target, igm1, igm2 string resource "google_compute_region_instance_group_manager" "igm-basic" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + name = "primary" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + } +<% end -%> target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] base_instance_name = "igm-basic" region = "us-central1" @@ -351,7 +386,14 @@ func testAccRegionInstanceGroupManager_basic(template, target, igm1, igm2 string resource "google_compute_region_instance_group_manager" "igm-no-tp" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + name = "primary" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + } +<% end -%> base_instance_name = "igm-no-tp" region = "us-central1" target_size = 2 @@ -394,7 +436,14 @@ func testAccRegionInstanceGroupManager_targetSizeZero(template, igm string) stri resource "google_compute_region_instance_group_manager" "igm-basic" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + name = "primary" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + } +<% end -%> base_instance_name = "igm-basic" region = "us-central1" } @@ -442,7 +491,14 @@ func testAccRegionInstanceGroupManager_update(template, target, igm string) stri resource "google_compute_region_instance_group_manager" "igm-update" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-update.self_link}" +<% else -%> + version { + name = "primary" + instance_template = "${google_compute_instance_template.igm-update.self_link}" + } +<% end -%> target_pools = ["${google_compute_target_pool.igm-update.self_link}"] base_instance_name = "igm-update" region = "us-central1" @@ -527,7 +583,14 @@ func testAccRegionInstanceGroupManager_update2(template1, target1, target2, temp resource "google_compute_region_instance_group_manager" "igm-update" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-update2.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.igm-update2.self_link}" + name = "primary" + } +<% end -%> target_pools = [ "${google_compute_target_pool.igm-update.self_link}", "${google_compute_target_pool.igm-update2.self_link}", @@ -580,7 +643,14 @@ func testAccRegionInstanceGroupManager_updateLifecycle(tag, igm string) string { resource "google_compute_region_instance_group_manager" "igm-update" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-update.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.igm-update.self_link}" + name = "primary" + } +<% end -%> base_instance_name = "igm-update" region = "us-central1" target_size = 2 @@ -625,7 +695,14 @@ func testAccRegionInstanceGroupManager_separateRegions(igm1, igm2 string) string resource "google_compute_region_instance_group_manager" "igm-basic" { description = "Terraform test instance group manager" name = "%s" - instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% if version.nil? || version == 'ga' -%> + instance_template = "${google_compute_instance_template.igm-update.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.igm-update.self_link}" + name = "primary" + } +<% end -%> base_instance_name = "igm-basic" region = "us-central1" target_size = 2 @@ -634,7 +711,14 @@ func testAccRegionInstanceGroupManager_separateRegions(igm1, igm2 string) string resource "google_compute_region_instance_group_manager" "igm-basic-2" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + name = "primary" + } +<% end -%> base_instance_name = "igm-basic-2" region = "us-west1" target_size = 2 @@ -679,7 +763,14 @@ resource "google_compute_target_pool" "igm-basic" { resource "google_compute_region_instance_group_manager" "igm-basic" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + name = "primary" + } +<% end -%> target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] base_instance_name = "igm-basic" region = "us-central1" @@ -698,6 +789,61 @@ resource "google_compute_http_health_check" "zero" { } `, template, target, igm, hck) } + +<% unless version.nil? || version == 'ga' -%> +func testAccRegionInstanceGroupManager_autoHealingPoliciesRemoved(template, target, igm, hck string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} +resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + can_ip_forward = false + tags = ["foo", "bar"] + disk { + source_image = "${data.google_compute_image.my_image.self_link}" + auto_delete = true + boot = true + } + network_interface { + network = "default" + } + metadata { + foo = "bar" + } + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } +} +resource "google_compute_target_pool" "igm-basic" { + description = "Resource created for Terraform acceptance testing" + name = "%s" + session_affinity = "CLIENT_IP_PROTO" +} +resource "google_compute_region_instance_group_manager" "igm-basic" { + description = "Terraform test instance group manager" + name = "%s" + version { + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + name = "primary" + } + target_pools = ["${google_compute_target_pool.igm-basic.self_link}"] + base_instance_name = "igm-basic" + region = "us-central1" + target_size = 2 +} +resource "google_compute_http_health_check" "zero" { + name = "%s" + request_path = "/" + check_interval_sec = 1 + timeout_sec = 1 +} + `, template, target, igm, hck) +} +<% end -%> + func testAccRegionInstanceGroupManager_versions(primaryTemplate string, canaryTemplate string, igm string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { @@ -798,7 +944,14 @@ resource "google_compute_instance_template" "igm-basic" { resource "google_compute_region_instance_group_manager" "igm-basic" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-basic.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + name = "primary" + } +<% end -%> base_instance_name = "igm-basic" region = "us-central1" target_size = 2 @@ -807,6 +960,7 @@ resource "google_compute_region_instance_group_manager" "igm-basic" { `, template, igm, strings.Join(zones, "\",\"")) } +<% if version.nil? || version == 'ga' -%> func testAccRegionInstanceGroupManager_updateStrategy(igm string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { @@ -852,6 +1006,7 @@ resource "google_compute_region_instance_group_manager" "igm-update-strategy" { } }`, igm) } +<% end -%> func testAccRegionInstanceGroupManager_rollingUpdatePolicy(igm string) string { return fmt.Sprintf(` @@ -887,14 +1042,26 @@ resource "google_compute_instance_template" "igm-rolling-update-policy" { resource "google_compute_region_instance_group_manager" "igm-rolling-update-policy" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" + name = "primary" + } +<% end -%> base_instance_name = "igm-rolling-update-policy" region = "us-central1" target_size = 4 distribution_policy_zones = ["us-central1-a", "us-central1-f"] +<% if version.nil? || version == 'ga' -%> update_strategy = "ROLLING_UPDATE" rolling_update_policy { +<% else -%> + + update_policy { +<% end -%> type = "PROACTIVE" minimal_action = "REPLACE" max_surge_fixed = 2 @@ -939,14 +1106,26 @@ resource "google_compute_instance_template" "igm-rolling-update-policy" { resource "google_compute_region_instance_group_manager" "igm-rolling-update-policy" { description = "Terraform test instance group manager" name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" +<% else -%> + version { + name = "primary" + instance_template = "${google_compute_instance_template.igm-rolling-update-policy.self_link}" + } +<% end -%> base_instance_name = "igm-rolling-update-policy" region = "us-central1" distribution_policy_zones = ["us-central1-a", "us-central1-f"] target_size = 3 - update_strategy = "ROLLING_UPDATE" +<% if version.nil? || version == 'ga' -%> + update_strategy = "ROLLING_UPDATE" rolling_update_policy { +<% else -%> + + update_policy { +<% end -%> type = "PROACTIVE" minimal_action = "REPLACE" max_surge_fixed = 2 diff --git a/provider/terraform/utils/iam_compute_subnetwork.go b/provider/terraform/utils/iam_compute_subnetwork.go.erb similarity index 92% rename from provider/terraform/utils/iam_compute_subnetwork.go rename to provider/terraform/utils/iam_compute_subnetwork.go.erb index 7f9eaa4dde78..7c19b6676080 100644 --- a/provider/terraform/utils/iam_compute_subnetwork.go +++ b/provider/terraform/utils/iam_compute_subnetwork.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -12,14 +13,18 @@ import ( var IamComputeSubnetworkSchema = map[string]*schema.Schema{ "subnetwork": { - Deprecated: "This resource is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% if version.nil? || version == 'ga' -%> + Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeString, Required: true, ForceNew: true, }, "project": { - Deprecated: "This resource is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% if version.nil? || version == 'ga' -%> + Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeString, Optional: true, Computed: true, @@ -27,7 +32,9 @@ var IamComputeSubnetworkSchema = map[string]*schema.Schema{ }, "region": { - Deprecated: "This resource is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% if version.nil? || version == 'ga' -%> + Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeString, Optional: true, Computed: true, diff --git a/provider/terraform/utils/node_config.go b/provider/terraform/utils/node_config.go.erb similarity index 98% rename from provider/terraform/utils/node_config.go rename to provider/terraform/utils/node_config.go.erb index 948f60db1171..297ab05c852e 100644 --- a/provider/terraform/utils/node_config.go +++ b/provider/terraform/utils/node_config.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -142,7 +143,9 @@ var schemaNodeConfig = &schema.Schema{ }, "taint": { +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeList, Optional: true, ForceNew: true, @@ -170,7 +173,9 @@ var schemaNodeConfig = &schema.Schema{ }, "workload_metadata_config": { +<% if version.nil? || version == 'ga' -%> Deprecated: "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details.", +<% end -%> Type: schema.TypeList, Optional: true, ForceNew: true, diff --git a/templates/terraform/examples/autoscaler_basic.tf.erb b/templates/terraform/examples/autoscaler_basic.tf.erb index 3ffd7c0f3bf0..ff6594e2d55c 100644 --- a/templates/terraform/examples/autoscaler_basic.tf.erb +++ b/templates/terraform/examples/autoscaler_basic.tf.erb @@ -46,7 +46,14 @@ resource "google_compute_instance_group_manager" "foobar" { name = "<%= ctx[:vars]['igm_name'] %>" zone = "us-central1-f" +<% if ctx[:version].nil? || ctx[:version] == 'ga' -%> instance_template = "${google_compute_instance_template.foobar.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" + } +<% end -%> target_pools = ["${google_compute_target_pool.foobar.self_link}"] base_instance_name = "foobar" } diff --git a/templates/terraform/examples/region_autoscaler_basic.tf.erb b/templates/terraform/examples/region_autoscaler_basic.tf.erb index 0373c85fa2fc..fb7206ead9c4 100644 --- a/templates/terraform/examples/region_autoscaler_basic.tf.erb +++ b/templates/terraform/examples/region_autoscaler_basic.tf.erb @@ -46,7 +46,14 @@ resource "google_compute_region_instance_group_manager" "foobar" { name = "<%= ctx[:vars]['rigm_name'] %>" region = "us-central1" +<% if ctx[:version].nil? || ctx[:version] == 'ga' -%> instance_template = "${google_compute_instance_template.foobar.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" + } +<% end -%> target_pools = ["${google_compute_target_pool.foobar.self_link}"] base_instance_name = "foobar" } From 15760e223f17f5752172e878383e69d911283c4b Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 17 Oct 2018 12:15:23 -0700 Subject: [PATCH 36/56] rubocop --- provider/core.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/core.rb b/provider/core.rb index b392ec305f93..23dca6958251 100644 --- a/provider/core.rb +++ b/provider/core.rb @@ -102,7 +102,7 @@ def copy_files(output_folder) end def compile_files(output_folder, version_name) - compile_file_list(output_folder, @config.files.compile, {version: version_name}) + compile_file_list(output_folder, @config.files.compile, version: version_name) end def compile_examples(output_folder) From efb2d8e5f1baea299deae183c22cc38a7e9da8e8 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 17 Oct 2018 12:18:02 -0700 Subject: [PATCH 37/56] fix args to compile_files --- provider/ansible/bundle.rb | 2 +- provider/chef/bundle.rb | 4 ++-- provider/puppet/bundle.rb | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/provider/ansible/bundle.rb b/provider/ansible/bundle.rb index e562db487688..2ffc20ab9224 100644 --- a/provider/ansible/bundle.rb +++ b/provider/ansible/bundle.rb @@ -47,7 +47,7 @@ def generate(output_folder, _types, version_name) version = product.version_obj_or_default(version_name) product.set_properties_based_on_version(version) end - compile_files(output_folder) + compile_files(output_folder, version_name) end def products diff --git a/provider/chef/bundle.rb b/provider/chef/bundle.rb index d8b315e22ae5..aed55250879d 100644 --- a/provider/chef/bundle.rb +++ b/provider/chef/bundle.rb @@ -40,7 +40,7 @@ class Manifest < Provider::Chef::Manifest attr_accessor :releases end - def generate(output_folder, _types, _version_name) + def generate(output_folder, _types, version_name) # Let's build all the dependencies off of the products we found on our # path and has the corresponding provider.yaml file @config.manifest.depends.concat( @@ -54,7 +54,7 @@ def generate(output_folder, _types, _version_name) copy_files(output_folder) compile_changelog(output_folder) - compile_files(output_folder) + compile_files(output_folder, version_name) end def products diff --git a/provider/puppet/bundle.rb b/provider/puppet/bundle.rb index c281dfcefb19..76df015da70a 100644 --- a/provider/puppet/bundle.rb +++ b/provider/puppet/bundle.rb @@ -35,7 +35,7 @@ def provider end end - def generate(output_folder, _types, _version_name) + def generate(output_folder, _types, version_name) # Let's build all the dependencies off of the products we found on our # path and has the corresponding provider.yaml file @config.manifest.releases = releases @@ -48,7 +48,7 @@ def generate(output_folder, _types, _version_name) compile_changelog(output_folder) copy_files(output_folder) - compile_files(output_folder) + compile_files(output_folder, version_name) end def products From fcd777704629022309bd3b55c08a25efb1e21e10 Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Wed, 17 Oct 2018 13:50:13 -0700 Subject: [PATCH 38/56] Basic structure for singular InSpec resources (#573) Merged PR #573. --- .ci/magic-modules/create-pr.sh | 2 +- build/inspec | 2 +- build/terraform | 2 +- products/compute/inspec.yaml | 81 ++++++++++++++++++++++++++ provider/inspec.rb | 9 +++ templates/inspec/singular_resource.erb | 29 ++++++++- 6 files changed, 121 insertions(+), 4 deletions(-) diff --git a/.ci/magic-modules/create-pr.sh b/.ci/magic-modules/create-pr.sh index 041f98dcb2a2..885492e1d0c9 100755 --- a/.ci/magic-modules/create-pr.sh +++ b/.ci/magic-modules/create-pr.sh @@ -82,7 +82,7 @@ if [ "$BRANCH_NAME" = "$ORIGINAL_PR_BRANCH" ]; then fi git checkout -b "$BRANCH_NAME" - if INSPEC_PR=$(hub pull-request -b "$INSPEC_REPO_USER/inspec:master" -F ./downstream_body); then + if INSPEC_PR=$(hub pull-request -b "$INSPEC_REPO_USER/inspec-gcp:master" -F ./downstream_body); then DEPENDENCIES="${DEPENDENCIES}depends: $INSPEC_PR ${NEWLINE}" else echo "InSpec - did not generate a PR." diff --git a/build/inspec b/build/inspec index 257d3f760307..f359ebbf01dc 160000 --- a/build/inspec +++ b/build/inspec @@ -1 +1 @@ -Subproject commit 257d3f760307559938568aeed74261df5cde4b87 +Subproject commit f359ebbf01dc1294dc5338ad2dc380a888a14563 diff --git a/build/terraform b/build/terraform index 56f6530712f9..d622ec466e68 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 56f6530712f9cbfa5db40e9238d35703931fa951 +Subproject commit d622ec466e688b09c601d34f5ce1c7e5796dadf3 diff --git a/products/compute/inspec.yaml b/products/compute/inspec.yaml index 6523b93ef4ab..bcd603cee34d 100644 --- a/products/compute/inspec.yaml +++ b/products/compute/inspec.yaml @@ -19,6 +19,87 @@ manifest: !ruby/object:Provider::Inspec::Manifest summary: 'InSpec resources for verifying GCP infrastructure' description: | InSpec resources for verifying GCP infrastructure +overrides: !ruby/object:Provider::ResourceOverrides + Address: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Autoscaler: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + BackendBucket: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + BackendService: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Disk: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + DiskType: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Firewall: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + ForwardingRule: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + GlobalAddress: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + GlobalForwardingRule: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + HealthCheck: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + HttpHealthCheck: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + HttpsHealthCheck: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Image: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Instance: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + InstanceGroup: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + InstanceGroupManager: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + InstanceTemplate: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + InterconnectAttachment: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + License: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + MachineType: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Network: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Region: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + RegionAutoscaler: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + RegionDisk: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + RegionDiskType: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Route: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Router: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Snapshot: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + SslCertificate: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + SslPolicy: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + Subnetwork: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetHttpProxy: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetHttpsProxy: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetPool: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetTcpProxy: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetVpnGateway: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + TargetSslProxy: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + UrlMap: !ruby/object:Provider::Chef::ResourceOverride + exclude: true + VpnTunnel: !ruby/object:Provider::Chef::ResourceOverride + exclude: true files: !ruby/object:Provider::Config::Files style: functions: diff --git a/provider/inspec.rb b/provider/inspec.rb index 7f66da907f30..69439d7b94de 100644 --- a/provider/inspec.rb +++ b/provider/inspec.rb @@ -21,6 +21,7 @@ module Provider # Code generator for Example Cookbooks that manage Google Cloud Platform # resources. class Inspec < Provider::Core + include Google::RubyUtils # Settings for the provider class Config < Provider::Config attr_reader :manifest @@ -53,6 +54,14 @@ def generate_resource(data) ) end + # Returns the url that this object can be retrieved from + # based off of the self link + def url(object) + url = object.self_link_url[1] + return url.join('') if url.is_a?(Array) + url.split("\n").join('') + end + # TODO? def generate_resource_tests(data) end diff --git a/templates/inspec/singular_resource.erb b/templates/inspec/singular_resource.erb index 3628efec92c1..0858313284a5 100644 --- a/templates/inspec/singular_resource.erb +++ b/templates/inspec/singular_resource.erb @@ -14,4 +14,31 @@ -%> <%= compile 'templates/license.erb' -%> -<%= lines(autogen_notice :ruby) -%> \ No newline at end of file +<%= lines(autogen_notice :ruby) -%> + +# A provider to manage <%= @api.name -%> resources. +class <%= object.name -%> < Inspec.resource(1) + + name 'google_<%= product_ns.downcase -%>_<%= object.name.downcase -%>' + desc '<%= object.name -%>' + supports platform: 'gcp-mm' + +<% object.properties.each do |prop| -%> + <%= "attr_reader :#{prop.out_name}" -%> + +<% end -%> + def base + '<%= object.self_link_url[0].join %>' + end + + def url + '<%= url(object) %>' + end + + # TODO + def parse end + + def exists? + !@fetched.nil? + end +end \ No newline at end of file From 2923c48fe1a1687dce1ca7dbeb40632232a6df0e Mon Sep 17 00:00:00 2001 From: Chris Stephens Date: Wed, 17 Oct 2018 14:36:13 -0700 Subject: [PATCH 39/56] move puppet logging to debug instead of info (#574) Merged PR #574. --- provider/test_data/property.rb | 2 +- provider/test_matrix.rb | 4 ++-- templates/puppet/resource.erb | 2 +- templates/puppet/type.erb | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/provider/test_data/property.rb b/provider/test_data/property.rb index ad76f36768ff..c51fde76d71f 100644 --- a/provider/test_data/property.rb +++ b/provider/test_data/property.rb @@ -31,7 +31,7 @@ def initialize(provider) # rubocop:disable Metrics/PerceivedComplexity def property(prop, index, comparator, value, start_indent = 0, name_override = nil) - Google::LOGGER.info \ + Google::LOGGER.debug \ "Generating test #{prop.out_name}[#{index}] #{comparator} #{value}" if prop.class <= Api::Type::ResourceRef diff --git a/provider/test_matrix.rb b/provider/test_matrix.rb index 3d37f2cc8ba9..0cd69faad908 100644 --- a/provider/test_matrix.rb +++ b/provider/test_matrix.rb @@ -26,7 +26,7 @@ def initialize end def add(matrix, file, object) - Google::LOGGER.info \ + Google::LOGGER.debug \ "Registering test matrix for #{object.name} @ #{file}" @matrixes << matrix end @@ -104,7 +104,7 @@ def pop(ensurable, exists = :none, changes = :none, has_name = :none, # Ensures that all test contexts are defined def verify - Google::LOGGER.info "Verifying test matrix for #{@object.name} @ #{@file}" + Google::LOGGER.debug "Verifying test matrix for #{@object.name} @ #{@file}" verify_topics verify_match_expectations fail_if_not_all_popped unless @hierarchy.empty? diff --git a/templates/puppet/resource.erb b/templates/puppet/resource.erb index c831e8f8caa9..ecce6b3df1cc 100644 --- a/templates/puppet/resource.erb +++ b/templates/puppet/resource.erb @@ -42,7 +42,7 @@ -%> <%= lines(emit_requires(requires)) -%> -<% Google::LOGGER.info "Generating #{object.name}: #{object.out_name}" -%> +<% Google::LOGGER.debug "Generating #{object.name}: #{object.out_name}" -%> Puppet::Type.type(:<%= object.out_name -%>).provide(:google) do mk_resource_methods diff --git a/templates/puppet/type.erb b/templates/puppet/type.erb index 6562ee68af8f..fe21a01ed614 100644 --- a/templates/puppet/type.erb +++ b/templates/puppet/type.erb @@ -25,7 +25,7 @@ -%> <%= lines(emit_requires(requires)) -%> -<% Google::LOGGER.info "Generating #{object.name}: #{object.out_name}" -%> +<% Google::LOGGER.debug "Generating #{object.name}: #{object.out_name}" -%> Puppet::Type.newtype(:<%= object.out_name -%>) do <%= format_description(object, 2, '@doc =') %> @@ -39,7 +39,7 @@ Puppet::Type.newtype(:<%= object.out_name -%>) do unless object.parameters.nil? object.parameters.each do |param| if param.class <= Api::Type::ResourceRef - Google::LOGGER.info \ + Google::LOGGER.debug \ "Generating autorequire #{object.name}.#{param.name}: #{param.type}" -%> autorequire(:<%= param.out_type -%>) do @@ -103,7 +103,7 @@ Puppet::Type.newtype(:<%= object.out_name -%>) do <% unless object.parameters.nil? -%> <% object.parameters.each do |p| -%> -<% Google::LOGGER.info "Generating param #{object.name}.#{p.name}:#{p.type}" -%> +<% Google::LOGGER.debug "Generating param #{object.name}.#{p.name}:#{p.type}" -%> <%= namevar = identities.include?(p.name) ? 'namevar: true' : nil @@ -132,7 +132,7 @@ Puppet::Type.newtype(:<%= object.out_name -%>) do <% end -%> <% end -%> <% object.properties.each do |p| - Google::LOGGER.info "Generating #{object.name}.#{p.name}: #{p.type}" + Google::LOGGER.debug "Generating #{object.name}.#{p.name}: #{p.type}" -%> <%= From 9533dd9a9ebdf7c4d4963b11bdc9420049d62c77 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 17 Oct 2018 14:48:21 -0700 Subject: [PATCH 40/56] small changes noticed when inspecting generated code --- products/compute/terraform.yaml | 1 + provider/terraform/custom_code.rb | 6 ++++-- .../resource_compute_region_instance_group_manager.go.erb | 4 ++-- .../tests/resource_compute_autoscaler_test.go.erb | 8 ++++---- ...> resource_compute_region_backend_service_test.go.erb} | 8 ++++++++ ...urce_compute_region_instance_group_manager_test.go.erb | 6 +++--- 6 files changed, 22 insertions(+), 11 deletions(-) rename provider/terraform/tests/{resource_compute_region_backend_service_test.go => resource_compute_region_backend_service_test.go.erb} (98%) diff --git a/products/compute/terraform.yaml b/products/compute/terraform.yaml index f6bf3923dde0..dda575607649 100644 --- a/products/compute/terraform.yaml +++ b/products/compute/terraform.yaml @@ -458,6 +458,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "region_autoscaler_basic" primary_resource_id: "foobar" + version: <%= _version_name %> vars: region_autoscaler_name: "my-region-autoscaler" instance_template_name: "my-instance-template" diff --git a/provider/terraform/custom_code.rb b/provider/terraform/custom_code.rb index 7d0cea0e3e16..cd39d36a5bd9 100644 --- a/provider/terraform/custom_code.rb +++ b/provider/terraform/custom_code.rb @@ -82,7 +82,8 @@ def config_documentation body = lines(compile_file( { vars: vars, - primary_resource_id: primary_resource_id + primary_resource_id: primary_resource_id, + version: version }, "templates/terraform/examples/#{name}.tf.erb" )) @@ -119,7 +120,8 @@ def config_example body = lines(compile_file( { vars: vars.map { |k, str| [k, "#{str}-${local.name_suffix}"] }.to_h, - primary_resource_id: primary_resource_id + primary_resource_id: primary_resource_id, + version: version }, "templates/terraform/examples/#{name}.tf.erb" )) diff --git a/provider/terraform/resources/resource_compute_region_instance_group_manager.go.erb b/provider/terraform/resources/resource_compute_region_instance_group_manager.go.erb index 4dc0ca1693da..14c059911022 100644 --- a/provider/terraform/resources/resource_compute_region_instance_group_manager.go.erb +++ b/provider/terraform/resources/resource_compute_region_instance_group_manager.go.erb @@ -254,7 +254,7 @@ func resourceComputeRegionInstanceGroupManager() *schema.Resource { Type: schema.TypeInt, Optional: true, <% if version.nil? || version == 'ga' -%> - Default: 1, + Default: 0, ConflictsWith: []string{"rolling_update_policy.0.max_surge_percent"}, <% else -%> Computed: true, @@ -277,7 +277,7 @@ func resourceComputeRegionInstanceGroupManager() *schema.Resource { Type: schema.TypeInt, Optional: true, <% if version.nil? || version == 'ga' -%> - Default: 1, + Default: 0, ConflictsWith: []string{"rolling_update_policy.0.max_unavailable_percent"}, <% else -%> Computed: true, diff --git a/provider/terraform/tests/resource_compute_autoscaler_test.go.erb b/provider/terraform/tests/resource_compute_autoscaler_test.go.erb index 3ebbb0d81610..5c47ae80e585 100644 --- a/provider/terraform/tests/resource_compute_autoscaler_test.go.erb +++ b/provider/terraform/tests/resource_compute_autoscaler_test.go.erb @@ -264,10 +264,10 @@ resource "google_compute_instance_group_manager" "foobar" { <% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.foobar.self_link}" <% else -%> - version { - instance_template = "${google_compute_instance_template.foobar.self_link}" - name = "primary" - } + version { + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" + } <% end -%> target_pools = ["${google_compute_target_pool.foobar.self_link}"] base_instance_name = "foobar" diff --git a/provider/terraform/tests/resource_compute_region_backend_service_test.go b/provider/terraform/tests/resource_compute_region_backend_service_test.go.erb similarity index 98% rename from provider/terraform/tests/resource_compute_region_backend_service_test.go rename to provider/terraform/tests/resource_compute_region_backend_service_test.go.erb index bde3198c4995..2d8fd722f102 100644 --- a/provider/terraform/tests/resource_compute_region_backend_service_test.go +++ b/provider/terraform/tests/resource_compute_region_backend_service_test.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -328,7 +329,14 @@ resource "google_compute_region_backend_service" "lipsum" { resource "google_compute_instance_group_manager" "foobar" { name = "%s" +<% if version.nil? || version == 'ga' -%> instance_template = "${google_compute_instance_template.foobar.self_link}" +<% else -%> + version { + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" + } +<% end -%> base_instance_name = "foobar" zone = "us-central1-f" target_size = 1 diff --git a/provider/terraform/tests/resource_compute_region_instance_group_manager_test.go.erb b/provider/terraform/tests/resource_compute_region_instance_group_manager_test.go.erb index ede3687b209d..7ca9e0728f09 100644 --- a/provider/terraform/tests/resource_compute_region_instance_group_manager_test.go.erb +++ b/provider/terraform/tests/resource_compute_region_instance_group_manager_test.go.erb @@ -696,10 +696,10 @@ func testAccRegionInstanceGroupManager_separateRegions(igm1, igm2 string) string description = "Terraform test instance group manager" name = "%s" <% if version.nil? || version == 'ga' -%> - instance_template = "${google_compute_instance_template.igm-update.self_link}" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" <% else -%> version { - instance_template = "${google_compute_instance_template.igm-update.self_link}" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" name = "primary" } <% end -%> @@ -1119,7 +1119,7 @@ resource "google_compute_region_instance_group_manager" "igm-rolling-update-poli distribution_policy_zones = ["us-central1-a", "us-central1-f"] target_size = 3 <% if version.nil? || version == 'ga' -%> - update_strategy = "ROLLING_UPDATE" + update_strategy = "ROLLING_UPDATE" rolling_update_policy { <% else -%> From 33a4c2a429df77c91f5034976788ab346a494830 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 17 Oct 2018 14:50:20 -0700 Subject: [PATCH 41/56] Terraform: Add project, organization, folder, service account tests to mm (#577) Merged PR #577. --- ...oogle_organization_iam_custom_role_test.go | 245 ++++++ .../resource_google_organization_iam_test.go | 206 +++++ ...esource_google_organization_policy_test.go | 391 +++++++++ ...esource_google_project_iam_binding_test.go | 257 ++++++ ...rce_google_project_iam_custom_role_test.go | 177 ++++ ...resource_google_project_iam_member_test.go | 169 ++++ ...resource_google_project_iam_policy_test.go | 780 ++++++++++++++++++ .../resource_google_project_migrate_test.go | 70 ++ ...google_project_organization_policy_test.go | 354 ++++++++ .../resource_google_project_service_test.go | 189 +++++ .../resource_google_project_services_test.go | 322 ++++++++ .../tests/resource_google_project_test.go | 576 +++++++++++++ ...esource_google_service_account_iam_test.go | 167 ++++ ...esource_google_service_account_key_test.go | 175 ++++ 14 files changed, 4078 insertions(+) create mode 100644 provider/terraform/tests/resource_google_organization_iam_custom_role_test.go create mode 100644 provider/terraform/tests/resource_google_organization_iam_test.go create mode 100644 provider/terraform/tests/resource_google_organization_policy_test.go create mode 100644 provider/terraform/tests/resource_google_project_iam_binding_test.go create mode 100644 provider/terraform/tests/resource_google_project_iam_custom_role_test.go create mode 100644 provider/terraform/tests/resource_google_project_iam_member_test.go create mode 100644 provider/terraform/tests/resource_google_project_iam_policy_test.go create mode 100644 provider/terraform/tests/resource_google_project_migrate_test.go create mode 100644 provider/terraform/tests/resource_google_project_organization_policy_test.go create mode 100644 provider/terraform/tests/resource_google_project_service_test.go create mode 100644 provider/terraform/tests/resource_google_project_services_test.go create mode 100644 provider/terraform/tests/resource_google_project_test.go create mode 100644 provider/terraform/tests/resource_google_service_account_iam_test.go create mode 100644 provider/terraform/tests/resource_google_service_account_key_test.go diff --git a/provider/terraform/tests/resource_google_organization_iam_custom_role_test.go b/provider/terraform/tests/resource_google_organization_iam_custom_role_test.go new file mode 100644 index 000000000000..6d7fef648515 --- /dev/null +++ b/provider/terraform/tests/resource_google_organization_iam_custom_role_test.go @@ -0,0 +1,245 @@ +package google + +import ( + "fmt" + "reflect" + "sort" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccOrganizationIamCustomRole_basic(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + roleId := "tfIamCustomRole" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleOrganizationIamCustomRoleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleOrganizationIamCustomRole_basic(org, roleId), + Check: testAccCheckGoogleOrganizationIamCustomRole( + "google_organization_iam_custom_role.foo", + "My Custom Role", + "foo", + "GA", + []string{"resourcemanager.projects.list"}), + }, + { + Config: testAccCheckGoogleOrganizationIamCustomRole_update(org, roleId), + Check: testAccCheckGoogleOrganizationIamCustomRole( + "google_organization_iam_custom_role.foo", + "My Custom Role Updated", + "bar", + "BETA", + []string{"resourcemanager.projects.list", "resourcemanager.organizations.get"}), + }, + { + ResourceName: "google_organization_iam_custom_role.foo", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccOrganizationIamCustomRole_undelete(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + roleId := "tfIamCustomRole" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleOrganizationIamCustomRoleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleOrganizationIamCustomRole_basic(org, roleId), + Check: testAccCheckGoogleOrganizationIamCustomRoleDeletionStatus("google_organization_iam_custom_role.foo", false), + }, + // Soft-delete + { + Config: testAccCheckGoogleOrganizationIamCustomRole_deleted(org, roleId), + Check: testAccCheckGoogleOrganizationIamCustomRoleDeletionStatus("google_organization_iam_custom_role.foo", true), + }, + // Undelete + { + Config: testAccCheckGoogleOrganizationIamCustomRole_basic(org, roleId), + Check: testAccCheckGoogleOrganizationIamCustomRoleDeletionStatus("google_organization_iam_custom_role.foo", false), + }, + }, + }) +} + +func TestAccOrganizationIamCustomRole_createAfterDestroy(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + roleId := "tfIamCustomRole" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleOrganizationIamCustomRoleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleOrganizationIamCustomRole_basic(org, roleId), + Check: testAccCheckGoogleOrganizationIamCustomRole( + "google_organization_iam_custom_role.foo", + "My Custom Role", + "foo", + "GA", + []string{"resourcemanager.projects.list"}), + }, + // Destroy resources + { + Config: " ", + Destroy: true, + }, + // Re-create with no existing state + { + Config: testAccCheckGoogleOrganizationIamCustomRole_basic(org, roleId), + Check: testAccCheckGoogleOrganizationIamCustomRole( + "google_organization_iam_custom_role.foo", + "My Custom Role", + "foo", + "GA", + []string{"resourcemanager.projects.list"}), + }, + }, + }) +} + +func testAccCheckGoogleOrganizationIamCustomRoleDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_organization_iam_custom_role" { + continue + } + + role, err := config.clientIAM.Organizations.Roles.Get(rs.Primary.ID).Do() + + if err != nil { + return err + } + + if !role.Deleted { + return fmt.Errorf("Iam custom role still exists") + } + + } + + return nil +} + +func testAccCheckGoogleOrganizationIamCustomRole(n, title, description, stage string, permissions []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + role, err := config.clientIAM.Organizations.Roles.Get(rs.Primary.ID).Do() + + if err != nil { + return err + } + + if title != role.Title { + return fmt.Errorf("Incorrect title. Expected %q, got %q", title, role.Title) + } + + if description != role.Description { + return fmt.Errorf("Incorrect description. Expected %q, got %q", description, role.Description) + } + + if stage != role.Stage { + return fmt.Errorf("Incorrect stage. Expected %q, got %q", stage, role.Stage) + } + + sort.Strings(permissions) + sort.Strings(role.IncludedPermissions) + if !reflect.DeepEqual(permissions, role.IncludedPermissions) { + return fmt.Errorf("Incorrect permissions. Expected %q, got %q", permissions, role.IncludedPermissions) + } + + return nil + } +} + +func testAccCheckGoogleOrganizationIamCustomRoleDeletionStatus(n string, deleted bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + role, err := config.clientIAM.Organizations.Roles.Get(rs.Primary.ID).Do() + + if err != nil { + return err + } + + if deleted != role.Deleted { + return fmt.Errorf("Incorrect deletion status. Expected %t, got %t", deleted, role.Deleted) + } + + return nil + } +} + +func testAccCheckGoogleOrganizationIamCustomRole_basic(orgId, roleId string) string { + return fmt.Sprintf(` +resource "google_organization_iam_custom_role" "foo" { + role_id = "%s" + org_id = "%s" + title = "My Custom Role" + description = "foo" + permissions = ["resourcemanager.projects.list"] +} +`, roleId, orgId) +} + +func testAccCheckGoogleOrganizationIamCustomRole_deleted(orgId, roleId string) string { + return fmt.Sprintf(` +resource "google_organization_iam_custom_role" "foo" { + role_id = "%s" + org_id = "%s" + title = "My Custom Role" + description = "foo" + permissions = ["resourcemanager.projects.list"] + deleted = true +} +`, roleId, orgId) +} + +func testAccCheckGoogleOrganizationIamCustomRole_update(orgId, roleId string) string { + return fmt.Sprintf(` +resource "google_organization_iam_custom_role" "foo" { + role_id = "%s" + org_id = "%s" + title = "My Custom Role Updated" + description = "bar" + permissions = ["resourcemanager.projects.list", "resourcemanager.organizations.get"] + stage = "BETA" +} +`, roleId, orgId) +} diff --git a/provider/terraform/tests/resource_google_organization_iam_test.go b/provider/terraform/tests/resource_google_organization_iam_test.go new file mode 100644 index 000000000000..8b0df19e9d99 --- /dev/null +++ b/provider/terraform/tests/resource_google_organization_iam_test.go @@ -0,0 +1,206 @@ +package google + +import ( + "fmt" + "reflect" + "sort" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/cloudresourcemanager/v1" +) + +// Bindings and members are tested serially to avoid concurrent updates of the org's IAM policy. +// When concurrent changes happen, the behavior is to abort and ask the user to retry allowing +// them to see the new diff instead of blindly overriding the policy stored in GCP. This desired +// behavior however induces flakiness in our acceptance tests, hence the need for running them +// serially. +// Policies are *not tested*, because testing them will ruin changes made to the test org. +func TestAccOrganizationIam(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + account := acctest.RandomWithPrefix("tf-test") + roleId := "tfIamTest" + acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test Iam Binding creation + Config: testAccOrganizationIamBinding_basic(account, roleId, org), + Check: testAccCheckGoogleOrganizationIamBindingExists("foo", "test-role", []string{ + fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + }), + }, + { + ResourceName: "google_organization_iam_binding.foo", + ImportStateId: fmt.Sprintf("%s organizations/%s/roles/%s", org, org, roleId), + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Iam Binding update + Config: testAccOrganizationIamBinding_update(account, roleId, org), + Check: testAccCheckGoogleOrganizationIamBindingExists("foo", "test-role", []string{ + fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + fmt.Sprintf("serviceAccount:%s-2@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + }), + }, + { + ResourceName: "google_organization_iam_binding.foo", + ImportStateId: fmt.Sprintf("%s organizations/%s/roles/%s", org, org, roleId), + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Iam Member creation (no update for member, no need to test) + Config: testAccOrganizationIamMember_basic(account, org), + Check: testAccCheckGoogleOrganizationIamMemberExists("foo", "roles/browser", + fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + ), + }, + { + ResourceName: "google_organization_iam_member.foo", + ImportStateId: fmt.Sprintf("%s roles/browser serviceAccount:%s@%s.iam.gserviceaccount.com", org, account, getTestProjectFromEnv()), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckGoogleOrganizationIamBindingExists(bindingResourceName, roleResourceName string, members []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + bindingRs, ok := s.RootModule().Resources["google_organization_iam_binding."+bindingResourceName] + if !ok { + return fmt.Errorf("Not found: %s", bindingResourceName) + } + + roleRs, ok := s.RootModule().Resources["google_organization_iam_custom_role."+roleResourceName] + if !ok { + return fmt.Errorf("Not found: %s", roleResourceName) + } + + config := testAccProvider.Meta().(*Config) + p, err := config.clientResourceManager.Organizations.GetIamPolicy("organizations/"+bindingRs.Primary.Attributes["org_id"], &cloudresourcemanager.GetIamPolicyRequest{}).Do() + if err != nil { + return err + } + + for _, binding := range p.Bindings { + if binding.Role == roleRs.Primary.ID { + sort.Strings(members) + sort.Strings(binding.Members) + + if reflect.DeepEqual(members, binding.Members) { + return nil + } + + return fmt.Errorf("Binding found but expected members is %v, got %v", members, binding.Members) + } + } + + return fmt.Errorf("No binding for role %q", roleRs.Primary.ID) + } +} + +func testAccCheckGoogleOrganizationIamMemberExists(n, role, member string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources["google_organization_iam_member."+n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + config := testAccProvider.Meta().(*Config) + p, err := config.clientResourceManager.Organizations.GetIamPolicy("organizations/"+rs.Primary.Attributes["org_id"], &cloudresourcemanager.GetIamPolicyRequest{}).Do() + if err != nil { + return err + } + + for _, binding := range p.Bindings { + if binding.Role == role { + for _, m := range binding.Members { + if m == member { + return nil + } + } + + return fmt.Errorf("Missing member %q, got %v", member, binding.Members) + } + } + + return fmt.Errorf("No binding for role %q", role) + } +} + +// We are using a custom role since iam_binding is authoritative on the member list and +// we want to avoid removing members from an existing role to prevent unwanted side effects. +func testAccOrganizationIamBinding_basic(account, role, org string) string { + return fmt.Sprintf(` +resource "google_service_account" "test-account" { + account_id = "%s" + display_name = "Iam Testing Account" +} + +resource "google_organization_iam_custom_role" "test-role" { + role_id = "%s" + org_id = "%s" + title = "Iam Testing Role" + permissions = ["genomics.datasets.get"] +} + +resource "google_organization_iam_binding" "foo" { + org_id = "%s" + role = "${google_organization_iam_custom_role.test-role.id}" + members = ["serviceAccount:${google_service_account.test-account.email}"] +} +`, account, role, org, org) +} + +func testAccOrganizationIamBinding_update(account, role, org string) string { + return fmt.Sprintf(` +resource "google_service_account" "test-account" { + account_id = "%s" + display_name = "Iam Testing Account" +} + +resource "google_organization_iam_custom_role" "test-role" { + role_id = "%s" + org_id = "%s" + title = "Iam Testing Role" + permissions = ["genomics.datasets.get"] +} + +resource "google_service_account" "test-account-2" { + account_id = "%s-2" + display_name = "Iam Testing Account" +} + +resource "google_organization_iam_binding" "foo" { + org_id = "%s" + role = "${google_organization_iam_custom_role.test-role.id}" + members = [ + "serviceAccount:${google_service_account.test-account.email}", + "serviceAccount:${google_service_account.test-account-2.email}" + ] +} +`, account, role, org, account, org) +} + +func testAccOrganizationIamMember_basic(account, org string) string { + return fmt.Sprintf(` +resource "google_service_account" "test-account" { + account_id = "%s" + display_name = "Iam Testing Account" +} + +resource "google_organization_iam_member" "foo" { + org_id = "%s" + role = "roles/browser" + member = "serviceAccount:${google_service_account.test-account.email}" +} +`, account, org) +} diff --git a/provider/terraform/tests/resource_google_organization_policy_test.go b/provider/terraform/tests/resource_google_organization_policy_test.go new file mode 100644 index 000000000000..b62a0bac9272 --- /dev/null +++ b/provider/terraform/tests/resource_google_organization_policy_test.go @@ -0,0 +1,391 @@ +package google + +import ( + "fmt" + "reflect" + "sort" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/cloudresourcemanager/v1" +) + +var DENIED_ORG_POLICIES = []string{ + "doubleclicksearch.googleapis.com", + "replicapoolupdater.googleapis.com", +} + +// Since each test here is acting on the same organization, run the tests serially to +// avoid race conditions and aborted operations. +func TestAccOrganizationPolicy(t *testing.T) { + testCases := map[string]func(t *testing.T){ + "boolean": testAccOrganizationPolicy_boolean, + "list_allowAll": testAccOrganizationPolicy_list_allowAll, + "list_allowSome": testAccOrganizationPolicy_list_allowSome, + "list_denySome": testAccOrganizationPolicy_list_denySome, + "list_update": testAccOrganizationPolicy_list_update, + "restore_policy": testAccOrganizationPolicy_restore_defaultTrue, + } + + for name, tc := range testCases { + // shadow the tc variable into scope so that when + // the loop continues, if t.Run hasn't executed tc(t) + // yet, we don't have a race condition + // see https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +func testAccOrganizationPolicy_boolean(t *testing.T) { + org := getTestOrgTargetFromEnv(t) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + // Test creation of an enforced boolean policy + Config: testAccOrganizationPolicyConfig_boolean(org, true), + Check: testAccCheckGoogleOrganizationBooleanPolicy("bool", true), + }, + { + // Test update from enforced to not + Config: testAccOrganizationPolicyConfig_boolean(org, false), + Check: testAccCheckGoogleOrganizationBooleanPolicy("bool", false), + }, + { + Config: " ", + Destroy: true, + }, + { + // Test creation of a not enforced boolean policy + Config: testAccOrganizationPolicyConfig_boolean(org, false), + Check: testAccCheckGoogleOrganizationBooleanPolicy("bool", false), + }, + { + // Test update from not enforced to enforced + Config: testAccOrganizationPolicyConfig_boolean(org, true), + Check: testAccCheckGoogleOrganizationBooleanPolicy("bool", true), + }, + { + ResourceName: "google_organization_policy.bool", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + +} + +func testAccOrganizationPolicy_list_allowAll(t *testing.T) { + org := getTestOrgTargetFromEnv(t) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccOrganizationPolicyConfig_list_allowAll(org), + Check: testAccCheckGoogleOrganizationListPolicyAll("list", "ALLOW"), + }, + { + ResourceName: "google_organization_policy.list", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccOrganizationPolicy_list_allowSome(t *testing.T) { + org := getTestOrgTargetFromEnv(t) + project := getTestProjectFromEnv() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccOrganizationPolicyConfig_list_allowSome(org, project), + Check: testAccCheckGoogleOrganizationListPolicyAllowedValues("list", []string{"projects/" + project, "projects/debian-cloud"}), + }, + { + ResourceName: "google_organization_policy.list", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccOrganizationPolicy_list_denySome(t *testing.T) { + org := getTestOrgTargetFromEnv(t) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccOrganizationPolicyConfig_list_denySome(org), + Check: testAccCheckGoogleOrganizationListPolicyDeniedValues("list", DENIED_ORG_POLICIES), + }, + { + ResourceName: "google_organization_policy.list", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccOrganizationPolicy_list_update(t *testing.T) { + org := getTestOrgTargetFromEnv(t) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccOrganizationPolicyConfig_list_allowAll(org), + Check: testAccCheckGoogleOrganizationListPolicyAll("list", "ALLOW"), + }, + { + Config: testAccOrganizationPolicyConfig_list_denySome(org), + Check: testAccCheckGoogleOrganizationListPolicyDeniedValues("list", DENIED_ORG_POLICIES), + }, + { + ResourceName: "google_organization_policy.list", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccOrganizationPolicy_restore_defaultTrue(t *testing.T) { + org := getTestOrgTargetFromEnv(t) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccOrganizationPolicyConfig_restore_defaultTrue(org), + Check: testAccCheckGoogleOrganizationRestoreDefaultTrue("restore", &cloudresourcemanager.RestoreDefault{}), + }, + { + ResourceName: "google_organization_policy.restore", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckGoogleOrganizationPolicyDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_organization_policy" { + continue + } + + org := "organizations/" + rs.Primary.Attributes["org_id"] + constraint := canonicalOrgPolicyConstraint(rs.Primary.Attributes["constraint"]) + policy, err := config.clientResourceManager.Organizations.GetOrgPolicy(org, &cloudresourcemanager.GetOrgPolicyRequest{ + Constraint: constraint, + }).Do() + + if err != nil { + return err + } + + if policy.ListPolicy != nil || policy.BooleanPolicy != nil { + return fmt.Errorf("Org policy with constraint '%s' hasn't been cleared", constraint) + } + } + return nil +} + +func testAccCheckGoogleOrganizationBooleanPolicy(n string, enforced bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + policy, err := getGoogleOrganizationPolicyTestResource(s, n) + if err != nil { + return err + } + + if policy.BooleanPolicy.Enforced != enforced { + return fmt.Errorf("Expected boolean policy enforcement to be '%t', got '%t'", enforced, policy.BooleanPolicy.Enforced) + } + + return nil + } +} + +func testAccCheckGoogleOrganizationListPolicyAll(n, policyType string) resource.TestCheckFunc { + return func(s *terraform.State) error { + policy, err := getGoogleOrganizationPolicyTestResource(s, n) + if err != nil { + return err + } + + if len(policy.ListPolicy.AllowedValues) > 0 || len(policy.ListPolicy.DeniedValues) > 0 { + return fmt.Errorf("The `values` field shouldn't be set") + } + + if policy.ListPolicy.AllValues != policyType { + return fmt.Errorf("Expected the list policy to '%s' all values, got '%s'", policyType, policy.ListPolicy.AllValues) + } + + return nil + } +} + +func testAccCheckGoogleOrganizationListPolicyAllowedValues(n string, values []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + policy, err := getGoogleOrganizationPolicyTestResource(s, n) + if err != nil { + return err + } + + sort.Strings(policy.ListPolicy.AllowedValues) + sort.Strings(values) + if !reflect.DeepEqual(policy.ListPolicy.AllowedValues, values) { + return fmt.Errorf("Expected the list policy to allow '%s', instead allowed '%s'", values, policy.ListPolicy.AllowedValues) + } + + return nil + } +} + +func testAccCheckGoogleOrganizationListPolicyDeniedValues(n string, values []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + policy, err := getGoogleOrganizationPolicyTestResource(s, n) + if err != nil { + return err + } + + sort.Strings(policy.ListPolicy.DeniedValues) + sort.Strings(values) + if !reflect.DeepEqual(policy.ListPolicy.DeniedValues, values) { + return fmt.Errorf("Expected the list policy to deny '%s', instead denied '%s'", values, policy.ListPolicy.DeniedValues) + } + + return nil + } +} + +func testAccCheckGoogleOrganizationRestoreDefaultTrue(n string, policyDefault *cloudresourcemanager.RestoreDefault) resource.TestCheckFunc { + return func(s *terraform.State) error { + + policy, err := getGoogleOrganizationPolicyTestResource(s, n) + if err != nil { + return err + } + + if !reflect.DeepEqual(policy.RestoreDefault, policyDefault) { + return fmt.Errorf("Expected the restore default '%s', instead denied, %s", policyDefault, policy.RestoreDefault) + } + + return nil + } +} + +func getGoogleOrganizationPolicyTestResource(s *terraform.State, n string) (*cloudresourcemanager.OrgPolicy, error) { + rn := "google_organization_policy." + n + rs, ok := s.RootModule().Resources[rn] + if !ok { + return nil, fmt.Errorf("Not found: %s", rn) + } + + if rs.Primary.ID == "" { + return nil, fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + return config.clientResourceManager.Organizations.GetOrgPolicy("organizations/"+rs.Primary.Attributes["org_id"], &cloudresourcemanager.GetOrgPolicyRequest{ + Constraint: rs.Primary.Attributes["constraint"], + }).Do() +} + +func testAccOrganizationPolicyConfig_boolean(org string, enforced bool) string { + return fmt.Sprintf(` +resource "google_organization_policy" "bool" { + org_id = "%s" + constraint = "constraints/compute.disableSerialPortAccess" + + boolean_policy { + enforced = %t + } +} +`, org, enforced) +} + +func testAccOrganizationPolicyConfig_list_allowAll(org string) string { + return fmt.Sprintf(` +resource "google_organization_policy" "list" { + org_id = "%s" + constraint = "constraints/serviceuser.services" + + list_policy { + allow { + all = true + } + } +} +`, org) +} + +func testAccOrganizationPolicyConfig_list_allowSome(org, project string) string { + return fmt.Sprintf(` +resource "google_organization_policy" "list" { + org_id = "%s" + constraint = "constraints/compute.trustedImageProjects" + + list_policy { + allow { + values = [ + "projects/%s", + "projects/debian-cloud" + ] + } + } +} +`, org, project) +} + +func testAccOrganizationPolicyConfig_list_denySome(org string) string { + return fmt.Sprintf(` +resource "google_organization_policy" "list" { + org_id = "%s" + constraint = "serviceuser.services" + + list_policy { + deny { + values = [ + "doubleclicksearch.googleapis.com", + "replicapoolupdater.googleapis.com", + ] + } + } +} +`, org) +} + +func testAccOrganizationPolicyConfig_restore_defaultTrue(org string) string { + return fmt.Sprintf(` +resource "google_organization_policy" "restore" { + org_id = "%s" + constraint = "serviceuser.services" + + restore_policy { + default = true + } +} +`, org) +} diff --git a/provider/terraform/tests/resource_google_project_iam_binding_test.go b/provider/terraform/tests/resource_google_project_iam_binding_test.go new file mode 100644 index 000000000000..1bdbf1448306 --- /dev/null +++ b/provider/terraform/tests/resource_google_project_iam_binding_test.go @@ -0,0 +1,257 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func projectIamBindingImportStep(resourceName, pid, role string) resource.TestStep { + return resource.TestStep{ + ResourceName: resourceName, + ImportStateId: fmt.Sprintf("%s %s", pid, role), + ImportState: true, + ImportStateVerify: true, + } +} + +// Test that an IAM binding can be applied to a project +func TestAccProjectIamBinding_basic(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + role := "roles/compute.instanceAdmin" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + // Apply an IAM binding + { + Config: testAccProjectAssociateBindingBasic(pid, pname, org, role), + }, + projectIamBindingImportStep("google_project_iam_binding.acceptance", pid, role), + }, + }) +} + +// Test that multiple IAM bindings can be applied to a project, one at a time +func TestAccProjectIamBinding_multiple(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + role := "roles/compute.instanceAdmin" + role2 := "roles/viewer" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + // Apply an IAM binding + { + Config: testAccProjectAssociateBindingBasic(pid, pname, org, role), + }, + // Apply another IAM binding + { + Config: testAccProjectAssociateBindingMultiple(pid, pname, org, role, role2), + }, + projectIamBindingImportStep("google_project_iam_binding.acceptance", pid, role), + projectIamBindingImportStep("google_project_iam_binding.multiple", pid, role2), + }, + }) +} + +// Test that multiple IAM bindings can be applied to a project all at once +func TestAccProjectIamBinding_multipleAtOnce(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + role := "roles/compute.instanceAdmin" + role2 := "roles/viewer" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + // Apply an IAM binding + { + Config: testAccProjectAssociateBindingMultiple(pid, pname, org, role, role2), + }, + projectIamBindingImportStep("google_project_iam_binding.acceptance", pid, role), + projectIamBindingImportStep("google_project_iam_binding.multiple", pid, role2), + }, + }) +} + +// Test that an IAM binding can be updated once applied to a project +func TestAccProjectIamBinding_update(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + role := "roles/compute.instanceAdmin" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + // Apply an IAM binding + { + Config: testAccProjectAssociateBindingBasic(pid, pname, org, role), + }, + projectIamBindingImportStep("google_project_iam_binding.acceptance", pid, role), + + // Apply an updated IAM binding + { + Config: testAccProjectAssociateBindingUpdated(pid, pname, org, role), + }, + projectIamBindingImportStep("google_project_iam_binding.acceptance", pid, role), + + // Drop the original member + { + Config: testAccProjectAssociateBindingDropMemberFromBasic(pid, pname, org, role), + }, + projectIamBindingImportStep("google_project_iam_binding.acceptance", pid, role), + }, + }) +} + +// Test that an IAM binding can be removed from a project +func TestAccProjectIamBinding_remove(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + role := "roles/compute.instanceAdmin" + role2 := "roles/viewer" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + // Apply multiple IAM bindings + { + Config: testAccProjectAssociateBindingMultiple(pid, pname, org, role, role2), + }, + projectIamBindingImportStep("google_project_iam_binding.acceptance", pid, role), + projectIamBindingImportStep("google_project_iam_binding.multiple", pid, role2), + + // Remove the bindings + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + }, + }) +} + +func testAccProjectAssociateBindingBasic(pid, name, org, role string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +resource "google_project_iam_binding" "acceptance" { + project = "${google_project.acceptance.project_id}" + members = ["user:admin@hashicorptest.com"] + role = "%s" +} +`, pid, name, org, role) +} + +func testAccProjectAssociateBindingMultiple(pid, name, org, role, role2 string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +resource "google_project_iam_binding" "acceptance" { + project = "${google_project.acceptance.project_id}" + members = ["user:admin@hashicorptest.com"] + role = "%s" +} + +resource "google_project_iam_binding" "multiple" { + project = "${google_project.acceptance.project_id}" + members = ["user:paddy@hashicorp.com"] + role = "%s" +} +`, pid, name, org, role, role2) +} + +func testAccProjectAssociateBindingUpdated(pid, name, org, role string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +resource "google_project_iam_binding" "acceptance" { + project = "${google_project.acceptance.project_id}" + members = ["user:admin@hashicorptest.com", "user:paddy@hashicorp.com"] + role = "%s" +} +`, pid, name, org, role) +} + +func testAccProjectAssociateBindingDropMemberFromBasic(pid, name, org, role string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +resource "google_project_iam_binding" "acceptance" { + project = "${google_project.acceptance.project_id}" + members = ["user:paddy@hashicorp.com"] + role = "%s" +} +`, pid, name, org, role) +} diff --git a/provider/terraform/tests/resource_google_project_iam_custom_role_test.go b/provider/terraform/tests/resource_google_project_iam_custom_role_test.go new file mode 100644 index 000000000000..f0e396ca0a30 --- /dev/null +++ b/provider/terraform/tests/resource_google_project_iam_custom_role_test.go @@ -0,0 +1,177 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccProjectIamCustomRole_basic(t *testing.T) { + t.Parallel() + + roleId := "tfIamCustomRole" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleProjectIamCustomRoleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleProjectIamCustomRole_basic(roleId), + Check: resource.TestCheckResourceAttr("google_project_iam_custom_role.foo", "stage", "GA"), + }, + { + ResourceName: "google_project_iam_custom_role.foo", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccCheckGoogleProjectIamCustomRole_update(roleId), + }, + { + ResourceName: "google_project_iam_custom_role.foo", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccProjectIamCustomRole_undelete(t *testing.T) { + t.Parallel() + + roleId := "tfIamCustomRole" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleProjectIamCustomRoleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleProjectIamCustomRole_basic(roleId), + Check: resource.TestCheckResourceAttr("google_project_iam_custom_role.foo", "deleted", "false"), + }, + { + ResourceName: "google_project_iam_custom_role.foo", + ImportState: true, + ImportStateVerify: true, + }, + // Soft-delete + { + Config: testAccCheckGoogleProjectIamCustomRole_deleted(roleId), + Check: resource.TestCheckResourceAttr("google_project_iam_custom_role.foo", "deleted", "true"), + }, + { + ResourceName: "google_project_iam_custom_role.foo", + ImportState: true, + ImportStateVerify: true, + }, + // Undelete + { + Config: testAccCheckGoogleProjectIamCustomRole_basic(roleId), + Check: resource.TestCheckResourceAttr("google_project_iam_custom_role.foo", "deleted", "false"), + }, + { + ResourceName: "google_project_iam_custom_role.foo", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccProjectIamCustomRole_createAfterDestroy(t *testing.T) { + t.Parallel() + + roleId := "tfIamCustomRole" + acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleProjectIamCustomRoleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleProjectIamCustomRole_basic(roleId), + }, + { + ResourceName: "google_project_iam_custom_role.foo", + ImportState: true, + ImportStateVerify: true, + }, + // Destroy resources + { + Config: " ", + Destroy: true, + }, + // Re-create with no existing state + { + Config: testAccCheckGoogleProjectIamCustomRole_basic(roleId), + }, + { + ResourceName: "google_project_iam_custom_role.foo", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckGoogleProjectIamCustomRoleDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_project_iam_custom_role" { + continue + } + + role, err := config.clientIAM.Projects.Roles.Get(rs.Primary.ID).Do() + + if err != nil { + return err + } + + if !role.Deleted { + return fmt.Errorf("Iam custom role still exists") + } + + } + + return nil +} + +func testAccCheckGoogleProjectIamCustomRole_basic(roleId string) string { + return fmt.Sprintf(` +resource "google_project_iam_custom_role" "foo" { + role_id = "%s" + title = "My Custom Role" + description = "foo" + permissions = ["iam.roles.list"] +} +`, roleId) +} + +func testAccCheckGoogleProjectIamCustomRole_deleted(roleId string) string { + return fmt.Sprintf(` +resource "google_project_iam_custom_role" "foo" { + role_id = "%s" + title = "My Custom Role" + description = "foo" + permissions = ["iam.roles.list"] + deleted = true +} +`, roleId) +} + +func testAccCheckGoogleProjectIamCustomRole_update(roleId string) string { + return fmt.Sprintf(` +resource "google_project_iam_custom_role" "foo" { + role_id = "%s" + title = "My Custom Role Updated" + description = "bar" + permissions = ["iam.roles.list", "iam.roles.create", "iam.roles.delete"] + stage = "BETA" +} +`, roleId) +} diff --git a/provider/terraform/tests/resource_google_project_iam_member_test.go b/provider/terraform/tests/resource_google_project_iam_member_test.go new file mode 100644 index 000000000000..df280216ad25 --- /dev/null +++ b/provider/terraform/tests/resource_google_project_iam_member_test.go @@ -0,0 +1,169 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func projectIamMemberImportStep(resourceName, pid, role, member string) resource.TestStep { + return resource.TestStep{ + ResourceName: resourceName, + ImportStateId: fmt.Sprintf("%s %s %s", pid, role, member), + ImportState: true, + ImportStateVerify: true, + } +} + +// Test that an IAM binding can be applied to a project +func TestAccProjectIamMember_basic(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + resourceName := "google_project_iam_member.acceptance" + role := "roles/compute.instanceAdmin" + member := "user:admin@hashicorptest.com" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + // Apply an IAM binding + { + Config: testAccProjectAssociateMemberBasic(pid, pname, org, role, member), + }, + projectIamMemberImportStep(resourceName, pid, role, member), + }, + }) +} + +// Test that multiple IAM bindings can be applied to a project +func TestAccProjectIamMember_multiple(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + skipIfEnvNotSet(t, "GOOGLE_ORG") + + pid := "terraform-" + acctest.RandString(10) + resourceName := "google_project_iam_member.acceptance" + resourceName2 := "google_project_iam_member.multiple" + role := "roles/compute.instanceAdmin" + member := "user:admin@hashicorptest.com" + member2 := "user:paddy@hashicorp.com" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + // Apply an IAM binding + { + Config: testAccProjectAssociateMemberBasic(pid, pname, org, role, member), + }, + projectIamMemberImportStep(resourceName, pid, role, member), + + // Apply another IAM binding + { + Config: testAccProjectAssociateMemberMultiple(pid, pname, org, role, member, role, member2), + }, + projectIamMemberImportStep(resourceName, pid, role, member), + projectIamMemberImportStep(resourceName2, pid, role, member2), + }, + }) +} + +// Test that an IAM binding can be removed from a project +func TestAccProjectIamMember_remove(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + skipIfEnvNotSet(t, "GOOGLE_ORG") + + pid := "terraform-" + acctest.RandString(10) + resourceName := "google_project_iam_member.acceptance" + role := "roles/compute.instanceAdmin" + member := "user:admin@hashicorptest.com" + member2 := "user:paddy@hashicorp.com" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + + // Apply multiple IAM bindings + { + Config: testAccProjectAssociateMemberMultiple(pid, pname, org, role, member, role, member2), + }, + projectIamMemberImportStep(resourceName, pid, role, member), + projectIamMemberImportStep(resourceName, pid, role, member2), + + // Remove the bindings + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + }, + }) +} + +func testAccProjectAssociateMemberBasic(pid, name, org, role, member string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +resource "google_project_iam_member" "acceptance" { + project = "${google_project.acceptance.project_id}" + role = "%s" + member = "%s" +} +`, pid, name, org, role, member) +} + +func testAccProjectAssociateMemberMultiple(pid, name, org, role, member, role2, member2 string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +resource "google_project_iam_member" "acceptance" { + project = "${google_project.acceptance.project_id}" + role = "%s" + member = "%s" +} + +resource "google_project_iam_member" "multiple" { + project = "${google_project.acceptance.project_id}" + role = "%s" + member = "%s" +} +`, pid, name, org, role, member, role2, member2) +} diff --git a/provider/terraform/tests/resource_google_project_iam_policy_test.go b/provider/terraform/tests/resource_google_project_iam_policy_test.go new file mode 100644 index 000000000000..a09460eda0ab --- /dev/null +++ b/provider/terraform/tests/resource_google_project_iam_policy_test.go @@ -0,0 +1,780 @@ +package google + +import ( + "encoding/json" + "fmt" + "reflect" + "sort" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/cloudresourcemanager/v1" +) + +func TestSubtractIamPolicy(t *testing.T) { + table := []struct { + a *cloudresourcemanager.Policy + b *cloudresourcemanager.Policy + expect cloudresourcemanager.Policy + }{ + { + a: &cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Role: "a", + Members: []string{ + "1", + "2", + }, + }, + { + Role: "b", + Members: []string{ + "1", + "2", + }, + }, + }, + }, + b: &cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Role: "a", + Members: []string{ + "3", + "4", + }, + }, + { + Role: "b", + Members: []string{ + "1", + "2", + }, + }, + }, + }, + expect: cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Role: "a", + Members: []string{ + "1", + "2", + }, + }, + }, + }, + }, + { + a: &cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Role: "a", + Members: []string{ + "1", + "2", + }, + }, + { + Role: "b", + Members: []string{ + "1", + "2", + }, + }, + }, + }, + b: &cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Role: "a", + Members: []string{ + "1", + "2", + }, + }, + { + Role: "b", + Members: []string{ + "1", + "2", + }, + }, + }, + }, + expect: cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{}, + }, + }, + { + a: &cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Role: "a", + Members: []string{ + "1", + "2", + "3", + }, + }, + { + Role: "b", + Members: []string{ + "1", + "2", + "3", + }, + }, + }, + }, + b: &cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Role: "a", + Members: []string{ + "1", + "3", + }, + }, + { + Role: "b", + Members: []string{ + "1", + "2", + "3", + }, + }, + }, + }, + expect: cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Role: "a", + Members: []string{ + "2", + }, + }, + }, + }, + }, + { + a: &cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Role: "a", + Members: []string{ + "1", + "2", + "3", + }, + }, + { + Role: "b", + Members: []string{ + "1", + "2", + "3", + }, + }, + }, + }, + b: &cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Role: "a", + Members: []string{ + "1", + "2", + "3", + }, + }, + { + Role: "b", + Members: []string{ + "1", + "2", + "3", + }, + }, + }, + }, + expect: cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{}, + }, + }, + } + + for _, test := range table { + c := subtractIamPolicy(test.a, test.b) + sort.Sort(sortableBindings(c.Bindings)) + for i, _ := range c.Bindings { + sort.Strings(c.Bindings[i].Members) + } + + if !reflect.DeepEqual(derefBindings(c.Bindings), derefBindings(test.expect.Bindings)) { + t.Errorf("\ngot %+v\nexpected %+v", derefBindings(c.Bindings), derefBindings(test.expect.Bindings)) + } + } +} + +// Test that an IAM policy can be applied to a project +func TestAccProjectIamPolicy_basic(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + resource.TestStep{ + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + // Apply an IAM policy from a data source. The application + // merges policies, so we validate the expected state. + resource.TestStep{ + Config: testAccProjectAssociatePolicyBasic(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectIamPolicyIsMerged("google_project_iam_policy.acceptance", "data.google_iam_policy.admin", pid), + ), + }, + resource.TestStep{ + ResourceName: "google_project_iam_policy.acceptance", + ImportState: true, + // Skipping the normal "ImportStateVerify" - Unfortunately, it's not + // really possible to make the imported policy match exactly, since + // the policy depends on the service account being used to create the + // project. + }, + // Finally, remove the custom IAM policy from config and apply, then + // confirm that the project is in its original state. + resource.TestStep{ + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + }, + }) +} + +// Test that an IAM policy can be applied to a project when no project is set in the resource +func TestAccProjectIamPolicy_defaultProject(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + resource.TestStep{ + Config: testAccProjectDefaultAssociatePolicyBasic(), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(getTestProjectFromEnv()), + ), + }, + // Apply an IAM policy from a data source. The application + // merges policies, so we validate the expected state. + resource.TestStep{ + Config: testAccProjectDefaultAssociatePolicyBasic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectIamPolicyIsMerged("google_project_iam_policy.acceptance", "data.google_iam_policy.admin", getTestProjectFromEnv()), + ), + }, + }, + }) +} + +// Test that a non-collapsed IAM policy doesn't perpetually diff +func TestAccProjectIamPolicy_expanded(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccProjectAssociatePolicyExpanded(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectIamPolicyExists("google_project_iam_policy.acceptance", "data.google_iam_policy.expanded", pid), + ), + }, + }, + }) +} + +func getStatePrimaryResource(s *terraform.State, res, expectedID string) (*terraform.InstanceState, error) { + // Get the project resource + resource, ok := s.RootModule().Resources[res] + if !ok { + return nil, fmt.Errorf("Not found: %s", res) + } + if resource.Primary.Attributes["id"] != expectedID && expectedID != "" { + return nil, fmt.Errorf("Expected project %q to match ID %q in state", resource.Primary.ID, expectedID) + } + return resource.Primary, nil +} + +func getGoogleProjectIamPolicyFromResource(resource *terraform.InstanceState) (cloudresourcemanager.Policy, error) { + var p cloudresourcemanager.Policy + ps, ok := resource.Attributes["policy_data"] + if !ok { + return p, fmt.Errorf("Resource %q did not have a 'policy_data' attribute. Attributes were %#v", resource.ID, resource.Attributes) + } + if err := json.Unmarshal([]byte(ps), &p); err != nil { + return p, fmt.Errorf("Could not unmarshal %s:\n: %v", ps, err) + } + return p, nil +} + +func getGoogleProjectIamPolicyFromState(s *terraform.State, res, expectedID string) (cloudresourcemanager.Policy, error) { + project, err := getStatePrimaryResource(s, res, expectedID) + if err != nil { + return cloudresourcemanager.Policy{}, err + } + return getGoogleProjectIamPolicyFromResource(project) +} + +func compareBindings(a, b []*cloudresourcemanager.Binding) bool { + a = mergeBindings(a) + b = mergeBindings(b) + sort.Sort(sortableBindings(a)) + sort.Sort(sortableBindings(b)) + return reflect.DeepEqual(derefBindings(a), derefBindings(b)) +} + +func testAccCheckGoogleProjectIamPolicyExists(projectRes, policyRes, pid string) resource.TestCheckFunc { + return func(s *terraform.State) error { + projectPolicy, err := getGoogleProjectIamPolicyFromState(s, projectRes, pid) + if err != nil { + return fmt.Errorf("Error retrieving IAM policy for project from state: %s", err) + } + policyPolicy, err := getGoogleProjectIamPolicyFromState(s, policyRes, "") + if err != nil { + return fmt.Errorf("Error retrieving IAM policy for data_policy from state: %s", err) + } + + // The bindings in both policies should be identical + if !compareBindings(projectPolicy.Bindings, policyPolicy.Bindings) { + return fmt.Errorf("Project and data source policies do not match: project policy is %+v, data resource policy is %+v", derefBindings(projectPolicy.Bindings), derefBindings(policyPolicy.Bindings)) + } + return nil + } +} + +func testAccCheckGoogleProjectIamPolicyIsMerged(projectRes, policyRes, pid string) resource.TestCheckFunc { + return func(s *terraform.State) error { + err := testAccCheckGoogleProjectIamPolicyExists(projectRes, policyRes, pid)(s) + if err != nil { + return err + } + + projectPolicy, err := getGoogleProjectIamPolicyFromState(s, projectRes, pid) + if err != nil { + return fmt.Errorf("Error retrieving IAM policy for project from state: %s", err) + } + + // Merge the project policy in Terraform state with the policy the project had before the config was applied + var expected []*cloudresourcemanager.Binding + expected = append(expected, originalPolicy.Bindings...) + expected = append(expected, projectPolicy.Bindings...) + expected = mergeBindings(expected) + + // Retrieve the actual policy from the project + c := testAccProvider.Meta().(*Config) + actual, err := getProjectIamPolicy(pid, c) + if err != nil { + return fmt.Errorf("Failed to retrieve IAM Policy for project %q: %s", pid, err) + } + // The bindings should match, indicating the policy was successfully applied and merged + if !compareBindings(actual.Bindings, expected) { + return fmt.Errorf("Actual and expected project policies do not match: actual policy is %+v, expected policy is %+v", derefBindings(actual.Bindings), derefBindings(expected)) + } + + return nil + } +} + +func TestIamRolesToMembersBinding(t *testing.T) { + table := []struct { + expect []*cloudresourcemanager.Binding + input map[string]map[string]bool + }{ + { + expect: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{ + "member-1", + "member-2", + }, + }, + }, + input: map[string]map[string]bool{ + "role-1": map[string]bool{ + "member-1": true, + "member-2": true, + }, + }, + }, + { + expect: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{ + "member-1", + "member-2", + }, + }, + }, + input: map[string]map[string]bool{ + "role-1": map[string]bool{ + "member-1": true, + "member-2": true, + }, + }, + }, + { + expect: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{}, + }, + }, + input: map[string]map[string]bool{ + "role-1": map[string]bool{}, + }, + }, + } + + for _, test := range table { + got := rolesToMembersBinding(test.input) + + sort.Sort(sortableBindings(got)) + for i, _ := range got { + sort.Strings(got[i].Members) + } + + if !reflect.DeepEqual(derefBindings(got), derefBindings(test.expect)) { + t.Errorf("got %+v, expected %+v", derefBindings(got), derefBindings(test.expect)) + } + } +} +func TestIamRolesToMembersMap(t *testing.T) { + table := []struct { + input []*cloudresourcemanager.Binding + expect map[string]map[string]bool + }{ + { + input: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{ + "member-1", + "member-2", + }, + }, + }, + expect: map[string]map[string]bool{ + "role-1": map[string]bool{ + "member-1": true, + "member-2": true, + }, + }, + }, + { + input: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{ + "member-1", + "member-2", + "member-1", + "member-2", + }, + }, + }, + expect: map[string]map[string]bool{ + "role-1": map[string]bool{ + "member-1": true, + "member-2": true, + }, + }, + }, + { + input: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + }, + }, + expect: map[string]map[string]bool{ + "role-1": map[string]bool{}, + }, + }, + } + + for _, test := range table { + got := rolesToMembersMap(test.input) + if !reflect.DeepEqual(got, test.expect) { + t.Errorf("got %+v, expected %+v", got, test.expect) + } + } +} + +func TestIamMergeBindings(t *testing.T) { + table := []struct { + input []*cloudresourcemanager.Binding + expect []cloudresourcemanager.Binding + }{ + { + input: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{ + "member-1", + "member-2", + }, + }, + { + Role: "role-1", + Members: []string{ + "member-3", + }, + }, + }, + expect: []cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{ + "member-1", + "member-2", + "member-3", + }, + }, + }, + }, + { + input: []*cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{ + "member-3", + "member-4", + }, + }, + { + Role: "role-1", + Members: []string{ + "member-2", + "member-1", + }, + }, + { + Role: "role-2", + Members: []string{ + "member-1", + }, + }, + { + Role: "role-1", + Members: []string{ + "member-5", + }, + }, + { + Role: "role-3", + Members: []string{ + "member-1", + }, + }, + { + Role: "role-2", + Members: []string{ + "member-2", + }, + }, + {Role: "empty-role", Members: []string{}}, + }, + expect: []cloudresourcemanager.Binding{ + { + Role: "role-1", + Members: []string{ + "member-1", + "member-2", + "member-3", + "member-4", + "member-5", + }, + }, + { + Role: "role-2", + Members: []string{ + "member-1", + "member-2", + }, + }, + { + Role: "role-3", + Members: []string{ + "member-1", + }, + }, + }, + }, + } + + for _, test := range table { + got := mergeBindings(test.input) + sort.Sort(sortableBindings(got)) + for i, _ := range got { + sort.Strings(got[i].Members) + } + + if !reflect.DeepEqual(derefBindings(got), test.expect) { + t.Errorf("\ngot %+v\nexpected %+v", derefBindings(got), test.expect) + } + } +} + +func derefBindings(b []*cloudresourcemanager.Binding) []cloudresourcemanager.Binding { + db := make([]cloudresourcemanager.Binding, len(b)) + + for i, v := range b { + db[i] = *v + sort.Strings(db[i].Members) + } + return db +} + +// Confirm that a project has an IAM policy with at least 1 binding +func testAccProjectExistingPolicy(pid string) resource.TestCheckFunc { + return func(s *terraform.State) error { + c := testAccProvider.Meta().(*Config) + var err error + originalPolicy, err = getProjectIamPolicy(pid, c) + if err != nil { + return fmt.Errorf("Failed to retrieve IAM Policy for project %q: %s", pid, err) + } + if len(originalPolicy.Bindings) == 0 { + return fmt.Errorf("Refuse to run test against project with zero IAM Bindings. This is likely an error in the test code that is not properly identifying the IAM policy of a project.") + } + return nil + } +} + +func testAccProjectDefaultAssociatePolicyBasic() string { + return fmt.Sprintf(` +resource "google_project_iam_policy" "acceptance" { + policy_data = "${data.google_iam_policy.admin.policy_data}" +} +data "google_iam_policy" "admin" { + binding { + role = "roles/storage.objectViewer" + members = [ + "user:evanbrown@google.com", + ] + } + binding { + role = "roles/compute.instanceAdmin" + members = [ + "user:evanbrown@google.com", + "user:evandbrown@gmail.com", + ] + } +} +`) +} + +func testAccProjectAssociatePolicyBasic(pid, name, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} +resource "google_project_iam_policy" "acceptance" { + project = "${google_project.acceptance.id}" + policy_data = "${data.google_iam_policy.admin.policy_data}" +} +data "google_iam_policy" "admin" { + binding { + role = "roles/storage.objectViewer" + members = [ + "user:evanbrown@google.com", + ] + } + binding { + role = "roles/compute.instanceAdmin" + members = [ + "user:evanbrown@google.com", + "user:evandbrown@gmail.com", + ] + } +} +`, pid, name, org) +} + +func testAccProject_createWithoutOrg(pid, name string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" +}`, pid, name) +} + +func testAccProject_create(pid, name, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +}`, pid, name, org) +} + +func testAccProject_createBilling(pid, name, org, billing string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" +}`, pid, name, org, billing) +} + +func testAccProjectAssociatePolicyExpanded(pid, name, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} +resource "google_project_iam_policy" "acceptance" { + project = "${google_project.acceptance.id}" + policy_data = "${data.google_iam_policy.expanded.policy_data}" + authoritative = false +} +data "google_iam_policy" "expanded" { + binding { + role = "roles/viewer" + members = [ + "user:paddy@carvers.co", + ] + } + + binding { + role = "roles/viewer" + members = [ + "user:paddy@hashicorp.com", + ] + } +}`, pid, name, org) +} diff --git a/provider/terraform/tests/resource_google_project_migrate_test.go b/provider/terraform/tests/resource_google_project_migrate_test.go new file mode 100644 index 000000000000..8aeff36404f0 --- /dev/null +++ b/provider/terraform/tests/resource_google_project_migrate_test.go @@ -0,0 +1,70 @@ +package google + +import ( + "testing" + + "github.com/hashicorp/terraform/terraform" +) + +func TestGoogleProjectMigrateState(t *testing.T) { + cases := map[string]struct { + StateVersion int + Attributes map[string]string + Expected map[string]string + Meta interface{} + }{ + "deprecate policy_data and support creation/deletion": { + StateVersion: 0, + Attributes: map[string]string{}, + Expected: map[string]string{ + "project_id": "test-project", + "skip_delete": "true", + }, + Meta: &Config{}, + }, + } + + for tn, tc := range cases { + is := &terraform.InstanceState{ + ID: "test-project", + Attributes: tc.Attributes, + } + is, err := resourceGoogleProjectMigrateState( + tc.StateVersion, is, tc.Meta) + + if err != nil { + t.Fatalf("bad: %s, err: %#v", tn, err) + } + + for k, v := range tc.Expected { + if is.Attributes[k] != v { + t.Fatalf( + "bad: %s\n\n expected: %#v -> %#v\n got: %#v -> %#v\n in: %#v", + tn, k, v, k, is.Attributes[k], is.Attributes) + } + } + } +} + +func TestGoogleProjectMigrateState_empty(t *testing.T) { + var is *terraform.InstanceState + var meta *Config + + // should handle nil + is, err := resourceGoogleProjectMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } + if is != nil { + t.Fatalf("expected nil instancestate, got: %#v", is) + } + + // should handle non-nil but empty + is = &terraform.InstanceState{} + is, err = resourceGoogleProjectMigrateState(0, is, meta) + + if err != nil { + t.Fatalf("err: %#v", err) + } +} diff --git a/provider/terraform/tests/resource_google_project_organization_policy_test.go b/provider/terraform/tests/resource_google_project_organization_policy_test.go new file mode 100644 index 000000000000..12067285c244 --- /dev/null +++ b/provider/terraform/tests/resource_google_project_organization_policy_test.go @@ -0,0 +1,354 @@ +package google + +import ( + "fmt" + "reflect" + "sort" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/cloudresourcemanager/v1" +) + +/* +Tests for `google_project_organization_policy` + +These are *not* run in parallel, as they all use the same project +and I end up with 409 Conflict errors from the API when they are +run in parallel. +*/ + +func TestAccProjectOrganizationPolicy_boolean(t *testing.T) { + projectId := getTestProjectFromEnv() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleProjectOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + // Test creation of an enforced boolean policy + Config: testAccProjectOrganizationPolicy_boolean(projectId, true), + Check: testAccCheckGoogleProjectOrganizationBooleanPolicy("bool", true), + }, + { + // Test update from enforced to not + Config: testAccProjectOrganizationPolicy_boolean(projectId, false), + Check: testAccCheckGoogleProjectOrganizationBooleanPolicy("bool", false), + }, + { + Config: " ", + Destroy: true, + }, + { + // Test creation of a not enforced boolean policy + Config: testAccProjectOrganizationPolicy_boolean(projectId, false), + Check: testAccCheckGoogleProjectOrganizationBooleanPolicy("bool", false), + }, + { + // Test update from not enforced to enforced + Config: testAccProjectOrganizationPolicy_boolean(projectId, true), + Check: testAccCheckGoogleProjectOrganizationBooleanPolicy("bool", true), + }, + }, + }) +} + +func TestAccProjectOrganizationPolicy_list_allowAll(t *testing.T) { + projectId := getTestProjectFromEnv() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleProjectOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccProjectOrganizationPolicy_list_allowAll(projectId), + Check: testAccCheckGoogleProjectOrganizationListPolicyAll("list", "ALLOW"), + }, + }, + }) +} + +func TestAccProjectOrganizationPolicy_list_allowSome(t *testing.T) { + project := getTestProjectFromEnv() + canonicalProject := canonicalProjectId(project) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleProjectOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccProjectOrganizationPolicy_list_allowSome(project), + Check: testAccCheckGoogleProjectOrganizationListPolicyAllowedValues("list", []string{canonicalProject}), + }, + }, + }) +} + +func TestAccProjectOrganizationPolicy_list_denySome(t *testing.T) { + projectId := getTestProjectFromEnv() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleProjectOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccProjectOrganizationPolicy_list_denySome(projectId), + Check: testAccCheckGoogleProjectOrganizationListPolicyDeniedValues("list", DENIED_ORG_POLICIES), + }, + }, + }) +} + +func TestAccProjectOrganizationPolicy_list_update(t *testing.T) { + projectId := getTestProjectFromEnv() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleProjectOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccProjectOrganizationPolicy_list_allowAll(projectId), + Check: testAccCheckGoogleProjectOrganizationListPolicyAll("list", "ALLOW"), + }, + { + Config: testAccProjectOrganizationPolicy_list_denySome(projectId), + Check: testAccCheckGoogleProjectOrganizationListPolicyDeniedValues("list", DENIED_ORG_POLICIES), + }, + }, + }) +} + +func TestAccProjectOrganizationPolicy_restore_defaultTrue(t *testing.T) { + projectId := getTestProjectFromEnv() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGoogleProjectOrganizationPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccProjectOrganizationPolicy_restore_defaultTrue(projectId), + Check: getGoogleProjectOrganizationRestoreDefaultTrue("restore", &cloudresourcemanager.RestoreDefault{}), + }, + }, + }) +} + +func testAccCheckGoogleProjectOrganizationPolicyDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_project_organization_policy" { + continue + } + + projectId := canonicalProjectId(rs.Primary.Attributes["project"]) + constraint := canonicalOrgPolicyConstraint(rs.Primary.Attributes["constraint"]) + policy, err := config.clientResourceManager.Projects.GetOrgPolicy(projectId, &cloudresourcemanager.GetOrgPolicyRequest{ + Constraint: constraint, + }).Do() + + if err != nil { + return err + } + + if policy.ListPolicy != nil || policy.BooleanPolicy != nil { + return fmt.Errorf("Org policy with constraint '%s' hasn't been cleared", constraint) + } + } + return nil +} + +func testAccCheckGoogleProjectOrganizationBooleanPolicy(n string, enforced bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + policy, err := getGoogleProjectOrganizationPolicyTestResource(s, n) + if err != nil { + return err + } + + if policy.BooleanPolicy.Enforced != enforced { + return fmt.Errorf("Expected boolean policy enforcement to be '%t', got '%t'", enforced, policy.BooleanPolicy.Enforced) + } + + return nil + } +} + +func testAccCheckGoogleProjectOrganizationListPolicyAll(n, policyType string) resource.TestCheckFunc { + return func(s *terraform.State) error { + policy, err := getGoogleProjectOrganizationPolicyTestResource(s, n) + if err != nil { + return err + } + + if policy.ListPolicy == nil { + return nil + } + + if len(policy.ListPolicy.AllowedValues) > 0 || len(policy.ListPolicy.DeniedValues) > 0 { + return fmt.Errorf("The `values` field shouldn't be set") + } + + if policy.ListPolicy.AllValues != policyType { + return fmt.Errorf("The list policy should %s all values", policyType) + } + + return nil + } +} + +func testAccCheckGoogleProjectOrganizationListPolicyAllowedValues(n string, values []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + policy, err := getGoogleProjectOrganizationPolicyTestResource(s, n) + if err != nil { + return err + } + + sort.Strings(policy.ListPolicy.AllowedValues) + sort.Strings(values) + if !reflect.DeepEqual(policy.ListPolicy.AllowedValues, values) { + return fmt.Errorf("Expected the list policy to allow '%s', instead allowed '%s'", values, policy.ListPolicy.AllowedValues) + } + + return nil + } +} + +func testAccCheckGoogleProjectOrganizationListPolicyDeniedValues(n string, values []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + policy, err := getGoogleProjectOrganizationPolicyTestResource(s, n) + if err != nil { + return err + } + + sort.Strings(policy.ListPolicy.DeniedValues) + sort.Strings(values) + if !reflect.DeepEqual(policy.ListPolicy.DeniedValues, values) { + return fmt.Errorf("Expected the list policy to deny '%s', instead denied '%s'", values, policy.ListPolicy.DeniedValues) + } + + return nil + } +} + +func getGoogleProjectOrganizationRestoreDefaultTrue(n string, policyDefault *cloudresourcemanager.RestoreDefault) resource.TestCheckFunc { + return func(s *terraform.State) error { + + policy, err := getGoogleProjectOrganizationPolicyTestResource(s, n) + if err != nil { + return err + } + + if !reflect.DeepEqual(policy.RestoreDefault, policyDefault) { + return fmt.Errorf("Expected the restore default '%s', instead denied, %s", policyDefault, policy.RestoreDefault) + } + + return nil + } +} + +func getGoogleProjectOrganizationPolicyTestResource(s *terraform.State, n string) (*cloudresourcemanager.OrgPolicy, error) { + rn := "google_project_organization_policy." + n + rs, ok := s.RootModule().Resources[rn] + if !ok { + return nil, fmt.Errorf("Not found: %s", rn) + } + + if rs.Primary.ID == "" { + return nil, fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + projectId := canonicalProjectId(rs.Primary.Attributes["project"]) + + return config.clientResourceManager.Projects.GetOrgPolicy(projectId, &cloudresourcemanager.GetOrgPolicyRequest{ + Constraint: rs.Primary.Attributes["constraint"], + }).Do() +} + +func testAccProjectOrganizationPolicy_boolean(pid string, enforced bool) string { + return fmt.Sprintf(` +resource "google_project_organization_policy" "bool" { + project = "%s" + constraint = "constraints/compute.disableSerialPortAccess" + + boolean_policy { + enforced = %t + } +} +`, pid, enforced) +} + +func testAccProjectOrganizationPolicy_list_allowAll(pid string) string { + return fmt.Sprintf(` +resource "google_project_organization_policy" "list" { + project = "%s" + constraint = "constraints/serviceuser.services" + + list_policy { + allow { + all = true + } + } +} +`, pid) +} + +func testAccProjectOrganizationPolicy_list_allowSome(pid string) string { + return fmt.Sprintf(` + +resource "google_project_organization_policy" "list" { + project = "%s" + constraint = "constraints/compute.trustedImageProjects" + + list_policy { + allow { + values = ["projects/%s"] + } + } +} +`, pid, pid) +} + +func testAccProjectOrganizationPolicy_list_denySome(pid string) string { + return fmt.Sprintf(` + +resource "google_project_organization_policy" "list" { + project = "%s" + constraint = "constraints/serviceuser.services" + + list_policy { + deny { + values = [ + "doubleclicksearch.googleapis.com", + "replicapoolupdater.googleapis.com", + ] + } + } +} +`, pid) +} + +func testAccProjectOrganizationPolicy_restore_defaultTrue(pid string) string { + return fmt.Sprintf(` +resource "google_project_organization_policy" "restore" { + project = "%s" + constraint = "constraints/serviceuser.services" + + restore_policy { + default = true + } +} +`, pid) +} + +func canonicalProjectId(project string) string { + if strings.HasPrefix(project, "projects/") { + return project + } + return fmt.Sprintf("projects/%s", project) +} diff --git a/provider/terraform/tests/resource_google_project_service_test.go b/provider/terraform/tests/resource_google_project_service_test.go new file mode 100644 index 000000000000..8f4b0e08bf42 --- /dev/null +++ b/provider/terraform/tests/resource_google_project_service_test.go @@ -0,0 +1,189 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +// Test that services can be enabled and disabled on a project +func TestAccProjectService_basic(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + services := []string{"iam.googleapis.com", "cloudresourcemanager.googleapis.com"} + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccProjectService_basic(services, pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckProjectService(services, pid, true), + ), + }, + resource.TestStep{ + ResourceName: "google_project_service.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"disable_on_destroy"}, + }, + resource.TestStep{ + ResourceName: "google_project_service.test2", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"disable_on_destroy"}, + }, + // Use a separate TestStep rather than a CheckDestroy because we need the project to still exist. + resource.TestStep{ + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckProjectService(services, pid, false), + ), + }, + // Create services with disabling turned off. + resource.TestStep{ + Config: testAccProjectService_noDisable(services, pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckProjectService(services, pid, true), + ), + }, + // Check that services are still enabled even after the resources are deleted. + resource.TestStep{ + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckProjectService(services, pid, true), + ), + }, + }, + }) +} + +func TestAccProjectService_handleNotFound(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + service := "iam.googleapis.com" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccProjectService_handleNotFound(service, pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckProjectService([]string{service}, pid, true), + ), + }, + // Delete the project, implicitly deletes service, expect the plan to want to create the service again + resource.TestStep{ + Config: testAccProjectService_handleNotFoundNoProject(service, pid), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckProjectService(services []string, pid string, expectEnabled bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + apiServices, err := getApiServices(pid, config, map[string]struct{}{}) + if err != nil { + return fmt.Errorf("Error listing services for project %q: %v", pid, err) + } + + for _, expected := range services { + exists := false + for _, actual := range apiServices { + if expected == actual { + exists = true + } + } + if expectEnabled && !exists { + return fmt.Errorf("Expected service %s is not enabled server-side (found %v)", expected, apiServices) + } + if !expectEnabled && exists { + return fmt.Errorf("Expected disabled service %s is enabled server-side", expected) + } + } + + return nil + } +} + +func testAccProjectService_basic(services []string, pid, name, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +resource "google_project_service" "test" { + project = "${google_project.acceptance.project_id}" + service = "%s" +} + +resource "google_project_service" "test2" { + project = "${google_project.acceptance.project_id}" + service = "%s" +} +`, pid, name, org, services[0], services[1]) +} + +func testAccProjectService_noDisable(services []string, pid, name, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +resource "google_project_service" "test" { + project = "${google_project.acceptance.project_id}" + service = "%s" + disable_on_destroy = false +} + +resource "google_project_service" "test2" { + project = "${google_project.acceptance.project_id}" + service = "%s" + disable_on_destroy = false +} +`, pid, name, org, services[0], services[1]) +} + +func testAccProjectService_handleNotFound(service, pid, name, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +// by passing through locals, we break the dependency chain +// see terraform-provider-google#1292 +locals { + project_id = "${google_project.acceptance.project_id}" +} + +resource "google_project_service" "test" { + project = "${local.project_id}" + service = "%s" +} +`, pid, name, org, service) +} + +func testAccProjectService_handleNotFoundNoProject(service, pid string) string { + return fmt.Sprintf(` +resource "google_project_service" "test" { + project = "%s" + service = "%s" +} +`, pid, service) +} diff --git a/provider/terraform/tests/resource_google_project_services_test.go b/provider/terraform/tests/resource_google_project_services_test.go new file mode 100644 index 000000000000..80983106d03d --- /dev/null +++ b/provider/terraform/tests/resource_google_project_services_test.go @@ -0,0 +1,322 @@ +package google + +import ( + "bytes" + "fmt" + "log" + "reflect" + "sort" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +// Test that services can be enabled and disabled on a project +func TestAccProjectServices_basic(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + services1 := []string{"iam.googleapis.com", "cloudresourcemanager.googleapis.com"} + services2 := []string{"cloudresourcemanager.googleapis.com"} + oobService := "iam.googleapis.com" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project with some services + resource.TestStep{ + Config: testAccProjectAssociateServicesBasic(services1, pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testProjectServicesMatch(services1, pid), + ), + }, + // Update services to remove one + resource.TestStep{ + Config: testAccProjectAssociateServicesBasic(services2, pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testProjectServicesMatch(services2, pid), + ), + }, + // Add a service out-of-band and ensure it is removed + resource.TestStep{ + PreConfig: func() { + config := testAccProvider.Meta().(*Config) + enableService(oobService, pid, config) + }, + Config: testAccProjectAssociateServicesBasic(services2, pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testProjectServicesMatch(services2, pid), + ), + }, + resource.TestStep{ + ResourceName: "google_project_services.acceptance", + ImportState: true, + ImportStateId: pid, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"disable_on_destroy"}, + }, + }, + }) +} + +// Test that services are authoritative when a project has existing +// sevices not represented in config +func TestAccProjectServices_authoritative(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + services := []string{"cloudresourcemanager.googleapis.com"} + oobService := "iam.googleapis.com" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project with no services + resource.TestStep{ + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectExists("google_project.acceptance", pid), + ), + }, + // Add a service out-of-band, then apply a config that creates a service. + // It should remove the out-of-band service. + resource.TestStep{ + PreConfig: func() { + config := testAccProvider.Meta().(*Config) + enableService(oobService, pid, config) + }, + Config: testAccProjectAssociateServicesBasic(services, pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testProjectServicesMatch(services, pid), + ), + }, + }, + }) +} + +// Test that services are authoritative when a project has existing +// sevices, some which are represented in the config and others +// that are not +func TestAccProjectServices_authoritative2(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + oobServices := []string{"iam.googleapis.com", "cloudresourcemanager.googleapis.com"} + services := []string{"iam.googleapis.com"} + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project with no services + resource.TestStep{ + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectExists("google_project.acceptance", pid), + ), + }, + // Add a service out-of-band, then apply a config that creates a service. + // It should remove the out-of-band service. + resource.TestStep{ + PreConfig: func() { + config := testAccProvider.Meta().(*Config) + for _, s := range oobServices { + enableService(s, pid, config) + } + }, + Config: testAccProjectAssociateServicesBasic(services, pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testProjectServicesMatch(services, pid), + ), + }, + }, + }) +} + +// Test that services that can't be enabled on their own (such as dataproc-control.googleapis.com) +// don't end up causing diffs when they are enabled as a side-effect of a different service's +// enablement. +func TestAccProjectServices_ignoreUnenablableServices(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + billingId := getTestBillingAccountFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + services := []string{ + "dataproc.googleapis.com", + // The following services are enabled as a side-effect of dataproc's enablement + "storage-component.googleapis.com", + "deploymentmanager.googleapis.com", + "replicapool.googleapis.com", + "replicapoolupdater.googleapis.com", + "resourceviews.googleapis.com", + "compute.googleapis.com", + "container.googleapis.com", + "containerregistry.googleapis.com", + "storage-api.googleapis.com", + "pubsub.googleapis.com", + "oslogin.googleapis.com", + "bigquery-json.googleapis.com", + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccProjectAssociateServicesBasic_withBilling(services, pid, pname, org, billingId), + Check: resource.ComposeTestCheckFunc( + testProjectServicesMatch(services, pid), + ), + }, + }, + }) +} + +func TestAccProjectServices_pagination(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + billingId := getTestBillingAccountFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + + // we need at least 50 services (doesn't matter what they are) to exercise the + // pagination handling code. + services := []string{ + "actions.googleapis.com", + "appengine.googleapis.com", + "appengineflex.googleapis.com", + "bigquery-json.googleapis.com", + "bigquerydatatransfer.googleapis.com", + "bigtableadmin.googleapis.com", + "bigtabletableadmin.googleapis.com", + "cloudbuild.googleapis.com", + "clouderrorreporting.googleapis.com", + "cloudfunctions.googleapis.com", + "cloudiot.googleapis.com", + "cloudkms.googleapis.com", + "cloudmonitoring.googleapis.com", + "cloudresourcemanager.googleapis.com", + "cloudtrace.googleapis.com", + "compute.googleapis.com", + "container.googleapis.com", + "containerregistry.googleapis.com", + "dataflow.googleapis.com", + "dataproc.googleapis.com", + "datastore.googleapis.com", + "deploymentmanager.googleapis.com", + "dialogflow.googleapis.com", + "dns.googleapis.com", + "endpoints.googleapis.com", + "firebaserules.googleapis.com", + "firestore.googleapis.com", + "genomics.googleapis.com", + "iam.googleapis.com", + "language.googleapis.com", + "logging.googleapis.com", + "ml.googleapis.com", + "monitoring.googleapis.com", + "oslogin.googleapis.com", + "pubsub.googleapis.com", + "replicapool.googleapis.com", + "replicapoolupdater.googleapis.com", + "resourceviews.googleapis.com", + "runtimeconfig.googleapis.com", + "servicecontrol.googleapis.com", + "servicemanagement.googleapis.com", + "sourcerepo.googleapis.com", + "spanner.googleapis.com", + "speech.googleapis.com", + "sql-component.googleapis.com", + "storage-api.googleapis.com", + "storage-component.googleapis.com", + "storagetransfer.googleapis.com", + "testing.googleapis.com", + "toolresults.googleapis.com", + "translate.googleapis.com", + "videointelligence.googleapis.com", + "vision.googleapis.com", + "zync.googleapis.com", + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccProjectAssociateServicesBasic_withBilling(services, pid, pname, org, billingId), + Check: resource.ComposeTestCheckFunc( + testProjectServicesMatch(services, pid), + ), + }, + }, + }) +} + +func testAccProjectAssociateServicesBasic(services []string, pid, name, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} +resource "google_project_services" "acceptance" { + project = "${google_project.acceptance.project_id}" + services = [%s] + disable_on_destroy = true +} +`, pid, name, org, testStringsToString(services)) +} + +func testAccProjectAssociateServicesBasic_withBilling(services []string, pid, name, org, billing string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" +} +resource "google_project_services" "acceptance" { + project = "${google_project.acceptance.project_id}" + services = [%s] + disable_on_destroy = false +} +`, pid, name, org, billing, testStringsToString(services)) +} + +func testProjectServicesMatch(services []string, pid string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + apiServices, err := getApiServices(pid, config, ignoreProjectServices) + if err != nil { + return fmt.Errorf("Error listing services for project %q: %v", pid, err) + } + + sort.Strings(services) + sort.Strings(apiServices) + if !reflect.DeepEqual(services, apiServices) { + return fmt.Errorf("Services in config (%v) do not exactly match services returned by API (%v)", services, apiServices) + } + + return nil + } +} + +func testStringsToString(s []string) string { + var b bytes.Buffer + for i, v := range s { + b.WriteString(fmt.Sprintf("\"%s\"", v)) + if i < len(s)-1 { + b.WriteString(",") + } + } + r := b.String() + log.Printf("[DEBUG]: Converted list of strings to %s", r) + return b.String() +} diff --git a/provider/terraform/tests/resource_google_project_test.go b/provider/terraform/tests/resource_google_project_test.go new file mode 100644 index 000000000000..dad6333d8d42 --- /dev/null +++ b/provider/terraform/tests/resource_google_project_test.go @@ -0,0 +1,576 @@ +package google + +import ( + "fmt" + "os" + "reflect" + "strconv" + "strings" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/cloudresourcemanager/v1" +) + +var ( + pname = "Terraform Acceptance Tests" + originalPolicy *cloudresourcemanager.Policy +) + +// Test that a Project resource can be created without an organization +func TestAccProject_createWithoutOrg(t *testing.T) { + t.Parallel() + + creds := multiEnvSearch(credsEnvVars) + if strings.Contains(creds, "iam.gserviceaccount.com") { + t.Skip("Service accounts cannot create projects without a parent. Requires user credentials.") + } + + pid := "terraform-" + acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // This step creates a new project + resource.TestStep{ + Config: testAccProject_createWithoutOrg(pid, pname), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectExists("google_project.acceptance", pid), + ), + }, + }, + }) +} + +// Test that a Project resource can be created and an IAM policy +// associated +func TestAccProject_create(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // This step creates a new project + resource.TestStep{ + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectExists("google_project.acceptance", pid), + ), + }, + }, + }) +} + +// Test that a Project resource can be created with an associated +// billing account +func TestAccProject_billing(t *testing.T) { + t.Parallel() + org := getTestOrgFromEnv(t) + skipIfEnvNotSet(t, "GOOGLE_BILLING_ACCOUNT_2") + billingId2 := os.Getenv("GOOGLE_BILLING_ACCOUNT_2") + billingId := getTestBillingAccountFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // This step creates a new project with a billing account + resource.TestStep{ + Config: testAccProject_createBilling(pid, pname, org, billingId), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, billingId), + ), + }, + // Make sure import supports billing account + resource.TestStep{ + ResourceName: "google_project.acceptance", + ImportState: true, + ImportStateVerify: true, + }, + // Update to a different billing account + resource.TestStep{ + Config: testAccProject_createBilling(pid, pname, org, billingId2), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, billingId2), + ), + }, + // Unlink the billing account + resource.TestStep{ + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, ""), + ), + }, + }, + }) +} + +// Test that a Project resource can be created with labels +func TestAccProject_labels(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccProject_labels(pid, pname, org, map[string]string{"test": "that"}), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectHasLabels("google_project.acceptance", pid, map[string]string{"test": "that"}), + ), + }, + // Make sure import supports labels + { + ResourceName: "google_project.acceptance", + ImportState: true, + ImportStateVerify: true, + }, + // update project with labels + { + Config: testAccProject_labels(pid, pname, org, map[string]string{"label": "label-value"}), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectExists("google_project.acceptance", pid), + testAccCheckGoogleProjectHasLabels("google_project.acceptance", pid, map[string]string{"label": "label-value"}), + ), + }, + // update project delete labels + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectExists("google_project.acceptance", pid), + testAccCheckGoogleProjectHasNoLabels("google_project.acceptance", pid), + ), + }, + }, + }) +} + +func TestAccProject_deleteDefaultNetwork(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + billingId := getTestBillingAccountFromEnv(t) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccProject_deleteDefaultNetwork(pid, pname, org, billingId), + }, + }, + }) +} + +func TestAccProject_parentFolder(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + folderDisplayName := "tf-test-" + acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccProject_parentFolder(pid, pname, folderDisplayName, org), + }, + }, + }) +} + +func TestAccProject_appEngineBasic(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := acctest.RandomWithPrefix("tf-test") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccProject_appEngineBasic(pid, org), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.name"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.url_dispatch_rule.#"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.code_bucket"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.default_hostname"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.default_bucket"), + ), + }, + resource.TestStep{ + ResourceName: "google_project.acceptance", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccProject_appEngineBasicWithBilling(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := acctest.RandomWithPrefix("tf-test") + billingId := getTestBillingAccountFromEnv(t) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccProject_appEngineBasicWithBilling(pid, org, billingId), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.name"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.url_dispatch_rule.#"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.code_bucket"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.default_hostname"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.default_bucket"), + ), + }, + resource.TestStep{ + ResourceName: "google_project.acceptance", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccProject_appEngineUpdate(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := acctest.RandomWithPrefix("tf-test") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccProject_appEngineNoApp(pid, org), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleProjectExists("google_project.acceptance", pid), + ), + }, + { + Config: testAccProject_appEngineBasic(pid, org), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.name"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.url_dispatch_rule.#"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.code_bucket"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.default_hostname"), + resource.TestCheckResourceAttrSet("google_project.acceptance", "app_engine.0.default_bucket"), + ), + }, + resource.TestStep{ + ResourceName: "google_project.acceptance", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccProject_appEngineUpdate(pid, org), + }, + resource.TestStep{ + ResourceName: "google_project.acceptance", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccProject_appEngineFeatureSettings(t *testing.T) { + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := acctest.RandomWithPrefix("tf-test") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccProject_appEngineFeatureSettings(pid, org), + }, + resource.TestStep{ + ResourceName: "google_project.acceptance", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccProject_appEngineFeatureSettingsUpdate(pid, org), + }, + resource.TestStep{ + ResourceName: "google_project.acceptance", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckGoogleProjectExists(r, pid string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[r] + if !ok { + return fmt.Errorf("Not found: %s", r) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + if rs.Primary.ID != pid { + return fmt.Errorf("Expected project %q to match ID %q in state", pid, rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckGoogleProjectHasBillingAccount(r, pid, billingId string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[r] + if !ok { + return fmt.Errorf("Not found: %s", r) + } + + // State should match expected + if rs.Primary.Attributes["billing_account"] != billingId { + return fmt.Errorf("Billing ID in state (%s) does not match expected value (%s)", rs.Primary.Attributes["billing_account"], billingId) + } + + // Actual value in API should match state and expected + // Read the billing account + config := testAccProvider.Meta().(*Config) + ba, err := config.clientBilling.Projects.GetBillingInfo(prefixedProject(pid)).Do() + if err != nil { + return fmt.Errorf("Error reading billing account for project %q: %v", prefixedProject(pid), err) + } + if billingId != strings.TrimPrefix(ba.BillingAccountName, "billingAccounts/") { + return fmt.Errorf("Billing ID returned by API (%s) did not match expected value (%s)", ba.BillingAccountName, billingId) + } + return nil + } +} + +func testAccCheckGoogleProjectHasLabels(r, pid string, expected map[string]string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[r] + if !ok { + return fmt.Errorf("Not found: %s", r) + } + + // State should have the same number of labels + if rs.Primary.Attributes["labels.%"] != strconv.Itoa(len(expected)) { + return fmt.Errorf("Expected %d labels, got %s", len(expected), rs.Primary.Attributes["labels.%"]) + } + + // Actual value in API should match state and expected + config := testAccProvider.Meta().(*Config) + + found, err := config.clientResourceManager.Projects.Get(pid).Do() + if err != nil { + return err + } + + actual := found.Labels + if !reflect.DeepEqual(actual, expected) { + // Determine only the different attributes + for k, v := range expected { + if av, ok := actual[k]; ok && v == av { + delete(expected, k) + delete(actual, k) + } + } + + spewConf := spew.NewDefaultConfig() + spewConf.SortKeys = true + return fmt.Errorf( + "Labels not equivalent. Difference is shown below. Top is actual, bottom is expected."+ + "\n\n%s\n\n%s", + spewConf.Sdump(actual), spewConf.Sdump(expected), + ) + } + return nil + } +} + +func testAccCheckGoogleProjectHasNoLabels(r, pid string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[r] + if !ok { + return fmt.Errorf("Not found: %s", r) + } + + // State should have zero labels + if rs.Primary.Attributes["labels.%"] != "0" { + return fmt.Errorf("Expected 0 labels, got %s", rs.Primary.Attributes["labels.%"]) + } + + // Actual value in API should match state and expected + config := testAccProvider.Meta().(*Config) + + found, err := config.clientResourceManager.Projects.Get(pid).Do() + if err != nil { + return err + } + + spewConf := spew.NewDefaultConfig() + spewConf.SortKeys = true + if found.Labels != nil { + return fmt.Errorf("Labels should be empty. Actual \n%s", spewConf.Sdump(found.Labels)) + } + return nil + } +} + +func testAccProject_labels(pid, name, org string, labels map[string]string) string { + r := fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" + labels {`, pid, name, org) + + l := "" + for key, value := range labels { + l += fmt.Sprintf("%q = %q\n", key, value) + } + + l += fmt.Sprintf("}\n}") + return r + l +} + +func testAccProject_deleteDefaultNetwork(pid, name, org, billing string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" + billing_account = "%s" # requires billing to enable compute API + auto_create_network = false +}`, pid, name, org, billing) +} + +func testAccProject_parentFolder(pid, projectName, folderName, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + # ensures we can set both org_id and folder_id as long as only one is not empty. + org_id = "" + folder_id = "${google_folder.folder1.id}" +} + +resource "google_folder" "folder1" { + display_name = "%s" + parent = "organizations/%s" +} + +`, pid, projectName, folderName, org) +} + +func testAccProject_appEngineNoApp(pid, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +}`, pid, pid, org) +} + +func testAccProject_appEngineBasic(pid, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" + + app_engine { + auth_domain = "hashicorptest.com" + location_id = "us-central" + serving_status = "SERVING" + } +}`, pid, pid, org) +} + +func testAccProject_appEngineBasicWithBilling(pid, org, billing string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" + + billing_account = "%s" + + app_engine { + auth_domain = "hashicorptest.com" + location_id = "us-central" + serving_status = "SERVING" + } +}`, pid, pid, org, billing) +} + +func testAccProject_appEngineUpdate(pid, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" + + app_engine { + auth_domain = "tf-test.club" + location_id = "us-central" + serving_status = "USER_DISABLED" + } +}`, pid, pid, org) +} + +func testAccProject_appEngineFeatureSettings(pid, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" + + app_engine { + location_id = "us-central" + + feature_settings { + "split_health_checks" = true + } + } +}`, pid, pid, org) +} + +func testAccProject_appEngineFeatureSettingsUpdate(pid, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" + + app_engine { + location_id = "us-central" + + feature_settings { + "split_health_checks" = false + } + } +}`, pid, pid, org) +} + +func skipIfEnvNotSet(t *testing.T, envs ...string) { + for _, k := range envs { + if os.Getenv(k) == "" { + t.Skipf("Environment variable %s is not set", k) + } + } +} diff --git a/provider/terraform/tests/resource_google_service_account_iam_test.go b/provider/terraform/tests/resource_google_service_account_iam_test.go new file mode 100644 index 000000000000..1e183d870059 --- /dev/null +++ b/provider/terraform/tests/resource_google_service_account_iam_test.go @@ -0,0 +1,167 @@ +package google + +import ( + "fmt" + "reflect" + "sort" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccServiceAccountIamBinding(t *testing.T) { + t.Parallel() + + account := acctest.RandomWithPrefix("tf-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccServiceAccountIamBinding_basic(account), + Check: testAccCheckGoogleServiceAccountIam(account, "roles/viewer", []string{ + fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + }), + }, + { + ResourceName: "google_service_account_iam_binding.foo", + ImportStateId: fmt.Sprintf("%s %s", getServiceAccountCanonicalId(account), "roles/viewer"), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceAccountIamMember(t *testing.T) { + t.Parallel() + + account := acctest.RandomWithPrefix("tf-test") + identity := fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccServiceAccountIamMember_basic(account), + Check: testAccCheckGoogleServiceAccountIam(account, "roles/editor", []string{identity}), + }, + { + ResourceName: "google_service_account_iam_member.foo", + ImportStateId: fmt.Sprintf("%s %s %s", getServiceAccountCanonicalId(account), "roles/editor", identity), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccServiceAccountIamPolicy(t *testing.T) { + t.Parallel() + + account := acctest.RandomWithPrefix("tf-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccServiceAccountIamPolicy_basic(account), + Check: testAccCheckGoogleServiceAccountIam(account, "roles/owner", []string{ + fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + }), + }, + { + ResourceName: "google_service_account_iam_policy.foo", + ImportStateId: getServiceAccountCanonicalId(account), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckGoogleServiceAccountIam(account, role string, members []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + p, err := config.clientIAM.Projects.ServiceAccounts.GetIamPolicy(getServiceAccountCanonicalId(account)).Do() + if err != nil { + return err + } + + for _, binding := range p.Bindings { + if binding.Role == role { + sort.Strings(members) + sort.Strings(binding.Members) + + if reflect.DeepEqual(members, binding.Members) { + return nil + } + + return fmt.Errorf("Binding found but expected members is %v, got %v", members, binding.Members) + } + } + + return fmt.Errorf("No binding for role %q", role) + } +} + +func getServiceAccountCanonicalId(account string) string { + return fmt.Sprintf("projects/%s/serviceAccounts/%s@%s.iam.gserviceaccount.com", getTestProjectFromEnv(), account, getTestProjectFromEnv()) +} + +func testAccServiceAccountIamBinding_basic(account string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Iam Testing Account" +} + +resource "google_service_account_iam_binding" "foo" { + service_account_id = "${google_service_account.test_account.id}" + role = "roles/viewer" + members = ["serviceAccount:${google_service_account.test_account.email}"] +} +`, account) +} + +func testAccServiceAccountIamMember_basic(account string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Iam Testing Account" +} + +resource "google_service_account_iam_member" "foo" { + service_account_id = "${google_service_account.test_account.id}" + role = "roles/editor" + member = "serviceAccount:${google_service_account.test_account.email}" +} +`, account) +} + +func testAccServiceAccountIamPolicy_basic(account string) string { + return fmt.Sprintf(` +resource "google_service_account" "test_account" { + account_id = "%s" + display_name = "Iam Testing Account" +} + +data "google_iam_policy" "foo" { + binding { + role = "roles/owner" + + members = ["serviceAccount:${google_service_account.test_account.email}"] + } +} + +resource "google_service_account_iam_policy" "foo" { + service_account_id = "${google_service_account.test_account.id}" + policy_data = "${data.google_iam_policy.foo.policy_data}" +} +`, account) +} diff --git a/provider/terraform/tests/resource_google_service_account_key_test.go b/provider/terraform/tests/resource_google_service_account_key_test.go new file mode 100644 index 000000000000..66d1c77b3f93 --- /dev/null +++ b/provider/terraform/tests/resource_google_service_account_key_test.go @@ -0,0 +1,175 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +// Test that a service account key can be created and destroyed +func TestAccServiceAccountKey_basic(t *testing.T) { + t.Parallel() + + resourceName := "google_service_account_key.acceptance" + accountID := "a" + acctest.RandString(10) + displayName := "Terraform Test" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccServiceAccountKey(accountID, displayName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleServiceAccountKeyExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "public_key"), + resource.TestCheckResourceAttrSet(resourceName, "valid_after"), + resource.TestCheckResourceAttrSet(resourceName, "valid_before"), + resource.TestCheckResourceAttrSet(resourceName, "private_key"), + ), + }, + }, + }) +} + +func TestAccServiceAccountKey_fromEmail(t *testing.T) { + t.Parallel() + + resourceName := "google_service_account_key.acceptance" + accountID := "a" + acctest.RandString(10) + displayName := "Terraform Test" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccServiceAccountKey_fromEmail(accountID, displayName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleServiceAccountKeyExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "public_key"), + resource.TestCheckResourceAttrSet(resourceName, "valid_after"), + resource.TestCheckResourceAttrSet(resourceName, "valid_before"), + resource.TestCheckResourceAttrSet(resourceName, "private_key"), + ), + }, + }, + }) +} + +func TestAccServiceAccountKey_pgp(t *testing.T) { + t.Parallel() + resourceName := "google_service_account_key.acceptance" + accountID := "a" + acctest.RandString(10) + displayName := "Terraform Test" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccServiceAccountKey_pgp(accountID, displayName, testKeyPairPubKey1), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleServiceAccountKeyExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "public_key"), + resource.TestCheckResourceAttrSet(resourceName, "private_key_encrypted"), + resource.TestCheckResourceAttrSet(resourceName, "private_key_fingerprint"), + ), + }, + }, + }) +} + +func testAccCheckGoogleServiceAccountKeyExists(r string) resource.TestCheckFunc { + return func(s *terraform.State) error { + + rs, ok := s.RootModule().Resources[r] + if !ok { + return fmt.Errorf("Not found: %s", r) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + config := testAccProvider.Meta().(*Config) + + _, err := config.clientIAM.Projects.ServiceAccounts.Keys.Get(rs.Primary.ID).Do() + if err != nil { + return err + } + + return nil + } +} + +func testAccServiceAccountKey(account, name string) string { + return fmt.Sprintf(` +resource "google_service_account" "acceptance" { + account_id = "%s" + display_name = "%s" +} + +resource "google_service_account_key" "acceptance" { + service_account_id = "${google_service_account.acceptance.name}" + public_key_type = "TYPE_X509_PEM_FILE" +} +`, account, name) +} + +func testAccServiceAccountKey_fromEmail(account, name string) string { + return fmt.Sprintf(` +resource "google_service_account" "acceptance" { + account_id = "%s" + display_name = "%s" +} + +resource "google_service_account_key" "acceptance" { + service_account_id = "${google_service_account.acceptance.email}" + public_key_type = "TYPE_X509_PEM_FILE" +} +`, account, name) +} + +func testAccServiceAccountKey_pgp(account, name string, key string) string { + return fmt.Sprintf(` +resource "google_service_account" "acceptance" { + account_id = "%s" + display_name = "%s" +} + +resource "google_service_account_key" "acceptance" { + service_account_id = "${google_service_account.acceptance.name}" + public_key_type = "TYPE_X509_PEM_FILE" + pgp_key = < Date: Wed, 17 Oct 2018 15:16:14 -0700 Subject: [PATCH 42/56] Terraform: Add import tests for CloudIoTRegistry (#576) Merged PR #576. --- build/terraform | 2 +- .../resources/resource_cloudiot_registry.go | 23 +-- .../tests/resource_cloudiot_registry_test.go | 181 ++++++++++++++++++ 3 files changed, 192 insertions(+), 14 deletions(-) create mode 100644 provider/terraform/tests/resource_cloudiot_registry_test.go diff --git a/build/terraform b/build/terraform index d622ec466e68..bc31bef9f9ee 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit d622ec466e688b09c601d34f5ce1c7e5796dadf3 +Subproject commit bc31bef9f9ee8c3d2ba595e25db0c2741e6b26d3 diff --git a/provider/terraform/resources/resource_cloudiot_registry.go b/provider/terraform/resources/resource_cloudiot_registry.go index a7aa0d066bf4..ae850fcb5204 100644 --- a/provider/terraform/resources/resource_cloudiot_registry.go +++ b/provider/terraform/resources/resource_cloudiot_registry.go @@ -76,6 +76,7 @@ func resourceCloudIoTRegistry() *schema.Resource { }, "mqtt_config": &schema.Schema{ Type: schema.TypeMap, + Computed: true, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -90,6 +91,7 @@ func resourceCloudIoTRegistry() *schema.Resource { }, "http_config": &schema.Schema{ Type: schema.TypeMap, + Computed: true, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -233,6 +235,11 @@ func resourceCloudIoTRegistryCreate(d *schema.ResourceData, meta interface{}) er d.SetId("") return err } + + // If we infer project and region, they are never actually set so we set them here + d.Set("project", project) + d.Set("region", region) + return resourceCloudIoTRegistryRead(d, meta) } @@ -317,19 +324,9 @@ func resourceCloudIoTRegistryRead(d *schema.ResourceData, meta interface{}) erro } else { d.Set("state_notification_config", nil) } - // If no config exist for mqtt or http config default values are omitted. - mqttState := res.MqttConfig.MqttEnabledState - _, hasMqttConfig := d.GetOk("mqtt_config") - if mqttState != mqttEnabled || hasMqttConfig { - d.Set("mqtt_config", - map[string]string{"mqtt_enabled_state": mqttState}) - } - httpState := res.HttpConfig.HttpEnabledState - _, hasHttpConfig := d.GetOk("http_config") - if httpState != httpEnabled || hasHttpConfig { - d.Set("http_config", - map[string]string{"http_enabled_state": httpState}) - } + + d.Set("mqtt_config", map[string]string{"mqtt_enabled_state": res.MqttConfig.MqttEnabledState}) + d.Set("http_config", map[string]string{"http_enabled_state": res.HttpConfig.HttpEnabledState}) credentials := make([]map[string]interface{}, len(res.Credentials)) for i, item := range res.Credentials { diff --git a/provider/terraform/tests/resource_cloudiot_registry_test.go b/provider/terraform/tests/resource_cloudiot_registry_test.go new file mode 100644 index 000000000000..03d81c56cef2 --- /dev/null +++ b/provider/terraform/tests/resource_cloudiot_registry_test.go @@ -0,0 +1,181 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccCloudIoTRegistry_basic(t *testing.T) { + t.Parallel() + + registryName := fmt.Sprintf("psregistry-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudIoTRegistryDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCloudIoTRegistry_basic(registryName), + Check: resource.ComposeTestCheckFunc( + testAccCloudIoTRegistryExists( + "google_cloudiot_registry.foobar"), + ), + }, + { + ResourceName: "google_cloudiot_registry.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccCloudIoTRegistry_extended(t *testing.T) { + t.Parallel() + + registryName := fmt.Sprintf("psregistry-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudIoTRegistryDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCloudIoTRegistry_extended(registryName), + Check: resource.ComposeTestCheckFunc( + testAccCloudIoTRegistryExists( + "google_cloudiot_registry.foobar"), + ), + }, + { + ResourceName: "google_cloudiot_registry.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccCloudIoTRegistry_update(t *testing.T) { + t.Parallel() + + registryName := fmt.Sprintf("psregistry-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudIoTRegistryDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCloudIoTRegistry_basic(registryName), + Check: resource.ComposeTestCheckFunc( + testAccCloudIoTRegistryExists( + "google_cloudiot_registry.foobar"), + ), + }, + resource.TestStep{ + Config: testAccCloudIoTRegistry_extended(registryName), + }, + resource.TestStep{ + Config: testAccCloudIoTRegistry_basic(registryName), + }, + { + ResourceName: "google_cloudiot_registry.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckCloudIoTRegistryDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_cloudiot_registry" { + continue + } + config := testAccProvider.Meta().(*Config) + registry, _ := config.clientCloudIoT.Projects.Locations.Registries.Get(rs.Primary.ID).Do() + if registry != nil { + return fmt.Errorf("Registry still present") + } + } + return nil +} + +func testAccCloudIoTRegistryExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + config := testAccProvider.Meta().(*Config) + _, err := config.clientCloudIoT.Projects.Locations.Registries.Get(rs.Primary.ID).Do() + if err != nil { + return fmt.Errorf("Registry does not exist") + } + return nil + } +} + +func testAccCloudIoTRegistry_basic(registryName string) string { + return fmt.Sprintf(` +resource "google_cloudiot_registry" "foobar" { + name = "%s" +}`, registryName) +} + +func testAccCloudIoTRegistry_extended(registryName string) string { + return fmt.Sprintf(` +resource "google_project_iam_binding" "cloud-iot-iam-binding" { + members = ["serviceAccount:cloud-iot@system.gserviceaccount.com"] + role = "roles/pubsub.publisher" +} + +resource "google_pubsub_topic" "default-devicestatus" { + name = "psregistry-test-devicestatus-%s" +} + +resource "google_pubsub_topic" "default-telemetry" { + name = "psregistry-test-telemetry-%s" +} + +resource "google_cloudiot_registry" "foobar" { + depends_on = ["google_project_iam_binding.cloud-iot-iam-binding"] + + name = "%s" + + event_notification_config = { + pubsub_topic_name = "${google_pubsub_topic.default-devicestatus.id}" + } + + state_notification_config = { + pubsub_topic_name = "${google_pubsub_topic.default-telemetry.id}" + } + + http_config = { + http_enabled_state = "HTTP_DISABLED" + } + + mqtt_config = { + mqtt_enabled_state = "MQTT_DISABLED" + } + + credentials = [ + { + "public_key_certificate" = { + format = "X509_CERTIFICATE_PEM" + certificate = "${file("test-fixtures/rsa_cert.pem")}" + } + }, + ] +} +`, acctest.RandString(10), acctest.RandString(10), registryName) +} From b80f580705d1eb2af248b744ebdbfb7fd9b394f0 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 17 Oct 2018 15:27:04 -0700 Subject: [PATCH 43/56] add version var to all examples --- products/compute/terraform.yaml | 60 ++++++++++++++++------- products/containeranalysis/terraform.yaml | 11 +++-- products/filestore/terraform.yaml | 11 +++-- products/redis/terraform.yaml | 24 ++++----- products/resourcemanager/terraform.yaml | 13 ++--- 5 files changed, 75 insertions(+), 44 deletions(-) diff --git a/products/compute/terraform.yaml b/products/compute/terraform.yaml index dda575607649..84c7aeef972b 100644 --- a/products/compute/terraform.yaml +++ b/products/compute/terraform.yaml @@ -16,23 +16,26 @@ overrides: !ruby/object:Provider::ResourceOverrides Address: !ruby/object:Provider::Terraform::ResourceOverride id_format: "{{project}}/{{region}}/{{name}}" example: - - !ruby/object:Provider::Terraform::Examples - name: "address_basic" - primary_resource_id: "ip_address" - vars: - address_name: "my-address" - - !ruby/object:Provider::Terraform::Examples - name: "address_with_subnetwork" - primary_resource_id: "internal_with_subnet_and_address" - vars: - address_name: "my-internal-address" - network_name: "my-network" - subnetwork_name: "my-subnet" - # TODO(rileykarson): Remove this example when instance is supported - - !ruby/object:Provider::Terraform::Examples - name: "instance_with_ip" - primary_resource_id: "static" - vars: + - !ruby/object:Provider::Terraform::Examples + name: "address_basic" + primary_resource_id: "ip_address" + version: <%= _version_name %> + vars: + address_name: "my-address" + - !ruby/object:Provider::Terraform::Examples + name: "address_with_subnetwork" + primary_resource_id: "internal_with_subnet_and_address" + version: <%= _version_name %> + vars: + address_name: "my-internal-address" + network_name: "my-network" + subnetwork_name: "my-subnet" + # TODO(rileykarson): Remove this example when instance is supported + - !ruby/object:Provider::Terraform::Examples + name: "instance_with_ip" + primary_resource_id: "static" + version: <%= _version_name %> + vars: address_name: "ipv4-address" instance_name: "vm-instance" properties: @@ -87,6 +90,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "backend_bucket_basic" primary_resource_id: "image_backend" + version: <%= _version_name %> vars: backend_bucket_name: "image-backend-bucket" bucket_name: "image-store-bucket" @@ -139,6 +143,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "disk_basic" primary_resource_id: "default" + version: <%= _version_name %> vars: disk_name: "test-disk" DiskType: !ruby/object:Provider::Terraform::ResourceOverride @@ -148,6 +153,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "firewall_basic" primary_resource_id: "default" + version: <%= _version_name %> vars: firewall_name: "test-firewall" network_name: "test-network" @@ -212,6 +218,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "forwarding_rule_basic" primary_resource_id: "default" + version: <%= _version_name %> vars: forwarding_rule_name: "website-forwarding-rule" target_pool_name: "website-target-pool" @@ -260,6 +267,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "global_address_basic" primary_resource_id: "default" + version: <%= _version_name %> vars: global_address_name: "global-appserver-ip" properties: @@ -290,6 +298,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "http_health_check_basic" primary_resource_id: "default" + version: <%= _version_name %> vars: http_health_check_name: "authentication-health-check" properties: @@ -320,6 +329,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "https_health_check_basic" primary_resource_id: "default" + version: <%= _version_name %> vars: https_health_check_name: "authentication-health-check" properties: @@ -342,6 +352,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "health_check_basic" primary_resource_id: "internal-health-check" + version: <%= _version_name %> vars: health_check_name: "internal-service-health-check" custom_code: !ruby/object:Provider::Terraform::CustomCode @@ -490,6 +501,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "region_disk_basic" primary_resource_id: "regiondisk" + version: <%= _version_name %> vars: region_disk_name: "my-region-disk" disk_name: "my-disk" @@ -501,6 +513,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "route_basic" primary_resource_id: "default" + version: <%= _version_name %> vars: route_name: "network-route" network_name: "compute-network" @@ -557,6 +570,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "router_basic" primary_resource_id: "foobar" + version: <%= _version_name %> vars: router_name: "my-router" network_name: "my-network" @@ -581,14 +595,17 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "ssl_certificate_basic" primary_resource_id: "default" + version: <%= _version_name %> ignore_read_extra: - "name_prefix" - !ruby/object:Provider::Terraform::Examples name: "ssl_certificate_random_provider" primary_resource_id: "default" + version: <%= _version_name %> - !ruby/object:Provider::Terraform::Examples name: "ssl_certificate_target_https_proxies" primary_resource_id: "default" + version: <%= _version_name %> vars: target_https_proxy_name: "test-proxy" url_map_name: "url-map" @@ -619,6 +636,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "ssl_policy_basic" primary_resource_id: "prod-ssl-policy" + version: <%= _version_name %> vars: production_ssl_policy_name: "production-ssl-policy" nonprod_ssl_policy_name: "nonprod-ssl-policy" @@ -696,6 +714,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "subnetwork_basic" primary_resource_id: "network-with-private-secondary-ip-ranges" + version: <%= _version_name %> vars: subnetwork_name: "test-subnetwork" network_name: "test-network" @@ -704,6 +723,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "target_http_proxy_basic" primary_resource_id: "default" + version: <%= _version_name %> vars: target_http_proxy_name: "test-proxy" url_map_name: "url-map" @@ -717,6 +737,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "target_https_proxy_basic" primary_resource_id: "default" + version: <%= _version_name %> vars: target_https_proxy_name: "test-proxy" ssl_certificate_name: "my-certificate" @@ -766,6 +787,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "target_ssl_proxy_basic" primary_resource_id: "default" + version: <%= _version_name %> vars: target_ssl_proxy_name: "test-proxy" ssl_certificate_name: "default-cert" @@ -785,6 +807,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "target_tcp_proxy_basic" primary_resource_id: "default" + version: <%= _version_name %> vars: target_tcp_proxy_name: "test-proxy" backend_service_name: "backend-service" @@ -803,6 +826,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "target_vpn_gateway_basic" primary_resource_id: "target_gateway" + version: <%= _version_name %> vars: target_vpn_gateway_name: "vpn1" network_name: "network1" @@ -828,6 +852,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "url_map_basic" primary_resource_id: "urlmap" + version: <%= _version_name %> vars: url_map_name: "urlmap" login_backend_service_name: "login" @@ -868,6 +893,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "vpn_tunnel_basic" primary_resource_id: "tunnel1" + version: <%= _version_name %> vars: vpn_tunnel_name: "tunnel1" target_vpn_gateway_name: "vpn1" diff --git a/products/containeranalysis/terraform.yaml b/products/containeranalysis/terraform.yaml index 670186456985..9025c1dca0ae 100644 --- a/products/containeranalysis/terraform.yaml +++ b/products/containeranalysis/terraform.yaml @@ -19,11 +19,12 @@ overrides: !ruby/object:Provider::ResourceOverrides custom_code: !ruby/object:Provider::Terraform::CustomCode pre_update: 'templates/terraform/pre_update/containeranalysis_note.erb' example: - - !ruby/object:Provider::Terraform::Examples - name: "container_analysis_note_basic" - primary_resource_id: "note" - vars: - note_name: "test-attestor-note" + - !ruby/object:Provider::Terraform::Examples + name: "container_analysis_note_basic" + primary_resource_id: "note" + version: <%= _version_name %> + vars: + note_name: "test-attestor-note" properties: name: !ruby/object:Provider::Terraform::PropertyOverride custom_flatten: 'templates/terraform/custom_flatten/name_from_self_link.erb' diff --git a/products/filestore/terraform.yaml b/products/filestore/terraform.yaml index 80b3801b0d1f..ad9f5b084c6e 100644 --- a/products/filestore/terraform.yaml +++ b/products/filestore/terraform.yaml @@ -18,11 +18,12 @@ overrides: !ruby/object:Provider::ResourceOverrides id_format: "{{project}}/{{zone}}/{{name}}" import_format: ["projects/{{project}}/locations/{{zone}}/instances/{{name}}"] example: - - !ruby/object:Provider::Terraform::Examples - name: "filestore_instance_basic" - primary_resource_id: "instance" - vars: - instance_name: "test-instance" + - !ruby/object:Provider::Terraform::Examples + name: "filestore_instance_basic" + primary_resource_id: "instance" + version: <%= _version_name %> + vars: + instance_name: "test-instance" properties: name: !ruby/object:Provider::Terraform::PropertyOverride custom_flatten: 'templates/terraform/custom_flatten/name_from_self_link.erb' diff --git a/products/redis/terraform.yaml b/products/redis/terraform.yaml index f188b9e0e4d0..24de6855b336 100644 --- a/products/redis/terraform.yaml +++ b/products/redis/terraform.yaml @@ -20,17 +20,19 @@ overrides: !ruby/object:Provider::ResourceOverrides custom_code: !ruby/object:Provider::Terraform::CustomCode pre_update: 'templates/terraform/pre_update/redis_instance.erb' example: - - !ruby/object:Provider::Terraform::Examples - name: "redis_instance_basic" - primary_resource_id: "cache" - vars: - instance_name: "memory-cache" - - !ruby/object:Provider::Terraform::Examples - name: "redis_instance_full" - primary_resource_id: "cache" - vars: - instance_name: "ha-memory-cache" - network_name: "authorized-network" + - !ruby/object:Provider::Terraform::Examples + name: "redis_instance_basic" + primary_resource_id: "cache" + version: <%= _version_name %> + vars: + instance_name: "memory-cache" + - !ruby/object:Provider::Terraform::Examples + name: "redis_instance_full" + primary_resource_id: "cache" + version: <%= _version_name %> + vars: + instance_name: "ha-memory-cache" + network_name: "authorized-network" properties: alternativeLocationId: !ruby/object:Provider::Terraform::PropertyOverride default_from_api: true diff --git a/products/resourcemanager/terraform.yaml b/products/resourcemanager/terraform.yaml index 39bec11253e5..51557c2d8cdb 100644 --- a/products/resourcemanager/terraform.yaml +++ b/products/resourcemanager/terraform.yaml @@ -20,12 +20,13 @@ overrides: !ruby/object:Provider::ResourceOverrides exclude: false import_format: ["{{parent}}/{{name}}"] example: - - !ruby/object:Provider::Terraform::Examples - name: "resource_manager_lien" - skip_test: true - primary_resource_id: "lien" - vars: - project_id: "staging-project" + - !ruby/object:Provider::Terraform::Examples + name: "resource_manager_lien" + skip_test: true + primary_resource_id: "lien" + version: <%= _version_name %> + vars: + project_id: "staging-project" properties: name: !ruby/object:Provider::Terraform::PropertyOverride custom_flatten: templates/terraform/custom_flatten/name_from_self_link.erb From 70d8f28f3117211f4f90fc9bb104d5f644adf7ef Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 17 Oct 2018 15:39:33 -0700 Subject: [PATCH 44/56] fix indentation --- products/redis/terraform.yaml | 4 ++-- products/resourcemanager/terraform.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/products/redis/terraform.yaml b/products/redis/terraform.yaml index 24de6855b336..103fde1cddfc 100644 --- a/products/redis/terraform.yaml +++ b/products/redis/terraform.yaml @@ -23,13 +23,13 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "redis_instance_basic" primary_resource_id: "cache" - version: <%= _version_name %> + version: <%= _version_name %> vars: instance_name: "memory-cache" - !ruby/object:Provider::Terraform::Examples name: "redis_instance_full" primary_resource_id: "cache" - version: <%= _version_name %> + version: <%= _version_name %> vars: instance_name: "ha-memory-cache" network_name: "authorized-network" diff --git a/products/resourcemanager/terraform.yaml b/products/resourcemanager/terraform.yaml index 51557c2d8cdb..9433c704822f 100644 --- a/products/resourcemanager/terraform.yaml +++ b/products/resourcemanager/terraform.yaml @@ -24,7 +24,7 @@ overrides: !ruby/object:Provider::ResourceOverrides name: "resource_manager_lien" skip_test: true primary_resource_id: "lien" - version: <%= _version_name %> + version: <%= _version_name %> vars: project_id: "staging-project" properties: From 8c280d1db572746c321d205517216efe9579a7a8 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 17 Oct 2018 15:47:08 -0700 Subject: [PATCH 45/56] more indentation fixes --- products/containeranalysis/terraform.yaml | 2 +- products/filestore/terraform.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/products/containeranalysis/terraform.yaml b/products/containeranalysis/terraform.yaml index 9025c1dca0ae..c495f062317e 100644 --- a/products/containeranalysis/terraform.yaml +++ b/products/containeranalysis/terraform.yaml @@ -22,7 +22,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "container_analysis_note_basic" primary_resource_id: "note" - version: <%= _version_name %> + version: <%= _version_name %> vars: note_name: "test-attestor-note" properties: diff --git a/products/filestore/terraform.yaml b/products/filestore/terraform.yaml index ad9f5b084c6e..a2c463ac0938 100644 --- a/products/filestore/terraform.yaml +++ b/products/filestore/terraform.yaml @@ -21,7 +21,7 @@ overrides: !ruby/object:Provider::ResourceOverrides - !ruby/object:Provider::Terraform::Examples name: "filestore_instance_basic" primary_resource_id: "instance" - version: <%= _version_name %> + version: <%= _version_name %> vars: instance_name: "test-instance" properties: From 84e25bd6c9315b285e5eef917aa9f0bb63ee665c Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 17 Oct 2018 15:47:17 -0700 Subject: [PATCH 46/56] Add Storage ObjectAccessControl to Terraform (#562) Merged PR #562. --- build/ansible | 2 +- build/chef/storage | 2 +- build/puppet/storage | 2 +- build/terraform | 2 +- products/storage/api.yaml | 4 + products/storage/object_access_control.yaml | 27 ++-- products/storage/terraform.yaml | 49 +++++++ ...ogle_storage_object_access_control_test.go | 125 ++++++++++++++++++ provider/terraform/utils/provider.go | 1 + provider/terraform/utils/transport.go | 6 + .../resourceref_as_string.go.erb | 17 +++ ...object_access_control_public_object.tf.erb | 16 +++ 12 files changed, 234 insertions(+), 19 deletions(-) create mode 100644 products/storage/terraform.yaml create mode 100644 provider/terraform/tests/resource_google_storage_object_access_control_test.go create mode 100644 templates/terraform/custom_expand/resourceref_as_string.go.erb create mode 100644 templates/terraform/examples/storage_object_access_control_public_object.tf.erb diff --git a/build/ansible b/build/ansible index f83c565380f4..e50c3ed8ccf0 160000 --- a/build/ansible +++ b/build/ansible @@ -1 +1 @@ -Subproject commit f83c565380f420564e797d8028fe493223b80f39 +Subproject commit e50c3ed8ccf09b11ce1f141f7225536b60299664 diff --git a/build/chef/storage b/build/chef/storage index b06ec3d89a51..c6da01556c01 160000 --- a/build/chef/storage +++ b/build/chef/storage @@ -1 +1 @@ -Subproject commit b06ec3d89a5184ee6a8a34ed50bb737f52cc3e2c +Subproject commit c6da01556c012691b803471e3e40ceb20cfbc208 diff --git a/build/puppet/storage b/build/puppet/storage index 3dcf41e31955..ee67e7ad7c4f 160000 --- a/build/puppet/storage +++ b/build/puppet/storage @@ -1 +1 @@ -Subproject commit 3dcf41e319558a971f0f3a7c1b87b670ed0bd3fa +Subproject commit ee67e7ad7c4fcec815a733da75bc04a68bb2b228 diff --git a/build/terraform b/build/terraform index bc31bef9f9ee..8f7102b72d06 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit bc31bef9f9ee8c3d2ba595e25db0c2741e6b26d3 +Subproject commit 8f7102b72d061951034a6e8bf404754f4d6b86d5 diff --git a/products/storage/api.yaml b/products/storage/api.yaml index 1716d9e89202..f36d2db35a8a 100644 --- a/products/storage/api.yaml +++ b/products/storage/api.yaml @@ -322,6 +322,10 @@ objects: kind: 'storage#objectAccessControl' base_url: b/{{bucket}}/o/{{object}}/acl self_link: b/{{bucket}}/o/{{object}}/acl/{{entity}} + references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Official Documentation': 'https://cloud.google.com/storage/docs/access-control/create-manage-lists' + api: 'https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls' description: | The ObjectAccessControls resources represent the Access Control Lists (ACLs) for objects within Google Cloud Storage. ACLs let you specify diff --git a/products/storage/object_access_control.yaml b/products/storage/object_access_control.yaml index 55f354baac89..ff695258b807 100644 --- a/products/storage/object_access_control.yaml +++ b/products/storage/object_access_control.yaml @@ -27,25 +27,20 @@ output: true - !ruby/object:Api::Type::String name: 'entity' + required: true description: | The entity holding the permission, in one of the following forms: - user-userId - user-email - group-groupId - group-email - domain-domain - project-team-projectId - allUsers - allAuthenticatedUsers - Examples: - The user liz@example.com would be user-liz@example.com. - The group example@googlegroups.com would be - group-example@googlegroups.com. - To refer to all members of the Google Apps for Business domain - example.com, the entity would be domain-example.com. - required: true + * user-{{userId}} + * user-{{email}} (such as "user-liz@example.com") + * group-{{groupId}} + * group-{{email}} (such as "group-example@googlegroups.com") + * domain-{{domain}} (such as "domain-example.com") + * project-team-{{projectId}} + * allUsers + * allAuthenticatedUsers - !ruby/object:Api::Type::String name: 'entityId' + output: true description: 'The ID for the entity' # | 'etag' is not applicable for state convergence. - !ruby/object:Api::Type::Integer @@ -63,6 +58,7 @@ - !ruby/object:Api::Type::NestedObject name: 'projectTeam' description: 'The project team associated with the entity' + output: true properties: - !ruby/object:Api::Type::String name: 'projectNumber' @@ -77,6 +73,7 @@ - !ruby/object:Api::Type::Enum name: 'role' description: 'The access permission for the entity.' + required: true values: - :OWNER - :READER diff --git a/products/storage/terraform.yaml b/products/storage/terraform.yaml new file mode 100644 index 000000000000..1eee3f47f5d6 --- /dev/null +++ b/products/storage/terraform.yaml @@ -0,0 +1,49 @@ +# Copyright 2017 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:Provider::Terraform::Config +overrides: !ruby/object:Provider::ResourceOverrides + Bucket: !ruby/object:Provider::Terraform::ResourceOverride + exclude: true + BucketAccessControl: !ruby/object:Provider::Terraform::ResourceOverride + exclude: true + ObjectAccessControl: !ruby/object:Provider::Terraform::ResourceOverride + example: + - !ruby/object:Provider::Terraform::Examples + name: "storage_object_access_control_public_object" + primary_resource_id: "public_rule" + skip_test: true + vars: + bucket_name: "static-content-bucket" + object_name: "public-object" + id_format: "{{bucket}}/{{object}}/{{entity}}" + import_format: ["{{bucket}}/{{object}}/{{entity}}"] + properties: + id: !ruby/object:Provider::Terraform::PropertyOverride + exclude: true + bucket: !ruby/object:Provider::Terraform::PropertyOverride + custom_expand: 'templates/terraform/custom_expand/resourceref_as_string.go.erb' + object: !ruby/object:Provider::Terraform::PropertyOverride + description: The name of the object to apply the access control to. + DefaultObjectACL: !ruby/object:Provider::Terraform::ResourceOverride + exclude: true + +# This is for copying files over +files: !ruby/object:Provider::Config::Files + # All of these files will be copied verbatim. + copy: +<%= lines(indent(compile('provider/terraform/common~copy.yaml'), 4)) -%> + # These files have templating (ERB) code that will be run. + # This is usually to add licensing info, autogeneration notices, etc. + compile: +<%= lines(indent(compile('provider/terraform/common~compile.yaml'), 4)) -%> diff --git a/provider/terraform/tests/resource_google_storage_object_access_control_test.go b/provider/terraform/tests/resource_google_storage_object_access_control_test.go new file mode 100644 index 000000000000..cb2985f8118e --- /dev/null +++ b/provider/terraform/tests/resource_google_storage_object_access_control_test.go @@ -0,0 +1,125 @@ +package google + +import ( + "fmt" + "io/ioutil" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccStorageObjectAccessControl_basic(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + objectName := testAclObjectName() + objectData := []byte("data data data") + ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644) + resource.Test(t, resource.TestCase{ + PreCheck: func() { + if errObjectAcl != nil { + panic(errObjectAcl) + } + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectAccessControlDestroy, + Steps: []resource.TestStep{ + { + Config: testGoogleStorageObjectAccessControlBasic(bucketName, objectName, "READER", "allUsers"), + }, + { + ResourceName: "google_storage_object_access_control.default", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccStorageObjectAccessControl_update(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + objectName := testAclObjectName() + objectData := []byte("data data data") + ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644) + resource.Test(t, resource.TestCase{ + PreCheck: func() { + if errObjectAcl != nil { + panic(errObjectAcl) + } + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectAccessControlDestroy, + Steps: []resource.TestStep{ + { + Config: testGoogleStorageObjectAccessControlBasic(bucketName, objectName, "READER", "allUsers"), + }, + { + ResourceName: "google_storage_object_access_control.default", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testGoogleStorageObjectAccessControlBasic(bucketName, objectName, "OWNER", "allUsers"), + }, + { + ResourceName: "google_storage_object_access_control.default", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccStorageObjectAccessControlDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_storage_bucket_acl" { + continue + } + + bucket := rs.Primary.Attributes["bucket"] + object := rs.Primary.Attributes["object"] + entity := rs.Primary.Attributes["entity"] + + rePairs, err := config.clientStorage.ObjectAccessControls.List(bucket, object).Do() + if err != nil { + return fmt.Errorf("Can't list role entity acl for object %s in bucket %s", object, bucket) + } + + for _, v := range rePairs.Items { + if v.Entity == entity { + return fmt.Errorf("found entity %s as role entity acl entry for object %s in bucket %s", entity, object, bucket) + } + } + + } + + return nil +} + +func testGoogleStorageObjectAccessControlBasic(bucketName, objectName, role, entity string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + source = "%s" +} + +resource "google_storage_object_access_control" "default" { + object = "${google_storage_bucket_object.object.name}" + bucket = "${google_storage_bucket.bucket.name}" + role = "%s" + entity = "%s" +} +`, bucketName, objectName, tfObjectAcl.Name(), role, entity) +} diff --git a/provider/terraform/utils/provider.go b/provider/terraform/utils/provider.go index 69c148aca755..85d752d06c1e 100644 --- a/provider/terraform/utils/provider.go +++ b/provider/terraform/utils/provider.go @@ -106,6 +106,7 @@ func Provider() terraform.ResourceProvider { GeneratedFilestoreResourcesMap, GeneratedRedisResourcesMap, GeneratedResourceManagerResourcesMap, + GeneratedStorageResourcesMap, GeneratedMonitoringResourcesMap, map[string]*schema.Resource{ "google_app_engine_application": resourceAppEngineApplication(), diff --git a/provider/terraform/utils/transport.go b/provider/terraform/utils/transport.go index 7b860c7b4ac3..c29b8dad97ea 100644 --- a/provider/terraform/utils/transport.go +++ b/provider/terraform/utils/transport.go @@ -62,6 +62,12 @@ func sendRequest(config *Config, method, rawurl string, body map[string]interfac return nil, err } + // 204 responses will have no body, so we're going to error with "EOF" if we + // try to parse it. Instead, we can just return nil. + if res.StatusCode == 204 { + return nil, nil + } + result := make(map[string]interface{}) if err := json.NewDecoder(res.Body).Decode(&result); err != nil { return nil, err diff --git a/templates/terraform/custom_expand/resourceref_as_string.go.erb b/templates/terraform/custom_expand/resourceref_as_string.go.erb new file mode 100644 index 000000000000..34e8b9099530 --- /dev/null +++ b/templates/terraform/custom_expand/resourceref_as_string.go.erb @@ -0,0 +1,17 @@ +<%# The license inside this block applies to this file. + # Copyright 2018 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 expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/templates/terraform/examples/storage_object_access_control_public_object.tf.erb b/templates/terraform/examples/storage_object_access_control_public_object.tf.erb new file mode 100644 index 000000000000..84179bd9c937 --- /dev/null +++ b/templates/terraform/examples/storage_object_access_control_public_object.tf.erb @@ -0,0 +1,16 @@ +resource "google_storage_object_access_control" "<%= ctx[:primary_resource_id] %>" { + object = "${google_storage_bucket_object.object.name}" + bucket = "${google_storage_bucket.bucket.name}" + role = "READER" + entity = "allUsers" +} + +resource "google_storage_bucket" "bucket" { + name = "<%= ctx[:vars]['bucket_name'] %>" +} + + resource "google_storage_bucket_object" "object" { + name = "<%= ctx[:vars]['object_name'] %>" + bucket = "${google_storage_bucket.bucket.name}" + source = "../static/img/header-logo.jpg" +} From 62afbbb35bc040448da68585c1f7426276135b0a Mon Sep 17 00:00:00 2001 From: emily Date: Wed, 17 Oct 2018 16:23:09 -0700 Subject: [PATCH 47/56] fix expand for machine type in composer environment (#571) Merged PR #571. --- .../resource_composer_environment.go | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/provider/terraform/resources/resource_composer_environment.go b/provider/terraform/resources/resource_composer_environment.go index dc220fbe5bc5..c9736ffbfa43 100644 --- a/provider/terraform/resources/resource_composer_environment.go +++ b/provider/terraform/resources/resource_composer_environment.go @@ -677,10 +677,34 @@ func expandComposerEnvironmentZone(v interface{}, d *schema.ResourceData, config return getRelativePath(zone) } -func expandComposerEnvironmentMachineType(v interface{}, d *schema.ResourceData, config *Config, nodeCfgZone interface{}) (string, error) { +func expandComposerEnvironmentMachineType(v interface{}, d *schema.ResourceData, config *Config, nodeCfgZone string) (string, error) { + machineType := v.(string) + requiredZone := GetResourceNameFromSelfLink(nodeCfgZone) + fv, err := ParseMachineTypesFieldValue(v.(string), d, config) if err != nil { - return "", nil + if requiredZone == "" { + return "", err + } + + // Try to construct machine type with zone/project given in config. + project, err := getProject(d, config) + if err != nil { + return "", err + } + + fv = &ZonalFieldValue{ + Project: project, + Zone: requiredZone, + Name: GetResourceNameFromSelfLink(machineType), + resourceType: "machineTypes", + } + } + + // Make sure zone in node_config.machineType matches node_config.zone if + // given. + if requiredZone != "" && fv.Zone != requiredZone { + return "", fmt.Errorf("node_config machine_type %q must be in node_config zone %q", machineType, requiredZone) } return fv.RelativeLink(), nil } From 19ace702c33d18cc597869411d1c1ab54551642f Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Wed, 17 Oct 2018 17:17:21 -0700 Subject: [PATCH 48/56] Terraform: Add storage, missing project resource test files to MM. (#582) Merged PR #582. --- .../tests/resource_storage_bucket_acl_test.go | 290 ++++++ .../tests/resource_storage_bucket_iam_test.go | 265 +++++ .../resource_storage_bucket_object_test.go | 377 +++++++ .../tests/resource_storage_bucket_test.go | 979 ++++++++++++++++++ ...esource_storage_default_object_acl_test.go | 218 ++++ .../resource_storage_notification_test.go | 252 +++++ .../tests/resource_storage_object_acl_test.go | 382 +++++++ .../resource_usage_export_bucket_test.go | 59 ++ 8 files changed, 2822 insertions(+) create mode 100644 provider/terraform/tests/resource_storage_bucket_acl_test.go create mode 100644 provider/terraform/tests/resource_storage_bucket_iam_test.go create mode 100644 provider/terraform/tests/resource_storage_bucket_object_test.go create mode 100644 provider/terraform/tests/resource_storage_bucket_test.go create mode 100644 provider/terraform/tests/resource_storage_default_object_acl_test.go create mode 100644 provider/terraform/tests/resource_storage_notification_test.go create mode 100644 provider/terraform/tests/resource_storage_object_acl_test.go create mode 100644 provider/terraform/tests/resource_usage_export_bucket_test.go diff --git a/provider/terraform/tests/resource_storage_bucket_acl_test.go b/provider/terraform/tests/resource_storage_bucket_acl_test.go new file mode 100644 index 000000000000..53885ffbad8f --- /dev/null +++ b/provider/terraform/tests/resource_storage_bucket_acl_test.go @@ -0,0 +1,290 @@ +package google + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +var ( + roleEntityBasic1 = "OWNER:user-paddy@hashicorp.com" + roleEntityBasic2 = "READER:user-paddy@carvers.co" + roleEntityBasic3_owner = "OWNER:user-paddy@paddy.io" + roleEntityBasic3_reader = "READER:user-foran.paddy@gmail.com" + + roleEntityOwners = "OWNER:project-owners-" + os.Getenv("GOOGLE_PROJECT_NUMBER") + roleEntityEditors = "OWNER:project-editors-" + os.Getenv("GOOGLE_PROJECT_NUMBER") + roleEntityViewers = "READER:project-viewers-" + os.Getenv("GOOGLE_PROJECT_NUMBER") +) + +func testBucketName() string { + return fmt.Sprintf("%s-%d", "tf-test-acl-bucket", acctest.RandInt()) +} + +func TestAccStorageBucketAcl_basic(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + skipIfEnvNotSet(t, "GOOGLE_PROJECT_NUMBER") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsAclBasic1(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic2), + ), + }, + }, + }) +} + +func TestAccStorageBucketAcl_upgrade(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + skipIfEnvNotSet(t, "GOOGLE_PROJECT_NUMBER") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsAclBasic1(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic2), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageBucketsAclBasic2(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic3_owner), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageBucketsAclBasicDelete(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageBucketAclDelete(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageBucketAclDelete(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageBucketAclDelete(bucketName, roleEntityBasic3_owner), + ), + }, + }, + }) +} + +func TestAccStorageBucketAcl_downgrade(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + skipIfEnvNotSet(t, "GOOGLE_PROJECT_NUMBER") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsAclBasic2(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic3_owner), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageBucketsAclBasic3(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic3_reader), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageBucketsAclBasicDelete(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageBucketAclDelete(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageBucketAclDelete(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageBucketAclDelete(bucketName, roleEntityBasic3_owner), + ), + }, + }, + }) +} + +func TestAccStorageBucketAcl_predefined(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsAclPredefined(bucketName), + }, + }, + }) +} + +// Test that we allow the API to reorder our role entities without perma-diffing. +func TestAccStorageBucketAcl_unordered(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + skipIfEnvNotSet(t, "GOOGLE_PROJECT_NUMBER") + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsAclUnordered(bucketName), + }, + }, + }) +} + +func testAccCheckGoogleStorageBucketAclDelete(bucket, roleEntityS string) resource.TestCheckFunc { + return func(s *terraform.State) error { + roleEntity, _ := getRoleEntityPair(roleEntityS) + config := testAccProvider.Meta().(*Config) + + _, err := config.clientStorage.BucketAccessControls.Get(bucket, roleEntity.Entity).Do() + + if err != nil { + return nil + } + + return fmt.Errorf("Error, entity %s still exists", roleEntity.Entity) + } +} + +func testAccCheckGoogleStorageBucketAcl(bucket, roleEntityS string) resource.TestCheckFunc { + return func(s *terraform.State) error { + roleEntity, _ := getRoleEntityPair(roleEntityS) + config := testAccProvider.Meta().(*Config) + + res, err := config.clientStorage.BucketAccessControls.Get(bucket, roleEntity.Entity).Do() + + if err != nil { + return fmt.Errorf("Error retrieving contents of acl for bucket %s: %s", bucket, err) + } + + if res.Role != roleEntity.Role { + return fmt.Errorf("Error, Role mismatch %s != %s", res.Role, roleEntity.Role) + } + + return nil + } +} + +func testAccStorageBucketAclDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_storage_bucket_acl" { + continue + } + + bucket := rs.Primary.Attributes["bucket"] + + _, err := config.clientStorage.BucketAccessControls.List(bucket).Do() + + if err == nil { + return fmt.Errorf("Acl for bucket %s still exists", bucket) + } + } + + return nil +} + +func testGoogleStorageBucketsAclBasic1(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s", "%s", "%s", "%s"] +} +`, bucketName, roleEntityOwners, roleEntityEditors, roleEntityViewers, roleEntityBasic1, roleEntityBasic2) +} + +func testGoogleStorageBucketsAclBasic2(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s", "%s", "%s", "%s"] +} +`, bucketName, roleEntityOwners, roleEntityEditors, roleEntityViewers, roleEntityBasic2, roleEntityBasic3_owner) +} + +func testGoogleStorageBucketsAclBasicDelete(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = [] +} +`, bucketName) +} + +func testGoogleStorageBucketsAclBasic3(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s", "%s", "%s", "%s"] +} +`, bucketName, roleEntityOwners, roleEntityEditors, roleEntityViewers, roleEntityBasic2, roleEntityBasic3_reader) +} + +func testGoogleStorageBucketsAclUnordered(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s", "%s", "%s", "%s"] +} +`, bucketName, roleEntityBasic1, roleEntityViewers, roleEntityOwners, roleEntityBasic2, roleEntityEditors) +} + +func testGoogleStorageBucketsAclPredefined(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + predefined_acl = "projectPrivate" + default_acl = "projectPrivate" +} +`, bucketName) +} diff --git a/provider/terraform/tests/resource_storage_bucket_iam_test.go b/provider/terraform/tests/resource_storage_bucket_iam_test.go new file mode 100644 index 000000000000..12fa2b957173 --- /dev/null +++ b/provider/terraform/tests/resource_storage_bucket_iam_test.go @@ -0,0 +1,265 @@ +package google + +import ( + "fmt" + "reflect" + "sort" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccStorageBucketIamBinding(t *testing.T) { + t.Parallel() + + bucket := acctest.RandomWithPrefix("tf-test") + account := acctest.RandomWithPrefix("tf-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test IAM Binding creation + Config: testAccStorageBucketIamBinding_basic(bucket, account), + Check: testAccCheckGoogleStorageBucketIam(bucket, "roles/storage.objectViewer", []string{ + fmt.Sprintf("serviceAccount:%s-1@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + }), + }, + { + // Test IAM Binding update + Config: testAccStorageBucketIamBinding_update(bucket, account), + Check: testAccCheckGoogleStorageBucketIam(bucket, "roles/storage.objectViewer", []string{ + fmt.Sprintf("serviceAccount:%s-1@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + fmt.Sprintf("serviceAccount:%s-2@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + }), + }, + }, + }) +} + +func TestAccStorageBucketIamPolicy(t *testing.T) { + t.Parallel() + + bucket := acctest.RandomWithPrefix("tf-test") + account := acctest.RandomWithPrefix("tf-test") + serviceAcct := getTestServiceAccountFromEnv(t) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test IAM Policy creation + Config: testAccStorageBucketIamPolicy_basic(bucket, account, serviceAcct), + Check: testAccCheckGoogleStorageBucketIam(bucket, "roles/storage.objectViewer", []string{ + fmt.Sprintf("serviceAccount:%s-1@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + }), + }, + { + // Test IAM Policy update + Config: testAccStorageBucketIamPolicy_update(bucket, account, serviceAcct), + Check: testAccCheckGoogleStorageBucketIam(bucket, "roles/storage.objectViewer", []string{ + fmt.Sprintf("serviceAccount:%s-1@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + fmt.Sprintf("serviceAccount:%s-2@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + }), + }, + }, + }) +} + +func TestAccStorageBucketIamMember(t *testing.T) { + t.Parallel() + + bucket := acctest.RandomWithPrefix("tf-test") + account := acctest.RandomWithPrefix("tf-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test Iam Member creation (no update for member, no need to test) + Config: testAccStorageBucketIamMember_basic(bucket, account), + Check: testAccCheckGoogleStorageBucketIam(bucket, "roles/storage.admin", []string{ + fmt.Sprintf("serviceAccount:%s-1@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + }), + }, + }, + }) +} + +func testAccCheckGoogleStorageBucketIam(bucket, role string, members []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + p, err := config.clientStorage.Buckets.GetIamPolicy(bucket).Do() + if err != nil { + return err + } + + for _, binding := range p.Bindings { + if binding.Role == role { + sort.Strings(members) + sort.Strings(binding.Members) + + if reflect.DeepEqual(members, binding.Members) { + return nil + } + + return fmt.Errorf("Binding found but expected members is %v, got %v", members, binding.Members) + } + } + + return fmt.Errorf("No binding for role %q", role) + } +} + +func testAccStorageBucketIamPolicy_update(bucket, account, serviceAcct string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_service_account" "test-account-1" { + account_id = "%s-1" + display_name = "Iam Testing Account" +} + +resource "google_service_account" "test-account-2" { + account_id = "%s-2" + display_name = "Iam Testing Account" +} + + +data "google_iam_policy" "foo-policy" { + binding { + role = "roles/storage.objectViewer" + + members = [ + "serviceAccount:${google_service_account.test-account-1.email}", + "serviceAccount:${google_service_account.test-account-2.email}", + ] + } + + binding { + role = "roles/storage.admin" + members = [ + "serviceAccount:%s", + ] + } +} + +resource "google_storage_bucket_iam_policy" "bucket-binding" { + bucket = "${google_storage_bucket.bucket.name}" + policy_data = "${data.google_iam_policy.foo-policy.policy_data}" +} + +`, bucket, account, account, serviceAcct) +} + +func testAccStorageBucketIamPolicy_basic(bucket, account, serviceAcct string) string { + return fmt.Sprintf(` + +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_service_account" "test-account-1" { + account_id = "%s-1" + display_name = "Iam Testing Account" +} + + +data "google_iam_policy" "foo-policy" { + binding { + role = "roles/storage.objectViewer" + members = [ + "serviceAccount:${google_service_account.test-account-1.email}", + ] + } + + binding { + role = "roles/storage.admin" + members = [ + "serviceAccount:%s", + ] + } +} + +resource "google_storage_bucket_iam_policy" "bucket-binding" { + bucket = "${google_storage_bucket.bucket.name}" + policy_data = "${data.google_iam_policy.foo-policy.policy_data}" +} + + +`, bucket, account, serviceAcct) +} + +func testAccStorageBucketIamBinding_basic(bucket, account string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_service_account" "test-account-1" { + account_id = "%s-1" + display_name = "Iam Testing Account" +} + +resource "google_storage_bucket_iam_binding" "foo" { + bucket = "${google_storage_bucket.bucket.name}" + role = "roles/storage.objectViewer" + members = [ + "serviceAccount:${google_service_account.test-account-1.email}", + ] +} +`, bucket, account) +} + +func testAccStorageBucketIamBinding_update(bucket, account string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_service_account" "test-account-1" { + account_id = "%s-1" + display_name = "Iam Testing Account" +} + +resource "google_service_account" "test-account-2" { + account_id = "%s-2" + display_name = "Iam Testing Account" +} + +resource "google_storage_bucket_iam_binding" "foo" { + bucket = "${google_storage_bucket.bucket.name}" + role = "roles/storage.objectViewer" + members = [ + "serviceAccount:${google_service_account.test-account-1.email}", + "serviceAccount:${google_service_account.test-account-2.email}", + ] +} +`, bucket, account, account) +} + +func testAccStorageBucketIamMember_basic(bucket, account string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_service_account" "test-account-1" { + account_id = "%s-1" + display_name = "Iam Testing Account" +} + +resource "google_storage_bucket_iam_member" "foo" { + bucket = "${google_storage_bucket.bucket.name}" + role = "roles/storage.admin" + member = "serviceAccount:${google_service_account.test-account-1.email}" +} +`, bucket, account) +} diff --git a/provider/terraform/tests/resource_storage_bucket_object_test.go b/provider/terraform/tests/resource_storage_bucket_object_test.go new file mode 100644 index 000000000000..b7df23aa8012 --- /dev/null +++ b/provider/terraform/tests/resource_storage_bucket_object_test.go @@ -0,0 +1,377 @@ +package google + +import ( + "crypto/md5" + "encoding/base64" + "fmt" + "io/ioutil" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "os" + + "google.golang.org/api/storage/v1" +) + +const ( + objectName = "tf-gce-test" + content = "now this is content!" +) + +func TestAccStorageObject_basic(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + data := []byte("data data data") + h := md5.New() + h.Write(data) + data_md5 := base64.StdEncoding.EncodeToString(h.Sum(nil)) + + testFile := getNewTmpTestFile(t, "tf-test") + ioutil.WriteFile(testFile.Name(), data, 0644) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsObjectBasic(bucketName, testFile.Name()), + Check: testAccCheckGoogleStorageObject(bucketName, objectName, data_md5), + }, + }, + }) +} + +func TestAccStorageObject_recreate(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + + writeFile := func(name string, data []byte) string { + h := md5.New() + h.Write(data) + data_md5 := base64.StdEncoding.EncodeToString(h.Sum(nil)) + + ioutil.WriteFile(name, data, 0644) + return data_md5 + } + testFile := getNewTmpTestFile(t, "tf-test") + data_md5 := writeFile(testFile.Name(), []byte("data data data")) + updatedName := testFile.Name() + ".update" + updated_data_md5 := writeFile(updatedName, []byte("datum")) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsObjectBasic(bucketName, testFile.Name()), + Check: testAccCheckGoogleStorageObject(bucketName, objectName, data_md5), + }, + resource.TestStep{ + PreConfig: func() { + err := os.Rename(updatedName, testFile.Name()) + if err != nil { + t.Errorf("Failed to rename %s to %s", updatedName, testFile.Name()) + } + }, + Config: testGoogleStorageBucketsObjectBasic(bucketName, testFile.Name()), + Check: testAccCheckGoogleStorageObject(bucketName, objectName, updated_data_md5), + }, + }, + }) +} + +func TestAccStorageObject_content(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + data := []byte(content) + h := md5.New() + h.Write(data) + data_md5 := base64.StdEncoding.EncodeToString(h.Sum(nil)) + + testFile := getNewTmpTestFile(t, "tf-test") + ioutil.WriteFile(testFile.Name(), data, 0644) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsObjectContent(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageObject(bucketName, objectName, data_md5), + resource.TestCheckResourceAttr( + "google_storage_bucket_object.object", "content_type", "text/plain; charset=utf-8"), + resource.TestCheckResourceAttr( + "google_storage_bucket_object.object", "storage_class", "STANDARD"), + ), + }, + }, + }) +} + +func TestAccStorageObject_withContentCharacteristics(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + data := []byte(content) + h := md5.New() + h.Write(data) + data_md5 := base64.StdEncoding.EncodeToString(h.Sum(nil)) + testFile := getNewTmpTestFile(t, "tf-test") + ioutil.WriteFile(testFile.Name(), data, 0644) + + disposition, encoding, language, content_type := "inline", "compress", "en", "binary/octet-stream" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsObject_optionalContentFields( + bucketName, disposition, encoding, language, content_type), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageObject(bucketName, objectName, data_md5), + resource.TestCheckResourceAttr( + "google_storage_bucket_object.object", "content_disposition", disposition), + resource.TestCheckResourceAttr( + "google_storage_bucket_object.object", "content_encoding", encoding), + resource.TestCheckResourceAttr( + "google_storage_bucket_object.object", "content_language", language), + resource.TestCheckResourceAttr( + "google_storage_bucket_object.object", "content_type", content_type), + ), + }, + }, + }) +} + +func TestAccStorageObject_dynamicContent(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsObjectDynamicContent(testBucketName()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "google_storage_bucket_object.object", "content_type", "text/plain; charset=utf-8"), + resource.TestCheckResourceAttr( + "google_storage_bucket_object.object", "storage_class", "STANDARD"), + ), + }, + }, + }) +} + +func TestAccStorageObject_cacheControl(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + data := []byte(content) + h := md5.New() + h.Write(data) + data_md5 := base64.StdEncoding.EncodeToString(h.Sum(nil)) + testFile := getNewTmpTestFile(t, "tf-test") + ioutil.WriteFile(testFile.Name(), data, 0644) + + cacheControl := "private" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsObject_cacheControl(bucketName, testFile.Name(), cacheControl), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageObject(bucketName, objectName, data_md5), + resource.TestCheckResourceAttr( + "google_storage_bucket_object.object", "cache_control", cacheControl), + ), + }, + }, + }) +} + +func TestAccStorageObject_storageClass(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + data := []byte(content) + h := md5.New() + h.Write(data) + data_md5 := base64.StdEncoding.EncodeToString(h.Sum(nil)) + testFile := getNewTmpTestFile(t, "tf-test") + ioutil.WriteFile(testFile.Name(), data, 0644) + + storageClass := "MULTI_REGIONAL" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsObject_storageClass(bucketName, storageClass), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageObject(bucketName, objectName, data_md5), + resource.TestCheckResourceAttr( + "google_storage_bucket_object.object", "storage_class", storageClass), + ), + }, + }, + }) +} + +func testAccCheckGoogleStorageObject(bucket, object, md5 string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + objectsService := storage.NewObjectsService(config.clientStorage) + + getCall := objectsService.Get(bucket, object) + res, err := getCall.Do() + + if err != nil { + return fmt.Errorf("Error retrieving contents of object %s: %s", object, err) + } + + if md5 != res.Md5Hash { + return fmt.Errorf("Error contents of %s garbled, md5 hashes don't match (%s, %s)", object, md5, res.Md5Hash) + } + + return nil + } +} + +func testAccStorageObjectDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_storage_bucket_object" { + continue + } + + bucket := rs.Primary.Attributes["bucket"] + name := rs.Primary.Attributes["name"] + + objectsService := storage.NewObjectsService(config.clientStorage) + + getCall := objectsService.Get(bucket, name) + _, err := getCall.Do() + + if err == nil { + return fmt.Errorf("Object %s still exists", name) + } + } + + return nil +} + +func testGoogleStorageBucketsObjectContent(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + content = "%s" +} +`, bucketName, objectName, content) +} + +func testGoogleStorageBucketsObjectDynamicContent(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + content = "${google_storage_bucket.bucket.project}" +} +`, bucketName, objectName) +} + +func testGoogleStorageBucketsObjectBasic(bucketName, sourceFilename string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + source = "%s" +} +`, bucketName, objectName, sourceFilename) +} + +func testGoogleStorageBucketsObject_optionalContentFields( + bucketName, disposition, encoding, language, content_type string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + content = "%s" + content_disposition = "%s" + content_encoding = "%s" + content_language = "%s" + content_type = "%s" +} +`, bucketName, objectName, content, disposition, encoding, language, content_type) +} + +func testGoogleStorageBucketsObject_cacheControl(bucketName, sourceFilename, cacheControl string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + source = "%s" + cache_control = "%s" +} +`, bucketName, objectName, sourceFilename, cacheControl) +} + +func testGoogleStorageBucketsObject_storageClass(bucketName string, storageClass string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + content = "%s" + storage_class = "%s" +} +`, bucketName, objectName, content, storageClass) +} + +// Creates a new tmp test file. Fails the current test if we cannot create +// new tmp file in the filesystem. +func getNewTmpTestFile(t *testing.T, prefix string) *os.File { + testFile, err := ioutil.TempFile("", prefix) + if err != nil { + t.Fatalf("Cannot create temp file: %s", err) + } + return testFile +} diff --git a/provider/terraform/tests/resource_storage_bucket_test.go b/provider/terraform/tests/resource_storage_bucket_test.go new file mode 100644 index 000000000000..5d78ebde436f --- /dev/null +++ b/provider/terraform/tests/resource_storage_bucket_test.go @@ -0,0 +1,979 @@ +package google + +import ( + "bytes" + "fmt" + "log" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "google.golang.org/api/googleapi" + "google.golang.org/api/storage/v1" +) + +func TestAccStorageBucket_basic(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acl-bucket-%d", acctest.RandInt()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccStorageBucket_basic(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "US"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "force_destroy", "false"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "project", getTestProjectFromEnv()), + ), + }, + resource.TestStep{ + ResourceName: "google_storage_bucket.bucket", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccStorageBucket_lowercaseLocation(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acl-bucket-%d", acctest.RandInt()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccStorageBucket_lowercaseLocation(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + ), + }, + }, + }) +} + +func TestAccStorageBucket_customAttributes(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acl-bucket-%d", acctest.RandInt()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccStorageBucket_customAttributes(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "EU"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "project", getTestProjectFromEnv()), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "force_destroy", "true"), + ), + }, + }, + }) +} + +func TestAccStorageBucket_lifecycleRules(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acc-bucket-%d", acctest.RandInt()) + + hash_step0_lc0_action := resourceGCSBucketLifecycleRuleActionHash(map[string]interface{}{"type": "SetStorageClass", "storage_class": "NEARLINE"}) + hash_step0_lc0_condition := resourceGCSBucketLifecycleRuleConditionHash(map[string]interface{}{"age": 2, "created_before": "", "is_live": false, "num_newer_versions": 0}) + + hash_step0_lc1_action := resourceGCSBucketLifecycleRuleActionHash(map[string]interface{}{"type": "Delete", "storage_class": ""}) + hash_step0_lc1_condition := resourceGCSBucketLifecycleRuleConditionHash(map[string]interface{}{"age": 10, "created_before": "", "is_live": false, "num_newer_versions": 0}) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccStorageBucket_lifecycleRules(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "project", getTestProjectFromEnv()), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.#", "2"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.0.action.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.0.action.%d.type", hash_step0_lc0_action), "SetStorageClass"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.0.action.%d.storage_class", hash_step0_lc0_action), "NEARLINE"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.0.condition.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.0.condition.%d.age", hash_step0_lc0_condition), "2"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.1.action.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.1.action.%d.type", hash_step0_lc1_action), "Delete"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.1.condition.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.1.condition.%d.age", hash_step0_lc1_condition), "10"), + ), + }, + resource.TestStep{ + ResourceName: "google_storage_bucket.bucket", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccStorageBucket_storageClass(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acc-bucket-%d", acctest.RandInt()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccStorageBucket_storageClass(bucketName, "MULTI_REGIONAL", ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "project", getTestProjectFromEnv()), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "storage_class", "MULTI_REGIONAL"), + ), + }, + { + Config: testAccStorageBucket_storageClass(bucketName, "NEARLINE", ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "project", getTestProjectFromEnv()), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "storage_class", "NEARLINE"), + ), + }, + { + Config: testAccStorageBucket_storageClass(bucketName, "REGIONAL", "US-CENTRAL1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "project", getTestProjectFromEnv()), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "storage_class", "REGIONAL"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "US-CENTRAL1"), + ), + }, + { + ResourceName: "google_storage_bucket.bucket", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccStorageBucket_update(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acl-bucket-%d", acctest.RandInt()) + + hash_step2_lc0_action := resourceGCSBucketLifecycleRuleActionHash(map[string]interface{}{"type": "Delete", "storage_class": ""}) + hash_step2_lc0_condition := resourceGCSBucketLifecycleRuleConditionHash(map[string]interface{}{"age": 10, "created_before": "", "is_live": false, "num_newer_versions": 0}) + + hash_step3_lc0_action := resourceGCSBucketLifecycleRuleActionHash(map[string]interface{}{"type": "SetStorageClass", "storage_class": "NEARLINE"}) + hash_step3_lc0_condition := resourceGCSBucketLifecycleRuleConditionHash(map[string]interface{}{"age": 2, "created_before": "", "is_live": false, "num_newer_versions": 0}) + + hash_step3_lc1_action := resourceGCSBucketLifecycleRuleActionHash(map[string]interface{}{"type": "Delete", "storage_class": ""}) + hash_step3_lc1_condition := resourceGCSBucketLifecycleRuleConditionHash(map[string]interface{}{"age": 10, "created_before": "", "is_live": false, "num_newer_versions": 2}) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccStorageBucket_basic(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "project", getTestProjectFromEnv()), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "US"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "force_destroy", "false"), + ), + }, + resource.TestStep{ + Config: testAccStorageBucket_customAttributes(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "project", getTestProjectFromEnv()), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "EU"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "force_destroy", "true"), + ), + }, + resource.TestStep{ + Config: testAccStorageBucket_customAttributes_withLifecycle1(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "project", getTestProjectFromEnv()), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "EU"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "force_destroy", "true"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.0.action.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.0.action.%d.type", hash_step2_lc0_action), "Delete"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.0.condition.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.0.condition.%d.age", hash_step2_lc0_condition), "10"), + ), + }, + resource.TestStep{ + Config: testAccStorageBucket_customAttributes_withLifecycle2(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "EU"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "force_destroy", "true"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.#", "2"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.0.action.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.0.action.%d.type", hash_step3_lc0_action), "SetStorageClass"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.0.action.%d.storage_class", hash_step3_lc0_action), "NEARLINE"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.0.condition.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.0.condition.%d.age", hash_step3_lc0_condition), "2"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.1.action.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.1.action.%d.type", hash_step3_lc1_action), "Delete"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.1.condition.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.1.condition.%d.age", hash_step3_lc1_condition), "10"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", fmt.Sprintf("lifecycle_rule.1.condition.%d.num_newer_versions", hash_step3_lc1_condition), "2"), + ), + }, + resource.TestStep{ + Config: testAccStorageBucket_customAttributes(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "location", "EU"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "force_destroy", "true"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "lifecycle_rule.#", "0"), + ), + }, + }, + }) +} + +func TestAccStorageBucket_forceDestroy(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acl-bucket-%d", acctest.RandInt()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccStorageBucket_customAttributes(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + ), + }, + resource.TestStep{ + Config: testAccStorageBucket_customAttributes(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketPutItem(bucketName), + ), + }, + resource.TestStep{ + Config: testAccStorageBucket_customAttributes(acctest.RandomWithPrefix("tf-test-acl-bucket")), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketMissing(bucketName), + ), + }, + }, + }) +} + +func TestAccStorageBucket_forceDestroyWithVersioning(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acc-bucket-%d", acctest.RandInt()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccStorageBucket_forceDestroyWithVersioning(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + ), + }, + resource.TestStep{ + Config: testAccStorageBucket_forceDestroyWithVersioning(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketPutItem(bucketName), + ), + }, + resource.TestStep{ + Config: testAccStorageBucket_forceDestroyWithVersioning(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketPutItem(bucketName), + ), + }, + }, + }) +} + +func TestAccStorageBucket_versioning(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acl-bucket-%d", acctest.RandInt()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccStorageBucket_versioning(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "versioning.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "versioning.0.enabled", "true"), + ), + }, + resource.TestStep{ + ResourceName: "google_storage_bucket.bucket", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccStorageBucket_logging(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acl-bucket-%d", acctest.RandInt()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccStorageBucket_logging(bucketName, "log-bucket"), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "logging.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "logging.0.log_bucket", "log-bucket"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "logging.0.log_object_prefix", bucketName), + ), + }, + resource.TestStep{ + Config: testAccStorageBucket_loggingWithPrefix(bucketName, "another-log-bucket", "object-prefix"), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "logging.#", "1"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "logging.0.log_bucket", "another-log-bucket"), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "logging.0.log_object_prefix", "object-prefix"), + ), + }, + resource.TestStep{ + Config: testAccStorageBucket_basic(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + resource.TestCheckResourceAttr( + "google_storage_bucket.bucket", "logging.#", "0"), + ), + }, + }, + }) +} + +func TestAccStorageBucket_cors(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acl-bucket-%d", acctest.RandInt()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageBucketsCors(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + ), + }, + }, + }) + + if len(bucket.Cors) != 2 { + t.Errorf("Expected # of cors elements to be 2, got %d", len(bucket.Cors)) + } + + firstArr := bucket.Cors[0] + if firstArr.MaxAgeSeconds != 10 { + t.Errorf("Expected first block's MaxAgeSeconds to be 10, got %d", firstArr.MaxAgeSeconds) + } + + for i, v := range []string{"abc", "def"} { + if firstArr.Origin[i] != v { + t.Errorf("Expected value in first block origin to be to be %v, got %v", v, firstArr.Origin[i]) + } + } + + for i, v := range []string{"a1a"} { + if firstArr.Method[i] != v { + t.Errorf("Expected value in first block method to be to be %v, got %v", v, firstArr.Method[i]) + } + } + + for i, v := range []string{"123", "456", "789"} { + if firstArr.ResponseHeader[i] != v { + t.Errorf("Expected value in first block response headerto be to be %v, got %v", v, firstArr.ResponseHeader[i]) + } + } + + secondArr := bucket.Cors[1] + if secondArr.MaxAgeSeconds != 5 { + t.Errorf("Expected second block's MaxAgeSeconds to be 5, got %d", secondArr.MaxAgeSeconds) + } + + for i, v := range []string{"ghi", "jkl"} { + if secondArr.Origin[i] != v { + t.Errorf("Expected value in second block origin to be to be %v, got %v", v, secondArr.Origin[i]) + } + } + + for i, v := range []string{"z9z"} { + if secondArr.Method[i] != v { + t.Errorf("Expected value in second block method to be to be %v, got %v", v, secondArr.Method[i]) + } + } + + for i, v := range []string{"000"} { + if secondArr.ResponseHeader[i] != v { + t.Errorf("Expected value in second block response headerto be to be %v, got %v", v, secondArr.ResponseHeader[i]) + } + } +} + +func TestAccStorageBucket_encryption(t *testing.T) { + t.Parallel() + + projectId := "terraform-" + acctest.RandString(10) + projectOrg := getTestOrgFromEnv(t) + projectBillingAccount := getTestBillingAccountFromEnv(t) + keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + bucketName := fmt.Sprintf("tf-test-crypto-bucket-%d", acctest.RandInt()) + var bucket storage.Bucket + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccStorageBucket_encryption(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName, bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + ), + }, + }, + }) +} + +func TestAccStorageBucket_labels(t *testing.T) { + t.Parallel() + + var bucket storage.Bucket + bucketName := fmt.Sprintf("tf-test-acl-bucket-%d", acctest.RandInt()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageBucketDestroy, + Steps: []resource.TestStep{ + // Going from two labels + resource.TestStep{ + Config: testAccStorageBucket_updateLabels(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + testAccCheckStorageBucketHasLabel(&bucket, "my-label", "my-updated-label-value"), + testAccCheckStorageBucketHasLabel(&bucket, "a-new-label", "a-new-label-value"), + ), + }, + resource.TestStep{ + ResourceName: "google_storage_bucket.bucket", + ImportState: true, + ImportStateVerify: true, + }, + // Down to only one label (test single label deletion) + resource.TestStep{ + Config: testAccStorageBucket_labels(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + testAccCheckStorageBucketHasLabel(&bucket, "my-label", "my-label-value"), + ), + }, + resource.TestStep{ + ResourceName: "google_storage_bucket.bucket", + ImportState: true, + ImportStateVerify: true, + }, + // And make sure deleting all labels work + resource.TestStep{ + Config: testAccStorageBucket_basic(bucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageBucketExists( + "google_storage_bucket.bucket", bucketName, &bucket), + testAccCheckStorageBucketHasNoLabels(&bucket), + ), + }, + resource.TestStep{ + ResourceName: "google_storage_bucket.bucket", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckStorageBucketExists(n string, bucketName string, bucket *storage.Bucket) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Project_ID is set") + } + + config := testAccProvider.Meta().(*Config) + + found, err := config.clientStorage.Buckets.Get(rs.Primary.ID).Do() + if err != nil { + return err + } + + if found.Id != rs.Primary.ID { + return fmt.Errorf("Bucket not found") + } + + if found.Name != bucketName { + return fmt.Errorf("expected name %s, got %s", bucketName, found.Name) + } + + *bucket = *found + return nil + } +} + +func testAccCheckStorageBucketHasLabel(bucket *storage.Bucket, key, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + val, ok := bucket.Labels[key] + if !ok { + return fmt.Errorf("Label with key %s not found", key) + } + + if val != value { + return fmt.Errorf("Label value did not match for key %s: expected %s but found %s", key, value, val) + } + return nil + } +} + +func testAccCheckStorageBucketHasNoLabels(bucket *storage.Bucket) resource.TestCheckFunc { + return func(s *terraform.State) error { + if len(bucket.Labels) > 0 { + return fmt.Errorf("Expected 0 labels, found %v", bucket.Labels) + } + return nil + } +} + +func testAccCheckStorageBucketPutItem(bucketName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + data := bytes.NewBufferString("test") + dataReader := bytes.NewReader(data.Bytes()) + object := &storage.Object{Name: "bucketDestroyTestFile"} + + // This needs to use Media(io.Reader) call, otherwise it does not go to /upload API and fails + if res, err := config.clientStorage.Objects.Insert(bucketName, object).Media(dataReader).Do(); err == nil { + log.Printf("[INFO] Created object %v at location %v\n\n", res.Name, res.SelfLink) + } else { + return fmt.Errorf("Objects.Insert failed: %v", err) + } + + return nil + } +} + +func testAccCheckStorageBucketMissing(bucketName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + _, err := config.clientStorage.Buckets.Get(bucketName).Do() + if err == nil { + return fmt.Errorf("Found %s", bucketName) + } + + if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { + return nil + } + + return err + } +} + +func testAccStorageBucketDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_storage_bucket" { + continue + } + + _, err := config.clientStorage.Buckets.Get(rs.Primary.ID).Do() + if err == nil { + return fmt.Errorf("Bucket still exists") + } + } + + return nil +} + +func testAccStorageBucket_basic(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} +`, bucketName) +} + +func testAccStorageBucket_lowercaseLocation(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + location = "eu" +} +`, bucketName) +} + +func testAccStorageBucket_customAttributes(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + location = "EU" + force_destroy = "true" +} +`, bucketName) +} + +func testAccStorageBucket_customAttributes_withLifecycle1(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + location = "EU" + force_destroy = "true" + lifecycle_rule { + action { + type = "Delete" + } + condition { + age = 10 + } + } +} +`, bucketName) +} + +func testAccStorageBucket_customAttributes_withLifecycle2(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + location = "EU" + force_destroy = "true" + lifecycle_rule { + action { + type = "SetStorageClass" + storage_class = "NEARLINE" + } + condition { + age = 2 + } + } + lifecycle_rule { + action { + type = "Delete" + } + condition { + age = 10 + num_newer_versions = 2 + } + } +} +`, bucketName) +} + +func testAccStorageBucket_storageClass(bucketName, storageClass, location string) string { + var locationBlock string + if location != "" { + locationBlock = fmt.Sprintf(` + location = "%s"`, location) + } + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + storage_class = "%s"%s +} +`, bucketName, storageClass, locationBlock) +} + +func testGoogleStorageBucketsCors(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + cors { + origin = ["abc", "def"] + method = ["a1a"] + response_header = ["123", "456", "789"] + max_age_seconds = 10 + } + + cors { + origin = ["ghi", "jkl"] + method = ["z9z"] + response_header = ["000"] + max_age_seconds = 5 + } +} +`, bucketName) +} + +func testAccStorageBucket_forceDestroyWithVersioning(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + force_destroy = "true" + versioning = { + enabled = "true" + } +} +`, bucketName) +} + +func testAccStorageBucket_versioning(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + versioning = { + enabled = "true" + } +} +`, bucketName) +} + +func testAccStorageBucket_logging(bucketName string, logBucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + logging = { + log_bucket = "%s" + } +} +`, bucketName, logBucketName) +} + +func testAccStorageBucket_loggingWithPrefix(bucketName string, logBucketName string, prefix string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + logging = { + log_bucket = "%s" + log_object_prefix = "%s" + } +} +`, bucketName, logBucketName, prefix) +} + +func testAccStorageBucket_lifecycleRules(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + lifecycle_rule { + action { + type = "SetStorageClass" + storage_class = "NEARLINE" + } + condition { + age = 2 + } + } + lifecycle_rule { + action { + type = "Delete" + } + condition { + age = 10 + } + } +} +`, bucketName) +} + +func testAccStorageBucket_labels(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + labels { + my-label = "my-label-value" + } +} +`, bucketName) +} + +func testAccStorageBucket_encryption(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName, bucketName string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + name = "%s" + project_id = "%s" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project_services" "acceptance" { + project = "${google_project.acceptance.project_id}" + + services = [ + "cloudkms.googleapis.com", + ] +} + +resource "google_kms_key_ring" "key_ring" { + project = "${google_project_services.acceptance.project}" + name = "%s" + location = "us" +} + +resource "google_kms_crypto_key" "crypto_key" { + name = "%s" + key_ring = "${google_kms_key_ring.key_ring.id}" + rotation_period = "1000000s" +} + +resource "google_storage_bucket" "bucket" { + name = "%s" + encryption { + default_kms_key_name = "${google_kms_crypto_key.crypto_key.self_link}" + } +} + `, projectId, projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName, bucketName) +} + +func testAccStorageBucket_updateLabels(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + labels { + my-label = "my-updated-label-value" + a-new-label = "a-new-label-value" + } +} +`, bucketName) +} diff --git a/provider/terraform/tests/resource_storage_default_object_acl_test.go b/provider/terraform/tests/resource_storage_default_object_acl_test.go new file mode 100644 index 000000000000..bf4de1b9ff40 --- /dev/null +++ b/provider/terraform/tests/resource_storage_default_object_acl_test.go @@ -0,0 +1,218 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccStorageDefaultObjectAcl_basic(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageDefaultObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic1, roleEntityBasic2), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), + ), + }, + }, + }) +} + +func TestAccStorageDefaultObjectAcl_upgrade(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageDefaultObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic1, roleEntityBasic2), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic2, roleEntityBasic3_owner), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_owner), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasicDelete(bucketName, roleEntityBasic1), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic3_reader), + ), + }, + }, + }) +} + +func TestAccStorageDefaultObjectAcl_downgrade(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageDefaultObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic2, roleEntityBasic3_owner), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_owner), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntityBasic2, roleEntityBasic3_reader), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic3_reader), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageDefaultObjectsAclBasicDelete(bucketName, roleEntityBasic1), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageDefaultObjectAcl(bucketName, roleEntityBasic1), + testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic2), + testAccCheckGoogleStorageDefaultObjectAclDelete(bucketName, roleEntityBasic3_reader), + ), + }, + }, + }) +} + +// Test that we allow the API to reorder our role entities without perma-diffing. +func TestAccStorageDefaultObjectAcl_unordered(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageDefaultObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageDefaultObjectAclUnordered(bucketName), + }, + }, + }) +} + +func testAccCheckGoogleStorageDefaultObjectAcl(bucket, roleEntityS string) resource.TestCheckFunc { + return func(s *terraform.State) error { + roleEntity, _ := getRoleEntityPair(roleEntityS) + config := testAccProvider.Meta().(*Config) + + res, err := config.clientStorage.DefaultObjectAccessControls.Get(bucket, + roleEntity.Entity).Do() + + if err != nil { + return fmt.Errorf("Error retrieving contents of storage default Acl for bucket %s: %s", bucket, err) + } + + if res.Role != roleEntity.Role { + return fmt.Errorf("Error, Role mismatch %s != %s", res.Role, roleEntity.Role) + } + + return nil + } +} + +func testAccStorageDefaultObjectAclDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + + if rs.Type != "google_storage_default_object_acl" { + continue + } + + bucket := rs.Primary.Attributes["bucket"] + + _, err := config.clientStorage.DefaultObjectAccessControls.List(bucket).Do() + if err == nil { + return fmt.Errorf("Default Storage Object Acl for bucket %s still exists", bucket) + } + } + return nil +} + +func testAccCheckGoogleStorageDefaultObjectAclDelete(bucket, roleEntityS string) resource.TestCheckFunc { + return func(s *terraform.State) error { + roleEntity, _ := getRoleEntityPair(roleEntityS) + config := testAccProvider.Meta().(*Config) + + _, err := config.clientStorage.DefaultObjectAccessControls.Get(bucket, roleEntity.Entity).Do() + + if err != nil { + return nil + } + + return fmt.Errorf("Error, Object Default Acl Entity still exists %s for bucket %s", + roleEntity.Entity, bucket) + } +} + +func testGoogleStorageDefaultObjectsAclBasicDelete(bucketName, roleEntity string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_default_object_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s"] +} +`, bucketName, roleEntity) +} + +func testGoogleStorageDefaultObjectsAclBasic(bucketName, roleEntity1, roleEntity2 string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_default_object_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s"] +} +`, bucketName, roleEntity1, roleEntity2) +} + +func testGoogleStorageDefaultObjectAclUnordered(bucketName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_default_object_acl" "acl" { + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s", "%s", "%s", "%s"] +} +`, bucketName, roleEntityBasic1, roleEntityViewers, roleEntityOwners, roleEntityBasic2, roleEntityEditors) +} diff --git a/provider/terraform/tests/resource_storage_notification_test.go b/provider/terraform/tests/resource_storage_notification_test.go new file mode 100644 index 000000000000..6a6b8f114e8f --- /dev/null +++ b/provider/terraform/tests/resource_storage_notification_test.go @@ -0,0 +1,252 @@ +package google + +import ( + "fmt" + "os" + "reflect" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "google.golang.org/api/storage/v1" +) + +var ( + payload = "JSON_API_V1" +) + +func TestAccStorageNotification_basic(t *testing.T) { + t.Parallel() + + skipIfEnvNotSet(t, "GOOGLE_PROJECT") + + var notification storage.Notification + bucketName := testBucketName() + topicName := fmt.Sprintf("tf-pstopic-test-%d", acctest.RandInt()) + topic := fmt.Sprintf("//pubsub.googleapis.com/projects/%s/topics/%s", os.Getenv("GOOGLE_PROJECT"), topicName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageNotificationDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageNotificationBasic(bucketName, topicName, topic), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageNotificationExists( + "google_storage_notification.notification", ¬ification), + resource.TestCheckResourceAttr( + "google_storage_notification.notification", "bucket", bucketName), + resource.TestCheckResourceAttr( + "google_storage_notification.notification", "topic", topic), + resource.TestCheckResourceAttr( + "google_storage_notification.notification", "payload_format", payload), + resource.TestCheckResourceAttr( + "google_storage_notification.notification_with_prefix", "object_name_prefix", "foobar"), + ), + }, + resource.TestStep{ + ResourceName: "google_storage_notification.notification", + ImportState: true, + ImportStateVerify: true, + }, + resource.TestStep{ + ResourceName: "google_storage_notification.notification_with_prefix", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccStorageNotification_withEventsAndAttributes(t *testing.T) { + t.Parallel() + + skipIfEnvNotSet(t, "GOOGLE_PROJECT") + + var notification storage.Notification + bucketName := testBucketName() + topicName := fmt.Sprintf("tf-pstopic-test-%d", acctest.RandInt()) + topic := fmt.Sprintf("//pubsub.googleapis.com/projects/%s/topics/%s", os.Getenv("GOOGLE_PROJECT"), topicName) + eventType1 := "OBJECT_FINALIZE" + eventType2 := "OBJECT_ARCHIVE" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccStorageNotificationDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageNotificationOptionalEventsAttributes(bucketName, topicName, topic, eventType1, eventType2), + Check: resource.ComposeTestCheckFunc( + testAccCheckStorageNotificationExists( + "google_storage_notification.notification", ¬ification), + resource.TestCheckResourceAttr( + "google_storage_notification.notification", "bucket", bucketName), + resource.TestCheckResourceAttr( + "google_storage_notification.notification", "topic", topic), + resource.TestCheckResourceAttr( + "google_storage_notification.notification", "payload_format", payload), + testAccCheckStorageNotificationCheckEventType( + ¬ification, []string{eventType1, eventType2}), + testAccCheckStorageNotificationCheckAttributes( + ¬ification, "new-attribute", "new-attribute-value"), + ), + }, + resource.TestStep{ + ResourceName: "google_storage_notification.notification", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccStorageNotificationDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_storage_notification" { + continue + } + + bucket, notificationID := resourceStorageNotificationParseID(rs.Primary.ID) + + _, err := config.clientStorage.Notifications.Get(bucket, notificationID).Do() + if err == nil { + return fmt.Errorf("Notification configuration still exists") + } + } + + return nil +} + +func testAccCheckStorageNotificationExists(resource string, notification *storage.Notification) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resource] + if !ok { + return fmt.Errorf("Not found: %s", resource) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + bucket, notificationID := resourceStorageNotificationParseID(rs.Primary.ID) + + found, err := config.clientStorage.Notifications.Get(bucket, notificationID).Do() + if err != nil { + return err + } + + if found.Id != notificationID { + return fmt.Errorf("Storage notification configuration not found") + } + + *notification = *found + + return nil + } +} + +func testAccCheckStorageNotificationCheckEventType(notification *storage.Notification, eventTypes []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if !reflect.DeepEqual(notification.EventTypes, eventTypes) { + return fmt.Errorf("Target event types are incorrect. Expected %s, got %s", eventTypes, notification.EventTypes) + } + return nil + } +} + +func testAccCheckStorageNotificationCheckAttributes(notification *storage.Notification, key, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + val, ok := notification.CustomAttributes[key] + if !ok { + return fmt.Errorf("Custom attribute with key %s not found", key) + } + + if val != value { + return fmt.Errorf("Custom attribute value did not match for key %s: expected %s but found %s", key, value, val) + } + return nil + } +} + +func testGoogleStorageNotificationBasic(bucketName, topicName, topic string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_pubsub_topic" "topic" { + name = "%s" +} + +// We have to provide GCS default storage account with the permission +// to publish to a Cloud Pub/Sub topic from this project +// Otherwise notification configuration won't work +data "google_storage_project_service_account" "gcs_account" {} + +resource "google_pubsub_topic_iam_binding" "binding" { + topic = "${google_pubsub_topic.topic.name}" + role = "roles/pubsub.publisher" + + members = ["serviceAccount:${data.google_storage_project_service_account.gcs_account.email_address}"] +} + +resource "google_storage_notification" "notification" { + bucket = "${google_storage_bucket.bucket.name}" + payload_format = "JSON_API_V1" + topic = "${google_pubsub_topic.topic.id}" + depends_on = ["google_pubsub_topic_iam_binding.binding"] +} + +resource "google_storage_notification" "notification_with_prefix" { + bucket = "${google_storage_bucket.bucket.name}" + payload_format = "JSON_API_V1" + topic = "${google_pubsub_topic.topic.id}" + object_name_prefix = "foobar" + depends_on = ["google_pubsub_topic_iam_binding.binding"] +} + +`, bucketName, topicName) +} + +func testGoogleStorageNotificationOptionalEventsAttributes(bucketName, topicName, topic, eventType1, eventType2 string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_pubsub_topic" "topic" { + name = "%s" +} + +// We have to provide GCS default storage account with the permission +// to publish to a Cloud Pub/Sub topic from this project +// Otherwise notification configuration won't work +data "google_storage_project_service_account" "gcs_account" {} + +resource "google_pubsub_topic_iam_binding" "binding" { + topic = "${google_pubsub_topic.topic.name}" + role = "roles/pubsub.publisher" + + members = ["serviceAccount:${data.google_storage_project_service_account.gcs_account.email_address}"] +} + +resource "google_storage_notification" "notification" { + bucket = "${google_storage_bucket.bucket.name}" + payload_format = "JSON_API_V1" + topic = "${google_pubsub_topic.topic.id}" + event_types = ["%s","%s"] + custom_attributes { + new-attribute = "new-attribute-value" + } + depends_on = ["google_pubsub_topic_iam_binding.binding"] +} + +`, bucketName, topicName, eventType1, eventType2) +} diff --git a/provider/terraform/tests/resource_storage_object_acl_test.go b/provider/terraform/tests/resource_storage_object_acl_test.go new file mode 100644 index 000000000000..3b10b6b0c349 --- /dev/null +++ b/provider/terraform/tests/resource_storage_object_acl_test.go @@ -0,0 +1,382 @@ +package google + +import ( + "fmt" + "io/ioutil" + "math/rand" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +var tfObjectAcl, errObjectAcl = ioutil.TempFile("", "tf-gce-test") + +func testAclObjectName() string { + return fmt.Sprintf("%s-%d", "tf-test-acl-object", + rand.New(rand.NewSource(time.Now().UnixNano())).Int()) +} + +func TestAccStorageObjectAcl_basic(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + objectName := testAclObjectName() + objectData := []byte("data data data") + ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644) + resource.Test(t, resource.TestCase{ + PreCheck: func() { + if errObjectAcl != nil { + panic(errObjectAcl) + } + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageObjectsAclBasic1(bucketName, objectName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageObjectAcl(bucketName, + objectName, roleEntityBasic1), + testAccCheckGoogleStorageObjectAcl(bucketName, + objectName, roleEntityBasic2), + ), + }, + }, + }) +} + +func TestAccStorageObjectAcl_upgrade(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + objectName := testAclObjectName() + objectData := []byte("data data data") + ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644) + resource.Test(t, resource.TestCase{ + PreCheck: func() { + if errObjectAcl != nil { + panic(errObjectAcl) + } + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageObjectsAclBasic1(bucketName, objectName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageObjectAcl(bucketName, + objectName, roleEntityBasic1), + testAccCheckGoogleStorageObjectAcl(bucketName, + objectName, roleEntityBasic2), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageObjectsAclBasic2(bucketName, objectName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageObjectAcl(bucketName, + objectName, roleEntityBasic2), + testAccCheckGoogleStorageObjectAcl(bucketName, + objectName, roleEntityBasic3_owner), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageObjectsAclBasicDelete(bucketName, objectName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageObjectAclDelete(bucketName, + objectName, roleEntityBasic1), + testAccCheckGoogleStorageObjectAclDelete(bucketName, + objectName, roleEntityBasic2), + testAccCheckGoogleStorageObjectAclDelete(bucketName, + objectName, roleEntityBasic3_reader), + ), + }, + }, + }) +} + +func TestAccStorageObjectAcl_downgrade(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + objectName := testAclObjectName() + objectData := []byte("data data data") + ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644) + resource.Test(t, resource.TestCase{ + PreCheck: func() { + if errObjectAcl != nil { + panic(errObjectAcl) + } + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageObjectsAclBasic2(bucketName, objectName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageObjectAcl(bucketName, + objectName, roleEntityBasic2), + testAccCheckGoogleStorageObjectAcl(bucketName, + objectName, roleEntityBasic3_owner), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageObjectsAclBasic3(bucketName, objectName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageObjectAcl(bucketName, + objectName, roleEntityBasic2), + testAccCheckGoogleStorageObjectAcl(bucketName, + objectName, roleEntityBasic3_reader), + ), + }, + + resource.TestStep{ + Config: testGoogleStorageObjectsAclBasicDelete(bucketName, objectName), + Check: resource.ComposeTestCheckFunc( + testAccCheckGoogleStorageObjectAclDelete(bucketName, + objectName, roleEntityBasic1), + testAccCheckGoogleStorageObjectAclDelete(bucketName, + objectName, roleEntityBasic2), + testAccCheckGoogleStorageObjectAclDelete(bucketName, + objectName, roleEntityBasic3_reader), + ), + }, + }, + }) +} + +func TestAccStorageObjectAcl_predefined(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + objectName := testAclObjectName() + objectData := []byte("data data data") + ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644) + resource.Test(t, resource.TestCase{ + PreCheck: func() { + if errObjectAcl != nil { + panic(errObjectAcl) + } + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageObjectsAclPredefined(bucketName, objectName), + }, + }, + }) +} + +// Test that we allow the API to reorder our role entities without perma-diffing. +func TestAccStorageObjectAcl_unordered(t *testing.T) { + t.Parallel() + + bucketName := testBucketName() + objectName := testAclObjectName() + objectData := []byte("data data data") + ioutil.WriteFile(tfObjectAcl.Name(), objectData, 0644) + resource.Test(t, resource.TestCase{ + PreCheck: func() { + if errObjectAcl != nil { + panic(errObjectAcl) + } + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccStorageObjectAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testGoogleStorageObjectAclUnordered(bucketName, objectName), + }, + }, + }) +} + +func testAccCheckGoogleStorageObjectAcl(bucket, object, roleEntityS string) resource.TestCheckFunc { + return func(s *terraform.State) error { + roleEntity, _ := getRoleEntityPair(roleEntityS) + config := testAccProvider.Meta().(*Config) + + res, err := config.clientStorage.ObjectAccessControls.Get(bucket, + object, roleEntity.Entity).Do() + + if err != nil { + return fmt.Errorf("Error retrieving contents of acl for bucket %s: %s", bucket, err) + } + + if res.Role != roleEntity.Role { + return fmt.Errorf("Error, Role mismatch %s != %s", res.Role, roleEntity.Role) + } + + return nil + } +} + +func testAccCheckGoogleStorageObjectAclDelete(bucket, object, roleEntityS string) resource.TestCheckFunc { + return func(s *terraform.State) error { + roleEntity, _ := getRoleEntityPair(roleEntityS) + config := testAccProvider.Meta().(*Config) + + _, err := config.clientStorage.ObjectAccessControls.Get(bucket, + object, roleEntity.Entity).Do() + + if err != nil { + return nil + } + + return fmt.Errorf("Error, Entity still exists %s", roleEntity.Entity) + } +} + +func testAccStorageObjectAclDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_storage_bucket_acl" { + continue + } + + bucket := rs.Primary.Attributes["bucket"] + object := rs.Primary.Attributes["object"] + + _, err := config.clientStorage.ObjectAccessControls.List(bucket, object).Do() + + if err == nil { + return fmt.Errorf("Acl for bucket %s still exists", bucket) + } + } + + return nil +} + +func testGoogleStorageObjectsAclBasicDelete(bucketName string, objectName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + source = "%s" +} + +resource "google_storage_object_acl" "acl" { + object = "${google_storage_bucket_object.object.name}" + bucket = "${google_storage_bucket.bucket.name}" + role_entity = [] +} +`, bucketName, objectName, tfObjectAcl.Name()) +} + +func testGoogleStorageObjectsAclBasic1(bucketName string, objectName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + source = "%s" +} + +resource "google_storage_object_acl" "acl" { + object = "${google_storage_bucket_object.object.name}" + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s"] +} +`, bucketName, objectName, tfObjectAcl.Name(), + roleEntityBasic1, roleEntityBasic2) +} + +func testGoogleStorageObjectsAclBasic2(bucketName string, objectName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + source = "%s" +} + +resource "google_storage_object_acl" "acl" { + object = "${google_storage_bucket_object.object.name}" + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s"] +} +`, bucketName, objectName, tfObjectAcl.Name(), + roleEntityBasic2, roleEntityBasic3_owner) +} + +func testGoogleStorageObjectsAclBasic3(bucketName string, objectName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + source = "%s" +} + +resource "google_storage_object_acl" "acl" { + object = "${google_storage_bucket_object.object.name}" + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s"] +} +`, bucketName, objectName, tfObjectAcl.Name(), + roleEntityBasic2, roleEntityBasic3_reader) +} + +func testGoogleStorageObjectsAclPredefined(bucketName string, objectName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + source = "%s" +} + +resource "google_storage_object_acl" "acl" { + object = "${google_storage_bucket_object.object.name}" + bucket = "${google_storage_bucket.bucket.name}" + predefined_acl = "projectPrivate" +} +`, bucketName, objectName, tfObjectAcl.Name()) +} + +func testGoogleStorageObjectAclUnordered(bucketName, objectName string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" +} + +resource "google_storage_bucket_object" "object" { + name = "%s" + bucket = "${google_storage_bucket.bucket.name}" + source = "%s" +} + +resource "google_storage_object_acl" "acl" { + object = "${google_storage_bucket_object.object.name}" + bucket = "${google_storage_bucket.bucket.name}" + role_entity = ["%s", "%s", "%s", "%s", "%s"] +} +`, bucketName, objectName, tfObjectAcl.Name(), roleEntityBasic1, roleEntityViewers, roleEntityOwners, roleEntityBasic2, roleEntityEditors) +} diff --git a/provider/terraform/tests/resource_usage_export_bucket_test.go b/provider/terraform/tests/resource_usage_export_bucket_test.go new file mode 100644 index 000000000000..4a5b86c155db --- /dev/null +++ b/provider/terraform/tests/resource_usage_export_bucket_test.go @@ -0,0 +1,59 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccComputeResourceUsageExportBucket(t *testing.T) { + org := getTestOrgFromEnv(t) + billingId := getTestBillingAccountFromEnv(t) + + baseProject := "ub-" + acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccResourceUsageExportBucket(baseProject, org, billingId), + }, + // Test import. + resource.TestStep{ + ResourceName: "google_project_usage_export_bucket.ueb", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccResourceUsageExportBucket(baseProject, org, billingId string) string { + return fmt.Sprintf(` +resource "google_project" "base" { + project_id = "%s" + name = "Export Bucket Base" + org_id = "%s" + billing_account = "%s" +} + +resource "google_project_service" "service" { + project = "${google_project.base.project_id}" + service = "compute.googleapis.com" +} + +resource "google_storage_bucket" "bucket" { + name = "b-${google_project.base.project_id}" + project = "${google_project_service.service.project}" +} + +resource "google_project_usage_export_bucket" "ueb" { + project = "${google_project.base.project_id}" + bucket_name = "${google_storage_bucket.bucket.name}" + prefix = "foobar" +} +`, baseProject, org, billingId) +} From 1f6c48c3a1746a8ca0e13989c260801c10818883 Mon Sep 17 00:00:00 2001 From: emily Date: Thu, 18 Oct 2018 10:53:23 -0700 Subject: [PATCH 49/56] nested exclude for versioned properties (#575) Merged PR #575. --- api/type.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/api/type.rb b/api/type.rb index 8bebe90df7be..0c13a411a51d 100644 --- a/api/type.rb +++ b/api/type.rb @@ -310,6 +310,12 @@ def requires end [property_file] end + + def exclude_if_not_in_version(version) + super + @item_type.exclude_if_not_in_version(version) \ + if @item_type.is_a? NestedObject + end end # Represents an enum, and store is valid values @@ -499,6 +505,11 @@ def all_properties def properties @properties.reject(&:exclude) end + + def exclude_if_not_in_version(version) + super + @properties.each { |p| p.exclude_if_not_in_version(version) } + end end # Represents an array of name=value pairs, and stores its items' type From abe3d0c17b6c91057da1bd781102ce3e20b32f79 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Thu, 18 Oct 2018 13:17:32 -0700 Subject: [PATCH 50/56] maps and variables in ci --- .ci/ci.yml.tmpl | 48 ++++++++------- .ci/magic-modules/create-pr.sh | 10 +--- .ci/magic-modules/generate-terraform.sh | 75 ++++++++++-------------- .ci/magic-modules/generate-terraform.yml | 5 +- .ci/unit-tests/run.sh | 13 +--- .ci/unit-tests/task.yml | 4 +- .ci/vars.tmpl | 28 ++++++++- 7 files changed, 89 insertions(+), 94 deletions(-) diff --git a/.ci/ci.yml.tmpl b/.ci/ci.yml.tmpl index fbec57f746e5..6788886c6112 100644 --- a/.ci/ci.yml.tmpl +++ b/.ci/ci.yml.tmpl @@ -44,17 +44,13 @@ resources: authorship_restriction: true no_label: automerged - - name: terraform-intermediate +{% for v in vars.terraform_v.itervalues() %} + - name: {{ v.short_name }}-intermediate type: git-branch source: - uri: git@github.com:((github-account.username))/terraform-provider-google.git - private_key: ((repo-key.private_key)) - - - name: terraform-beta-intermediate - type: git-branch - source: - uri: git@github.com:((github-account.username))/terraform-provider-google-beta.git + uri: git@github.com:((github-account.username))/{{ v.provider_name }}.git private_key: ((repo-key.private_key)) +{% endfor %} - name: ansible-intermediate type: git-branch @@ -142,23 +138,25 @@ jobs: status: pending path: magic-modules - aggregate: + {% for k, v in vars.terraform_v.iteritems() %} - do: # consumes: magic-modules-branched # produces: terraform-generated - - task: generate-terraform + - task: generate-{{v.short_name}} file: magic-modules-branched/.ci/magic-modules/generate-terraform.yml params: - VERSIONS: "{{','.join(vars.terraform_versions)}}" + VERSION: {{k}} + PROVIDER_NAME: {{v.provider_name}} + SHORT_NAME: {{v.short_name}} # Puts 'terraform-generated' into the robot's fork. - aggregate: - {% for version in vars.terraform_versions %} - - put: terraform{{ '-'+version if version else '' }}-intermediate + - put: {{v.short_name}}-intermediate params: - repository: terraform{{ '-'+version if version else '' }}-generated + repository: terraform-generated/{{k}} branch_file: magic-modules-branched/branchname only_if_diff: true force: true - {% endfor %} + {% endfor %} - do: # consumes: magic-modules-branched # produces: ansible-generated @@ -237,7 +235,7 @@ jobs: CREDS: ((repo-key.private_key)) PUPPET_MODULES: {{','.join(vars.puppet_modules)}} CHEF_MODULES: {{','.join(vars.chef_modules)}} - TERRAFORM_VERSIONS: "{{','.join(vars.terraform_versions)}}" + TERRAFORM_VERSIONS: {{vars.terraform_v.values()|map(attribute='short_name')|join(',')}} TERRAFORM_ENABLED: true ANSIBLE_ENABLED: true INSPEC_ENABLED: true @@ -255,17 +253,17 @@ jobs: version: every trigger: true params: - submodules: [build/terraform, build/terraform-beta] + submodules: [{{','.join(vars.terraform_submodules)}}] passed: [mm-generate] - aggregate: - {% for version in vars.terraform_versions %} - - task: test-terraform{{ '-'+version if version else '' }} + {% for v in vars.terraform_v.itervalues() %} + - task: test-{{v.short_name}} file: magic-modules/.ci/unit-tests/task.yml timeout: 30m - {% if version %} params: - VERSION: {{ version }} - {% endif %} + PROVIDER_NAME: {{v.provider_name}} + SHORT_NAME: {{v.short_name}} + TEST_DIR: {{v.test_dir}} {% endfor %} on_failure: do: @@ -426,7 +424,7 @@ jobs: CHEF_REPO_USER: GoogleCloudPlatform CHEF_MODULES: {{','.join(vars.chef_modules)}} {%- endif %} - TERRAFORM_VERSIONS: "{{','.join(vars.terraform_versions)}}" + TERRAFORM_VERSIONS: "{{','.join(vars.terraform_properties_serialized)}}" on_failure: put: magic-modules-new-prs params: @@ -440,10 +438,10 @@ jobs: # the pipeline (when a PR needs to be updated), this does that updating by pushing # the new code to the repository/branch from which a pull request is already open. - aggregate: - {% for version in vars.terraform_versions %} - - put: terraform{{ '-'+version if version else '' }}-intermediate + {% for v in vars.terraform_v.itervalues() %} + - put: {{v.short_name}}-intermediate params: - repository: magic-modules-with-comment/build/terraform{{ '-'+version if version else '' }} + repository: magic-modules-with-comment/build/{{ v.short_name }} branch_file: magic-modules-with-comment/original_pr_branch_name # Every time a change runs through this pipeline, it will generate a commit with # a different hash - the hash includes timestamps. Therefore, even if there's no diff --git a/.ci/magic-modules/create-pr.sh b/.ci/magic-modules/create-pr.sh index d1370291dda0..04e2ae03b28b 100755 --- a/.ci/magic-modules/create-pr.sh +++ b/.ci/magic-modules/create-pr.sh @@ -37,13 +37,9 @@ if [ "$BRANCH_NAME" = "$ORIGINAL_PR_BRANCH" ]; then # we will need to create a PR using 'hub'. if [ -n "$TERRAFORM_REPO_USER" ]; then for VERSION in "${TERRAFORM_VERSIONS[@]}"; do - if [ -n "$VERSION" ]; then - PROVIDER_NAME="terraform-provider-google-$VERSION" - SUBMODULE_DIR="terraform-$VERSION" - else - PROVIDER_NAME="terraform-provider-google" - SUBMODULE_DIR="terraform" - fi + IFS=":" read -ra TERRAFORM_DATA <<< "$VERSION" + PROVIDER_NAME="${TERRAFORM_DATA[0]}" + SUBMODULE_DIR="${TERRAFORM_DATA[1]}" pushd build/$SUBMODULE_DIR diff --git a/.ci/magic-modules/generate-terraform.sh b/.ci/magic-modules/generate-terraform.sh index 899be50a939e..c8e4853f2555 100755 --- a/.ci/magic-modules/generate-terraform.sh +++ b/.ci/magic-modules/generate-terraform.sh @@ -14,57 +14,42 @@ PATCH_DIR="$(pwd)/patches" export GOPATH="${PWD}/go" mkdir -p "${GOPATH}/src/github.com/terraform-providers" -IFS="," read -ra VERSION_ARRAY <<< "$VERSIONS" -for VERSION in "${VERSION_ARRAY[@]}"; do +pushd magic-modules-branched +ln -s "${PWD}/build/$SHORT_NAME/" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" +popd - if [ -n "$VERSION" ]; then - PROVIDER_NAME="terraform-provider-google-$VERSION" - SUBMODULE_DIR="terraform-$VERSION" - VERSION_FLAG="-v $VERSION" - else - PROVIDER_NAME="terraform-provider-google" - SUBMODULE_DIR="terraform" - VERSION_FLAG="" - fi +pushd "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" +go get +popd - pushd magic-modules-branched - ln -s "${PWD}/build/$SUBMODULE_DIR/" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" - popd +pushd magic-modules-branched +LAST_COMMIT_AUTHOR="$(git log --pretty="%an <%ae>" -n1 HEAD)" +bundle install - pushd "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" - go get - popd +# Build all terraform products +bundle exec compiler -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" $VERSION - pushd magic-modules-branched - LAST_COMMIT_AUTHOR="$(git log --pretty="%an <%ae>" -n1 HEAD)" - bundle install - - # Build all terraform products - bundle exec compiler -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" $VERSION_FLAG - - # This command can crash - if that happens, the script should not fail. - set +e - TERRAFORM_COMMIT_MSG="$(python .ci/magic-modules/extract_from_pr_description.py --tag $SUBMODULE_DIR < .git/body)" - set -e - if [ -z "$TERRAFORM_COMMIT_MSG" ]; then - TERRAFORM_COMMIT_MSG="Magic Modules changes." - fi - - pushd "build/$SUBMODULE_DIR" - # These config entries will set the "committer". - git config --global user.email "magic-modules@google.com" - git config --global user.name "Modular Magician" +# This command can crash - if that happens, the script should not fail. +set +e +TERRAFORM_COMMIT_MSG="$(python .ci/magic-modules/extract_from_pr_description.py --tag $SHORT_NAME < .git/body)" +set -e +if [ -z "$TERRAFORM_COMMIT_MSG" ]; then + TERRAFORM_COMMIT_MSG="Magic Modules changes." +fi - git add -A - # Set the "author" to the commit's real author. - git commit -m "$TERRAFORM_COMMIT_MSG" --author="$LAST_COMMIT_AUTHOR" || true # don't crash if no changes - git checkout -B "$(cat ../../branchname)" +pushd "build/$SHORT_NAME" +# These config entries will set the "committer". +git config --global user.email "magic-modules@google.com" +git config --global user.name "Modular Magician" - apply_patches "$PATCH_DIR/terraform-providers/$PROVIDER_NAME" "$TERRAFORM_COMMIT_MSG" "$LAST_COMMIT_AUTHOR" "2.0.0" +git add -A +# Set the "author" to the commit's real author. +git commit -m "$TERRAFORM_COMMIT_MSG" --author="$LAST_COMMIT_AUTHOR" || true # don't crash if no changes +git checkout -B "$(cat ../../branchname)" - popd - popd +apply_patches "$PATCH_DIR/terraform-providers/$PROVIDER_NAME" "$TERRAFORM_COMMIT_MSG" "$LAST_COMMIT_AUTHOR" "2.0.0" - git clone magic-modules-branched/build/$SUBMODULE_DIR ./$SUBMODULE_DIR-generated +popd +popd -done +git clone magic-modules-branched/build/$SHORT_NAME ./terraform-generated/$VERSION diff --git a/.ci/magic-modules/generate-terraform.yml b/.ci/magic-modules/generate-terraform.yml index 5b9ef618bac0..09c246817517 100644 --- a/.ci/magic-modules/generate-terraform.yml +++ b/.ci/magic-modules/generate-terraform.yml @@ -16,10 +16,11 @@ inputs: outputs: - name: terraform-generated - - name: terraform-beta-generated run: path: magic-modules-branched/.ci/magic-modules/generate-terraform.sh params: - VERSIONS: "" + VERSION: "" + PROVIDER_NAME: "" + SHORT_NAME: "" diff --git a/.ci/unit-tests/run.sh b/.ci/unit-tests/run.sh index 5cea80e47800..d58fd5f866c3 100755 --- a/.ci/unit-tests/run.sh +++ b/.ci/unit-tests/run.sh @@ -7,20 +7,9 @@ export GOPATH=${PWD}/go set -x - -if [ -n "$VERSION" ]; then - PROVIDER_NAME="terraform-provider-google-$VERSION" - SUBMODULE_DIR="terraform-$VERSION" - TEST_DIR="google-$VERSION" -else - PROVIDER_NAME="terraform-provider-google" - SUBMODULE_DIR="terraform" - TEST_DIR="google" -fi - # Create GOPATH structure mkdir -p "${GOPATH}/src/github.com/terraform-providers" -ln -s "${PWD}/magic-modules/build/$SUBMODULE_DIR" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" +ln -s "${PWD}/magic-modules/build/$SHORT_NAME" "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" cd "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME" diff --git a/.ci/unit-tests/task.yml b/.ci/unit-tests/task.yml index 37afe47f33b8..67e73810c91a 100644 --- a/.ci/unit-tests/task.yml +++ b/.ci/unit-tests/task.yml @@ -9,4 +9,6 @@ image_resource: run: path: magic-modules/.ci/unit-tests/run.sh params: - VERSION: "" + PROVIDER_NAME: "" + SHORT_NAME: "" + TEST_DIR: "" diff --git a/.ci/vars.tmpl b/.ci/vars.tmpl index 292bc632c67f..5ad8ef11aef7 100644 --- a/.ci/vars.tmpl +++ b/.ci/vars.tmpl @@ -1,16 +1,35 @@ {% set chef_modules = ['_bundle', 'auth', 'compute', 'sql', 'storage', 'spanner', 'container', 'dns', 'iam'] %} {% set puppet_modules = ['_bundle', 'auth', 'bigquery', 'compute', 'sql', 'storage', 'spanner', 'container', 'dns', 'pubsub', 'resourcemanager'] %} {% set puppet_no_release = ['bigquery'] %} +{% set terraform_v = { + 'ga': { + 'provider_name': 'terraform-provider-google', + 'short_name': 'terraform', + 'test_dir': 'google' + }, + 'beta': { + 'provider_name': 'terraform-provider-google-beta', + 'short_name': 'terraform-beta', + 'test_dir': 'google-beta' + } + } +%} {% macro names_as_list(repo, names) -%} {% for name in names %} build/{{repo}}/{{name}} {%- endfor %} {% endmacro -%} +{% macro build_folder(names) -%} +{% for name in names %} +build/{{name}} +{%- endfor %} +{% endmacro -%} {% set puppet_submodules = names_as_list('puppet', puppet_modules).split() %} {% set chef_submodules = names_as_list('chef', chef_modules).split() %} +{% set terraform_submodules = build_folder(terraform_v.values()|map(attribute='short_name')).split() %} {% set all_submodules = puppet_submodules + chef_submodules + - (['build/terraform', 'build/terraform-beta'] + ['build/ansible'] + ['build/inspec']) + (terraform_submodules + ['build/ansible'] + ['build/inspec']) %} {% set all_submodules_yaml_format = '[' + ','.join(all_submodules) + ']' %} {% set chef_test_excludes = { @@ -37,4 +56,9 @@ build/{{repo}}/{{name}} ] } %} -{% set terraform_versions = ['', 'beta'] %} +{% macro serialize_terraform_properties(objs) -%} +{% for obj in objs %} +{{obj.provider_name}}:{{obj.short_name}} +{%- endfor %} +{% endmacro -%} +{% set terraform_properties_serialized = serialize_terraform_properties(terraform_v.values()).split() %} From ff195533adf8df3a288af240d408f8348bbecf93 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Thu, 18 Oct 2018 14:14:34 -0700 Subject: [PATCH 51/56] fix point-to-submodules --- .ci/ci.yml.tmpl | 2 +- .ci/magic-modules/point-to-submodules.sh | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.ci/ci.yml.tmpl b/.ci/ci.yml.tmpl index 6788886c6112..1631dd7320ee 100644 --- a/.ci/ci.yml.tmpl +++ b/.ci/ci.yml.tmpl @@ -235,7 +235,7 @@ jobs: CREDS: ((repo-key.private_key)) PUPPET_MODULES: {{','.join(vars.puppet_modules)}} CHEF_MODULES: {{','.join(vars.chef_modules)}} - TERRAFORM_VERSIONS: {{vars.terraform_v.values()|map(attribute='short_name')|join(',')}} + TERRAFORM_VERSIONS: "{{','.join(vars.terraform_properties_serialized)}}" TERRAFORM_ENABLED: true ANSIBLE_ENABLED: true INSPEC_ENABLED: true diff --git a/.ci/magic-modules/point-to-submodules.sh b/.ci/magic-modules/point-to-submodules.sh index 3abea58386cb..b14dc85af643 100755 --- a/.ci/magic-modules/point-to-submodules.sh +++ b/.ci/magic-modules/point-to-submodules.sh @@ -46,13 +46,10 @@ done if [ "$TERRAFORM_ENABLED" = "true" ]; then IFS="," read -ra TERRAFORM_VERSIONS <<< "$TERRAFORM_VERSIONS" for VERSION in "${TERRAFORM_VERSIONS[@]}"; do - if [ -n "$VERSION" ]; then - PROVIDER_NAME="terraform-provider-google-$VERSION" - SUBMODULE_DIR="terraform-$VERSION" - else - PROVIDER_NAME="terraform-provider-google" - SUBMODULE_DIR="terraform" - fi + IFS=":" read -ra TERRAFORM_DATA <<< "$VERSION" + PROVIDER_NAME="${TERRAFORM_DATA[0]}" + SUBMODULE_DIR="${TERRAFORM_DATA[1]}" + git config -f .gitmodules "submodule.build/$SUBMODULE_DIR.branch" "$BRANCH" git config -f .gitmodules "submodule.build/$SUBMODULE_DIR.url" "git@github.com:$GH_USERNAME/$PROVIDER_NAME.git" git submodule sync "build/$SUBMODULE_DIR" From be6dd563a999279fa59058340e4e527ba46a10af Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Thu, 18 Oct 2018 14:31:17 -0700 Subject: [PATCH 52/56] fix create-pr --- .ci/magic-modules/create-pr.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.ci/magic-modules/create-pr.sh b/.ci/magic-modules/create-pr.sh index 04e2ae03b28b..ca6d048323c3 100755 --- a/.ci/magic-modules/create-pr.sh +++ b/.ci/magic-modules/create-pr.sh @@ -172,11 +172,10 @@ else if [ -n "$TERRAFORM_REPO_USER" ]; then for VERSION in "${TERRAFORM_VERSIONS[@]}"; do - if [ -n "$VERSION" ]; then - pushd "build/terraform-$VERSION" - else - pushd build/terraform - fi + IFS=":" read -ra TERRAFORM_DATA <<< "$VERSION" + PROVIDER_NAME="${TERRAFORM_DATA[0]}" + SUBMODULE_DIR="${TERRAFORM_DATA[1]}" + pushd "build/$SUBMODULE_DIR" git branch -f "$ORIGINAL_PR_BRANCH" popd done From 91291cc6d8f4dee8147bed83ad70a31397351cdc Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Thu, 18 Oct 2018 15:05:25 -0700 Subject: [PATCH 53/56] add missing -v --- .ci/magic-modules/generate-terraform.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/magic-modules/generate-terraform.sh b/.ci/magic-modules/generate-terraform.sh index c8e4853f2555..98e37a7fe695 100755 --- a/.ci/magic-modules/generate-terraform.sh +++ b/.ci/magic-modules/generate-terraform.sh @@ -27,7 +27,7 @@ LAST_COMMIT_AUTHOR="$(git log --pretty="%an <%ae>" -n1 HEAD)" bundle install # Build all terraform products -bundle exec compiler -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" $VERSION +bundle exec compiler -a -e terraform -o "${GOPATH}/src/github.com/terraform-providers/$PROVIDER_NAME/" -v $VERSION # This command can crash - if that happens, the script should not fail. set +e From 234e48cc3b3cafb835ac22ee37607b1fa2313aca Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Thu, 18 Oct 2018 17:09:18 -0700 Subject: [PATCH 54/56] small changes the magician tried to overwrite --- .../tests/resource_compute_disk_test.go | 772 ------------------ .../examples/region_autoscaler_basic.tf.erb | 4 +- 2 files changed, 2 insertions(+), 774 deletions(-) delete mode 100644 provider/terraform/tests/resource_compute_disk_test.go diff --git a/provider/terraform/tests/resource_compute_disk_test.go b/provider/terraform/tests/resource_compute_disk_test.go deleted file mode 100644 index 74dd3abb487f..000000000000 --- a/provider/terraform/tests/resource_compute_disk_test.go +++ /dev/null @@ -1,772 +0,0 @@ -package google - -import ( - "fmt" - "os" - "regexp" - "strconv" - "testing" - - "github.com/hashicorp/terraform/helper/acctest" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" - "google.golang.org/api/compute/v1" -) - -func TestDiskImageDiffSuppress(t *testing.T) { - cases := map[string]struct { - Old, New string - ExpectDiffSuppress bool - }{ - // Full & partial links - "matching self_link with different api version": { - Old: "https://www.googleapis.com/compute/beta/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - ExpectDiffSuppress: true, - }, - "matching image partial self_link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "projects/debian-cloud/global/images/debian-8-jessie-v20171213", - ExpectDiffSuppress: true, - }, - "matching image partial no project self_link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "global/images/debian-8-jessie-v20171213", - ExpectDiffSuppress: true, - }, - "different image self_link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-jessie-v20171213", - ExpectDiffSuppress: false, - }, - "different image partial self_link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "projects/debian-cloud/global/images/debian-7-jessie-v20171213", - ExpectDiffSuppress: false, - }, - "different image partial no project self_link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "global/images/debian-7-jessie-v20171213", - ExpectDiffSuppress: false, - }, - // Image name - "matching image name": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "debian-8-jessie-v20171213", - ExpectDiffSuppress: true, - }, - "different image name": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "debian-7-jessie-v20171213", - ExpectDiffSuppress: false, - }, - // Image short hand - "matching image short hand": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "debian-cloud/debian-8-jessie-v20171213", - ExpectDiffSuppress: true, - }, - "matching image short hand but different project": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "different-cloud/debian-8-jessie-v20171213", - ExpectDiffSuppress: false, - }, - "different image short hand": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "debian-cloud/debian-7-jessie-v20171213", - ExpectDiffSuppress: false, - }, - // Image Family - "matching image family": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "family/debian-8", - ExpectDiffSuppress: true, - }, - "matching image family self link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/family/debian-8", - ExpectDiffSuppress: true, - }, - "matching unconventional image family self link": { - Old: "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1404-trusty-v20180122", - New: "https://www.googleapis.com/compute/v1/projects/projects/ubuntu-os-cloud/global/images/family/ubuntu-1404-lts", - ExpectDiffSuppress: true, - }, - "matching image family partial self link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "projects/debian-cloud/global/images/family/debian-8", - ExpectDiffSuppress: true, - }, - "matching unconventional image family partial self link": { - Old: "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1404-trusty-v20180122", - New: "projects/ubuntu-os-cloud/global/images/family/ubuntu-1404-lts", - ExpectDiffSuppress: true, - }, - "matching image family partial no project self link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "global/images/family/debian-8", - ExpectDiffSuppress: true, - }, - "matching image family short hand": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "debian-cloud/debian-8", - ExpectDiffSuppress: true, - }, - "matching image family short hand with project short name": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "debian/debian-8", - ExpectDiffSuppress: true, - }, - "matching unconventional image family short hand": { - Old: "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1404-trusty-v20180122", - New: "ubuntu-os-cloud/ubuntu-1404-lts", - ExpectDiffSuppress: true, - }, - "matching unconventional image family - minimal": { - Old: "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-minimal-1804-bionic-v20180705", - New: "ubuntu-minimal-1804-lts", - ExpectDiffSuppress: true, - }, - "different image family": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "family/debian-7", - ExpectDiffSuppress: false, - }, - "different image family self link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/family/debian-7", - ExpectDiffSuppress: false, - }, - "different image family partial self link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "projects/debian-cloud/global/images/family/debian-7", - ExpectDiffSuppress: false, - }, - "different image family partial no project self link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "global/images/family/debian-7", - ExpectDiffSuppress: false, - }, - "matching image family but different project in self link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "https://www.googleapis.com/compute/v1/projects/other-cloud/global/images/family/debian-8", - ExpectDiffSuppress: false, - }, - "different image family but different project in partial self link": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "projects/other-cloud/global/images/family/debian-8", - ExpectDiffSuppress: false, - }, - "different image family short hand": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "debian-cloud/debian-7", - ExpectDiffSuppress: false, - }, - "matching image family shorthand but different project": { - Old: "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-8-jessie-v20171213", - New: "different-cloud/debian-8", - ExpectDiffSuppress: false, - }, - } - - for tn, tc := range cases { - if diskImageDiffSuppress("image", tc.Old, tc.New, nil) != tc.ExpectDiffSuppress { - t.Errorf("bad: %s, %q => %q expect DiffSuppress to return %t", tn, tc.Old, tc.New, tc.ExpectDiffSuppress) - } - } -} - -// Test that all the naming pattern for public images are supported. -func TestAccComputeDisk_imageDiffSuppressPublicVendorsFamilyNames(t *testing.T) { - t.Parallel() - - if os.Getenv(resource.TestEnvVar) == "" { - t.Skip(fmt.Sprintf("Network access not allowed; use %s=1 to enable", resource.TestEnvVar)) - } - - config := getInitializedConfig(t) - - for _, publicImageProject := range imageMap { - token := "" - for paginate := true; paginate; { - resp, err := config.clientCompute.Images.List(publicImageProject).Filter("deprecated.replacement ne .*images.*").PageToken(token).Do() - if err != nil { - t.Fatalf("Can't list public images for project %q", publicImageProject) - } - - for _, image := range resp.Items { - if !diskImageDiffSuppress("image", image.SelfLink, "family/"+image.Family, nil) { - t.Errorf("should suppress diff for image %q and family %q", image.SelfLink, image.Family) - } - } - token := resp.NextPageToken - paginate = token != "" - } - } -} - -func TestAccComputeDisk_basic(t *testing.T) { - t.Parallel() - - diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - var disk compute.Disk - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckComputeDiskDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccComputeDisk_basic(diskName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foobar", &disk), - testAccCheckComputeDiskHasLabel(&disk, "my-label", "my-label-value"), - testAccCheckComputeDiskHasLabelFingerprint(&disk, "google_compute_disk.foobar"), - ), - }, - resource.TestStep{ - ResourceName: "google_compute_disk.foobar", - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccComputeDisk_timeout(t *testing.T) { - t.Parallel() - - diskName := acctest.RandomWithPrefix("tf-test-disk") - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccComputeDisk_timeout(diskName), - ExpectError: regexp.MustCompile("timeout"), - }, - }, - }) -} - -func TestAccComputeDisk_update(t *testing.T) { - t.Parallel() - - diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - var disk compute.Disk - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: testAccComputeDisk_basic(diskName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foobar", &disk), - resource.TestCheckResourceAttr("google_compute_disk.foobar", "size", "50"), - testAccCheckComputeDiskHasLabel(&disk, "my-label", "my-label-value"), - testAccCheckComputeDiskHasLabelFingerprint(&disk, "google_compute_disk.foobar"), - ), - }, - { - Config: testAccComputeDisk_updated(diskName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foobar", &disk), - resource.TestCheckResourceAttr("google_compute_disk.foobar", "size", "100"), - testAccCheckComputeDiskHasLabel(&disk, "my-label", "my-updated-label-value"), - testAccCheckComputeDiskHasLabel(&disk, "a-new-label", "a-new-label-value"), - testAccCheckComputeDiskHasLabelFingerprint(&disk, "google_compute_disk.foobar"), - ), - }, - }, - }) -} - -func TestAccComputeDisk_fromSnapshot(t *testing.T) { - t.Parallel() - - diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - firstDiskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - snapshotName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - projectName := getTestProjectFromEnv() - - var disk compute.Disk - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckComputeDiskDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccComputeDisk_fromSnapshot(projectName, firstDiskName, snapshotName, diskName, "self_link"), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.seconddisk", &disk), - ), - }, - resource.TestStep{ - Config: testAccComputeDisk_fromSnapshot(projectName, firstDiskName, snapshotName, diskName, "name"), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.seconddisk", &disk), - ), - }, - }, - }) -} - -func TestAccComputeDisk_encryption(t *testing.T) { - t.Parallel() - - diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - var disk compute.Disk - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckComputeDiskDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccComputeDisk_encryption(diskName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foobar", &disk), - testAccCheckEncryptionKey( - "google_compute_disk.foobar", &disk), - ), - }, - }, - }) -} - -func TestAccComputeDisk_deleteDetach(t *testing.T) { - t.Parallel() - - diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - instanceName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - var disk compute.Disk - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckComputeDiskDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccComputeDisk_deleteDetach(instanceName, diskName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", &disk), - ), - }, - // this needs to be a second step so we refresh and see the instance - // listed as attached to the disk; the instance is created after the - // disk. and the disk's properties aren't refreshed unless there's - // another step - resource.TestStep{ - Config: testAccComputeDisk_deleteDetach(instanceName, diskName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", &disk), - testAccCheckComputeDiskInstances( - "google_compute_disk.foo", &disk), - ), - }, - }, - }) -} - -func TestAccComputeDisk_deleteDetachIGM(t *testing.T) { - t.Parallel() - - diskName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - diskName2 := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - mgrName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - var disk compute.Disk - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckComputeDiskDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccComputeDisk_deleteDetachIGM(diskName, mgrName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", &disk), - ), - }, - // this needs to be a second step so we refresh and see the instance - // listed as attached to the disk; the instance is created after the - // disk. and the disk's properties aren't refreshed unless there's - // another step - resource.TestStep{ - Config: testAccComputeDisk_deleteDetachIGM(diskName, mgrName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", &disk), - testAccCheckComputeDiskInstances( - "google_compute_disk.foo", &disk), - ), - }, - // Change the disk name to recreate the instances - resource.TestStep{ - Config: testAccComputeDisk_deleteDetachIGM(diskName2, mgrName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", &disk), - ), - }, - // Add the extra step like before - resource.TestStep{ - Config: testAccComputeDisk_deleteDetachIGM(diskName2, mgrName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeDiskExists( - "google_compute_disk.foo", &disk), - testAccCheckComputeDiskInstances( - "google_compute_disk.foo", &disk), - ), - }, - }, - }) -} - -func TestAccComputeDisk_computeDiskUserRegex(t *testing.T) { - - shouldPass := []string{ - - "https://www.googleapis.com/compute/v1/projects/project-id/zones/us-central1/instances/123", - "https://www.googleapis.com/compute/v1/projects/123123/zones/us-central1/instances/123", - "https://www.googleapis.com/compute/v1/projects/hashicorptest.net:project-123/zones/us-central1/instances/123", - "https://www.googleapis.com/compute/v1/projects/123/zones/456/instances/789", - } - - shouldFail := []string{ - "https://www.googleapis.com/compute/v1/projects/project#/zones/us-central1/instances/123", - "https://www.googleapis.com/compute/v1/projects/project/zones/us-central#/instances/123", - "https://www.googleapis.com/compute/v1/projects/project/zones/us-central1/instances/?", - "https://www.googleapis.com/compute/v1/projects/foo.com:bar:baz/zones/us-central1/instances/?", - "https://www.googleapis.com/compute/v1/projects/foo.com:/zones/us-central1/instances/?", - } - - for _, element := range shouldPass { - if !computeDiskUserRegex.MatchString(element) { - t.Error("computeDiskUserRegex should match on '" + element + "' but doesn't") - } - } - - for _, element := range shouldFail { - if computeDiskUserRegex.MatchString(element) { - t.Error("computeDiskUserRegex shouldn't match on '" + element + "' but does") - } - } - -} - -func testAccCheckComputeDiskDestroy(s *terraform.State) error { - config := testAccProvider.Meta().(*Config) - - for _, rs := range s.RootModule().Resources { - if rs.Type != "google_compute_disk" { - continue - } - - _, err := config.clientCompute.Disks.Get( - config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() - if err == nil { - return fmt.Errorf("Disk still exists") - } - } - - return nil -} - -func testAccCheckComputeDiskExists(n string, disk *compute.Disk) resource.TestCheckFunc { - return func(s *terraform.State) error { - p := getTestProjectFromEnv() - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - config := testAccProvider.Meta().(*Config) - - found, err := config.clientCompute.Disks.Get( - p, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() - if err != nil { - return err - } - - if found.Name != rs.Primary.ID { - return fmt.Errorf("Disk not found") - } - - *disk = *found - - return nil - } -} - -func testAccCheckComputeDiskHasLabel(disk *compute.Disk, key, value string) resource.TestCheckFunc { - return func(s *terraform.State) error { - val, ok := disk.Labels[key] - if !ok { - return fmt.Errorf("Label with key %s not found", key) - } - - if val != value { - return fmt.Errorf("Label value did not match for key %s: expected %s but found %s", key, value, val) - } - return nil - } -} - -func testAccCheckComputeDiskHasLabelFingerprint(disk *compute.Disk, resourceName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - state := s.RootModule().Resources[resourceName] - if state == nil { - return fmt.Errorf("Unable to find resource named %s", resourceName) - } - - labelFingerprint := state.Primary.Attributes["label_fingerprint"] - if labelFingerprint != disk.LabelFingerprint { - return fmt.Errorf("Label fingerprints do not match: api returned %s but state has %s", - disk.LabelFingerprint, labelFingerprint) - } - - return nil - } -} - -func testAccCheckEncryptionKey(n string, disk *compute.Disk) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - attr := rs.Primary.Attributes["disk_encryption_key.0.sha256"] - if disk.DiskEncryptionKey == nil { - return fmt.Errorf("Disk %s has mismatched encryption key.\nTF State: %+v\nGCP State: ", n, attr) - } else if attr != disk.DiskEncryptionKey.Sha256 { - return fmt.Errorf("Disk %s has mismatched encryption key.\nTF State: %+v.\nGCP State: %+v", - n, attr, disk.DiskEncryptionKey.Sha256) - } - return nil - } -} - -func testAccCheckComputeDiskInstances(n string, disk *compute.Disk) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - attr := rs.Primary.Attributes["users.#"] - if strconv.Itoa(len(disk.Users)) != attr { - return fmt.Errorf("Disk %s has mismatched users.\nTF State: %+v\nGCP State: %+v", n, rs.Primary.Attributes["users"], disk.Users) - } - - for pos, user := range disk.Users { - if rs.Primary.Attributes["users."+strconv.Itoa(pos)] != user { - return fmt.Errorf("Disk %s has mismatched users.\nTF State: %+v.\nGCP State: %+v", - n, rs.Primary.Attributes["users"], disk.Users) - } - } - return nil - } -} - -func testAccComputeDisk_basic(diskName string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_disk" "foobar" { - name = "%s" - image = "${data.google_compute_image.my_image.self_link}" - size = 50 - type = "pd-ssd" - zone = "us-central1-a" - labels { - my-label = "my-label-value" - } -}`, diskName) -} - -func testAccComputeDisk_timeout(diskName string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_disk" "foobar" { - name = "%s" - image = "${data.google_compute_image.my_image.self_link}" - type = "pd-ssd" - zone = "us-central1-a" - - timeouts { - create = "1s" - } -}`, diskName) -} - -func testAccComputeDisk_updated(diskName string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_disk" "foobar" { - name = "%s" - image = "${data.google_compute_image.my_image.self_link}" - size = 100 - type = "pd-ssd" - zone = "us-central1-a" - labels { - my-label = "my-updated-label-value" - a-new-label = "a-new-label-value" - } -}`, diskName) -} - -func testAccComputeDisk_fromSnapshot(projectName, firstDiskName, snapshotName, diskName, ref_selector string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_disk" "foobar" { - name = "d1-%s" - image = "${data.google_compute_image.my_image.self_link}" - size = 50 - type = "pd-ssd" - zone = "us-central1-a" - project = "%s" -} - -resource "google_compute_snapshot" "snapdisk" { - name = "%s" - source_disk = "${google_compute_disk.foobar.name}" - zone = "us-central1-a" - project = "%s" -} - -resource "google_compute_disk" "seconddisk" { - name = "d2-%s" - snapshot = "${google_compute_snapshot.snapdisk.%s}" - type = "pd-ssd" - zone = "us-central1-a" - project = "%s" -}`, firstDiskName, projectName, snapshotName, projectName, diskName, ref_selector, projectName) -} - -func testAccComputeDisk_encryption(diskName string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_disk" "foobar" { - name = "%s" - image = "${data.google_compute_image.my_image.self_link}" - size = 50 - type = "pd-ssd" - zone = "us-central1-a" - disk_encryption_key { - raw_key = "SGVsbG8gZnJvbSBHb29nbGUgQ2xvdWQgUGxhdGZvcm0=" - } -}`, diskName) -} - -func testAccComputeDisk_deleteDetach(instanceName, diskName string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_disk" "foo" { - name = "%s" - image = "${data.google_compute_image.my_image.self_link}" - size = 50 - type = "pd-ssd" - zone = "us-central1-a" -} - -resource "google_compute_instance" "bar" { - name = "%s" - machine_type = "n1-standard-1" - zone = "us-central1-a" - - boot_disk { - initialize_params { - image = "${data.google_compute_image.my_image.self_link}" - } - } - - attached_disk { - source = "${google_compute_disk.foo.self_link}" - } - - network_interface { - network = "default" - } -}`, diskName, instanceName) -} - -func testAccComputeDisk_deleteDetachIGM(diskName, mgrName string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_disk" "foo" { - name = "%s" - image = "${data.google_compute_image.my_image.self_link}" - size = 50 - type = "pd-ssd" - zone = "us-central1-a" -} - -resource "google_compute_instance_template" "template" { - machine_type = "g1-small" - - disk { - boot = true - source = "${google_compute_disk.foo.name}" - auto_delete = false - } - - network_interface { - network = "default" - } - - lifecycle { - create_before_destroy = true - } -} - -resource "google_compute_instance_group_manager" "manager" { - name = "%s" - base_instance_name = "disk-igm" - instance_template = "${google_compute_instance_template.template.self_link}" - zone = "us-central1-a" - target_size = 1 -}`, diskName, mgrName) -} diff --git a/templates/terraform/examples/region_autoscaler_basic.tf.erb b/templates/terraform/examples/region_autoscaler_basic.tf.erb index fb7206ead9c4..5001e689172b 100644 --- a/templates/terraform/examples/region_autoscaler_basic.tf.erb +++ b/templates/terraform/examples/region_autoscaler_basic.tf.erb @@ -50,8 +50,8 @@ resource "google_compute_region_instance_group_manager" "foobar" { instance_template = "${google_compute_instance_template.foobar.self_link}" <% else -%> version { - instance_template = "${google_compute_instance_template.foobar.self_link}" - name = "primary" + instance_template = "${google_compute_instance_template.foobar.self_link}" + name = "primary" } <% end -%> target_pools = ["${google_compute_target_pool.foobar.self_link}"] From b6e66c2de271a510e764a4d23ea09e749b34fae6 Mon Sep 17 00:00:00 2001 From: Riley Karson Date: Fri, 19 Oct 2018 09:26:02 -0700 Subject: [PATCH 55/56] Add OiCS link generation to the Terraform docs. (#572) Merged PR #572. --- build/terraform | 2 +- provider/terraform/custom_code.rb | 16 ++++++++++++++++ templates/terraform/resource.html.markdown.erb | 14 +++++++++++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/build/terraform b/build/terraform index 8f7102b72d06..f6e614601289 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 8f7102b72d061951034a6e8bf404754f4d6b86d5 +Subproject commit f6e61460128913c6e00862a50b065fd3ab4228d4 diff --git a/provider/terraform/custom_code.rb b/provider/terraform/custom_code.rb index 3a77e7ae8342..ff3729b824c2 100644 --- a/provider/terraform/custom_code.rb +++ b/provider/terraform/custom_code.rb @@ -123,6 +123,22 @@ def config_example substitute_example_paths body end + def oics_link + hash = { + cloudshell_git_repo: 'https://github.com/terraform-google-modules/docs-examples.git', + cloudshell_working_dir: @name, + cloudshell_image: 'gcr.io/graphite-cloud-shell-images/terraform:latest', + open_in_editor: 'main.tf', + cloudshell_print: './motd', + cloudshell_tutorial: './tutorial.md' + } + URI::HTTPS.build( + host: 'console.cloud.google.com', + path: '/cloudshell/open', + query: URI.encode_www_form(hash) + ) + end + def substitute_test_paths(config) config = config.gsub('path/to/private.key', 'test-fixtures/ssl_cert/test.key') config.gsub('path/to/certificate.crt', 'test-fixtures/ssl_cert/test.crt') diff --git a/templates/terraform/resource.html.markdown.erb b/templates/terraform/resource.html.markdown.erb index dcbbed33dcbb..5c92f49f555c 100644 --- a/templates/terraform/resource.html.markdown.erb +++ b/templates/terraform/resource.html.markdown.erb @@ -77,11 +77,19 @@ To get more information about <%= object.name -%>, see: <% end -%> <% unless object.example.empty? -%> -## Example Usage + <%- object.example.each do |example| -%> + <%- unless example.skip_test -%> + + <%- end -%> +## Example Usage - <%= example.name.camelize(:upper).uncombine %> + -<% object.example.each do |example| -%> <%= example.config_documentation -%> -<%- end %> + <%- end %> <%- end -%> ## Argument Reference From c9daa49f6da196e0381723fa03d449f15054eff5 Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Fri, 19 Oct 2018 17:55:09 +0000 Subject: [PATCH 56/56] Update tracked submodules -> HEAD on Fri Oct 19 17:55:09 UTC 2018 Tracked submodules are build/puppet/_bundle build/puppet/auth build/puppet/bigquery build/puppet/compute build/puppet/sql build/puppet/storage build/puppet/spanner build/puppet/container build/puppet/dns build/puppet/pubsub build/puppet/resourcemanager build/chef/_bundle build/chef/auth build/chef/compute build/chef/sql build/chef/storage build/chef/spanner build/chef/container build/chef/dns build/chef/iam build/terraform-beta build/terraform build/ansible build/inspec. --- build/terraform | 2 +- build/terraform-beta | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/terraform b/build/terraform index f6e614601289..ba1a535f671e 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit f6e61460128913c6e00862a50b065fd3ab4228d4 +Subproject commit ba1a535f671eb5d60c5f424b3737984bd8b455f6 diff --git a/build/terraform-beta b/build/terraform-beta index 2b5eb99d6b42..be82687b5104 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit 2b5eb99d6b42129a7f21478ce49aa668db5ebffa +Subproject commit be82687b51040bcc5769943c251673fd23bf699f