Skip to content

Commit dda05b1

Browse files
appengine: Add app_engine_bundled_services and make fields immutable (GoogleCloudPlatform#18062)
1 parent 9f0cdbf commit dda05b1

3 files changed

Lines changed: 288 additions & 7 deletions

File tree

mmv1/products/appengine/StandardAppVersion.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,17 @@ samples:
7474
org_id: ORG_ID
7575
ignore_read_extra:
7676
- delete_service_on_destroy
77+
- name: app_engine_standard_app_version_bundled_services
78+
primary_resource_id: gae-std-app-ver-bundled
79+
steps:
80+
- name: app_engine_standard_app_version_bundled_services
81+
resource_id_vars:
82+
project_id: tf-test-project
83+
service_id: bundled-service
84+
bucket_name: tf-test-gae-bkt-bundled
85+
sa_email: gae-sa
86+
ignore_read_extra:
87+
- delete_service_on_destroy
7788
virtual_fields:
7889
- name: noop_on_destroy
7990
description: |
@@ -125,6 +136,10 @@ properties:
125136
type: Boolean
126137
description: |
127138
Allows App Engine second generation runtimes to access the legacy bundled services.
139+
Cannot specify both `app_engine_apis` and 'app_engine_bundled_services` together.
140+
immutable: true
141+
conflicts:
142+
- app_engine_bundled_services
128143
- name: runtimeApiVersion
129144
type: String
130145
description: |
@@ -443,3 +458,32 @@ properties:
443458
**Note:** When managing the number of instances at runtime through the App Engine Admin API or the (now deprecated) Python 2
444459
Modules API set_num_instances() you must use `lifecycle.ignore_changes = ["manual_scaling"[0].instances]` to prevent drift detection.
445460
required: true
461+
- name: 'appEngineBundledServices'
462+
type: Array
463+
# Developer Note: This field is write-only because the App Engine GET Admin API
464+
# does not return it in the response (we use ignore_read: true to prevent state drift to null).
465+
# It is marked is_set: true to represent the list as a set in Terraform so configuration
466+
# ordering differences do not trigger spurious plans.
467+
description: |
468+
A list of legacy bundled services to enable for this version on an App Engine second-generation runtime.
469+
Cannot specify both `app_engine_apis` and `app_engine_bundled_services` together.
470+
is_set: true
471+
immutable: true
472+
ignore_read: true
473+
item_type:
474+
type: Enum
475+
enum_values:
476+
- 'BUNDLED_SERVICE_TYPE_APP_IDENTITY_SERVICE'
477+
- 'BUNDLED_SERVICE_TYPE_BLOBSTORE'
478+
- 'BUNDLED_SERVICE_TYPE_CAPABILITY_SERVICE'
479+
- 'BUNDLED_SERVICE_TYPE_DATASTORE_V3'
480+
- 'BUNDLED_SERVICE_TYPE_IMAGES'
481+
- 'BUNDLED_SERVICE_TYPE_MAIL'
482+
- 'BUNDLED_SERVICE_TYPE_MEMCACHE'
483+
- 'BUNDLED_SERVICE_TYPE_MODULES'
484+
- 'BUNDLED_SERVICE_TYPE_SEARCH'
485+
- 'BUNDLED_SERVICE_TYPE_TASKQUEUES'
486+
- 'BUNDLED_SERVICE_TYPE_URLFETCH'
487+
- 'BUNDLED_SERVICE_TYPE_USERS'
488+
conflicts:
489+
- app_engine_apis
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
resource "google_service_account" "service_account" {
2+
account_id = "{{index $.ResourceIdVars "sa_email"}}"
3+
display_name = "Test Service Account for GAE"
4+
}
5+
6+
resource "google_project_iam_member" "gae_api" {
7+
project = google_service_account.service_account.project
8+
role = "roles/compute.networkUser"
9+
member = "serviceAccount:${google_service_account.service_account.email}"
10+
}
11+
12+
resource "google_project_iam_member" "storage_viewer" {
13+
project = google_service_account.service_account.project
14+
role = "roles/storage.objectViewer"
15+
member = "serviceAccount:${google_service_account.service_account.email}"
16+
}
17+
18+
resource "google_storage_bucket" "bucket" {
19+
name = "{{index $.ResourceIdVars "bucket_name"}}"
20+
location = "US"
21+
}
22+
23+
resource "google_storage_bucket_object" "requirements" {
24+
name = "requirements.txt"
25+
bucket = google_storage_bucket.bucket.name
26+
source = "./test-fixtures/hello-world-flask/requirements.txt"
27+
}
28+
29+
resource "google_storage_bucket_object" "main" {
30+
name = "main.py"
31+
bucket = google_storage_bucket.bucket.name
32+
source = "./test-fixtures/hello-world-flask/main.py"
33+
}
34+
35+
resource "google_app_engine_standard_app_version" "{{$.PrimaryResourceId}}" {
36+
version_id = "v1"
37+
service = "{{index $.ResourceIdVars "service_id"}}"
38+
runtime = "python310"
39+
40+
deployment {
41+
files {
42+
name = "main.py"
43+
source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/${google_storage_bucket_object.main.name}"
44+
}
45+
files {
46+
name = "requirements.txt"
47+
source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/${google_storage_bucket_object.requirements.name}"
48+
}
49+
}
50+
51+
entrypoint {
52+
shell = "gunicorn -b :$PORT main:app"
53+
}
54+
55+
# Testing the app_engine_bundled_services field
56+
app_engine_bundled_services = ["BUNDLED_SERVICE_TYPE_MAIL", "BUNDLED_SERVICE_TYPE_DATASTORE_V3"]
57+
58+
delete_service_on_destroy = true
59+
service_account = google_service_account.service_account.email
60+
61+
depends_on = [
62+
google_project_iam_member.gae_api,
63+
google_project_iam_member.storage_viewer,
64+
]
65+
}

mmv1/third_party/terraform/services/appengine/resource_app_engine_standard_app_version_test.go

Lines changed: 179 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ import (
66
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
77
"github.com/hashicorp/terraform-provider-google/google/acctest"
88
"github.com/hashicorp/terraform-provider-google/google/envvar"
9-
_ "github.com/hashicorp/terraform-provider-google/google/services/appengine"
10-
_ "github.com/hashicorp/terraform-provider-google/google/services/resourcemanager"
11-
_ "github.com/hashicorp/terraform-provider-google/google/services/storage"
12-
_ "github.com/hashicorp/terraform-provider-google/google/services/vpcaccess"
139
)
1410

1511
func TestAccAppEngineStandardAppVersion_update(t *testing.T) {
@@ -87,7 +83,7 @@ resource "google_app_engine_standard_app_version" "foo" {
8783
project = google_project_service.project.project
8884
version_id = "v1"
8985
service = "default"
90-
runtime = "python38"
86+
runtime = "python310"
9187
9288
entrypoint {
9389
shell = "gunicorn -b :$PORT main:app"
@@ -204,7 +200,7 @@ resource "google_app_engine_standard_app_version" "foo" {
204200
project = google_project_service.project.project
205201
version_id = "v1"
206202
service = "default"
207-
runtime = "python38"
203+
runtime = "python310"
208204
209205
vpc_access_connector {
210206
name = "${google_vpc_access_connector.bar.id}"
@@ -297,7 +293,7 @@ resource "google_app_engine_standard_app_version" "foo" {
297293
project = google_project_service.project.project
298294
version_id = "v1"
299295
service = "default"
300-
runtime = "python38"
296+
runtime = "python310"
301297
302298
entrypoint {
303299
shell = "gunicorn -b :$PORT main:app"
@@ -348,3 +344,179 @@ resource "google_storage_bucket_object" "main" {
348344
source = "./test-fixtures/hello-world-flask/main.py"
349345
}`, context)
350346
}
347+
348+
func TestAccAppEngineStandardAppVersion_updateBundledServices(t *testing.T) {
349+
t.Skip("https://github.com/hashicorp/terraform-provider-google/issues/18936")
350+
t.Parallel()
351+
352+
context := map[string]interface{}{
353+
"org_id": envvar.GetTestOrgFromEnv(t),
354+
"billing_account": envvar.GetTestBillingAccountFromEnv(t),
355+
"random_suffix": acctest.RandString(t, 10),
356+
}
357+
358+
acctest.VcrTest(t, resource.TestCase{
359+
PreCheck: func() { acctest.AccTestPreCheck(t) },
360+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
361+
ExternalProviders: map[string]resource.ExternalProvider{
362+
"time": {},
363+
},
364+
CheckDestroy: testAccCheckAppEngineStandardAppVersionDestroyProducer(t),
365+
Steps: []resource.TestStep{
366+
{
367+
Config: testAccAppEngineStandardAppVersion_bundledServices(context),
368+
},
369+
{
370+
ResourceName: "google_app_engine_standard_app_version.foo",
371+
ImportState: true,
372+
ImportStateVerify: true,
373+
ImportStateVerifyIgnore: []string{"env_variables", "deployment", "entrypoint", "service", "noop_on_destroy"},
374+
},
375+
{
376+
Config: testAccAppEngineStandardAppVersion_bundledServicesUpdate(context),
377+
},
378+
{
379+
ResourceName: "google_app_engine_standard_app_version.foo",
380+
ImportState: true,
381+
ImportStateVerify: true,
382+
ImportStateVerifyIgnore: []string{"env_variables", "deployment", "entrypoint", "service", "noop_on_destroy"},
383+
},
384+
},
385+
})
386+
}
387+
388+
func testAccAppEngineStandardAppVersion_bundledServices(context map[string]interface{}) string {
389+
return acctest.Nprintf(`
390+
resource "google_project" "my_project" {
391+
name = "tf-test-appeng-std%{random_suffix}"
392+
project_id = "tf-test-appeng-std%{random_suffix}"
393+
org_id = "%{org_id}"
394+
billing_account = "%{billing_account}"
395+
deletion_policy = "DELETE"
396+
}
397+
398+
resource "google_app_engine_application" "app" {
399+
project = google_project.my_project.project_id
400+
location_id = "us-central"
401+
}
402+
403+
resource "google_project_service" "project" {
404+
project = google_project.my_project.project_id
405+
service = "appengine.googleapis.com"
406+
disable_dependent_services = false
407+
}
408+
409+
resource "google_app_engine_standard_app_version" "foo" {
410+
project = google_project_service.project.project
411+
version_id = "v1"
412+
service = "default"
413+
runtime = "python310"
414+
415+
app_engine_bundled_services = ["BUNDLED_SERVICE_TYPE_MAIL", "BUNDLED_SERVICE_TYPE_USERS"]
416+
417+
entrypoint {
418+
shell = "gunicorn -b :$PORT main:app"
419+
}
420+
421+
deployment {
422+
files {
423+
name = "main.py"
424+
source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/${google_storage_bucket_object.main.name}"
425+
}
426+
files {
427+
name = "requirements.txt"
428+
source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/${google_storage_bucket_object.requirements.name}"
429+
}
430+
}
431+
432+
instance_class = "F2"
433+
noop_on_destroy = true
434+
}
435+
436+
resource "google_storage_bucket" "bucket" {
437+
project = google_project.my_project.project_id
438+
name = "tf-test-%{random_suffix}-standard-ae-bucket"
439+
location = "US"
440+
}
441+
442+
resource "google_storage_bucket_object" "requirements" {
443+
name = "requirements.txt"
444+
bucket = google_storage_bucket.bucket.name
445+
source = "./test-fixtures/hello-world-flask/requirements.txt"
446+
}
447+
448+
resource "google_storage_bucket_object" "main" {
449+
name = "main.py"
450+
bucket = google_storage_bucket.bucket.name
451+
source = "./test-fixtures/hello-world-flask/main.py"
452+
}
453+
`, context)
454+
}
455+
456+
func testAccAppEngineStandardAppVersion_bundledServicesUpdate(context map[string]interface{}) string {
457+
return acctest.Nprintf(`
458+
resource "google_project" "my_project" {
459+
name = "tf-test-appeng-std%{random_suffix}"
460+
project_id = "tf-test-appeng-std%{random_suffix}"
461+
org_id = "%{org_id}"
462+
billing_account = "%{billing_account}"
463+
deletion_policy = "DELETE"
464+
}
465+
466+
resource "google_app_engine_application" "app" {
467+
project = google_project.my_project.project_id
468+
location_id = "us-central"
469+
}
470+
471+
resource "google_project_service" "project" {
472+
project = google_project.my_project.project_id
473+
service = "appengine.googleapis.com"
474+
disable_dependent_services = false
475+
}
476+
477+
resource "google_app_engine_standard_app_version" "foo" {
478+
project = google_project_service.project.project
479+
version_id = "v1"
480+
service = "default"
481+
runtime = "python310"
482+
483+
app_engine_bundled_services = ["mail", "user"]
484+
485+
entrypoint {
486+
shell = "gunicorn -b :$PORT main:app"
487+
}
488+
489+
deployment {
490+
files {
491+
name = "main.py"
492+
source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/${google_storage_bucket_object.main.name}"
493+
}
494+
files {
495+
name = "requirements.txt"
496+
source_url = "https://storage.googleapis.com/${google_storage_bucket.bucket.name}/${google_storage_bucket_object.requirements.name}"
497+
}
498+
}
499+
500+
instance_class = "F2"
501+
noop_on_destroy = true
502+
}
503+
504+
resource "google_storage_bucket" "bucket" {
505+
project = google_project.my_project.project_id
506+
name = "tf-test-%{random_suffix}-standard-ae-bucket"
507+
location = "US"
508+
}
509+
510+
resource "google_storage_bucket_object" "requirements" {
511+
name = "requirements.txt"
512+
bucket = google_storage_bucket.bucket.name
513+
source = "./test-fixtures/hello-world-flask/requirements.txt"
514+
}
515+
516+
resource "google_storage_bucket_object" "main" {
517+
name = "main.py"
518+
bucket = google_storage_bucket.bucket.name
519+
source = "./test-fixtures/hello-world-flask/main.py"
520+
}
521+
`, context)
522+
}

0 commit comments

Comments
 (0)