-
Notifications
You must be signed in to change notification settings - Fork 405
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
User-defined Metadata for ASG Creation, ASG NextGroup & SWF AutoDeployment #592
base: master
Are you sure you want to change the base?
Conversation
asgard-pull-requests #453 FAILURE |
asgard-pull-requests #454 UNSTABLE |
asgard-pull-requests #455 FAILURE |
Hi @Xorlev, it seems like a reasonable pull request, and thank you for taking the time to put it together. At this point, we are beyond a tipping point with the work that we are doing toward Asgard 2.0, and I'm afraid there isn't a code-based migration path. Given that, we won't be adding any new, non-trivial functionality to this version of Asgard. We have an open source story for Asgard 2.0, and exactly the code that you've provided has a very real fit in the Asgard of the future. We're working diligently to get that code into a position that is open source-able, and I expect that you will hear more about our plans in the coming months. Thanks again for the effort. |
Sorry to hear that. Is there a timeline for Asgard 2.0? Until then, we'll just be maintaining a fork, but I imagine these changes would be helpful for other organizations who've used Asgard, given that it's been pitched as a better way to deploy on AWS. I understand that Netflix will not be contributing changes to Asgard 1.x, but the community you've built does seem interested. Additionally, is it Netflix's intention to build Asgard 2.0 internally and only open source it ex post facto, vs. developing it in the open where the community could enrich the next iteration? |
I certainly see the value for other organizations using Asgard, and I think even the pattern of implementation fits well into the new model. We'll be developing Asgard 2.0 in the open, and we'll be much better situated to engage the community than we have been with Asgard 1. We're taking all of the lessons learned in both technologies and open source project management and applying them wholesale to the coming changes. Some more details on what we're doing here: #486 (comment) We haven't made any official announcements about this yet, as we're still in a sprint to gain feature parity with the existing systems. Presently, we feel that we are close to that, and once that foundation is set we'll be in a better position to support our open source position. As soon as we're internally confident, we'll publish a TechBlog article outlining the roadmap. |
@Xorlev can you share an example request on how you are passing in custom user-data? I am trying to do something very similar. |
Sure:
Where any property after Note: I have made some improvements since posting this PR. I'll get the changes out of our fork and merge them into this PR. It was mainly bug fixes (places the metadata wasn't wired up properly). Basic modification of the existing impl: /**
* Custom impl which mostly copies the default impl, just overlaying userMetaData on top of the generated properties
* before emitting the export statements. Also quotes values.
*/
class FCAdvancedUserDataProvider implements AdvancedUserDataProvider {
@Autowired
ConfigService configService
@Autowired
ApplicationService applicationService
@Override
String buildUserData(LaunchContext launchContext) {
// Call the legacy plugin by default, because that is what many users have already overwritten.
UserContext userContext = launchContext.userContext
String groupName = launchContext.autoScalingGroup?.autoScalingGroupName
String launchConfigName = launchContext.launchConfiguration?.launchConfigurationName
String appName = Relationships.appNameFromGroupName(groupName)
buildUserDataForVariables(userContext, appName, groupName, launchConfigName, launchContext.userMetaData)
}
String buildUserDataForVariables(UserContext userContext, String appName, String autoScalingGroupName,
String launchConfigName, Map<String, String> userMetaData) {
Map<String, String> props = mapProperties(userContext, appName, autoScalingGroupName, launchConfigName)
if (userMetaData) {
props += userMetaData.collectEntries { k, v -> [k.toUpperCase(), v] }
}
String result = props.collect { k, v ->
if (!v.startsWith("\"")) v = "\"$v\""
"export ${k.toUpperCase()}=${v}"
}.join('\n') + '\n'
DatatypeConverter.printBase64Binary(result.bytes)
}
/**
* Creates a map of environment keys to values for use in constructing user data strings, based on the specified
* cloud objects associated with the current deployment.
*
* @param userContext who, where, why
* @param appName the name of the application being deployed
* @param autoScalingGroupName the name of the ASG which will launch and manage the instances
* @param launchConfigName the name of the launch configuration for launching the instances
* @return a map of keys to values for the deployment environment
*/
Map<String, String> mapProperties(UserContext userContext, String appName, String autoScalingGroupName,
String launchConfigName) {
Names names = Names.parseName(autoScalingGroupName)
String monitorBucket = applicationService.getMonitorBucket(userContext, appName, names.cluster)
String appGroup = applicationService.getRegisteredApplication(userContext, appName)?.group
[
(prependNamespace(UserDataPropertyKeys.ENVIRONMENT)): configService.accountName ?: '',
(prependNamespace(UserDataPropertyKeys.MONITOR_BUCKET)): monitorBucket ?: '',
(prependNamespace(UserDataPropertyKeys.APP)): appName ?: '',
(prependNamespace(UserDataPropertyKeys.APP_GROUP)): appGroup ?: '',
(prependNamespace(UserDataPropertyKeys.STACK)): names.stack ?: '',
(prependNamespace(UserDataPropertyKeys.CLUSTER)): names.cluster ?: '',
(prependNamespace(UserDataPropertyKeys.AUTO_SCALE_GROUP)): autoScalingGroupName ?: '',
(prependNamespace(UserDataPropertyKeys.LAUNCH_CONFIG)): launchConfigName ?: '',
(UserDataPropertyKeys.EC2_REGION): userContext.region.code ?: '',
] + Relationships.labeledEnvVarsMap(names, configService.userDataVarPrefix)
}
private String prependNamespace(String key) {
"${configService.userDataVarPrefix}${key}"
}
} |
@Xorlev Thank you sir! |
asgard-pull-requests #468 FAILURE |
This change threads a new parameter through from all the places ASGs are built.
userMetaData
is a map of strings that can be defined during REST API calls, either to ASG creation, nextGroup, or autodeployment. In its default implementation, it does nothing, merely threads it through to theLaunchTemplateService
and embeds it inside theLaunchContext
to be accessed by userdata plugins. Once inside the LaunchContext, it's available for extensions toAdvancedUserDataProvider
.Our use case
Our shop doesn't use AMIs for versions yet. However, we love sequential ASGs and autodeployment flows with judgement steps and use them daily at the tradeoff of not specifying a particular artifact ID (always latest). Each deploy we need to start passing through an artifact ID for our base AMI to fetch, especially for fast rollbacks and safe autoscaling.
This change allows us to write an
AdvancedUserDataProvider
that adds all of theuserMetaData
KV pairs to the map of properties thePropertiesUserDataProvider
normally writes as environmental variables. This way, we can pass in ourAPP_REVISION
as an environmental variable (from a REST call) and our AMI can pull the proper artifact.The change is general enough to support a variety of new use cases I haven't even thought of yet.
It's important to note that this has no bearing on the UI at present and does not copy userMetaData from ASG to ASG without explicit REST calls.