Remove google_project.app_engine (#2386)

<!-- This change is generated by MagicModules. -->
/cc @rileykarson
This commit is contained in:
The Magician 2018-11-02 09:47:16 -07:00 committed by Nathan McKinley
parent 1b1538a6a4
commit ed18fb9ea0
3 changed files with 23 additions and 463 deletions

View File

@ -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.#: "" => "<computed>" 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
}

View File

@ -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) == "" {

View File

@ -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.