-
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
Cloud Assemblies and the App Model #233
Comments
I think I also want Constructs to be able to spread over multiple stacks. And i don't necessarily mean nested stacks. |
Okay, this is starting to fall into place. Seems like we can generalize this even more and finally define the app model. No need to store a revision in comments, GitHub has revisions on the issue description... |
Introduced the concept of an app model which is essentially a desired state description of a full app. |
What about our "toolkit stack" (the stack that contains the bucket which we upload artifacts to)? Can we extend the app model to include it as well? Let's try:
Resources:
AssetsStack:
Type: AWS::CloudFormation::Stack
Properties:
# inline version of the "toolkit stack" template, now it's not "special" anymore
TemplateFile: "./assets-template.json"
MyLambdaCodePackage:
Type: AWS::S3::Object
Properties:
File: "./my-handler.zip"
Bucket: ${AssetsStack.AssetsBucket}
MyTemplate:
Type: AWS::S3::Object
Properties:
File: "./my-template.json"
Bucket: ${AssetsStack.AssetsBucket}
MyStack:
Type: AWS::CloudFormation::Stack
Proeprties:
TemplateURL: ${MyTemplate.URL}
Parameters:
MyLambdaCodeS3Bucket: ${MyLambdaCodePackage.Bucket}
MyLambdaCodeS3Key: ${MyLambdaCodePackage.Key} This requires that |
A few humble comments (James Joyce's Stream-Of-Conciseness style):
ó Éirinn le craic |
That's a good point. I think we will need to use higher level abstractions for the app model also to allow tools to reach the desired state in different ways. For example, the assets bucket (e.g. where runtime code or CloudFormation templates or deployment artifacts are consumed) can either be a bucket created by the toolkit when used for development or the pipeline artifacts bucket in CI/CD. Either way, the desired state of an "asset store" with a bunch of specific artifacts is reached. So let's see what types of "app resources" we need for the app model:
Would it be useful to model the "Asset Store"? Probably not really needed. An asset by definition is stored somewhere. The details of how/where/who defines the asset store can be left undefined in the model, and implemented differently by different tools. So now, the stack + runtime code example will look like this: Resources:
MyLambdaCodePackage:
Type: AWS::App::Asset
Properties:
File: "my-handler.zip"
MyTemplate:
Type: AWS::App::Asset
Properties:
File: "my-template.json"
MyStack:
Type: AWS::App::Stack
Properties:
TemplateURL: ${MyTemplate.URL}
Parameters:
MyLambdaCodeS3Bucket: ${MyLambdaCodePackage.Bucket}
MyLambdaCodeS3Key: ${MyLambdaCodePackage.Key} |
In order to not confuse people (and ourselves) too much, how about we change the namespace of our app-level resources?
Don't understand why we have to go through the filesystem all of a sudden. Can we not do the same thing as before with
We will still need to specify a I think I would like to introduce the concept of an That is not to say you couldn't just address the
They will have to be There's a limit of 50 or 60 parameters. We might run into that for complicated apps.
Local Apropos of nothing: I still wonder whether it makes sense for customers to define The rules are not that complicated:
|
I like it! It generalizes nicely! We'll have to build a copy of CloudFormation's evaluation engine, but I guess it won't be too complicated? Critical questions:
|
The more I think about The biggest downside to this is that people migrating over from CloudFormation, who want to recreate their existing templates in CDK, are going to want strict control over them. I think we should give them that control without putting the concept of a |
Nice!
+1 - this makes it much clearer that these aren't specific AWS resources! @eladb et al: How does the packaging story play in here? As part of my normal development workflow I usually want to 'package' my app and deploy it in quick succession. Then again as part of my CI/CD pipeline I need to be able to build my application before I deploy it. Is the idea that packaging (e.g. compilation, Could I define a The other option is to reverse this, I suspect that many customers are going to want to be able to invoke CDK as part of their 'regular' build-tool. As a JVM developer I just want to be able to do Thoughts? |
You are right. Stacks as an abstraction are not really important, and perhaps at some point we can get rid of them when you define apps at a high level. Still, they are a mechanism that apps can use to isolate regions/account and updates, and therefore they make sense at the app model layer I believe. |
@kiiadi, agreed about Regarding packaging - that's a good point. At the moment, the model is that packaging is done by idiomatic tools and consumed as assets by the app model, but I am not sure that's good enough. We need to think about it further. I think the idea of a construct performing a build is very interesting (and I am not sure that the "synthesis" terminology breaks, i.e. "this construct represents your Lambda's handler, and in order to synthesize it you need to build the code"). But I agree about IDEs and native tools. I am not sure it's a good idea for us to go to the software build business if we can avoid it and let people use their normal tools and IDEs. |
@rix0rrr, instead of
Since we are going to want to emit multiple artifacts, which can potentially include GiBs of runtime code, I believe we should emit those directly into the filesystem. By the way, in many cases (e.g. those GiBs of runtime code), the construct can just emit a symlink to the actual code instead. If we go down the STDOUT path design, we will eventually define a filesystem protocol, which is not our business.
You mean in the case where you only want to deploy a single resource from the app model (i.e. a dev stack). Yes, that should be possible. In this case, the toolkit should be able to deploy all it's dependencies as well. Added to the design.
That's a good idea. Maybe just
I will add to the doc. It's in the list. It will be awesome (🤞)
Also in the TODO list.
As mentioned above, I think that's a good idea, but not at the app model level. These are abstractions we should easily implement in the CDK itself. Makes sense? |
Hey guys, Can we try to tackle not-just-AWS case for the format? I just want to avoid ECS-EKS situation. And we can be pioneers for this initiative in the industry. |
Addendum: Do you guys think it would make sense to factor-out deployment system from templating one? Going to the extreme that two can be written in different languages. For instance, cloud assembly deployment toolkit can be written in Go/Rust (because it's cool and new and compiled and bundled and self-sufficient ...) and templating system can be CDK, Cloud-templates, Troposphere, GoFormation, or something internal to a company who wants to use the format. That would be really cool. |
This is partly true. I believe structuring the code per "stacks" and naming them accordingly is actually a good practice. something like the following:
|
Formalize the concept of a cloud assembly to allow decoupling "synth" and "deploy". the main motivation is to allow "deploy" to run in a controlled environment without needing to execute the app for security purposes. `cdk synth` now produces a self-contained assembly in the output directory, which we call a "cloud assembly". this directory includes synthesized templates (similar to current behavior) but also a "manifest.json" file and the asset staging directories. `cdk deploy -a assembly-dir` will now skip synthesis and will directly deploy the assembly. To that end, we modified the behavior of asset staging such that all synth output always goes to the output directory, which is by default `cdk.out` (can be set with `--output`). if there's a single template, it is printed to stdout, otherwise the name of output directory is printed. This PR also includes multiple clean ups and deprecations of stale APIs and modules such as: - `cdk.AppProps` includes various new options. - An error is thrown if references between stacks that are not in the same app (mostly in test cases). - Added the token creation stack trace for errors emitted by tokens - Added `ConstructNode.root` which returns the tree root. **TESTING**: verified that all integration tests passed and added a few tests to verify zip and docker assets as well as cloud assemblies. See: https://github.com/awslabs/cdk-ops/pull/364 Closes #1893 Closes #2093 Closes #1954 Closes #2310 Related #2073 Closes #1245 Closes #341 Closes #956 Closes #233 BREAKING CHANGE: *IMPORTANT*: apps created with the CDK version 0.33.0 and above cannot be used with an older CLI version. - `--interactive` has been removed - `--numbered` has been removed - `--staging` is now a boolean flag that indicates whether assets should be copied to the `--output` directory or directly referenced (`--no-staging` is useful for e.g. local debugging with SAM CLI) - Assets (e.g. Lambda code assets) are now referenced relative to the output directory. - `SynthUtils.templateForStackName` has been removed (use `SynthUtils.synthesize(stack).template`). - `cxapi.SynthesizedStack` renamed to `cxapi.CloudFormationStackArtifact` with multiple API changes. - `cdk.App.run()` now returns a `cxapi.CloudAssembly` instead of `cdk.ISynthesisSession`. - `cdk.App.synthesizeStack` and `synthesizeStacks` has been removed. The `cxapi.CloudAssembly` object returned from `app.run()` can be used as an alternative to explore a synthesized assembly programmatically (resolves #2016). - `cdk.CfnElement.creationTimeStamp` may now return `undefined` if there is no stack trace captured. - `cdk.ISynthesizable.synthesize` now accepts a `cxapi.CloudAssemblyBuilder` instead of `ISynthesisSession`. - `cdk`: The concepts of a synthesis session and session stores have been removed (`cdk.FileSystemStore`, cdk.InMemoryStore`, `cdk.SynthesisSession`, `cdk.ISynthesisSession` and `cdk.SynthesisOptions`). - No more support for legacy manifests (`cdk.ManifestOptions` member `legacyManifest` has been removed) - All support for build/bundle manifest have been removed (`cxapi.BuildManifest`, `cxapi.BuildStep`, etc). - Multiple deprecations and breaking changes in the `cxapi` module (`cxapi.AppRuntime` has been renamed to `cxapi.RuntimeInfo`, `cxapi.SynthesizeResponse`, `cxapi.SynthesizedStack` has been removed) - The `@aws-cdk/applet-js` program is no longer available. Use [decdk](https://github.com/awslabs/aws-cdk/tree/master/packages/decdk) as an alternative.
As we progress in our design for an end-to-end developer experience for cloud applications, and start to venture into more complex use cases, there's a common pattern that keeps emerging, where more than a single artifact is needed in order to deploy cloud applications. Moreover, these artifacts interact with each other.
Some examples:
In all of these cases, we still want the developer and CI/CD workflows to operate at the app level. What does it mean? It means that if a construct defines a Lambda function that uses some runtime code, when a developer invokes
cdk deploy
, the runtime code should be uploaded to an S3 bucket and the CloudFormation template should reference the correct bucket/key (and also make sure Lambda has permissions to read from the bucket). This should also seamlessly work within a CI/CD pipeline.Since the CDK's unit of reuse is a construct library, any solution must not look at the problem from the app's perspective, but from a construct perspective. It should be possible for a construct library to encapsulate runtime code, nested stacks, etc. Then, when this library is consumed, the workflow above should continue to work exactly in the same way.
Design approach
At the moment, synthesis is actually performed at the
App
level and is tailored to produce a single artifact (CloudFormation template). The proposed design will allow any construct in the tree to participate in the synthesis process and emit arbitrary artifacts.But it is not sufficient to just emit multiple artifacts. We need to model their interaction somehow (dependencies, data-flow, how do these artifact interact with cloud resources, etc).
Generalizing this, we effective need to have some way to describe the model for our app. If a CloudFormation template define the model for a single stack, we need a way to describe an entire cloud application.
Naturally, we should prefer a desired state configuration approach where the app model doesn't describe steps but rather the desired state, and then tools (like the CDK toolkit or CI/CD systems) can help achieve this desired state.
Let's say that the toolkit only knows how to work with app model files, which describe the desired state of an app in a format similar to CloudFormation templates:
This is not a CloudFormation template! It's an App Model file. It uses the same structure to define the desired state of an entire application. This file, together with all the artifacts synthesized from the app form a self-contained cloud app package ("cloud executable"?).
When tools read this file, they can produce a deployment plan for this app:
./my-handler.zip
and./my-template.json
to an S3 bucket.CreateStack
API, use the S3 URL to specify the template URL and pass in parameters that resolve to the location of the S3 bucket and key of the Lambda runtime archive.The power of this approach is that it is highly extensible. Anyone can implement App Resources which will participate in this process. The desired state approach deems that each resource needs to be able to be CREATED, UPDATED or DELETED, and also DIFFed against the desired state.
Implementation
Synthesis
Each construct in the tree may implement a method
synthesize(workdir)
which will be called during synthesis. Constructs can emit files (or symlinks) into a working directory at this time.App Model Resources
Similarly to CloudFormation Resources, we can defined constructs that represent app model resources (
AppStack
,AppAsset
). Similarly to how CloudFormation resources are implemented, these constructs will implement a methodtoAppModel()
which will return an app model JSON chunk to be merged into the complete app model.The
App
construct is now a simple construct, in it'ssynthesis()
method, it will collect all app resources from the tree, merge them together and emit anapp-model.json
(orindex.json
) into the working directory.The toolkit will expect to always find an
index.json
file in the working directory. It will read the file and form a deployment plan (calculate dependency graph based on references). Then, it will execute the deployment plan.The toolkit can either deploy the entire app or only a subset of the resources, in which case it can also deploy any dependencies of this set.
Each app resource will have a "provider" implemented in the toolkit via an extensible framework. Providers will implement the following operations:
In the normal flow, the toolkit will simply invoke the create/update operation in topological order (and concurrently if possible). It will supply the contents of the
Properties
object which represents the desired state. If a property includes a reference to another resource (viaFn::GetAtt
), it will replace the token with the value from the other resource.TODO
List of use cases we should tackle for this new design:
The text was updated successfully, but these errors were encountered: