winglang / wing

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
https://winglang.io
Other
5.06k stars 198 forks source link

Stack scoping with custom resource suffixes #7201

Open JensRoland opened 1 month ago

JensRoland commented 1 month ago

Use Case

There are many cases where it can be useful to run multiple instances of the same application on a single cloud account:

As a concrete example, let's assume that you have an App UserService in one repo which depends on an App PlatformInfra from a different repo. These are in different repos because they are owned by different teams and have very different risk and security models. You really want the ability to deploy multiple PlatformInfra instances at once; you might have your stable main-branch PlatformInfra stack running and also an alternative next-version-candidate PlatformInfra stack, which is still not quite done. On top of this, the UserService team would have their stable main-branch UserService running on top of the stable PlatformInfra, but they would also like to have a PR branch of the UserService running on top of the next-version-candidate PlatformInfra for testing with the new infra.

To achieve this kind of collision-free "scoped deployment", all deployed resources must have unique ARNs / resource IDs (and sometimes names too, as in the case of domain names and S3 buckets which have global namespaces).

Some IaC tools make this simpler than others, usually by incorporating a randomly-generated 'StackId' into all resource identifiers. However, this mechanism of randomly generated resource IDs suffers from a few downsides:

Proposed Solution

At a previous company I built a CLI tool which handled this in a flexible and general way, and I would recommend taking a similar approach with Wing:

Instead of randomness, incorporate a custom stack suffix provided on compile (or ideally deploy) time. The wing compile command would take this suffix as an argument:

> cd ~/git/PlatformInfra
> wing compile --suffix next-candidate

# and

> cd ~/git/UserService
> wing compile --suffix pr-126

Cross-App dependencies would be specified similarly:

> wing compile --suffix pr-126 --dependencies PlatformInfra:next-candidate,MasterData:main,SharedEventBus:main

and the compile/deploy command will simply add the suffix to the IDs for all resources imported from that particular external stack.

Implementation Notes

The natural pattern for teams using this feature is to have a main suffix which corresponds to their stable main branch and have the tool always default to main for unspecified dependencies.

A convenient extension of this pattern is to combine the target environment and custom suffix, so you actually end up with suffixes like -pr-126-test and -main-prod. This is necessary so your test resources don't collide with your prod resources. These combined suffixes can be done by hand (the teams simply format the suffixes like that by convention) or enforced (Wing adding an --env parameter and combining the strings behind the scenes). In the tool I wrote, we went for adding an --env parameter, since this made our CI pipelines trivial to build.

This works incredibly well in practice, allows devs to work seamlessly with simple or multi-region dependencies and ad-hoc deployments for dev, testing or PRs without collisions or interference.

The complexity (from the perspective of implementing this into Wing) is in ensuring that suffixes are added to all the right IDs, that those IDs don't exceed character limits or use illegal chars, and in finding ways to implement this across different IaC toolchains. It's not trivial, but it makes for an incredible developer experience.

Component

Compiler, CLI

Community Notes