Skip to content
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

Preparation for MultiAccount support #5157

Merged
merged 7 commits into from
Jun 4, 2022

Conversation

bblommers
Copy link
Collaborator

@bblommers bblommers commented May 22, 2022

This PR is an first step towards support for multi-accounts, by changing the underlying data structure to enable this feature.

Current implementation:

Most services use an instance of the BackendDict-class to persist the state.
The BackendDict overrides a dictionary, and stores data in the following format:

{
  "us-east-1": ServiceBackend("us-east-1"),
  "us-east-2": ServiceBackend(..), 
  ..
}

Because global services such as S3 do not have any regions, those backends were persisted as straightforward dictionaries:

{"global": S3Backend()}

Proposed implementation:

All services now use the BackendDict, with an option to specify whether this is a global service.
The underlying data structure of the BackendDict now looks like this:

{
  "account_id": {
    "us-east-1": ServiceBackend(region_name="..", account_id=".."),
    ..,
  },
  ..
}

Benefits of this approach:

  • There is a logical separation between accounts
  • The BaseBackend becomes responsible for setting/resetting the region_name and account_id, reducing code duplication
  • Because of that, each ServiceBackend has direct access to it's account id, and a user longer has to worry about overriding it on a case-by-case basis

Noticeable aspects of this PR:

There is no change in functionality.

  • The BackendDict will always return the same account_id for now, regardless of what the user specifies.
  • No ServiceBackend uses self.account_id, but continues to import get_account_id

A lot of users access the internal API using backend_dict["us-east-1"].model.attribute = "..".
The __getitem__-method of BackendDict has been overridden to still support this, enabling backwards compatibility

The meat of this PR lies in moto/core/utils/BackendDict - all other changes are a logical consequence of the changes to that class.

Future PR's:

Because of the massive scope of this change, the current PR only prepares multi-account support, but without any functional changes.
A future PR is necessary to then unlock this functionality. This PR would consist of the following changes:

  • Ensure that each ServiceResponse has access to the AccountID for that specific request, using either get_account_id or IAM
  • Ensure that each ServiceResponse retrieves the correct ServiceBackend, using backend_dict[account_id][region_name]
  • Change all services to use self.account_id, instead of get_account_id()
  • Remove the option to access backend_dict[region_name] directly, as we would have no context about which account we want to use

Because this PR would be a breaking change, it would have to be released as part of the next major release.

Related PR's:
#4699 introduced the BackendDict in the first place
#5098 added another way to override AccountID

TODO:

Need to verify that the scaffold-script still creates the expected templates.

@bblommers bblommers requested a review from bpandola May 22, 2022 12:20
@bblommers
Copy link
Collaborator Author

@whummer @thrau FYI. As far as I can tell, this should not have an impact on on LocalStack. Let me know if you need some time to review/test this though.

@codecov-commenter
Copy link

codecov-commenter commented May 22, 2022

Codecov Report

Merging #5157 (d984968) into master (620f15a) will decrease coverage by 0.02%.
The diff coverage is 99.03%.

@@            Coverage Diff             @@
##           master    #5157      +/-   ##
==========================================
- Coverage   95.91%   95.88%   -0.03%     
==========================================
  Files         686      686              
  Lines       67827    67483     -344     
==========================================
- Hits        65055    64708     -347     
- Misses       2772     2775       +3     
Flag Coverage Δ
servertests 36.52% <89.63%> (-0.34%) ⬇️
unittests 95.82% <99.03%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
moto/batch_simple/models.py 93.33% <ø> (-0.42%) ⬇️
moto/ec2instanceconnect/models.py 100.00% <ø> (ø)
moto/kinesisvideoarchivedmedia/models.py 100.00% <ø> (ø)
moto/rekognition/models.py 100.00% <ø> (ø)
moto/applicationautoscaling/models.py 94.33% <75.00%> (-0.12%) ⬇️
moto/config/models.py 99.22% <87.50%> (-0.13%) ⬇️
moto/rds/models.py 93.96% <90.00%> (-0.03%) ⬇️
moto/cognitoidp/models.py 95.35% <92.30%> (-0.24%) ⬇️
moto/acm/models.py 90.68% <100.00%> (-0.19%) ⬇️
moto/apigateway/models.py 94.34% <100.00%> (-0.03%) ⬇️
... and 142 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 620f15a...d984968. Read the comment docs.

@thrau
Copy link
Contributor

thrau commented May 22, 2022

this looks great @bblommers! excited to see that multi-account is coming to moto :-) i'm sure this will simplify things also on our end for multi-account. i haven't fully understood the ramifications of the change yet, but as you said it looks like this won't have much impact since it's backwards compatible. it may affect some monkeypatches to backends but we can iron those out.
we can try to run a localstack ci build with the changes and see what happens.

Copy link
Contributor

@whummer whummer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, that is a massive set of changes, big kudos @bblommers and thanks for keeping us in the loop, 🙏 really appreciated! Great to see that you were also able to simplify/streamline some of the __init__ super calls, as well as default reset(..) implementations. 👍

My only question would be around hardcoding account ID "123456789012" in the new logic in BackendDict (see comments) - other than that, this is a huge enhancement and I think we can mostly work around the changes in here, as @thrau mentioned. 👍 Looking forward to seeing this in action! 🥳

The latter two will be phased out in the future, and we can remove this method.
"""
if re.match(r"[0-9]+", account_id_or_region):
self._create_account_specific_backend("123456789012")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if we should use account_id_or_region here instead?

self._create_account_specific_backend(account_id_or_region)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was done on purpose - hardcoding the ID keeps everything in a single account for now, ensuring that the current behaviour doesn't change.

The follow-up PR will definitely change this to get_account_id, but that will need a few other changes - and many more tests.
Wrapping everything in one PR would make it even more unwieldy and impossible to review, that's why I chose to break it up in two steps: 1 PR to prepare, and 1 PR to actually change the behaviour.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. That makes a lot of sense, thanks for clarifying! 👍

return True
else:
region = account_id_or_region
self._create_account_specific_backend("123456789012")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar/related to the comment above - should we replace "123456789012" with get_account_id() here? This would help us encapsulate the access to the default region returned in get_account_id() (and will also make it easier to customize it).

@bblommers bblommers marked this pull request as ready for review June 4, 2022 11:28
@bblommers bblommers merged commit 79a2a9d into getmoto:master Jun 4, 2022
@bblommers bblommers added this to the 3.1.12 milestone Jun 4, 2022
@github-actions
Copy link
Contributor

github-actions bot commented Jun 4, 2022

This is now part of moto >= 3.1.12.dev8

sahilshah6196 pushed a commit to EBSCOIS/moto that referenced this pull request Jul 1, 2022
@bblommers bblommers deleted the prep-multi-account-support branch August 28, 2022 12:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants