-
Notifications
You must be signed in to change notification settings - Fork 1.8k
App Engine Service Split Traffic resource #2269
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
Conversation
Hello! I am a robot who works on Magic Modules PRs. I have detected that you are a community contributor, so your PR will be assigned to someone with a commit-bit on this repo for initial review. They will authorize it to run through our CI pipeline, which will generate downstream PRs. Thanks for your contribution! A human will be with you soon. @emilymye, please review this PR or find an appropriate assignee. |
@rileykarson I noticed that there is no option for the custom_create code in the generator. I thought it fitting to add this. This helps in Appengine Service Resource which does not have Create method. If custom_create was not deliberately added, then I can move the resource under third_party as the custom resource. Thanks |
Example template to follow. |
For Terraform, I think we want to expose this under another name than As a user, I'd expect my config to look like: resource "google_app_engine_service" "frontend" {
id = "my-frontend-service"
}
resource "google_app_engine_standard_app_version" "frontend_v2" {
version_id = "v2"
service = google_app_engine_service.frontend.id
...
} When in reality, it's more likely to be like this, because the dependency is flipped: resource "google_app_engine_standard_app_version" "frontend_v2" {
version_id = "v2"
service = "my-frontend-service"
...
}
resource "google_app_engine_service" "frontend" {
id = google_app_engine_standard_version.frontend_v2.service
} Instead, I'd lean towards exposing the resource as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like StandardVersion, this is going to end up a bit of an atypical resource. Per my comment above, I think we should focus on the traffic splitting scenario since it's the only configurable portion of a service, and we should make this resource apply the specified traffic split on Create
as well as Update
.
products/appengine/api.yaml
Outdated
name: 'split' | ||
description: | | ||
Mapping that defines fractional HTTP traffic diversion to different versions within the service. | ||
required: false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
required: false | |
required: true |
Since the resource doesn't do anything else, I'm incline to make the traffic split required: true
products/appengine/api.yaml
Outdated
@@ -73,24 +73,58 @@ objects: | |||
For example, an application that handles customer requests might include separate services to handle tasks such as backend data analysis or API requests from mobile devices. | |||
Each service has a collection of versions that define a specific set of code used to implement the functionality of that service. | |||
base_url: 'apps/{{project}}/services' | |||
self_link: 'apps/{{project}}/services/{{id}}' | |||
update_url: 'apps/{{project}}/services/{{service_id}}?migrateTraffic=False&updateMask=split' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
update_url: 'apps/{{project}}/services/{{service_id}}?migrateTraffic=False&updateMask=split' | |
update_url: 'apps/{{project}}/services/{{service_id}}?migrateTraffic={{migrateTraffic}}' |
We can expose migrateTraffic
as configurable. If you add it to parameters:
(instead of properties:
), and make it url_param_only: true
in terraform.yaml
, this should work as intended.
The update mask should be appended automatically by setting update_mask: true
.
provider/terraform/custom_code.rb
Outdated
# This code replaces the entire create method. Since the create | ||
# method's function header can't be changed, the template | ||
# inserts that for you - do not include it in your custom code. | ||
attr_reader :custom_create |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To answer your question, this not existing was just a matter of not needing it yet.
products/appengine/terraform.yaml
Outdated
import_format: ["apps/{{project}}/services/{{service_id}}"] | ||
mutex: "apps/{{project}}/services/{{service_id}}" | ||
custom_code: !ruby/object:Provider::Terraform::CustomCode | ||
custom_create: templates/terraform/custom_create/noop_on_create_appengine_version.go.erb |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of doing nothing on update, what if we set the traffic split on create as well? This would mirror the IAM Policy resources that immediately set their values on create.
Setting immediately on singleton resources like this is a little unfortunate- Terraform won't show the old values in plan. On the other hand, it means that creating the resource actually does something, and the resource won't effectively always permadiff. IMO that matches to our expectations better, especially given the precedent in IAM.
To accomplish this, you can set the create_url
to the same value as the update_url and create_verb
to PATCH
in api.yaml
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doing this may require using a custom encoder to add an update mask on create, since we haven't added a resource doing an HTTP PATCH on Terraform Create action before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure whether I need to add create_encoder in this case. So there are two things:
- Having updateMask in the create_url, I think this can be handled by just adding the constant like:
create_url: 'apps/{{project}}/services/{{service_id}}?{{migrateTraffic}}&updateMask=split'
- Identifying if we need to add shardBy also in the updateMask (I am looking into it)
products/appengine/api.yaml
Outdated
required: false | ||
properties: | ||
- !ruby/object:Api::Type::Enum | ||
name: 'shardBy' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is one of these values automatically selected by the API? Or does it actually return UNSPECIFIED
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am going to test it shortly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you get a chance to test?
products/appengine/api.yaml
Outdated
- :IP | ||
- :RANDOM | ||
- !ruby/object:Api::Type::KeyValuePairs | ||
name: 'allocations' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels required: true
. Did you find it wasn't the case?
@rileykarson Struggling with few things:
thanks |
@rileykarson Kindly review this. I have made all the required changes, One question is still open. |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry! I had some other work to get done yesterday and didn't have time to make a review pass.
Per your questions;
- Yep, adding it was correct. I'm not sure why we restrict the list of verbs allowed.
- If you make the
id
/service
(renamed based on my comment below) fieldurl_param_only
, it will be excluded from the update_mask. - Sorry! I changed my mind a little since before, do you actually mind defining it as an entirely separate resource in
api.yaml
? - Sounds good to me- I don't think there's a meaningful deletion state for a service's traffic, so deletion doing nothing makes the most sense.
products/appengine/api.yaml
Outdated
@@ -67,30 +67,74 @@ objects: | |||
- ALLOW | |||
- DENY | |||
- !ruby/object:Api::Resource | |||
name: 'Service' | |||
name: 'ServiceSplitTraffic' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of renaming Service
, would you mind duplicating this into a separate resource definition? It feels a little silly, but it's the best way to represent fine-grained resources right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will also require exclusions in Ansible / InSpec for the Magician to work.
products/appengine/api.yaml
Outdated
properties: | ||
- !ruby/object:Api::Type::String | ||
name: 'name' | ||
output: true | ||
description: | | ||
Full path to the Service resource in the API. Example apps/myapp/services/default. | ||
This field is used in responses only. Any value specified here in a request is ignored. | ||
- !ruby/object:Api::Type::String | ||
name: 'id' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have some liberty in terms of what we can call this field because it doesn't actually correspond to any fields we care about in the API. What do you think of service
instead of id
/service_id
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wdyt here?
8e35ff1
to
6cb1cfc
Compare
@rileykarson Please review now. Also here is my loud thinking on this: Use case 1: Problem: In this use case terraform destroy will leave the service myapp and version v1 when destroying all the resources. Solution: Use the google_app_engine_service resource to delete the final version. For this we shall need the google_app_engine_service resource as I did in the first iteration which will ignore the create but allow deletion. |
@rileykarson Can you please review this. And give your comments. |
Hi! I'm the modular magician, I work on Magic Modules. Pull request statusesNo diff detected in Ansible. New Pull RequestsI built this PR into one or more new PRs on other repositories, and when those are closed, this PR will also be merged and closed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of an approach that requires adding a google_app_engine_service
resource that's only really used for deletion, I'd prefer we add delete_service_on_destroy
to google_app_engine_standard_app_version
. That boolean would be mutually exclusive with noop_on_destroy
, and would delete the service at the same time as that version.
Terraform will process deletion of versions in parallel for most configs; that means that the version that deletes the parent may delete it before the other version is "deleted" by Terraform. That'll be fine, though, deletion succeeds if the parent / resource isn't found.
products/appengine/api.yaml
Outdated
properties: | ||
- !ruby/object:Api::Type::String | ||
name: 'name' | ||
output: true | ||
description: | | ||
Full path to the Service resource in the API. Example apps/myapp/services/default. | ||
This field is used in responses only. Any value specified here in a request is ignored. | ||
- !ruby/object:Api::Type::String | ||
name: 'id' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wdyt here?
products/appengine/api.yaml
Outdated
required: false | ||
properties: | ||
- !ruby/object:Api::Type::Enum | ||
name: 'shardBy' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you get a chance to test?
Hi! I'm the modular magician, I work on Magic Modules. Pull request statusesterraform-provider-google-beta already has an open PR. New Pull RequestsI didn't open any new pull requests because of this PR. |
provider/terraform/custom_code.rb
Outdated
@@ -96,6 +96,10 @@ class CustomCode < Api::Object | |||
# method's function header can't be changed, the template | |||
# inserts that for you - do not include it in your custom code. | |||
attr_reader :custom_delete | |||
# This code replaces the entire create method. Since the create |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as we're not using this, do you mind removing it + the changes in resource.erb
? I'd rather not add unused code.
94e9be6
to
a8210ad
Compare
Hi! I'm the modular magician. Your PR generated some diffs in downstreams - here they are. Diff report:Terraform GA: Diff ( 20 files changed, 697 insertions(+), 94 deletions(-)) |
@rileykarson DONE :) Had a bad time with this one.... even the changes from resource.erb were not going away git acting up with resolving merge conflict. |
Hi! I'm the modular magician. Your PR generated some diffs in downstreams - here they are. Diff report:Terraform GA: Diff ( 5 files changed, 647 insertions(+), 2 deletions(-)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks for working through me on the drawn out process to get this resource added! Since AppEngine is one of GCP's oldest APIs, it doesn't tend to fit the model of API behaviour we built MM with, or the model of declarative resources that's evolved since AppEngine's creation.
…#2269) * Appengine Service resource * Added custom_create in custom_code. * Added appengine service resource * Updates based on review * Updates based on review * update based on review * Updates * Update * Update * Updated the example * Updated the example and website link * Skip Delete on test and noop * Rebased and merged the appengine terraform.yaml * Updates * Fixes * updated the example and excluded split from reading * updated the example * Reverted custom_create related code * Reverted resource.erb * updates * removed the error check * Updates * Fixes * Fixed the import in test script * Updates as per review * chanded the operation to OpAsync * Updated the Objects from Async to OpAsync * Fixed the example primary resource name * Testing by removing id_format and import_format * reverted import and id format and also the example * Fixed the id_format * Updates based on review * Typo fix * Changed url_param_only to api_name * Updated the resource.erb from upstream/master
Release Note for Downstream PRs (will be copied)