Skip to content
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

Merged
merged 4 commits into from
Dec 20, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions google/data_source_google_container_cluster.go
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 {
Copy link
Contributor

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.

Copy link
Contributor Author

@sl1pm4t sl1pm4t Nov 22, 2017

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.

clusterName := d.Get("name").(string)

d.SetId(clusterName)

return resourceContainerClusterRead(d, meta)
}
106 changes: 106 additions & 0 deletions google/data_source_google_container_cluster_test.go
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))
1 change: 1 addition & 0 deletions google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func Provider() terraform.ResourceProvider {
"google_compute_zones": dataSourceGoogleComputeZones(),
"google_compute_instance_group": dataSourceGoogleComputeInstanceGroup(),
"google_compute_region_instance_group": dataSourceGoogleComputeRegionInstanceGroup(),
"google_container_cluster": dataSourceGoogleContainerCluster(),
"google_container_engine_versions": dataSourceGoogleContainerEngineVersions(),
"google_active_folder": dataSourceGoogleActiveFolder(),
"google_iam_policy": dataSourceGoogleIamPolicy(),
Expand Down
62 changes: 62 additions & 0 deletions google/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The 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)
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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
}
}
Loading