Merge pull request #1389 from dod-ccpo/state-machine-error-handling
state machine triggers for resuming progress from a failed state
This commit is contained in:
commit
e3397390d3
159
Pipfile.lock
generated
159
Pipfile.lock
generated
@ -26,10 +26,10 @@
|
|||||||
},
|
},
|
||||||
"alembic": {
|
"alembic": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:d412982920653db6e5a44bfd13b1d0db5685cbaaccaf226195749c706e1e862a"
|
"sha256:2df2519a5b002f881517693b95626905a39c5faf4b5a1f94de4f1441095d1d26"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.3.3"
|
"version": "==1.4.0"
|
||||||
},
|
},
|
||||||
"amqp": {
|
"amqp": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -47,10 +47,10 @@
|
|||||||
},
|
},
|
||||||
"azure-core": {
|
"azure-core": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b8ccbd901d085048e4e3e72627b066923c5bd3780e4c43cf9cf9948aee9bdf9e",
|
"sha256:8bdb12b8e937c5bdf495faadf7741ea1958436e2d24c9c5ca7bd7a5ca7a9e42f",
|
||||||
"sha256:e2cd99f0c0aef12c168d498cb5bc47a3a45c8ab08112183e3ec97e4dcb33ceb9"
|
"sha256:bcfc4502c4cfbdcbe82301119439f52542fa4a42dfc1dadd647bba8b01819823"
|
||||||
],
|
],
|
||||||
"version": "==1.2.1"
|
"version": "==1.2.2"
|
||||||
},
|
},
|
||||||
"azure-graphrbac": {
|
"azure-graphrbac": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -62,11 +62,11 @@
|
|||||||
},
|
},
|
||||||
"azure-identity": {
|
"azure-identity": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4ce65058461c277991763ed3f121efc6b9eb9c2edefb62c414dfa85c814690d3",
|
"sha256:17fa904e0447fd2a2dc19909379edb769b05656dbaf4863b8c4fdfb2bb54350c",
|
||||||
"sha256:b32acd1cdb6202bfe10d9a0858dc463d8960295da70ae18097eb3b85ab12cb91"
|
"sha256:7e9c85e3f82f1e29e5edfc7beb3030b25e8b8fd02b65d5ea1c67f13cde01da0f"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.2.0"
|
"version": "==1.3.0"
|
||||||
},
|
},
|
||||||
"azure-keyvault": {
|
"azure-keyvault": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -78,17 +78,17 @@
|
|||||||
},
|
},
|
||||||
"azure-keyvault-keys": {
|
"azure-keyvault-keys": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2983fa42e20a0e6bf6b87976716129c108e613e0292d34c5b0f0c8dc1d488e89",
|
"sha256:1c230c052b9f0b9ecaee97347fe4ebf3fcc798f92edbfd618ea264efc61ad554",
|
||||||
"sha256:38c27322637a2c52620a8b96da1942ad6a8d22d09b5a01f6fa257f7a51e52ed0"
|
"sha256:711af402a0000ac329406253470c1198cc452adc8638608461ae54c8dce92afc"
|
||||||
],
|
],
|
||||||
"version": "==4.0.0"
|
"version": "==4.0.1"
|
||||||
},
|
},
|
||||||
"azure-keyvault-secrets": {
|
"azure-keyvault-secrets": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2eae9264a8f6f59277e1a9bfdbc8b0a15969ee5a80d8efe403d7744805b4a481",
|
"sha256:0afd85eaa94962fc8ad9e71348ba0873d723d531163894ee3175f138c3180fe4",
|
||||||
"sha256:97a602406a833e8f117c540c66059c818f4321a35168dd17365fab1e4527d718"
|
"sha256:0f92705444f55ca0d5a892172eda898f42678602834f03fa8012e9979e5fe619"
|
||||||
],
|
],
|
||||||
"version": "==4.0.0"
|
"version": "==4.0.1"
|
||||||
},
|
},
|
||||||
"azure-mgmt-authorization": {
|
"azure-mgmt-authorization": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -116,11 +116,11 @@
|
|||||||
},
|
},
|
||||||
"azure-mgmt-resource": {
|
"azure-mgmt-resource": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:455a10bbae15673c7879d7515b38e1548cb1a8982dd35029ab3192565262c573",
|
"sha256:a77707bad5551bd558da450045cd2f7097fb8cbaf68610a510a9e413f8a9cf3e",
|
||||||
"sha256:c2ad10cab63999c0a88ee498bc36200ee7f6e6e5d4bf82712bde882eda11146f"
|
"sha256:d90b7d8f237b71b54cfd06480dc1ecd7dac81b22301bf2f4ead98a53cf269b6a"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==8.0.0"
|
"version": "==8.0.1"
|
||||||
},
|
},
|
||||||
"azure-mgmt-subscription": {
|
"azure-mgmt-subscription": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -186,41 +186,36 @@
|
|||||||
},
|
},
|
||||||
"cffi": {
|
"cffi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0b49274afc941c626b605fb59b59c3485c17dc776dc3cc7cc14aca74cc19cc42",
|
"sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff",
|
||||||
"sha256:0e3ea92942cb1168e38c05c1d56b0527ce31f1a370f6117f1d490b8dcd6b3a04",
|
"sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b",
|
||||||
"sha256:135f69aecbf4517d5b3d6429207b2dff49c876be724ac0c8bf8e1ea99df3d7e5",
|
"sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac",
|
||||||
"sha256:19db0cdd6e516f13329cba4903368bff9bb5a9331d3410b1b448daaadc495e54",
|
"sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0",
|
||||||
"sha256:2781e9ad0e9d47173c0093321bb5435a9dfae0ed6a762aabafa13108f5f7b2ba",
|
"sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384",
|
||||||
"sha256:291f7c42e21d72144bb1c1b2e825ec60f46d0a7468f5346841860454c7aa8f57",
|
"sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26",
|
||||||
"sha256:2c5e309ec482556397cb21ede0350c5e82f0eb2621de04b2633588d118da4396",
|
"sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6",
|
||||||
"sha256:2e9c80a8c3344a92cb04661115898a9129c074f7ab82011ef4b612f645939f12",
|
"sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b",
|
||||||
"sha256:32a262e2b90ffcfdd97c7a5e24a6012a43c61f1f5a57789ad80af1d26c6acd97",
|
"sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e",
|
||||||
"sha256:3c9fff570f13480b201e9ab69453108f6d98244a7f495e91b6c654a47486ba43",
|
"sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd",
|
||||||
"sha256:415bdc7ca8c1c634a6d7163d43fb0ea885a07e9618a64bda407e04b04333b7db",
|
"sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2",
|
||||||
"sha256:42194f54c11abc8583417a7cf4eaff544ce0de8187abaf5d29029c91b1725ad3",
|
"sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66",
|
||||||
"sha256:4424e42199e86b21fc4db83bd76909a6fc2a2aefb352cb5414833c030f6ed71b",
|
"sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc",
|
||||||
"sha256:4a43c91840bda5f55249413037b7a9b79c90b1184ed504883b72c4df70778579",
|
"sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8",
|
||||||
"sha256:599a1e8ff057ac530c9ad1778293c665cb81a791421f46922d80a86473c13346",
|
"sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55",
|
||||||
"sha256:5c4fae4e9cdd18c82ba3a134be256e98dc0596af1e7285a3d2602c97dcfa5159",
|
"sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4",
|
||||||
"sha256:5ecfa867dea6fabe2a58f03ac9186ea64da1386af2159196da51c4904e11d652",
|
"sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5",
|
||||||
"sha256:62f2578358d3a92e4ab2d830cd1c2049c9c0d0e6d3c58322993cc341bdeac22e",
|
"sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d",
|
||||||
"sha256:6471a82d5abea994e38d2c2abc77164b4f7fbaaf80261cb98394d5793f11b12a",
|
"sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78",
|
||||||
"sha256:6d4f18483d040e18546108eb13b1dfa1000a089bcf8529e30346116ea6240506",
|
"sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa",
|
||||||
"sha256:71a608532ab3bd26223c8d841dde43f3516aa5d2bf37b50ac410bb5e99053e8f",
|
"sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793",
|
||||||
"sha256:74a1d8c85fb6ff0b30fbfa8ad0ac23cd601a138f7509dc617ebc65ef305bb98d",
|
"sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f",
|
||||||
"sha256:7b93a885bb13073afb0aa73ad82059a4c41f4b7d8eb8368980448b52d4c7dc2c",
|
"sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a",
|
||||||
"sha256:7d4751da932caaec419d514eaa4215eaf14b612cff66398dd51129ac22680b20",
|
"sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f",
|
||||||
"sha256:7f627141a26b551bdebbc4855c1157feeef18241b4b8366ed22a5c7d672ef858",
|
"sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30",
|
||||||
"sha256:8169cf44dd8f9071b2b9248c35fc35e8677451c52f795daa2bb4643f32a540bc",
|
"sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f",
|
||||||
"sha256:aa00d66c0fab27373ae44ae26a66a9e43ff2a678bf63a9c7c1a9a4d61172827a",
|
"sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3",
|
||||||
"sha256:ccb032fda0873254380aa2bfad2582aedc2959186cce61e3a17abc1a55ff89c3",
|
"sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c"
|
||||||
"sha256:d754f39e0d1603b5b24a7f8484b22d2904fa551fe865fd0d4c3332f078d20d4e",
|
|
||||||
"sha256:d75c461e20e29afc0aee7172a0950157c704ff0dd51613506bd7d82b718e7410",
|
|
||||||
"sha256:dcd65317dd15bc0451f3e01c80da2216a31916bdcffd6221ca1202d96584aa25",
|
|
||||||
"sha256:e570d3ab32e2c2861c4ebe6ffcad6a8abf9347432a37608fe1fbd157b3f0036b",
|
|
||||||
"sha256:fd43a88e045cf992ed09fa724b5315b790525f2676883a6ea64e3263bae6549d"
|
|
||||||
],
|
],
|
||||||
"version": "==1.13.2"
|
"version": "==1.14.0"
|
||||||
},
|
},
|
||||||
"chardet": {
|
"chardet": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -296,11 +291,11 @@
|
|||||||
},
|
},
|
||||||
"flask-wtf": {
|
"flask-wtf": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:5d14d55cfd35f613d99ee7cba0fc3fbbe63ba02f544d349158c14ca15561cc36",
|
"sha256:57b3faf6fe5d6168bda0c36b0df1d05770f8e205e18332d0376ddb954d17aef2",
|
||||||
"sha256:d9a9e366b32dcbb98ef17228e76be15702cd2600675668bca23f63a7947fd5ac"
|
"sha256:d417e3a0008b5ba583da1763e4db0f55a1269d9dd91dcc3eb3c026d3c5dbd720"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.14.2"
|
"version": "==0.14.3"
|
||||||
},
|
},
|
||||||
"idna": {
|
"idna": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -333,10 +328,10 @@
|
|||||||
},
|
},
|
||||||
"jinja2": {
|
"jinja2": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250",
|
"sha256:c10142f819c2d22bdcd17548c46fa9b77cf4fda45097854c689666bf425e7484",
|
||||||
"sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"
|
"sha256:c922560ac46888d47384de1dbdc3daaa2ea993af4b26a436dec31fa2c19ec668"
|
||||||
],
|
],
|
||||||
"version": "==2.11.1"
|
"version": "==3.0.0a1"
|
||||||
},
|
},
|
||||||
"kombu": {
|
"kombu": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -685,10 +680,10 @@
|
|||||||
},
|
},
|
||||||
"zipp": {
|
"zipp": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:ccc94ed0909b58ffe34430ea5451f07bc0c76467d7081619a454bf5c98b89e28",
|
"sha256:5c56e330306215cd3553342cfafc73dda2c60792384117893f3a83f8a1209f50",
|
||||||
"sha256:feae2f18633c32fc71f2de629bfb3bd3c9325cd4419642b1f1da42ee488d9b98"
|
"sha256:d65287feb793213ffe11c0f31b81602be31448f38aeb8ffc2eb286c4f6f6657e"
|
||||||
],
|
],
|
||||||
"version": "==2.1.0"
|
"version": "==2.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {
|
"develop": {
|
||||||
@ -699,14 +694,6 @@
|
|||||||
],
|
],
|
||||||
"version": "==1.4.3"
|
"version": "==1.4.3"
|
||||||
},
|
},
|
||||||
"appnope": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0",
|
|
||||||
"sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"
|
|
||||||
],
|
|
||||||
"markers": "sys_platform == 'darwin'",
|
|
||||||
"version": "==0.1.0"
|
|
||||||
},
|
|
||||||
"astroid": {
|
"astroid": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:71ea07f44df9568a75d0f354c49143a4575d90645e9fead6dfb52c26a85ed13a",
|
"sha256:71ea07f44df9568a75d0f354c49143a4575d90645e9fead6dfb52c26a85ed13a",
|
||||||
@ -871,10 +858,10 @@
|
|||||||
},
|
},
|
||||||
"gitpython": {
|
"gitpython": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:9c2398ffc3dcb3c40b27324b316f08a4f93ad646d5a6328cafbb871aa79f5e42",
|
"sha256:99c77677f31f255e130f3fed4c8e0eebb35f1a09df98ff965fff6774f71688cf",
|
||||||
"sha256:c155c6a2653593ccb300462f6ef533583a913e17857cfef8fc617c246b6dc245"
|
"sha256:99cd0403cecd8a13b95d2e045b9fcaa7837137fcc5ec3105f2c413305d82c143"
|
||||||
],
|
],
|
||||||
"version": "==3.0.5"
|
"version": "==3.0.7"
|
||||||
},
|
},
|
||||||
"honcho": {
|
"honcho": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -944,10 +931,10 @@
|
|||||||
},
|
},
|
||||||
"jinja2": {
|
"jinja2": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250",
|
"sha256:c10142f819c2d22bdcd17548c46fa9b77cf4fda45097854c689666bf425e7484",
|
||||||
"sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"
|
"sha256:c922560ac46888d47384de1dbdc3daaa2ea993af4b26a436dec31fa2c19ec668"
|
||||||
],
|
],
|
||||||
"version": "==2.11.1"
|
"version": "==3.0.0a1"
|
||||||
},
|
},
|
||||||
"lazy-object-proxy": {
|
"lazy-object-proxy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -1056,10 +1043,10 @@
|
|||||||
},
|
},
|
||||||
"parso": {
|
"parso": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:1376bdc8cb81377ca481976933773295218a2df47d3e1182ba76d372b1acb128",
|
"sha256:56b2105a80e9c4df49de85e125feb6be69f49920e121406f15e7acde6c9dfc57",
|
||||||
"sha256:597f36de5102a8db05ffdf7ecdc761838b86565a4a111604c6e78beaedf1b045"
|
"sha256:951af01f61e6dccd04159042a0706a31ad437864ec6e25d0d7a96a9fbb9b0095"
|
||||||
],
|
],
|
||||||
"version": "==0.6.0"
|
"version": "==0.6.1"
|
||||||
},
|
},
|
||||||
"pathspec": {
|
"pathspec": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -1158,11 +1145,11 @@
|
|||||||
},
|
},
|
||||||
"pytest-flask": {
|
"pytest-flask": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:283730b469604ecb94caac28df99a40b7c785b828dd8d3323596718b51dfaeb2",
|
"sha256:9001f6128c5c4a0d243ce46c117f3691052828d2faf39ac151b8388657dce447",
|
||||||
"sha256:d874781b622210d8c5d8061cdb091cb059fcb12203125110bd8e6f9256ccbf49"
|
"sha256:cbd8c5b9f8f1b83e9c159ac4294964807c4934317a5fba181739ac15e1b823e6"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.15.0"
|
"version": "==0.15.1"
|
||||||
},
|
},
|
||||||
"pytest-mock": {
|
"pytest-mock": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -1270,10 +1257,10 @@
|
|||||||
},
|
},
|
||||||
"stevedore": {
|
"stevedore": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:01d9f4beecf0fbd070ddb18e5efb10567801ba7ef3ddab0074f54e3cd4e91730",
|
"sha256:18afaf1d623af5950cc0f7e75e70f917784c73b652a34a12d90b309451b5500b",
|
||||||
"sha256:e0739f9739a681c7a1fda76a102b65295e96a144ccdb552f2ae03c5f0abe8a14"
|
"sha256:a4e7dc759fb0f2e3e2f7d8ffe2358c19d45b9b8297f393ef1256858d82f69c9b"
|
||||||
],
|
],
|
||||||
"version": "==1.31.0"
|
"version": "==1.32.0"
|
||||||
},
|
},
|
||||||
"text-unidecode": {
|
"text-unidecode": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@ -1361,10 +1348,10 @@
|
|||||||
},
|
},
|
||||||
"zipp": {
|
"zipp": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:ccc94ed0909b58ffe34430ea5451f07bc0c76467d7081619a454bf5c98b89e28",
|
"sha256:5c56e330306215cd3553342cfafc73dda2c60792384117893f3a83f8a1209f50",
|
||||||
"sha256:feae2f18633c32fc71f2de629bfb3bd3c9325cd4419642b1f1da42ee488d9b98"
|
"sha256:d65287feb793213ffe11c0f31b81602be31448f38aeb8ffc2eb286c4f6f6657e"
|
||||||
],
|
],
|
||||||
"version": "==2.1.0"
|
"version": "==2.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -61,12 +61,13 @@ class UnknownServerException(GeneralCSPException):
|
|||||||
"""An error occured on the CSP side (5xx) and we don't know why
|
"""An error occured on the CSP side (5xx) and we don't know why
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, server_error):
|
def __init__(self, status_code, server_error):
|
||||||
|
self.status_code = status_code
|
||||||
self.server_error = server_error
|
self.server_error = server_error
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def message(self):
|
def message(self):
|
||||||
return "A server error occured: {}".format(self.server_error)
|
return f"A server error with status code [{self.status_code}] occured: {self.server_error}"
|
||||||
|
|
||||||
|
|
||||||
class EnvironmentCreationException(GeneralCSPException):
|
class EnvironmentCreationException(GeneralCSPException):
|
||||||
|
@ -72,7 +72,7 @@ class MockCloudProvider(CloudProviderInterface):
|
|||||||
AUTHENTICATION_EXCEPTION = AuthenticationException("Authentication failure.")
|
AUTHENTICATION_EXCEPTION = AuthenticationException("Authentication failure.")
|
||||||
AUTHORIZATION_EXCEPTION = AuthorizationException("Not authorized.")
|
AUTHORIZATION_EXCEPTION = AuthorizationException("Not authorized.")
|
||||||
NETWORK_EXCEPTION = ConnectionException("Network failure.")
|
NETWORK_EXCEPTION = ConnectionException("Network failure.")
|
||||||
SERVER_EXCEPTION = UnknownServerException("Not our fault.")
|
SERVER_EXCEPTION = UnknownServerException(500, "Not our fault.")
|
||||||
|
|
||||||
SERVER_FAILURE_PCT = 1
|
SERVER_FAILURE_PCT = 1
|
||||||
NETWORK_FAILURE_PCT = 7
|
NETWORK_FAILURE_PCT = 7
|
||||||
@ -129,10 +129,6 @@ class MockCloudProvider(CloudProviderInterface):
|
|||||||
payload is an instance of TenantCSPPayload data class
|
payload is an instance of TenantCSPPayload data class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._authorize("admin")
|
|
||||||
|
|
||||||
self._delay(1, 5)
|
|
||||||
|
|
||||||
self._maybe_raise(self.NETWORK_FAILURE_PCT, self.NETWORK_EXCEPTION)
|
self._maybe_raise(self.NETWORK_FAILURE_PCT, self.NETWORK_EXCEPTION)
|
||||||
self._maybe_raise(self.SERVER_FAILURE_PCT, self.SERVER_EXCEPTION)
|
self._maybe_raise(self.SERVER_FAILURE_PCT, self.SERVER_EXCEPTION)
|
||||||
self._maybe_raise(self.UNAUTHORIZED_RATE, self.AUTHORIZATION_EXCEPTION)
|
self._maybe_raise(self.UNAUTHORIZED_RATE, self.AUTHORIZATION_EXCEPTION)
|
||||||
|
@ -109,6 +109,14 @@ def _build_transitions(csp_stages):
|
|||||||
dest=compose_state(csp_stage, StageStates.FAILED),
|
dest=compose_state(csp_stage, StageStates.FAILED),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
transitions.append(
|
||||||
|
dict(
|
||||||
|
trigger="resume_progress_" + csp_stage.name.lower(),
|
||||||
|
source=compose_state(csp_stage, StageStates.FAILED),
|
||||||
|
dest=compose_state(csp_stage, StageStates.IN_PROGRESS),
|
||||||
|
conditions=["is_ready_resume_progress"],
|
||||||
|
)
|
||||||
|
)
|
||||||
return states, transitions
|
return states, transitions
|
||||||
|
|
||||||
|
|
||||||
@ -130,15 +138,20 @@ class FSMMixin:
|
|||||||
]
|
]
|
||||||
|
|
||||||
def fail_stage(self, stage):
|
def fail_stage(self, stage):
|
||||||
fail_trigger = "fail" + stage
|
fail_trigger = f"fail_{stage}"
|
||||||
|
|
||||||
if fail_trigger in self.machine.get_triggers(self.current_state.name):
|
if fail_trigger in self.machine.get_triggers(self.current_state.name):
|
||||||
self.trigger(fail_trigger)
|
self.trigger(fail_trigger)
|
||||||
app.logger.info(
|
app.logger.info(
|
||||||
f"calling fail trigger '{fail_trigger}' for '{self.__repr__()}'"
|
f"calling fail trigger '{fail_trigger}' for '{self.__repr__()}'"
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
app.logger.info(
|
||||||
|
f"could not locate fail trigger '{fail_trigger}' for '{self.__repr__()}'"
|
||||||
|
)
|
||||||
|
|
||||||
def finish_stage(self, stage):
|
def finish_stage(self, stage):
|
||||||
finish_trigger = "finish_" + stage
|
finish_trigger = f"finish_{stage}"
|
||||||
if finish_trigger in self.machine.get_triggers(self.current_state.name):
|
if finish_trigger in self.machine.get_triggers(self.current_state.name):
|
||||||
app.logger.info(
|
app.logger.info(
|
||||||
f"calling finish trigger '{finish_trigger}' for '{self.__repr__()}'"
|
f"calling finish trigger '{finish_trigger}' for '{self.__repr__()}'"
|
||||||
|
@ -15,13 +15,31 @@ from atst.database import db
|
|||||||
from atst.models.types import Id
|
from atst.models.types import Id
|
||||||
from atst.models.base import Base
|
from atst.models.base import Base
|
||||||
import atst.models.mixins as mixins
|
import atst.models.mixins as mixins
|
||||||
from atst.models.mixins.state_machines import FSMStates, AzureStages, _build_transitions
|
from atst.models.mixins.state_machines import (
|
||||||
|
FSMStates,
|
||||||
|
AzureStages,
|
||||||
|
StageStates,
|
||||||
|
_build_transitions,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class StateMachineMisconfiguredError(Exception):
|
||||||
|
def __init__(self, class_details):
|
||||||
|
self.class_details = class_details
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return self.class_details
|
||||||
|
|
||||||
|
|
||||||
def _stage_to_classname(stage):
|
def _stage_to_classname(stage):
|
||||||
return "".join(map(lambda word: word.capitalize(), stage.split("_")))
|
return "".join(map(lambda word: word.capitalize(), stage.split("_")))
|
||||||
|
|
||||||
|
|
||||||
|
def _stage_state_to_stage_name(state, stage_state):
|
||||||
|
return state.name.split(f"_{stage_state.name}")[0].lower()
|
||||||
|
|
||||||
|
|
||||||
def get_stage_csp_class(stage, class_type):
|
def get_stage_csp_class(stage, class_type):
|
||||||
"""
|
"""
|
||||||
given a stage name and class_type return the class
|
given a stage name and class_type return the class
|
||||||
@ -34,7 +52,9 @@ def get_stage_csp_class(stage, class_type):
|
|||||||
importlib.import_module("atst.domain.csp.cloud.models"), cls_name
|
importlib.import_module("atst.domain.csp.cloud.models"), cls_name
|
||||||
)
|
)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
print("could not import CSP Result class <%s>" % cls_name)
|
raise StateMachineMisconfiguredError(
|
||||||
|
f"could not import CSP Payload/Result class {cls_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@add_state_features(Tags)
|
@add_state_features(Tags)
|
||||||
@ -74,7 +94,7 @@ class PortfolioStateMachine(
|
|||||||
return f"<PortfolioStateMachine(state='{self.current_state.name}', portfolio='{self.portfolio.name}'"
|
return f"<PortfolioStateMachine(state='{self.current_state.name}', portfolio='{self.portfolio.name}'"
|
||||||
|
|
||||||
@reconstructor
|
@reconstructor
|
||||||
def attach_machine(self):
|
def attach_machine(self, stages=AzureStages):
|
||||||
"""
|
"""
|
||||||
This is called as a result of a sqlalchemy query.
|
This is called as a result of a sqlalchemy query.
|
||||||
Attach a machine depending on the current state.
|
Attach a machine depending on the current state.
|
||||||
@ -86,7 +106,7 @@ class PortfolioStateMachine(
|
|||||||
auto_transitions=False,
|
auto_transitions=False,
|
||||||
after_state_change="after_state_change",
|
after_state_change="after_state_change",
|
||||||
)
|
)
|
||||||
states, transitions = _build_transitions(AzureStages)
|
states, transitions = _build_transitions(stages)
|
||||||
self.machine.add_states(self.system_states + states)
|
self.machine.add_states(self.system_states + states)
|
||||||
self.machine.add_transitions(self.system_transitions + transitions)
|
self.machine.add_transitions(self.system_transitions + transitions)
|
||||||
|
|
||||||
@ -122,7 +142,23 @@ class PortfolioStateMachine(
|
|||||||
app.logger.info(
|
app.logger.info(
|
||||||
f"could not locate 'create trigger' for {self.__repr__()}"
|
f"could not locate 'create trigger' for {self.__repr__()}"
|
||||||
)
|
)
|
||||||
self.fail_stage(stage)
|
self.trigger("fail")
|
||||||
|
|
||||||
|
elif self.current_state == FSMStates.FAILED:
|
||||||
|
# get the first trigger that starts with 'resume_progress_'
|
||||||
|
resume_progress_trigger = next(
|
||||||
|
filter(
|
||||||
|
lambda trigger: trigger.startswith("resume_progress_"),
|
||||||
|
self.machine.get_triggers(FSMStates.FAILED.name),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if resume_progress_trigger:
|
||||||
|
self.trigger(resume_progress_trigger, **kwargs)
|
||||||
|
else:
|
||||||
|
app.logger.info(
|
||||||
|
f"could not locate 'resume progress trigger' for {self.__repr__()}"
|
||||||
|
)
|
||||||
|
|
||||||
elif state_obj.is_CREATED:
|
elif state_obj.is_CREATED:
|
||||||
# if last CREATED state then transition to COMPLETED
|
# if last CREATED state then transition to COMPLETED
|
||||||
@ -147,15 +183,18 @@ class PortfolioStateMachine(
|
|||||||
self.trigger(create_trigger, **kwargs)
|
self.trigger(create_trigger, **kwargs)
|
||||||
|
|
||||||
def after_in_progress_callback(self, event):
|
def after_in_progress_callback(self, event):
|
||||||
stage = self.current_state.name.split("_IN_PROGRESS")[0].lower()
|
|
||||||
|
|
||||||
# Accumulate payload w/ creds
|
# Accumulate payload w/ creds
|
||||||
payload = event.kwargs.get("csp_data")
|
payload = event.kwargs.get("csp_data")
|
||||||
|
current_stage = _stage_state_to_stage_name(
|
||||||
|
self.current_state, StageStates.IN_PROGRESS
|
||||||
|
)
|
||||||
|
payload_data_cls = get_stage_csp_class(current_stage, "payload")
|
||||||
|
|
||||||
payload_data_cls = get_stage_csp_class(stage, "payload")
|
|
||||||
if not payload_data_cls:
|
if not payload_data_cls:
|
||||||
app.logger.info(f"could not resolve payload data class for stage {stage}")
|
app.logger.info(
|
||||||
self.fail_stage(stage)
|
f"could not resolve payload data class for stage {current_stage}"
|
||||||
|
)
|
||||||
|
self.fail_stage(current_stage)
|
||||||
try:
|
try:
|
||||||
payload_data = payload_data_cls(**payload)
|
payload_data = payload_data_cls(**payload)
|
||||||
except PydanticValidationError as exc:
|
except PydanticValidationError as exc:
|
||||||
@ -165,13 +204,13 @@ class PortfolioStateMachine(
|
|||||||
app.logger.info(exc.json())
|
app.logger.info(exc.json())
|
||||||
print(exc.json())
|
print(exc.json())
|
||||||
app.logger.info(payload)
|
app.logger.info(payload)
|
||||||
self.fail_stage(stage)
|
self.fail_stage(current_stage)
|
||||||
else:
|
|
||||||
# TODO: Determine best place to do this, maybe @reconstructor
|
# TODO: Determine best place to do this, maybe @reconstructor
|
||||||
self.csp = app.csp.cloud
|
self.csp = app.csp.cloud
|
||||||
|
|
||||||
try:
|
try:
|
||||||
func_name = f"create_{stage}"
|
func_name = f"create_{current_stage}"
|
||||||
response = getattr(self.csp, func_name)(payload_data)
|
response = getattr(self.csp, func_name)(payload_data)
|
||||||
if self.portfolio.csp_data is None:
|
if self.portfolio.csp_data is None:
|
||||||
self.portfolio.csp_data = {}
|
self.portfolio.csp_data = {}
|
||||||
@ -187,17 +226,16 @@ class PortfolioStateMachine(
|
|||||||
print(exc.json())
|
print(exc.json())
|
||||||
app.logger.info(payload_data)
|
app.logger.info(payload_data)
|
||||||
# TODO: Ensure that failing the stage does not preclude a Celery retry
|
# TODO: Ensure that failing the stage does not preclude a Celery retry
|
||||||
self.fail_stage(stage)
|
self.fail_stage(current_stage)
|
||||||
# TODO: catch and handle general CSP exception here
|
# TODO: catch and handle general CSP exception here
|
||||||
except (ConnectionException, UnknownServerException) as exc:
|
except (ConnectionException, UnknownServerException) as exc:
|
||||||
app.logger.error(
|
app.logger.error(
|
||||||
f"CSP api call. Caught exception for {self.__repr__()}.",
|
f"CSP api call. Caught exception for {self.__repr__()}.", exc_info=1,
|
||||||
exc_info=1,
|
|
||||||
)
|
)
|
||||||
# TODO: Ensure that failing the stage does not preclude a Celery retry
|
# TODO: Ensure that failing the stage does not preclude a Celery retry
|
||||||
self.fail_stage(stage)
|
self.fail_stage(current_stage)
|
||||||
|
|
||||||
self.finish_stage(stage)
|
self.finish_stage(current_stage)
|
||||||
|
|
||||||
def is_csp_data_valid(self, event):
|
def is_csp_data_valid(self, event):
|
||||||
"""
|
"""
|
||||||
@ -211,6 +249,13 @@ class PortfolioStateMachine(
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def is_ready_resume_progress(self, event):
|
||||||
|
"""
|
||||||
|
This function guards advancing states from FAILED to *_IN_PROGRESS.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def application_id(self):
|
def application_id(self):
|
||||||
return None
|
return None
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
import json
|
import json
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import pendulum
|
import pendulum
|
||||||
import pydantic
|
import pydantic
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.factories import ApplicationFactory, EnvironmentFactory
|
from tests.factories import ApplicationFactory, EnvironmentFactory
|
||||||
from tests.mock_azure import AUTH_CREDENTIALS, mock_azure
|
from tests.mock_azure import AUTH_CREDENTIALS, mock_azure
|
||||||
|
|
||||||
|
from atst.domain.csp.cloud.exceptions import (
|
||||||
|
AuthenticationException,
|
||||||
|
UserProvisioningException,
|
||||||
|
ConnectionException,
|
||||||
|
UnknownServerException,
|
||||||
|
SecretException,
|
||||||
|
)
|
||||||
from atst.domain.csp.cloud import AzureCloudProvider
|
from atst.domain.csp.cloud import AzureCloudProvider
|
||||||
from atst.domain.csp.cloud.models import (
|
from atst.domain.csp.cloud.models import (
|
||||||
AdminRoleDefinitionCSPPayload,
|
AdminRoleDefinitionCSPPayload,
|
||||||
@ -66,6 +73,23 @@ from atst.domain.csp.cloud.exceptions import UserProvisioningException
|
|||||||
BILLING_ACCOUNT_NAME = "52865e4c-52e8-5a6c-da6b-c58f0814f06f:7ea5de9d-b8ce-4901-b1c5-d864320c7b03_2019-05-31"
|
BILLING_ACCOUNT_NAME = "52865e4c-52e8-5a6c-da6b-c58f0814f06f:7ea5de9d-b8ce-4901-b1c5-d864320c7b03_2019-05-31"
|
||||||
|
|
||||||
|
|
||||||
|
def mock_requests_response(
|
||||||
|
status=200, content="CONTENT", json_data=None, raise_for_status=None
|
||||||
|
):
|
||||||
|
mock_resp = Mock()
|
||||||
|
# mock raise_for_status call w/optional error
|
||||||
|
mock_resp.raise_for_status = Mock()
|
||||||
|
if raise_for_status:
|
||||||
|
mock_resp.raise_for_status.side_effect = raise_for_status
|
||||||
|
# set status code and content
|
||||||
|
mock_resp.status_code = status
|
||||||
|
mock_resp.content = content
|
||||||
|
# add json data if provided
|
||||||
|
if json_data:
|
||||||
|
mock_resp.json = mock.Mock(return_value=json_data)
|
||||||
|
return mock_resp
|
||||||
|
|
||||||
|
|
||||||
def mock_management_group_create(mock_azure, spec_dict):
|
def mock_management_group_create(mock_azure, spec_dict):
|
||||||
mock_azure.sdk.managementgroups.ManagementGroupsAPI.return_value.management_groups.create_or_update.return_value.result.return_value = (
|
mock_azure.sdk.managementgroups.ManagementGroupsAPI.return_value.management_groups.create_or_update.return_value.result.return_value = (
|
||||||
spec_dict
|
spec_dict
|
||||||
@ -190,7 +214,18 @@ def test_create_tenant(mock_azure: AzureCloudProvider):
|
|||||||
"userId": "1153801116406515559",
|
"userId": "1153801116406515559",
|
||||||
}
|
}
|
||||||
mock_result.status_code = 200
|
mock_result.status_code = 200
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.post.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
payload = TenantCSPPayload(
|
payload = TenantCSPPayload(
|
||||||
**dict(
|
**dict(
|
||||||
user_id="admin",
|
user_id="admin",
|
||||||
@ -203,30 +238,16 @@ def test_create_tenant(mock_azure: AzureCloudProvider):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
mock_azure = mock_get_secret(mock_azure)
|
mock_azure = mock_get_secret(mock_azure)
|
||||||
result = mock_azure.create_tenant(payload)
|
|
||||||
body: TenantCSPResult = result.get("body")
|
|
||||||
assert body.tenant_id == "60ff9d34-82bf-4f21-b565-308ef0533435"
|
|
||||||
|
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_tenant(payload)
|
||||||
|
|
||||||
def test_create_tenant_fails(mock_azure: AzureCloudProvider):
|
result: TenantCSPResult = mock_azure.create_tenant(payload)
|
||||||
mock_result = Mock()
|
assert result.tenant_id == "60ff9d34-82bf-4f21-b565-308ef0533435"
|
||||||
mock_result.json.return_value = {"error": "body"}
|
|
||||||
mock_result.status_code = 403
|
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
|
||||||
payload = TenantCSPPayload(
|
|
||||||
**dict(
|
|
||||||
user_id="admin",
|
|
||||||
password="JediJan13$coot", # pragma: allowlist secret
|
|
||||||
domain_name="jediccpospawnedtenant2",
|
|
||||||
first_name="Tedry",
|
|
||||||
last_name="Tenet",
|
|
||||||
country_code="US",
|
|
||||||
password_recovery_email_address="thomas@promptworks.com",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
mock_azure = mock_get_secret(mock_azure)
|
|
||||||
result = mock_azure.create_tenant(payload)
|
|
||||||
assert result.get("status") == "error"
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_billing_profile_creation(mock_azure: AzureCloudProvider):
|
def test_create_billing_profile_creation(mock_azure: AzureCloudProvider):
|
||||||
@ -240,7 +261,20 @@ def test_create_billing_profile_creation(mock_azure: AzureCloudProvider):
|
|||||||
"Retry-After": "10",
|
"Retry-After": "10",
|
||||||
}
|
}
|
||||||
mock_result.status_code = 202
|
mock_result.status_code = 202
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
|
||||||
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.post.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = BillingProfileCreationCSPPayload(
|
payload = BillingProfileCreationCSPPayload(
|
||||||
**dict(
|
**dict(
|
||||||
address=dict(
|
address=dict(
|
||||||
@ -256,8 +290,16 @@ def test_create_billing_profile_creation(mock_azure: AzureCloudProvider):
|
|||||||
billing_account_name=BILLING_ACCOUNT_NAME,
|
billing_account_name=BILLING_ACCOUNT_NAME,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
result = mock_azure.create_billing_profile_creation(payload)
|
with pytest.raises(ConnectionException):
|
||||||
body: BillingProfileCreationCSPResult = result.get("body")
|
mock_azure.create_billing_profile_creation(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_billing_profile_creation(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_billing_profile_creation(payload)
|
||||||
|
|
||||||
|
body: BillingProfileCreationCSPResult = mock_azure.create_billing_profile_creation(
|
||||||
|
payload
|
||||||
|
)
|
||||||
assert body.billing_profile_retry_after == 10
|
assert body.billing_profile_retry_after == 10
|
||||||
|
|
||||||
|
|
||||||
@ -297,7 +339,18 @@ def test_validate_billing_profile_creation(mock_azure: AzureCloudProvider):
|
|||||||
},
|
},
|
||||||
"type": "Microsoft.Billing/billingAccounts/billingProfiles",
|
"type": "Microsoft.Billing/billingAccounts/billingProfiles",
|
||||||
}
|
}
|
||||||
mock_azure.sdk.requests.get.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.get.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = BillingProfileVerificationCSPPayload(
|
payload = BillingProfileVerificationCSPPayload(
|
||||||
**dict(
|
**dict(
|
||||||
@ -305,9 +358,16 @@ def test_validate_billing_profile_creation(mock_azure: AzureCloudProvider):
|
|||||||
billing_profile_verify_url="https://management.azure.com/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/operationResults/createBillingProfile_478d5706-71f9-4a8b-8d4e-2cbaca27a668?api-version=2019-10-01-preview",
|
billing_profile_verify_url="https://management.azure.com/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/operationResults/createBillingProfile_478d5706-71f9-4a8b-8d4e-2cbaca27a668?api-version=2019-10-01-preview",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_billing_profile_verification(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_billing_profile_verification(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_billing_profile_verification(payload)
|
||||||
|
|
||||||
result = mock_azure.create_billing_profile_verification(payload)
|
body: BillingProfileVerificationCSPResult = mock_azure.create_billing_profile_verification(
|
||||||
body: BillingProfileVerificationCSPResult = result.get("body")
|
payload
|
||||||
|
)
|
||||||
assert body.billing_profile_name == "KQWI-W2SU-BG7-TGB"
|
assert body.billing_profile_name == "KQWI-W2SU-BG7-TGB"
|
||||||
assert (
|
assert (
|
||||||
body.billing_profile_properties.billing_profile_display_name
|
body.billing_profile_properties.billing_profile_display_name
|
||||||
@ -336,7 +396,18 @@ def test_create_billing_profile_tenant_access(mock_azure: AzureCloudProvider):
|
|||||||
"type": "Microsoft.Billing/billingRoleAssignments",
|
"type": "Microsoft.Billing/billingRoleAssignments",
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.post.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = BillingProfileTenantAccessCSPPayload(
|
payload = BillingProfileTenantAccessCSPPayload(
|
||||||
**dict(
|
**dict(
|
||||||
@ -346,9 +417,16 @@ def test_create_billing_profile_tenant_access(mock_azure: AzureCloudProvider):
|
|||||||
billing_profile_name="KQWI-W2SU-BG7-TGB",
|
billing_profile_name="KQWI-W2SU-BG7-TGB",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_billing_profile_tenant_access(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_billing_profile_tenant_access(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_billing_profile_tenant_access(payload)
|
||||||
|
|
||||||
result = mock_azure.create_billing_profile_tenant_access(payload)
|
body: BillingProfileTenantAccessCSPResult = mock_azure.create_billing_profile_tenant_access(
|
||||||
body: BillingProfileTenantAccessCSPResult = result.get("body")
|
payload
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
body.billing_role_assignment_name
|
body.billing_role_assignment_name
|
||||||
== "40000000-aaaa-bbbb-cccc-100000000000_0a5f4926-e3ee-4f47-a6e3-8b0a30a40e3d"
|
== "40000000-aaaa-bbbb-cccc-100000000000_0a5f4926-e3ee-4f47-a6e3-8b0a30a40e3d"
|
||||||
@ -367,7 +445,18 @@ def test_create_task_order_billing_creation(mock_azure: AzureCloudProvider):
|
|||||||
"Retry-After": "10",
|
"Retry-After": "10",
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_azure.sdk.requests.patch.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.patch.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = TaskOrderBillingCreationCSPPayload(
|
payload = TaskOrderBillingCreationCSPPayload(
|
||||||
**dict(
|
**dict(
|
||||||
@ -376,9 +465,16 @@ def test_create_task_order_billing_creation(mock_azure: AzureCloudProvider):
|
|||||||
billing_profile_name="KQWI-W2SU-BG7-TGB",
|
billing_profile_name="KQWI-W2SU-BG7-TGB",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_task_order_billing_creation(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_task_order_billing_creation(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_task_order_billing_creation(payload)
|
||||||
|
|
||||||
result = mock_azure.create_task_order_billing_creation(payload)
|
body: TaskOrderBillingCreationCSPResult = mock_azure.create_task_order_billing_creation(
|
||||||
body: TaskOrderBillingCreationCSPResult = result.get("body")
|
payload
|
||||||
|
)
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
body.task_order_billing_verify_url
|
body.task_order_billing_verify_url
|
||||||
@ -428,7 +524,18 @@ def test_create_task_order_billing_verification(mock_azure):
|
|||||||
},
|
},
|
||||||
"type": "Microsoft.Billing/billingAccounts/billingProfiles",
|
"type": "Microsoft.Billing/billingAccounts/billingProfiles",
|
||||||
}
|
}
|
||||||
mock_azure.sdk.requests.get.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.get.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = TaskOrderBillingVerificationCSPPayload(
|
payload = TaskOrderBillingVerificationCSPPayload(
|
||||||
**dict(
|
**dict(
|
||||||
@ -436,9 +543,16 @@ def test_create_task_order_billing_verification(mock_azure):
|
|||||||
task_order_billing_verify_url="https://management.azure.com/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/operationResults/createBillingProfile_478d5706-71f9-4a8b-8d4e-2cbaca27a668?api-version=2019-10-01-preview",
|
task_order_billing_verify_url="https://management.azure.com/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/operationResults/createBillingProfile_478d5706-71f9-4a8b-8d4e-2cbaca27a668?api-version=2019-10-01-preview",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_task_order_billing_verification(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_task_order_billing_verification(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_task_order_billing_verification(payload)
|
||||||
|
|
||||||
result = mock_azure.create_task_order_billing_verification(payload)
|
body: TaskOrderBillingVerificationCSPResult = mock_azure.create_task_order_billing_verification(
|
||||||
body: TaskOrderBillingVerificationCSPResult = result.get("body")
|
payload
|
||||||
|
)
|
||||||
assert body.billing_profile_name == "KQWI-W2SU-BG7-TGB"
|
assert body.billing_profile_name == "KQWI-W2SU-BG7-TGB"
|
||||||
assert (
|
assert (
|
||||||
body.billing_profile_enabled_plan_details.enabled_azure_plans[0].get("skuId")
|
body.billing_profile_enabled_plan_details.enabled_azure_plans[0].get("skuId")
|
||||||
@ -463,7 +577,18 @@ def test_create_billing_instruction(mock_azure: AzureCloudProvider):
|
|||||||
"type": "Microsoft.Billing/billingAccounts/billingProfiles/billingInstructions",
|
"type": "Microsoft.Billing/billingAccounts/billingProfiles/billingInstructions",
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_azure.sdk.requests.put.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.put.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = BillingInstructionCSPPayload(
|
payload = BillingInstructionCSPPayload(
|
||||||
**dict(
|
**dict(
|
||||||
@ -477,8 +602,13 @@ def test_create_billing_instruction(mock_azure: AzureCloudProvider):
|
|||||||
billing_profile_name="KQWI-W2SU-BG7-TGB",
|
billing_profile_name="KQWI-W2SU-BG7-TGB",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
result = mock_azure.create_billing_instruction(payload)
|
with pytest.raises(ConnectionException):
|
||||||
body: BillingInstructionCSPResult = result.get("body")
|
mock_azure.create_billing_instruction(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_billing_instruction(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_billing_instruction(payload)
|
||||||
|
body: BillingInstructionCSPResult = mock_azure.create_billing_instruction(payload)
|
||||||
assert body.reported_clin_name == "TO1:CLIN001"
|
assert body.reported_clin_name == "TO1:CLIN001"
|
||||||
|
|
||||||
|
|
||||||
@ -494,7 +624,18 @@ def test_create_product_purchase(mock_azure: AzureCloudProvider):
|
|||||||
"Retry-After": "10",
|
"Retry-After": "10",
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.post.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = ProductPurchaseCSPPayload(
|
payload = ProductPurchaseCSPPayload(
|
||||||
**dict(
|
**dict(
|
||||||
@ -503,9 +644,14 @@ def test_create_product_purchase(mock_azure: AzureCloudProvider):
|
|||||||
billing_profile_name="KQWI-W2SU-BG7-TGB",
|
billing_profile_name="KQWI-W2SU-BG7-TGB",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_product_purchase(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_product_purchase(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_product_purchase(payload)
|
||||||
|
|
||||||
result = mock_azure.create_product_purchase(payload)
|
body: ProductPurchaseCSPResult = mock_azure.create_product_purchase(payload)
|
||||||
body: ProductPurchaseCSPResult = result.get("body")
|
|
||||||
assert (
|
assert (
|
||||||
body.product_purchase_verify_url
|
body.product_purchase_verify_url
|
||||||
== "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/operationResults/patchBillingProfile_KQWI-W2SU-BG7-TGB:02715576-4118-466c-bca7-b1cd3169ff46?api-version=2019-10-01-preview"
|
== "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/operationResults/patchBillingProfile_KQWI-W2SU-BG7-TGB:02715576-4118-466c-bca7-b1cd3169ff46?api-version=2019-10-01-preview"
|
||||||
@ -540,7 +686,18 @@ def test_create_product_purchase_verification(mock_azure):
|
|||||||
"type": "Microsoft.Billing/billingAccounts/billingProfiles/invoiceSections/products",
|
"type": "Microsoft.Billing/billingAccounts/billingProfiles/invoiceSections/products",
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_azure.sdk.requests.get.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.get.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = ProductPurchaseVerificationCSPPayload(
|
payload = ProductPurchaseVerificationCSPPayload(
|
||||||
**dict(
|
**dict(
|
||||||
@ -548,9 +705,16 @@ def test_create_product_purchase_verification(mock_azure):
|
|||||||
product_purchase_verify_url="https://management.azure.com/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/operationResults/createBillingProfile_478d5706-71f9-4a8b-8d4e-2cbaca27a668?api-version=2019-10-01-preview",
|
product_purchase_verify_url="https://management.azure.com/providers/Microsoft.Billing/billingAccounts/7c89b735-b22b-55c0-ab5a-c624843e8bf6:de4416ce-acc6-44b1-8122-c87c4e903c91_2019-05-31/operationResults/createBillingProfile_478d5706-71f9-4a8b-8d4e-2cbaca27a668?api-version=2019-10-01-preview",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_product_purchase_verification(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_product_purchase_verification(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_product_purchase_verification(payload)
|
||||||
|
|
||||||
result = mock_azure.create_product_purchase_verification(payload)
|
body: ProductPurchaseVerificationCSPResult = mock_azure.create_product_purchase_verification(
|
||||||
body: ProductPurchaseVerificationCSPResult = result.get("body")
|
payload
|
||||||
|
)
|
||||||
assert body.premium_purchase_date == "01/31/2020"
|
assert body.premium_purchase_date == "01/31/2020"
|
||||||
|
|
||||||
|
|
||||||
@ -566,12 +730,31 @@ def test_create_tenant_principal_app(mock_azure: AzureCloudProvider):
|
|||||||
mock_result.ok = True
|
mock_result.ok = True
|
||||||
mock_result.json.return_value = {"appId": "appId", "id": "id"}
|
mock_result.json.return_value = {"appId": "appId", "id": "id"}
|
||||||
|
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.post.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
mock_azure = mock_get_secret(mock_azure)
|
mock_azure = mock_get_secret(mock_azure)
|
||||||
|
|
||||||
payload = TenantPrincipalAppCSPPayload(
|
payload = TenantPrincipalAppCSPPayload(
|
||||||
**{"tenant_id": "6d2d2d6c-a6d6-41e1-8bb1-73d11475f8f4"}
|
**{"tenant_id": "6d2d2d6c-a6d6-41e1-8bb1-73d11475f8f4"}
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant_principal_app(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant_principal_app(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_tenant_principal_app(payload)
|
||||||
|
|
||||||
result: TenantPrincipalAppCSPResult = mock_azure.create_tenant_principal_app(
|
result: TenantPrincipalAppCSPResult = mock_azure.create_tenant_principal_app(
|
||||||
payload
|
payload
|
||||||
)
|
)
|
||||||
@ -591,7 +774,19 @@ def test_create_tenant_principal(mock_azure: AzureCloudProvider):
|
|||||||
mock_result.ok = True
|
mock_result.ok = True
|
||||||
mock_result.json.return_value = {"id": "principal_id"}
|
mock_result.json.return_value = {"id": "principal_id"}
|
||||||
|
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.post.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
mock_azure = mock_get_secret(mock_azure)
|
mock_azure = mock_get_secret(mock_azure)
|
||||||
|
|
||||||
payload = TenantPrincipalCSPPayload(
|
payload = TenantPrincipalCSPPayload(
|
||||||
@ -600,6 +795,12 @@ def test_create_tenant_principal(mock_azure: AzureCloudProvider):
|
|||||||
"principal_app_id": "appId",
|
"principal_app_id": "appId",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant_principal(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant_principal(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_tenant_principal(payload)
|
||||||
|
|
||||||
result: TenantPrincipalCSPResult = mock_azure.create_tenant_principal(payload)
|
result: TenantPrincipalCSPResult = mock_azure.create_tenant_principal(payload)
|
||||||
|
|
||||||
@ -618,7 +819,18 @@ def test_create_tenant_principal_credential(mock_azure: AzureCloudProvider):
|
|||||||
mock_result.ok = True
|
mock_result.ok = True
|
||||||
mock_result.json.return_value = {"secretText": "new secret key"}
|
mock_result.json.return_value = {"secretText": "new secret key"}
|
||||||
|
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.post.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
mock_azure = mock_get_secret(mock_azure)
|
mock_azure = mock_get_secret(mock_azure)
|
||||||
|
|
||||||
@ -629,6 +841,12 @@ def test_create_tenant_principal_credential(mock_azure: AzureCloudProvider):
|
|||||||
"principal_app_object_id": "appObjId",
|
"principal_app_object_id": "appObjId",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant_principal_credential(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant_principal_credential(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_tenant_principal_credential(payload)
|
||||||
|
|
||||||
result: TenantPrincipalCredentialCSPResult = mock_azure.create_tenant_principal_credential(
|
result: TenantPrincipalCredentialCSPResult = mock_azure.create_tenant_principal_credential(
|
||||||
payload
|
payload
|
||||||
@ -654,12 +872,29 @@ def test_create_admin_role_definition(mock_azure: AzureCloudProvider):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_azure.sdk.requests.get.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.get.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
mock_azure = mock_get_secret(mock_azure)
|
mock_azure = mock_get_secret(mock_azure)
|
||||||
|
|
||||||
payload = AdminRoleDefinitionCSPPayload(
|
payload = AdminRoleDefinitionCSPPayload(
|
||||||
**{"tenant_id": "6d2d2d6c-a6d6-41e1-8bb1-73d11475f8f4"}
|
**{"tenant_id": "6d2d2d6c-a6d6-41e1-8bb1-73d11475f8f4"}
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_admin_role_definition(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_admin_role_definition(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_admin_role_definition(payload)
|
||||||
|
|
||||||
result: AdminRoleDefinitionCSPResult = mock_azure.create_admin_role_definition(
|
result: AdminRoleDefinitionCSPResult = mock_azure.create_admin_role_definition(
|
||||||
payload
|
payload
|
||||||
@ -680,7 +915,18 @@ def test_create_tenant_admin_ownership(mock_azure: AzureCloudProvider):
|
|||||||
mock_result.ok = True
|
mock_result.ok = True
|
||||||
mock_result.json.return_value = {"id": "id"}
|
mock_result.json.return_value = {"id": "id"}
|
||||||
|
|
||||||
mock_azure.sdk.requests.put.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.put.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = TenantAdminOwnershipCSPPayload(
|
payload = TenantAdminOwnershipCSPPayload(
|
||||||
**{
|
**{
|
||||||
@ -688,6 +934,12 @@ def test_create_tenant_admin_ownership(mock_azure: AzureCloudProvider):
|
|||||||
"user_object_id": "971efe4d-1e80-4e39-b3b9-4e5c63ad446d",
|
"user_object_id": "971efe4d-1e80-4e39-b3b9-4e5c63ad446d",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant_admin_ownership(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant_admin_ownership(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_tenant_admin_ownership(payload)
|
||||||
|
|
||||||
result: TenantAdminOwnershipCSPResult = mock_azure.create_tenant_admin_ownership(
|
result: TenantAdminOwnershipCSPResult = mock_azure.create_tenant_admin_ownership(
|
||||||
payload
|
payload
|
||||||
@ -708,7 +960,18 @@ def test_create_tenant_principal_ownership(mock_azure: AzureCloudProvider):
|
|||||||
mock_result.ok = True
|
mock_result.ok = True
|
||||||
mock_result.json.return_value = {"id": "id"}
|
mock_result.json.return_value = {"id": "id"}
|
||||||
|
|
||||||
mock_azure.sdk.requests.put.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.put.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = TenantPrincipalOwnershipCSPPayload(
|
payload = TenantPrincipalOwnershipCSPPayload(
|
||||||
**{
|
**{
|
||||||
@ -716,6 +979,12 @@ def test_create_tenant_principal_ownership(mock_azure: AzureCloudProvider):
|
|||||||
"principal_id": "971efe4d-1e80-4e39-b3b9-4e5c63ad446d",
|
"principal_id": "971efe4d-1e80-4e39-b3b9-4e5c63ad446d",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant_principal_ownership(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_tenant_principal_ownership(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_tenant_principal_ownership(payload)
|
||||||
|
|
||||||
result: TenantPrincipalOwnershipCSPResult = mock_azure.create_tenant_principal_ownership(
|
result: TenantPrincipalOwnershipCSPResult = mock_azure.create_tenant_principal_ownership(
|
||||||
payload
|
payload
|
||||||
@ -736,7 +1005,18 @@ def test_create_principal_admin_role(mock_azure: AzureCloudProvider):
|
|||||||
mock_result.ok = True
|
mock_result.ok = True
|
||||||
mock_result.json.return_value = {"id": "id"}
|
mock_result.json.return_value = {"id": "id"}
|
||||||
|
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.post.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = PrincipalAdminRoleCSPPayload(
|
payload = PrincipalAdminRoleCSPPayload(
|
||||||
**{
|
**{
|
||||||
@ -745,6 +1025,12 @@ def test_create_principal_admin_role(mock_azure: AzureCloudProvider):
|
|||||||
"admin_role_def_id": uuid4().hex,
|
"admin_role_def_id": uuid4().hex,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_principal_admin_role(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_principal_admin_role(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_principal_admin_role(payload)
|
||||||
|
|
||||||
result: PrincipalAdminRoleCSPResult = mock_azure.create_principal_admin_role(
|
result: PrincipalAdminRoleCSPResult = mock_azure.create_principal_admin_role(
|
||||||
payload
|
payload
|
||||||
@ -768,7 +1054,20 @@ def test_create_subscription_creation(mock_azure: AzureCloudProvider):
|
|||||||
"Retry-After": 10,
|
"Retry-After": 10,
|
||||||
}
|
}
|
||||||
mock_result.json.return_value = {}
|
mock_result.json.return_value = {}
|
||||||
mock_azure.sdk.requests.put.return_value = mock_result
|
|
||||||
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.put.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
management_group_id = str(uuid4())
|
management_group_id = str(uuid4())
|
||||||
payload = SubscriptionCreationCSPPayload(
|
payload = SubscriptionCreationCSPPayload(
|
||||||
**dict(
|
**dict(
|
||||||
@ -780,6 +1079,12 @@ def test_create_subscription_creation(mock_azure: AzureCloudProvider):
|
|||||||
invoice_section_name="6HMZ-2HLO-PJA-TGB",
|
invoice_section_name="6HMZ-2HLO-PJA-TGB",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_subscription_creation(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_subscription_creation(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_subscription_creation(payload)
|
||||||
|
|
||||||
result: SubscriptionCreationCSPResult = mock_azure.create_subscription_creation(
|
result: SubscriptionCreationCSPResult = mock_azure.create_subscription_creation(
|
||||||
payload
|
payload
|
||||||
@ -801,7 +1106,19 @@ def test_create_subscription_verification(mock_azure: AzureCloudProvider):
|
|||||||
mock_result.json.return_value = {
|
mock_result.json.return_value = {
|
||||||
"subscriptionLink": "/subscriptions/60fbbb72-0516-4253-ab18-c92432ba3230"
|
"subscriptionLink": "/subscriptions/60fbbb72-0516-4253-ab18-c92432ba3230"
|
||||||
}
|
}
|
||||||
mock_azure.sdk.requests.get.return_value = mock_result
|
|
||||||
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.get.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = SubscriptionVerificationCSPPayload(
|
payload = SubscriptionVerificationCSPPayload(
|
||||||
**dict(
|
**dict(
|
||||||
@ -809,6 +1126,12 @@ def test_create_subscription_verification(mock_azure: AzureCloudProvider):
|
|||||||
subscription_verify_url="https://verify.me",
|
subscription_verify_url="https://verify.me",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_subscription_verification(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.create_subscription_verification(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.create_subscription_verification(payload)
|
||||||
|
|
||||||
result: SuscriptionVerificationCSPResult = mock_azure.create_subscription_verification(
|
result: SuscriptionVerificationCSPResult = mock_azure.create_subscription_verification(
|
||||||
payload
|
payload
|
||||||
@ -837,7 +1160,20 @@ def test_get_reporting_data(mock_azure: AzureCloudProvider):
|
|||||||
"type": "Microsoft.CostManagement/query",
|
"type": "Microsoft.CostManagement/query",
|
||||||
}
|
}
|
||||||
mock_result.ok = True
|
mock_result.ok = True
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
|
||||||
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.post.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
mock_azure = mock_get_secret(mock_azure)
|
mock_azure = mock_get_secret(mock_azure)
|
||||||
|
|
||||||
# Subset of a profile's CSP data that we care about for reporting
|
# Subset of a profile's CSP data that we care about for reporting
|
||||||
@ -851,14 +1187,19 @@ def test_get_reporting_data(mock_azure: AzureCloudProvider):
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
payload = ReportingCSPPayload(
|
||||||
data: CostManagementQueryCSPResult = mock_azure.get_reporting_data(
|
|
||||||
ReportingCSPPayload(
|
|
||||||
from_date=pendulum.now().subtract(years=1).add(days=1).format("YYYY-MM-DD"),
|
from_date=pendulum.now().subtract(years=1).add(days=1).format("YYYY-MM-DD"),
|
||||||
to_date=pendulum.now().format("YYYY-MM-DD"),
|
to_date=pendulum.now().format("YYYY-MM-DD"),
|
||||||
**csp_data,
|
**csp_data,
|
||||||
)
|
)
|
||||||
)
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.get_reporting_data(payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure.get_reporting_data(payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure.get_reporting_data(payload)
|
||||||
|
|
||||||
|
data: CostManagementQueryCSPResult = mock_azure.get_reporting_data(payload)
|
||||||
|
|
||||||
assert isinstance(data, CostManagementQueryCSPResult)
|
assert isinstance(data, CostManagementQueryCSPResult)
|
||||||
assert data.name == "e82d0cda-2ffb-4476-a98a-425c83c216f9"
|
assert data.name == "e82d0cda-2ffb-4476-a98a-425c83c216f9"
|
||||||
@ -868,7 +1209,20 @@ def test_get_reporting_data(mock_azure: AzureCloudProvider):
|
|||||||
def test_get_reporting_data_malformed_payload(mock_azure: AzureCloudProvider):
|
def test_get_reporting_data_malformed_payload(mock_azure: AzureCloudProvider):
|
||||||
mock_result = Mock()
|
mock_result = Mock()
|
||||||
mock_result.ok = True
|
mock_result.ok = True
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
|
||||||
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.post.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
mock_azure = mock_get_secret(mock_azure)
|
mock_azure = mock_get_secret(mock_azure)
|
||||||
|
|
||||||
# Malformed csp_data payloads that should throw pydantic validation errors
|
# Malformed csp_data payloads that should throw pydantic validation errors
|
||||||
@ -905,6 +1259,26 @@ def test_get_secret(mock_azure: AzureCloudProvider):
|
|||||||
assert mock_azure.get_secret("secret key") == "my secret"
|
assert mock_azure.get_secret("secret key") == "my secret"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_secret_secret_exception(mock_azure: AzureCloudProvider):
|
||||||
|
with patch.object(
|
||||||
|
AzureCloudProvider,
|
||||||
|
"_get_client_secret_credential_obj",
|
||||||
|
wraps=mock_azure._get_client_secret_credential_obj,
|
||||||
|
) as _get_client_secret_credential_obj:
|
||||||
|
_get_client_secret_credential_obj.return_value = {}
|
||||||
|
|
||||||
|
mock_azure.sdk.secrets.SecretClient.return_value.get_secret.return_value = (
|
||||||
|
"my secret"
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_azure.sdk.secrets.SecretClient.return_value.get_secret.side_effect = [
|
||||||
|
mock_azure.sdk.azure_exceptions.HttpResponseError,
|
||||||
|
]
|
||||||
|
|
||||||
|
with pytest.raises(SecretException):
|
||||||
|
mock_azure.get_secret("secret key") == "my secret"
|
||||||
|
|
||||||
|
|
||||||
def test_set_secret(mock_azure: AzureCloudProvider):
|
def test_set_secret(mock_azure: AzureCloudProvider):
|
||||||
with patch.object(
|
with patch.object(
|
||||||
AzureCloudProvider,
|
AzureCloudProvider,
|
||||||
@ -916,15 +1290,45 @@ def test_set_secret(mock_azure: AzureCloudProvider):
|
|||||||
mock_azure.sdk.secrets.SecretClient.return_value.set_secret.return_value = (
|
mock_azure.sdk.secrets.SecretClient.return_value.set_secret.return_value = (
|
||||||
"my secret"
|
"my secret"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert mock_azure.set_secret("secret key", "secret_value") == "my secret"
|
assert mock_azure.set_secret("secret key", "secret_value") == "my secret"
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_secret_secret_exception(mock_azure: AzureCloudProvider):
|
||||||
|
with patch.object(
|
||||||
|
AzureCloudProvider,
|
||||||
|
"_get_client_secret_credential_obj",
|
||||||
|
wraps=mock_azure._get_client_secret_credential_obj,
|
||||||
|
) as _get_client_secret_credential_obj:
|
||||||
|
_get_client_secret_credential_obj.return_value = {}
|
||||||
|
|
||||||
|
mock_azure.sdk.secrets.SecretClient.return_value.set_secret.return_value = (
|
||||||
|
"my secret"
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_azure.sdk.secrets.SecretClient.return_value.set_secret.side_effect = [
|
||||||
|
mock_azure.sdk.azure_exceptions.HttpResponseError,
|
||||||
|
]
|
||||||
|
|
||||||
|
with pytest.raises(SecretException):
|
||||||
|
mock_azure.set_secret("secret key", "secret_value")
|
||||||
|
|
||||||
|
|
||||||
def test_create_active_directory_user(mock_azure: AzureCloudProvider):
|
def test_create_active_directory_user(mock_azure: AzureCloudProvider):
|
||||||
mock_result = Mock()
|
mock_result = Mock()
|
||||||
mock_result.ok = True
|
mock_result.ok = True
|
||||||
mock_result.json.return_value = {"id": "id"}
|
mock_result.json.return_value = {"id": "id"}
|
||||||
mock_azure.sdk.requests.post.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.post.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
|
|
||||||
payload = UserCSPPayload(
|
payload = UserCSPPayload(
|
||||||
tenant_id=uuid4().hex,
|
tenant_id=uuid4().hex,
|
||||||
@ -933,6 +1337,12 @@ def test_create_active_directory_user(mock_azure: AzureCloudProvider):
|
|||||||
email="test@testerson.test",
|
email="test@testerson.test",
|
||||||
password="asdfghjkl", # pragma: allowlist secret
|
password="asdfghjkl", # pragma: allowlist secret
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure._create_active_directory_user("token", payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure._create_active_directory_user("token", payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure._create_active_directory_user("token", payload)
|
||||||
|
|
||||||
result = mock_azure._create_active_directory_user("token", payload)
|
result = mock_azure._create_active_directory_user("token", payload)
|
||||||
|
|
||||||
@ -942,8 +1352,18 @@ def test_create_active_directory_user(mock_azure: AzureCloudProvider):
|
|||||||
def test_update_active_directory_user_email(mock_azure: AzureCloudProvider):
|
def test_update_active_directory_user_email(mock_azure: AzureCloudProvider):
|
||||||
mock_result = Mock()
|
mock_result = Mock()
|
||||||
mock_result.ok = True
|
mock_result.ok = True
|
||||||
mock_azure.sdk.requests.patch.return_value = mock_result
|
mock_http_error_resp = mock_requests_response(
|
||||||
|
status=500,
|
||||||
|
raise_for_status=mock_azure.sdk.requests.exceptions.HTTPError(
|
||||||
|
"500 Server Error"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
mock_azure.sdk.requests.patch.side_effect = [
|
||||||
|
mock_azure.sdk.requests.exceptions.ConnectionError,
|
||||||
|
mock_azure.sdk.requests.exceptions.Timeout,
|
||||||
|
mock_http_error_resp,
|
||||||
|
mock_result,
|
||||||
|
]
|
||||||
payload = UserCSPPayload(
|
payload = UserCSPPayload(
|
||||||
tenant_id=uuid4().hex,
|
tenant_id=uuid4().hex,
|
||||||
display_name="Test Testerson",
|
display_name="Test Testerson",
|
||||||
@ -951,6 +1371,12 @@ def test_update_active_directory_user_email(mock_azure: AzureCloudProvider):
|
|||||||
email="test@testerson.test",
|
email="test@testerson.test",
|
||||||
password="asdfghjkl", # pragma: allowlist secret
|
password="asdfghjkl", # pragma: allowlist secret
|
||||||
)
|
)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure._update_active_directory_user_email("token", uuid4().hex, payload)
|
||||||
|
with pytest.raises(ConnectionException):
|
||||||
|
mock_azure._update_active_directory_user_email("token", uuid4().hex, payload)
|
||||||
|
with pytest.raises(UnknownServerException, match=r".*500 Server Error.*"):
|
||||||
|
mock_azure._update_active_directory_user_email("token", uuid4().hex, payload)
|
||||||
|
|
||||||
result = mock_azure._update_active_directory_user_email(
|
result = mock_azure._update_active_directory_user_email(
|
||||||
"token", uuid4().hex, payload
|
"token", uuid4().hex, payload
|
||||||
@ -985,7 +1411,6 @@ def test_create_user(mock_azure: AzureCloudProvider):
|
|||||||
)
|
)
|
||||||
|
|
||||||
result = mock_azure.create_user(payload)
|
result = mock_azure.create_user(payload)
|
||||||
|
|
||||||
assert result.id == "id"
|
assert result.id == "id"
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from atst.domain.csp import MockCloudProvider
|
from atst.domain.csp import MockCloudProvider
|
||||||
from atst.domain.csp.cloud.models import EnvironmentCSPPayload, EnvironmentCSPResult
|
from atst.domain.csp.cloud.models import (
|
||||||
|
EnvironmentCSPPayload,
|
||||||
|
EnvironmentCSPResult,
|
||||||
|
TenantCSPPayload,
|
||||||
|
TenantCSPResult,
|
||||||
|
BillingProfileCreationCSPPayload,
|
||||||
|
BillingProfileCreationCSPResult,
|
||||||
|
)
|
||||||
|
|
||||||
from tests.factories import EnvironmentFactory, EnvironmentRoleFactory, UserFactory
|
from tests.factories import EnvironmentFactory, EnvironmentRoleFactory, UserFactory
|
||||||
|
|
||||||
@ -28,6 +35,43 @@ def test_create_environment(mock_csp: MockCloudProvider):
|
|||||||
assert isinstance(result, EnvironmentCSPResult)
|
assert isinstance(result, EnvironmentCSPResult)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_tenant(mock_csp: MockCloudProvider):
|
||||||
|
payload = TenantCSPPayload(
|
||||||
|
**dict(
|
||||||
|
user_id="admin",
|
||||||
|
password="JediJan13$coot", # pragma: allowlist secret
|
||||||
|
domain_name="jediccpospawnedtenant2",
|
||||||
|
first_name="Tedry",
|
||||||
|
last_name="Tenet",
|
||||||
|
country_code="US",
|
||||||
|
password_recovery_email_address="thomas@promptworks.com",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
result = mock_csp.create_tenant(payload)
|
||||||
|
assert isinstance(result, TenantCSPResult)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_billing_profile_creation(mock_csp: MockCloudProvider):
|
||||||
|
|
||||||
|
payload = BillingProfileCreationCSPPayload(
|
||||||
|
**dict(
|
||||||
|
address=dict(
|
||||||
|
address_line_1="123 S Broad Street, Suite 2400",
|
||||||
|
company_name="Promptworks",
|
||||||
|
city="Philadelphia",
|
||||||
|
region="PA",
|
||||||
|
country="US",
|
||||||
|
postal_code="19109",
|
||||||
|
),
|
||||||
|
tenant_id="60ff9d34-82bf-4f21-b565-308ef0533435",
|
||||||
|
billing_profile_display_name="Test Billing Profile",
|
||||||
|
billing_account_name="123123",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
result = mock_csp.create_billing_profile_creation(payload)
|
||||||
|
assert isinstance(result, BillingProfileCreationCSPResult)
|
||||||
|
|
||||||
|
|
||||||
def test_create_or_update_user(mock_csp: MockCloudProvider):
|
def test_create_or_update_user(mock_csp: MockCloudProvider):
|
||||||
env_role = EnvironmentRoleFactory.create()
|
env_role = EnvironmentRoleFactory.create()
|
||||||
csp_user_id = mock_csp.create_or_update_user(CREDENTIALS, env_role, "csp_role_id")
|
csp_user_id = mock_csp.create_or_update_user(CREDENTIALS, env_role, "csp_role_id")
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
import pydantic
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
from tests.factories import (
|
from tests.factories import (
|
||||||
PortfolioStateMachineFactory,
|
PortfolioStateMachineFactory,
|
||||||
@ -8,13 +11,30 @@ from tests.factories import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from atst.models import FSMStates, PortfolioStateMachine, TaskOrder
|
from atst.models import FSMStates, PortfolioStateMachine, TaskOrder
|
||||||
from atst.models.mixins.state_machines import AzureStages, StageStates, compose_state
|
from atst.domain.csp.cloud.models import BillingProfileCreationCSPPayload
|
||||||
|
from atst.models.mixins.state_machines import (
|
||||||
|
AzureStages,
|
||||||
|
StageStates,
|
||||||
|
compose_state,
|
||||||
|
_build_transitions,
|
||||||
|
_build_csp_states,
|
||||||
|
)
|
||||||
from atst.models.portfolio import Portfolio
|
from atst.models.portfolio import Portfolio
|
||||||
from atst.models.portfolio_state_machine import get_stage_csp_class
|
from atst.models.portfolio_state_machine import (
|
||||||
|
get_stage_csp_class,
|
||||||
|
_stage_to_classname,
|
||||||
|
_stage_state_to_stage_name,
|
||||||
|
StateMachineMisconfiguredError,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# TODO: Write failure case tests
|
# TODO: Write failure case tests
|
||||||
|
|
||||||
|
|
||||||
|
class AzureStagesTest(Enum):
|
||||||
|
TENANT = "tenant"
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def portfolio():
|
def portfolio():
|
||||||
# TODO: setup clin/to as active/funded/ready
|
# TODO: setup clin/to as active/funded/ready
|
||||||
@ -37,14 +57,65 @@ def test_state_machine_trigger_next_transition(portfolio):
|
|||||||
assert sm.current_state == FSMStates.STARTED
|
assert sm.current_state == FSMStates.STARTED
|
||||||
|
|
||||||
|
|
||||||
def test_state_machine_compose_state(portfolio):
|
def test_state_machine_compose_state():
|
||||||
PortfolioStateMachineFactory.create(portfolio=portfolio)
|
|
||||||
assert (
|
assert (
|
||||||
compose_state(AzureStages.TENANT, StageStates.CREATED)
|
compose_state(AzureStages.TENANT, StageStates.CREATED)
|
||||||
== FSMStates.TENANT_CREATED
|
== FSMStates.TENANT_CREATED
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stage_to_classname():
|
||||||
|
assert (
|
||||||
|
_stage_to_classname(AzureStages.BILLING_PROFILE_CREATION.name)
|
||||||
|
== "BillingProfileCreation"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_stage_csp_class():
|
||||||
|
csp_class = get_stage_csp_class(list(AzureStages)[0].name.lower(), "payload")
|
||||||
|
assert isinstance(csp_class, pydantic.main.ModelMetaclass)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_stage_csp_class_import_fail():
|
||||||
|
with pytest.raises(StateMachineMisconfiguredError):
|
||||||
|
csp_class = get_stage_csp_class("doesnotexist", "payload")
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_transitions():
|
||||||
|
states, transitions = _build_transitions(AzureStagesTest)
|
||||||
|
assert [s.get("name").name for s in states] == [
|
||||||
|
"TENANT_CREATED",
|
||||||
|
"TENANT_IN_PROGRESS",
|
||||||
|
"TENANT_FAILED",
|
||||||
|
]
|
||||||
|
assert [s.get("tags") for s in states] == [
|
||||||
|
["TENANT", "CREATED"],
|
||||||
|
["TENANT", "IN_PROGRESS"],
|
||||||
|
["TENANT", "FAILED"],
|
||||||
|
]
|
||||||
|
assert [t.get("trigger") for t in transitions] == [
|
||||||
|
"complete",
|
||||||
|
"create_tenant",
|
||||||
|
"finish_tenant",
|
||||||
|
"fail_tenant",
|
||||||
|
"resume_progress_tenant",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_csp_states():
|
||||||
|
states = _build_csp_states(AzureStagesTest)
|
||||||
|
assert list(states) == [
|
||||||
|
"UNSTARTED",
|
||||||
|
"STARTING",
|
||||||
|
"STARTED",
|
||||||
|
"COMPLETED",
|
||||||
|
"FAILED",
|
||||||
|
"TENANT_CREATED",
|
||||||
|
"TENANT_IN_PROGRESS",
|
||||||
|
"TENANT_FAILED",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_state_machine_valid_data_classes_for_stages(portfolio):
|
def test_state_machine_valid_data_classes_for_stages(portfolio):
|
||||||
PortfolioStateMachineFactory.create(portfolio=portfolio)
|
PortfolioStateMachineFactory.create(portfolio=portfolio)
|
||||||
for stage in AzureStages:
|
for stage in AzureStages:
|
||||||
@ -52,6 +123,37 @@ def test_state_machine_valid_data_classes_for_stages(portfolio):
|
|||||||
assert get_stage_csp_class(stage.name.lower(), "result") is not None
|
assert get_stage_csp_class(stage.name.lower(), "result") is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_attach_machine(portfolio):
|
||||||
|
sm = PortfolioStateMachineFactory.create(portfolio=portfolio)
|
||||||
|
sm.machine = None
|
||||||
|
sm.attach_machine(stages=AzureStagesTest)
|
||||||
|
assert list(sm.machine.events) == [
|
||||||
|
"init",
|
||||||
|
"start",
|
||||||
|
"reset",
|
||||||
|
"fail",
|
||||||
|
"complete",
|
||||||
|
"create_tenant",
|
||||||
|
"finish_tenant",
|
||||||
|
"fail_tenant",
|
||||||
|
"resume_progress_tenant",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_fail_stage(portfolio):
|
||||||
|
sm = PortfolioStateMachineFactory.create(portfolio=portfolio)
|
||||||
|
sm.state = FSMStates.TENANT_IN_PROGRESS
|
||||||
|
sm.fail_stage("tenant")
|
||||||
|
assert sm.state == FSMStates.TENANT_FAILED
|
||||||
|
|
||||||
|
|
||||||
|
def test_stage_state_to_stage_name():
|
||||||
|
stage = _stage_state_to_stage_name(
|
||||||
|
FSMStates.TENANT_IN_PROGRESS, StageStates.IN_PROGRESS
|
||||||
|
)
|
||||||
|
assert stage == "tenant"
|
||||||
|
|
||||||
|
|
||||||
def test_state_machine_initialization(portfolio):
|
def test_state_machine_initialization(portfolio):
|
||||||
|
|
||||||
sm = PortfolioStateMachineFactory.create(portfolio=portfolio)
|
sm = PortfolioStateMachineFactory.create(portfolio=portfolio)
|
||||||
|
@ -66,6 +66,12 @@ def mock_policy():
|
|||||||
return Mock(spec=policy)
|
return Mock(spec=policy)
|
||||||
|
|
||||||
|
|
||||||
|
def mock_azure_exceptions():
|
||||||
|
from azure.core import exceptions
|
||||||
|
|
||||||
|
return exceptions
|
||||||
|
|
||||||
|
|
||||||
def mock_adal():
|
def mock_adal():
|
||||||
import adal
|
import adal
|
||||||
|
|
||||||
@ -75,7 +81,9 @@ def mock_adal():
|
|||||||
def mock_requests():
|
def mock_requests():
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
return Mock(spec=requests)
|
mock_requests = Mock(wraps=requests)
|
||||||
|
mock_requests.exceptions = requests.exceptions
|
||||||
|
return mock_requests
|
||||||
|
|
||||||
|
|
||||||
def mock_secrets():
|
def mock_secrets():
|
||||||
@ -101,6 +109,7 @@ class MockAzureSDK(object):
|
|||||||
self.graphrbac = mock_graphrbac()
|
self.graphrbac = mock_graphrbac()
|
||||||
self.credentials = mock_credentials()
|
self.credentials = mock_credentials()
|
||||||
self.identity = mock_identity()
|
self.identity = mock_identity()
|
||||||
|
self.azure_exceptions = mock_azure_exceptions()
|
||||||
self.policy = mock_policy()
|
self.policy = mock_policy()
|
||||||
self.secrets = mock_secrets()
|
self.secrets = mock_secrets()
|
||||||
self.requests = mock_requests()
|
self.requests = mock_requests()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user