diff --git a/.secrets.baseline b/.secrets.baseline index 1b2144eb..770a1776 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -26,7 +26,7 @@ "results": { "Pipfile.lock": [ { - "hashed_secret": "a355e3e3983231194cfc5981d48ea7ff9a7f9cb6", + "hashed_secret": "307e5f510c60e98b8590cfd40e6dcacb0a285e42", "is_secret": false, "is_verified": false, "line_number": 4, @@ -90,21 +90,21 @@ "hashed_secret": "aa419433d95be86df254d499243bee1d5173f1ae", "is_secret": false, "is_verified": false, - "line_number": 5, + "line_number": 7, "type": "Secret Keyword" }, { "hashed_secret": "afc848c316af1a89d49826c5ae9d00ed769415f3", "is_secret": false, "is_verified": false, - "line_number": 18, + "line_number": 20, "type": "Secret Keyword" }, { "hashed_secret": "abcdb568713c255c81376829da20004ba9463fd3", "is_secret": false, "is_verified": false, - "line_number": 24, + "line_number": 26, "type": "Secret Keyword" } ], diff --git a/Pipfile.lock b/Pipfile.lock index 168163da..13e68fd1 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -18,10 +18,10 @@ "default": { "alembic": { "hashes": [ - "sha256:5609afbb2ab142a991b15ae436347c475f8a517f1610f2fd1b09cdca7c311f3f" + "sha256:9f907d7e8b286a1cfb22db9084f9ce4fde7ad7956bb496dc7c952e10ac90e36a" ], "index": "pypi", - "version": "==1.2.0" + "version": "==1.2.1" }, "amqp": { "hashes": [ @@ -85,18 +85,18 @@ }, "boto3": { "hashes": [ - "sha256:0e4d047feb4d7d701e9b2107f10bb8d674952243385cd35d0b413a273c299751", - "sha256:67f957389cf56fb4c24c1093c6d58baebe6cf18139f6dca0f8a177239b0a4f8c" + "sha256:2fc1c407a5ab08cfcf54eb4171d85c523bd27019ab890de257d018af2770f71d", + "sha256:c215cf2c8e5e7b28ae7544b1cbdbc3216bef983d7adb8b701a64f9b893e0320b" ], "index": "pypi", - "version": "==1.9.232" + "version": "==1.9.238" }, "botocore": { "hashes": [ - "sha256:724d2349198c6f15f3cee0c0e4d33ecf4435e6d0db311bb79a3a28f6cf5a4090", - "sha256:a57a8fd0145c68e31bb4baab549b27a12f6695068c8dd5f2901d8dc06572dbeb" + "sha256:1ca993f0dc70591e0fca6cf3837ee9be52fd4fbbf1aa96ba1d4a860b41f676b7", + "sha256:6ec3297b87d3e2c4d88b009f91061aaecdb2ceef6d9be9386394571353909adb" ], - "version": "==1.12.232" + "version": "==1.12.238" }, "celery": { "hashes": [ @@ -214,11 +214,11 @@ }, "flask-sqlalchemy": { "hashes": [ - "sha256:0c9609b0d72871c540a7945ea559c8fdf5455192d2db67219509aed680a3d45a", - "sha256:8631bbea987bc3eb0f72b1f691d47bd37ceb795e73b59ab48586d76d75a7c605" + "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", + "sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d" ], "index": "pypi", - "version": "==2.4.0" + "version": "==2.4.1" }, "flask-wtf": { "hashes": [ @@ -265,10 +265,10 @@ }, "kombu": { "hashes": [ - "sha256:55274dc75eb3c3994538b0973a0fadddb236b698a4bc135b8aa4981e0a710b8f", - "sha256:e5f0312dfb9011bebbf528ccaf118a6c2b5c3b8244451f08381fb23e7715809b" + "sha256:31edb84947996fdda065b6560c128d5673bb913ff34aa19e7b84755217a24deb", + "sha256:c9078124ce2616b29cf6607f0ac3db894c59154252dee6392cdbbe15e5c4b566" ], - "version": "==4.6.4" + "version": "==4.6.5" }, "lockfile": { "hashes": [ @@ -480,11 +480,11 @@ }, "urllib3": { "hashes": [ - "sha256:2f3eadfea5d92bc7899e75b5968410b749a054b492d5a6379c1344a1481bc2cb", - "sha256:9c6c593cb28f52075016307fc26b0a0f8e82bc7d1ff19aaaa959b91710a56c47" + "sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398", + "sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86" ], "markers": "python_version >= '3.4'", - "version": "==1.25.5" + "version": "==1.25.6" }, "vine": { "hashes": [ @@ -548,10 +548,10 @@ }, "astroid": { "hashes": [ - "sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4", - "sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4" + "sha256:98c665ad84d10b18318c5ab7c3d203fe11714cbad2a4aef4f44651f415392754", + "sha256:b7546ffdedbf7abcfbff93cd1de9e9980b1ef744852689decc5aeada324238c6" ], - "version": "==2.2.5" + "version": "==2.3.1" }, "atomicwrites": { "hashes": [ @@ -636,35 +636,35 @@ }, "coverage": { "hashes": [ - "sha256:108efa19b676e62590a7a13084098e35183479c0d9608131c20b0921c5a72dc0", - "sha256:16fe3ef881eff27bab287f91dadb4ff0ce4388b9e928d84cbf148a83cc70b3a1", - "sha256:1d0bbc11421827d1100da82ac8dc929532b97ad464038475a0f6505cbf83d6ea", - "sha256:23a8ca5b3c9673f775cc151e85a737f1a967df2ec02b09e8c5a3b606ff2050bf", - "sha256:24b890e51455276762b55cb06fa1c922066e8fc18d1deb1a6399b4d24dfa8ea2", - "sha256:2f0041757ca4801f3c6a74d1660862fdb18a25aea302dd0ce9b067ddbb06b667", - "sha256:3169aba03baddfccdab7cc04cf0878dbf76fc06d300bc35639129a6b794d6484", - "sha256:364fb1bf0f999af2e7f4b1a1e614b2af8c3e0017d11af716aad25f911b7cd0c7", - "sha256:5256856d23f3e45959e7e3a8f9d4cbad3d1613e5660cb8117cd1417798efc395", - "sha256:5b26daa1e1a1147455bf62cd682e504e68f1d1e04235374d50a5248a3c792b1c", - "sha256:60247c8f0c756732e2cfe21f03e6847b923b9a9eaff61f04dc64d3047ec1b669", - "sha256:6463d51507308eb3973340d903537f17ece2ee1e6513aa0c27548fc3a09b0471", - "sha256:64cbadf7a884b299794238bc4391752130e74f71e919993b50c1c431786ef2a2", - "sha256:6de85748ea39ce819ad6d90e660da43964457a1f5cd25262e962a7c7c87945b3", - "sha256:6f95b4794bd84f64aeca25087d8e3abc416aad76842afcac34fa6c3a6f61c62e", - "sha256:778fa184aa3079fa3cbd240e2f5b36771c3382db26bc7bf78aea9d06212c6c66", - "sha256:790a9c5e2dbdf6c41eec9776ed663e99bd36c1604e3bf2e8ae3b123181bfee9f", - "sha256:7d97c1aec0b68b4ea5e3c9edb9fc3f951e8a52360f4bad3aacab9a77defe5b17", - "sha256:93cefddcc0b541d3c52981a232947bf085a38092b0812317f1adb56f02869bcb", - "sha256:95e49867ac616ec63ecd69ea005e65e4b896a48b8db7f9f3ad69f37be29324b7", - "sha256:aca423563eafba66a7c15125391b267befd1e45238de5e1a119ae1fb4ea83b5c", - "sha256:baef7c35e7fce738d9637e9c7a6aa79cb79085e4de49c2ec517ce19239a660f6", - "sha256:c10ccf0797ffce85e93a40aff3a96a3adb63c734f95b59384a7c9522ed25c9e2", - "sha256:ca39704a05bba1886c384a4d7944fda72c53fe5e61979cd933d22084678ad4c1", - "sha256:f6e96d5eee578187f5b7e9266bf646b73de29e2dd7adca8bd83e383680ce1f4c", - "sha256:fc6524511fa664cb4e91401229eedd0dad4ba6ded9c4423fee2f698d78908d9c", - "sha256:fdf2e7e5f074495ad6ea796ca0d245aa6a8b9e4c546ffbf8d30aaaee6601af0f" + "sha256:094378c3a35594335a840ea04d473c019e6d4fe10e343cd0d7fb5e87f8b7e926", + "sha256:10216222f3e4139910b6230d0ca0fe9d10ee98837eb83d29525722d628729d20", + "sha256:147478e21cba12c63b3454df5a2fb77b44df630428cffa3a36a6813e38157eab", + "sha256:230ce08965190c0f69196be34a07a795981b2b02b21419c2e1918a882b3eeab0", + "sha256:2469621d680a4c71cdbd3ea4dbed9d199bba93f21d2be1c107ded907b2db41a8", + "sha256:26526174d11fb2163832628d943edd452e07528b0ecc0c83c88256a59a32287c", + "sha256:2690bf0835f34ef3451860b02471e9560e4b3caf7413abeaa7544af72eb6d9ed", + "sha256:2b6f2d9a60413e75651cebe33c3f2f66d61209db44e8b9cf6d8d66fb0cb01fda", + "sha256:3ce91c6b92160ecefedf95a8c61fbf4fb36b0addef1a40c654acf1ad390653d0", + "sha256:43d16d7e9e9eaace3d9f1828b617b1be248f90d031a4b2dc1b6e1c88f1602dcf", + "sha256:52b6455da5f547cad72fd5cfc57a16678573fda6c695d257b5c266a44dbbd172", + "sha256:533f3036c8f58e6381fcca3306fe988740638c62c7fc86b7fae9c74b85ac3cdc", + "sha256:62d2abe5c733394058cb381d088bcab64a18da3ce9dc9a8ef2a18e122cbe47f1", + "sha256:72c34f99164679e44a5cbf19bf1a13be4e715c680816302b6ceca49b979fde91", + "sha256:81fc07feed4e40a7c0bdd266efa65e5afc83b5e0f1063007acc6759a957322a1", + "sha256:82093e673182c761ce54dfab17f026a06be3c011fee9b653855b9a2649f20232", + "sha256:87947fef728f72860407c446fd9b4a0f98e39e91ad7ae80803c02a85738e63ef", + "sha256:8b18c5a5a6b35b6311d2c356782ce3c7bacf6d987d9dc479178577391bf1c7dd", + "sha256:90e1850e993aa6b81bafaf672c8e508eaa17fbb5eb23aba93f7f4df822f3bd29", + "sha256:99f71e365bcb03a8debe1a75061329c9e45379f244a229442319d64c53c4e844", + "sha256:9b2c559104a90bf0043d6ef262ca205326d1fe6ec572dcf59e34be9289432793", + "sha256:ad22b073d92ea65b063e612154c72d6367dec3dd47ed33c02e3ab339eabe7bf3", + "sha256:bc3648da235fee2113a8cb80154d9fff4e2689d2d4a11ad35c1ecae23454b539", + "sha256:d0e2478bde68c5d853bcd306b5aae8fbe80417e87957a21fa6ee71edb90639f2", + "sha256:d3e6912d2370925222d2bfb3bd2ba02e9698b8da89cf7192ddf80cbb9f2455ee", + "sha256:d4fa98e3e15863568ea89eaec5e0866ca763980bdc56098dd9316865c111a28e", + "sha256:ee924a23457b373241ff39d21570360afd8ccb58520eb1e8e18eb00827b73e2d" ], - "version": "==5.0a6" + "version": "==5.0a7" }, "decorator": { "hashes": [ @@ -675,11 +675,11 @@ }, "detect-secrets": { "hashes": [ - "sha256:7e1820a3c4ac412a7a2cec13075c274ae4bfc9167b4b831ad3c7f0e6208c9488", - "sha256:bacb5842f149f39799409039fafb1902554ac0c71a9764cc8a8ffab85f99efc1" + "sha256:d6b22e93fa5ccdf11391f87d18c45cba64e11463fdb367e2314cdbeba6963ec0", + "sha256:e2189cd21619fc95a3ee7ec7adfb61adf66e2e4e78d518318a6025ca0f62b364" ], "index": "pypi", - "version": "==0.12.6" + "version": "==0.12.7" }, "docopt": { "hashes": [ @@ -712,10 +712,10 @@ }, "gitdb2": { "hashes": [ - "sha256:83361131a1836661a155172932a13c08bda2db3674e4caa32368aa6eb02f38c2", - "sha256:e3a0141c5f2a3f635c7209d56c496ebe1ad35da82fe4d3ec4aaa36278d70648a" + "sha256:1b6df1433567a51a4a9c1a5a0de977aa351a405cc56d7d35f3388bad1f630350", + "sha256:96bbb507d765a7f51eb802554a9cfe194a174582f772e0d89f4e87288c288b7b" ], - "version": "==2.0.5" + "version": "==2.0.6" }, "gitpython": { "hashes": [ @@ -939,11 +939,11 @@ }, "pylint": { "hashes": [ - "sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09", - "sha256:723e3db49555abaf9bf79dc474c6b9e2935ad82230b10c1138a71ea41ac0fff1" + "sha256:7edbae11476c2182708063ac387a8f97c760d9cfe36a5ede0ca996f90cf346c8", + "sha256:844ce067788028c1a35086a5c66bc5e599ddd851841c41d6ee1623b36774d9f2" ], "index": "pypi", - "version": "==2.3.1" + "version": "==2.4.2" }, "pytest": { "hashes": [ @@ -978,11 +978,11 @@ }, "pytest-mock": { "hashes": [ - "sha256:43ce4e9dd5074993e7c021bb1c22cbb5363e612a2b5a76bc6d956775b10758b7", - "sha256:5bf5771b1db93beac965a7347dc81c675ec4090cb841e49d9d34637a25c30568" + "sha256:3fd5ffb33b7041aea60a77f77b98e05d5acd577d53a01bf2ff0ca9780c6e3d84", + "sha256:a89104018a4083b5c402e23b15b855924b74d9a3511e94f230a33621b72e35e1" ], "index": "pypi", - "version": "==1.10.4" + "version": "==1.11.0" }, "pytest-watch": { "hashes": [ @@ -1050,10 +1050,10 @@ }, "soupsieve": { "hashes": [ - "sha256:8662843366b8d8779dec4e2f921bebec9afd856a5ff2e82cd419acc5054a1a92", - "sha256:a5a6166b4767725fd52ae55fee8c8b6137d9a51e9f1edea461a062a759160118" + "sha256:605f89ad5fdbfefe30cdc293303665eff2d188865d4dbe4eb510bba1edfbfce3", + "sha256:b91d676b330a0ebd5b21719cb6e9b57c57d433671f65b9c28dd3461d9a1ed0b6" ], - "version": "==1.9.3" + "version": "==1.9.4" }, "stevedore": { "hashes": [ @@ -1078,10 +1078,10 @@ }, "traitlets": { "hashes": [ - "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", - "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9" + "sha256:262089114405f22f4833be96b31e143ab906d7764a22c04c71fee0bbda4787ba", + "sha256:6ad5b30dacd5e2424c46cc94a0aeab990d98ae17d181acea2cc4272ac3409fca" ], - "version": "==4.3.2" + "version": "==4.3.3.dev0" }, "typed-ast": { "hashes": [ @@ -1101,16 +1101,16 @@ "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12" ], - "markers": "implementation_name == 'cpython'", + "markers": "implementation_name == 'cpython' and python_version < '3.8'", "version": "==1.4.0" }, "urllib3": { "hashes": [ - "sha256:2f3eadfea5d92bc7899e75b5968410b749a054b492d5a6379c1344a1481bc2cb", - "sha256:9c6c593cb28f52075016307fc26b0a0f8e82bc7d1ff19aaaa959b91710a56c47" + "sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398", + "sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86" ], "markers": "python_version >= '3.4'", - "version": "==1.25.5" + "version": "==1.25.6" }, "watchdog": { "hashes": [ diff --git a/atst/domain/csp/cloud.py b/atst/domain/csp/cloud.py index 2bf5ed29..a1eebffb 100644 --- a/atst/domain/csp/cloud.py +++ b/atst/domain/csp/cloud.py @@ -472,7 +472,7 @@ class AWSCloudProvider(CloudProviderInterface): } ] - def __init__(self, config): + def __init__(self, config, boto3=None): self.config = config self.access_key_id = config["AWS_ACCESS_KEY_ID"] @@ -484,9 +484,12 @@ class AWSCloudProvider(CloudProviderInterface): self.root_account_username = "atat" self.root_account_policy_name = "OrganizationAccountAccessRole" - import boto3 + if boto3: + self.boto3 = boto3 + else: + import boto3 - self.boto3 = boto3 + self.boto3 = boto3 def root_creds(): return {"username": self.access_key_id, "password": self.secret_key} @@ -494,7 +497,7 @@ class AWSCloudProvider(CloudProviderInterface): def create_environment( self, auth_credentials: Dict, user: User, environment: Environment ): - # TODO: Make credential structure uniform accross CSPs + org_client = self._get_client("organizations") account_name = uuid4().hex @@ -552,7 +555,9 @@ class AWSCloudProvider(CloudProviderInterface): CreateAccountRequestId=account_request["CreateAccountStatus"]["Id"] ) except WaiterError: - raise ValueError("Failed to create account.") # TODO + raise EnvironmentCreationException( + environment.id, "Failed to create account." + ) # We need to re-fetch this since the Waiter throws away the success response for some reason. created_account_status = org_client.describe_create_account_status( diff --git a/tests/domain/cloud/test_aws_csp.py b/tests/domain/cloud/test_aws_csp.py index e69de29b..d6b37186 100644 --- a/tests/domain/cloud/test_aws_csp.py +++ b/tests/domain/cloud/test_aws_csp.py @@ -0,0 +1,22 @@ +import pytest + +from atst.domain.csp.cloud import EnvironmentCreationException + +# pylint: disable=unused-import +from tests.mock_boto3 import mock_aws, mock_boto3, AUTH_CREDENTIALS +from tests.factories import EnvironmentFactory + + +def test_create_environment_succeeds(mock_aws): + environment = EnvironmentFactory.create() + account_id = mock_aws.create_environment( + AUTH_CREDENTIALS, environment.creator, environment + ) + assert "account-id" == account_id + + +@pytest.mark.mock_boto3({"organizations.describe_create_account.failure": True}) +def test_create_environment_raises_x_when_account_creation_fails(mock_aws): + environment = EnvironmentFactory.create() + with pytest.raises(EnvironmentCreationException): + mock_aws.create_environment(AUTH_CREDENTIALS, environment.creator, environment) diff --git a/tests/mock_boto3.py b/tests/mock_boto3.py new file mode 100644 index 00000000..5a646936 --- /dev/null +++ b/tests/mock_boto3.py @@ -0,0 +1,77 @@ +import pytest +from unittest.mock import Mock + +from atst.domain.csp.cloud import AWSCloudProvider + + +AWS_CONFIG = { + "AWS_ACCESS_KEY_ID": "", + "AWS_SECRET_KEY": "", + "AWS_REGION_NAME": "us-fake-1", +} +AUTH_CREDENTIALS = { + "aws_access_key_id": AWS_CONFIG["AWS_ACCESS_KEY_ID"], + "aws_secret_access_key": AWS_CONFIG["AWS_SECRET_KEY"], +} + + +def mock_boto_organizations(_config=None, **kwargs): + describe_create_account_status = ( + "SUCCEEDED" + if _config.get("organizations.describe_create_account.failure", False) == False + else "FAILED" + ) + + import boto3 + + mock = Mock(wraps=boto3.client("organizations", **kwargs)) + + # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/organizations.html#Organizations.Client.create_account + mock.create_account = Mock( + return_value={ + "CreateAccountStatus": { + "Id": "create-account-status-id", + "AccountName": "account-name", + "AccountId": "account-id", + "State": "SUCCEEDED", + } + } + ) + + # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/organizations.html#Organizations.Client.describe_create_account_status + mock.describe_create_account_status = Mock( + return_value={ + "CreateAccountStatus": { + "Id": "create-account-status-id", + "AccountName": "account-name", + "AccountId": "account-id", + "State": describe_create_account_status, + } + } + ) + return mock + + +class MockBoto3: + def __init__(self, config=None): + self.config = config or {} + + def client(self, client_name, **kwargs): + return {"organizations": mock_boto_organizations}[client_name]( + **kwargs, _config=self.config + ) + + +@pytest.fixture(scope="function") +def mock_boto3(request): + marks = request.node.get_closest_marker("mock_boto3") + if marks: + mock_config = marks.args[0] if len(marks.args) else {} + else: + mock_config = {} + return MockBoto3(mock_config) + + +@pytest.fixture(scope="function") +def mock_aws(mock_boto3): + return AWSCloudProvider(AWS_CONFIG, boto3=mock_boto3)