From 67344f49fc8d8917df5463a3cfa79cd5a8c497ca Mon Sep 17 00:00:00 2001 From: thomas jouannic Date: Tue, 7 Nov 2017 17:11:10 +0100 Subject: [PATCH 1/4] Allow users to fetch project details based on a name --- google/data_source_google_project.go | 100 +++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 google/data_source_google_project.go diff --git a/google/data_source_google_project.go b/google/data_source_google_project.go new file mode 100644 index 00000000000..6c011b403fe --- /dev/null +++ b/google/data_source_google_project.go @@ -0,0 +1,100 @@ +package google + +import ( + "fmt" + "log" + "sort" + "strconv" + "time" + + "github.com/hashicorp/terraform/helper/schema" + "google.golang.org/api/cloudresourcemanager/v1" +) + +func dataSourceGoogleProject() *schema.Resource { + return &schema.Resource{ + Read: dataSourceGoogleProjectRead, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "project_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "number": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceGoogleProjectRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + filter := fmt.Sprintf("name:%s", d.Get("name").(string)) + firstCall := config.clientResourceManager.Projects.List().Filter(filter) + + resp, err := firstCall.Do() + if err != nil { + return fmt.Errorf("Error reading Project: %s", err) + } + + if len(resp.Projects) < 1 { + // The project doesn't exist + return fmt.Errorf("Project Not Found") + } + + var activeProjects []*cloudresourcemanager.Project + + // filter any project that is not in "ACTIVE" state + for _, project := range resp.Projects { + if project.LifecycleState == "ACTIVE" { + activeProjects = append(activeProjects, project) + } + } + + // fetch the entire projects collection but only append those which are active + nextToken := resp.NextPageToken + + for nextToken != "" { + call := firstCall.PageToken(nextToken) + + resp, err := call.Do() + if err != nil { + return fmt.Errorf("Error reading Project: %s", err) + } + + for _, project := range resp.Projects { + if project.LifecycleState == "ACTIVE" { + activeProjects = append(activeProjects, project) + } + } + + nextToken = resp.NextPageToken + } + + // sort by ascending order of creation + sort.Slice(activeProjects, func(i, j int) bool { + iCreateTime, _ := time.Parse(time.RFC3339, activeProjects[i].CreateTime) + jCreateTime, _ := time.Parse(time.RFC3339, activeProjects[j].CreateTime) + + return iCreateTime.Before(jCreateTime) + }) + + // the last element is the most recent one + lastActiveProject := activeProjects[len(activeProjects)-1] + + log.Printf("[DEBUG] Google project found: %q", lastActiveProject) + + d.Set("project_id", lastActiveProject.ProjectId) + d.Set("number", strconv.FormatInt(int64(lastActiveProject.ProjectNumber), 10)) + d.SetId(lastActiveProject.ProjectId) + + return nil +} From 5d1ef7b0a5a4abb84654cf98c5d5b4be4013ac99 Mon Sep 17 00:00:00 2001 From: thomas jouannic Date: Tue, 7 Nov 2017 17:13:19 +0100 Subject: [PATCH 2/4] Add an acceptance test for the "google_project" datasource --- google/data_source_google_project_test.go | 86 +++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 google/data_source_google_project_test.go diff --git a/google/data_source_google_project_test.go b/google/data_source_google_project_test.go new file mode 100644 index 00000000000..08f3dbc30a7 --- /dev/null +++ b/google/data_source_google_project_test.go @@ -0,0 +1,86 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDataSourceGoogleProject(t *testing.T) { + t.Parallel() + + skipIfEnvNotSet(t, + []string{ + "GOOGLE_ORG", + }..., + ) + + name := "foobar" + randSuffix := acctest.RandString(10) + pid := fmt.Sprintf("%s-%s", name, randSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceGoogleProjectConfig(pid, name, org), + Check: resource.ComposeTestCheckFunc( + testAccDataSourceGoogleProjectCheck("data.google_project.my_project", "google_project.foobar"), + ), + }, + }, + }) +} + +func testAccDataSourceGoogleProjectCheck(data_source_name string, resource_name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[data_source_name] + if !ok { + return fmt.Errorf("root module has no resource called %s", data_source_name) + } + + rs, ok := s.RootModule().Resources[resource_name] + if !ok { + return fmt.Errorf("can't find %s in state", resource_name) + } + + ds_attr := ds.Primary.Attributes + rs_attr := rs.Primary.Attributes + + project_attrs_to_test := []string{ + "project_id", + "number", + "name", + } + + for _, attr_to_check := range project_attrs_to_test { + if ds_attr[attr_to_check] != rs_attr[attr_to_check] { + return fmt.Errorf( + "%s is %s; want %s", + attr_to_check, + ds_attr[attr_to_check], + rs_attr[attr_to_check], + ) + } + } + + return nil + } +} + +func testAccDataSourceGoogleProjectConfig(pid string, name string, org string) string { + return fmt.Sprintf(` +resource "google_project" "foobar" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +data "google_project" "my_project" { + name = "${google_project.foobar.name}" +}`, pid, name, org) +} From 00d7083c2541810531e2692d88f41b5acb2fa045 Mon Sep 17 00:00:00 2001 From: thomas jouannic Date: Tue, 7 Nov 2017 17:13:46 +0100 Subject: [PATCH 3/4] Enable the "google_project" ds in the provider --- google/provider.go | 1 + 1 file changed, 1 insertion(+) diff --git a/google/provider.go b/google/provider.go index 2448388a5fd..b5338315fe2 100644 --- a/google/provider.go +++ b/google/provider.go @@ -58,6 +58,7 @@ func Provider() terraform.ResourceProvider { "google_compute_instance_group": dataSourceGoogleComputeInstanceGroup(), "google_container_engine_versions": dataSourceGoogleContainerEngineVersions(), "google_iam_policy": dataSourceGoogleIamPolicy(), + "google_project": dataSourceGoogleProject(), "google_storage_object_signed_url": dataSourceGoogleSignedUrl(), }, From acb2447955dfd8bc6da0dcfbde0041e48d9d961f Mon Sep 17 00:00:00 2001 From: thomas jouannic Date: Tue, 7 Nov 2017 17:14:13 +0100 Subject: [PATCH 4/4] Add documentation for the "google_project" ds --- website/docs/d/google_project.html.markdown | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 website/docs/d/google_project.html.markdown diff --git a/website/docs/d/google_project.html.markdown b/website/docs/d/google_project.html.markdown new file mode 100644 index 00000000000..e406e9ddbca --- /dev/null +++ b/website/docs/d/google_project.html.markdown @@ -0,0 +1,40 @@ +--- +layout: "google" +page_title: "Google: google_project" +sidebar_current: "docs-google-datasource-project" +description: |- + Provides the Google Project details based on a name +--- + +# google\_project + +Provides access to the latest available Google Project details based a given name. +See more about [project details](https://cloud.google.com/resource-manager/docs/cloud-platform-resource-hierarchy#projects) in the upstream docs. + +``` +data "google_project" "foo" { + name = "foobar" +} + +resource "google_project_services" "project" { + project = "${data.google_project.foo.project_id}" + services = ["iam.googleapis.com", "cloudresourcemanager.googleapis.com"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The display name of the project. + +## Attributes Reference + +The following attribute is exported: + +In addition to the arguments listed above, the following computed attributes are +exported: + +* `project_id` - The project ID. + +* `number` - The numeric identifier of the project.