Skip to content

Commit

Permalink
Terraform generate iam (#2006)
Browse files Browse the repository at this point in the history
Merged PR #2006.
  • Loading branch information
slevenick authored and modular-magician committed Jul 10, 2019
1 parent 8639042 commit ccfda0a
Show file tree
Hide file tree
Showing 19 changed files with 670 additions and 506 deletions.
2 changes: 1 addition & 1 deletion build/terraform
2 changes: 1 addition & 1 deletion build/terraform-beta
2 changes: 1 addition & 1 deletion products/pubsub/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ objects:
description: |
A named resource to which messages are sent by publishers.
iam_policy: !ruby/object:Api::Resource::IamPolicy
exclude: true
exclude: false
method_name_separator: ':'
properties:
- !ruby/object:Api::Type::String
Expand Down
1 change: 1 addition & 0 deletions products/pubsub/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ overrides: !ruby/object:Overrides::ResourceOverrides
- !ruby/object:Provider::Terraform::Examples
name: "pubsub_topic_basic"
primary_resource_id: "example"
primary_resource_name: "fmt.Sprintf(\"example-topic-%s\", context[\"random_suffix\"])"
vars:
topic_name: "example-topic"
- !ruby/object:Provider::Terraform::Examples
Expand Down
4 changes: 4 additions & 0 deletions products/sourcerepo/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
legacy_name: 'sourcerepo'
overrides: !ruby/object:Overrides::ResourceOverrides
Repository: !ruby/object:Overrides::Terraform::ResourceOverride
iam_policy: !ruby/object:Api::Resource::IamPolicy
exclude: false
method_name_separator: ':'
id_format: '{{project}}/{{name}}'
examples:
- !ruby/object:Provider::Terraform::Examples
name: "sourcerepo_repository_basic"
primary_resource_id: "my-repo"
primary_resource_name: "fmt.Sprintf(\"my-repository-%s\", context[\"random_suffix\"])"
vars:
repository_name: "my-repository"
properties:
Expand Down
2 changes: 2 additions & 0 deletions provider/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -423,5 +423,7 @@ def effective_copyright_year(out_file)
end
Time.now.year
end

def generate_iam_policy(data) end
end
end
35 changes: 35 additions & 0 deletions provider/terraform.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,41 @@ def generate_operation(output_folder, _types, version_name)
self)
end

# Generate the IAM policy for this object. This is used to query and test
# IAM policies separately from the resource itself
def generate_iam_policy(data)
dir = data.version == 'beta' ? 'google-beta' : 'google'
target_folder = File.join(data.output_folder, dir)

name = data.object.name.underscore
product_name = data.product.name.underscore
filepath = File.join(target_folder, "iam_#{product_name}_#{name}.go")

data.generate('templates/terraform/iam_policy.go.erb', filepath, self)

generated_test_name = "iam_#{product_name}_#{name}_generated_test.go"
filepath = File.join(target_folder, generated_test_name)
data.generate(
'templates/terraform/examples/base_configs/iam_test_file.go.erb',
filepath,
self
)

generate_iam_documentation(data)
end

def generate_iam_documentation(data)
target_folder = data.output_folder
target_folder = File.join(target_folder, 'website', 'docs', 'r')
FileUtils.mkpath target_folder
name = data.object.name.underscore
product_name = data.product.name.underscore

filepath =
File.join(target_folder, "#{product_name}_#{name}_iam.html.markdown")
data.generate('templates/terraform/resource_iam.html.markdown.erb', filepath, self)
end

def build_object_data(object, output_folder, version)
TerraformFileTemplate.file_for_resource(output_folder, object, version, @config, build_env)
end
Expand Down
25 changes: 16 additions & 9 deletions provider/terraform/examples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ class Examples < Api::Object
# Whether to skip generating tests for this resource
attr_reader :skip_test

# The name of the primary resource for use in IAM tests. IAM tests need
# a reference to the primary resource to create IAM policies for
attr_reader :primary_resource_name

def config_documentation
docs_defaults = {
PROJECT_NAME: 'my-project-name',
Expand Down Expand Up @@ -117,6 +121,16 @@ def config_documentation
end

def config_test
body = config_test_body
lines(compile_file(
{
content: body
},
'templates/terraform/examples/base_configs/test_body.go.erb'
))
end

def config_test_body
@vars ||= {}
@test_env_vars ||= {}
body = lines(compile_file(
Expand All @@ -128,15 +142,7 @@ def config_test
"templates/terraform/examples/#{name}.tf.erb"
))

body = substitute_test_paths body

lines(compile_file(
{
content: body,
count: vars.length
},
'templates/terraform/examples/base_configs/test_body.go.erb'
))
substitute_test_paths body
end

def config_example
Expand Down Expand Up @@ -189,6 +195,7 @@ def validate
check :vars, type: Hash
check :test_env_vars, type: Hash
check :ignore_read_extra, type: Array, item_type: String, default: []
check :primary_resource_name, type: String
check :skip_test, type: TrueClass
end
end
Expand Down
2 changes: 2 additions & 0 deletions provider/terraform_object_library.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,7 @@ def copy_common_files(output_folder, _version_name)
end

def generate_resource_tests(data) end

def generate_iam_policy(data) end
end
end
182 changes: 182 additions & 0 deletions templates/terraform/examples/base_configs/iam_test_file.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<%= lines(autogen_notice :go) -%>

package google

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)

<%# Since most resources define a "basic" config as their first example, we can reuse that config to create a resource to test IAM resources with. -%>
<% example = object.examples.reject(&:skip_test)
.reject { |e| @api.version_obj_or_closest(version) < @api.version_obj_or_closest(e.min_version) }
.first -%>
<% resource_name = product_ns + object.name -%>
<%
individual_url = object.self_link_url
params = individual_url.scan(/({{)(\w+)(}})/).map { |arr| arr[1] }
-%>
<%
if @config.legacy_name.nil?
terraform_name = "google_" + (product_ns + object.name).underscore
else
terraform_name = "google_" + @config.legacy_name + '_' + object.name.underscore
end
-%>
<% import_url = individual_url.gsub(/({{)(\w+)(}})/, '%s').gsub(object.__product.base_url, '') -%>
<% import_str = Array.new(params.length, '%s').join('/') -%>
<% import_qualifiers = [] -%>
<% params.each do |param| -%>
<% if param == 'project' -%>
<% import_qualifiers.push('getTestProjectFromEnv()') -%>
<% elsif param == 'zone' -%>
<% import_qualifiers.push('getTestZoneFromEnv(t)') -%>
<% elsif param == 'region' || param == 'location' -%>
<% import_qualifiers.push('getTestRegionFromEnv(t)') -%>
<% end -%>
<% end -%>
func TestAcc<%= resource_name -%>IamBindingGenerated(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"random_suffix": acctest.RandString(10),
"role": "roles/editor",
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheck<%= resource_name -%>Destroy,
Steps: []resource.TestStep{
{
Config: testAcc<%= resource_name -%>IamBinding_basicGenerated(context),
},
{
ResourceName: "<%= terraform_name -%>_iam_binding.foo",
ImportStateId: fmt.Sprintf("<%= import_url -%> roles/editor", <%= import_qualifiers.join(', ') -%>, <%= example.primary_resource_name -%>),
ImportState: true,
ImportStateVerify: true,
},
{
// Test Iam Binding update
Config: testAcc<%= resource_name -%>IamBinding_updateGenerated(context),
},
{
ResourceName: "<%= terraform_name -%>_iam_binding.foo",
ImportStateId: fmt.Sprintf("<%= import_url -%> roles/editor", <%= import_qualifiers.join(', ') -%>, <%= example.primary_resource_name -%>),
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAcc<%= resource_name -%>IamMemberGenerated(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"random_suffix": acctest.RandString(10),
"role": "roles/editor",
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheck<%= resource_name -%>Destroy,
Steps: []resource.TestStep{
{
// Test Iam Member creation (no update for member, no need to test)
Config: testAcc<%= resource_name -%>IamMember_basicGenerated(context),
},
{
ResourceName: "<%= terraform_name -%>_iam_member.foo",
ImportStateId: fmt.Sprintf("<%= import_url -%> roles/editor user:[email protected]", <%= import_qualifiers.join(', ') -%>, <%= example.primary_resource_name -%>),
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAcc<%= resource_name -%>IamPolicyGenerated(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"random_suffix": acctest.RandString(10),
"role": "roles/editor",
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheck<%= resource_name -%>Destroy,
Steps: []resource.TestStep{
{
Config: testAcc<%= resource_name -%>IamPolicy_basicGenerated(context),
},
{
ResourceName: "<%= terraform_name -%>_iam_policy.foo",
ImportStateId: fmt.Sprintf("<%= import_url -%>", <%= import_qualifiers.join(', ') -%>, <%= example.primary_resource_name -%>),
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAcc<%= resource_name -%>IamMember_basicGenerated(context map[string]interface{}) string {
return Nprintf(`
<%= example.config_test_body -%>

resource "<%= terraform_name -%>_iam_member" "foo" {
<%= object.name.underscore -%> = "${<%= terraform_name -%>.<%= example.primary_resource_id -%>.id}"
role = "%{role}"
member = "user:[email protected]"
}
`, context)
}

func testAcc<%= resource_name -%>IamPolicy_basicGenerated(context map[string]interface{}) string {
return Nprintf(`
<%= example.config_test_body -%>

data "google_iam_policy" "foo" {
binding {
role = "%{role}"
members = ["user:[email protected]"]
}
}

resource "<%= terraform_name -%>_iam_policy" "foo" {
<%= object.name.underscore -%> = "${<%= terraform_name -%>.<%= example.primary_resource_id -%>.id}"
policy_data = "${data.google_iam_policy.foo.policy_data}"
}
`, context)
}

func testAcc<%= resource_name -%>IamBinding_basicGenerated(context map[string]interface{}) string {
return Nprintf(`
<%= example.config_test_body -%>

resource "<%= terraform_name -%>_iam_binding" "foo" {
<%= object.name.underscore -%> = "${<%= terraform_name -%>.<%= example.primary_resource_id -%>.id}"
role = "%{role}"
members = ["user:[email protected]"]
}
`, context)
}

func testAcc<%= resource_name -%>IamBinding_updateGenerated(context map[string]interface{}) string {
return Nprintf(`
<%= example.config_test_body -%>

resource "<%= terraform_name -%>_iam_binding" "foo" {
<%= object.name.underscore -%> = "${<%= terraform_name -%>.<%= example.primary_resource_id -%>.id}"
role = "%{role}"
members = ["user:[email protected]", "user:[email protected]"]
}
`, context)
}
Loading

0 comments on commit ccfda0a

Please sign in to comment.