-
Notifications
You must be signed in to change notification settings - Fork 166
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
Formalize the existence of external (read-only) stacks #618
base: master
Are you sure you want to change the base?
Conversation
Should't the unit tests also be running on Python 3 now? |
stacker/config/__init__.py
Outdated
self.external = True | ||
|
||
def validate_fqn(self, data, value): | ||
if (not value and not data["stack_name"]) or \ |
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.
I just noticed this is wrong, since we can infer the CF name from the stack name. Will fix!
enabled=stack_def.enabled, | ||
protected=stack_def.protected, | ||
) | ||
if isinstance(stack_def, ExternalStackModel): |
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.
I like the change to inject a fake Stack
implementation, but I'm wondering if an external
flag + config.ExternalStack
is overkill. At the moment, a locked stack that has no blueprint or template_path should be considered a read-only stack.
What if the condition here was just:
if stack_def.locked and not stack_def.blueprint and not stack_def.template_path:
# ExternalStack
That would be a much smaller change and help reduce the size of this PR (and be more backwards compatible with existing configs).
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.
That was my first approach, but I noticed more than half of an external stack's attributes would be completely invalid/useless, and that I needed to add conditions that checked stack.external
in dozens of places. Additionally, being loose with parsing can be an easy source of mistakes, so much that one of Stacker's own functional tests had a mistyped key that was never noticed! While there were a significant amount of changes, adding enough tests to the Stack models to cover all cases where external was at would be as big if not bigger.
Interesting note: I found 20+ usages of invalid options in stacks in my company's repo when using this PR (due to enabling strict parsing). Most of them were harmless (using I can make a PR that just enables strict parsing before this one (which is larger and a bit more controversial). What do you think @ejholmes? edit: PR here: #623 |
|
||
namespace: other-example | ||
stacks: | ||
- name: vpc |
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 throw a comment here stating that this stack won't be changed/build/etc because of the external flag (it is redundant, but I think it's 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.
Sure.
docs/stack-config-managed.yml
Outdated
# dependencies are automatically created when using an output from a stack in | ||
# the variables of another stack. Use it when you know that a hidden dependency | ||
# exists (or one is documented in the CloudFormation reference). | ||
dependencies: |
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 requires
, 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.
Ping on this one ^^
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.
Indeed, I missed it :)
@ejholmes @danielkza - I'm thinking we should spin a release of stacker this week, it's been quite a while, and we have a lot of features/updates already. Do you think we should wait for this? What's left for discussing here? |
I'm looking to revive this PR as I update our internal Stacker fork to the latest master. The strict parsing change has been integrated for a while already, any other concerns I should handle to get this merged? @ejholmes @phobologic |
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.
Some minor changes, otherwise this looks good to me. Thanks!
docs/config.rst
Outdated
Stacks can be classified as: | ||
|
||
Managed | ||
Stacks that are fully managed by the current Stacker configuration. |
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.
Worth mentioning this is the default?
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.
Yeah, I can clarify this section a bit!
docs/config.rst
Outdated
Stacks that are fully managed by the current Stacker configuration. | ||
They will be created, updated or destroyed as needed. | ||
External | ||
Stacks that will not be modified by the current Stacker configuration. |
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.
Might be worth saying that these stacks are configured with the external
flag.
docs/stack-config-managed.yml
Outdated
# dependencies are automatically created when using an output from a stack in | ||
# the variables of another stack. Use it when you know that a hidden dependency | ||
# exists (or one is documented in the CloudFormation reference). | ||
dependencies: |
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.
Ping on this one ^^
def set_outputs(self, outputs): | ||
self.outputs = outputs | ||
|
||
def should_submit(self): |
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 does this end up looking like in the CLI output when we hit an external stack? Does it say it's submitting/creating?
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.
I believe it prints "skipped". Do you think we should add an additional status to print something different?
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.
Hmm - yeah, that might be useful. It can still be skipped, just with a slightly different message maybe?
tests/suite.bats
Outdated
@@ -162,7 +162,7 @@ namespace: ${STACKER_NAMESPACE} | |||
stacks: | |||
- name: vpc | |||
class_path: stacker.tests.fixtures.mock_blueprints.VPC | |||
stack_policy: tests/fixtures/stack_policies/default.json | |||
stack_policy_path: fixtures/stack_policies/default.json |
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 broke these up into individual files, so you'll probably want to merge master and get that cleared up here.
05e6bbb
to
ee1ac01
Compare
Rebased to latest master. Was a bit harder than what I hoped, and I ended up having to make some changes to the build action tests. I'll address your review comments in further commits @phobologic :) |
I also found some issues running the functional tests, good thing we have those! |
ee1ac01
to
caf7860
Compare
I'm circling back to this. Rebased to latest master again, but unfortunately I'm seeing failed unit tests locally due to #709. |
To import outputs from stacks that are not managed directly by a Stacker config and require custom settings (such as a profile or region), it was previously possible to just omit both a `class_path` and `template_path`. Make that pattern "official" by introducing an `external` option, and a simplified `ExternalStack` model that omits all the stack keys that do not make sense if we're never going to create/update the stack. Note: the upgrade to schematics 2.1.0 is required due to a bug [1] where polymorphic models were not properly validated. [1]: schematics/schematics#440
Don't ever attempt to update them, but always submit them so outputs can be grabbed.
This makes handling the difference between managed and external stacks much easier. Also clean up the build tests a little bit by creating a TestStack class instead of using Mocks.
caf7860
to
3b13657
Compare
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 looks good @danielkza - can you merge master in (it looks like it's confused about some docs changes), and then we'll get it merged. Thanks!
3b13657
to
2ce72e2
Compare
@phobologic Things looked up-to-date to me, but I took the opportunity to improve the wording on the docs a little bit and forced-pushed. |
Weird! I wonder why it is showing the additions of stack_name, which is already in the master branch. Ok, once tests pass I'll go ahead and merge :) |
I think it's because I moved it around in the file a little bit. |
Currently, the
ref
andxref
lookups always use the same AWS profile and region that Stacker is initialized with. That means they cannot access outputs in other accounts or regions. It is possible to create a stack that will just "pull" those outputs by addinglocked: true
to it. But that still does not make it possible to reference a stack outside of the current namespace, if one is configured. Additionally, options that should never take effect for a "read-only" stack were allowed, such asclass_path
orstack_policy_path
.To handle that use case, introduce and document the
external: yes
option for stacks. External stacks do not have any of the options related to creating or updating resources, such asclass_path
andtemplate_path
, and are used only as sources of values to other stacks (through outputs and lookups).Additionally, add an
fqn
option to external stacks to fix the namespace issue.Internal changes
To ensure the separation has the desired effect, enable
strict
mode while parsing configuration into models, but only for non-top-level keys. Achieve it by manually filtering unrecognized top-level keys before forwarding the data for parsing.To better separate external and managed stacks in config, rework the model hierarchy a little: add
BaseStack
that only contains the shared config options between both stack types, make it a superclass ofStack
, and introduceExternalStack
. Unfortunately Schematics 2.0 has a bug where it did not validate models parsed using polymorphism, so upgrade to version 2.1.Move decisions on whether a stack needs to be submitted or updated to the
stacker.stack.Stack
class, to simply the logic and allow it to differ between external and managed stacks. Introducestacker.stack.ExternalStack
with "empty" implementations of all the methods/properties related to templates, blueprints and updates.Documentation updates
Rework the Stack documentation to list options allowed for all stacks, external and managed stacks separately. Document everything in YAML format, so that people can copy snippets to their configs from the different parts. Fix some typos and clarify some details in the process.
Open questions
The
external/managed
terminology is the best I could come up with, but I still feel it can be improved. Any suggestions of alternatives are welcome!Notes
I ran functional tests manually on my all account and they passed - I even had to fix one that had
stack_policy_path
misspelled and was no longer accepted due to strict parsing.