diff --git a/google-beta/resource_google_project.go b/google-beta/resource_google_project.go index 553c06b730..9c9fa8d711 100644 --- a/google-beta/resource_google_project.go +++ b/google-beta/resource_google_project.go @@ -9,8 +9,6 @@ import ( "time" "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/helper/validation" - appengine "google.golang.org/api/appengine/v1" "google.golang.org/api/cloudbilling/v1" "google.golang.org/api/cloudresourcemanager/v1" "google.golang.org/api/googleapi" @@ -30,8 +28,7 @@ func resourceGoogleProject() *schema.Resource { Importer: &schema.ResourceImporter{ State: resourceProjectImportState, }, - MigrateState: resourceGoogleProjectMigrateState, - CustomizeDiff: resourceGoogleProjectCustomizeDiff, + MigrateState: resourceGoogleProjectMigrateState, Schema: map[string]*schema.Schema{ "project_id": &schema.Schema{ @@ -92,12 +89,10 @@ func resourceGoogleProject() *schema.Resource { Set: schema.HashString, }, "app_engine": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Computed: true, - Elem: appEngineResource(), - MaxItems: 1, - Deprecated: "Use the google_app_engine_application resource instead.", + Type: schema.TypeList, + Elem: appEngineResource(), + Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, }, } @@ -110,68 +105,57 @@ func appEngineResource() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, "location_id": &schema.Schema{ Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validation.StringInSlice([]string{ - "northamerica-northeast1", - "us-central", - "us-west2", - "us-east1", - "us-east4", - "southamerica-east1", - "europe-west", - "europe-west2", - "europe-west3", - "asia-northeast1", - "asia-south1", - "australia-southeast1", - }, false), + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, "serving_status": &schema.Schema{ Type: schema.TypeString, Optional: true, - ValidateFunc: validation.StringInSlice([]string{ - "UNSPECIFIED", - "SERVING", - "USER_DISABLED", - "SYSTEM_DISABLED", - }, false), Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, "feature_settings": &schema.Schema{ Type: schema.TypeList, Optional: true, Computed: true, - MaxItems: 1, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", Elem: appEngineFeatureSettingsResource(), }, "name": &schema.Schema{ Type: schema.TypeString, Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, "url_dispatch_rule": &schema.Schema{ Type: schema.TypeList, Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", Elem: appEngineURLDispatchRuleResource(), }, "code_bucket": &schema.Schema{ Type: schema.TypeString, Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, "default_hostname": &schema.Schema{ Type: schema.TypeString, Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, "default_bucket": &schema.Schema{ Type: schema.TypeString, Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, "gcr_domain": &schema.Schema{ Type: schema.TypeString, Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, }, } @@ -183,14 +167,17 @@ func appEngineURLDispatchRuleResource() *schema.Resource { "domain": &schema.Schema{ Type: schema.TypeString, Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, "path": &schema.Schema{ Type: schema.TypeString, Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, "service": &schema.Schema{ Type: schema.TypeString, Computed: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, }, } @@ -202,18 +189,12 @@ func appEngineFeatureSettingsResource() *schema.Resource { "split_health_checks": &schema.Schema{ Type: schema.TypeBool, Optional: true, + Removed: "This field has been removed. Use the google_app_engine_application resource instead.", }, }, } } -func resourceGoogleProjectCustomizeDiff(diff *schema.ResourceDiff, meta interface{}) error { - if old, _ := diff.GetChange("app_engine.0.location_id"); diff.HasChange("app_engine.0.location_id") && old != nil && old.(string) != "" { - return fmt.Errorf("Cannot change app_engine.0.location_id once the app is created.") - } - return nil -} - func resourceGoogleProjectCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) @@ -261,24 +242,6 @@ func resourceGoogleProjectCreate(d *schema.ResourceData, meta interface{}) error } } - // set up App Engine, too - app, err := expandAppEngineApp(d) - if err != nil { - return err - } - if app != nil { - log.Printf("[DEBUG] Enabling App Engine") - // enable the app engine APIs so we can create stuff - if err = enableService("appengine.googleapis.com", project.ProjectId, config); err != nil { - return fmt.Errorf("Error enabling the App Engine Admin API required to configure App Engine applications: %s", err) - } - log.Printf("[DEBUG] Enabled App Engine") - err = createAppEngineApp(config, pid, app) - if err != nil { - return err - } - } - err = resourceGoogleProjectRead(d, meta) if err != nil { return err @@ -301,23 +264,6 @@ func resourceGoogleProjectCreate(d *schema.ResourceData, meta interface{}) error return nil } -func createAppEngineApp(config *Config, pid string, app *appengine.Application) error { - app.Id = pid - log.Printf("[DEBUG] Creating App Engine App") - op, err := config.clientAppEngine.Apps.Create(app).Do() - if err != nil { - return fmt.Errorf("Error creating App Engine application: %s", err.Error()) - } - - // Wait for the operation to complete - waitErr := appEngineOperationWait(config.clientAppEngine, op, pid, "App Engine app to create") - if waitErr != nil { - return waitErr - } - log.Printf("[DEBUG] Created App Engine App") - return nil -} - func resourceGoogleProjectRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) pid := d.Id() @@ -340,6 +286,10 @@ func resourceGoogleProjectRead(d *schema.ResourceData, meta interface{}) error { d.Set("name", p.Name) d.Set("labels", p.Labels) + // We get app_engine.#: "" => "" without this set + // Remove when app_engine field is removed from schema completely + d.Set("app_engine", nil) + if p.Parent != nil { switch p.Parent.Type { case "organization": @@ -371,29 +321,6 @@ func resourceGoogleProjectRead(d *schema.ResourceData, meta interface{}) error { d.Set("billing_account", _ba) } - // read the App Engine app, if one exists - // we don't have the config available for import, so we can't rely on - // that to read it. And honestly, we want to know if an App exists that - // shouldn't. So this tries to read it, sets it to empty if none exists, - // or sets it in state if one does exist. - app, err := config.clientAppEngine.Apps.Get(pid).Do() - if err != nil && !isGoogleApiErrorWithCode(err, 404) && !isApiNotEnabledError(err) { - return fmt.Errorf("Error retrieving App Engine application %q: %s", pid, err.Error()) - } else if isGoogleApiErrorWithCode(err, 404) { - d.Set("app_engine", []map[string]interface{}{}) - } else if isApiNotEnabledError(err) { - log.Printf("[WARN] App Engine Admin API not enabled, please enable it to read App Engine info about project %q: %s", pid, err.Error()) - d.Set("app_engine", []map[string]interface{}{}) - } else { - appBlocks, err := flattenAppEngineApp(app) - if err != nil { - return fmt.Errorf("Error serializing App Engine app: %s", err.Error()) - } - err = d.Set("app_engine", appBlocks) - if err != nil { - return fmt.Errorf("Error setting App Engine application in state. This is a bug, please report it at https://github.com/terraform-providers/terraform-provider-google/issues. Error is:\n%s", err.Error()) - } - } return nil } @@ -498,39 +425,7 @@ func resourceGoogleProjectUpdate(d *schema.ResourceData, meta interface{}) error d.SetPartial("labels") } - // App Engine App has changed - if ok := d.HasChange("app_engine"); ok { - app, err := expandAppEngineApp(d) - if err != nil { - return err - } - // ignore if app is now not set; that should force new resource using customizediff - if app != nil { - if old, new := d.GetChange("app_engine.#"); (old == nil || old.(int) < 1) && new != nil && new.(int) > 0 { - err = createAppEngineApp(config, pid, app) - if err != nil { - return err - } - } else { - log.Printf("[DEBUG] Updating App Engine App") - op, err := config.clientAppEngine.Apps.Patch(pid, app).UpdateMask("authDomain,servingStatus,featureSettings.splitHealthChecks").Do() - if err != nil { - return fmt.Errorf("Error creating App Engine application: %s", err.Error()) - } - - // Wait for the operation to complete - waitErr := appEngineOperationWait(config.clientAppEngine, op, pid, "App Engine app to update") - if waitErr != nil { - return waitErr - } - log.Printf("[DEBUG] Updated App Engine App") - } - d.SetPartial("app_engine") - } - } - d.Partial(false) - return resourceGoogleProjectRead(d, meta) } @@ -617,86 +512,3 @@ func updateProjectBillingAccount(d *schema.ResourceData, config *Config) error { return fmt.Errorf("Timed out waiting for billing account to return correct value. Waiting for %s, got %s.", name, strings.TrimPrefix(ba.BillingAccountName, "billingAccounts/")) } - -func expandAppEngineApp(d *schema.ResourceData) (*appengine.Application, error) { - blocks := d.Get("app_engine").([]interface{}) - if len(blocks) < 1 { - return nil, nil - } - if len(blocks) > 1 { - return nil, fmt.Errorf("only one app_engine block may be defined per project") - } - result := &appengine.Application{ - AuthDomain: d.Get("app_engine.0.auth_domain").(string), - LocationId: d.Get("app_engine.0.location_id").(string), - Id: d.Get("project_id").(string), - GcrDomain: d.Get("app_engine.0.gcr_domain").(string), - ServingStatus: d.Get("app_engine.0.serving_status").(string), - } - featureSettings, err := expandAppEngineFeatureSettings(d, "app_engine.0.") - if err != nil { - return nil, err - } - result.FeatureSettings = featureSettings - return result, nil -} - -func flattenAppEngineApp(app *appengine.Application) ([]map[string]interface{}, error) { - result := map[string]interface{}{ - "auth_domain": app.AuthDomain, - "code_bucket": app.CodeBucket, - "default_bucket": app.DefaultBucket, - "default_hostname": app.DefaultHostname, - "location_id": app.LocationId, - "name": app.Name, - "serving_status": app.ServingStatus, - } - dispatchRules, err := flattenAppEngineDispatchRules(app.DispatchRules) - if err != nil { - return nil, err - } - result["url_dispatch_rule"] = dispatchRules - featureSettings, err := flattenAppEngineFeatureSettings(app.FeatureSettings) - if err != nil { - return nil, err - } - result["feature_settings"] = featureSettings - return []map[string]interface{}{result}, nil -} - -func expandAppEngineFeatureSettings(d *schema.ResourceData, prefix string) (*appengine.FeatureSettings, error) { - blocks := d.Get(prefix + "feature_settings").([]interface{}) - if len(blocks) < 1 { - return nil, nil - } - if len(blocks) > 1 { - return nil, fmt.Errorf("only one feature_settings block may be defined per app") - } - return &appengine.FeatureSettings{ - SplitHealthChecks: d.Get(prefix + "feature_settings.0.split_health_checks").(bool), - // force send SplitHealthChecks, so if it's set to false it still gets disabled - ForceSendFields: []string{"SplitHealthChecks"}, - }, nil -} - -func flattenAppEngineFeatureSettings(settings *appengine.FeatureSettings) ([]map[string]interface{}, error) { - if settings == nil { - return []map[string]interface{}{}, nil - } - result := map[string]interface{}{ - "split_health_checks": settings.SplitHealthChecks, - } - return []map[string]interface{}{result}, nil -} - -func flattenAppEngineDispatchRules(rules []*appengine.UrlDispatchRule) ([]map[string]interface{}, error) { - results := make([]map[string]interface{}, 0, len(rules)) - for _, rule := range rules { - results = append(results, map[string]interface{}{ - "domain": rule.Domain, - "path": rule.Path, - "service": rule.Service, - }) - } - return results, nil -} diff --git a/google-beta/resource_google_project_test.go b/google-beta/resource_google_project_test.go index 833c95f122..89e699402d 100644 --- a/google-beta/resource_google_project_test.go +++ b/google-beta/resource_google_project_test.go @@ -187,134 +187,6 @@ func TestAccProject_parentFolder(t *testing.T) { }) } -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] @@ -495,96 +367,6 @@ resource "google_folder" "folder1" { `, 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) == "" { diff --git a/website/docs/r/google_project.html.markdown b/website/docs/r/google_project.html.markdown index fb88135d5c..48b963e093 100755 --- a/website/docs/r/google_project.html.markdown +++ b/website/docs/r/google_project.html.markdown @@ -61,20 +61,6 @@ resource "google_folder" "department1" { } ``` -To create a project with an App Engine app attached - -```hcl -resource "google_project" "my-app-engine-app" { - name = "App Engine Project" - project_id = "app-engine-project" - org_id = "1234567" - - app_engine { - location_id = "us-central" - } -} -``` - ## Argument Reference The following arguments are supported: @@ -113,19 +99,6 @@ The following arguments are supported: name to match the GCP Console UI. Setting this field to false will enable the Compute Engine API which is required to delete the network. -* `app_engine` - (Optional) A block of configuration to enable an App Engine app. Setting this - field will enabled the App Engine Admin API, which is required to manage the app. - -The `app_engine` block has the following configuration options: - -* `location_id` - (Required) The [location](https://cloud.google.com/appengine/docs/locations) - to serve the app from. -* `auth_domain` - (Optional) The domain to authenticate users with when using App Engine's User API. -* `serving_status` - (Optional) The serving status of the app. Note that this can't be updated at the moment. -* `feature_settings` - (Optional) A block of optional settings to configure specific App Engine features: - * `split_health_checks` - (Optional) Set to false to use the legacy health check instead of the readiness - and liveness checks. - ## Attributes Reference In addition to the arguments listed above, the following computed attributes are @@ -133,13 +106,6 @@ exported: * `number` - The numeric identifier of the project. -* `app_engine.0.name` - Unique name of the app, usually `apps/{PROJECT_ID}` -* `app_engine.0.url_dispatch_rule` - A list of dispatch rule blocks. Each block has a `domain`, `path`, and `service` field. -* `app_engine.0.code_bucket` - The GCS bucket code is being stored in for this app. -* `app_engine.0.default_hostname` - The default hostname for this app. -* `app_engine.0.default_bucket` - The GCS bucket content is being stored in for this app. -* `app_engine.0.gcr_domain` - The GCR domain used for storing managed Docker images for this app. - ## Import Projects can be imported using the `project_id`, e.g.