129 lines
4.5 KiB
Python
129 lines
4.5 KiB
Python
import random
|
|
import time
|
|
import pytest
|
|
|
|
from moto.core import BaseBackend
|
|
from moto.core.utils import AccountSpecificBackend, BackendDict
|
|
from threading import Thread
|
|
|
|
|
|
class ExampleBackend(BaseBackend):
|
|
def __init__(self, region_name, account_id):
|
|
super().__init__(region_name, account_id)
|
|
|
|
|
|
def test_backend_dict_returns_nothing_by_default():
|
|
backend_dict = BackendDict(ExampleBackend, "ebs")
|
|
backend_dict.should.equal({})
|
|
|
|
|
|
def test_backend_dict_contains_known_regions():
|
|
backend_dict = BackendDict(ExampleBackend, "ec2")
|
|
backend_dict.should.have.key("eu-north-1")
|
|
backend_dict["eu-north-1"].should.be.a(ExampleBackend)
|
|
|
|
|
|
def test_backend_dict_known_regions_can_be_retrieved_directly():
|
|
backend_dict = BackendDict(ExampleBackend, "ec2")
|
|
backend_dict["eu-west-1"].should.be.a(ExampleBackend)
|
|
|
|
|
|
def test_backend_dict_can_get_known_regions():
|
|
backend_dict = BackendDict(ExampleBackend, "ec2")
|
|
backend_dict.get("us-east-1").should.be.a(ExampleBackend)
|
|
|
|
|
|
def test_backend_dict_does_not_contain_unknown_regions():
|
|
backend_dict = BackendDict(ExampleBackend, "ec2")
|
|
backend_dict.shouldnt.have.key("mars-south-1")
|
|
|
|
|
|
def test_backend_dict_fails_when_retrieving_unknown_regions():
|
|
backend_dict = BackendDict(ExampleBackend, "ec2")
|
|
with pytest.raises(KeyError):
|
|
backend_dict["mars-south-1"] # pylint: disable=pointless-statement
|
|
|
|
|
|
def test_backend_dict_can_retrieve_for_specific_account():
|
|
backend_dict = BackendDict(ExampleBackend, "ec2")
|
|
|
|
# Retrieve AccountSpecificBackend after checking it exists
|
|
backend_dict.should.have.key("000000")
|
|
backend = backend_dict.get("000000")
|
|
backend.should.be.a(AccountSpecificBackend)
|
|
|
|
# Retrieve AccountSpecificBackend by assuming it exists
|
|
backend = backend_dict["012345"]
|
|
backend.should.be.a(AccountSpecificBackend)
|
|
|
|
backend.should.have.key("eu-north-1")
|
|
regional_backend = backend["eu-north-1"]
|
|
regional_backend.should.be.a(ExampleBackend)
|
|
regional_backend.region_name.should.equal("eu-north-1")
|
|
# We always return a fixed account_id for now, until we have proper multi-account support
|
|
regional_backend.account_id.should.equal("123456789012")
|
|
|
|
|
|
def test_backend_dict_can_ignore_boto3_regions():
|
|
backend_dict = BackendDict(ExampleBackend, "ec2", use_boto3_regions=False)
|
|
backend_dict.get("us-east-1").should.equal(None)
|
|
|
|
|
|
def test_backend_dict_can_specify_additional_regions():
|
|
backend_dict = BackendDict(
|
|
ExampleBackend, "ec2", additional_regions=["region1", "global"]
|
|
)
|
|
backend_dict.get("us-east-1").should.be.a(ExampleBackend)
|
|
backend_dict.get("region1").should.be.a(ExampleBackend)
|
|
backend_dict.get("global").should.be.a(ExampleBackend)
|
|
|
|
# Unknown regions still do not exist
|
|
backend_dict.get("us-east-3").should.equal(None)
|
|
|
|
|
|
class TestMultiThreadedAccess:
|
|
class SlowExampleBackend(BaseBackend):
|
|
def __init__(self, region_name, account_id):
|
|
super().__init__(region_name, account_id)
|
|
time.sleep(0.1)
|
|
self.data = []
|
|
|
|
def setup(self):
|
|
self.backend = BackendDict(TestMultiThreadedAccess.SlowExampleBackend, "ec2")
|
|
|
|
def test_access_a_slow_backend_concurrently(self):
|
|
"""
|
|
Usecase that we want to avoid:
|
|
|
|
Thread 1 comes in, and sees the backend does not exist for this region
|
|
Thread 1 starts creating the backend
|
|
Thread 2 comes in, and sees the backend does not exist for this region
|
|
Thread 2 starts creating the backend
|
|
Thread 1 finishes creating the backend, initializes the list and adds a new value to it
|
|
Thread 2 finishes creating the backend, re-initializes the list and adds a new value to it
|
|
|
|
Creating the Backend for a region should only be invoked once at a time, and the execution flow should look like:
|
|
|
|
Thread 1 comes in, and sees the backend does not exist for this region
|
|
Thread 1 starts creating the backend
|
|
Thread 2 comes in and is blocked
|
|
Thread 1 finishes creating the backend, initializes the list and adds a new value to it
|
|
Thread 2 gains access, and re-uses the created backend
|
|
Thread 2 adds a new value to the list
|
|
"""
|
|
|
|
def access(random_number):
|
|
self.backend["123456789012"]["us-east-1"].data.append(random_number)
|
|
|
|
threads = []
|
|
|
|
for _ in range(0, 15):
|
|
x = Thread(target=access, args=(random.randint(100, 200),))
|
|
x.start()
|
|
threads.append(x)
|
|
|
|
for x in threads:
|
|
x.join()
|
|
|
|
self.backend["123456789012"]["us-east-1"].data.should.have.length_of(15)
|