-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
feat(aws-ec2): add support for CloudFormation::Init #792
Conversation
Make a new construct to manager User Data, which should make it easier to apply the features correctly. Fixes #623 and 777.
@rix0rrr I merged and aligned this from master and converted indentation to 2 spaces. Enjoy! |
@mindstorms6 I believe you had some nifty ideas around this, can you take a look? |
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.
README?
this.userData = new ec2.UserData(this, 'UserData', { | ||
os: machineImage.os, | ||
defaultSignalResource: this.autoScalingGroup | ||
}); |
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.
Indentation
@@ -290,9 +297,11 @@ export class AutoScalingGroup extends cdk.Construct implements cdk.ITaggable, el | |||
/** | |||
* Add command to the startup script of fleet instances. | |||
* The command must be in the scripting language supported by the fleet's OS (i.e. Linux/Windows). | |||
* | |||
* @deprecated Use userdata.addCommands() instead. |
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.
What's wrong with this as sugar? Looks pretty useful...
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.
Maybe. I want to guide people towards the object that has convenience methods for common operations, so they don't go templating some complicated strings if there are better solutions available.
const resource = new cdk.Resource(this, 'Resource', { | ||
// I know this thing says it lives in the AWS::CloudFormation namespace, | ||
// but it really doesn't make sense there. | ||
type: 'AWS::CloudFormation::Init', |
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.
Something is weird. I believe AWS::CloudFormation::Init
is a metadata key, not a resource type:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-init.html
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.
Doh, you're right! Completely misread that.
/** | ||
* Whether to stop executing as soon as an error is encountered | ||
* | ||
* @default 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.
Wouldn't it make sense that this would be true
by default?
Also, we should document what this means on each platform?
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.
Yes, we probably do want that, but then we have to do signaling very differently (which I think is still a good thing!)
Signaling is supposed to be used this way:
some_command
cfn-signal -e $? ...
You must always run cfn-signal
, but then use it to send the error code of the previous command (typically but not necessarily this will be cfn-init
). Of course, this breaks down and becomes complicated if you run multiple commands.
An alternative that I like better is to install a bash error trap:
set -euo pipefail
signal_failure() {
cfn-signal --success false
}
trap signal_failure ERR
command1
command2
command3
cfn-signal --success true
And this will do the right thing. But it is a lot more ceremony...
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.
Why not trap exit and just send the last status. If they choose not to exit on error, strict
(I think). The signal behavior would be the same. This would simplify the need for two signal commands.
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.
Yes, I thought the same later, just hadn't figured out if I can use trap abd $! together in that way
if (options.verbose !== false) { bashFlags.push('x'); } | ||
if (options.strict) { bashFlags.push('eu'); } | ||
|
||
const bashCommand = '#!/bin/bash' + (bashFlags.length > 0) ? ` -${bashFlags.join('')}` : ''; |
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 it's possible to pass in flags like this instead of set -euo pipefail...
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.
Yes:
huijbers ~/W/P/ecs-cdk(ecs-demo)> bash -euo pipefail
bash-3.2$ echo $HELLO
bash: HELLO: unbound variable
huijbers ~/W/P/ecs-cdk(ecs-demo)>
public createUserData(scripts: string[], options: UserDataOptions): string { | ||
const bashFlags = []; | ||
if (options.verbose !== false) { bashFlags.push('x'); } | ||
if (options.strict) { bashFlags.push('eu'); } |
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.
By the way, I'd expect strict
to also imply pipefail
.
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.
Sure. I was on the fence for that one, but I can totally add it.
const parts = ['cfn-signal', | ||
`--region ${new cdk.AwsRegion()}`, | ||
`--stack ${new cdk.AwsStackId()}`, | ||
`--resource ${resource.logicalId}` |
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 should be the EC2 instance that you want to signal
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.
Or AutoScalingGroup.
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 this in quite a few userdata sections
# Ensure our PATH is set correctly (on Amazon Linux, cfn-signal is in /opt/aws/bin)
. ~/.bash_profile
This has served us well, but perhaps we want to offer a path override for the location of cfn-signal?
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.
Feels like we should hardcode the path in the script (w/ cdk override)
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.
A couple of questions. This does look it would cover my use case. So far all of mine are ASG based not ec2.
const parts = ['cfn-signal', | ||
`--region ${new cdk.AwsRegion()}`, | ||
`--stack ${new cdk.AwsStackId()}`, | ||
`--resource ${resource.logicalId}` |
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 this in quite a few userdata sections
# Ensure our PATH is set correctly (on Amazon Linux, cfn-signal is in /opt/aws/bin)
. ~/.bash_profile
This has served us well, but perhaps we want to offer a path override for the location of cfn-signal?
/** | ||
* Whether to stop executing as soon as an error is encountered | ||
* | ||
* @default 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.
Why not trap exit and just send the last status. If they choose not to exit on error, strict
(I think). The signal behavior would be the same. This would simplify the need for two signal commands.
Closing this PR for now. The branch is still in the repo and we can reopen as needed. |
Can we consider bringing this back into the roadmap? |
Start of an API to manage UserData in a more principled way, hide the details of resource signaling and support CloudFormation Init.
Soliciting opinions from @moofish32 and @taichi to see if this solves their use cases--I'm not 100% sure I have all the details right.
Proposed to fix #623 and #777.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license.