-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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
CodeDeploy: Blue/Green deployments and In-Place deployments with traffic control. #1162
CodeDeploy: Blue/Green deployments and In-Place deployments with traffic control. #1162
Conversation
- Adds DeploymentStyle, LoadBalancerInfo, and BlueGreenDeploymentConfiguration
…affic control. - There was no explicit test for this scenario and the docs referred only to a blue/green configuration.
|
||
* `deployment_type` - (Optional) Indicates whether to run a standard deployment or a blue/green deployment. Valid Values are `IN_PLACE` or `BLUE_GREEN`. | ||
|
||
* `IN_PLACE` deployment type is not supported with the `WITH_TRAFFIC_CONTROL` deployment option. |
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 is no longer true, right?
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.
You are correct. I will fix this.
- change "standard" to "in-place" - remove deployment_type restrictions
Documentation updated to reflect support of traffic control with in-place deployments. |
Will this PR be merged in the next version release? If so, when can that be expected to happen? This functionality will be much appreciated. |
Ooh, this just bit us, too! |
Thanks for the heads up. @LegNeato There is now an optional I can add support for this feature, although I'd prefer if the PR was merged as is. It has been six months after all. ;) |
It just hit us too. Is there any plan for when this will be merged to master? |
- Use an ALB in a deployment by specifying a Target Group.
Added support for using an Application Load Balancer in a deployment. A note on
These exceptions are mentioned in the documentation for this resource. |
@niclic Good work! It is undoubtedly a long-awaited feature for both CodeDeploy and Terraform. 🥇 ping @radeksimko: is there anything else that needs to be done in order to get this merged? |
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.
Hi @niclic
thank you for taking the time to submit this PR and being thorough in testing. It is looking very good and very close to mergeable state.
I left you comments there, some are more important (about optional fields & nil
checks), some are just nitpicks.
I'm not feeling overly comfortable about making all the fields Computed
, but it seems there's no way to remove loadBalancerInfo
neither blueGreenDeploymentConfiguration
. I'll raise a support ticket with AWS about this.
That said I believe that deployment_style
doesn't really need to be Computed
.
One more note - the codebase may not be entirely consistent at this point, but we have a naming convention we use for functions which convert SDK structs to schema-compatible []interface{}
etc. and back. We call them flatteners and expanders.
Using this convention buildDeploymentStyle
would be called expandDeploymentStyle
, deploymentStyleToMap
would be called flattenDeploymentStyle
.
It's no big deal, but it helps to keep it aligned.
Let me know what you think.
if len(list) == 1 { | ||
style := list[0].(map[string]interface{}) | ||
result.DeploymentOption = aws.String(style["deployment_option"].(string)) | ||
result.DeploymentType = aws.String(style["deployment_type"].(string)) |
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.
Given that these options are both optional I think we'll need to wrap this in something like this
if v, ok := style["deployment_option"]; ok {
result.DeploymentOption = aws.String(v.(string))
}
m := attr.([]interface{})[0].(map[string]interface{}) | ||
deploymentReadyOption := &codedeploy.DeploymentReadyOption{ | ||
ActionOnTimeout: aws.String(m["action_on_timeout"].(string)), | ||
WaitTimeInMinutes: aws.Int64(int64(m["wait_time_in_minutes"].(int))), |
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 mentioned above - these are also optional fields, so we should only be passing them to the API if they're actually defined in the config.
m := attr.([]interface{})[0].(map[string]interface{}) | ||
blueInstanceTerminationOption := &codedeploy.BlueInstanceTerminationOption{ | ||
Action: aws.String(m["action"].(string)), | ||
TerminationWaitTimeInMinutes: aws.Int64(int64(m["termination_wait_time_in_minutes"].(int))), |
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 mentioned above - these are also optional fields, so we should only be passing them to the API if they're actually defined in the config.
|
||
item := make(map[string]interface{}) | ||
item["deployment_option"] = *style.DeploymentOption | ||
item["deployment_type"] = *style.DeploymentType |
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.
Are you sure that none of these options will ever be nil
? Otherwise this would cause a crash here.
|
||
lbInfo := make(map[string]interface{}) | ||
lbInfo["elb_info"] = schema.NewSet(loadBalancerInfoHash, elbs) | ||
lbInfo["target_group_info"] = schema.NewSet(loadBalancerInfoHash, targetGroups) |
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.
It may look unintuitive I think we should be able to just use []interface{}
here, i.e. elbs
and targetGroups
directly. We can avoid the custom hashing function that way.
blueInstanceTerminationOption["action"] = *config.TerminateBlueInstancesOnDeploymentSuccess.Action | ||
blueInstanceTerminationOption["termination_wait_time_in_minutes"] = *config.TerminateBlueInstancesOnDeploymentSuccess.TerminationWaitTimeInMinutes | ||
m["terminate_blue_instances_on_deployment_success"] = append(c, blueInstanceTerminationOption) | ||
|
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.
Are you sure that none of these options will ever be nil
? Otherwise this would cause a crash here.
@@ -670,6 +1015,21 @@ func resourceAwsCodeDeployTriggerConfigHash(v interface{}) int { | |||
return hashcode.String(buf.String()) | |||
} | |||
|
|||
func loadBalancerInfoHash(v interface{}) int { |
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 custom hash function shouldn't be necessary as the nested schema seems quite simple, so the default one (which is used when Set
is not defined) should suffice.
Just attaching an example config which ends up in a crash: resource "aws_codedeploy_deployment_group" "example" {
app_name = "${aws_codedeploy_app.example.name}"
deployment_group_name = "example-group"
service_role_arn = "${aws_iam_role.example.arn}"
deployment_style {
deployment_option = "WITH_TRAFFIC_CONTROL"
}
load_balancer_info {
elb_info {
name = "example-elb"
}
}
blue_green_deployment_config {
deployment_ready_option {
action_on_timeout = "STOP_DEPLOYMENT"
}
}
}
|
Thank you so much for the review! I really appreciate getting great feedback. I wil review your comments in detail and address them with new changes as soon as possible. Naming Question: Do you think I should change the names of only the functions in this PR, or for all similar functions in this resource file? Optional v Required All types (and their sub types) are indicated as being optional in the SDK, which obviously is not the case (your simple test proves that). However, this was the reason why I hesitated marking them as required in terraform - to avoid a mismatch between the official documentation and terraform, which might be confusing. But if they are going to be indicated as being optional to users of terraform, then more robust null checking is mandatory. Question: For these cases, would it be better to mark them with |
I'd only change the ones you have created as part of this PR and deal with the rest in a separate PR as this one is already getting quite big in terms of LOC. 😅 Related to this - we also tend to store all flatteners & expanders in here: https://github.com/terraform-providers/terraform-provider-aws/blob/master/aws/structure.go to keep the LOC in Again - no big deal, but it's a convention we developed over time and it's nice to stick to it, if we can.
I'm honestly not sure, but I remember I marked some fields Required in the past in a different API, despite them being documented as optional to later discover they actually were optional, just under certain context - e.g. in combination with some other fields and/or values of those fields. I don't know if that's the case here, but I'd double check this with AWS support before drifting away from documentation. Keeping it optional and adding |
TODO
|
- Don't pass "optional" fields to the api. - Check for nil on "optional" fields.
- be consistent about returning nil when there is no valid input - returning empty objects can produce exceptions for required fields
RE:
On reflection, I'm not sure why Also, I did not add any |
@niclic What I meant originally is to keep both fields as lbInfo["elb_info"] = elbs
lbInfo["target_group_info"] = targetGroups See how we do the slice -> set conversion here: https://github.com/hashicorp/terraform/blob/master/helper/schema/field_writer_map.go#L271-L309
"elb_info": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Set: schema.HashString,
Elem: &schema.Schema{Type: schema.TypeString},
}, The nested structure in our case here isn't a simple string though, it's a Let me know if that makes sense and/or whether you need any further help. |
Okay. When I remove the custom hash function, I get this error in any test that includes
I may be missing something, but if I change See the sequence of commits above. Note that currently, only one |
Also, I'm considering removing the flatten/expand function tests. Here are the tests I added for this PR (there are many others form earlier contributions, never mind all the brittle validation functions as well, but that's for another time).
I'm not sure if these sorts of tests add much value. I wrote them to guide the writing of flatten and expand functions. There are plenty of acceptance tests covering these resources now and besides, despite their lines of code, the tests are very basic, and do not cover every possible code path. Thoughts? |
RE:
If I send an empty
I'm probably missing something here. But it seems that removing I have sent a question to AWS SDK support regarding the using of |
You're right and I apologise for providing misleading info and letting you spend the extra time on this! 😞
One of the reasons we should use TypeSet instead of TypeList here is because the names of ELBs or Target Groups may come from the API in an unpredictable order (AFAIK) and the user would see spurious diff because ordering in the config doesn't match the one in the API response. To sum this up - do you mind removing the last two commits? Thanks and sorry again!
I think they're useful, I'd keep them there.
I think the intention of a test isn't to cover every possible codepath, that's almost impossible in reality. It's however useful to have tests which test different parts/functions of the program, so that if that small part breaks we can have a small, isolated test making it obvious which part is broken. If you had an acceptance test failure you'd need to dig deeper to understand that it's caused by flattener/expander.
I see, technically Terraform does what it's told - Here's what I received on 17th September:
I don't expect quick response or solution there, so I'd keep it
How do you feel about adding the extra nil checks and keeping it |
No problem!
Sounds good to me. I will remove the last couple of commits and revisit the overall PR before signing off. Thanks again for all your help. @radeksimko |
@niclic Hey, I'd like to cut next release of the provider this week and have this PR in it, so let me know if you're ok with me making the proposed changes and pushing them to your branch. |
I had planned on getting this completed this past weekend, but best laid plans, etc. I am totally fine with you completing this and finally seeing it merged. Thanks for all your help! |
06258ff
to
deccd09
Compare
deccd09
to
78777df
Compare
@radeksimko or @niclic Since you guys seem to be behind this functionality, what is the preferred way to keep track of the ASG created during a deploy with
Currently I am bringing in a ASG from another module, which is destroyed when I deploy the app. I have tried to |
Hey @JimtotheB! Did you find out how to fix this? Running into the same problem. |
Please see this comment, which explains this issue in detail, plus some options for working around it. |
I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks! |
Issue: #504
This PR adds Blue/Green Deployment support to CodeDeploy Deployment Groups, as well as supporting In-Place deployments with Traffic Control (i.e. Load Balancer).
It was originally opened on February 4, 2017, as hashicorp/terraform#11700.
This PR adds the following resources to a Deployment Group:
The original work is contained in a single new commit. The original PR retains all original commits and discussion.
These enhancements can also be used for in-place deployments with traffic control.
Since this scenario was not supported when the original PR was created, I created a new commit that includes a test for this specific configuration, as well as updating the documentation to reflect this recent change. No code changes were required to support this scenario.