Compare commits

...

3 Commits

Author SHA1 Message Date
Administrator 6eaa839d51 Upgrade ruff and typer
ci/woodpecker/push/woodpecker Pipeline failed Details
Also update integration test to use Debian 12 instead of Debian 11.
2024-03-25 20:10:24 +00:00
Administrator 6cad92952a v11.1.0: Automatic per-token SSH key support
ci/woodpecker/push/woodpecker Pipeline failed Details
ci/woodpecker/tag/woodpecker Pipeline failed Details
2024-03-16 01:06:05 +00:00
Administrator 6bc5791980 v11.0.1: Minor bug fixes
ci/woodpecker/push/woodpecker Pipeline failed Details
ci/woodpecker/tag/woodpecker Pipeline failed Details
2024-02-29 04:31:06 +00:00
9 changed files with 353 additions and 206 deletions

View File

@ -13,6 +13,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Nothing yet.
## [11.1.0 - 2024-03-16]
## Added
### Library
- `ssh_key` to `client.Client()` and to `client.Token()`. This acts as a default SSH key when launching servers this way.
### CLI
- Support for automatic per-token SSH keys (can be overridden with `--ssh-key-file` still.) To generate, run: `ssh-keygen -C "" -t ed25519 -f ~/.sporestack/sshkey/{token}/id_ed25519`
- This means that you don't have to pass `--ssh-key-file` if you are using a token that has a locally associated SSH key.
- When launching a server with `sporestack server launch`, it will suggest adding a readymade configuration to `~/.ssh/config` to utilize whatever key you selected.
## Summary
These changes should make it easier to stay private with SporeStack, conveniently, by utilizing a SSH key per token. In general, we recommend using one unique SSH key per token that you have.
## [11.0.1 - 2024-02-29]
## Fixed
- If a server is deleted during the launch wait phase, it will give up rather than trying to wait forever for an IP address that will never come.
- `--hostname` matching is smarter in case of duplicate hostnames.
## [11.0.0 - 2024-02-26]
## Changed

View File

@ -1,10 +1,10 @@
format:
black .
ruff --fix .
ruff check --fix .
test:
black --check .
ruff .
ruff check .
$(MAKE) test-typing
$(MAKE) test-pytest

View File

@ -13,7 +13,7 @@ pytest = "~=8.0"
pytest-cov = "~=4.0"
pytest-mock = "~=3.6"
pytest-socket = "~=0.7.0"
ruff = "~=0.2.2"
ruff = "~=0.3.4"
respx = "~=0.20.1"

332
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "d7224b7608b769ca806e6c2505d458ebab1e7891330334083a1b74136cf9de16"
"sha256": "15a7310843309e669fad4d9f9e835c3d1922cb21c237a1d43d328c680a178d01"
},
"pipfile-spec": 6,
"requires": {},
@ -75,11 +75,11 @@
},
"pydantic": {
"hashes": [
"sha256:37a5432e54b12fecaa1049c5195f3d860a10e01bdfd24f1840ef14bd0d3aeab3",
"sha256:a09be1c3d28f3abe37f8a78af58284b236a92ce520105ddc91a6d29ea1176ba7"
"sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6",
"sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"
],
"markers": "python_version >= '3.8'",
"version": "==2.6.2"
"version": "==2.6.4"
},
"pydantic-core": {
"hashes": [
@ -183,7 +183,7 @@
},
"sporestack": {
"editable": true,
"markers": "python_version ~= '3.7'",
"markers": "python_version ~= '3.8'",
"path": "."
},
"typing-extensions": {
@ -215,41 +215,41 @@
},
"black": {
"hashes": [
"sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8",
"sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8",
"sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd",
"sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9",
"sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31",
"sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92",
"sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f",
"sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29",
"sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4",
"sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693",
"sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218",
"sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a",
"sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23",
"sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0",
"sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982",
"sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894",
"sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540",
"sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430",
"sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b",
"sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2",
"sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6",
"sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"
"sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f",
"sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93",
"sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11",
"sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0",
"sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9",
"sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5",
"sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213",
"sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d",
"sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7",
"sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837",
"sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f",
"sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395",
"sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995",
"sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f",
"sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597",
"sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959",
"sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5",
"sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb",
"sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4",
"sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7",
"sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd",
"sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==24.2.0"
"version": "==24.3.0"
},
"build": {
"hashes": [
"sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b",
"sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f"
"sha256:8ed0851ee76e6e38adce47e4bee3b51c771d86c64cf578d0c2245567ee200e73",
"sha256:8eea65bb45b1aac2e734ba2cc8dad3a6d97d97901a395bd0ed3e7b46953d2a31"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==1.0.3"
"version": "==1.1.1"
},
"certifi": {
"hashes": [
@ -368,61 +368,61 @@
"toml"
],
"hashes": [
"sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa",
"sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003",
"sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f",
"sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c",
"sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e",
"sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0",
"sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9",
"sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52",
"sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e",
"sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454",
"sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0",
"sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079",
"sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352",
"sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f",
"sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30",
"sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe",
"sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113",
"sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765",
"sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc",
"sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e",
"sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501",
"sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7",
"sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2",
"sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f",
"sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4",
"sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524",
"sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c",
"sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51",
"sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840",
"sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6",
"sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee",
"sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e",
"sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45",
"sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba",
"sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d",
"sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3",
"sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10",
"sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e",
"sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb",
"sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9",
"sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a",
"sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47",
"sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1",
"sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3",
"sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914",
"sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328",
"sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6",
"sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d",
"sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0",
"sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94",
"sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc",
"sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"
"sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c",
"sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63",
"sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7",
"sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f",
"sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8",
"sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf",
"sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0",
"sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384",
"sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76",
"sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7",
"sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d",
"sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70",
"sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f",
"sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818",
"sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b",
"sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d",
"sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec",
"sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083",
"sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2",
"sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9",
"sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd",
"sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade",
"sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e",
"sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a",
"sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227",
"sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87",
"sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c",
"sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e",
"sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c",
"sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e",
"sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd",
"sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec",
"sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562",
"sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8",
"sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677",
"sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357",
"sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c",
"sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd",
"sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49",
"sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286",
"sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1",
"sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf",
"sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51",
"sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409",
"sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384",
"sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e",
"sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978",
"sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57",
"sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e",
"sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2",
"sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48",
"sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"
],
"markers": "python_version >= '3.8'",
"version": "==7.4.3"
"version": "==7.4.4"
},
"docutils": {
"hashes": [
@ -486,11 +486,11 @@
},
"importlib-metadata": {
"hashes": [
"sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e",
"sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"
"sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570",
"sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"
],
"markers": "python_version >= '3.8'",
"version": "==7.0.1"
"version": "==7.1.0"
},
"iniconfig": {
"hashes": [
@ -508,6 +508,22 @@
"markers": "python_version >= '3.8'",
"version": "==3.3.1"
},
"jaraco.context": {
"hashes": [
"sha256:4dad2404540b936a20acedec53355bdaea223acb88fd329fa6de9261c941566e",
"sha256:5d9e95ca0faa78943ed66f6bc658dd637430f16125d86988e77844c741ff2f11"
],
"markers": "python_version >= '3.7'",
"version": "==4.3.0"
},
"jaraco.functools": {
"hashes": [
"sha256:c279cb24c93d694ef7270f970d499cab4d3813f4e08273f95398651a634f0925",
"sha256:daf276ddf234bea897ef14f43c4e1bf9eefeac7b7a82a4dd69228ac20acff68d"
],
"markers": "python_version >= '3.8'",
"version": "==4.0.0"
},
"jinja2": {
"hashes": [
"sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa",
@ -518,11 +534,11 @@
},
"keyring": {
"hashes": [
"sha256:4446d35d636e6a10b8bce7caa66913dd9eca5fd222ca03a3d42c38608ac30836",
"sha256:e730ecffd309658a08ee82535a3b5ec4b4c8669a9be11efb66249d8e0aeb9a25"
"sha256:9a15cd280338920388e8c1787cb8792b9755dabb3e7c61af5ac1f8cd437cefde",
"sha256:fc024ed53c7ea090e30723e6bd82f58a39dc25d9a6797d866203ecd0ee6306cb"
],
"markers": "python_version >= '3.8'",
"version": "==24.3.0"
"version": "==25.0.0"
},
"markdown-it-py": {
"hashes": [
@ -616,37 +632,37 @@
},
"mypy": {
"hashes": [
"sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6",
"sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d",
"sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02",
"sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d",
"sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3",
"sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3",
"sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3",
"sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66",
"sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259",
"sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835",
"sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd",
"sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d",
"sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8",
"sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07",
"sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b",
"sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e",
"sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6",
"sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae",
"sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9",
"sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d",
"sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a",
"sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592",
"sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218",
"sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817",
"sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4",
"sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410",
"sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"
"sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6",
"sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913",
"sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129",
"sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc",
"sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974",
"sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374",
"sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150",
"sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03",
"sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9",
"sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02",
"sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89",
"sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2",
"sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d",
"sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3",
"sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612",
"sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e",
"sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3",
"sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e",
"sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd",
"sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04",
"sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed",
"sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185",
"sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf",
"sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b",
"sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4",
"sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f",
"sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==1.8.0"
"version": "==1.9.0"
},
"mypy-extensions": {
"hashes": [
@ -679,11 +695,11 @@
},
"packaging": {
"hashes": [
"sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
"sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"
"sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5",
"sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"
],
"markers": "python_version >= '3.7'",
"version": "==23.2"
"version": "==24.0"
},
"pathspec": {
"hashes": [
@ -704,11 +720,11 @@
},
"pkginfo": {
"hashes": [
"sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546",
"sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046"
"sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297",
"sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097"
],
"markers": "python_version >= '3.6'",
"version": "==1.9.6"
"version": "==1.10.0"
},
"platformdirs": {
"hashes": [
@ -744,12 +760,12 @@
},
"pytest": {
"hashes": [
"sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd",
"sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"
"sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7",
"sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==8.0.2"
"version": "==8.1.1"
},
"pytest-cov": {
"hashes": [
@ -762,12 +778,12 @@
},
"pytest-mock": {
"hashes": [
"sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f",
"sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"
"sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f",
"sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"
],
"index": "pypi",
"markers": "python_version >= '3.8'",
"version": "==3.12.0"
"version": "==3.14.0"
},
"pytest-socket": {
"hashes": [
@ -821,35 +837,35 @@
},
"rich": {
"hashes": [
"sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa",
"sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"
"sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222",
"sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"
],
"markers": "python_full_version >= '3.7.0'",
"version": "==13.7.0"
"version": "==13.7.1"
},
"ruff": {
"hashes": [
"sha256:0a9efb032855ffb3c21f6405751d5e147b0c6b631e3ca3f6b20f917572b97eb6",
"sha256:0c126da55c38dd917621552ab430213bdb3273bb10ddb67bc4b761989210eb6e",
"sha256:1695700d1e25a99d28f7a1636d85bafcc5030bba9d0578c0781ba1790dbcf51c",
"sha256:1ec49be4fe6ddac0503833f3ed8930528e26d1e60ad35c2446da372d16651ce9",
"sha256:3b65494f7e4bed2e74110dac1f0d17dc8e1f42faaa784e7c58a98e335ec83d7e",
"sha256:5e1439c8f407e4f356470e54cdecdca1bd5439a0673792dbe34a2b0a551a2fe3",
"sha256:5e22676a5b875bd72acd3d11d5fa9075d3a5f53b877fe7b4793e4673499318ba",
"sha256:6a61ea0ff048e06de273b2e45bd72629f470f5da8f71daf09fe481278b175001",
"sha256:940de32dc8853eba0f67f7198b3e79bc6ba95c2edbfdfac2144c8235114d6726",
"sha256:b0c232af3d0bd8f521806223723456ffebf8e323bd1e4e82b0befb20ba18388e",
"sha256:c9d15fc41e6054bfc7200478720570078f0b41c9ae4f010bcc16bd6f4d1aacdd",
"sha256:cc9a91ae137d687f43a44c900e5d95e9617cb37d4c989e462980ba27039d239d",
"sha256:d450b7fbff85913f866a5384d8912710936e2b96da74541c82c1b458472ddb39",
"sha256:d920499b576f6c68295bc04e7b17b6544d9d05f196bb3aac4358792ef6f34325",
"sha256:e62ed7f36b3068a30ba39193a14274cd706bc486fad521276458022f7bccb31d",
"sha256:ecd46e3106850a5c26aee114e562c329f9a1fbe9e4821b008c4404f64ff9ce73",
"sha256:f63d96494eeec2fc70d909393bcd76c69f35334cdbd9e20d089fb3f0640216ca"
"sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365",
"sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488",
"sha256:60c870a7d46efcbc8385d27ec07fe534ac32f3b251e4fc44b3cbfd9e09609ef4",
"sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9",
"sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232",
"sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91",
"sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369",
"sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed",
"sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102",
"sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e",
"sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c",
"sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7",
"sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378",
"sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854",
"sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6",
"sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50",
"sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==0.2.2"
"version": "==0.3.4"
},
"sniffio": {
"hashes": [
@ -894,20 +910,20 @@
},
"wheel": {
"hashes": [
"sha256:177f9c9b0d45c47873b619f5b650346d632cdc35fb5e4d25058e09c9e581433d",
"sha256:c45be39f7882c9d34243236f2d63cbd58039e360f85d0913425fbd7ceea617a8"
"sha256:465ef92c69fa5c5da2d1cf8ac40559a8c940886afcef87dcf14b9470862f1d85",
"sha256:55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81"
],
"index": "pypi",
"markers": "python_version >= '3.7'",
"version": "==0.42.0"
"markers": "python_version >= '3.8'",
"version": "==0.43.0"
},
"zipp": {
"hashes": [
"sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31",
"sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"
"sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b",
"sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"
],
"markers": "python_version >= '3.8'",
"version": "==3.17.0"
"version": "==3.18.1"
}
}
}

View File

@ -25,14 +25,14 @@ sporestack token list
sporestack token import importediminvalid --key "imaninvalidkey"
sporestack token list | grep importediminvalid
sporestack token list | grep imaninvalidkey
sporestack server launch --no-quote --token neverbeencreated --operating-system debian-11 --days 1 2>&1 | grep 'does not exist'
sporestack server launch --no-quote --token neverbeencreated --operating-system debian-12 --days 1 2>&1 | grep 'does not exist'
# Online tests start here.
sporestack server launch --no-quote --token importediminvalid --operating-system debian-11 --days 1 2>&1 | grep 'String should have at least'
sporestack server launch --no-quote --token importediminvalid --operating-system debian-12 --days 1 2>&1 | grep 'String should have at least'
sporestack server flavors | grep vcpu
sporestack server operating-systems | grep debian-11
sporestack server operating-systems | grep debian-12
sporestack server regions | grep sfo3
sporestack api-changelog
@ -54,7 +54,7 @@ sporestack token invoices realtestingtoken
sporestack token topup realtestingtoken --currency xmr --dollars 26 --no-wait
sporestack server list --token realtestingtoken
sporestack server launch --no-quote --token realtestingtoken --operating-system debian-11 --days 1 --hostname sporestackpythonintegrationtestdelme
sporestack server launch --no-quote --token realtestingtoken --operating-system debian-12 --days 1 --hostname sporestackpythonintegrationtestdelme
sporestack server list --token realtestingtoken | grep sporestackpythonintegrationtestdelme
sporestack server topup --token realtestingtoken --hostname sporestackpythonintegrationtestdelme --days 1
sporestack server info --token realtestingtoken --hostname sporestackpythonintegrationtestdelme

View File

@ -2,4 +2,4 @@
__all__ = ["api", "api_client", "client", "exceptions"]
__version__ = "11.0.0"
__version__ = "11.1.0"

View File

@ -12,6 +12,7 @@ from .models import Currency, Invoice, ServerUpdateRequest, TokenInfo
log = logging.getLogger(__name__)
LATEST_API_VERSION = 2
"""This is probably not used anymore."""
CLEARNET_ENDPOINT = "https://api.sporestack.com"
TOR_ENDPOINT = (

View File

@ -8,7 +8,7 @@ import os
import sys
import time
from pathlib import Path
from typing import TYPE_CHECKING, Any, Dict, Optional
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
import typer
@ -54,6 +54,7 @@ cli.add_typer(token_cli, name="token")
server_cli = typer.Typer(help="Commands to interact with SporeStack servers.")
cli.add_typer(server_cli, name="server")
log = logging.getLogger(__name__)
_log_level = os.getenv("LOG_LEVEL", "warning").upper()
_numeric_log_level = getattr(logging, _log_level, None)
if _numeric_log_level is None:
@ -65,6 +66,8 @@ DEFAULT_TOKEN = "primary"
DEFAULT_FLAVOR = "vps-1vcpu-1gb"
# Users may have a different key file, but this is the most common.
DEFAULT_SSH_KEY_FILE = HOME / ".ssh" / "id_rsa.pub"
DEFAULT_TOKEN_SSH_KEY_PRIVATE = Path("id_ed25519")
DEFAULT_TOKEN_SSH_KEY_PUBLIC = DEFAULT_TOKEN_SSH_KEY_PRIVATE.with_suffix(".pub")
# On disk format
TOKEN_VERSION = 1
@ -94,6 +97,27 @@ def invoice_qr(invoice: "Invoice") -> None:
qr.terminal()
def normalize_ssh_key_file(ssh_key_file: Union[Path, None], token: str) -> Path:
if ssh_key_file is None:
token_specific_path = ssh_key_path(token)
token_specific_key = token_specific_path / DEFAULT_TOKEN_SSH_KEY_PUBLIC
if token_specific_key.exists():
ssh_key_file = token_specific_key
elif DEFAULT_SSH_KEY_FILE.exists():
ssh_key_file = DEFAULT_SSH_KEY_FILE
if ssh_key_file is None:
typer.echo(
"No SSH key specified with --ssh-key-file, nor was "
f"{token_specific_key} or {DEFAULT_SSH_KEY_FILE} found.",
err=True,
)
typer.echo("You can generate a SSH key with `ssh-key-gen`", err=True)
raise typer.Exit(code=1)
return ssh_key_file
@server_cli.command()
def launch(
days: Annotated[
@ -120,9 +144,24 @@ def launch(
],
hostname: Annotated[
str,
typer.Option(help="Give the server a hostname to help remember what it's for."),
typer.Option(
help=(
"Give the server a hostname to help remember what it's for. "
"(Note: This is visible to us.)"
)
),
] = "",
ssh_key_file: Path = DEFAULT_SSH_KEY_FILE,
ssh_key_file: Annotated[
Union[Path, None],
typer.Option(
help=(
"SSH key that the new server will allow to login as root. Defaults "
"to the token-specific SSH key, or ~/.ssh/id_rsa.pub if the former "
"was not found."
),
show_default=False,
),
] = None,
flavor: Annotated[
str, typer.Option(help="Run `sporestack server flavors` to see more options.")
] = DEFAULT_FLAVOR,
@ -151,21 +190,16 @@ def launch(
typer.echo(f"Launching server with token {token}...", err=True)
_token = load_token(token)
from . import utils
ssh_key_file = normalize_ssh_key_file(ssh_key_file=ssh_key_file, token=token)
typer.echo(f"Using SSH key: {ssh_key_file}")
from .client import Client
client = Client(api_client=get_api_client(), client_token=_token)
typer.echo(f"Loading SSH key from {ssh_key_file}...")
if not ssh_key_file.exists():
msg = f"{ssh_key_file} does not exist. "
msg += "You can try generating a key file with `ssh-keygen`"
typer.echo(msg, err=True)
raise typer.Exit(code=1)
ssh_key = ssh_key_file.read_text()
machine_id = utils.random_machine_id()
client = Client(
api_client=get_api_client(),
client_token=_token,
ssh_key=ssh_key_file.read_text(),
)
if quote:
quote_response = client.server_quote(days=days, flavor=flavor)
@ -185,20 +219,23 @@ def launch(
)
server = client.token.launch_server(
machine_id=machine_id,
days=days,
flavor=flavor,
operating_system=operating_system,
ssh_key=ssh_key,
region=region,
hostname=hostname,
autorenew=autorenew,
)
if wait:
tries = 360
tries = 60
while tries > 0:
response = server.info()
if response.deleted_at > 0:
typer.echo(
"Server creation failed, was deleted while waiting.", err=True
)
raise typer.Exit(code=1)
if response.ipv4 != "":
break
typer.echo("Waiting for server to build...", err=True)
@ -211,9 +248,44 @@ def launch(
raise typer.Exit(code=1)
else:
print_machine_info(response)
return
print_machine_info(server.info())
if not wait:
print_machine_info(server.info())
return
typer.echo("Consider adding the following to ~/.ssh/config...")
config = (
"\nHost {host}\n"
"\tHostname {hostname}\n"
f"\tIdentityFile {str(ssh_key_file).strip('.pub')}\n"
"\tUser root\n"
"\t # Remove this comment if you wish to connect via Tor. "
"ProxyCommand nc -x localhost:9050 %h %p\n"
)
typer.echo("If you wish to connect with IPv4:")
typer.echo(
config.format(
host=hostname if hostname != "" else response.ipv4, hostname=response.ipv4
)
)
typer.echo("Or if you wish to connect with IPv6:")
typer.echo(
config.format(
host=hostname if hostname != "" else response.ipv6, hostname=response.ipv6
)
)
msg = (
"If you've done that, you should be able to run `ssh {host}` "
"to connect to the server."
)
if hostname != "":
typer.echo(msg.format(host=hostname))
else:
typer.echo(msg.format(host=f"({response.ipv4} or {response.ipv6})"))
@server_cli.command()
@ -246,20 +318,6 @@ def server_info_path() -> Path:
# Put servers in a subdirectory
servers_dir = SPORESTACK_DIR / "servers"
# Migrate existing server.json files into servers subdirectory
if (
SPORESTACK_DIR.exists()
and not servers_dir.exists()
and len(list(SPORESTACK_DIR.glob("*.json"))) > 0
):
typer.echo(
f"Migrating server profiles found in {SPORESTACK_DIR} to {servers_dir}.",
err=True,
)
servers_dir.mkdir()
for json_file in SPORESTACK_DIR.glob("*.json"):
json_file.rename(servers_dir / json_file.name)
# Make it, if it doesn't exist already.
SPORESTACK_DIR.mkdir(exist_ok=True)
servers_dir.mkdir(exist_ok=True)
@ -276,6 +334,15 @@ def token_path() -> Path:
return token_dir
def ssh_key_path(token: str) -> Path:
ssh_key_dir = SPORESTACK_DIR / "sshkey" / token
# Make it, if it doesn't exist already.
ssh_key_dir.mkdir(exist_ok=True, parents=True)
return ssh_key_dir
def get_machine_info(hostname: str) -> Dict[str, Any]:
"""
Get info from disk.
@ -523,11 +590,29 @@ def _get_machine_id(machine_id: str, hostname: str, token: str) -> str:
api_client = APIClient(api_endpoint=get_api_endpoint())
candidates = []
for server in api_client.servers_launched_from_token(token=_token).servers:
if server.forgotten_at is not None:
continue
if server.hostname == hostname:
return server.machine_id
candidates.append(server)
if len(candidates) == 1:
return candidates[0].machine_id
remaining_candidates = []
for candidate in candidates:
if candidate.deleted_at == 0:
remaining_candidates.append(candidate)
if len(remaining_candidates) == 1:
return remaining_candidates[0].machine_id
elif len(remaining_candidates) > 1:
typer.echo(
"Too many servers match that hostname. Please use --machine-id, instead.",
err=True,
)
raise typer.Exit(code=1)
typer.echo(
f"Could not find any servers matching the hostname: {hostname}", err=True
@ -825,6 +910,15 @@ def token_create(
)
typer.echo(f"{token}'s key is {_token}.")
typer.echo("Save it, don't share it, and don't lose it!")
typer.echo()
typer.echo("Optional: Make a SSH key just for this token.")
token_ssh_key_path = ssh_key_path(token) / DEFAULT_TOKEN_SSH_KEY_PRIVATE
typer.echo(f'Run: ssh-keygen -C "" -t ed25519 -f "{token_ssh_key_path}"')
typer.echo(
"If you do this, servers launched from that token will default to use "
"that key and you won't have to pass --ssh-key-file every time you "
"launch a server!"
)
@token_cli.command(name="import")
@ -970,7 +1064,7 @@ def token_info(token: Annotated[str, typer.Argument()] = DEFAULT_TOKEN) -> None:
@token_cli.command()
def servers(token: Annotated[str, typer.Argument()] = DEFAULT_TOKEN) -> None:
"""Returns server info for servers launched by a given token."""
"""Use sporestack server list --token TOKEN instead!"""
_token = load_token(token)
from .api_client import APIClient

View File

@ -69,9 +69,11 @@ class Server:
class Token:
token: str = field(default_factory=random_token)
api_client: APIClient = field(default_factory=APIClient)
ssh_key: Union[str, None] = None
"""SSH public key for launching new servers with."""
def add(self, dollars: int, currency: Currency) -> Invoice:
"""Add to token"""
"""Fund the token."""
response = self.api_client.token_add(
token=self.token,
dollars=dollars,
@ -119,15 +121,20 @@ class Token:
def launch_server(
self,
ssh_key: str,
flavor: str,
days: int,
operating_system: str,
ssh_key: Union[str, None] = None,
region: Union[str, None] = None,
hostname: str = "",
autorenew: bool = False,
machine_id: str = random_machine_id(),
) -> Server:
if ssh_key is None:
if self.ssh_key is not None:
ssh_key = self.ssh_key
else:
raise ValueError("ssh_key must be set in Client() or launch_server().")
self.api_client.server_launch(
machine_id=machine_id,
days=days,
@ -150,6 +157,8 @@ class Client:
"""Token to manage/pay for servers with."""
api_client: APIClient = field(default_factory=APIClient)
"""Your own API Client, perhaps if you want to connect through Tor."""
ssh_key: Union[str, None] = None
"""SSH public key for launching new servers with."""
def flavors(self) -> api.Flavors.Response:
"""Returns available flavors (server sizes)."""
@ -174,4 +183,6 @@ class Client:
@property
def token(self) -> Token:
"""Returns a Token object with the api_client and token specified."""
return Token(token=self.client_token, api_client=self.api_client)
return Token(
token=self.client_token, api_client=self.api_client, ssh_key=self.ssh_key
)