diff --git a/Pipfile b/Pipfile index 939edef4..48bf9d83 100644 --- a/Pipfile +++ b/Pipfile @@ -24,7 +24,7 @@ graphlib-backport = ">=1.0.3" jinja2 = ">=3.0.1" munch = ">=2.4.0" pyyaml = ">=5.4.1" -rapyuta-io = ">=1.15.0" +rapyuta-io = ">=1.15.1" tabulate = ">=0.8.0" pyrfc3339 = ">=1.1" directory-tree = ">=0.0.3.1" diff --git a/Pipfile.lock b/Pipfile.lock index d7247d3b..84aca260 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "1c56ef72745fb3bf17c8a9fe0e1a55b53617407c0ebf890612dcd3219e2723ac" + "sha256": "a0d65286d616e25dd243f5ab007b77664bbb0c71a6065d9e34851f4c73ed4261" }, "pipfile-spec": 6, "requires": { @@ -34,11 +34,11 @@ }, "certifi": { "hashes": [ - "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", - "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" + "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516", + "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56" ], "markers": "python_version >= '3.6'", - "version": "==2024.2.2" + "version": "==2024.6.2" }, "charset-normalizer": { "hashes": [ @@ -264,11 +264,11 @@ }, "jinja2": { "hashes": [ - "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", - "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" + "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", + "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d" ], "index": "pypi", - "version": "==3.1.3" + "version": "==3.1.4" }, "jsonschema": { "hashes": [ @@ -370,11 +370,11 @@ }, "prompt-toolkit": { "hashes": [ - "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d", - "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6" + "sha256:07c60ee4ab7b7e90824b61afa840c8f5aad2d46b3e2e10acc33d8ecc94a49089", + "sha256:a29b89160e494e3ea8622b09fa5897610b437884dcdcd054fdc1308883326c2a" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.0.43" + "version": "==3.0.45" }, "pyrfc3339": { "hashes": [ @@ -527,11 +527,11 @@ }, "rapyuta-io": { "hashes": [ - "sha256:a7d4131caee32465289220c01b3866ec8fc71f6a841087e5f8acfe15e0b8f291", - "sha256:ff11fd6945fd04e006f9d2bedec6f4539985e28cfb7020312c6171dd19c130cb" + "sha256:0bb7073a06c5552510906f54236f1110a401ba73d3836ce239d7e24627192bc0", + "sha256:8ef3f8b54ca83a6e8a7d5ac63380103f3d511de32b758af5b6c69ef1637920ba" ], "index": "pypi", - "version": "==1.15.0" + "version": "==1.15.1" }, "rapyuta-io-cli": { "path": ".", @@ -539,11 +539,11 @@ }, "requests": { "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" ], - "markers": "python_version >= '3.7'", - "version": "==2.31.0" + "markers": "python_version >= '3.8'", + "version": "==2.32.3" }, "semver": { "hashes": [ @@ -555,11 +555,11 @@ }, "setuptools": { "hashes": [ - "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987", - "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32" + "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4", + "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0" ], "markers": "python_version >= '3.8'", - "version": "==69.5.1" + "version": "==70.0.0" }, "shellingham": { "hashes": [ @@ -657,11 +657,11 @@ }, "babel": { "hashes": [ - "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363", - "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287" + "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb", + "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413" ], - "markers": "python_version >= '3.7'", - "version": "==2.14.0" + "markers": "python_version >= '3.8'", + "version": "==2.15.0" }, "beautifulsoup4": { "hashes": [ @@ -695,11 +695,11 @@ }, "certifi": { "hashes": [ - "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", - "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" + "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516", + "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56" ], "markers": "python_version >= '3.6'", - "version": "==2024.2.2" + "version": "==2024.6.2" }, "charset-normalizer": { "hashes": [ @@ -854,11 +854,11 @@ }, "jinja2": { "hashes": [ - "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa", - "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90" + "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", + "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d" ], "index": "pypi", - "version": "==3.1.3" + "version": "==3.1.4" }, "markupsafe": { "hashes": [ @@ -989,30 +989,30 @@ }, "platformdirs": { "hashes": [ - "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf", - "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1" + "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", + "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3" ], "markers": "python_version >= '3.8'", - "version": "==4.2.1" + "version": "==4.2.2" }, "plette": { "extras": [ "validation" ], "hashes": [ - "sha256:12c51cd69e8e15d0bba9ea6028d9119cf143ebc418a1b6d2e7ae053db05eb768", - "sha256:a853b7a8f9e106c652a44ad356a88ac06c45036cc6ee01c6ba6165cfd752982c" + "sha256:26b0ec1ee16c1bcab719a69c5717a146cc4a100d5e7311f9b57c41d7181a35bf", + "sha256:9582f77ef8a74aa2a84e872652309083d5d6139a7c44272555f3bdeeb706669a" ], "markers": "python_version >= '3.7'", - "version": "==1.0.0" + "version": "==2.0.2" }, "pygments": { "hashes": [ - "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c", - "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367" + "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", + "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a" ], - "markers": "python_version >= '3.7'", - "version": "==2.17.2" + "markers": "python_version >= '3.8'", + "version": "==2.18.0" }, "pyparsing": { "hashes": [ @@ -1032,110 +1032,96 @@ }, "regex": { "hashes": [ - "sha256:00169caa125f35d1bca6045d65a662af0202704489fada95346cfa092ec23f39", - "sha256:03576e3a423d19dda13e55598f0fd507b5d660d42c51b02df4e0d97824fdcae3", - "sha256:03e68f44340528111067cecf12721c3df4811c67268b897fbe695c95f860ac42", - "sha256:0534b034fba6101611968fae8e856c1698da97ce2efb5c2b895fc8b9e23a5834", - "sha256:08dea89f859c3df48a440dbdcd7b7155bc675f2fa2ec8c521d02dc69e877db70", - "sha256:0a38d151e2cdd66d16dab550c22f9521ba79761423b87c01dae0a6e9add79c0d", - "sha256:0c8290b44d8b0af4e77048646c10c6e3aa583c1ca67f3b5ffb6e06cf0c6f0f89", - "sha256:10188fe732dec829c7acca7422cdd1bf57d853c7199d5a9e96bb4d40db239c73", - "sha256:1210365faba7c2150451eb78ec5687871c796b0f1fa701bfd2a4a25420482d26", - "sha256:12f6a3f2f58bb7344751919a1876ee1b976fe08b9ffccb4bbea66f26af6017b9", - "sha256:159dc4e59a159cb8e4e8f8961eb1fa5d58f93cb1acd1701d8aff38d45e1a84a6", - "sha256:20b7a68444f536365af42a75ccecb7ab41a896a04acf58432db9e206f4e525d6", - "sha256:23cff1b267038501b179ccbbd74a821ac4a7192a1852d1d558e562b507d46013", - "sha256:2c72608e70f053643437bd2be0608f7f1c46d4022e4104d76826f0839199347a", - "sha256:3399dd8a7495bbb2bacd59b84840eef9057826c664472e86c91d675d007137f5", - "sha256:34422d5a69a60b7e9a07a690094e824b66f5ddc662a5fc600d65b7c174a05f04", - "sha256:370c68dc5570b394cbaadff50e64d705f64debed30573e5c313c360689b6aadc", - "sha256:3a1018e97aeb24e4f939afcd88211ace472ba566efc5bdf53fd8fd7f41fa7170", - "sha256:3d5ac5234fb5053850d79dd8eb1015cb0d7d9ed951fa37aa9e6249a19aa4f336", - "sha256:4313ab9bf6a81206c8ac28fdfcddc0435299dc88cad12cc6305fd0e78b81f9e4", - "sha256:445ca8d3c5a01309633a0c9db57150312a181146315693273e35d936472df912", - "sha256:479595a4fbe9ed8f8f72c59717e8cf222da2e4c07b6ae5b65411e6302af9708e", - "sha256:4918fd5f8b43aa7ec031e0fef1ee02deb80b6afd49c85f0790be1dc4ce34cb50", - "sha256:4aba818dcc7263852aabb172ec27b71d2abca02a593b95fa79351b2774eb1d2b", - "sha256:4e819a806420bc010489f4e741b3036071aba209f2e0989d4750b08b12a9343f", - "sha256:4facc913e10bdba42ec0aee76d029aedda628161a7ce4116b16680a0413f658a", - "sha256:549c3584993772e25f02d0656ac48abdda73169fe347263948cf2b1cead622f3", - "sha256:5c02fcd2bf45162280613d2e4a1ca3ac558ff921ae4e308ecb307650d3a6ee51", - "sha256:5f580c651a72b75c39e311343fe6875d6f58cf51c471a97f15a938d9fe4e0d37", - "sha256:62120ed0de69b3649cc68e2965376048793f466c5a6c4370fb27c16c1beac22d", - "sha256:6295004b2dd37b0835ea5c14a33e00e8cfa3c4add4d587b77287825f3418d310", - "sha256:65436dce9fdc0aeeb0a0effe0839cb3d6a05f45aa45a4d9f9c60989beca78b9c", - "sha256:684008ec44ad275832a5a152f6e764bbe1914bea10968017b6feaecdad5736e0", - "sha256:684e52023aec43bdf0250e843e1fdd6febbe831bd9d52da72333fa201aaa2335", - "sha256:6cc38067209354e16c5609b66285af17a2863a47585bcf75285cab33d4c3b8df", - "sha256:6f2f017c5be19984fbbf55f8af6caba25e62c71293213f044da3ada7091a4455", - "sha256:743deffdf3b3481da32e8a96887e2aa945ec6685af1cfe2bcc292638c9ba2f48", - "sha256:7571f19f4a3fd00af9341c7801d1ad1967fc9c3f5e62402683047e7166b9f2b4", - "sha256:7731728b6568fc286d86745f27f07266de49603a6fdc4d19c87e8c247be452af", - "sha256:785c071c982dce54d44ea0b79cd6dfafddeccdd98cfa5f7b86ef69b381b457d9", - "sha256:78fddb22b9ef810b63ef341c9fcf6455232d97cfe03938cbc29e2672c436670e", - "sha256:7bb966fdd9217e53abf824f437a5a2d643a38d4fd5fd0ca711b9da683d452969", - "sha256:7cbc5d9e8a1781e7be17da67b92580d6ce4dcef5819c1b1b89f49d9678cc278c", - "sha256:803b8905b52de78b173d3c1e83df0efb929621e7b7c5766c0843704d5332682f", - "sha256:80b696e8972b81edf0af2a259e1b2a4a661f818fae22e5fa4fa1a995fb4a40fd", - "sha256:81500ed5af2090b4a9157a59dbc89873a25c33db1bb9a8cf123837dcc9765047", - "sha256:89ec7f2c08937421bbbb8b48c54096fa4f88347946d4747021ad85f1b3021b3c", - "sha256:8ba6745440b9a27336443b0c285d705ce73adb9ec90e2f2004c64d95ab5a7598", - "sha256:8c91e1763696c0eb66340c4df98623c2d4e77d0746b8f8f2bee2c6883fd1fe18", - "sha256:8d015604ee6204e76569d2f44e5a210728fa917115bef0d102f4107e622b08d5", - "sha256:8d1f86f3f4e2388aa3310b50694ac44daefbd1681def26b4519bd050a398dc5a", - "sha256:8f83b6fd3dc3ba94d2b22717f9c8b8512354fd95221ac661784df2769ea9bba9", - "sha256:8fc6976a3395fe4d1fbeb984adaa8ec652a1e12f36b56ec8c236e5117b585427", - "sha256:904c883cf10a975b02ab3478bce652f0f5346a2c28d0a8521d97bb23c323cc8b", - "sha256:911742856ce98d879acbea33fcc03c1d8dc1106234c5e7d068932c945db209c0", - "sha256:91797b98f5e34b6a49f54be33f72e2fb658018ae532be2f79f7c63b4ae225145", - "sha256:95399831a206211d6bc40224af1c635cb8790ddd5c7493e0bd03b85711076a53", - "sha256:956b58d692f235cfbf5b4f3abd6d99bf102f161ccfe20d2fd0904f51c72c4c66", - "sha256:98c1165f3809ce7774f05cb74e5408cd3aa93ee8573ae959a97a53db3ca3180d", - "sha256:9ab40412f8cd6f615bfedea40c8bf0407d41bf83b96f6fc9ff34976d6b7037fd", - "sha256:9df1bfef97db938469ef0a7354b2d591a2d438bc497b2c489471bec0e6baf7c4", - "sha256:a01fe2305e6232ef3e8f40bfc0f0f3a04def9aab514910fa4203bafbc0bb4682", - "sha256:a70b51f55fd954d1f194271695821dd62054d949efd6368d8be64edd37f55c86", - "sha256:a7ccdd1c4a3472a7533b0a7aa9ee34c9a2bef859ba86deec07aff2ad7e0c3b94", - "sha256:b340cccad138ecb363324aa26893963dcabb02bb25e440ebdf42e30963f1a4e0", - "sha256:b74586dd0b039c62416034f811d7ee62810174bb70dffcca6439f5236249eb09", - "sha256:b9d320b3bf82a39f248769fc7f188e00f93526cc0fe739cfa197868633d44701", - "sha256:ba2336d6548dee3117520545cfe44dc28a250aa091f8281d28804aa8d707d93d", - "sha256:ba8122e3bb94ecda29a8de4cf889f600171424ea586847aa92c334772d200331", - "sha256:bd727ad276bb91928879f3aa6396c9a1d34e5e180dce40578421a691eeb77f47", - "sha256:c21fc21a4c7480479d12fd8e679b699f744f76bb05f53a1d14182b31f55aac76", - "sha256:c2d0e7cbb6341e830adcbfa2479fdeebbfbb328f11edd6b5675674e7a1e37730", - "sha256:c2ef6f7990b6e8758fe48ad08f7e2f66c8f11dc66e24093304b87cae9037bb4a", - "sha256:c4ed75ea6892a56896d78f11006161eea52c45a14994794bcfa1654430984b22", - "sha256:cccc79a9be9b64c881f18305a7c715ba199e471a3973faeb7ba84172abb3f317", - "sha256:d0800631e565c47520aaa04ae38b96abc5196fe8b4aa9bd864445bd2b5848a7a", - "sha256:d2da13568eff02b30fd54fccd1e042a70fe920d816616fda4bf54ec705668d81", - "sha256:d61ae114d2a2311f61d90c2ef1358518e8f05eafda76eaf9c772a077e0b465ec", - "sha256:d83c2bc678453646f1a18f8db1e927a2d3f4935031b9ad8a76e56760461105dd", - "sha256:dd5acc0a7d38fdc7a3a6fd3ad14c880819008ecb3379626e56b163165162cc46", - "sha256:df79012ebf6f4efb8d307b1328226aef24ca446b3ff8d0e30202d7ebcb977a8c", - "sha256:e0a2df336d1135a0b3a67f3bbf78a75f69562c1199ed9935372b82215cddd6e2", - "sha256:e2f142b45c6fed48166faeb4303b4b58c9fcd827da63f4cf0a123c3480ae11fb", - "sha256:e697e1c0238133589e00c244a8b676bc2cfc3ab4961318d902040d099fec7483", - "sha256:e757d475953269fbf4b441207bb7dbdd1c43180711b6208e129b637792ac0b93", - "sha256:e87ab229332ceb127a165612d839ab87795972102cb9830e5f12b8c9a5c1b508", - "sha256:ea355eb43b11764cf799dda62c658c4d2fdb16af41f59bb1ccfec517b60bcb07", - "sha256:ec7e0043b91115f427998febaa2beb82c82df708168b35ece3accb610b91fac1", - "sha256:eeaa0b5328b785abc344acc6241cffde50dc394a0644a968add75fcefe15b9d4", - "sha256:f2d80a6749724b37853ece57988b39c4e79d2b5fe2869a86e8aeae3bbeef9eb0", - "sha256:fa454d26f2e87ad661c4f0c5a5fe4cf6aab1e307d1b94f16ffdfcb089ba685c0", - "sha256:fb83cc090eac63c006871fd24db5e30a1f282faa46328572661c0a24a2323a08", - "sha256:fd80d1280d473500d8086d104962a82d77bfbf2b118053824b7be28cd5a79ea5" + "sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649", + "sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35", + "sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb", + "sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68", + "sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5", + "sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133", + "sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0", + "sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d", + "sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da", + "sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f", + "sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d", + "sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53", + "sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa", + "sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a", + "sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890", + "sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67", + "sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c", + "sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2", + "sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced", + "sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741", + "sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f", + "sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa", + "sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf", + "sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4", + "sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5", + "sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2", + "sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384", + "sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7", + "sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014", + "sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704", + "sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5", + "sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2", + "sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49", + "sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1", + "sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694", + "sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629", + "sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6", + "sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435", + "sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c", + "sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835", + "sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e", + "sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201", + "sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62", + "sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5", + "sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16", + "sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f", + "sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1", + "sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f", + "sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f", + "sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145", + "sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3", + "sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed", + "sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143", + "sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca", + "sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9", + "sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa", + "sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850", + "sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80", + "sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe", + "sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656", + "sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388", + "sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1", + "sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294", + "sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3", + "sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d", + "sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b", + "sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40", + "sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600", + "sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c", + "sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569", + "sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456", + "sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9", + "sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb", + "sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e", + "sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f", + "sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d", + "sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a", + "sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a", + "sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796" ], - "markers": "python_version >= '3.7'", - "version": "==2024.4.16" + "markers": "python_version >= '3.8'", + "version": "==2024.5.15" }, "requests": { "hashes": [ - "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", - "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" + "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" ], - "markers": "python_version >= '3.7'", - "version": "==2.31.0" + "markers": "python_version >= '3.8'", + "version": "==2.32.3" }, "requirementslib": { "hashes": [ @@ -1147,11 +1133,11 @@ }, "setuptools": { "hashes": [ - "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987", - "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32" + "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4", + "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0" ], "markers": "python_version >= '3.8'", - "version": "==69.5.1" + "version": "==70.0.0" }, "six": { "hashes": [ @@ -1194,11 +1180,11 @@ }, "sphinx-click": { "hashes": [ - "sha256:6812c2db62d3fae71a4addbe5a8a0a16c97eb491f3cd63fe34b4ed7e07236f33", - "sha256:ae97557a4e9ec646045089326c3b90e026c58a45e083b8f35f17d5d6558d08a0" + "sha256:1e0a3c83bcb7c55497751b19d07ebe56b5d7b85eb76dd399cf9061b497adc317", + "sha256:f5d664321dc0c6622ff019f1e1c84e58ce0cecfddeb510e004cf60c2a3ab465b" ], "index": "pypi", - "version": "==5.1.0" + "version": "==6.0.0" }, "sphinxcontrib-applehelp": { "hashes": [ @@ -1256,13 +1242,21 @@ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.10.2" }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version < '3.11'", + "version": "==2.0.1" + }, "tomlkit": { "hashes": [ - "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b", - "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3" + "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f", + "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c" ], "markers": "python_version >= '3.7'", - "version": "==0.12.4" + "version": "==0.12.5" }, "typed-ast": { "hashes": [ diff --git a/riocli/apply/manifests/package-nonros-device.yaml b/riocli/apply/manifests/package-nonros-device.yaml index 23ce8332..ab63e04b 100644 --- a/riocli/apply/manifests/package-nonros-device.yaml +++ b/riocli/apply/manifests/package-nonros-device.yaml @@ -28,7 +28,7 @@ spec: periodSeconds: 10 # How often (in seconds) to perform the probe. failureThreshold: 1 # Minimum consecutive failures for the probe to be considered failed after having succeeded. successThreshold: 3 # Minimum consecutive successes for the probe to be considered successful after having failed. - timeoutSeconds: 1 # Number of seconds after which the probe times out. + timeoutSeconds: 10 # Number of seconds after which the probe times out. Minimun: 10 docker: image: "busybox:latest" diff --git a/riocli/apply/manifests/package-ros-device-no-rosbag.yaml b/riocli/apply/manifests/package-ros-device-no-rosbag.yaml index c23cd55a..f0e894e2 100644 --- a/riocli/apply/manifests/package-ros-device-no-rosbag.yaml +++ b/riocli/apply/manifests/package-ros-device-no-rosbag.yaml @@ -29,7 +29,7 @@ spec: periodSeconds: 10 # How often (in seconds) to perform the probe. failureThreshold: 1 # Minimum consecutive failures for the probe to be considered failed after having succeeded. successThreshold: 3 # Minimum consecutive successes for the probe to be considered successful after having failed. - timeoutSeconds: 1 # Number of seconds after which the probe times out. + timeoutSeconds: 10 # Number of seconds after which the probe times out. Mininum: 10 docker: image: "busybox:latest" imagePullPolicy: "Always" # Always, Never, IfNotPresent(default) diff --git a/riocli/apply/manifests/package-ros-device-rosbag.yaml b/riocli/apply/manifests/package-ros-device-rosbag.yaml index 4d6fe284..8c1eec66 100644 --- a/riocli/apply/manifests/package-ros-device-rosbag.yaml +++ b/riocli/apply/manifests/package-ros-device-rosbag.yaml @@ -29,7 +29,7 @@ spec: periodSeconds: 10 # How often (in seconds) to perform the probe. failureThreshold: 1 # Minimum consecutive failures for the probe to be considered failed after having succeeded. successThreshold: 3 # Minimum consecutive successes for the probe to be considered successful after having failed. - timeoutSeconds: 1 # Number of seconds after which the probe times out. + timeoutSeconds: 10 # Number of seconds after which the probe times out. Minimum: 10 docker: image: "busybox:latest" imagePullPolicy: "Always" # Always, Never, IfNotPresent(default) diff --git a/riocli/apply/manifests/package.yaml b/riocli/apply/manifests/package.yaml index dfebe20d..aecaf737 100644 --- a/riocli/apply/manifests/package.yaml +++ b/riocli/apply/manifests/package.yaml @@ -161,7 +161,7 @@ spec: periodSeconds: 10 # How often (in seconds) to perform the probe. failureThreshold: 1 # Minimum consecutive failures for the probe to be considered failed after having succeeded. successThreshold: 3 # Minimum consecutive successes for the probe to be considered successful after having failed. - timeoutSeconds: 1 # Number of seconds after which the probe times out. + timeoutSeconds: 10 # Number of seconds after which the probe times out. Minimum: 10 docker: image: "busybox:latest" imagePullPolicy: "Always" # Always, Never, IfNotPresent(default) @@ -357,7 +357,7 @@ spec: periodSeconds: 10 # How often (in seconds) to perform the probe. failureThreshold: 1 # Minimum consecutive failures for the probe to be considered failed after having succeeded. successThreshold: 3 # Minimum consecutive successes for the probe to be considered successful after having failed. - timeoutSeconds: 1 # Number of seconds after which the probe times out. + timeoutSeconds: 10 # Number of seconds after which the probe times out. Minimum: 10 docker: image: "busybox:latest" imagePullPolicy: "Always" # Always, Never, IfNotPresent(default) @@ -379,7 +379,7 @@ spec: periodSeconds: 10 # How often (in seconds) to perform the probe. failureThreshold: 1 # Minimum consecutive failures for the probe to be considered failed after having succeeded. successThreshold: 3 # Minimum consecutive successes for the probe to be considered successful after having failed. - timeoutSeconds: 1 # Number of seconds after which the probe times out. + timeoutSeconds: 10 # Number of seconds after which the probe times out. Minimum: 10 docker: image: "nginx:latest" imagePullPolicy: "Always" # Always, Never, IfNotPresent(default) @@ -427,7 +427,7 @@ spec: periodSeconds: 10 # How often (in seconds) to perform the probe. failureThreshold: 1 # Minimum consecutive failures for the probe to be considered failed after having succeeded. successThreshold: 3 # Minimum consecutive successes for the probe to be considered successful after having failed. - timeoutSeconds: 1 # Number of seconds after which the probe times out. + timeoutSeconds: 10 # Number of seconds after which the probe times out. Minimum: 10 docker: image: "busybox:latest" imagePullPolicy: "Always" # Always, Never, IfNotPresent(default) diff --git a/riocli/apply/resolver.py b/riocli/apply/resolver.py index 4cc3808b..46e21a0a 100644 --- a/riocli/apply/resolver.py +++ b/riocli/apply/resolver.py @@ -116,7 +116,7 @@ def find_depends(self, depends, *args): def _guid_functor(self, kind): mapping = { - 'secret': lambda x: munchify(x).metadata.name, + 'secret': lambda x: munchify(x).metadata.guid, "project": lambda x: munchify(x).metadata.guid, "package": lambda x: munchify(x)['id'], "staticroute": lambda x: munchify(x)['metadata']['guid'], diff --git a/riocli/configtree/import_keys.py b/riocli/configtree/import_keys.py index d8c89b32..612b545e 100644 --- a/riocli/configtree/import_keys.py +++ b/riocli/configtree/import_keys.py @@ -97,7 +97,8 @@ def import_keys( try: client = new_v2_client(with_project=(not with_org)) - with Revision(tree_name=tree_name, commit=commit, client=client, spinner=spinner) as rev: + with Revision(tree_name=tree_name, commit=commit, client=client, spinner=spinner, + with_org=with_org) as rev: rev_id = rev.revision_id for key, value in data.items(): diff --git a/riocli/configtree/revision.py b/riocli/configtree/revision.py index 90e7d9aa..0edbf0ad 100644 --- a/riocli/configtree/revision.py +++ b/riocli/configtree/revision.py @@ -41,7 +41,7 @@ def __init__(self, tree_name: str, commit: bool = False, force_new: bool = False, spinner: Optional[Yaspin] = None, - with_project: bool = True): + with_org: bool = True): self._tree_name = tree_name self._client = client @@ -52,7 +52,9 @@ def __init__(self, tree_name: str, self._explicit = False self._data = {} self._org_guid = self._config.organization_guid - self._project_guid = self._config.project_guid if with_project else None + self._project_guid = None + if not with_org: + self._project_guid = self._config.project_guid rev = get_revision_from_state(self._org_guid, self._project_guid, self._tree_name) @@ -188,8 +190,14 @@ def init_revision( Initialize a new revision for the Config tree """ config = get_config_from_context(ctx) - rev = get_revision_from_state(org_guid=config.organization_guid, project_guid=config.project_guid, + project_guid = None + if not with_org: + project_guid = config.project_guid + + rev = get_revision_from_state(org_guid=config.organization_guid, + project_guid=project_guid, tree_name=tree_name) + if not force and rev is not None and not rev.committed: spinner.text = click.style( 'Revision {} is already present. Subsequent commands will re-use it. \n' @@ -201,7 +209,7 @@ def init_revision( try: client = new_v2_client(with_project=(not with_org)) - Revision(tree_name=tree_name, force_new=force, spinner=spinner, client=client) + Revision(tree_name=tree_name, force_new=force, spinner=spinner, client=client, with_org=with_org) except Exception as e: spinner.text = click.style( 'Failed to initialize Config tree revision: {}'.format(e), Colors.RED) @@ -235,13 +243,15 @@ def commit_revision( """ config = get_config_from_context(ctx) - project_guid = config.project_guid - if with_org: - project_guid = None + project_guid = None + if not with_org: + project_guid = config.project_guid if not rev_id: - rev = get_revision_from_state(org_guid=config.organization_guid, project_guid=project_guid, + rev = get_revision_from_state(org_guid=config.organization_guid, + project_guid=project_guid, tree_name=tree_name) + if not rev or rev.committed: spinner.text = click.style( 'RevisionID not provided as argument and not found in the State file.', @@ -252,7 +262,8 @@ def commit_revision( try: client = new_v2_client(with_project=(not with_org)) - rev = Revision(tree_name=tree_name, rev_id=rev_id, spinner=spinner, client=client) + rev = Revision(tree_name=tree_name, rev_id=rev_id, spinner=spinner, + client=client, with_org=with_org) rev.commit(msg=message) except Exception as e: spinner.text = click.style( @@ -287,8 +298,14 @@ def put_key_in_revision( """ config = get_config_from_context(ctx) - rev = get_revision_from_state(org_guid=config.organization_guid, project_guid=config.project_guid, + project_guid = None + if not with_org: + project_guid = config.project_guid + + rev = get_revision_from_state(org_guid=config.organization_guid, + project_guid=project_guid, tree_name=tree_name) + if not rev or rev.committed: spinner.text = click.style( 'RevisionID not provided as argument and not found in the State file. \n' @@ -300,7 +317,8 @@ def put_key_in_revision( try: client = new_v2_client(with_project=(not with_org)) - with Revision(tree_name=tree_name, spinner=spinner, client=client) as rev: + with Revision(tree_name=tree_name, spinner=spinner, client=client, + with_org=with_org) as rev: rev.store(key=key, value=value) spinner.write(click.style( '\t{} Key {} added.'.format(Symbols.SUCCESS, key) @@ -338,8 +356,14 @@ def put_file_in_revision( """ config = get_config_from_context(ctx) - rev = get_revision_from_state(org_guid=config.organization_guid, project_guid=config.project_guid, + project_guid = None + if not with_org: + project_guid = config.project_guid + + rev = get_revision_from_state(org_guid=config.organization_guid, + project_guid=project_guid, tree_name=tree_name) + if not rev or rev.committed: spinner.text = click.style( 'RevisionID not provided as argument and not found in the State file. \n' @@ -351,7 +375,8 @@ def put_file_in_revision( try: client = new_v2_client(with_project=(not with_org)) - with Revision(tree_name=tree_name, spinner=spinner, client=client) as rev: + with Revision(tree_name=tree_name, spinner=spinner, client=client, + with_org=with_org) as rev: rev.store_file(key=key, file_path=file_path) spinner.write(click.style( '\t{} File {} added.'.format(Symbols.SUCCESS, key) @@ -385,10 +410,15 @@ def delete_key_in_revision( """ Delete the key in the uncommitted revision """ - config = get_config_from_context(ctx) - rev = get_revision_from_state(org_guid=config.organization_guid, project_guid=config.project_guid, + project_guid = None + if not with_org: + project_guid = config.project_guid + + rev = get_revision_from_state(org_guid=config.organization_guid, + project_guid=project_guid, tree_name=tree_name) + if not rev or rev.committed: spinner.text = click.style( 'RevisionID not provided as argument and not found in the State file. \n' @@ -400,7 +430,8 @@ def delete_key_in_revision( try: client = new_v2_client(with_project=(not with_org)) - with Revision(tree_name=tree_name, spinner=spinner, client=client) as rev: + with Revision(tree_name=tree_name, spinner=spinner, client=client, + with_org=with_org) as rev: rev.delete(key=key) spinner.write(click.style( '\t{} Key {} removed.'.format(Symbols.SUCCESS, key) @@ -434,8 +465,14 @@ def list_revision_keys( """ if not rev_id: config = get_config_from_context(ctx) - rev = get_revision_from_state(org_guid=config.organization_guid, project_guid=config.project_guid, + project_guid = None + if not with_org: + project_guid = config.project_guid + + rev = get_revision_from_state(org_guid=config.organization_guid, + project_guid=project_guid, tree_name=tree_name) + if not rev or rev.committed: click.echo( click.style( @@ -445,6 +482,8 @@ def list_revision_keys( ) raise SystemExit(1) + rev_id = rev.rev_id + try: client = new_v2_client(with_project=(not with_org)) tree = client.get_config_tree(tree_name=tree_name, rev_id=rev_id) diff --git a/riocli/configtree/tree.py b/riocli/configtree/tree.py index 72674f39..c8e0e4ea 100644 --- a/riocli/configtree/tree.py +++ b/riocli/configtree/tree.py @@ -191,10 +191,15 @@ def set_tree_revision( """ config = get_config_from_context(ctx) + project_guid = None + if not with_org: + project_guid = config.project_guid if not rev_id: - rev = get_revision_from_state(org_guid=config.organization_guid, project_guid=config.project_guid, + rev = get_revision_from_state(org_guid=config.organization_guid, + project_guid=project_guid, tree_name=tree_name) + if not rev or not rev.committed: spinner.text = click.style( 'RevisionID not provided as argument and not found in the State file.', diff --git a/riocli/device/__init__.py b/riocli/device/__init__.py index 723dcc1e..58e02149 100644 --- a/riocli/device/__init__.py +++ b/riocli/device/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021 Rapyuta Robotics +# Copyright 2024 Rapyuta Robotics # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ from riocli.device.label import device_labels from riocli.device.list import list_devices from riocli.device.metric import device_metrics +from riocli.device.migrate import migrate_project from riocli.device.onboard import device_onboard from riocli.device.tools import tools from riocli.device.topic import device_topics @@ -57,3 +58,4 @@ def device(): device.add_command(list_devices) device.add_command(tools) device.add_command(toggle_vpn) +device.add_command(migrate_project) diff --git a/riocli/device/migrate.py b/riocli/device/migrate.py new file mode 100644 index 00000000..bc09b005 --- /dev/null +++ b/riocli/device/migrate.py @@ -0,0 +1,69 @@ +# Copyright 2024 Rapyuta Robotics +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import click +from click_help_colors import HelpColorsCommand +from yaspin.core import Yaspin + +from riocli.config import new_client +from riocli.device.util import migrate_device_to_project, name_to_guid +from riocli.project.util import name_to_guid as project_name_to_guid + +from riocli.constants import Colors, Symbols +from riocli.utils.spinner import with_spinner + + +@click.command( + 'migrate', + cls=HelpColorsCommand, + help_headers_color=Colors.YELLOW, + help_options_color=Colors.GREEN +) +@click.argument('device-name', type=str) +@click.argument('project-name', type=str) +@click.option('--enable-vpn', is_flag=True, + type=click.BOOL, default=False, + help="Enable VPN after migrating to the destination project.") +@click.option('--advertise-routes', is_flag=True, + type=click.BOOL, default=False, + help="Advertise subnets configured in project to VPN peers") +@name_to_guid +@project_name_to_guid +@click.pass_context +@with_spinner(text="Migrating device...") +def migrate_project(ctx: click.Context, device_name: str, device_guid: str, + project_name: str, project_guid: str, + enable_vpn: bool, advertise_routes: bool, + spinner: Yaspin) -> None: + """ + Migrate the device from current project to the target project. + """ + try: + migrate_device_to_project(ctx, device_guid, project_guid) + spinner.write( + click.style('{} Device {} migrated successfully.'.format(Symbols.SUCCESS, device_name), + fg=Colors.GREEN)) + + if enable_vpn: + spinner.text = 'Enabling VPN on device...' + client = new_client() + client.set_project(project_guid) + client.toggle_features(device_id=device_guid, features=[('vpn', True)], + config={'vpn': {'advertise_routes': advertise_routes}}) + spinner.write(click.style('{} Enabled VPN on the device.'.format(Symbols.SUCCESS), fg=Colors.GREEN)) + except Exception as e: + spinner.text = click.style( + 'Failed to migrate device: {}'.format(e), Colors.RED) + spinner.red.fail(Symbols.ERROR) + raise SystemExit(1) from e + diff --git a/riocli/device/util.py b/riocli/device/util.py index 80df9132..28ce2b1e 100644 --- a/riocli/device/util.py +++ b/riocli/device/util.py @@ -1,4 +1,4 @@ -# Copyright 2023 Rapyuta Robotics +# Copyright 2024 Rapyuta Robotics # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,13 +15,16 @@ import re import typing from pathlib import Path +import json import click from rapyuta_io import Client from rapyuta_io.clients import LogUploads from rapyuta_io.clients.device import Device +from rapyuta_io.utils import RestClient +from rapyuta_io.utils.rest_client import HttpMethod -from riocli.config import new_client +from riocli.config import get_config_from_context, new_client from riocli.constants import Colors from riocli.utils import is_valid_uuid @@ -120,6 +123,21 @@ def fetch_devices( return result +def migrate_device_to_project(ctx: click.Context, device_id: str, dest_project_id: str) -> None: + config = get_config_from_context(ctx) + host = config.data.get('core_api_host', 'https://gaapiserver.apps.okd4v2.prod.rapyuta.io') + url = '{}/api/device-manager/v0/devices/{}/migrate'.format(host, device_id) + headers = config.get_auth_header() + payload = {'project': dest_project_id} + + response = RestClient(url).method(HttpMethod.PUT).headers(headers).execute(payload) + err_msg = 'error in the api call' + data = json.loads(response.text) + + if not response.ok: + err_msg = data.get('response', {}).get('error', '') + raise Exception(err_msg) + def find_request_id(requests: typing.List[LogUploads], file_name: str) -> (str, str): for request in requests: diff --git a/riocli/package/model.py b/riocli/package/model.py index 93ab20b7..c802ed68 100644 --- a/riocli/package/model.py +++ b/riocli/package/model.py @@ -200,13 +200,18 @@ def _map_executable(self, exec): if 'livenessProbe' in exec: exec_object.livenessProbe = exec.livenessProbe - if exec.get('runAsBash'): - if 'command' in exec: - exec_object.cmd = ['/bin/bash', '-c', exec.command] - else: - # TODO verify this is right for secret? - if 'command' in exec: - exec_object.cmd = [exec.command] + if 'command' in exec: + c = [] + + if exec.get('runAsBash'): + c = ['/bin/bash', '-c'] + + if isinstance(exec.command, list): + c.extend(exec.command) + else: + c.append(exec.command) + + exec_object.cmd = c if exec.type == 'docker': exec_object.docker = exec.docker.image diff --git a/riocli/project/__init__.py b/riocli/project/__init__.py index 5e729ce2..a45cf8f9 100644 --- a/riocli/project/__init__.py +++ b/riocli/project/__init__.py @@ -20,6 +20,7 @@ from riocli.project.inspect import inspect_project from riocli.project.list import list_projects from riocli.project.select import select_project +from riocli.project.update_owner import update_owner from riocli.project.whoami import whoami @@ -43,3 +44,4 @@ def project() -> None: project.add_command(inspect_project) project.add_command(features) project.add_command(whoami) +project.add_command(update_owner) diff --git a/riocli/project/update_owner.py b/riocli/project/update_owner.py new file mode 100644 index 00000000..2d48a128 --- /dev/null +++ b/riocli/project/update_owner.py @@ -0,0 +1,91 @@ +# Copyright 2024 Rapyuta Robotics +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import click +from click_help_colors import HelpColorsCommand +from email_validator import EmailNotValidError, validate_email + +from riocli.config import get_config_from_context +from riocli.constants import Colors, Symbols +from riocli.project.util import name_to_guid +from riocli.utils.selector import show_selection + + +@click.command( + 'update-owner', + cls=HelpColorsCommand, + help_headers_color=Colors.YELLOW, + help_options_color=Colors.GREEN, +) +@click.argument('project-name', type=str, required=True) +@click.option('--user-email', type=str, help="Email of the new owner") +@name_to_guid +@click.pass_context +def update_owner( + ctx: click.Context, + project_name: str, + project_guid: str, + user_email: str, +) -> None: + """ + Update the owner of the project. + + The command will show an interactive list of users in the project if + you do not specify --user-email. You can select the new owner from the list. + """ + config = get_config_from_context(ctx) + client = config.new_v2_client(with_project=False) + + try: + project = client.get_project(project_guid) + except Exception as e: + click.secho('{} Failed to fetch project: {}'.format(Symbols.ERROR, e), fg=Colors.RED) + raise SystemExit(1) + + project_users = project.spec.users + + user_guid = None + + if user_email: + try: + validate_email(user_email) + except EmailNotValidError as e: + click.secho('{} {} is not a valid email address'.format(Symbols.ERROR, user_email), fg=Colors.RED) + raise SystemExit(1) from e + + for u in project_users: + if u['emailID'] == user_email: + user_guid = u['userGUID'] + break + else: + ranger = {u['userGUID']: '{} {} ({})'.format(u['firstName'], u['lastName'], u['emailID']) + for u in project_users} + user_guid = show_selection( + ranger, + header='Select a new project owner:', + prompt='Select', + show_keys=False, + highlight_item=project.metadata.creatorGUID, + ) + + if user_guid is None: + click.secho('{} User not found in project'.format(Symbols.ERROR), fg=Colors.RED) + raise SystemExit(1) + + try: + client.update_project_owner(project_guid, user_guid) + click.secho('{} Owner updated successfully'.format(Symbols.SUCCESS), fg=Colors.GREEN) + except Exception as e: + click.secho('{} Failed to update owner: {}'.format(Symbols.ERROR, e), fg=Colors.RED) + raise SystemExit(1) diff --git a/riocli/secret/list.py b/riocli/secret/list.py index e090ec53..4e312a68 100644 --- a/riocli/secret/list.py +++ b/riocli/secret/list.py @@ -34,7 +34,7 @@ def list_secrets() -> None: try: client = new_v2_client(with_project=True) secrets = client.list_secrets() - secrets = sorted(secrets, key=lambda s: s.name.lower()) + secrets = sorted(secrets, key=lambda s: s.metadata.name.lower()) _display_secret_list(secrets, show_header=True) except Exception as e: click.secho(str(e), fg=Colors.RED) @@ -49,7 +49,7 @@ def _display_secret_list( if show_header: headers = ('ID', 'Name', 'Created At', 'Creator') - data = [ [secret.guid, secret.name, - secret.createdAt, secret.creatorGUID] for secret in secrets ] + data = [ [secret.metadata.guid, secret.metadata.name, + secret.metadata.createdAt, secret.metadata.creatorGUID] for secret in secrets ] tabulate_data(data, headers) diff --git a/riocli/utils/selector.py b/riocli/utils/selector.py index 942638ab..fbf1bb9b 100644 --- a/riocli/utils/selector.py +++ b/riocli/utils/selector.py @@ -1,4 +1,4 @@ -# Copyright 2021 Rapyuta Robotics +# Copyright 2024 Rapyuta Robotics # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,36 +11,84 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from typing import Any from typing import Union import click from click import types +from riocli.constants import Colors -def show_selection(ranger: Union[list, dict], header: str = '', prompt: str = 'Select the option'): + +def show_selection( + ranger: Union[list, dict], + header: str = '', + prompt: str = 'Select the option', + show_keys: bool = True, + highlight_item: str = None, +) -> Any: + """ + Show a selection prompt to the user. + + :param ranger: List or dictionary of options + :param header: Header to show before the options + :param prompt: Prompt to show after the options + :param show_keys: Show keys in the dictionary (not applicable for lists) + :param highlight_item: Highlight the selected item in the list (key in case of dict) + + :return: Selected option + """ if isinstance(ranger, dict): - return _show_selection_dict(ranger, header, prompt) - elif isinstance(ranger, list): - return _show_selection_list(ranger, header, prompt) + return _show_selection_dict(ranger, header, prompt, show_keys, highlight_item) + if isinstance(ranger, list): + return _show_selection_list(ranger, header, prompt, highlight_item) + + +def _show_selection_list( + ranger: list, + header: str, + prompt: str, + highlight_item: Any = None, +) -> Any: + click.secho(header, fg=Colors.YELLOW) -def _show_selection_list(ranger: list, header: str, prompt: str): - fmt = header for idx, opt in enumerate(ranger): - fmt = '{}\n{}) {}'.format(fmt, idx + 1, opt) + fmt = '{}) {}'.format(idx + 1, opt) + if highlight_item is not None and opt == highlight_item: + fmt = click.style(fmt, bold=True, italic=True) + + click.secho(fmt) + + prompt = click.style(prompt, fg=Colors.BLUE) + choice = click.prompt(prompt, type=types.IntParamType()) - fmt = '{}\n{}'.format(fmt, prompt) - choice = click.prompt(fmt, type=types.IntParamType()) return ranger[choice - 1] -def _show_selection_dict(ranger: dict, header: str, prompt: str): - options = [] - fmt = click.style(header, fg='yellow') +def _show_selection_dict( + ranger: dict, + header: str, + prompt: str, + show_keys: bool = True, + highlight_item: Any = None, +) -> Any: + click.secho(header, fg=Colors.YELLOW) + for idx, key in enumerate(ranger): - options.append(key) - fmt = '{}\n{}) {} - {}'.format(fmt, idx + 1, key, ranger[key]) + if show_keys: + fmt = '{}) {} - {}'.format(idx + 1, key, ranger[key]) + else: + fmt = '{}) {}'.format(idx + 1, ranger[key]) + + if highlight_item is not None and key == highlight_item: + fmt = click.style(fmt, bold=True, italic=True) + + click.secho(fmt) + + prompt = click.style(prompt, fg=Colors.BLUE) + choice = click.prompt(prompt, type=types.IntParamType()) + + options = list(ranger.keys()) - fmt = '{}\n{}'.format(fmt, click.style(prompt, fg='blue')) - choice = click.prompt(fmt, type=types.IntParamType()) return options[choice - 1] diff --git a/riocli/v2client/client.py b/riocli/v2client/client.py index be92a81b..21664cd0 100644 --- a/riocli/v2client/client.py +++ b/riocli/v2client/client.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations -import os import http import json -import magic -from typing import List, Optional, Dict, Any +import os from hashlib import md5 +from typing import List, Optional, Dict, Any +import magic import requests from munch import munchify, Munch from rapyuta_io.utils.rest_client import HttpMethod, RestClient @@ -150,6 +150,24 @@ def update_project(self, project_guid: str, spec: dict) -> Munch: return munchify(data) + def update_project_owner(self, project_guid: str, new_owner_guid: str) -> Munch: + """ + Update an existing project's owner (creator) + """ + url = "{}/v2/projects/{}/owner/".format(self._host, project_guid) + headers = self._get_auth_header(with_project=False) + response = RestClient(url).method(HttpMethod.PUT).headers( + headers).execute(payload={'metadata': {'creatorGUID': new_owner_guid}}) + + handle_server_errors(response) + + data = json.loads(response.text) + if not response.ok: + err_msg = data.get('error') + raise Exception("projects: {}".format(err_msg)) + + return munchify(data) + def delete_project(self, project_guid: str) -> Munch: """ Delete a project by its GUID @@ -256,7 +274,6 @@ def list_instance_bindings(self, instance_name: str, labels: str = '') -> List: client = RestClient(url).method(HttpMethod.GET).headers(headers) return self._walk_pages(client, params={'labelSelector': labels}) - def create_instance_binding(self, instance_name, binding: dict) -> Munch: """ Create a new managed service instance binding @@ -523,8 +540,8 @@ def delete_config_tree(self, tree_name: str) -> Munch: return munchify(data) def get_config_tree(self, tree_name: str, rev_id: Optional[str] = None, - include_data: bool = False, filter_content_types: Optional[List[str]] = None, - filter_prefixes: Optional[List[str]] = None) -> Munch: + include_data: bool = False, filter_content_types: Optional[List[str]] = None, + filter_prefixes: Optional[List[str]] = None) -> Munch: url = "{}/v2/configtrees/{}/".format(self._host, tree_name) query = { 'includeData': include_data, @@ -606,7 +623,6 @@ def store_keys_in_revision(self, tree_name: str, rev_id: str, payload: Any) -> M return munchify(data) - def store_key_in_revision(self, tree_name: str, rev_id: str, key: str, value: str, perms: int = 644) -> Munch: url = "{}/v2/configtrees/{}/revisions/{}/{}".format(self._host, tree_name, rev_id, key) headers = self._get_auth_header(with_org=True) diff --git a/setup.py b/setup.py index e2d70773..80cfd37f 100644 --- a/setup.py +++ b/setup.py @@ -15,6 +15,7 @@ setup( name="rapyuta-io-cli", + python_requires=">=3.8", packages=find_packages(), package_data={ 'riocli': [ @@ -31,6 +32,13 @@ author="Rapyuta Robotics", author_email="opensource@rapyuta-robotics.com", url="http://docs.rapyuta.io", + classifiers=[ + "Programming Language :: Python", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + ], install_requires=[ "pretty-traceback>=2022.1018", "argparse>=1.4.0", @@ -47,7 +55,7 @@ "python-dateutil>=2.8.2", "pytz", "pyyaml>=5.4.1", - "rapyuta-io>=1.15.0", + "rapyuta-io>=1.15.1", "requests>=2.20.0", "setuptools", "six>=1.13.0",