-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New datasource: google_container_cluster #740
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package google | ||
|
||
import ( | ||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func dataSourceGoogleContainerCluster() *schema.Resource { | ||
// Generate datasource schema from resource | ||
dsSchema := datasourceSchemaFromResourceSchema(resourceContainerCluster().Schema) | ||
|
||
// Fixup the schema flags of the Required and Optional input attributes | ||
fixDatasourceSchemaFlags(dsSchema, true, "name", "zone") | ||
fixDatasourceSchemaFlags(dsSchema, false, "project") | ||
|
||
return &schema.Resource{ | ||
Read: datasourceContainerClusterRead, | ||
Schema: dsSchema, | ||
} | ||
} | ||
|
||
func datasourceContainerClusterRead(d *schema.ResourceData, meta interface{}) error { | ||
clusterName := d.Get("name").(string) | ||
|
||
d.SetId(clusterName) | ||
|
||
return resourceContainerClusterRead(d, meta) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func TestAccGoogleContainerClusterDatasource_basic(t *testing.T) { | ||
t.Parallel() | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccContainerClusterDatasourceConfig, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccDataSourceGoogleContainerClusterCheck("data.google_container_cluster.kubes", "google_container_cluster.kubes"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccDataSourceGoogleContainerClusterCheck(dataSourceName string, resourceName string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
ds, ok := s.RootModule().Resources[dataSourceName] | ||
if !ok { | ||
return fmt.Errorf("root module has no resource called %s", dataSourceName) | ||
} | ||
|
||
rs, ok := s.RootModule().Resources[resourceName] | ||
if !ok { | ||
return fmt.Errorf("can't find %s in state", resourceName) | ||
} | ||
|
||
dsAttr := ds.Primary.Attributes | ||
rsAttr := rs.Primary.Attributes | ||
|
||
clusterAttrToCheck := []string{ | ||
"name", | ||
"zone", | ||
"additional_zones", | ||
"addons_config", | ||
"cluster_ipv4_cidr", | ||
"description", | ||
"enable_kubernetes_alpha", | ||
"enable_legacy_abac", | ||
"endpoint", | ||
"enable_legacy_abac", | ||
"instance_group_urls", | ||
"ip_allocation_policy", | ||
"logging_service", | ||
"maintenance_policy", | ||
"master_auth", | ||
"master_auth.0.password", | ||
"master_auth.0.username", | ||
"master_auth.0.client_certificate", | ||
"master_auth.0.client_key", | ||
"master_auth.0.cluster_ca_certificate", | ||
"master_authorized_networks_config", | ||
"master_version", | ||
"min_master_version", | ||
"monitoring_service", | ||
"network", | ||
"network_policy", | ||
"node_version", | ||
"subnetwork", | ||
} | ||
|
||
for _, attr := range clusterAttrToCheck { | ||
if dsAttr[attr] != rsAttr[attr] { | ||
return fmt.Errorf( | ||
"%s is %s; want %s", | ||
attr, | ||
dsAttr[attr], | ||
rsAttr[attr], | ||
) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
var testAccContainerClusterDatasourceConfig = fmt.Sprintf(` | ||
resource "google_container_cluster" "kubes" { | ||
name = "cluster-test-%s" | ||
zone = "us-central1-a" | ||
initial_node_count = 1 | ||
|
||
master_auth { | ||
username = "mr.yoda" | ||
password = "adoy.rm" | ||
} | ||
} | ||
|
||
data "google_container_cluster" "kubes" { | ||
name = "${google_container_cluster.kubes.name}" | ||
zone = "${google_container_cluster.kubes.zone}" | ||
} | ||
`, acctest.RandString(10)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -344,3 +344,65 @@ func extractFirstMapConfig(m []interface{}) map[string]interface{} { | |
|
||
return m[0].(map[string]interface{}) | ||
} | ||
|
||
// datasourceSchemaFromResourceSchema is a recursive func that | ||
// converts an existing Resource schema to a Datasource schema. | ||
// All schema elements are copied, but certain attributes are ignored or changed: | ||
// - all attributes have Computed = true | ||
// - all attributes have ForceNew, Required = false | ||
// - Validation funcs and attributes (e.g. MaxItems) are not copied | ||
func datasourceSchemaFromResourceSchema(rs map[string]*schema.Schema) map[string]*schema.Schema { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great idea, thanks so much for adding this! For a bit more visibility within the provider, it might make more sense to move these helper fns into their own file (datasource_helpers.go or something like that) |
||
ds := make(map[string]*schema.Schema, len(rs)) | ||
for k, v := range rs { | ||
log.Printf("[DEBUG] datasourceSchemaFromResourceSchema: %s", k) | ||
|
||
dv := &schema.Schema{ | ||
Computed: true, | ||
ForceNew: false, | ||
Required: false, | ||
Description: v.Description, | ||
Type: v.Type, | ||
} | ||
|
||
switch v.Type { | ||
case schema.TypeSet: | ||
dv.Set = v.Set | ||
fallthrough | ||
case schema.TypeList: | ||
// List & Set types are generally used for 2 cases: | ||
// - a list/set of simple primative values (e.g. list of strings) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: s/primative/primitive |
||
// - a sub resource | ||
if elem, ok := v.Elem.(*schema.Resource); ok { | ||
// handle the case where the Element is a sub-resource | ||
dv.Elem = &schema.Resource{ | ||
Schema: datasourceSchemaFromResourceSchema(elem.Schema), | ||
} | ||
} else { | ||
// handle simple primative case | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and here :) |
||
dv.Elem = v.Elem | ||
} | ||
|
||
default: | ||
// Elem of all other types are copied as-is | ||
dv.Elem = v.Elem | ||
|
||
} | ||
ds[k] = dv | ||
|
||
} | ||
return ds | ||
} | ||
|
||
// fixDatasourceSchemaFlags is a convenience func that toggles the Computed, | ||
// Optional + Required flags on a schema element. This is useful when the schema | ||
// has been generated (using `datasourceSchemaFromResourceSchema` above for | ||
// example) and therefore the attribute flags were not set appropriately when | ||
// first added to the schema definition. Currently only supports top-level | ||
// schema elements. | ||
func fixDatasourceSchemaFlags(schema map[string]*schema.Schema, required bool, keys ...string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional idea: it might be a bit more readable in the resources themselves to have two additional fns: addRequiredFieldsToSchema and addOptionalFieldsToSchema (or something along those lines), that just call into this one with the appropriate bool value. |
||
for _, v := range keys { | ||
schema[v].Computed = false | ||
schema[v].Optional = !required | ||
schema[v].Required = required | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious why you decided to have only a subset of the resource schema fields in the data source schema. If you had the same set of fields, then you would be able to reuse the read function (https://github.com/terraform-providers/terraform-provider-google/blob/master/google/data_source_google_compute_instance_group.go is a good example of this). If there is a reason why certain schema elements are excluded, I'd love to see a comment explaining why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea, I hadn't considered using the resources Read func, but that makes a lot of sense.
I stripped down the schema to a subset that I thought would be useful in a datasource, based on the notion if it wasn't going to be useful, remove it so there's less to maintain in the read func. Now that you've pointed out we can re-use the read func from the resource it makes sense to keep the whole schema.