From 7b05493e4e55c89ccbe6ba378ea705e5ee53a09b Mon Sep 17 00:00:00 2001 From: Mohammad Alisafaee Date: Tue, 7 Feb 2023 23:26:26 +0100 Subject: [PATCH] fix(cli): various renku session fixes (#3292) * fix(cli): various renku session fixes * remove anonymous sessions --- .github/workflows/test_deploy.yml | 21 +- poetry.lock | 185 ++++++++--------- renku/core/constant.py | 17 ++ renku/core/dataset/providers/api.py | 17 +- renku/core/errors.py | 4 + renku/core/session/docker.py | 186 ++++++++++-------- renku/core/session/renkulab.py | 148 +++++--------- renku/core/session/session.py | 48 +++-- renku/core/util/jwt.py | 35 ++++ renku/domain_model/session.py | 15 +- renku/ui/cli/init.py | 24 +-- renku/ui/cli/session.py | 34 ++-- renku/ui/cli/utils/color.py | 2 +- renku/version.py | 2 +- tests/cli/test_init.py | 4 +- tests/cli/test_migrate.py | 2 +- tests/cli/test_session.py | 5 +- tests/cli/test_template.py | 51 +++-- tests/core/commands/test_merge.py | 18 +- tests/core/plugins/test_session.py | 16 +- tests/fixtures/repository.py | 2 +- tests/fixtures/session.py | 11 +- tests/fixtures/templates.py | 4 +- tests/service/fixtures/service_integration.py | 2 +- 24 files changed, 453 insertions(+), 400 deletions(-) create mode 100644 renku/core/util/jwt.py diff --git a/.github/workflows/test_deploy.yml b/.github/workflows/test_deploy.yml index 351b297017..531ed7a6dc 100644 --- a/.github/workflows/test_deploy.yml +++ b/.github/workflows/test_deploy.yml @@ -9,6 +9,7 @@ on: env: DEPENDENCY_CACHE_PREFIX: "v2" NETWORK_CACHE_PREFIX: "v1" + TEMPLATES_VERSION: "0.4.1" # To create a new cache key when only templates are updated jobs: set-matrix: runs-on: ubuntu-latest @@ -61,7 +62,7 @@ jobs: path: | ${{ env.pythonLocation }} renku/templates - key: ${{env.DEPENDENCY_CACHE_PREFIX}}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{hashFiles('poetry.lock')}}-${{hashFiles('Makefile')}} + key: ${{ env.DEPENDENCY_CACHE_PREFIX }}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }}-${{ hashFiles('Makefile') }}-${{ env.TEMPLATES_VERSION }} - name: Install dependencies if: steps.dependency-cache.outputs.cache-hit != 'true' || 'refs/heads/master' == github.ref || 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/') env: @@ -113,7 +114,7 @@ jobs: path: | ${{ env.pythonLocation }} renku/templates - key: ${{env.DEPENDENCY_CACHE_PREFIX}}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{hashFiles('poetry.lock')}}-${{hashFiles('Makefile')}} + key: ${{ env.DEPENDENCY_CACHE_PREFIX }}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }}-${{ hashFiles('Makefile') }}-${{ env.TEMPLATES_VERSION }} - name: Install dependencies if: steps.dependency-cache.outputs.cache-hit != 'true' || 'refs/heads/master' == github.ref || 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/') env: @@ -158,7 +159,7 @@ jobs: path: | ${{ env.pythonLocation }} renku/templates - key: ${{env.DEPENDENCY_CACHE_PREFIX}}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{hashFiles('poetry.lock')}}-${{hashFiles('Makefile')}} + key: ${{ env.DEPENDENCY_CACHE_PREFIX }}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }}-${{ hashFiles('Makefile') }}-${{ env.TEMPLATES_VERSION }} - name: Install dependencies if: steps.dependency-cache.outputs.cache-hit != 'true' || 'refs/heads/master' == github.ref || 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/') env: @@ -201,7 +202,7 @@ jobs: path: | ${{ env.pythonLocation }} renku/templates - key: ${{env.DEPENDENCY_CACHE_PREFIX}}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{hashFiles('poetry.lock')}}-${{hashFiles('Makefile')}} + key: ${{ env.DEPENDENCY_CACHE_PREFIX }}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }}-${{ hashFiles('Makefile') }}-${{ env.TEMPLATES_VERSION }} - name: Install dependencies if: steps.dependency-cache.outputs.cache-hit != 'true' || 'refs/heads/master' == github.ref || 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/') run: | @@ -248,7 +249,7 @@ jobs: path: | ${{ env.pythonLocation }} renku/templates - key: ${{env.DEPENDENCY_CACHE_PREFIX}}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{hashFiles('poetry.lock')}}-${{hashFiles('Makefile')}} + key: ${{ env.DEPENDENCY_CACHE_PREFIX }}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }}-${{ hashFiles('Makefile') }}-${{ env.TEMPLATES_VERSION }} - name: Install dependencies if: steps.dependency-cache.outputs.cache-hit != 'true' || 'refs/heads/master' == github.ref || 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/') env: @@ -319,7 +320,7 @@ jobs: path: | ${{ env.pythonLocation }} renku/templates - key: ${{env.DEPENDENCY_CACHE_PREFIX}}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{hashFiles('poetry.lock')}}-${{hashFiles('Makefile')}} + key: ${{ env.DEPENDENCY_CACHE_PREFIX }}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }}-${{ hashFiles('Makefile') }}-${{ env.TEMPLATES_VERSION }} - name: Install dependencies if: steps.dependency-cache.outputs.cache-hit != 'true' || 'refs/heads/master' == github.ref || 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/') env: @@ -396,7 +397,7 @@ jobs: path: | ${{ env.pythonLocation }} renku/templates - key: ${{env.DEPENDENCY_CACHE_PREFIX}}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{hashFiles('poetry.lock')}}-${{hashFiles('Makefile')}} + key: ${{ env.DEPENDENCY_CACHE_PREFIX }}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }}-${{ hashFiles('Makefile') }}-${{ env.TEMPLATES_VERSION }} - name: Install dependencies if: steps.dependency-cache.outputs.cache-hit != 'true' || 'refs/heads/master' == github.ref || 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/') env: @@ -473,7 +474,7 @@ jobs: path: | ${{ env.pythonLocation }} renku/templates - key: ${{env.DEPENDENCY_CACHE_PREFIX}}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{hashFiles('poetry.lock')}}-${{hashFiles('Makefile')}} + key: ${{ env.DEPENDENCY_CACHE_PREFIX }}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }}-${{ hashFiles('Makefile') }}-${{ env.TEMPLATES_VERSION }} - name: Install dependencies if: steps.dependency-cache.outputs.cache-hit != 'true' || 'refs/heads/master' == github.ref || 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/') env: @@ -709,7 +710,7 @@ jobs: path: | ${{ env.pythonLocation }} renku/templates - key: ${{env.DEPENDENCY_CACHE_PREFIX}}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{hashFiles('poetry.lock')}}-${{hashFiles('Makefile')}} + key: ${{ env.DEPENDENCY_CACHE_PREFIX }}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }}-${{ hashFiles('Makefile') }}-${{ env.TEMPLATES_VERSION }} - name: Install dependencies if: steps.dependency-cache.outputs.cache-hit != 'true' || 'refs/heads/master' == github.ref || 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/') env: @@ -806,7 +807,7 @@ jobs: path: | ${{ env.pythonLocation }} renku/templates - key: ${{env.DEPENDENCY_CACHE_PREFIX}}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{hashFiles('poetry.lock')}}-${{hashFiles('Makefile')}} + key: ${{ env.DEPENDENCY_CACHE_PREFIX }}-${{ runner.os }}-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }}-${{ hashFiles('Makefile') }}-${{ env.TEMPLATES_VERSION }} - name: Install dependencies if: steps.dependency-cache.outputs.cache-hit != 'true' || 'refs/heads/master' == github.ref || 'refs/heads/develop' == github.ref || startsWith(github.ref, 'refs/tags/') env: diff --git a/poetry.lock b/poetry.lock index ba1a1b0623..93fc2fe7b6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -5,7 +5,7 @@ name = "addict" version = "2.4.0" description = "Addict is a dictionary whose items can be set using both attribute and item syntax." category = "main" -optional = true +optional = false python-versions = "*" files = [ {file = "addict-2.4.0-py3-none-any.whl", hash = "sha256:249bb56bbfd3cdc2a004ea0ff4c2b6ddc84d53bc2194761636eb314d5cfa5dfc"}, @@ -29,7 +29,7 @@ name = "ansicon" version = "1.89.0" description = "Python wrapper for loading Jason Hood's ANSICON" category = "main" -optional = true +optional = false python-versions = "*" files = [ {file = "ansicon-1.89.0-py2.py3-none-any.whl", hash = "sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec"}, @@ -218,7 +218,7 @@ name = "blessed" version = "1.19.1" description = "Easy, practical library for making terminal apps, by providing an elegant, well-documented interface to Colors, Keyboard input, and screen Positioning capabilities." category = "main" -optional = true +optional = false python-versions = ">=2.7" files = [ {file = "blessed-1.19.1-py2.py3-none-any.whl", hash = "sha256:63b8554ae2e0e7f43749b6715c734cc8f3883010a809bf16790102563e6cf25b"}, @@ -319,14 +319,14 @@ redis = ["redis (>=2.10.5)"] [[package]] name = "cachetools" -version = "5.2.1" +version = "5.3.0" description = "Extensible memoizing collections and decorators" category = "main" optional = false python-versions = "~=3.7" files = [ - {file = "cachetools-5.2.1-py3-none-any.whl", hash = "sha256:8462eebf3a6c15d25430a8c27c56ac61340b2ecf60c9ce57afc2b97e450e47da"}, - {file = "cachetools-5.2.1.tar.gz", hash = "sha256:5991bc0e08a1319bb618d3195ca5b6bc76646a49c21d55962977197b301cc1fe"}, + {file = "cachetools-5.3.0-py3-none-any.whl", hash = "sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"}, + {file = "cachetools-5.3.0.tar.gz", hash = "sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14"}, ] [[package]] @@ -843,7 +843,7 @@ name = "dill" version = "0.3.6" description = "serialize all of python" category = "main" -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, @@ -919,7 +919,7 @@ name = "enlighten" version = "1.11.1" description = "Enlighten Progress Bar" category = "main" -optional = true +optional = false python-versions = "*" files = [ {file = "enlighten-1.11.1-py2.py3-none-any.whl", hash = "sha256:e825eb534ca80778bb7d46e5581527b2a6fae559b6cf09e290a7952c6e11961e"}, @@ -1122,7 +1122,7 @@ name = "future" version = "0.18.3" description = "Clean single-source support for Python 3 and 2" category = "main" -optional = true +optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ {file = "future-0.18.3.tar.gz", hash = "sha256:34a17436ed1e96697a86f9de3d15a3b0be01d8bc8de9c1dffd59fb8234ed5307"}, @@ -1233,14 +1233,14 @@ tests = ["freezegun", "pytest", "pytest-cov"] [[package]] name = "identify" -version = "2.5.13" +version = "2.5.15" description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "identify-2.5.13-py2.py3-none-any.whl", hash = "sha256:8aa48ce56e38c28b6faa9f261075dea0a942dfbb42b341b4e711896cbb40f3f7"}, - {file = "identify-2.5.13.tar.gz", hash = "sha256:abb546bca6f470228785338a01b539de8a85bbf46491250ae03363956d8ebb10"}, + {file = "identify-2.5.15-py2.py3-none-any.whl", hash = "sha256:1f4b36c5f50f3f950864b2a047308743f064eaa6f6645da5e5c780d1c7125487"}, + {file = "identify-2.5.15.tar.gz", hash = "sha256:c22aa206f47cc40486ecf585d27ad5f40adbfc494a3fa41dc3ed0499a23b123f"}, ] [package.extras] @@ -1421,7 +1421,7 @@ name = "jinxed" version = "1.2.0" description = "Jinxed Terminal Library" category = "main" -optional = true +optional = false python-versions = "*" files = [ {file = "jinxed-1.2.0-py2.py3-none-any.whl", hash = "sha256:cfc2b2e4e3b4326954d546ba6d6b9a7a796ddcb0aef8d03161d005177eb0d48b"}, @@ -1607,7 +1607,6 @@ files = [ {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0"}, {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e"}, {file = "lxml-4.9.2-cp35-cp35m-win32.whl", hash = "sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df"}, - {file = "lxml-4.9.2-cp35-cp35m-win_amd64.whl", hash = "sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5"}, {file = "lxml-4.9.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53"}, {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7"}, {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe"}, @@ -1617,7 +1616,6 @@ files = [ {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74"}, {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38"}, {file = "lxml-4.9.2-cp36-cp36m-win32.whl", hash = "sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5"}, - {file = "lxml-4.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3"}, {file = "lxml-4.9.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03"}, {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941"}, {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726"}, @@ -1669,52 +1667,62 @@ source = ["Cython (>=0.29.7)"] [[package]] name = "markupsafe" -version = "2.1.1" +version = "2.1.2" description = "Safely add untrusted strings to HTML/XML markup." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, ] [[package]] @@ -2123,14 +2131,14 @@ pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" [[package]] name = "pathspec" -version = "0.10.3" +version = "0.11.0" description = "Utility library for gitignore style pattern matching of file paths." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, - {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, + {file = "pathspec-0.11.0-py3-none-any.whl", hash = "sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229"}, + {file = "pathspec-0.11.0.tar.gz", hash = "sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"}, ] [[package]] @@ -2428,14 +2436,14 @@ virtualenv = ">=20.10.0" [[package]] name = "prefixed" -version = "0.5.0" +version = "0.7.0" description = "Prefixed alternative numeric library" category = "main" -optional = true +optional = false python-versions = "*" files = [ - {file = "prefixed-0.5.0-py2.py3-none-any.whl", hash = "sha256:debab03014863087eb013750a2e71daa5f6a295cee46b44ba1b90d7262c1b92d"}, - {file = "prefixed-0.5.0.tar.gz", hash = "sha256:b134d734136250b17b68eede65a3370fab0134412cb66bc8be3568ff05bdf8e4"}, + {file = "prefixed-0.7.0-py2.py3-none-any.whl", hash = "sha256:537b0e4ff4516c4578f277a41d7104f769d6935ae9cdb0f88fed82ec7b3c0ca5"}, + {file = "prefixed-0.7.0.tar.gz", hash = "sha256:0b54d15e602eb8af4ac31b1db21a37ea95ce5890e0741bb0dd9ded493cefbbe9"}, ] [[package]] @@ -2553,7 +2561,7 @@ name = "py-tes" version = "0.4.2" description = "Library for communicating with the GA4GH Task Execution API" category = "main" -optional = true +optional = false python-versions = ">=2.7, <4" files = [ {file = "py-tes-0.4.2.tar.gz", hash = "sha256:f6926cd59b7dfc8e37840955bf1cc7c43ad4d99ba5eae100b6156c918617472c"}, @@ -3135,14 +3143,14 @@ yaml = ["PyYaml (>=5.2)"] [[package]] name = "pytz" -version = "2022.7" +version = "2022.7.1" description = "World timezone definitions, modern and historical" category = "main" optional = false python-versions = "*" files = [ - {file = "pytz-2022.7-py2.py3-none-any.whl", hash = "sha256:93007def75ae22f7cd991c84e02d434876818661f8df9ad5df9e950ff4e52cfd"}, - {file = "pytz-2022.7.tar.gz", hash = "sha256:7ccfae7b4b2c067464a6733c6261673fdb8fd1be905460396b97a073e9fa683a"}, + {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, + {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, ] [[package]] @@ -3626,14 +3634,14 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "65.7.0" +version = "66.1.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "setuptools-65.7.0-py3-none-any.whl", hash = "sha256:8ab4f1dbf2b4a65f7eec5ad0c620e84c34111a68d3349833494b9088212214dd"}, - {file = "setuptools-65.7.0.tar.gz", hash = "sha256:4d3c92fac8f1118bb77a22181355e29c239cabfe2b9effdaa665c66b711136d7"}, + {file = "setuptools-66.1.1-py3-none-any.whl", hash = "sha256:6f590d76b713d5de4e49fe4fbca24474469f53c83632d5d0fd056f7ff7e8112b"}, + {file = "setuptools-66.1.1.tar.gz", hash = "sha256:ac4008d396bc9cd983ea483cb7139c0240a07bbc74ffb6232fceffedc6cf03a8"}, ] [package.extras] @@ -3988,7 +3996,7 @@ name = "toil" version = "5.7.1" description = "Pipeline management software for clusters." category = "main" -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "toil-5.7.1-py3-none-any.whl", hash = "sha256:81041ba89867a3a37e265a5e073c60a6dfe9ed03342fb3a53bb48f7c04ef1b4b"}, @@ -4152,26 +4160,26 @@ files = [ [[package]] name = "types-python-dateutil" -version = "2.8.19.5" +version = "2.8.19.6" description = "Typing stubs for python-dateutil" category = "dev" optional = false python-versions = "*" files = [ - {file = "types-python-dateutil-2.8.19.5.tar.gz", hash = "sha256:ab91fc5f715f7d76d9a50d3dd74d0c68dfe38a54f0239cfa0506575ae4d87a9d"}, - {file = "types_python_dateutil-2.8.19.5-py3-none-any.whl", hash = "sha256:253c267e71cac148003db200cb3fc572ab0e2f994b34a4c1de5d3f550f0ad5b2"}, + {file = "types-python-dateutil-2.8.19.6.tar.gz", hash = "sha256:4a6f4cc19ce4ba1a08670871e297bf3802f55d4f129e6aa2443f540b6cf803d2"}, + {file = "types_python_dateutil-2.8.19.6-py3-none-any.whl", hash = "sha256:cfb7d31021c6bce6f3362c69af6e3abb48fe3e08854f02487e844ff910deec2a"}, ] [[package]] name = "types-pyyaml" -version = "6.0.12.2" +version = "6.0.12.3" description = "Typing stubs for PyYAML" category = "dev" optional = false python-versions = "*" files = [ - {file = "types-PyYAML-6.0.12.2.tar.gz", hash = "sha256:6840819871c92deebe6a2067fb800c11b8a063632eb4e3e755914e7ab3604e83"}, - {file = "types_PyYAML-6.0.12.2-py3-none-any.whl", hash = "sha256:1e94e80aafee07a7e798addb2a320e32956a373f376655128ae20637adb2655b"}, + {file = "types-PyYAML-6.0.12.3.tar.gz", hash = "sha256:17ce17b3ead8f06e416a3b1d5b8ddc6cb82a422bb200254dd8b469434b045ffc"}, + {file = "types_PyYAML-6.0.12.3-py3-none-any.whl", hash = "sha256:879700e9f215afb20ab5f849590418ab500989f83a57e635689e1d50ccc63f0c"}, ] [[package]] @@ -4329,26 +4337,26 @@ redis = ">=3.0.0" [[package]] name = "wcwidth" -version = "0.2.5" +version = "0.2.6" description = "Measures the displayed width of unicode strings in a terminal" category = "main" optional = false python-versions = "*" files = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, + {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, + {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, ] [[package]] name = "websocket-client" -version = "1.4.2" +version = "1.5.0" description = "WebSocket client for Python with low level API options" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "websocket-client-1.4.2.tar.gz", hash = "sha256:d6e8f90ca8e2dd4e8027c4561adeb9456b54044312dba655e7cae652ceb9ae59"}, - {file = "websocket_client-1.4.2-py3-none-any.whl", hash = "sha256:d6b06432f184438d99ac1f456eaf22fe1ade524c3dd16e661142dc54e9cba574"}, + {file = "websocket-client-1.5.0.tar.gz", hash = "sha256:561ca949e5bbb5d33409a37235db55c279235c78ee407802f1d2314fff8a8536"}, + {file = "websocket_client-1.5.0-py3-none-any.whl", hash = "sha256:fb5d81b95d350f3a54838ebcb4c68a5353bbd1412ae8f068b1e5280faeb13074"}, ] [package.extras] @@ -4644,14 +4652,14 @@ test = ["manuel", "mock", "zc.customdoctests", "zope.testing", "zope.testrunner" [[package]] name = "zeo" -version = "5.3.0" +version = "5.4.0" description = "ZEO - Single-server client-server database server for ZODB" category = "main" optional = false python-versions = ">=2.7.9,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" files = [ - {file = "ZEO-5.3.0-py2.py3-none-any.whl", hash = "sha256:cec59227cb87d938b000a875fa0049433f7a3cc2f0452b95876c890f485584b6"}, - {file = "ZEO-5.3.0.tar.gz", hash = "sha256:ffc54334f8384d6faf1693dce823c0a5fcd429fa015c47d2d49d1bd689997157"}, + {file = "ZEO-5.4.0-py2.py3-none-any.whl", hash = "sha256:c0a1e38361394d397b5ec8552315a262d4978ac873ed109d81f6687d04178d00"}, + {file = "ZEO-5.4.0.tar.gz", hash = "sha256:50f640b36a992f66ed58caf55be80faf26deaccd4f02a84caae3c440f6320133"}, ] [package.dependencies] @@ -4667,7 +4675,7 @@ ZODB = ">=5.1.1" [package.extras] docs = ["Sphinx", "repoze.sphinx.autointerface", "sphinx-rtd-theme"] msgpack = ["msgpack-python"] -test = ["ZODB (>=5.5.1)", "manuel", "mock", "msgpack (<1)", "random2", "zope.testing", "zope.testrunner"] +test = ["ZConfig", "ZODB (>=5.5.1)", "ZopeUndo", "manuel", "mock", "msgpack (<1)", "transaction", "zdaemon", "zope.testing", "zope.testrunner"] uvloop = ["uvloop (>=0.5.1)"] [[package]] @@ -4919,9 +4927,8 @@ cffi = ["cffi (>=1.11)"] [extras] service = ["apispec", "apispec-webframeworks", "circus", "flask", "gunicorn", "marshmallow", "marshmallow-oneofschema", "pillow", "python-dotenv", "redis", "rq", "rq-scheduler", "sentry-sdk", "walrus"] -toil = ["toil"] [metadata] lock-version = "2.0" python-versions = "^3.7.1" -content-hash = "1da16337e22e3f68e5ff182b140a54e459ee6489211a6535f78974c928043f18" +content-hash = "b1e8a803183bfcc430dc9992e57f1113594a91bb99bd639c0e196ada0fa6d310" diff --git a/renku/core/constant.py b/renku/core/constant.py index c3bab7583f..f48cc692a3 100644 --- a/renku/core/constant.py +++ b/renku/core/constant.py @@ -18,6 +18,7 @@ """Renku core constants.""" import os +from enum import IntEnum from pathlib import Path APP_NAME = "Renku" @@ -88,3 +89,19 @@ ".gitattributes", ".gitignore", ] + + +class ProviderPriority(IntEnum): + """Defines the order in which providers are checked (highest order is checked first). + + For example, to check if a dataset provider supports a URI, providers that support more specific URIs should have a + higher priority so that they are checked first. + """ + + HIGHEST = 1 + HIGHER = 2 + HIGH = 3 + NORMAL = 4 + LOW = 5 + LOWER = 6 + LOWEST = 7 diff --git a/renku/core/dataset/providers/api.py b/renku/core/dataset/providers/api.py index 00394bc71a..a35248a767 100644 --- a/renku/core/dataset/providers/api.py +++ b/renku/core/dataset/providers/api.py @@ -17,11 +17,11 @@ import abc from collections import UserDict -from enum import IntEnum from pathlib import Path from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, Union from renku.core import errors +from renku.core.constant import ProviderPriority from renku.core.plugin import hookimpl from renku.core.util.util import NO_VALUE, NoValueType from renku.domain_model.dataset_provider import IDatasetProviderPlugin @@ -37,21 +37,6 @@ from renku.domain_model.dataset import Dataset, DatasetTag -class ProviderPriority(IntEnum): - """Defines the order in which a provider is checked to see if it supports a URI. - - Providers that support more specific URIs should have a higher priority so that they are checked first. - """ - - HIGHEST = 1 - HIGHER = 2 - HIGH = 3 - NORMAL = 4 - LOW = 5 - LOWER = 6 - LOWEST = 7 - - class ProviderApi(IDatasetProviderPlugin): """Interface defining provider methods.""" diff --git a/renku/core/errors.py b/renku/core/errors.py index 4d33dcdf79..6050923932 100644 --- a/renku/core/errors.py +++ b/renku/core/errors.py @@ -656,6 +656,10 @@ def __init__(self, reason: str): super().__init__(f"Docker failed: {reason}") +class DockerAPIError(DockerError): + """Raised when error has returned from the Docker API.""" + + class SessionStartError(RenkuException): """Raised when an error occurs trying to start sessions.""" diff --git a/renku/core/session/docker.py b/renku/core/session/docker.py index 70380c7d0d..0bc9af55ea 100644 --- a/renku/core/session/docker.py +++ b/renku/core/session/docker.py @@ -18,7 +18,7 @@ """Docker based interactive session provider.""" from pathlib import Path -from typing import Any, Dict, Iterable, List, Optional, cast +from typing import Any, Dict, Iterable, List, Optional, Tuple, cast from uuid import uuid4 import docker @@ -26,6 +26,7 @@ from renku.core import errors from renku.core.config import get_value +from renku.core.constant import ProviderPriority from renku.core.plugin import hookimpl from renku.core.util import communication from renku.domain_model.project_context import project_context @@ -36,6 +37,8 @@ class DockerSessionProvider(ISessionProvider): """A docker based interactive session provider.""" JUPYTER_PORT = 8888 + # NOTE: Give the docker provider a higher priority so that it's checked first + priority: ProviderPriority = ProviderPriority.HIGHEST def __init__(self): self._docker_client = None @@ -63,7 +66,7 @@ def _get_jupyter_urls(ports: Dict[str, Any], auth_token: str, jupyter_port: int port_key = f"{jupyter_port}/tcp" if port_key not in ports: return list() - return map(lambda x: f'http://{x["HostIp"]}:{x["HostPort"]}/?token={auth_token}', ports[port_key]) + return map(lambda x: f"http://{x['HostIp']}:{x['HostPort']}/?token={auth_token}", ports[port_key]) def _get_docker_containers(self, project_name: str) -> List[docker.models.containers.Container]: return self.docker_client().containers.list(filters={"label": f"renku_project={project_name}"}) @@ -72,6 +75,10 @@ def get_name(self) -> str: """Return session provider's name.""" return "docker" + def is_remote_provider(self) -> bool: + """Return True for remote providers (i.e. not local Docker).""" + return False + def build_image(self, image_descriptor: Path, image_name: str, config: Optional[Dict[str, Any]]): """Builds the container image.""" self.docker_client().images.build(path=str(image_descriptor), tag=image_name) @@ -84,7 +91,7 @@ def find_image(self, image_name: str, config: Optional[Dict[str, Any]]) -> bool: except docker.errors.ImageNotFound: try: self.docker_client().images.get_registry_data(image_name) - except docker.errors.NotFound: + except docker.errors.APIError: return False else: return True @@ -133,96 +140,107 @@ def session_start( mem_request: Optional[str] = None, disk_request: Optional[str] = None, gpu_request: Optional[str] = None, - ) -> str: + ) -> Tuple[str, str]: """Creates an interactive session. Returns: - str: a unique id for the created interactive session. + Tuple[str, str]: Provider message and a possible warning message. """ - try: - docker_is_running = self.docker_client().ping() - if not docker_is_running: - raise errors.DockerError( - "Could not communicate with the docker instance. Please make sure it is running!" + def session_start_helper(consider_disk_request: bool): + try: + docker_is_running = self.docker_client().ping() + if not docker_is_running: + raise errors.DockerError( + "Could not communicate with the docker instance. Please make sure it is running!" + ) + + auth_token = uuid4().hex + default_url = get_value("interactive", "default_url") + + # resource requests + resource_requests: Dict[str, Any] = dict() + if cpu_request: + # based on the docker go cli: func ParseCPUs + resource_requests["nano_cpus"] = int(cpu_request * 10**9) + + if mem_request: + resource_requests["mem_limit"] = mem_request + + if consider_disk_request and disk_request: + resource_requests["storage_opt"] = {"size": disk_request} + + if gpu_request: + if gpu_request == "all": + resource_requests["device_requests"] = [ + docker.types.DeviceRequest(count=-1, capabilities=[["compute", "utility"]]) + ] + else: + resource_requests["device_requests"] = [ + docker.types.DeviceRequest(count=[gpu_request], capabilities=[["compute", "utility"]]) + ] + + # NOTE: set git user + image_data = self.docker_client().api.inspect_image(image_name) + working_dir = image_data.get("Config", {}).get("WorkingDir", None) + + if working_dir is None: + working_dir = "/home/jovyan" + + work_dir = Path(working_dir) / "work" / project_name.split("/")[-1] + + volumes = [f"{str(project_context.path.resolve())}:{work_dir}"] + + user = project_context.repository.get_user() + environment = { + "GIT_AUTHOR_NAME": user.name, + "GIT_AUTHOR_EMAIL": user.email, + "GIT_COMMITTER_EMAIL": user.email, + "EMAIL": user.email, + } + + container = self.docker_client().containers.run( + image_name, + 'jupyter notebook --NotebookApp.ip="0.0.0.0"' + f" --NotebookApp.port={DockerSessionProvider.JUPYTER_PORT}" + f' --NotebookApp.token="{auth_token}" --NotebookApp.default_url="{default_url}"' + f" --NotebookApp.notebook_dir={work_dir}", + detach=True, + labels={"renku_project": project_name, "jupyter_token": auth_token}, + ports={f"{DockerSessionProvider.JUPYTER_PORT}/tcp": None}, + remove=True, + environment=environment, + volumes=volumes, + working_dir=str(work_dir), + **resource_requests, ) - auth_token = uuid4().hex - default_url = get_value("interactive", "default_url") - - # resource requests - resource_requests: Dict[str, Any] = dict() - if cpu_request: - # based on the docker go cli: func ParseCPUs - resource_requests["nano_cpus"] = int(cpu_request * 10**9) - - if mem_request: - resource_requests["mem_limit"] = mem_request - - if disk_request: - resource_requests["storage_opt"] = {"size": disk_request} - - if gpu_request: - if gpu_request == "all": - resource_requests["device_requests"] = [ - docker.types.DeviceRequest(count=-1, capabilities=[["compute", "utility"]]) - ] - else: - resource_requests["device_requests"] = [ - docker.types.DeviceRequest(count=[gpu_request], capabilities=[["compute", "utility"]]) - ] - - # NOTE: set git user - image_data = self.docker_client().api.inspect_image(image_name) - working_dir = image_data.get("Config", {}).get("WorkingDir", None) - - if working_dir is None: - working_dir = "/home/jovyan" - - work_dir = Path(working_dir) / "work" / project_name.split("/")[-1] - - volumes = [f"{str(project_context.path.resolve())}:{work_dir}"] - - user = project_context.repository.get_user() - environment = { - "GIT_AUTHOR_NAME": user.name, - "GIT_AUTHOR_EMAIL": user.email, - "GIT_COMMITTER_EMAIL": user.email, - "EMAIL": user.email, - } - - container = self.docker_client().containers.run( - image_name, - f'jupyter notebook --NotebookApp.ip="0.0.0.0" --NotebookApp.port={DockerSessionProvider.JUPYTER_PORT}' - f' --NotebookApp.token="{auth_token}" --NotebookApp.default_url="{default_url}"' - f" --NotebookApp.notebook_dir={work_dir}", - detach=True, - labels={"renku_project": project_name, "jupyter_token": auth_token}, - ports={f"{DockerSessionProvider.JUPYTER_PORT}/tcp": None}, - remove=True, - environment=environment, - volumes=volumes, - working_dir=str(work_dir), - **resource_requests, - ) + if not container.ports: + container.reload() + + jupyter_urls = DockerSessionProvider._get_jupyter_urls( + container.ports, auth_token, jupyter_port=DockerSessionProvider.JUPYTER_PORT + ) - if not container.ports: - container.reload() + message = f"The session for '{image_name}' has been successfully started. It is available at:\n\t" + message += "\n\t".join(jupyter_urls) + return message + except docker.errors.BuildError as error: + raise errors.DockerError("Couldn't build the image. See inner exception for details.") from error + except docker.errors.APIError as error: + raise errors.DockerAPIError("Docker API returned an error. See inner exception for details.") from error + except ReadTimeout as error: + raise errors.DockerError( + "Couldn't reach the Docker API. Is the docker service running and up to date?" + ) from error - jupyter_urls = DockerSessionProvider._get_jupyter_urls( - container.ports, auth_token, jupyter_port=DockerSessionProvider.JUPYTER_PORT - ) - message = f"The session for '{image_name}' has been successfully started. It is available at:\n\t" - message += "\n\t".join(jupyter_urls) - return message - except docker.errors.BuildError as error: - raise errors.DockerError("Couldn't build the image. See inner exception for details.") from error - except docker.errors.APIError as error: - raise errors.DockerError("Docker API returned an error. See inner exception for details.") from error - except ReadTimeout as error: - raise errors.DockerError( - "Couldn't reach the Docker API. Is the docker service running and up to date?" - ) from error + try: + result = session_start_helper(consider_disk_request=True) + except errors.DockerAPIError: + warning_message = "Cannot start a session with the disk request: Ignoring the disk request" + return session_start_helper(consider_disk_request=False), warning_message + else: + return result, "" def session_stop(self, project_name: str, session_name: Optional[str], stop_all: bool) -> bool: """Stops all or a given interactive session.""" diff --git a/renku/core/session/renkulab.py b/renku/core/session/renkulab.py index a37654edd7..cd2b669bf5 100644 --- a/renku/core/session/renkulab.py +++ b/renku/core/session/renkulab.py @@ -23,60 +23,16 @@ from typing import Any, Dict, List, Optional, Tuple, Union from renku.core import errors -from renku.core.config import get_value, set_value +from renku.core.login import read_renku_token from renku.core.plugin import hookimpl from renku.core.session.utils import get_renku_project_name, get_renku_url from renku.core.util import communication, requests from renku.core.util.git import get_remote +from renku.core.util.jwt import is_token_expired from renku.domain_model.project_context import project_context from renku.domain_model.session import ISessionProvider, Session -def _get_token(renku_url: str) -> Tuple[str, bool]: - """Get a token for authenticating with renku. - - If the user is logged in then the JWT token from renku login will be used. - Otherwise the anonymous user token will be used. Returns the token and a flag to - indicate if the user is registered (true) or anonymous(false). - """ - registered_token = get_value(section="http", key=urllib.parse.urlparse(renku_url).netloc) - if not registered_token: - return _get_anonymous_credentials(renku_url=renku_url), False - return registered_token, True - - -def _get_anonymous_credentials(renku_url: str) -> str: - def _get_anonymous_token() -> Optional[str]: - import requests - - with requests.Session() as session: - url = urllib.parse.urljoin(renku_url, "api/user") - try: - session.get( - url, - headers={ - "X-Requested-With": "XMLHttpRequest", - "X-Forwarded-Uri": "/api/user", - }, - ) - except (requests.exceptions.RequestException, requests.exceptions.ConnectionError): - pass - return session.cookies.get("anon-id") - - renku_host = urllib.parse.urlparse(renku_url).netloc - anon_token = get_value(section="anonymous_token", key=renku_host) - if not anon_token: - anon_token = _get_anonymous_token() - if not anon_token: - raise errors.AuthenticationError( - "Could not get anonymous user token from Renku. " - f"Ensure the Renku deployment at {renku_url} supports anonymous sessions or use " - f"'renku login {renku_host}' to log in." - ) - set_value(section="anonymous_token", key=renku_host, value=anon_token, global_only=True) - return anon_token - - class RenkulabSessionProvider(ISessionProvider): """A session provider that uses the notebook service API to launch sessions.""" @@ -102,24 +58,23 @@ def _notebooks_url(self) -> str: self.__notebooks_url = url return self.__notebooks_url - def _token(self) -> str: + def _get_token(self) -> str: """Get the JWT token used to authenticate against Renku.""" - token, _ = _get_token(renku_url=self._renku_url()) + token = read_renku_token(endpoint=self._renku_url()) if token is None: raise errors.AuthenticationError("Please run the renku login command to authenticate with Renku.") + elif is_token_expired(token): + raise errors.AuthenticationError( + "Authentication token is expired: Please run the renku login command to authenticate with Renku." + ) return token - def _is_user_registered(self) -> bool: - _, is_user_registered = _get_token(renku_url=self._renku_url()) - return is_user_registered - def _auth_header(self) -> Dict[str, str]: """Get the authentication header with the JWT token or cookie needed to authenticate with Renku.""" - if self._is_user_registered(): - return {"Authorization": f"Bearer {self._token()}"} - return {"Cookie": f"anon-id={self._token()}"} + return {"Authorization": f"Bearer {self._get_token()}"} - def _get_renku_project_name_parts(self): + @staticmethod + def _get_renku_project_name_parts(): repository = project_context.repository if project_context.remote.name and project_context.remote.owner: if get_remote(repository, name="renku-backup-origin") and project_context.remote.owner.startswith("repos/"): @@ -181,8 +136,6 @@ def _wait_for_image( def pre_start_checks(self): """Check if the state of the repository is as expected before starting a session.""" - if not self._is_user_registered(): - return repository = project_context.repository if repository.is_dirty(untracked_files=True): @@ -195,7 +148,8 @@ def pre_start_checks(self): repository.add(all=True) repository.commit("Automated commit by Renku CLI.") - def _remote_head_hexsha(self): + @staticmethod + def _remote_head_hexsha(): remote = get_remote(repository=project_context.repository) if remote is None: @@ -203,10 +157,15 @@ def _remote_head_hexsha(self): return remote.head - @staticmethod - def _send_renku_request(req_type: str, *args, **kwargs): + def _send_renku_request(self, req_type: str, *args, **kwargs): res = getattr(requests, req_type)(*args, **kwargs) if res.status_code == 401: + # NOTE: Check if logged in to KC but not the Renku UI + token = read_renku_token(endpoint=self._renku_url()) + if token and not is_token_expired(token): + raise errors.AuthenticationError( + f"Please log in the Renku UI at {self._renku_url()} to complete authentication with Renku" + ) raise errors.AuthenticationError( "Please run the renku login command to authenticate with Renku or to refresh your expired credentials." ) @@ -216,14 +175,14 @@ def get_name(self) -> str: """Return session provider's name.""" return "renkulab" + def is_remote_provider(self) -> bool: + """Return True for remote providers (i.e. not local Docker).""" + return True + def build_image(self, image_descriptor: Path, image_name: str, config: Optional[Dict[str, Any]]): """Builds the container image.""" if self.find_image(image_name, config=config): return - if not self._is_user_registered(): - raise errors.NotebookSessionImageNotExistError( - f"Renku cannot find the image {image_name} and use it in an anonymous session." - ) repository = project_context.repository if repository.head.commit.hexsha != self._remote_head_hexsha(): repository.push() @@ -282,33 +241,26 @@ def session_start( mem_request: Optional[str] = None, disk_request: Optional[str] = None, gpu_request: Optional[str] = None, - ) -> str: + ) -> Tuple[str, str]: """Creates an interactive session. Returns: - str: a unique id for the created interactive session. + Tuple[str, str]: Provider message and a possible warning message. """ repository = project_context.repository session_commit = repository.head.commit.hexsha - if not self._is_user_registered(): - communication.warn( - "You are starting a session as an anonymous user. " - "None of the local changes in this project will be reflected in your session. " - "In addition, any changes you make in the new session will be lost when " - "the session is shut down." + if repository.head.commit.hexsha != self._remote_head_hexsha(): + # INFO: The user is registered, the image is pinned or already available + # but the local repository is not fully in sync with the remote + communication.confirm( + "You have unpushed commits that will not be present in your session. " + "Renku can automatically push these commits so that they are present " + "in the session you are launching. Do you wish to proceed?", + abort=True, ) - else: - if repository.head.commit.hexsha != self._remote_head_hexsha(): - # INFO: The user is registered, the image is pinned or already available - # but the local repository is not fully in sync with the remote - communication.confirm( - "You have unpushed commits that will not be present in your session. " - "Renku can automatically push these commits so that they are present " - "in the session you are launching. Do you wish to proceed?", - abort=True, - ) - repository.push() + repository.push() + server_options: Dict[str, Union[str, float]] = {} if cpu_request: server_options["cpu_request"] = cpu_request @@ -333,7 +285,7 @@ def session_start( if res.status_code in [200, 201]: session_name = res.json()["name"] self._wait_for_session_status(session_name, "running") - return session_name + return f"Session {session_name} successfully started", "" raise errors.RenkulabSessionError("Cannot start session via the notebook service because " + res.text) def session_stop(self, project_name: str, session_name: Optional[str], stop_all: bool) -> bool: @@ -359,20 +311,12 @@ def session_stop(self, project_name: str, session_name: Optional[str], stop_all: def session_url(self, session_name: str) -> str: """Get the URL of the interactive session.""" - if self._is_user_registered(): - project_name_parts = self._get_renku_project_name_parts() - session_url_parts = [ - "projects", - project_name_parts["namespace"], - project_name_parts["project"], - "sessions/show", - session_name, - ] - return urllib.parse.urljoin(self._renku_url(), "/".join(session_url_parts)) - else: - # NOTE: The sessions/show logic of the UI expects a cookie to already be present - # with the anonymous user ID, but in this case we need to open a new browser window - # and need to pass the token in the URL, that is why anonymous sessions will be shown - # and opened in the full session view not in the i-frame view like registered sessions - session_url_parts = ["sessions", f"{session_name}?token={self._token()}"] - return urllib.parse.urljoin(self._renku_url(), "/".join(session_url_parts)) + project_name_parts = self._get_renku_project_name_parts() + session_url_parts = [ + "projects", + project_name_parts["namespace"], + project_name_parts["project"], + "sessions/show", + session_name, + ] + return urllib.parse.urljoin(self._renku_url(), "/".join(session_url_parts)) diff --git a/renku/core/session/session.py b/renku/core/session/session.py index 3a9df80dd6..2ac81dbb79 100644 --- a/renku/core/session/session.py +++ b/renku/core/session/session.py @@ -18,8 +18,7 @@ """Interactive session business logic.""" import webbrowser -from itertools import chain -from typing import List, Optional +from typing import List, Optional, Tuple from pydantic import validate_arguments @@ -40,7 +39,7 @@ def _safe_get_provider(provider: str) -> ISessionProvider: @validate_arguments(config=dict(arbitrary_types_allowed=True)) -def session_list(config_path: Optional[str], provider: Optional[str] = None): +def session_list(config_path: Optional[str], provider: Optional[str] = None) -> Tuple[List[Session], bool, List[str]]: """List interactive sessions.""" def list_sessions(session_provider: ISessionProvider) -> List[Session]: @@ -56,7 +55,20 @@ def list_sessions(session_provider: ISessionProvider) -> List[Session]: providers = [_safe_get_provider(provider)] if provider else get_supported_session_providers() - return list(chain(*map(list_sessions, providers))) + all_sessions = [] + warning_messages = [] + all_local = True + for session_provider in sorted(providers, key=lambda p: p.priority): + try: + sessions = list_sessions(session_provider) + except errors.RenkuException as e: + warning_messages.append(f"Cannot get sessions list from '{session_provider.get_name()}': {e}") + else: + if session_provider.is_remote_provider(): + all_local = False + all_sessions.extend(sessions) + + return all_sessions, all_local, warning_messages @validate_arguments(config=dict(arbitrary_types_allowed=True)) @@ -91,11 +103,10 @@ def session_start( if not provider_api.find_image(image_name, config): communication.confirm( - f"The container image '{image_name}' does not exists. Would you like to build it?", - abort=True, + f"The container image '{image_name}' does not exists. Would you like to build it?", abort=True ) with communication.busy(msg=f"Building image {image_name}"): - _ = provider_api.build_image(project_context.docker_path.parent, image_name, config) + provider_api.build_image(project_context.docker_path.parent, image_name, config) communication.echo(f"Image {image_name} built successfully.") else: if not provider_api.find_image(image_name, config): @@ -115,7 +126,7 @@ def session_start( gpu = gpu_request or get_value("interactive", "gpu_request") with communication.busy(msg="Waiting for session to start..."): - session_name = provider_api.session_start( + provider_message, warning_message = provider_api.session_start( config=config, project_name=project_name, image_name=image_name, @@ -124,8 +135,10 @@ def session_start( disk_request=disk_limit, gpu_request=gpu, ) - communication.echo(msg=f"Session {session_name} successfully started") - return session_name + + if warning_message: + communication.warn(warning_message) + communication.echo(provider_message) @validate_arguments(config=dict(arbitrary_types_allowed=True)) @@ -147,8 +160,21 @@ def stop_sessions(session_provider: ISessionProvider) -> bool: providers = [_safe_get_provider(provider)] if provider else get_supported_session_providers() + is_stopped = False + warning_messages = [] with communication.busy(msg=f"Waiting for {session_detail} to stop..."): - is_stopped = any(map(stop_sessions, providers)) + for session_provider in sorted(providers, key=lambda p: p.priority): + try: + is_stopped = stop_sessions(session_provider) + except errors.RenkuException as e: + warning_messages.append(f"Cannot stop sessions in provider '{session_provider.get_name()}': {e}") + + if is_stopped and session_name: + break + + if warning_messages: + for message in warning_messages: + communication.warn(message) if not is_stopped: if not session_name: diff --git a/renku/core/util/jwt.py b/renku/core/util/jwt.py new file mode 100644 index 0000000000..618fcb72a8 --- /dev/null +++ b/renku/core/util/jwt.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2018-2022 - Swiss Data Science Center (SDSC) +# A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and +# Eidgenössische Technische Hochschule Zürich (ETHZ). +# +# 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. +"""JWT utilities.""" + +from datetime import datetime, timezone + +import jwt + + +def is_token_expired(token: str) -> bool: + """Return True if the given token is expired.""" + try: + decoded_token = jwt.decode(token, options={"verify_signature": False}) + except jwt.DecodeError: + return True + + expiration_date = decoded_token.get("exp", 0) + + # NOTE: ``datetime.utcnow`` doesn't have timezone info, so we use ``datetime.now`` + return expiration_date < datetime.now(tz=timezone.utc).timestamp() diff --git a/renku/domain_model/session.py b/renku/domain_model/session.py index 70027cfbcd..beaa7f14ef 100644 --- a/renku/domain_model/session.py +++ b/renku/domain_model/session.py @@ -21,7 +21,9 @@ from abc import ABCMeta, abstractmethod from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple + +from renku.core.constant import ProviderPriority class Session: @@ -36,11 +38,18 @@ def __init__(self, id: str, status: str, url: str): class ISessionProvider(metaclass=ABCMeta): """Abstract class for a interactive session provider.""" + priority: ProviderPriority = ProviderPriority.NORMAL + @abstractmethod def get_name(self) -> str: """Return session provider's name.""" pass + @abstractmethod + def is_remote_provider(self) -> bool: + """Return True for remote providers (i.e. not local Docker).""" + raise NotImplementedError + @abstractmethod def build_image(self, image_descriptor: Path, image_name: str, config: Optional[Dict[str, Any]]): """Builds the container image. @@ -97,7 +106,7 @@ def session_start( mem_request: Optional[str] = None, disk_request: Optional[str] = None, gpu_request: Optional[str] = None, - ) -> str: + ) -> Tuple[str, str]: """Creates an interactive session. Args: @@ -110,7 +119,7 @@ def session_start( gpu_request(Optional[str]): GPU device request for the session. Returns: - str: a unique id for the created interactive session. + Tuple[str, str]: Provider message and a possible warning message. """ pass diff --git a/renku/ui/cli/init.py b/renku/ui/cli/init.py index 4446aaa7fb..e0791024d3 100644 --- a/renku/ui/cli/init.py +++ b/renku/ui/cli/init.py @@ -69,9 +69,9 @@ ... OK INDEX ID DESCRIPTION PARAMETERS - ----- -------------- -------------------------- ---------------------- - 1 python-minimal Basic Python Project:[...] description: proj[...] - 2 R-minimal Basic R Project: The [...] description: proj[...] + ----- ------ -------------------------- ---------------------- + 1 python Basic Python Project:[...] description: proj[...] + 2 R Basic R Project: The [...] description: proj[...] Please choose a template by typing the index: @@ -86,7 +86,7 @@ .. code-block:: console - $ renku init --template-id python-minimal --parameter \ + $ renku init --template-id python --parameter \ "description"="my new shiny project" Initializing new Renku repository... OK @@ -113,7 +113,7 @@ "@type": "https://schema.org/Organization", \ "https://schema.org/legalName": "ETHZ"}' > metadata.json - $ renku init --template-id python-minimal --parameter \ + $ renku init --template-id python --parameter \ "description"="my new shiny project" --metadata metadata.json Initializing new Renku repository... OK @@ -136,13 +136,13 @@ $ echo "FROM python:3.7-alpine" > Dockerfile $ renku init - INDEX ID PARAMETERS - ------- -------------- ------------ - 1 python-minimal description - 2 R-minimal description - 3 bioc-minimal description - 4 julia-minimal description - 5 minimal + INDEX ID PARAMETERS + ------- --------- ------------ + 1 python description + 2 R description + 3 bioc description + 4 julia description + 5 minimal Please choose a template by typing the index: 1 The template requires a value for "description": Test Project Initializing Git repository... diff --git a/renku/ui/cli/session.py b/renku/ui/cli/session.py index 0c531344ee..10be3458af 100644 --- a/renku/ui/cli/session.py +++ b/renku/ui/cli/session.py @@ -151,6 +151,7 @@ from lazy_object_proxy import Proxy from renku.command.format.session import SESSION_FORMATS +from renku.command.util import WARNING from renku.core import errors from renku.ui.cli.utils.callback import ClickCallback from renku.ui.cli.utils.plugins import get_supported_session_providers_names @@ -187,7 +188,16 @@ def list_sessions(provider, config, format): from renku.command.session import session_list_command result = session_list_command().build().execute(provider=provider, config_path=config) - click.echo(SESSION_FORMATS[format](result.output)) + sessions, all_local, warning_messages = result.output + + click.echo(SESSION_FORMATS[format](sessions)) + + if warning_messages: + click.echo() + if all_local and sessions: + click.echo(WARNING + "Only showing sessions from local provider") + for message in warning_messages: + click.echo(WARNING + message) @session.command("start") @@ -218,21 +228,15 @@ def start(provider, config, image, cpu, disk, gpu, memory): from renku.command.session import session_start_command communicator = ClickCallback() - result = ( - session_start_command() - .with_communicator(communicator) - .build() - .execute( - provider=provider, - config_path=config, - image_name=image, - cpu_request=cpu, - mem_request=memory, - disk_request=disk, - gpu_request=gpu, - ) + session_start_command().with_communicator(communicator).build().execute( + provider=provider, + config_path=config, + image_name=image, + cpu_request=cpu, + mem_request=memory, + disk_request=disk, + gpu_request=gpu, ) - click.echo(result.output) @session.command("stop") diff --git a/renku/ui/cli/utils/color.py b/renku/ui/cli/utils/color.py index 4edd0852ce..d220fb1c6c 100644 --- a/renku/ui/cli/utils/color.py +++ b/renku/ui/cli/utils/color.py @@ -17,7 +17,7 @@ # limitations under the License. """Terminal Color definitions.""" -YELLOW = (196, 160, 0) +YELLOW = "yellow" MAGENTA = "magenta" GREEN = "green" RED = "red" diff --git a/renku/version.py b/renku/version.py index a53942a2ff..303a9ba316 100644 --- a/renku/version.py +++ b/renku/version.py @@ -25,7 +25,7 @@ from importlib_metadata import distribution, version # type: ignore __version__ = version("renku") -__template_version__ = "0.3.5" +__template_version__ = "0.4.1" __minimum_project_version__ = "1.7.0" diff --git a/tests/cli/test_init.py b/tests/cli/test_init.py index f40f1aa10c..cec12f7836 100644 --- a/tests/cli/test_init.py +++ b/tests/cli/test_init.py @@ -67,8 +67,8 @@ def test_template_selection_helpers(isolated_runner): assert "Please choose a template by typing its index:" in stripped_output - assert "1 python-minimal" in stripped_output - assert "2 R-minimal" in stripped_output + assert "1 python" in stripped_output + assert "2 R" in stripped_output def test_init(isolated_runner, project_init): diff --git a/tests/cli/test_migrate.py b/tests/cli/test_migrate.py index 2a44a3d10b..8bcfd1dbe9 100644 --- a/tests/cli/test_migrate.py +++ b/tests/cli/test_migrate.py @@ -142,7 +142,7 @@ def test_migrations_runs(isolated_runner, old_project): assert "Successfully applied" in result.output assert "OK" in result.output - result = isolated_runner.invoke(cli, ["migrate"]) + result = isolated_runner.invoke(cli, ["migrate", "--skip-template-update"]) assert 0 == result.exit_code, format_result_exception(result) assert "No migrations required." in result.output diff --git a/tests/cli/test_session.py b/tests/cli/test_session.py index 16eeb4afc8..b0d978601b 100644 --- a/tests/cli/test_session.py +++ b/tests/cli/test_session.py @@ -17,6 +17,7 @@ # limitations under the License. """Test ``service`` command.""" +import re from unittest.mock import MagicMock from renku.ui.cli import cli @@ -34,9 +35,9 @@ def test_session_up_down(runner, project, dummy_session_provider, monkeypatch): for _ in range(3): result = runner.invoke(cli, ["session", "start", "-p", "dummy"]) assert 0 == result.exit_code, format_result_exception(result) - assert "successfully started" in result.output + assert "session-random-" in result.output - session_id = result.output.splitlines()[-1] + session_id = re.findall(r".*(session-random-.*-name).*", result.output, re.MULTILINE)[0] result = runner.invoke(cli, ["session", "ls", "-p", "dummy"]) assert 0 == result.exit_code, format_result_exception(result) diff --git a/tests/cli/test_template.py b/tests/cli/test_template.py index 2f629ab51a..b7c362d0f1 100644 --- a/tests/cli/test_template.py +++ b/tests/cli/test_template.py @@ -46,7 +46,7 @@ def test_template_list(isolated_runner): result = isolated_runner.invoke(cli, command, replace_argv=False) assert 0 == result.exit_code, format_result_exception(result) - assert "python-minimal" in result.output + assert "python" in result.output finally: sys.argv = argv @@ -62,14 +62,14 @@ def test_template_list_from_source(isolated_runner): result = isolated_runner.invoke(cli, command + ["--source", TEMPLATES_URL]) assert 0 == result.exit_code, format_result_exception(result) - assert "python-minimal" in result.output - assert "julia-minimal" in result.output + assert "python" in result.output + assert "julia" in result.output result = isolated_runner.invoke(cli, command + ["-s", TEMPLATES_URL, "--reference", "0.3.2"]) assert 0 == result.exit_code, format_result_exception(result) - assert "python-minimal" in result.output - assert "julia-minimal" in result.output + assert "python" in result.output + assert "julia" in result.output finally: sys.argv = argv @@ -81,7 +81,7 @@ def test_template_show(isolated_runner): sys.argv = command try: - result = isolated_runner.invoke(cli, command + ["R-minimal"]) + result = isolated_runner.invoke(cli, command + ["R"]) assert 0 == result.exit_code, format_result_exception(result) assert re.search("^Name: Basic R (.*) Project$", result.output, re.MULTILINE) is not None @@ -135,18 +135,18 @@ def test_template_set_failure(runner, project, with_injection): assert 1 == result.exit_code, format_result_exception(result) assert "Project already has a template" in result.output with with_injection(): - assert "python-minimal" == project_context.project.template_metadata.template_id + assert "python" == project_context.project.template_metadata.template_id def test_template_set(runner, project, with_injection): """Test setting a new template in a project.""" from renku.version import __template_version__ - result = runner.invoke(cli, ["template", "set", "--force", "R-minimal"]) + result = runner.invoke(cli, ["template", "set", "--force", "R"]) assert 0 == result.exit_code, format_result_exception(result) with with_injection(): - assert "R-minimal" == project_context.project.template_metadata.template_id + assert "R" == project_context.project.template_metadata.template_id assert __template_version__ == project_context.project.template_metadata.template_version assert __template_version__ == project_context.project.template_metadata.template_ref @@ -158,11 +158,11 @@ def test_template_set_overwrites_modified(runner, project, with_injection): """Test setting a new template in a project overwrite modified files.""" write_and_commit_file(project.repository, "Dockerfile", "my-modifications") - result = runner.invoke(cli, ["template", "set", "--force", "R-minimal"]) + result = runner.invoke(cli, ["template", "set", "--force", "R"]) assert 0 == result.exit_code, format_result_exception(result) with with_injection(): - assert "R-minimal" == project_context.project.template_metadata.template_id + assert "R" == project_context.project.template_metadata.template_id assert "my-modifications" not in (project.path / "Dockerfile").read_text() assert not project.repository.is_dirty(untracked_files=True) @@ -172,11 +172,11 @@ def test_template_set_interactive(runner, project, with_injection, overwrite, fo """Test setting a template in interactive mode.""" write_and_commit_file(project.repository, "Dockerfile", "my-modifications") - result = runner.invoke(cli, ["template", "set", "-f", "R-minimal", "-i"], input=f"{overwrite}\n" * 420) + result = runner.invoke(cli, ["template", "set", "-f", "R", "-i"], input=f"{overwrite}\n" * 420) assert 0 == result.exit_code, format_result_exception(result) with with_injection(): - assert "R-minimal" == project_context.project.template_metadata.template_id + assert "R" == project_context.project.template_metadata.template_id assert ("my-modifications" in (project.path / "Dockerfile").read_text()) is found assert not project.repository.is_dirty(untracked_files=True) @@ -187,7 +187,7 @@ def test_template_set_preserve_renku_version(runner, project): new_content = re.sub(r"^\s*ARG RENKU_VERSION=(.+)$", "ARG RENKU_VERSION=0.0.42", content, flags=re.MULTILINE) write_and_commit_file(project.repository, "Dockerfile", new_content) - result = runner.invoke(cli, ["template", "set", "-f", "R-minimal", "--interactive"], input="y\n" * 420) + result = runner.invoke(cli, ["template", "set", "-f", "R", "--interactive"], input="y\n" * 420) assert 0 == result.exit_code, format_result_exception(result) @@ -212,7 +212,7 @@ def test_template_set_uses_renku_version_when_non_existing(tmpdir, runner): assert "RENKU_VERSION" not in project_context.docker_path.read_text() - assert 0 == runner.invoke(cli, ["template", "set", "python-minimal"]).exit_code + assert 0 == runner.invoke(cli, ["template", "set", "python"]).exit_code assert f"RENKU_VERSION={__version__}" in project_context.docker_path.read_text() @@ -221,7 +221,7 @@ def test_template_set_dry_run(runner, project): """Test set dry-run doesn't make any changes.""" commit_sha_before = project.repository.head.commit.hexsha - result = runner.invoke(cli, ["template", "set", "-f", "R-minimal", "--dry-run"]) + result = runner.invoke(cli, ["template", "set", "-f", "R", "--dry-run"]) assert 0 == result.exit_code, format_result_exception(result) assert not project.repository.is_dirty() @@ -232,25 +232,23 @@ def test_template_set_dry_run(runner, project): def test_template_update(runner, project, with_injection): """Test updating a template.""" result = runner.invoke( - cli, - ["template", "set", "-f", "python-minimal", "-s", TEMPLATES_URL, "-r", "0.3.2"] - + ["-p", "description=fixed-version"], + cli, ["template", "set", "-f", "python", "-s", TEMPLATES_URL, "-r", "0.4.0", "-p", "description=fixed-version"] ) assert 0 == result.exit_code, format_result_exception(result) with with_injection(): - assert "python-minimal" == project_context.project.template_metadata.template_id - assert "0.3.2" == project_context.project.template_metadata.template_ref - assert "b9ab266fba136bdecfa91dc8d7b6d36b9d427012" == project_context.project.template_metadata.template_version + assert "python" == project_context.project.template_metadata.template_id + assert "0.4.0" == project_context.project.template_metadata.template_ref + assert "5df0ae30086e6ac46ff2b1cc038f3e2dfcf74962" == project_context.project.template_metadata.template_version result = runner.invoke(cli, ["template", "update"]) assert 0 == result.exit_code, format_result_exception(result) assert "Template is up-to-date" not in result.output with with_injection(): - assert "python-minimal" == project_context.project.template_metadata.template_id - assert Version(project_context.project.template_metadata.template_ref) > Version("0.3.2") - assert "6c59d8863841baeca8f30062fd16c650cf67da3b" != project_context.project.template_metadata.template_version + assert "python" == project_context.project.template_metadata.template_id + assert Version(project_context.project.template_metadata.template_ref) > Version("0.4.0") + assert "5df0ae30086e6ac46ff2b1cc038f3e2dfcf74962" != project_context.project.template_metadata.template_version result = runner.invoke(cli, ["template", "update"]) @@ -284,8 +282,7 @@ def test_template_update_dry_run(runner, project): """Test update dry-run doesn't make any changes.""" result = runner.invoke( cli, - ["template", "set", "-f", "python-minimal", "-s", TEMPLATES_URL, "-r", "0.3.2"] - + ["-p", "description=fixed-version"], + ["template", "set", "-f", "python", "-s", TEMPLATES_URL, "-r", "0.4.0", "-p", "description=fixed-version"], ) assert 0 == result.exit_code, format_result_exception(result) diff --git a/tests/core/commands/test_merge.py b/tests/core/commands/test_merge.py index 2ca86bc8d6..83b7774930 100644 --- a/tests/core/commands/test_merge.py +++ b/tests/core/commands/test_merge.py @@ -310,7 +310,7 @@ def test_merge_project_both_template_changed(mocker): creator=Person.from_string("John Doe "), name="my-project", template_metadata=ProjectTemplateMetadata( - template_id="python-minimal", + template_id="python", template_source="renku", template_ref="master", template_version="abcdef", @@ -320,7 +320,7 @@ def test_merge_project_both_template_changed(mocker): creator=Person.from_string("John Doe "), name="my-project", template_metadata=ProjectTemplateMetadata( - template_id="python-minimal1", + template_id="python1", template_source="renku1", template_ref="master1", template_version="12345", @@ -330,7 +330,7 @@ def test_merge_project_both_template_changed(mocker): creator=Person.from_string("John Doe "), name="my-project", template_metadata=ProjectTemplateMetadata( - template_id="python-minimal2", + template_id="python2", template_source="renku2", template_ref="master2", template_version="78910", @@ -356,7 +356,7 @@ def test_merge_project_local_template_changed(): creator=Person.from_string("John Doe "), name="my-project", template_metadata=ProjectTemplateMetadata( - template_id="python-minimal", + template_id="python", template_source="renku", template_ref="master", template_version="abcdef", @@ -366,7 +366,7 @@ def test_merge_project_local_template_changed(): creator=Person.from_string("John Doe "), name="my-project", template_metadata=ProjectTemplateMetadata( - template_id="python-minimal1", + template_id="python1", template_source="renku1", template_ref="master1", template_version="12345", @@ -376,7 +376,7 @@ def test_merge_project_local_template_changed(): creator=Person.from_string("John Doe "), name="my-project", template_metadata=ProjectTemplateMetadata( - template_id="python-minimal", + template_id="python", template_source="renku", template_ref="master", template_version="abcdef", @@ -394,7 +394,7 @@ def test_merge_project_remote_template_changed(): creator=Person.from_string("John Doe "), name="my-project", template_metadata=ProjectTemplateMetadata( - template_id="python-minimal", + template_id="python", template_source="renku", template_ref="master", template_version="abcdef", @@ -404,7 +404,7 @@ def test_merge_project_remote_template_changed(): creator=Person.from_string("John Doe "), name="my-project", template_metadata=ProjectTemplateMetadata( - template_id="python-minimal", + template_id="python", template_source="renku", template_ref="master", template_version="abcdef", @@ -414,7 +414,7 @@ def test_merge_project_remote_template_changed(): creator=Person.from_string("John Doe "), name="my-project", template_metadata=ProjectTemplateMetadata( - template_id="python-minimal1", + template_id="python1", template_source="renku1", template_ref="master1", template_version="12345", diff --git a/tests/core/plugins/test_session.py b/tests/core/plugins/test_session.py index 30faeb1d6d..47f795250c 100644 --- a/tests/core/plugins/test_session.py +++ b/tests/core/plugins/test_session.py @@ -38,7 +38,7 @@ def fake_start( disk_request, gpu_request, ): - return "0xdeadbeef" + return "0xdeadbeef", "" def fake_stop(self, project_name, session_name, stop_all): @@ -89,6 +89,7 @@ def test_session_start( parameters, result, with_injection, + mock_communication, ): with patch.multiple( session_provider, @@ -108,7 +109,8 @@ def test_session_start( with pytest.raises(result): session_start(provider=provider_name, config_path=None, **parameters) else: - assert session_start(provider=provider_name, config_path=None, **parameters) == result + session_start(provider=provider_name, config_path=None, **parameters) + assert result in mock_communication.stdout_lines @pytest.mark.parametrize( @@ -170,11 +172,11 @@ def test_session_list( ): with patch.multiple(session_provider, session_list=fake_session_list, **provider_patches): with with_injection(): + provider = provider_name if provider_exists else "no_provider" + if not isinstance(result, list) and issubclass(result, Exception): with pytest.raises(result): - session_list(provider=provider_name if provider_exists else "no_provider", config_path=None) + session_list(provider=provider, config_path=None) else: - assert ( - session_list(provider=provider_name if provider_exists else "no_provider", config_path=None) - == result - ) + sessions, _, _ = session_list(provider=provider, config_path=None) + assert sessions == result diff --git a/tests/fixtures/repository.py b/tests/fixtures/repository.py index 670d67216e..3f132b64ff 100644 --- a/tests/fixtures/repository.py +++ b/tests/fixtures/repository.py @@ -104,7 +104,7 @@ def project(fake_home) -> Generator[RenkuProject, None, None]: with isolated_filesystem(fake_home.parent, delete=True) as project_path: with project_context.with_path(project_path): communication.disable() - result = RenkuRunner().invoke(init, [".", "--template-id", "python-minimal"], "\n", catch_exceptions=False) + result = RenkuRunner().invoke(init, [".", "--template-id", "python"], "\n", catch_exceptions=False) communication.enable() assert 0 == result.exit_code, format_result_exception(result) diff --git a/tests/fixtures/session.py b/tests/fixtures/session.py index 0c96274223..ebea8d3b75 100644 --- a/tests/fixtures/session.py +++ b/tests/fixtures/session.py @@ -17,7 +17,7 @@ # limitations under the License. """Renku session fixtures.""" -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple import pytest @@ -38,6 +38,9 @@ class _DummySessionProvider(ISessionProvider): def get_name(self): return "dummy" + def is_remote_provider(self): + return False + def build_image(self, image_descriptor: Path, image_name: str, config: Optional[Dict[str, Any]]): pass @@ -60,10 +63,10 @@ def session_start( mem_request: Optional[str] = None, disk_request: Optional[str] = None, gpu_request: Optional[str] = None, - ) -> str: - name = uuid4().hex + ) -> Tuple[str, str]: + name = f"session-random-{uuid4().hex}-name" self.sessions.append(name) - return name + return name, "" def session_stop(self, project_name: str, session_name: Optional[str], stop_all: bool) -> bool: if stop_all: diff --git a/tests/fixtures/templates.py b/tests/fixtures/templates.py index f8c2d01342..1102b4464a 100644 --- a/tests/fixtures/templates.py +++ b/tests/fixtures/templates.py @@ -35,7 +35,7 @@ def template_metadata(): yield { "__template_source__": "renku", "__template_ref__": renku_version, - "__template_id__": "python-minimal", + "__template_id__": "python", "__namespace__": "", "__repository__": "", "__project_description__": "no description", @@ -49,7 +49,7 @@ def template(template_metadata): """Yield template data.""" template = { "url": "https://github.com/SwissDataScienceCenter/renku-project-template", - "id": "python-minimal", + "id": "python", "index": 1, "ref": "master", # TODO: Add template parameters here once parameters are added to the template. diff --git a/tests/service/fixtures/service_integration.py b/tests/service/fixtures/service_integration.py index 6020f37fef..6b45e8ef1c 100644 --- a/tests/service/fixtures/service_integration.py +++ b/tests/service/fixtures/service_integration.py @@ -239,7 +239,7 @@ def _mock_owner(self, data, **kwargs): with chdir(remote_repo_checkout_path): result = runner.invoke( - cli, ["init", ".", "--template-id", "python-minimal", "--force"], "\n", catch_exceptions=False + cli, ["init", ".", "--template-id", "python", "--force"], "\n", catch_exceptions=False ) assert 0 == result.exit_code, format_result_exception(result)