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

add app engine flex version #3110

Merged
2 changes: 2 additions & 0 deletions products/appengine/ansible.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides
exclude: true
StandardAppVersion: !ruby/object:Overrides::Ansible::ResourceOverride
exclude: true
FlexibleAppVersion: !ruby/object:Overrides::Ansible::ResourceOverride
exclude: true
DomainMapping: !ruby/object:Overrides::Ansible::ResourceOverride
exclude: true
files: !ruby/object:Provider::Config::Files
Expand Down
651 changes: 645 additions & 6 deletions products/appengine/api.yaml

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions products/appengine/inspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides
exclude: true
service: !ruby/object:Overrides::Inspec::PropertyOverride
exclude: true
FlexibleAppVersion: !ruby/object:Overrides::Inspec::ResourceOverride
exclude: true
Service: !ruby/object:Overrides::Inspec::ResourceOverride
exclude: true
ServiceSplitTraffic: !ruby/object:Overrides::Inspec::ResourceOverride
Expand Down
54 changes: 54 additions & 0 deletions products/appengine/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,60 @@ overrides: !ruby/object:Overrides::ResourceOverrides
service_name: "ae-service"
test_env_vars:
org_id: :ORG_ID
FlexibleAppVersion: !ruby/object:Overrides::Terraform::ResourceOverride
import_format: ["apps/{{project}}/services/{{service}}/versions/{{version_id}}"]
mutex: "apps/{{project}}"
error_retry_predicates: ["isAppEngineRetryableError"]
parameters:
service: !ruby/object:Overrides::Terraform::PropertyOverride
default_from_api: true
required: false
virtual_fields:
- !ruby/object:Provider::Terraform::VirtualFields
name: 'noop_on_destroy'
description: |
If set to `true`, the application version will not be deleted.
- !ruby/object:Provider::Terraform::VirtualFields
name: 'delete_service_on_destroy'
description: |
If set to `true`, the service will be deleted if it is the last version.
custom_code: !ruby/object:Provider::Terraform::CustomCode
custom_delete: templates/terraform/custom_delete/appversion_delete.go.erb
test_check_destroy: templates/terraform/custom_check_destroy/appengine.go.erb
encoder: templates/terraform/encoders/flex_app_version.go.erb
properties:
id: !ruby/object:Overrides::Terraform::PropertyOverride
name: 'version_id'
deployment: !ruby/object:Overrides::Terraform::PropertyOverride
ignore_read: true
entrypoint: !ruby/object:Overrides::Terraform::PropertyOverride
ignore_read: true
envVariables: !ruby/object:Overrides::Terraform::PropertyOverride
ignore_read: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these are things that can be updated by other tools after create you should override the description here indicating that Terraform won't detect drift.

network.subnetworkName: !ruby/object:Overrides::Terraform::PropertyOverride
name: 'subnetwork'
betaSettings: !ruby/object:Overrides::Terraform::PropertyOverride
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there default betaSettings that come from the api?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there are, use_deployment_manager, no_appserver_affinity, and has_docker_image that I've seen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is confusing - those values are returned in the response, but if I do an update (which is just a POST for a new version), it says those values (no_appserver_affinity and use_deployment_manager) aren't allowed to be set in the flexible environment. Which makes me think we should ignore_read these, however, if there aren't conflicts with other fields, we probably want to write them to state.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be set by the user at all? If they can't be sent in a Create or Update then I would just remove the field completely. We can always add them back in as fully computed if a user needs this as an output. Growing a resource is much easier than shrinking it.

# https://issuetracker.google.com/issues/150157861
ignore_read: true
# default_from_api: true
# maxConcurrentRequests defaults to a runtime-specific value
automaticScaling.maxConcurrentRequests: !ruby/object:Overrides::Terraform::PropertyOverride
default_from_api: true
# runtimeApiVersion defaults to a runtime-specific value
runtimeApiVersion: !ruby/object:Overrides::Terraform::PropertyOverride
default_from_api: true
examples:
- !ruby/object:Provider::Terraform::Examples
name: "app_engine_flexible_app_version"
primary_resource_id: "myapp_v1"
ignore_read_extra:
- "delete_service_on_destroy"
vars:
project_id: "tf-test-project"
bucket_name: "appengine-static-content"
service_name: "service-"
test_env_vars:
org_id: :ORG_ID
Service: !ruby/object:Overrides::Terraform::ResourceOverride
exclude: true
DomainMapping: !ruby/object:Overrides::Terraform::ResourceOverride
Expand Down
10 changes: 5 additions & 5 deletions templates/terraform/custom_delete/appversion_delete.go.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

if d.Get("noop_on_destroy") == true {
log.Printf("[DEBUG] Keeping the StandardAppVersion %q", d.Id())
log.Printf("[DEBUG] Keeping the AppVersion %q", d.Id())
return nil
}
config := meta.(*Config)
Expand Down Expand Up @@ -43,19 +43,19 @@ if d.Get("delete_service_on_destroy") == true {
return err
}
var obj map[string]interface{}
log.Printf("[DEBUG] Deleting StandardAppVersion %q", d.Id())
log.Printf("[DEBUG] Deleting AppVersion %q", d.Id())
res, err := sendRequestWithTimeout(config, "DELETE", project, url, obj, d.Timeout(schema.TimeoutDelete)<%= object.error_retry_predicates ? ", " + object.error_retry_predicates.join(',') : "" -%>)
if err != nil {
return handleNotFoundError(err, d, "StandardAppVersion")
return handleNotFoundError(err, d, "AppVersion")
}
err = appEngineOperationWaitTime(
config, res, project, "Deleting StandardAppVersion",
config, res, project, "Deleting AppVersion",
int(d.Timeout(schema.TimeoutDelete).Minutes()))

if err != nil {
return err
}
log.Printf("[DEBUG] Finished deleting StandardAppVersion %q: %#v", d.Id(), res)
log.Printf("[DEBUG] Finished deleting AppVersion %q: %#v", d.Id(), res)
return nil


Expand Down
2 changes: 2 additions & 0 deletions templates/terraform/encoders/flex_app_version.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
obj["env"] = "flex"
return obj, nil
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
resource "google_app_engine_flexible_app_version" "<%= ctx[:primary_resource_id] %>" {
version_id = "v1"
service = "<%= ctx[:vars]['service_name'] %>"
runtime = "nodejs"

entrypoint {
shell = "node ./app.js"
}

deployment {
zip {
source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/${google_storage_bucket_object.object.name}"
}
}

liveness_check {
path = "/"
}

readiness_check {
path = "/"
}

env_variables = {
port = "8080"
}

automatic_scaling {
cool_down_period = "120s"
cpu_utilization {
target_utilization = 0.5
}
}


delete_service_on_destroy = true
}

resource "google_app_engine_flexible_app_version" "myflexapp_v2" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I have a slight preference for these to be 2 different example blocks instead of 2 app_versions in a single example. If possible I like to have each example be a single use case so that the example can have a title block like "NodeJs Example with scaling" and a user doesn't have to parse both whole resource blocks to understand the difference between the two. As pointed out by a user aws s3 bucket does a better job of this than many of our resources.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm totally fine with that, I copied this from the standard app (again), and it appears both versions are the same. I'm guessing the example of it was just having 2 versions to the same service? But if that doesn't seem like a great thing to show in an example, I'm happy to remove v2.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only difference I saw was noop_on_destroy = true vs delete_service_on_destroy = true which doesn't seem like it's worth all of the wasted real estate on the docs page.

version_id = "v2"
service = "<%= ctx[:vars]['service_name'] %>"
runtime = "nodejs"

entrypoint {
shell = "node ./app.js"
}

deployment {
zip {
source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/${google_storage_bucket_object.object.name}"
}
}

liveness_check {
path = "/"
}

readiness_check {
path = "/"
}

env_variables = {
port = "8080"
}

automatic_scaling {
cool_down_period = "120s"
cpu_utilization {
target_utilization = 0.5
}
}

noop_on_destroy = true
}

resource "google_storage_bucket" "bucket" {
name = "<%= ctx[:vars]['bucket_name'] %>"
}

resource "google_storage_bucket_object" "object" {
name = "hello-world.zip"
bucket = google_storage_bucket.bucket.name
source = "./test-fixtures/appengine/hello-world.zip"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package google

import (
"context"
"log"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

// This will sweep both Standard and Flexible App Engine App Versions
func init() {
resource.AddTestSweepers("AppEngineAppVersion", &resource.Sweeper{
Name: "AppEngineAppVersion",
F: testSweepAppEngineAppVersion,
})
}

// At the time of writing, the CI only passes us-central1 as the region
func testSweepAppEngineAppVersion(region string) error {
resourceName := "AppEngineAppVersion"
log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName)

config, err := sharedConfigForRegion(region)
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err)
return err
}

err = config.LoadAndValidate(context.Background())
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err)
return err
}

servicesUrl := "https://appengine.googleapis.com/v1/apps/" + config.Project + "/services"
res, err := sendRequest(config, "GET", config.Project, servicesUrl, nil)
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", servicesUrl, err)
return nil
}

resourceList, ok := res["services"]
if !ok {
log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.")
return nil
}

rl := resourceList.([]interface{})

log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName)
// items who don't match the tf-test prefix
nonPrefixCount := 0
for _, ri := range rl {
obj := ri.(map[string]interface{})
if obj["id"] == nil {
log.Printf("[INFO][SWEEPER_LOG] %s resource id was nil", resourceName)
return nil
}

id := obj["id"].(string)
// Only sweep resources with the test prefix
if !strings.HasPrefix(id, "tf-test") {
nonPrefixCount++
continue
}

deleteUrl := servicesUrl + "/" + id
// Don't wait on operations as we may have a lot to delete
_, err = sendRequest(config, "DELETE", config.Project, deleteUrl, nil)
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err)
} else {
log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, id)
}
}

if nonPrefixCount > 0 {
log.Printf("[INFO][SWEEPER_LOG] %d items without tf_test prefix remain.", nonPrefixCount)
}

return nil
}
Loading