From e10abe4191d96a0715c29eb00bbc2f7051af2d15 Mon Sep 17 00:00:00 2001 From: Damien Churchill Date: Thu, 24 Jan 2019 13:30:29 +0000 Subject: [PATCH 01/69] response will always be None, pass in the exception --- nomad/api/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nomad/api/base.py b/nomad/api/base.py index cca3a09..51c7c28 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -158,5 +158,5 @@ def _request(self, method, endpoint, params=None, data=None, json=None, headers= else: raise nomad.api.exceptions.BaseNomadException(response) - except requests.RequestException: - raise nomad.api.exceptions.BaseNomadException(response) + except requests.RequestException as error: + raise nomad.api.exceptions.BaseNomadException(error) From 9b0c6aa4d46e58835293cd91502f27cd34b2140d Mon Sep 17 00:00:00 2001 From: Ryan Vilim Date: Thu, 25 Jul 2019 11:56:57 -0400 Subject: [PATCH 02/69] Rename alocations.md to allocations.md Allocations was spelled wrong, leading to a broken link on the main page --- docs/api/{alocations.md => allocations.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/api/{alocations.md => allocations.md} (100%) diff --git a/docs/api/alocations.md b/docs/api/allocations.md similarity index 100% rename from docs/api/alocations.md rename to docs/api/allocations.md From dd0a5dd80cafa2991d653106ba14fb1acbae155c Mon Sep 17 00:00:00 2001 From: Ilya Stavitsky Date: Mon, 9 Sep 2019 11:43:30 +0300 Subject: [PATCH 03/69] Adding __str__ method to improve errors readability --- nomad/api/exceptions.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nomad/api/exceptions.py b/nomad/api/exceptions.py index 685f4c7..21dc801 100644 --- a/nomad/api/exceptions.py +++ b/nomad/api/exceptions.py @@ -3,23 +3,23 @@ class BaseNomadException(Exception): def __init__(self, nomad_resp): self.nomad_resp = nomad_resp + def __str__(self): + return 'The {0} was raised with following response: {1}.'.format(self.__class__.__name__, self.nomad_resp.text) + class URLNotFoundNomadException(BaseNomadException): """The requeted URL given does not exist""" - def __init__(self, nomad_resp): - self.nomad_resp = nomad_resp - + pass + class URLNotAuthorizedNomadException(BaseNomadException): """The requested URL is not authorized. ACL""" - def __init__(self, nomad_resp): - self.nomad_resp = nomad_resp + pass class BadRequestNomadException(BaseNomadException): """Validation failure and if a parameter is modified in the request, it could potentially succeed.""" - def __init__(self, nomad_resp): - self.nomad_resp = nomad_resp + pass class InvalidParameters(Exception): From fc0975f2cbb29ee2a541e3186c2b173fe9f79b3b Mon Sep 17 00:00:00 2001 From: Eric Lee Date: Wed, 11 Sep 2019 09:27:58 -0400 Subject: [PATCH 04/69] support NOMAD_CLIENT_CERT and NOMAD_CLIENT_KEY https://nomadproject.io/guides/security/securing-nomad.html When using Nomad in a secured fashion, allow the user to specify environment variables NOMAD_CLIENT_CERT and NOMAD_CLIENT_KEY to specify custom a cert and key. This behavior matches that of the official Nomad cli. --- nomad/__init__.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/nomad/__init__.py b/nomad/__init__.py index 0eb04bf..eff880a 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -4,18 +4,19 @@ class Nomad(object): - def __init__(self, - host='127.0.0.1', - secure=False, + def __init__(self, + host='127.0.0.1', + secure=False, port=4646, address=os.getenv('NOMAD_ADDR', None), namespace=os.getenv('NOMAD_NAMESPACE', None), - token=os.getenv('NOMAD_TOKEN', None), - timeout=5, - region=os.getenv('NOMAD_REGION', None), - version='v1', - verify=False, - cert=()): + token=os.getenv('NOMAD_TOKEN', None), + timeout=5, + region=os.getenv('NOMAD_REGION', None), + version='v1', + verify=False, + cert=(os.getenv('NOMAD_CLIENT_CERT', None), + os.getenv('NOMAD_CLIENT_KEY', None))): """ Nomad api client https://github.com/jrxFive/python-nomad/ @@ -51,7 +52,7 @@ def __init__(self, self.version = version self.token = token self.verify = verify - self.cert = cert + self.cert = cert if all(cert) else () self.__namespace = namespace self.requester_settings = { From 8fca9e44c22f440b6be522caa202fdce5c6c9139 Mon Sep 17 00:00:00 2001 From: Ilya Stavitsky Date: Sun, 15 Sep 2019 11:30:59 +0300 Subject: [PATCH 05/69] Removing pass --- nomad/api/exceptions.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/nomad/api/exceptions.py b/nomad/api/exceptions.py index 21dc801..394cd87 100644 --- a/nomad/api/exceptions.py +++ b/nomad/api/exceptions.py @@ -9,17 +9,14 @@ def __str__(self): class URLNotFoundNomadException(BaseNomadException): """The requeted URL given does not exist""" - pass class URLNotAuthorizedNomadException(BaseNomadException): """The requested URL is not authorized. ACL""" - pass class BadRequestNomadException(BaseNomadException): """Validation failure and if a parameter is modified in the request, it could potentially succeed.""" - pass class InvalidParameters(Exception): From 7a349eaadf2a0c297f8bfc318b366230922ed9c2 Mon Sep 17 00:00:00 2001 From: Eric Lee Date: Tue, 8 Oct 2019 19:08:57 -0400 Subject: [PATCH 06/69] add README mention of NOMAD_CLIENT_CERT and NOMAD_CLIENT_KEY --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 91497aa..c614de4 100644 --- a/README.md +++ b/README.md @@ -48,14 +48,16 @@ n.job.deregister_job(j) ## Environment Variables -This library also supports environment variables: `NOMAD_ADDR`, `NOMAD_NAMESPACE`, `NOMAD_TOKEN`, `NOMAD_REGION` for ease of configuration -and unifying with nomad cli tools and other libraries. +This library also supports environment variables: `NOMAD_ADDR`, `NOMAD_NAMESPACE`, `NOMAD_TOKEN`, `NOMAD_REGION`, `NOMAD_CLIENT_CERT`, and `NOMAD_CLIENT_KEY` +for ease of configuration and unifying with nomad cli tools and other libraries. ```bash NOMAD_ADDR=http://127.0.0.1:4646 NOMAD_NAMESPACE=default NOMAD_TOKEN=xxxx-xxxx-xxxx-xxxx NOMAD_REGION=us-east-1a +NOMAD_CLIENT_CERT=/path/to/tls/client.crt +NOMAD_CLIENT_KEY=/path/to/tls/client.key ``` ## Class Dunders From e2aec8079c47827a655aae703fa0e610bb1490e8 Mon Sep 17 00:00:00 2001 From: Atilla Mas Date: Thu, 7 Nov 2019 08:41:31 +0100 Subject: [PATCH 07/69] Fix example --- docs/api/job.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/job.md b/docs/api/job.md index b9ddf42..40b68b8 100644 --- a/docs/api/job.md +++ b/docs/api/job.md @@ -349,7 +349,7 @@ import nomad my_nomad = nomad.Nomad(host='192.168.33.10') -prior_job_version = my_nomad.job.job.get_deployment("example")["JobVersion"] +current_job_version = my_nomad.job.job.get_deployment("example")["JobVersion"] prior_job_version = current_job_version - 1 From 14a9e597371a1e3e83ad4b625c8c31c6fc2b2f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Moser?= Date: Mon, 3 Feb 2020 13:02:21 +0100 Subject: [PATCH 08/69] remove py34 support --- .travis.yml | 1 - setup.py | 1 - 2 files changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7d1b295..96a744e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,6 @@ services: language: python python: - '2.7' -- '3.4' - '3.5' - '3.6' env: diff --git a/setup.py b/setup.py index 8a30cdd..07831bd 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,6 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', ], From c3d1ee79167392dafd355034d70eba0b96c6c4be Mon Sep 17 00:00:00 2001 From: Sherman Mui Date: Tue, 4 Feb 2020 11:19:06 -0800 Subject: [PATCH 09/69] namespaces: client, node --- nomad/api/base.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nomad/api/base.py b/nomad/api/base.py index cca3a09..dcb16ed 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -36,7 +36,9 @@ def _required_namespace(self, endpoint): "allocations", "deployment", "deployments", - "acl" + "acl", + "client", + "node" ] # split 0 -> Api Version # split 1 -> Working Endpoint From 44bff33b8d95f63a2dfab49fb02fa3c67f1ac540 Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Fri, 27 Mar 2020 11:43:17 -0400 Subject: [PATCH 10/69] 1.2.0 release update development status and python version support --- setup.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 07831bd..e009946 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='python-nomad', - version='1.1.0', + version='1.2.0', install_requires=['requests'], packages=['nomad', 'nomad.api'], url='http://github.com/jrxfive/python-nomad', @@ -11,12 +11,14 @@ author_email='jrxfive@gmail.com', description='Client library for Hashicorp Nomad', classifiers=[ - 'Development Status :: 3 - Alpha', + 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', ], keywords='nomad hashicorp client', ) From ca040baa6913e2ca611e04e7d7c1c60867cf319b Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Fri, 27 Mar 2020 11:58:55 -0400 Subject: [PATCH 11/69] ensure python 3.7 and 3.8 are supported/tested against --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 96a744e..c7ec557 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,8 @@ python: - '2.7' - '3.5' - '3.6' +- '3.7' +- '3.8' env: global: - NOMAD_IP="127.0.0.1" From 7006cefeb255c6d014834f9e0ebc981d24d56fd7 Mon Sep 17 00:00:00 2001 From: jonathanrcross Date: Sun, 29 Mar 2020 10:20:27 -0400 Subject: [PATCH 12/69] update encrypted token to deploy to pypi change condition to python 3.7 python. Switch from disutils to setuptools in setup.py (#105) --- .travis.yml | 17 +++++++++-------- setup.py | 9 +++++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index c7ec557..e9f2f2c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,13 @@ sudo: required - deploy: provider: pypi user: jrxfive password: - secure: KJFVOr1iqX1gh61IAzwztkn777Ya1bgN/Et+k5aI4hA+47n9kEPnoJLXP+Fak/OtwSx01oRElRgz/IP5y5yflLrnaTzLY4G7dpUsHf/bwOaWO4UEy43lnpTrjmhHEIUptHtufmue0FYSx4l/Os6XZYOZdUyJtLw+Zr72AuzMQlpgQhdwdjWngvI2FIX8gvoqda7rAJkyZyuW3vRJUdngDvzjWGhy8sRnZwCD56MLEbgnfD08Q2mSxbtHOb3B79EJdGZ3hqGV2AGdYuWfbf2q1yoUCH90AOCzKams8t+JCS/oRAOSlsG1iFMidpjtl+QKBashHrhJ/7xYNrmF49Hj6I8QFpZf4idUZQfo+uhoQd9YzzmEPb/Ys+PPD7Coy8RnD4xQpHQCMe6M+WJhEdFbXlNmjLLaNqm8Vfw9fnpWiFj3d64fZnr/TjTwKDLTVrITbEr95hs0jHx65VCon8ef2Yyt/w/rL66Nz7Skk7YzATloGaMNaennB7MzTxsTdjIvHmBFh2NlhRIAznYTzABewn7ktXHY6bAr7gIJRBr+2pdjQ+BMmtbvimHlUZ75xh94JpDv6vZKdyu2nkm9j444dwBEbCuqa8gPLasiik7OeKSoT0NtofwESb3lliHWsMDM1+kPx6aj1GYS1Kp7WndxVIKd3bEibhBgZY4JmuSWyuo= + secure: Wwy6E8My1CXssswhsVOZZDnEL/cGYFnZVF/Pq/X9T1DmRuVnCDOMmZ43t10ASXt4C7pCUVdhmlmnwbyOvNHbhMQ79EXH7Bqno7AnP2C8mC+l7u3KXqrXpvCQ+lIno8JwS3Wp3DQas+ROQ3VlyLenqoR+4bihWDQc573wwH8XM+9RM9jJcuwg2xc30sJAxG624loyD6S4BjMIit2ND/+MgupbiG69nBx9yWLrdJKj1BF0YO9hmumZ928asM+UdtrU2QtVT84g8I2aC5tM34kRZRiM/OQdApm93BVGwlo1hmAQ1YHJevZflMGSAouJjieOVxl4hkN+gYVyHxO1YjnzjgCTWzbKMtmCr7KtputclRwrf7wrsxl6FRn2HmOKPxLfY9CnVjv6r2e64iiYWmj4g8J/OgI1TD8B6k4e/ca2Oen/iu8/uiZWLT0XEOAtWLxpAc7kGwcfbXnfVNRUPgQY0ePLsI+Lp3VY7hh65YQQDaHqH+/Ib/MNS1XAhZMrMM1iJ4YgNMsmLuBXXI63Ef3T/T4Tw7plydw6jFy2Z2tyIG8/IFnV4t8CofvThX/Wfof8fX0YphuIFPGJZVzVRW+FSQbx8iniTDeu1Q7d76RxXYdoMd5Aesbn/dwWX22KLHO/x2gKlBNjPIVuvx4XxIsQGsan/7u1582MEC681AjggE4= on: tags: true repo: jrxFive/python-nomad - condition: $TRAVIS_PYTHON_VERSION = "2.7" - + condition: $TRAVIS_PYTHON_VERSION = "3.7" services: - docker language: python @@ -35,15 +33,18 @@ before_install: - curl -L -o /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip - yes | unzip -d /tmp /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip - MAJOR_VERSION=`echo ${NOMAD_VERSION} | cut -d "." -f 2` -- if [[ ${MAJOR_VERSION} -gt 6 ]]; then echo "Nomad version $NOMAD_VERSION supports acls";export ACL_ENABLED="--acl-enabled"; else echo "Nomad version $NOMAD_VERSION";export ACL_ENABLED=""; fi -- /tmp/nomad agent -dev -bind ${NOMAD_IP} -node pynomad1 ${ACL_ENABLED} > /dev/null 2>&1 & +- if [[ ${MAJOR_VERSION} -gt 6 ]]; then echo "Nomad version $NOMAD_VERSION supports + acls";export ACL_ENABLED="--acl-enabled"; else echo "Nomad version $NOMAD_VERSION";export + ACL_ENABLED=""; fi +- "/tmp/nomad agent -dev -bind ${NOMAD_IP} -node pynomad1 ${ACL_ENABLED} > /dev/null + 2>&1 &" - sleep 30 install: - pip install -r requirements.txt -r requirements-dev.txt - pip install codecov before_script: - - /tmp/nomad init - - /tmp/nomad run -output example.nomad > example.json +- "/tmp/nomad init" +- "/tmp/nomad run -output example.nomad > example.json" script: - py.test --cov=nomad --cov-report=term-missing --runxfail tests/ after_success: diff --git a/setup.py b/setup.py index e009946..fd6b9a7 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,9 @@ -from distutils.core import setup +import setuptools -setup( +with open("README.md", "r") as fh: + long_description = fh.read() + +setuptools.setup( name='python-nomad', version='1.2.0', install_requires=['requests'], @@ -10,6 +13,8 @@ author='jrxfive', author_email='jrxfive@gmail.com', description='Client library for Hashicorp Nomad', + long_description=long_description, + long_description_content_type="text/markdown", classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', From e038c688d3b38ef5f2ec944516769e1884e45ded Mon Sep 17 00:00:00 2001 From: jonathanrcross Date: Sun, 29 Mar 2020 11:07:25 -0400 Subject: [PATCH 13/69] switch user to token, add wheel and skip existing options (#106) --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e9f2f2c..92759ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,9 @@ sudo: required deploy: provider: pypi - user: jrxfive + user: __token__ + distributions: "sdist bdist_wheel" + skip_existing: true password: secure: Wwy6E8My1CXssswhsVOZZDnEL/cGYFnZVF/Pq/X9T1DmRuVnCDOMmZ43t10ASXt4C7pCUVdhmlmnwbyOvNHbhMQ79EXH7Bqno7AnP2C8mC+l7u3KXqrXpvCQ+lIno8JwS3Wp3DQas+ROQ3VlyLenqoR+4bihWDQc573wwH8XM+9RM9jJcuwg2xc30sJAxG624loyD6S4BjMIit2ND/+MgupbiG69nBx9yWLrdJKj1BF0YO9hmumZ928asM+UdtrU2QtVT84g8I2aC5tM34kRZRiM/OQdApm93BVGwlo1hmAQ1YHJevZflMGSAouJjieOVxl4hkN+gYVyHxO1YjnzjgCTWzbKMtmCr7KtputclRwrf7wrsxl6FRn2HmOKPxLfY9CnVjv6r2e64iiYWmj4g8J/OgI1TD8B6k4e/ca2Oen/iu8/uiZWLT0XEOAtWLxpAc7kGwcfbXnfVNRUPgQY0ePLsI+Lp3VY7hh65YQQDaHqH+/Ib/MNS1XAhZMrMM1iJ4YgNMsmLuBXXI63Ef3T/T4Tw7plydw6jFy2Z2tyIG8/IFnV4t8CofvThX/Wfof8fX0YphuIFPGJZVzVRW+FSQbx8iniTDeu1Q7d76RxXYdoMd5Aesbn/dwWX22KLHO/x2gKlBNjPIVuvx4XxIsQGsan/7u1582MEC681AjggE4= on: From ff6e8dcefc5729a9d51f04ce1f791ecfc721cdef Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Mon, 30 Mar 2020 21:04:54 -0400 Subject: [PATCH 14/69] add safety check to verify that nomad_resp is indeed an requests.Response object and has text attribute. If not stringify given exception/value. --- nomad/api/exceptions.py | 8 +++++++- tests/test_base.py | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/nomad/api/exceptions.py b/nomad/api/exceptions.py index 394cd87..1ffd818 100644 --- a/nomad/api/exceptions.py +++ b/nomad/api/exceptions.py @@ -1,10 +1,16 @@ +import requests + + class BaseNomadException(Exception): """General Error occurred when interacting with nomad API""" def __init__(self, nomad_resp): self.nomad_resp = nomad_resp def __str__(self): - return 'The {0} was raised with following response: {1}.'.format(self.__class__.__name__, self.nomad_resp.text) + if isinstance(self.nomad_resp, requests.Response) and hasattr(self.nomad_resp, "text"): + return 'The {0} was raised with following response: {1}.'.format(self.__class__.__name__, self.nomad_resp.text) + else: + return 'The {0} was raised due to the following error: {1}'.format(self.__class__.__name__, str(self.nomad_resp)) class URLNotFoundNomadException(BaseNomadException): diff --git a/tests/test_base.py b/tests/test_base.py index 60b1071..a519d74 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -46,6 +46,13 @@ def test_base_delete_connection_error(): j = n.job.deregister_job("example") +def test_base_raise_exception_(): + n = nomad.Nomad( + host="162.16.10.102", port=common.NOMAD_PORT, timeout=0.001, verify=False) + with pytest.raises(nomad.api.exceptions.BaseNomadException): + j = n.job.deregister_job("example") + + @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") def test_base_get_connnection_not_authorized(): n = nomad.Nomad( From f24cf31341afd2f2af04f397124714ec00f1fcb4 Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Mon, 30 Mar 2020 21:31:49 -0400 Subject: [PATCH 15/69] add tests to ensure no regression for bug --- tests/test_base.py | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/tests/test_base.py b/tests/test_base.py index a519d74..110794f 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,9 +1,13 @@ -import pytest -import tests.common as common -import nomad import os + +import mock +import pytest +import requests import responses +import nomad +import tests.common as common + def test_base_region_qs(): n = nomad.Nomad(host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN, region="random") @@ -46,11 +50,33 @@ def test_base_delete_connection_error(): j = n.job.deregister_job("example") -def test_base_raise_exception_(): - n = nomad.Nomad( - host="162.16.10.102", port=common.NOMAD_PORT, timeout=0.001, verify=False) - with pytest.raises(nomad.api.exceptions.BaseNomadException): - j = n.job.deregister_job("example") +@mock.patch("nomad.api.base.requests.Session") +def test_base_raise_exception_not_requests_response_object(mock_requests): + mock_requests().delete.side_effect = [requests.RequestException()] + + try: + n = nomad.Nomad( + host="162.16.10.102", + port=common.NOMAD_PORT, + timeout=0.001, + verify=False + ) + + _ = n.job.deregister_job("example") + + except nomad.api.exceptions.BaseNomadException as err: + assert hasattr(err, "text") is False + assert isinstance(err.nomad_resp, requests.RequestException) + assert "raised due" in str(err) + + +def test_base_raise_exception_is_requests_response_object(nomad_setup): + try: + _ = nomad_setup.job.deregister_job("example") + except nomad.api.exceptions.BaseNomadException as err: + assert hasattr(err, "text") is True + assert isinstance(err.nomad_resp, requests.Response) + assert "raised with" in str(err) @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") From b7e3c09809d69680dc5af8d9458b8f578eb91159 Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Tue, 31 Mar 2020 09:00:52 -0400 Subject: [PATCH 16/69] switch to pytest.raises to ensure that assertions are raised and not silently passing --- tests/test_base.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/test_base.py b/tests/test_base.py index 110794f..efe1c40 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -54,7 +54,7 @@ def test_base_delete_connection_error(): def test_base_raise_exception_not_requests_response_object(mock_requests): mock_requests().delete.side_effect = [requests.RequestException()] - try: + with pytest.raises(nomad.api.exceptions.BaseNomadException) as excinfo: n = nomad.Nomad( host="162.16.10.102", port=common.NOMAD_PORT, @@ -64,19 +64,24 @@ def test_base_raise_exception_not_requests_response_object(mock_requests): _ = n.job.deregister_job("example") - except nomad.api.exceptions.BaseNomadException as err: - assert hasattr(err, "text") is False - assert isinstance(err.nomad_resp, requests.RequestException) - assert "raised due" in str(err) + # excinfo is a ExceptionInfo instance, which is a wrapper around the actual exception raised. + # The main attributes of interest are .type, .value and .traceback. + # https://docs.pytest.org/en/3.0.1/assert.html#assertions-about-expected-exceptions + assert hasattr(excinfo.value.nomad_resp, "text") is False + assert isinstance(excinfo.value.nomad_resp, requests.RequestException) + assert "raised due" in str(excinfo) def test_base_raise_exception_is_requests_response_object(nomad_setup): - try: - _ = nomad_setup.job.deregister_job("example") - except nomad.api.exceptions.BaseNomadException as err: - assert hasattr(err, "text") is True - assert isinstance(err.nomad_resp, requests.Response) - assert "raised with" in str(err) + with pytest.raises(nomad.api.exceptions.BaseNomadException) as excinfo: + _ = nomad_setup.job.get_job("examplezz") + + # excinfo is a ExceptionInfo instance, which is a wrapper around the actual exception raised. + # The main attributes of interest are .type, .value and .traceback. + # https://docs.pytest.org/en/3.0.1/assert.html#assertions-about-expected-exceptions + assert hasattr(excinfo.value.nomad_resp, "text") is True + assert isinstance(excinfo.value.nomad_resp, requests.Response) + assert "raised with" in str(excinfo) @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") From c758391b7e367bb67fbaa04947460b945da9b7a7 Mon Sep 17 00:00:00 2001 From: jonathanrcross Date: Wed, 1 Apr 2020 08:57:12 -0400 Subject: [PATCH 17/69] bump version to 1.2.1 for exception hotfix (#110) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fd6b9a7..41cfe13 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name='python-nomad', - version='1.2.0', + version='1.2.1', install_requires=['requests'], packages=['nomad', 'nomad.api'], url='http://github.com/jrxfive/python-nomad', From d0556f6504585c81fd3c8aaeec6d07bb46c9e46b Mon Sep 17 00:00:00 2001 From: Jeroen Knitel Date: Thu, 30 Apr 2020 18:49:51 +0200 Subject: [PATCH 18/69] Support stopping an allocation --- docs/api/allocation.md | 16 ++++++++++++++++ nomad/api/allocation.py | 12 ++++++++++++ tests/test_allocation.py | 5 +++++ 3 files changed, 33 insertions(+) diff --git a/docs/api/allocation.md b/docs/api/allocation.md index 524817c..2c331d2 100644 --- a/docs/api/allocation.md +++ b/docs/api/allocation.md @@ -15,3 +15,19 @@ allocation = my_nomad.allocation.get_allocation('32c54571-fb79-97d2-ee38-16673ba print (allocation) ``` + +### Stop an allocation + +This endpoint stops and reschedules a specific allocation. + +https://www.nomadproject.io/api-docs/allocations/#stop-allocation + +Example of stopping an allocation + +``` +import nomad + +my_nomad = nomad.Nomad(host='192.168.33.10') + +my_nomad.allocation.stop_allocation('32c54571-fb79-97d2-ee38-16673bab692c') +``` diff --git a/nomad/api/allocation.py b/nomad/api/allocation.py index 30b23ef..2967d30 100644 --- a/nomad/api/allocation.py +++ b/nomad/api/allocation.py @@ -55,3 +55,15 @@ def get_allocation(self, id): - nomad.api.exceptions.URLNotFoundNomadException """ return self.request(id, method="get").json() + + def stop_allocation(self, id): + """ Stop a specific allocation. + + https://www.nomadproject.io/api-docs/allocations/#stop-allocation + + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + """ + return self.request(id, "stop", method="post").json() diff --git a/tests/test_allocation.py b/tests/test_allocation.py index c8c9a36..9dcdbe1 100644 --- a/tests/test_allocation.py +++ b/tests/test_allocation.py @@ -19,6 +19,11 @@ def test_get_allocation(nomad_setup): assert isinstance(nomad_setup.allocation.get_allocation(id), dict) == True +def test_stop_allocation(nomad_setup): + id = nomad_setup.job.get_allocations("example")[0]["ID"] + assert isinstance(nomad_setup.allocation.stop_allocation(id), dict) == True + + def test_dunder_getitem_exist(nomad_setup): id = nomad_setup.job.get_allocations("example")[0]["ID"] a = nomad_setup.allocation[id] From d5788ffdcd3906c69bc166937b6873720bddc0be Mon Sep 17 00:00:00 2001 From: Jeroen Knitel Date: Fri, 1 May 2020 15:39:57 +0200 Subject: [PATCH 19/69] Stop allocation support is present on Nomad >= 0.9.2 --- tests/test_allocation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_allocation.py b/tests/test_allocation.py index 9dcdbe1..33173fe 100644 --- a/tests/test_allocation.py +++ b/tests/test_allocation.py @@ -3,6 +3,7 @@ import uuid import responses import tests.common as common +import os # integration tests requires nomad Vagrant VM or Binary running @@ -18,7 +19,7 @@ def test_get_allocation(nomad_setup): id = nomad_setup.job.get_allocations("example")[0]["ID"] assert isinstance(nomad_setup.allocation.get_allocation(id), dict) == True - +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 9, 2), reason="Nomad alloc stop not supported") def test_stop_allocation(nomad_setup): id = nomad_setup.job.get_allocations("example")[0]["ID"] assert isinstance(nomad_setup.allocation.stop_allocation(id), dict) == True From 1cf9840a041be9d2ae22b7c3097e7aa232f2e52c Mon Sep 17 00:00:00 2001 From: Jeroen Knitel Date: Wed, 6 May 2020 21:15:07 +0200 Subject: [PATCH 20/69] Adhere to PEP8 --- tests/test_allocation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_allocation.py b/tests/test_allocation.py index 33173fe..0d02137 100644 --- a/tests/test_allocation.py +++ b/tests/test_allocation.py @@ -19,6 +19,7 @@ def test_get_allocation(nomad_setup): id = nomad_setup.job.get_allocations("example")[0]["ID"] assert isinstance(nomad_setup.allocation.get_allocation(id), dict) == True + @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 9, 2), reason="Nomad alloc stop not supported") def test_stop_allocation(nomad_setup): id = nomad_setup.job.get_allocations("example")[0]["ID"] From 33ab459566cd1e4a8a9408298db284802b736dc3 Mon Sep 17 00:00:00 2001 From: phreakocious Date: Thu, 11 Feb 2021 08:49:57 -0600 Subject: [PATCH 21/69] add function for allocation restart endpoint --- nomad/api/client.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/nomad/api/client.py b/nomad/api/client.py index f7784ba..4178f57 100644 --- a/nomad/api/client.py +++ b/nomad/api/client.py @@ -300,6 +300,18 @@ def read_allocation_stats(self, id): """ return self.request(id, "stats", method="get").json() + def restart_allocation(self, id): + """ Restart a specific allocation. + + https://www.nomadproject.io/api-docs/allocations/#restart-allocation + + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + """ + return self.request(id, "restart", method="post").json() + class gc_allocation(Requester): From 73701a47740c0220be222a5420211894c8896701 Mon Sep 17 00:00:00 2001 From: Damanpreet Singh Date: Tue, 20 Apr 2021 10:36:06 +0530 Subject: [PATCH 22/69] added support for namespace in get_jobs --- nomad/api/jobs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nomad/api/jobs.py b/nomad/api/jobs.py index c3abb03..de3ee06 100644 --- a/nomad/api/jobs.py +++ b/nomad/api/jobs.py @@ -63,19 +63,21 @@ def __iter__(self): jobs = self.get_jobs() return iter(jobs) - def get_jobs(self, prefix=None): + def get_jobs(self, prefix=None, namespace=None): """ Lists all the jobs registered with Nomad. https://www.nomadproject.io/docs/http/jobs.html arguments: - prefix :(str) optional, specifies a string to filter jobs on based on an prefix. This is specified as a querystring parameter. + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. returns: list raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - params = {"prefix": prefix} + params = {"prefix": prefix, "namespace": namespace} return self.request(method="get", params=params).json() def register_job(self, job): From fcaa4a7e55e1a510cc8f611d8e1e575fc9220ce3 Mon Sep 17 00:00:00 2001 From: amirsaar Date: Mon, 31 May 2021 20:19:41 +0300 Subject: [PATCH 23/69] Documentation fix 'drain_node_with_spec' wasn't documented correctly, documentation said to use `drain_node` which doesn't support the spec in it's signature --- docs/api/node.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/api/node.md b/docs/api/node.md index 91e3d1d..9accd9d 100644 --- a/docs/api/node.md +++ b/docs/api/node.md @@ -86,16 +86,16 @@ import nomad my_nomad = nomad.Nomad(host='192.168.33.10') #enable drain mode -my_nomad.node.drain_node('ed1bbae7-c38a-df2d-1de7-50dbc753fc98', drain_spec={"Duration": "100000000"}) +my_nomad.node.drain_node_with_spec('ed1bbae7-c38a-df2d-1de7-50dbc753fc98', drain_spec={"Duration": "100000000"}) #enable drain mode but leave system jobs on the specificed node -my_nomad.node.drain_node('ed1bbae7-c38a-df2d-1de7-50dbc753fc98', drain_spec={"Duration": "100000000", "IgnoreSystemJobs": True}) +my_nomad.node.drain_node_with_spec('ed1bbae7-c38a-df2d-1de7-50dbc753fc98', drain_spec={"Duration": "100000000", "IgnoreSystemJobs": True}) #disable drain but leave node in an ineligible state -my_nomad.node.drain_node('ed1bbae7-c38a-df2d-1de7-50dbc753fc98', drain_spec={}) +my_nomad.node.drain_node_with_spec('ed1bbae7-c38a-df2d-1de7-50dbc753fc98', drain_spec={}) #disable drain and put node in an eligible state -my_nomad.node.drain_node('ed1bbae7-c38a-df2d-1de7-50dbc753fc98', drain_spec={}, mark_eligible=True) +my_nomad.node.drain_node_with_spec('ed1bbae7-c38a-df2d-1de7-50dbc753fc98', drain_spec={}, mark_eligible=True) ``` ### Eligible Node From 1cc1077a8225cc924999cbb8f74db58f8298a8b4 Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Mon, 30 Aug 2021 11:24:18 -0400 Subject: [PATCH 24/69] update base._query_string_builder to take params as argument (#120) * update base._query_string_builder to take params as argument to avoid overwriting namespace and region if given from caller as params. update requirements-dev pinned versions, remove region from batch_parametrized.json tests failed due to region not existing. Removed some problematic tests that need to be tested in a better manner. * add global for region to parameterized job. Remove python 2.7 and 3.5 from official testing matrix (technically still supported) --- .travis.yml | 4 +--- example_batch_parameterized.json | 2 +- nomad/api/base.py | 12 ++++++---- requirements-dev.txt | 8 +++---- tests/test_base.py | 29 ++++++++++++++++++++-- tests/test_client.py | 41 +++++++------------------------- tests/test_job.py | 9 +++++-- 7 files changed, 55 insertions(+), 50 deletions(-) diff --git a/.travis.yml b/.travis.yml index 92759ea..0ae865d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,6 @@ services: - docker language: python python: -- '2.7' -- '3.5' - '3.6' - '3.7' - '3.8' @@ -50,4 +48,4 @@ before_script: script: - py.test --cov=nomad --cov-report=term-missing --runxfail tests/ after_success: -- codecov +- test $NOMAD_VERSION=="0.8.3" && codecov diff --git a/example_batch_parameterized.json b/example_batch_parameterized.json index 1be8e91..9e100b2 100644 --- a/example_batch_parameterized.json +++ b/example_batch_parameterized.json @@ -1,6 +1,6 @@ { "Job": { - "Region": "example-region", + "Region": "global", "ID": "example-batch", "ParentID": "", "Name": "example-batch", diff --git a/nomad/api/base.py b/nomad/api/base.py index 9c421e9..29e6cf4 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -2,7 +2,6 @@ import nomad.api.exceptions from requests.packages.urllib3.exceptions import InsecureRequestWarning - requests.packages.urllib3.disable_warnings(InsecureRequestWarning) @@ -61,13 +60,16 @@ def _url_builder(self, endpoint): return url - def _query_string_builder(self, endpoint): + def _query_string_builder(self, endpoint, params=None): qs = {} - if self.namespace and self._required_namespace(endpoint): + if not isinstance(params, dict): + params = {} + + if ("namespace" not in params) and (self.namespace and self._required_namespace(endpoint)): qs["namespace"] = self.namespace - if self.region: + if "region" not in params and self.region: qs["region"] = self.region return qs @@ -88,7 +90,7 @@ def request(self, *args, **kwargs): def _request(self, method, endpoint, params=None, data=None, json=None, headers=None, allow_redirects=None): url = self._url_builder(endpoint) - qs = self._query_string_builder(endpoint) + qs = self._query_string_builder(endpoint=endpoint, params=params) if params: params.update(qs) diff --git a/requirements-dev.txt b/requirements-dev.txt index 6dc78cd..4f8b4d2 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,7 @@ -coverage==4.1 -pytest==2.9.1 -pytest-cov==2.2.1 +coverage==5.2.1 +pytest==6.2.4 +pytest-cov==2.12.1 mkdocs==0.15.3 mock==1.2.0 responses==0.9.0 -flaky==3.4.0 \ No newline at end of file +flaky==3.7.0 \ No newline at end of file diff --git a/tests/test_base.py b/tests/test_base.py index efe1c40..08b5711 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -28,6 +28,31 @@ def test_base_region_and_namespace_qs(): assert qs["namespace"] == "test" +def test_base_region_and_namespace_qs_namespace_override(): + n = nomad.Nomad(host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN, region="random", namespace="test") + qs = n.jobs._query_string_builder("v1/jobs", {"namespace": "new-namespace"}) + + assert "namespace" not in qs + assert "region" in qs + assert qs["region"] == "random" + + +def test_base_region_and_namespace_qs_region_override(): + n = nomad.Nomad(host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN, region="random", namespace="test") + qs = n.jobs._query_string_builder("v1/jobs", {"region": "new-region"}) + + assert "region" not in qs + assert "namespace" in qs + assert qs["namespace"] == "test" + + +def test_base_region_and_namespace_qs_overrides_via_params(): + n = nomad.Nomad(host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN, region="random", namespace="test") + qs = n.jobs._query_string_builder("v1/jobs", {"namespace": "new-namespace", "region": "new-region"}) + + assert qs == {} + + # integration tests requires nomad Vagrant VM or Binary running def test_base_get_connection_error(): n = nomad.Nomad( @@ -69,7 +94,7 @@ def test_base_raise_exception_not_requests_response_object(mock_requests): # https://docs.pytest.org/en/3.0.1/assert.html#assertions-about-expected-exceptions assert hasattr(excinfo.value.nomad_resp, "text") is False assert isinstance(excinfo.value.nomad_resp, requests.RequestException) - assert "raised due" in str(excinfo) + assert "raised due" in str(excinfo.value) def test_base_raise_exception_is_requests_response_object(nomad_setup): @@ -81,7 +106,7 @@ def test_base_raise_exception_is_requests_response_object(nomad_setup): # https://docs.pytest.org/en/3.0.1/assert.html#assertions-about-expected-exceptions assert hasattr(excinfo.value.nomad_resp, "text") is True assert isinstance(excinfo.value.nomad_resp, requests.Response) - assert "raised with" in str(excinfo) + assert "raised with" in str(excinfo.value) @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") diff --git a/tests/test_client.py b/tests/test_client.py index d585bb0..19c24a1 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -16,7 +16,14 @@ def test_register_job(nomad_setup): nomad_setup.job.register_job("example", job) assert "example" in nomad_setup.job - time.sleep(20) + max_iterations = 6 + + while nomad_setup.job["example"]["Status"] != "running": + time.sleep(5) + if max_iterations == 0: + raise Exception("register_job, job 'example' did not start") + + max_iterations -= 1 @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 6), reason="Not supported in version") @@ -32,22 +39,6 @@ def test_stat_stat_file(nomad_setup): f = nomad_setup.client.stat.stat_file(a) -@flaky(max_runs=5, min_passes=1) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 6), reason="Not supported in version") -def test_cat_read_file(nomad_setup): - - a = nomad_setup.allocations.get_allocations()[0]["ID"] - f = nomad_setup.client.cat.read_file(a, "/redis/executor.out") - - -@flaky(max_runs=5, min_passes=1) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 1), reason="Not supported in version") -def test_read_file_offset(nomad_setup): - - a = nomad_setup.allocations.get_allocations()[0]["ID"] - _ = nomad_setup.client.read_at.read_file_offset(a, 1, 10, "/redis/executor.out") - - @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version") def test_streamfile_fail(nomad_setup): @@ -56,14 +47,6 @@ def test_streamfile_fail(nomad_setup): _ = nomad_setup.client.stream_file.stream(a, 1, "start", "/redis/executor") #invalid file name -@flaky(max_runs=5, min_passes=1) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version") -def test_streamlogs(nomad_setup): - - a = nomad_setup.allocations.get_allocations()[0]["ID"] - _ = nomad_setup.client.stream_logs.stream(a, "redis", "stderr", False) - - @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 6), reason="Not supported in version") def test_read_stats(nomad_setup): @@ -77,14 +60,6 @@ def test_read_allocation_stats(nomad_setup): f = nomad_setup.client.allocation.read_allocation_stats(a) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version") -def test_gc_allocation_fail(nomad_setup): - - a = nomad_setup.allocations.get_allocations()[0]["ID"] - with pytest.raises(nomad.api.exceptions.URLNotFoundNomadException): - f = nomad_setup.client.gc_allocation.garbage_collect(a) # attempt on non-stopped allocation - - @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version") def test_gc_all_allocations(nomad_setup): diff --git a/tests/test_job.py b/tests/test_job.py index 792f734..f299e95 100644 --- a/tests/test_job.py +++ b/tests/test_job.py @@ -1,7 +1,11 @@ -import pytest -import nomad import json import os + +import pytest +import nomad + +from flaky import flaky + from nomad.api import exceptions @@ -42,6 +46,7 @@ def test_delete_job(nomad_setup): test_register_job(nomad_setup) +@flaky(max_runs=5, min_passes=1) @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 3), reason="Nomad dispatch not supported") def test_dispatch_job(nomad_setup): with open("example_batch_parameterized.json") as fh: From 100e68bba6cd44722e9ea544dd709d27bff21e24 Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Tue, 31 Aug 2021 12:29:51 -0400 Subject: [PATCH 25/69] Rndmh3ro namespace get job with tests (#121) * add support for namespace argument in get_job * fixes #118 * add tests for get_jobs with namespace override. Update responses dependency. * update requests minor version * check for namespace to get_job, if not pass empty dict * add namespace tests for get_job Co-authored-by: Sebastian Gumprich Co-authored-by: Jonathan Cross <> --- .travis.yml | 14 ++++---------- nomad/api/job.py | 11 +++++++++-- nomad/api/jobs.py | 5 ++++- requirements-dev.txt | 4 ++-- requirements.txt | 2 +- tests/test_job.py | 41 +++++++++++++++++++++++++++++++++++++++ tests/test_jobs.py | 46 +++++++++++++++++++++++++++++++++++++++++--- tests/test_node.py | 1 + 8 files changed, 105 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0ae865d..e68f2bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,21 +14,15 @@ services: - docker language: python python: -- '3.6' - '3.7' -- '3.8' env: global: - NOMAD_IP="127.0.0.1" - NOMAD_PORT="4646" + - NOMAD_LATEST="1.1.4" matrix: - - NOMAD_VERSION="0.3.2" - - NOMAD_VERSION="0.4.1" - - NOMAD_VERSION="0.5.6" - - NOMAD_VERSION="0.6.0" - - NOMAD_VERSION="0.7.1" - - NOMAD_VERSION="0.8.1" - - NOMAD_VERSION="0.8.3" + - NOMAD_VERSION="1.0.0" + - NOMAD_VERSION="1.1.4" before_install: - curl -L -o /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip - yes | unzip -d /tmp /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip @@ -48,4 +42,4 @@ before_script: script: - py.test --cov=nomad --cov-report=term-missing --runxfail tests/ after_success: -- test $NOMAD_VERSION=="0.8.3" && codecov +- test $NOMAD_VERSION = $NOMAD_LATEST && codecov diff --git a/nomad/api/job.py b/nomad/api/job.py index f3ee0f7..b1c0a3a 100644 --- a/nomad/api/job.py +++ b/nomad/api/job.py @@ -48,19 +48,26 @@ def __getitem__(self, item): except nomad.api.exceptions.URLNotFoundNomadException: raise KeyError - def get_job(self, id): + def get_job(self, id, namespace=None): """ Query a single job for its specification and status. https://www.nomadproject.io/docs/http/job.html arguments: - id + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, method="get").json() + params = {} + + if namespace: + params["namespace"] = namespace + + return self.request(id, method="get", params=params).json() def get_versions(self, id): """ This endpoint reads information about all versions of a job. diff --git a/nomad/api/jobs.py b/nomad/api/jobs.py index de3ee06..09e6f3a 100644 --- a/nomad/api/jobs.py +++ b/nomad/api/jobs.py @@ -77,7 +77,10 @@ def get_jobs(self, prefix=None, namespace=None): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - params = {"prefix": prefix, "namespace": namespace} + params = {"prefix": prefix} + if namespace: + params["namespace"] = namespace + return self.request(method="get", params=params).json() def register_job(self, job): diff --git a/requirements-dev.txt b/requirements-dev.txt index 4f8b4d2..feb5499 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,5 +3,5 @@ pytest==6.2.4 pytest-cov==2.12.1 mkdocs==0.15.3 mock==1.2.0 -responses==0.9.0 -flaky==3.7.0 \ No newline at end of file +flaky==3.7.0 +responses==0.13.4 diff --git a/requirements.txt b/requirements.txt index c20f36f..a8ed785 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -requests==2.20.0 +requests==2.26.0 diff --git a/tests/test_job.py b/tests/test_job.py index f299e95..a5019e1 100644 --- a/tests/test_job.py +++ b/tests/test_job.py @@ -1,8 +1,12 @@ import json import os +import uuid import pytest +import responses + import nomad +import tests.common as common from flaky import flaky @@ -22,6 +26,43 @@ def test_get_job(nomad_setup): assert isinstance(nomad_setup.job.get_job("example"), dict) == True +@responses.activate +def test_get_jobs_with_namespace_override_no_namespace_declared_on_create_incorrect_declared_namespace(nomad_setup): + responses.add( + responses.GET, + "http://{ip}:{port}/v1/job/18a0f501-41d5-ae43-ff61-1d8ec3ec8314?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + status=200, + json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + ) + + with pytest.raises(exceptions.BaseNomadException): + nomad_setup.job.get_job(id=str(uuid.uuid4())) + + +@responses.activate +def test_get_jobs_with_namespace_override_no_namespace_declared_on_create(nomad_setup): + responses.add( + responses.GET, + "http://{ip}:{port}/v1/job/18a0f501-41d5-ae43-ff61-1d8ec3ec8314?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + status=200, + json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + ) + + nomad_setup.job.get_job(id="18a0f501-41d5-ae43-ff61-1d8ec3ec8314", namespace=common.NOMAD_NAMESPACE) + + +@responses.activate +def test_get_jobs_with_namespace_override_namespace_declared_on_create(nomad_setup_with_namespace): + responses.add( + responses.GET, + "http://{ip}:{port}/v1/job/18a0f501-41d5-ae43-ff61-1d8ec3ec8314?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace="override-namespace"), + status=200, + json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + ) + + nomad_setup_with_namespace.job.get_job(id="18a0f501-41d5-ae43-ff61-1d8ec3ec8314", namespace="override-namespace") + + def test_get_allocations(nomad_setup): j = nomad_setup.job["example"] a = nomad_setup.job.get_allocations("example") diff --git a/tests/test_jobs.py b/tests/test_jobs.py index 7adabc3..860bf58 100644 --- a/tests/test_jobs.py +++ b/tests/test_jobs.py @@ -4,6 +4,10 @@ import responses import tests.common as common + +from nomad.api.exceptions import BaseNomadException + + # integration tests requires nomad Vagrant VM or Binary running def test_register_job(nomad_setup): @@ -72,10 +76,9 @@ def test_dunder_iter(nomad_setup): def test_dunder_len(nomad_setup): assert len(nomad_setup.jobs) >= 0 -@responses.activate -# + # fix No data when you are using namespaces #82 -# +@responses.activate def test_get_jobs_with_namespace(nomad_setup_with_namespace): responses.add( responses.GET, @@ -84,3 +87,40 @@ def test_get_jobs_with_namespace(nomad_setup_with_namespace): json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] ) assert common.NOMAD_NAMESPACE in nomad_setup_with_namespace.jobs.get_jobs()[0]["Namespace"] + + +@responses.activate +def test_get_jobs_with_namespace_override_no_namespace_declared_on_create_incorrect_declared_namespace(nomad_setup): + responses.add( + responses.GET, + "http://{ip}:{port}/v1/jobs?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + status=200, + json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + ) + + with pytest.raises(BaseNomadException): + nomad_setup.jobs.get_jobs(namespace="should-raise") + + +@responses.activate +def test_get_jobs_with_namespace_override_no_namespace_declared_on_create(nomad_setup): + responses.add( + responses.GET, + "http://{ip}:{port}/v1/jobs?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + status=200, + json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + ) + + nomad_setup.jobs.get_jobs(namespace=common.NOMAD_NAMESPACE) + + +@responses.activate +def test_get_jobs_with_namespace_override_namespace_declared_on_create(nomad_setup_with_namespace): + responses.add( + responses.GET, + "http://{ip}:{port}/v1/jobs?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace="override-namespace"), + status=200, + json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + ) + + nomad_setup_with_namespace.jobs.get_jobs(namespace="override-namespace") \ No newline at end of file diff --git a/tests/test_node.py b/tests/test_node.py index 6d02806..d63274b 100644 --- a/tests/test_node.py +++ b/tests/test_node.py @@ -23,6 +23,7 @@ def test_evaluate_node(nomad_setup): assert "EvalIDs" in nomad_setup.node.evaluate_node(nodeID) +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) > (1, 1, 0), reason="Not supported in version") def test_drain_node(nomad_setup): nodeID = nomad_setup.nodes["pynomad1"]["ID"] assert "EvalIDs" in nomad_setup.node.drain_node(nodeID) From e57f6683a217d379947ed34412b27b566730b8ab Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Tue, 31 Aug 2021 13:01:05 -0400 Subject: [PATCH 26/69] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 41cfe13..04315bd 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name='python-nomad', - version='1.2.1', + version='1.3.0', install_requires=['requests'], packages=['nomad', 'nomad.api'], url='http://github.com/jrxfive/python-nomad', From f6dcba8176ded3fdcd5a76d9ded8cab3d3091c02 Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Fri, 3 Sep 2021 16:04:47 -0400 Subject: [PATCH 27/69] Add support for Event Stream. (#122) * Add support for Event Stream. Co-authored-by: Jonathan Cross <> --- README.md | 2 + docs/api/event.md | 81 ++++++++++++++++++++++++++ nomad/__init__.py | 61 +++++++++++--------- nomad/api/__init__.py | 33 ++++++----- nomad/api/base.py | 57 ++++++++++-------- nomad/api/event.py | 125 ++++++++++++++++++++++++++++++++++++++++ nomad/api/exceptions.py | 4 ++ tests/test_event.py | 40 +++++++++++++ 8 files changed, 335 insertions(+), 68 deletions(-) create mode 100644 docs/api/event.md create mode 100644 nomad/api/event.py create mode 100644 tests/test_event.py diff --git a/README.md b/README.md index c614de4..bd61d7b 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ NOMAD_CLIENT_KEY=/path/to/tls/client.key |client|N|N|N|N |evaluation|Y|N|Y|N |evaluations|Y|Y|Y|Y +|event|N|N|N|N |job|Y|N|Y|N |jobs|Y|Y|Y|Y |node|Y|N|Y|N @@ -119,6 +120,7 @@ NOMAD_IP=127.0.0.1 NOMAD_VERSION= py.test --cov=nomad --cov-re - [x] Client [:link:](docs/api/client.md) - [x] Evaluation [:link:](docs/api/evaluation.md) - [x] Evaluations [:link:](docs/api/evaluations.md) + - [x] Event [:link:](docs/api/event.md) - [x] Job [:link:](docs/api/job.md) - [x] Jobs [:link:](docs/api/jobs.md) - [x] Namespace [:link:](docs/api/namespace.md) diff --git a/docs/api/event.md b/docs/api/event.md new file mode 100644 index 0000000..7b235a3 --- /dev/null +++ b/docs/api/event.md @@ -0,0 +1,81 @@ +## Event + +## Event Stream + +This will setup an event stream. To avoid blocking and having more control to the user it will return a +tuple of (threading.Thread, threading.Event and queue.Queue). You can use your own `queue.Queue` if you want +to use LIFO or SimpleQueue or simply extend upon that. + +### Default +Will listen to all topics + +``` +import nomad +n = nomad.Nomad() + +stream, stream_exit_event, events = n.event.stream.get_stream() +stream.start() + +while True: + event = events.get() + print(event) + events.task_done() +``` + +### Set Index, Namespace and Topic(s) of Interest + +``` +import nomad +n = nomad.Nomad() + +stream, stream_exit_event, events = n.event.stream.get_stream(index=0, topic={"Node": "*"}, namespace="not-default") +stream.start() + +while True: + event = events.get() + print(event) + events.task_done() +``` + +### Cancel thread/Optimistically exit +We will use the `stream_exit_event` to get the thread to return/exit gracefully. This isn't immediate +as we have to wait for an event or set an arbitrary timeout value to close/open the connection again. + +In this example we will set `stream_exit_event` right before the timeout, knowing that it needs to re-establish +the connection to the stream. Using a try/except with queue.Queue.get(timeout=) we will check if the thread +is still alive; if it isn't we break the loop. + +``` +import nomad +import threading +import time +import queue + + +def stop_stream(exit_event, timeout): + print("start sleep") + time.sleep(timeout) + print("set exit event") + exit_event.set() + + +n = nomad.Nomad() + +stream, stream_exit_event, events = n.event.stream.get_stream(index=0, topic={"Node": "*"}, timeout=3.2) +stream.start() + +stop = threading.Thread(target=stop_stream, args=(stream_exit_event, 3.0)) +stop.start() + +while True: + if not stream.is_alive(): + print("not alive") + break + + try: + event = events.get(timeout=1.0) + print(event) + events.task_done() + except queue.Empty: + continue +``` \ No newline at end of file diff --git a/nomad/__init__.py b/nomad/__init__.py index eff880a..4c0301c 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -1,22 +1,22 @@ - import nomad.api as api import os + class Nomad(object): def __init__(self, - host='127.0.0.1', - secure=False, - port=4646, - address=os.getenv('NOMAD_ADDR', None), - namespace=os.getenv('NOMAD_NAMESPACE', None), - token=os.getenv('NOMAD_TOKEN', None), - timeout=5, - region=os.getenv('NOMAD_REGION', None), - version='v1', - verify=False, - cert=(os.getenv('NOMAD_CLIENT_CERT', None), - os.getenv('NOMAD_CLIENT_KEY', None))): + host='127.0.0.1', + secure=False, + port=4646, + address=os.getenv('NOMAD_ADDR', None), + namespace=os.getenv('NOMAD_NAMESPACE', None), + token=os.getenv('NOMAD_TOKEN', None), + timeout=5, + region=os.getenv('NOMAD_REGION', None), + version='v1', + verify=False, + cert=(os.getenv('NOMAD_CLIENT_CERT', None), + os.getenv('NOMAD_CLIENT_KEY', None))): """ Nomad api client https://github.com/jrxFive/python-nomad/ @@ -68,28 +68,29 @@ def __init__(self, "region": self.region } - self._jobs = api.Jobs(**self.requester_settings) - self._job = api.Job(**self.requester_settings) - self._nodes = api.Nodes(**self.requester_settings) - self._node = api.Node(**self.requester_settings) - self._allocations = api.Allocations(**self.requester_settings) - self._allocation = api.Allocation(**self.requester_settings) - self._evaluations = api.Evaluations(**self.requester_settings) - self._evaluation = api.Evaluation(**self.requester_settings) + self._acl = api.Acl(**self.requester_settings) self._agent = api.Agent(**self.requester_settings) + self._allocation = api.Allocation(**self.requester_settings) + self._allocations = api.Allocations(**self.requester_settings) self._client = api.Client(**self.requester_settings) - self._deployments = api.Deployments(**self.requester_settings) self._deployment = api.Deployment(**self.requester_settings) + self._deployments = api.Deployments(**self.requester_settings) + self._evaluation = api.Evaluation(**self.requester_settings) + self._evaluations = api.Evaluations(**self.requester_settings) + self._event = api.Event(**self.requester_settings) + self._job = api.Job(**self.requester_settings) + self._jobs = api.Jobs(**self.requester_settings) + self._metrics = api.Metrics(**self.requester_settings) + self._namespace = api.Namespace(**self.requester_settings) + self._namespaces = api.Namespaces(**self.requester_settings) + self._node = api.Node(**self.requester_settings) + self._nodes = api.Nodes(**self.requester_settings) + self._operator = api.Operator(**self.requester_settings) self._regions = api.Regions(**self.requester_settings) + self._sentinel = api.Sentinel(**self.requester_settings) self._status = api.Status(**self.requester_settings) self._system = api.System(**self.requester_settings) - self._operator = api.Operator(**self.requester_settings) self._validate = api.Validate(**self.requester_settings) - self._namespaces = api.Namespaces(**self.requester_settings) - self._namespace = api.Namespace(**self.requester_settings) - self._acl = api.Acl(**self.requester_settings) - self._sentinel = api.Sentinel(**self.requester_settings) - self._metrics = api.Metrics(**self.requester_settings) def get_uri(self): if self.secure: @@ -136,6 +137,10 @@ def evaluations(self): def evaluation(self): return self._evaluation + @property + def event(self): + return self._event + @property def agent(self): return self._agent diff --git a/nomad/api/__init__.py b/nomad/api/__init__.py index b91f125..1f6e873 100644 --- a/nomad/api/__init__.py +++ b/nomad/api/__init__.py @@ -1,24 +1,25 @@ import nomad.api.exceptions -from nomad.api.base import Requester -from nomad.api.jobs import Jobs -from nomad.api.job import Job -from nomad.api.nodes import Nodes -from nomad.api.node import Node +from nomad.api.acl import Acl from nomad.api.agent import Agent -from nomad.api.allocations import Allocations from nomad.api.allocation import Allocation -from nomad.api.evaluations import Evaluations -from nomad.api.evaluation import Evaluation +from nomad.api.allocations import Allocations +from nomad.api.base import Requester from nomad.api.client import Client +from nomad.api.deployment import Deployment +from nomad.api.deployments import Deployments +from nomad.api.evaluation import Evaluation +from nomad.api.evaluations import Evaluations +from nomad.api.event import Event +from nomad.api.job import Job +from nomad.api.jobs import Jobs +from nomad.api.metrics import Metrics +from nomad.api.namespace import Namespace +from nomad.api.namespaces import Namespaces +from nomad.api.node import Node +from nomad.api.nodes import Nodes +from nomad.api.operator import Operator from nomad.api.regions import Regions +from nomad.api.sentinel import Sentinel from nomad.api.status import Status from nomad.api.system import System -from nomad.api.operator import Operator from nomad.api.validate import Validate -from nomad.api.deployments import Deployments -from nomad.api.deployment import Deployment -from nomad.api.namespaces import Namespaces -from nomad.api.namespace import Namespace -from nomad.api.acl import Acl -from nomad.api.sentinel import Sentinel -from nomad.api.metrics import Metrics diff --git a/nomad/api/base.py b/nomad/api/base.py index 29e6cf4..c502be2 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -83,12 +83,14 @@ def request(self, *args, **kwargs): data=kwargs.get("data", None), json=kwargs.get("json", None), headers=kwargs.get("headers", None), - allow_redirects=kwargs.get("allow_redirects", False) + allow_redirects=kwargs.get("allow_redirects", False), + timeout=kwargs.get("timeout", self.timeout), + stream=kwargs.get("stream", False) ) return response - def _request(self, method, endpoint, params=None, data=None, json=None, headers=None, allow_redirects=None): + def _request(self, method, endpoint, params=None, data=None, json=None, headers=None, allow_redirects=None, timeout=None, stream=False): url = self._url_builder(endpoint) qs = self._query_string_builder(endpoint=endpoint, params=params) @@ -109,46 +111,47 @@ def _request(self, method, endpoint, params=None, data=None, json=None, headers= method = method.lower() if method == "get": response = self.session.get( - url=url, - params=params, + allow_redirects=allow_redirects, + cert=self.cert, headers=headers, - timeout=self.timeout, + params=params, + stream=stream, + timeout=timeout, + url=url, verify=self.verify, - cert=self.cert, - allow_redirects=allow_redirects ) elif method == "post": response = self.session.post( - url=url, - params=params, - json=json, - headers=headers, + allow_redirects=allow_redirects, + cert=self.cert, data=data, - timeout=self.timeout, + headers=headers, + json=json, + params=params, + timeout=timeout, + url=url, verify=self.verify, - cert=self.cert, - allow_redirects=allow_redirects ) elif method == "put": response = self.session.put( - url=url, - params=params, - json=json, - headers=headers, + cert=self.cert, data=data, + headers=headers, + json=json, + params=params, + timeout=timeout, + url=url, verify=self.verify, - cert=self.cert, - timeout=self.timeout ) elif method == "delete": response = self.session.delete( - url=url, - params=params, + cert=self.cert, headers=headers, + params=params, + timeout=timeout, + url=url, verify=self.verify, - cert=self.cert, - timeout=self.timeout ) if response.ok: @@ -162,5 +165,11 @@ def _request(self, method, endpoint, params=None, data=None, json=None, headers= else: raise nomad.api.exceptions.BaseNomadException(response) + except requests.exceptions.ConnectionError as error: + if all([stream, timeout]): + raise nomad.api.exceptions.TimeoutNomadException(error) + + raise nomad.api.exceptions.BaseNomadException(error) + except requests.RequestException as error: raise nomad.api.exceptions.BaseNomadException(error) diff --git a/nomad/api/event.py b/nomad/api/event.py new file mode 100644 index 0000000..cdd2f13 --- /dev/null +++ b/nomad/api/event.py @@ -0,0 +1,125 @@ +import json +import threading +import queue + +import requests + +from nomad.api.base import Requester +from nomad.api.exceptions import TimeoutNomadException + + +class Event(object): + + def __str__(self): + return "{0}".format(self.__dict__) + + def __repr__(self): + return "{0}".format(self.__dict__) + + def __getattr__(self, item): + raise AttributeError + + def __init__(self, **kwargs): + self.stream = stream(**kwargs) + + +class stream(Requester): + """ + The /event/stream endpoint is used to stream events generated by Nomad. + + https://www.nomadproject.io/api-docs/events + """ + + ENDPOINT = "event/stream" + + def __init__(self, **kwargs): + super(stream, self).__init__(**kwargs) + + def _get_stream(self, method, params, timeout, event_queue, exit_event): + """ + Used as threading target, to obtain json() value + Args: + method: + params: + timeout: + event_queue: + exit_event: + """ + + while exit_event.is_set() is False: + try: + with self.request(method=method, params=params, timeout=timeout, stream=True) as resp: + for raw_msg in resp.iter_lines(): + msg = json.loads(raw_msg) + + # don't send heartbeats + if msg: + event_queue.put(msg) + + if exit_event.is_set(): + return + + except requests.exceptions.ConnectionError: + continue + + def get_stream(self, index=0, topic=None, namespace=None, event_queue=None, timeout=None): + """ + Usage: + stream, stream_exit_event, events = n.event.stream.get_stream() + stream.start() + + while True: + event = events.get() + print(event) + events.task_done() + + Args: + index: (int), Specifies the index to start streaming events from. If the requested index is no longer + in the buffer the stream will start at the next available index. + + topic: (None or dict), Specifies a topic to subscribe to and filter on. The default is to subscribe to all topics. + Multiple topics may be specified by passing multiple topic parameters. + A valid topic parameter includes a topic type and an optional filter_key separated by a colon :. + As an example ?topic=Deployment:redis would subscribe to all Deployment events for a job redis. + an additional topic &topic=Deployment:web would include deployment events for redis and web. + To only subscribe to Node events a topic parameter of ?topic=Node without a separator value + would be used. ?topic=Node:* is also valid. + + namespace: (str) Specifies the target namespace to filter on. Specifying * includes all namespaces + for event types that support namespaces. + + event_queue: (None or queue.Queue) for thread listener to push events onto. + + timeout: (None or int), override timeout (seconds) so connection is not closed. + Defaults to timeout in constructor if not given. + + Returns: (threading.Thread), (threading.Event) (queue.Queue) + """ + + params = { + "index": index, + } + + if namespace: + params["namespace"] = namespace + + if topic: + params["topic"] = topic + + if event_queue is None: + event_queue = queue.Queue() + + stream_exit_event = threading.Event() + _stream = threading.Thread( + name="python-nomad-event-stream", + target=self._get_stream, + kwargs={ + "method": "get", + "params": params, + "timeout": timeout, + "event_queue": event_queue, + "exit_event": stream_exit_event + } + ) + + return _stream, stream_exit_event, event_queue diff --git a/nomad/api/exceptions.py b/nomad/api/exceptions.py index 1ffd818..99f24aa 100644 --- a/nomad/api/exceptions.py +++ b/nomad/api/exceptions.py @@ -27,3 +27,7 @@ class BadRequestNomadException(BaseNomadException): class InvalidParameters(Exception): """Invalid parameters given""" + + +class TimeoutNomadException(requests.exceptions.RequestException): + """Timeout on request, if using a stream and timeout in conjunction""" diff --git a/tests/test_event.py b/tests/test_event.py new file mode 100644 index 0000000..bcbb4b4 --- /dev/null +++ b/tests/test_event.py @@ -0,0 +1,40 @@ +import json + + +# integration tests requires nomad Vagrant VM or Binary running +def test_register_job(nomad_setup): + + with open("example.json") as fh: + job = json.loads(fh.read()) + nomad_setup.job.register_job("example", job) + assert "example" in nomad_setup.job + + +def test_get_event_stream_default(nomad_setup): + + stream, stream_exit, events = nomad_setup.event.stream.get_stream() + stream.daemon = True + stream.start() + + test_register_job(nomad_setup) + event = events.get(timeout=5) + assert event + assert "Index" in event + + stream_exit.set() + + +def test_get_event_stream_with_customized_topic(nomad_setup): + stream, stream_exit, events = nomad_setup.event.stream.get_stream(topic={"Node": "*"}) + stream.daemon = True + stream.start() + + node_id = nomad_setup.nodes.get_nodes()[0]["ID"] + nomad_setup.node.drain_node(node_id) + + event = events.get(timeout=5) + assert event + assert "Index" in event + assert event["Events"][0]["Type"] in ("NodeRegistration", "NodeDeregistration", "NodeEligibility", "NodeDrain", "NodeEvent") + + stream_exit.set() From 768c57e1d248897d32c6d11497ac4255591034c7 Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Fri, 3 Sep 2021 16:31:30 -0400 Subject: [PATCH 28/69] add exception handling for import queue, python2.7 uses capital Q, bump release version (#123) Co-authored-by: Jonathan Cross <> --- nomad/api/event.py | 6 +++++- setup.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/nomad/api/event.py b/nomad/api/event.py index cdd2f13..658c1ca 100644 --- a/nomad/api/event.py +++ b/nomad/api/event.py @@ -1,12 +1,16 @@ import json import threading -import queue import requests from nomad.api.base import Requester from nomad.api.exceptions import TimeoutNomadException +try: + import queue +except ImportError: + import Queue as queue + class Event(object): diff --git a/setup.py b/setup.py index 04315bd..f95cbdd 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name='python-nomad', - version='1.3.0', + version='1.4.1', install_requires=['requests'], packages=['nomad', 'nomad.api'], url='http://github.com/jrxfive/python-nomad', From 1ae45d8eca21765e9f22b2e36d12282abac6c740 Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Sun, 5 Sep 2021 13:56:48 -0400 Subject: [PATCH 29/69] github-actions --- .github/workflows/main.yml | 88 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..54867e2 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,88 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python Nomad Test and Publish + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + registry_package: + types: [published] + release: + types: [created, published, released] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + env: + NOMAD_IP: 127.0.0.1 + NOMAD_PORT: 4646 + NOMAD_LATEST: 1.1.4 + + strategy: + fail-fast: false + matrix: + python-version: [2.7, 3.7] + nomad-verion: [1.0, 1.1.4] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Setup Nomad + shell: bash + run: | + curl -L -o /tmp/nomad_${{ matrix.nomad-version }}_linux_amd64.zip https://releases.hashicorp.com/nomad/${{ matrix.nomad-version }}/nomad_${{ matrix.nomad-version }}_linux_amd64.zip + yes | unzip -d /tmp /tmp/nomad_${{ matrix.nomad-version }}_linux_amd64.zip + MAJOR_VERSION=`echo ${{ matrix.nomad-version }} | cut -d "." -f 2` + + if [[ ${MAJOR_VERSION} -gt 6 ]] + then + echo "Nomad version $NOMAD_VERSION supports acls" + export ACL_ENABLED="--acl-enabled" + else + echo "Nomad version $NOMAD_VERSION" + export ACL_ENABLED="" + fi + + /tmp/nomad agent -dev -bind ${NOMAD_IP} -node pynomad1 ${ACL_ENABLED} > /dev/null 2>&1 & + sleep 30 + - name: Install Dependencies + shell: bash + run: | + python -m pip install --upgrade pip + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi + - name: Before Tests + shell: bash + run: | + /tmp/nomad init + /tmp/nomad run -output example.nomad > example.json + - name: Unit and Integration Tests + shell: bash + run: | + py.test --cov=nomad --cov-report=term-missing --runxfail tests/ + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v2 + - name: Create Package + shell: bash + run: | + python -m pip install build + python -m build --sdist --wheel + - name: Publish Test PyPi + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.TEST_PYPI_API_TOKEN }} + repository_url: https://test.pypi.org/legacy/ + - name: Publish/Release Package + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} From dc6bd474a18580dd24e80ba989837509bbafbc6c Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sun, 5 Sep 2021 21:25:20 +0200 Subject: [PATCH 30/69] Update job.md --- docs/api/job.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/job.md b/docs/api/job.md index 40b68b8..9e7d39d 100644 --- a/docs/api/job.md +++ b/docs/api/job.md @@ -180,7 +180,7 @@ for evaluation in evaluations: ``` -### List job deploymetns +### List job deployments This endpoint lists a single job's deployments From a09434e44d36f960e399876e442d071a97b17e0d Mon Sep 17 00:00:00 2001 From: Jonathan Cross Date: Mon, 6 Sep 2021 12:13:59 -0400 Subject: [PATCH 31/69] GitHub actions integration (#125) * create separate requirements file for py27, set pytest to last release on py27. Check for python version when install dev requirements. Set --acl-enabled instead of checking for minor/(was named major accidentally) version, acl has been set for anything above 0.6.0. * update README badges, remove travis.yml Co-authored-by: Jonathan Cross <> --- .github/workflows/main.yml | 51 ++++++++++++++++++++++---------------- .travis.yml | 45 --------------------------------- README.md | 10 +++++--- requirements-dev-py27.txt | 7 ++++++ tests/test_agent.py | 15 ----------- tests/test_event.py | 6 ++++- 6 files changed, 49 insertions(+), 85 deletions(-) delete mode 100644 .travis.yml create mode 100644 requirements-dev-py27.txt diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 54867e2..38a1a03 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,15 +18,15 @@ jobs: build: runs-on: ubuntu-latest env: - NOMAD_IP: 127.0.0.1 - NOMAD_PORT: 4646 - NOMAD_LATEST: 1.1.4 + NOMAD_IP: '127.0.0.1' + NOMAD_PORT: '4646' + NOMAD_LATEST: '1.1.4' strategy: fail-fast: false matrix: - python-version: [2.7, 3.7] - nomad-verion: [1.0, 1.1.4] + python-version: ['2.7', '3.7'] + nomad-version: ['1.0.0', '1.1.4'] steps: - uses: actions/checkout@v2 @@ -34,36 +34,44 @@ jobs: uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - - name: Setup Nomad + - name: Setup Nomad ${{ matrix.nomad-version }} + env: + NOMAD_VERSION: ${{ matrix.nomad-version }} shell: bash run: | - curl -L -o /tmp/nomad_${{ matrix.nomad-version }}_linux_amd64.zip https://releases.hashicorp.com/nomad/${{ matrix.nomad-version }}/nomad_${{ matrix.nomad-version }}_linux_amd64.zip - yes | unzip -d /tmp /tmp/nomad_${{ matrix.nomad-version }}_linux_amd64.zip - MAJOR_VERSION=`echo ${{ matrix.nomad-version }} | cut -d "." -f 2` - - if [[ ${MAJOR_VERSION} -gt 6 ]] - then - echo "Nomad version $NOMAD_VERSION supports acls" - export ACL_ENABLED="--acl-enabled" - else - echo "Nomad version $NOMAD_VERSION" - export ACL_ENABLED="" - fi - - /tmp/nomad agent -dev -bind ${NOMAD_IP} -node pynomad1 ${ACL_ENABLED} > /dev/null 2>&1 & + echo $NOMAD_VERSION + echo ${NOMAD_VERSION} + + echo "downloading nomad" + curl -L -o /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip + + echo "unzip nomad" + unzip -d /tmp /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip + + echo "starting nomad server" + /tmp/nomad agent -dev -bind ${NOMAD_IP} -node pynomad1 --acl-enabled > /dev/null 2>&1 & sleep 30 - name: Install Dependencies + env: + PYTHON_VERSION: ${{ matrix.python-version }} shell: bash run: | python -m pip install --upgrade pip if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi + if [ $PYTHON_VERSION = "2.7" ] + then + pip install -r requirements-dev-py27.txt + else + pip install -r requirements-dev.txt + fi - name: Before Tests shell: bash run: | /tmp/nomad init /tmp/nomad run -output example.nomad > example.json - name: Unit and Integration Tests + env: + NOMAD_VERSION: ${{ matrix.nomad-version }} shell: bash run: | py.test --cov=nomad --cov-report=term-missing --runxfail tests/ @@ -80,6 +88,7 @@ jobs: user: __token__ password: ${{ secrets.TEST_PYPI_API_TOKEN }} repository_url: https://test.pypi.org/legacy/ + skip_existing: true - name: Publish/Release Package if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e68f2bf..0000000 --- a/.travis.yml +++ /dev/null @@ -1,45 +0,0 @@ -sudo: required -deploy: - provider: pypi - user: __token__ - distributions: "sdist bdist_wheel" - skip_existing: true - password: - secure: Wwy6E8My1CXssswhsVOZZDnEL/cGYFnZVF/Pq/X9T1DmRuVnCDOMmZ43t10ASXt4C7pCUVdhmlmnwbyOvNHbhMQ79EXH7Bqno7AnP2C8mC+l7u3KXqrXpvCQ+lIno8JwS3Wp3DQas+ROQ3VlyLenqoR+4bihWDQc573wwH8XM+9RM9jJcuwg2xc30sJAxG624loyD6S4BjMIit2ND/+MgupbiG69nBx9yWLrdJKj1BF0YO9hmumZ928asM+UdtrU2QtVT84g8I2aC5tM34kRZRiM/OQdApm93BVGwlo1hmAQ1YHJevZflMGSAouJjieOVxl4hkN+gYVyHxO1YjnzjgCTWzbKMtmCr7KtputclRwrf7wrsxl6FRn2HmOKPxLfY9CnVjv6r2e64iiYWmj4g8J/OgI1TD8B6k4e/ca2Oen/iu8/uiZWLT0XEOAtWLxpAc7kGwcfbXnfVNRUPgQY0ePLsI+Lp3VY7hh65YQQDaHqH+/Ib/MNS1XAhZMrMM1iJ4YgNMsmLuBXXI63Ef3T/T4Tw7plydw6jFy2Z2tyIG8/IFnV4t8CofvThX/Wfof8fX0YphuIFPGJZVzVRW+FSQbx8iniTDeu1Q7d76RxXYdoMd5Aesbn/dwWX22KLHO/x2gKlBNjPIVuvx4XxIsQGsan/7u1582MEC681AjggE4= - on: - tags: true - repo: jrxFive/python-nomad - condition: $TRAVIS_PYTHON_VERSION = "3.7" -services: -- docker -language: python -python: -- '3.7' -env: - global: - - NOMAD_IP="127.0.0.1" - - NOMAD_PORT="4646" - - NOMAD_LATEST="1.1.4" - matrix: - - NOMAD_VERSION="1.0.0" - - NOMAD_VERSION="1.1.4" -before_install: -- curl -L -o /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip -- yes | unzip -d /tmp /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip -- MAJOR_VERSION=`echo ${NOMAD_VERSION} | cut -d "." -f 2` -- if [[ ${MAJOR_VERSION} -gt 6 ]]; then echo "Nomad version $NOMAD_VERSION supports - acls";export ACL_ENABLED="--acl-enabled"; else echo "Nomad version $NOMAD_VERSION";export - ACL_ENABLED=""; fi -- "/tmp/nomad agent -dev -bind ${NOMAD_IP} -node pynomad1 ${ACL_ENABLED} > /dev/null - 2>&1 &" -- sleep 30 -install: -- pip install -r requirements.txt -r requirements-dev.txt -- pip install codecov -before_script: -- "/tmp/nomad init" -- "/tmp/nomad run -output example.nomad > example.json" -script: -- py.test --cov=nomad --cov-report=term-missing --runxfail tests/ -after_success: -- test $NOMAD_VERSION = $NOMAD_LATEST && codecov diff --git a/README.md b/README.md index bd61d7b..60b5307 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ # python-nomad -Branch | Status | Coverage | ----| ---| --- -master | [![Build Status](https://travis-ci.org/jrxFive/python-nomad.svg?branch=master)](https://travis-ci.org/jrxFive/python-nomad) | [![codecov](https://codecov.io/gh/jrxFive/python-nomad/branch/master/graph/badge.svg)](https://codecov.io/gh/jrxFive/python-nomad) +[![Python Nomad Test and Publish](https://github.com/jrxFive/python-nomad/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/jrxFive/python-nomad/actions/workflows/main.yml) +[![codecov](https://codecov.io/gh/jrxFive/python-nomad/branch/master/graph/badge.svg)](https://codecov.io/gh/jrxFive/python-nomad) +[![PyPI version](https://badge.fury.io/py/python-nomad.svg)](https://badge.fury.io/py/python-nomad) +[![PyPI pyversions](https://img.shields.io/pypi/pyversions/python-nomad.svg)](https://pypi.python.org/pypi/python-nomad/) +[![Downloads](https://pepy.tech/badge/python-nomad/month)](https://pepy.tech/project/python-nomad) +[![Downloads](https://static.pepy.tech/personalized-badge/python-nomad?period=total&units=international_system&left_color=black&right_color=blue&left_text=Downloads)](https://pepy.tech/project/python-nomad) +[![PyPI license](https://img.shields.io/pypi/l/python-nomad.svg)](https://pypi.python.org/pypi/python-nomad/) ## Installation diff --git a/requirements-dev-py27.txt b/requirements-dev-py27.txt new file mode 100644 index 0000000..1b80493 --- /dev/null +++ b/requirements-dev-py27.txt @@ -0,0 +1,7 @@ +coverage==5.2.1 +pytest==4.6.11 +pytest-cov==2.12.1 +mkdocs==0.15.3 +mock==1.2.0 +flaky==3.7.0 +responses==0.13.4 diff --git a/tests/test_agent.py b/tests/test_agent.py index 16c9138..dccceb3 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -29,21 +29,6 @@ def test_join_agent(nomad_setup): assert r["num_joined"] == 0 -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 4, 1), reason="Not supported in version") -def test_update_servers(nomad_setup): - known_servers = nomad_setup.agent.get_servers() - r = nomad_setup.agent.update_servers(known_servers) - assert r == 200 - assert known_servers[0] in nomad_setup.agent.get_servers() - - # 0.8 enforces list of known servers to the provided list releases below do allow this functionality - try: - nomad_setup.agent.update_servers(known_servers + ["10.1.10.200:4829"]) - assert "10.1.10.200:4829" in nomad_setup.agent.get_servers() - except nomad_exceptions.BaseNomadException: - pass - - def test_force_leave(nomad_setup): r = nomad_setup.agent.force_leave("nope") assert r == 200 diff --git a/tests/test_event.py b/tests/test_event.py index bcbb4b4..33a805f 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -1,5 +1,7 @@ import json +from flaky import flaky + # integration tests requires nomad Vagrant VM or Binary running def test_register_job(nomad_setup): @@ -10,6 +12,7 @@ def test_register_job(nomad_setup): assert "example" in nomad_setup.job +@flaky(max_runs=5, min_passes=1) def test_get_event_stream_default(nomad_setup): stream, stream_exit, events = nomad_setup.event.stream.get_stream() @@ -24,13 +27,14 @@ def test_get_event_stream_default(nomad_setup): stream_exit.set() +@flaky(max_runs=5, min_passes=1) def test_get_event_stream_with_customized_topic(nomad_setup): stream, stream_exit, events = nomad_setup.event.stream.get_stream(topic={"Node": "*"}) stream.daemon = True stream.start() node_id = nomad_setup.nodes.get_nodes()[0]["ID"] - nomad_setup.node.drain_node(node_id) + nomad_setup.node.drain_node_with_spec(node_id, None) event = events.get(timeout=5) assert event From e7951a176a14a32a565c67c28467cf9204c4302f Mon Sep 17 00:00:00 2001 From: Cory Downey Date: Sat, 20 Nov 2021 10:21:10 -0500 Subject: [PATCH 32/69] Update deployment docs I think these examples incorrectly reference the `pause_deployment` function --- docs/api/deployment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/deployment.md b/docs/api/deployment.md index f5ae5a9..9445cd8 100644 --- a/docs/api/deployment.md +++ b/docs/api/deployment.md @@ -116,7 +116,7 @@ import nomad my_nomad = nomad.Nomad(host='192.168.33.10') -promote = my_nomad.deployment.pause_deployment("52c47d49-eefa-540f-f0f1-d25ba298c87f",True) +promote = my_nomad.deployment.promote_deployment_all("52c47d49-eefa-540f-f0f1-d25ba298c87f",True) ``` #### Promote Groups @@ -130,7 +130,7 @@ import nomad my_nomad = nomad.Nomad(host='192.168.33.10') -promote = my_nomad.deployment.pause_deployment("52c47d49-eefa-540f-f0f1-d25ba298c87f",groups=['task1','task2']) +promote = my_nomad.deployment.promote_deployment_groups("52c47d49-eefa-540f-f0f1-d25ba298c87f",groups=['task1','task2']) ``` From 84d99cac72ffa1251716ad096c489645082355d1 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii Date: Mon, 5 Sep 2022 16:23:49 +0200 Subject: [PATCH 33/69] add namepsace arguments and python 3.10 support --- .github/workflows/main.yml | 2 +- nomad/api/allocations.py | 7 ++++++- nomad/api/deployments.py | 7 ++++++- requirements.txt | 2 +- setup.py | 2 ++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 38a1a03..61ba0bf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['2.7', '3.7'] + python-version: ['2.7', '3.7', '3.10'] nomad-version: ['1.0.0', '1.1.4'] steps: diff --git a/nomad/api/allocations.py b/nomad/api/allocations.py index eca4ec2..e532f0e 100644 --- a/nomad/api/allocations.py +++ b/nomad/api/allocations.py @@ -32,17 +32,22 @@ def __iter__(self): response = self.get_allocations() return iter(response) - def get_allocations(self, prefix=None): + def get_allocations(self, prefix=None, namespace=None): """ Lists all the allocations. https://www.nomadproject.io/docs/http/allocs.html arguments: - prefix :(str) optional, specifies a string to filter allocations on based on an prefix. This is specified as a querystring parameter. + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. returns: list raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ params = {"prefix": prefix} + if namespace: + params["namespace"] = namespace + return self.request(method="get", params=params).json() diff --git a/nomad/api/deployments.py b/nomad/api/deployments.py index b840758..92b7145 100644 --- a/nomad/api/deployments.py +++ b/nomad/api/deployments.py @@ -56,7 +56,7 @@ def __getitem__(self, item): except nomad.api.exceptions.URLNotFoundNomadException: raise KeyError - def get_deployments(self, prefix=""): + def get_deployments(self, prefix="", namespace=None): """ This endpoint lists all deployments. https://www.nomadproject.io/docs/http/deployments.html @@ -64,6 +64,8 @@ def get_deployments(self, prefix=""): optional_arguments: - prefix, (default "") Specifies a string to filter deployments on based on an index prefix. This is specified as a querystring parameter. + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. returns: list of dicts raises: @@ -71,4 +73,7 @@ def get_deployments(self, prefix=""): - nomad.api.exceptions.URLNotFoundNomadException """ params = {"prefix": prefix} + if namespace: + params["namespace"] = namespace + return self.request(params=params, method="get").json() diff --git a/requirements.txt b/requirements.txt index a8ed785..d15ce5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -requests==2.26.0 +requests==2.28.1 diff --git a/setup.py b/setup.py index f95cbdd..adf95a6 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,8 @@ 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', ], keywords='nomad hashicorp client', ) From 23c21c563ca2a6f85795f49207430503e0ffe3c9 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii Date: Mon, 5 Sep 2022 16:45:34 +0200 Subject: [PATCH 34/69] change requests lib version --- requirements.txt | 2 +- setup.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index d15ce5a..a743bbe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -requests==2.28.1 +requests==2.27.1 diff --git a/setup.py b/setup.py index adf95a6..47e58cc 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,6 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', From c6cab12664d90e9511d0b3b1954748bab9573988 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii Date: Mon, 5 Sep 2022 18:20:44 +0200 Subject: [PATCH 35/69] add tests --- README.md | 2 +- tests/test_allocations.py | 11 +++++++++++ tests/test_deployment.py | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 60b5307..55395b6 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ NOMAD_CLIENT_KEY=/path/to/tls/client.key * can either use the Vagrantfile for local integration testing or create environment variables `NOMAD_IP` and `NOMAD_PORT` that are assigned to a nomad binary that is running ``` -virutalenv venv +virtualenv venv source venv/bin/activate pip install -r requirements-dev.txt ``` diff --git a/tests/test_allocations.py b/tests/test_allocations.py index 962a1d7..0903991 100644 --- a/tests/test_allocations.py +++ b/tests/test_allocations.py @@ -59,3 +59,14 @@ def test_get_allocations_with_namespace(nomad_setup_with_namespace): json=[{"ID": "a8198d79-cfdb-6593-a999-1e9adabcba2e","EvalID": "5456bd7a-9fc0-c0dd-6131-cbee77f57577","Namespace": common.NOMAD_NAMESPACE, "Name": "example.cache[0]","NodeID": "fb2170a8-257d-3c64-b14d-bc06cc94e34c","PreviousAllocation": "516d2753-0513-cfc7-57ac-2d6fac18b9dc","NextAllocation": "cd13d9b9-4f97-7184-c88b-7b451981616b"}] ) assert common.NOMAD_NAMESPACE in nomad_setup_with_namespace.allocations.get_allocations()[0]["Namespace"] + +@responses.activate +def test_get_allocations_with_namespace_override_namespace_declared_on_create(nomad_setup_with_namespace): + responses.add( + responses.GET, + "http://{ip}:{port}/v1/allocations?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + status=200, + json=[{"ID": "a8198d79-cfdb-6593-a999-1e9adabcba2e","EvalID": "5456bd7a-9fc0-c0dd-6131-cbee77f57577","Namespace": common.NOMAD_NAMESPACE, "Name": "example.cache[0]","NodeID": "fb2170a8-257d-3c64-b14d-bc06cc94e34c","PreviousAllocation": "516d2753-0513-cfc7-57ac-2d6fac18b9dc","NextAllocation": "cd13d9b9-4f97-7184-c88b-7b451981616b"}] + ) + + nomad_setup_with_namespace.allocations.get_allocations("a8198d79-cfdb-6593-a999-1e9adabcba2e", namespace="override-namespace") \ No newline at end of file diff --git a/tests/test_deployment.py b/tests/test_deployment.py index e9a7640..d1b5edf 100644 --- a/tests/test_deployment.py +++ b/tests/test_deployment.py @@ -123,3 +123,14 @@ def test_get_deployment_with_namespace(nomad_setup_with_namespace): json={"ID": "70638f62-5c19-193e-30d6-f9d6e689ab8e","JobID": "example", "JobVersion": 1, "JobModifyIndex": 17, "JobSpecModifyIndex": 17, "JobCreateIndex": 7,"Namespace": common.NOMAD_NAMESPACE, "Name": "example.cache[0]"} ) assert common.NOMAD_NAMESPACE in nomad_setup_with_namespace.deployment.get_deployment("a8198d79-cfdb-6593-a999-1e9adabcba2e")["Namespace"] + +@responses.activate +def test_get_deployments_with_namespace_override_namespace_declared_on_create(nomad_setup_with_namespace): + responses.add( + responses.GET, + "http://{ip}:{port}/v1/deployment/a8198d79-cfdb-6593-a999-1e9adabcba2e?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + status=200, + json={"ID": "70638f62-5c19-193e-30d6-f9d6e689ab8e","JobID": "example", "JobVersion": 1, "JobModifyIndex": 17, "JobSpecModifyIndex": 17, "JobCreateIndex": 7,"Namespace": common.NOMAD_NAMESPACE, "Name": "example.cache[0]"} + ) + + nomad_setup_with_namespace.deployments.get_deployments("a8198d79-cfdb-6593-a999-1e9adabcba2e", namespace="override-namespace") \ No newline at end of file From 1796a61f8a7666e74d8a5d62fd60fd0de18f1047 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii Date: Sun, 18 Sep 2022 20:57:44 +0200 Subject: [PATCH 36/69] fix tests and README.me --- README.md | 4 ++-- tests/test_allocations.py | 7 ++++--- tests/test_deployment.py | 7 ++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 55395b6..2e46ed7 100644 --- a/README.md +++ b/README.md @@ -97,8 +97,8 @@ NOMAD_CLIENT_KEY=/path/to/tls/client.key * can either use the Vagrantfile for local integration testing or create environment variables `NOMAD_IP` and `NOMAD_PORT` that are assigned to a nomad binary that is running ``` -virtualenv venv -source venv/bin/activate +virtualenv .venv +source .venv/bin/activate pip install -r requirements-dev.txt ``` diff --git a/tests/test_allocations.py b/tests/test_allocations.py index 0903991..b35c445 100644 --- a/tests/test_allocations.py +++ b/tests/test_allocations.py @@ -62,11 +62,12 @@ def test_get_allocations_with_namespace(nomad_setup_with_namespace): @responses.activate def test_get_allocations_with_namespace_override_namespace_declared_on_create(nomad_setup_with_namespace): + override_namespace_name = "namespace=override-namespace" responses.add( responses.GET, - "http://{ip}:{port}/v1/allocations?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + "http://{ip}:{port}/v1/allocations?prefix=a8198d79-cfdb-6593-a999-1e9adabcba2e&namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=override_namespace_name), status=200, - json=[{"ID": "a8198d79-cfdb-6593-a999-1e9adabcba2e","EvalID": "5456bd7a-9fc0-c0dd-6131-cbee77f57577","Namespace": common.NOMAD_NAMESPACE, "Name": "example.cache[0]","NodeID": "fb2170a8-257d-3c64-b14d-bc06cc94e34c","PreviousAllocation": "516d2753-0513-cfc7-57ac-2d6fac18b9dc","NextAllocation": "cd13d9b9-4f97-7184-c88b-7b451981616b"}] + json=[{"ID": "a8198d79-cfdb-6593-a999-1e9adabcba2e","EvalID": "5456bd7a-9fc0-c0dd-6131-cbee77f57577","Namespace": override_namespace_name, "Name": "example.cache[0]","NodeID": "fb2170a8-257d-3c64-b14d-bc06cc94e34c","PreviousAllocation": "516d2753-0513-cfc7-57ac-2d6fac18b9dc","NextAllocation": "cd13d9b9-4f97-7184-c88b-7b451981616b"}] ) - nomad_setup_with_namespace.allocations.get_allocations("a8198d79-cfdb-6593-a999-1e9adabcba2e", namespace="override-namespace") \ No newline at end of file + nomad_setup_with_namespace.allocations.get_allocations("a8198d79-cfdb-6593-a999-1e9adabcba2e", namespace=override_namespace_name) \ No newline at end of file diff --git a/tests/test_deployment.py b/tests/test_deployment.py index d1b5edf..d27bab8 100644 --- a/tests/test_deployment.py +++ b/tests/test_deployment.py @@ -126,11 +126,12 @@ def test_get_deployment_with_namespace(nomad_setup_with_namespace): @responses.activate def test_get_deployments_with_namespace_override_namespace_declared_on_create(nomad_setup_with_namespace): + override_namespace_name = "override-namespace" responses.add( responses.GET, - "http://{ip}:{port}/v1/deployment/a8198d79-cfdb-6593-a999-1e9adabcba2e?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + "http://{ip}:{port}/v1/deployments?prefix=a8198d79-cfdb-6593-a999-1e9adabcba2e&namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=override_namespace_name), status=200, - json={"ID": "70638f62-5c19-193e-30d6-f9d6e689ab8e","JobID": "example", "JobVersion": 1, "JobModifyIndex": 17, "JobSpecModifyIndex": 17, "JobCreateIndex": 7,"Namespace": common.NOMAD_NAMESPACE, "Name": "example.cache[0]"} + json={"ID": "70638f62-5c19-193e-30d6-f9d6e689ab8e","JobID": "example", "JobVersion": 1, "JobModifyIndex": 17, "JobSpecModifyIndex": 17, "JobCreateIndex": 7,"Namespace": override_namespace_name, "Name": "example.cache[0]"} ) - nomad_setup_with_namespace.deployments.get_deployments("a8198d79-cfdb-6593-a999-1e9adabcba2e", namespace="override-namespace") \ No newline at end of file + nomad_setup_with_namespace.deployments.get_deployments("a8198d79-cfdb-6593-a999-1e9adabcba2e", namespace=override_namespace_name) \ No newline at end of file From 690add1b546d7996cac55bf9f0cb579e8c1493b9 Mon Sep 17 00:00:00 2001 From: Scott Minor Date: Mon, 10 Oct 2022 07:20:58 -0700 Subject: [PATCH 37/69] Add support for pre-populated Sessions (#132) This PR allows the caller to inject a `requests.Session` object that the Nomad client should use to make all requests, in case the client is used in an environment where cookies/headers/etc. unrelated to Nomad communication must be added to each request. Tested: not yet --- nomad/__init__.py | 9 +++++++-- nomad/api/base.py | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/nomad/__init__.py b/nomad/__init__.py index 4c0301c..f3a2485 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -16,7 +16,8 @@ def __init__(self, version='v1', verify=False, cert=(os.getenv('NOMAD_CLIENT_CERT', None), - os.getenv('NOMAD_CLIENT_KEY', None))): + os.getenv('NOMAD_CLIENT_KEY', None)), + session=None): """ Nomad api client https://github.com/jrxFive/python-nomad/ @@ -36,6 +37,8 @@ def __init__(self, be use to deploy or to ask info to nomad. - token (defaults to None), Specifies to append ACL token to the headers to make authentication on secured based nomad environemnts. + - session (defaults to None), allows for injecting a prepared requests.Session object that + all requests to Nomad should use. returns: Nomad api client object raises: @@ -53,6 +56,7 @@ def __init__(self, self.token = token self.verify = verify self.cert = cert if all(cert) else () + self.session = session self.__namespace = namespace self.requester_settings = { @@ -65,7 +69,8 @@ def __init__(self, "version": self.version, "verify": self.verify, "cert": self.cert, - "region": self.region + "region": self.region, + "session": self.session, } self._acl = api.Acl(**self.requester_settings) diff --git a/nomad/api/base.py b/nomad/api/base.py index c502be2..71036f9 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -9,7 +9,7 @@ class Requester(object): ENDPOINT = "" - def __init__(self, address=None, uri='http://127.0.0.1', port=4646, namespace=None, token=None, timeout=5, version='v1', verify=False, cert=(), region=None, **kwargs): + def __init__(self, address=None, uri='http://127.0.0.1', port=4646, namespace=None, token=None, timeout=5, version='v1', verify=False, cert=(), region=None, session=None, **kwargs): self.uri = uri self.port = port self.namespace = namespace @@ -19,7 +19,7 @@ def __init__(self, address=None, uri='http://127.0.0.1', port=4646, namespace=No self.verify = verify self.cert = cert self.address = address - self.session = requests.Session() + self.session = session or requests.Session() self.region = region def _endpoint_builder(self, *args): From 9cf0883e8145e107bdff55c551aafb0389110961 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii Date: Mon, 17 Oct 2022 01:19:57 +0200 Subject: [PATCH 38/69] add scaling policy endpoint --- .github/workflows/main.yml | 2 +- .gitignore | 3 ++ nomad/__init__.py | 5 +++ nomad/api/__init__.py | 1 + nomad/api/scaling.py | 68 ++++++++++++++++++++++++++++++++++++++ tests/test_scaling.py | 11 ++++++ 6 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 nomad/api/scaling.py create mode 100644 tests/test_scaling.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 38a1a03..6a8f313 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,7 @@ jobs: env: NOMAD_IP: '127.0.0.1' NOMAD_PORT: '4646' - NOMAD_LATEST: '1.1.4' + NOMAD_LATEST: '1.1.18' strategy: fail-fast: false diff --git a/.gitignore b/.gitignore index 246fd60..a5c391f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ .vagrant .build .venv + +example.json +example.nomad \ No newline at end of file diff --git a/nomad/__init__.py b/nomad/__init__.py index 4c0301c..242bab5 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -87,6 +87,7 @@ def __init__(self, self._nodes = api.Nodes(**self.requester_settings) self._operator = api.Operator(**self.requester_settings) self._regions = api.Regions(**self.requester_settings) + self._scaling = api.Scaling(**self.requester_settings) self._sentinel = api.Sentinel(**self.requester_settings) self._status = api.Status(**self.requester_settings) self._system = api.System(**self.requester_settings) @@ -161,6 +162,10 @@ def deployment(self): def regions(self): return self._regions + @property + def scaling(self): + return self._scaling + @property def status(self): return self._status diff --git a/nomad/api/__init__.py b/nomad/api/__init__.py index 1f6e873..3cd9fd9 100644 --- a/nomad/api/__init__.py +++ b/nomad/api/__init__.py @@ -19,6 +19,7 @@ from nomad.api.nodes import Nodes from nomad.api.operator import Operator from nomad.api.regions import Regions +from nomad.api.scaling import Scaling from nomad.api.sentinel import Sentinel from nomad.api.status import Status from nomad.api.system import System diff --git a/nomad/api/scaling.py b/nomad/api/scaling.py new file mode 100644 index 0000000..56776a2 --- /dev/null +++ b/nomad/api/scaling.py @@ -0,0 +1,68 @@ +import nomad.api.exceptions + +from nomad.api.base import Requester + + +class Scaling(Requester): + """ + Endpoints are used to list and view scaling policies. + + https://developer.hashicorp.com/nomad/api-docs/scaling-policies + """ + ENDPOINT = "scaling" + + def __init__(self, **kwargs): + super(Scaling, self).__init__(**kwargs) + + def __str__(self): + return "{0}".format(self.__dict__) + + def __repr__(self): + return "{0}".format(self.__dict__) + + def __getattr__(self, item): + raise AttributeError + + def get_scaling_policies(self, job="", type=""): + """ + This endpoint returns the scaling policies from all jobs. + + https://developer.hashicorp.com/nomad/api-docs/scaling-policies#list-scaling-policies + + arguments: + - job + - type + returns: list of dicts + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + """ + type_of_scaling_policies = [ + "horizontal", + "vertical_mem", + "vertical_cpu", + "", + ] # we have only horizontal in OSS + + if type not in type_of_scaling_policies: + raise nomad.api.exceptions.InvalidParameters("type is invalid " + "(expected values are {} but got {})".format(type_of_scaling_policies, type)) + + params = {"job": job, "type": type} + + return self.request("policies", method="get", params=params).json() + + def get_scaling_policy(self, id): + """ + This endpoint reads a specific scaling policy. + + https://developer.hashicorp.com/nomad/api-docs/scaling-policies#read-scaling-policy + + arguments: + - id + returns: list of dicts + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + """ + return self.request("policy/{}".format(id), method="get").json() diff --git a/tests/test_scaling.py b/tests/test_scaling.py new file mode 100644 index 0000000..686ad3b --- /dev/null +++ b/tests/test_scaling.py @@ -0,0 +1,11 @@ +import pytest + +from nomad.api import exceptions + +def test_scaling_list(nomad_setup): + result = nomad_setup.scaling.get_scaling_policies() + assert not result + +def test_scaling_policy_not_exist(nomad_setup): + with pytest.raises(exceptions.URLNotFoundNomadException): + nomad_setup.scaling.get_scaling_policy("example") From 587cc63cded8da025a6a982268429dd16dd9c83d Mon Sep 17 00:00:00 2001 From: Nikita Beletskii Date: Mon, 17 Oct 2022 01:22:54 +0200 Subject: [PATCH 39/69] bump nomad version in tests --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6a8f313..65e768d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false matrix: python-version: ['2.7', '3.7'] - nomad-version: ['1.0.0', '1.1.4'] + nomad-version: ['1.0.0', '1.1.18'] steps: - uses: actions/checkout@v2 From 3c6af97399721c1486eee6bc48be07f1ee771cfd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Oct 2022 16:30:08 -0400 Subject: [PATCH 40/69] Bump mkdocs from 0.15.3 to 1.2.3 (#135) Bumps [mkdocs](https://github.com/mkdocs/mkdocs) from 0.15.3 to 1.2.3. - [Release notes](https://github.com/mkdocs/mkdocs/releases) - [Commits](https://github.com/mkdocs/mkdocs/compare/0.15.3...1.2.3) --- updated-dependencies: - dependency-name: mkdocs dependency-type: direct:development ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements-dev-py27.txt | 2 +- requirements-dev.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-dev-py27.txt b/requirements-dev-py27.txt index 1b80493..a961256 100644 --- a/requirements-dev-py27.txt +++ b/requirements-dev-py27.txt @@ -1,7 +1,7 @@ coverage==5.2.1 pytest==4.6.11 pytest-cov==2.12.1 -mkdocs==0.15.3 +mkdocs==1.2.3 mock==1.2.0 flaky==3.7.0 responses==0.13.4 diff --git a/requirements-dev.txt b/requirements-dev.txt index feb5499..eac8c93 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,7 @@ coverage==5.2.1 pytest==6.2.4 pytest-cov==2.12.1 -mkdocs==0.15.3 +mkdocs==1.2.3 mock==1.2.0 flaky==3.7.0 responses==0.13.4 From da760de805117e6601c2585e44709302c42ddb82 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Thu, 3 Nov 2022 14:09:01 +0100 Subject: [PATCH 41/69] Add CHANGELOG.me, add all modern Nomad version to tests and fix CI (#137) * fix ci and add CHANGELOG.me * fix syntax * try to upgrade actions * add more Nomad versions --- .github/workflows/main.yml | 31 +++++++++++-------------------- CHANGELOG.md | 5 +++++ requirements-dev-py27.txt | 2 +- 3 files changed, 17 insertions(+), 21 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f5414a3..99f5e25 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,12 +26,11 @@ jobs: fail-fast: false matrix: python-version: ['2.7', '3.7', '3.10'] - nomad-version: ['1.0.0', '1.1.18'] - + nomad-version: ['1.0.18', '1.1.18', '1.2.14', '1.3.7', '1.4.2'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Setup Nomad ${{ matrix.nomad-version }} @@ -39,14 +38,14 @@ jobs: NOMAD_VERSION: ${{ matrix.nomad-version }} shell: bash run: | - echo $NOMAD_VERSION - echo ${NOMAD_VERSION} + echo $NOMAD_VERSION + echo ${NOMAD_VERSION} - echo "downloading nomad" - curl -L -o /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip + echo "downloading nomad" + curl -L -o /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip - echo "unzip nomad" - unzip -d /tmp /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip + echo "unzip nomad" + unzip -d /tmp /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip echo "starting nomad server" /tmp/nomad agent -dev -bind ${NOMAD_IP} -node pynomad1 --acl-enabled > /dev/null 2>&1 & @@ -58,9 +57,8 @@ jobs: run: | python -m pip install --upgrade pip if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - if [ $PYTHON_VERSION = "2.7" ] - then - pip install -r requirements-dev-py27.txt + if [ $PYTHON_VERSION = "2.7" ]; then + pip install -r requirements-dev-py27.txt else pip install -r requirements-dev.txt fi @@ -82,13 +80,6 @@ jobs: run: | python -m pip install build python -m build --sdist --wheel - - name: Publish Test PyPi - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.TEST_PYPI_API_TOKEN }} - repository_url: https://test.pypi.org/legacy/ - skip_existing: true - name: Publish/Release Package if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a43d1b7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +## Unreleased +* Add `namespace` agrument support for `get_allocations` and `get_deployments` endpoints (#133) +* Add Python 3.10 support (#133) +* Add support for pre-populated Sessions (#132) +* Add scaling policy endpoint (#136) diff --git a/requirements-dev-py27.txt b/requirements-dev-py27.txt index a961256..21de22f 100644 --- a/requirements-dev-py27.txt +++ b/requirements-dev-py27.txt @@ -1,7 +1,7 @@ coverage==5.2.1 pytest==4.6.11 pytest-cov==2.12.1 -mkdocs==1.2.3 +mkdocs==1.0.4 mock==1.2.0 flaky==3.7.0 responses==0.13.4 From 9ddf6883e54db63dd85931ff6263ae97135ee02b Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Tue, 8 Nov 2022 18:56:29 +0100 Subject: [PATCH 42/69] add search endpoint (#134) * add search endpoint --- .gitignore | 2 +- CHANGELOG.md | 1 + README.md | 1 + docs/api/search.md | 37 ++++++++++++++++++ mkdocs.yml | 1 + nomad/__init__.py | 5 +++ nomad/api/__init__.py | 1 + nomad/api/namespaces.py | 8 ++-- nomad/api/search.py | 83 +++++++++++++++++++++++++++++++++++++++++ tests/test_search.py | 34 +++++++++++++++++ 10 files changed, 168 insertions(+), 5 deletions(-) create mode 100644 docs/api/search.md create mode 100644 nomad/api/search.py create mode 100644 tests/test_search.py diff --git a/.gitignore b/.gitignore index a5c391f..0f1b3d3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,5 @@ .build .venv +example.nomad example.json -example.nomad \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a43d1b7..00f3317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,3 +3,4 @@ * Add Python 3.10 support (#133) * Add support for pre-populated Sessions (#132) * Add scaling policy endpoint (#136) +* Add support for /search endpoint (#134) diff --git a/README.md b/README.md index 2e46ed7..9edc914 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,7 @@ NOMAD_IP=127.0.0.1 NOMAD_VERSION= py.test --cov=nomad --cov-re - [x] Nodes [:link:](docs/api/nodes.md) - [x] Regions [:link:](docs/api/regions.md) - [x] Sentinel [:link:](docs/api/sentinel.md) + - [x] Search [:link:](docs/api/search.md) - [x] Status [:link:](docs/api/status.md) - [x] System [:link:](docs/api/system.md) - [x] Validate [:link:](docs/api/validate.md) diff --git a/docs/api/search.md b/docs/api/search.md new file mode 100644 index 0000000..7fab18a --- /dev/null +++ b/docs/api/search.md @@ -0,0 +1,37 @@ +## Search + +### Regular search + +https://developer.hashicorp.com/nomad/api-docs/search + +Search in context (can be: jobs, evals, allocs, nodes, deployment, plugins, volumes or all) with prefix + +`all` context means every context will be searched. + +Example: + +``` +import nomad + +my_nomad = nomad.Nomad(host='192.168.33.10') + +metrics = my_nomad.search("test", "jobs") +``` + +### Fuzzy search + +https://developer.hashicorp.com/nomad/api-docs/search#fuzzy-searching + +Search any text in context (can be: jobs, allocs, nodes, plugins, or all) + +`all` context means every context will be searched. + +Example: + +``` +import nomad + +my_nomad = nomad.Nomad(host='192.168.33.10') + +metrics = my_nomad.search.fuzzy("test", "jobs") +``` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 9bc017a..4119c91 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -24,6 +24,7 @@ pages: - Regions: 'api/regions.md' - Status: 'api/status.md' - System: 'api/system.md' + - Search: 'api/search.md' - Acl: 'api/acl.md' - Namespace: 'api/namespace.md' - Namespaces: 'api/namespaces.md' diff --git a/nomad/__init__.py b/nomad/__init__.py index b1ffe5e..6754b1f 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -94,6 +94,7 @@ def __init__(self, self._regions = api.Regions(**self.requester_settings) self._scaling = api.Scaling(**self.requester_settings) self._sentinel = api.Sentinel(**self.requester_settings) + self._search = api.Search(**self.requester_settings) self._status = api.Status(**self.requester_settings) self._system = api.System(**self.requester_settings) self._validate = api.Validate(**self.requester_settings) @@ -203,6 +204,10 @@ def acl(self): def sentinel(self): return self._sentinel + @property + def search(self): + return self._search + @property def metrics(self): return self._metrics diff --git a/nomad/api/__init__.py b/nomad/api/__init__.py index 3cd9fd9..c50314d 100644 --- a/nomad/api/__init__.py +++ b/nomad/api/__init__.py @@ -21,6 +21,7 @@ from nomad.api.regions import Regions from nomad.api.scaling import Scaling from nomad.api.sentinel import Sentinel +from nomad.api.search import Search from nomad.api.status import Status from nomad.api.system import System from nomad.api.validate import Validate diff --git a/nomad/api/namespaces.py b/nomad/api/namespaces.py index 9483074..12b39a1 100644 --- a/nomad/api/namespaces.py +++ b/nomad/api/namespaces.py @@ -32,8 +32,8 @@ def __contains__(self, item): for n in namespaces: if n["Name"] == item: return True - else: - return False + + return False except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -48,8 +48,8 @@ def __getitem__(self, item): for n in namespaces: if n["Name"] == item: return n - else: - raise KeyError + + raise KeyError except nomad.api.exceptions.URLNotFoundNomadException: raise KeyError diff --git a/nomad/api/search.py b/nomad/api/search.py new file mode 100644 index 0000000..66e4dc9 --- /dev/null +++ b/nomad/api/search.py @@ -0,0 +1,83 @@ +import nomad.api.exceptions + +from nomad.api.base import Requester + + +class Search(Requester): + """ + The endpoint returns matches for a given prefix and context, where a context can be jobs, allocations, evaluations, + nodes, deployments, plugins, namespaces, or volumes. + When using Nomad Enterprise, the allowed contexts include quotas. + Additionally, a prefix can be searched for within every context. + + https://developer.hashicorp.com/nomad/api-docs/search + """ + ENDPOINT = "search" + + def __init__(self, **kwargs): + super(Search, self).__init__(**kwargs) + + def __str__(self): + return "{0}".format(self.__dict__) + + def __repr__(self): + return "{0}".format(self.__dict__) + + def __getattr__(self, item): + raise AttributeError + + def search(self, prefix, context): + """ The endpoint returns matches for a given prefix and context, where a context can be jobs, + allocations, evaluations, nodes, deployments, plugins, namespaces, or volumes. + + https://developer.hashicorp.com/nomad/api-docs/search + arguments: + - prefix:(str) required, specifies the identifier against which matches will be found. + For example, if the given prefix were "a", potential matches might be "abcd", or "aabb". + - context:(str) defines the scope in which a search for a prefix operates. + Contexts can be: "jobs", "evals", "allocs", "nodes", "deployment", "plugins", + "volumes" or "all", where "all" means every context will be searched. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.InvalidParameters + """ + accetaple_contexts = ("jobs", "evals", "allocs", "nodes", "deployment", "plugins", "volumes", "all") + if context not in accetaple_contexts: + raise nomad.api.exceptions.InvalidParameters("context is invalid " + "(expected values are {} but got {})".format(accetaple_contexts, context)) + params = {"Prefix": prefix, "Context": context} + + return self.request(json=params, method="post").json() + + def fuzzy_search(self, text, context): + """ The /search/fuzzy endpoint returns partial substring matches for a given search term and context, + where a context can be jobs, allocations, nodes, plugins, or namespaces. Additionally, + fuzzy searching can be done across all contexts. + + https://developer.hashicorp.com/nomad/api-docs/search#fuzzy-searching + arguments: + - text:(str) required, specifies the identifier against which matches will be found. + For example, if the given text were "py", potential fuzzy matches might be "python", "spying", + or "happy". + - context:(str) defines the scope in which a search for a prefix operates. Contexts can be: + "jobs", "allocs", "nodes", "plugins", or "all", where "all" means every context will + be searched. + When "all" is selected, additional prefix matches will be included for the "deployments", + "evals", and "volumes" types. When searching in the "jobs" context, results that fuzzy match + "groups", "services", "tasks", "images", "commands", and "classes" are also included in the results. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + """ + + params = {"Text": text, "Context": context} + + accetaple_contexts = ("jobs", "allocs", "nodes", "plugins", "all") + if context not in accetaple_contexts: + raise nomad.api.exceptions.InvalidParameters("context is invalid " + "(expected values are {} but got {})".format(accetaple_contexts,context)) + + return self.request("fuzzy", json=params, method="post").json() diff --git a/tests/test_search.py b/tests/test_search.py new file mode 100644 index 0000000..6b291ae --- /dev/null +++ b/tests/test_search.py @@ -0,0 +1,34 @@ +import os +import pytest + +from nomad.api import exceptions + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") +def test_search(nomad_setup): + result = nomad_setup.search.search("example", "jobs") + assert "example" in result["Matches"]["jobs"] + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") +def test_search_incorrect_context(nomad_setup): + # job context doesn't exist + with pytest.raises(exceptions.InvalidParameters): + nomad_setup.search.search("example", "job") + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") +def test_search_fuzzy(nomad_setup): + result = nomad_setup.search.fuzzy_search("example", "jobs") + assert any(r['ID'] == 'example' for r in result["Matches"]["jobs"]) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") +def test_search_fuzzy_incorrect_context(nomad_setup): + # job context doesn't exist + with pytest.raises(exceptions.InvalidParameters): + nomad_setup.search.fuzzy_search("example", "job") + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") +def test_search_str(nomad_setup): + assert isinstance(str(nomad_setup.search), str) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") +def test_search_repr(nomad_setup): + assert isinstance(repr(nomad_setup.search), str) \ No newline at end of file From 14633079ca448c25407832162608f4a08b1b8b96 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Tue, 29 Nov 2022 13:06:44 +0100 Subject: [PATCH 43/69] Add support for variables endpoint (#141) --- .gitignore | 4 +- CHANGELOG.md | 5 +- README.md | 2 + docs/api/variable.md | 54 ++++++++++++++++++++ docs/api/variables.md | 20 ++++++++ nomad/__init__.py | 10 ++++ nomad/api/__init__.py | 2 + nomad/api/base.py | 2 + nomad/api/exceptions.py | 4 ++ nomad/api/variable.py | 110 ++++++++++++++++++++++++++++++++++++++++ nomad/api/variables.py | 70 +++++++++++++++++++++++++ run_tests.sh | 25 +++++++++ setup.py | 3 +- tests/test_namespace.py | 18 +++---- tests/test_variable.py | 103 +++++++++++++++++++++++++++++++++++++ tests/test_variables.py | 55 ++++++++++++++++++++ 16 files changed, 475 insertions(+), 12 deletions(-) create mode 100644 docs/api/variable.md create mode 100644 docs/api/variables.md create mode 100644 nomad/api/variable.py create mode 100644 nomad/api/variables.py create mode 100755 run_tests.sh create mode 100644 tests/test_variable.py create mode 100644 tests/test_variables.py diff --git a/.gitignore b/.gitignore index 0f1b3d3..ddeb60f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,7 @@ .build .venv -example.nomad +# dev related files example.json +example.nomad +nomad.log diff --git a/CHANGELOG.md b/CHANGELOG.md index 00f3317..8c89101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ -## Unreleased +## 1.5.0 * Add `namespace` agrument support for `get_allocations` and `get_deployments` endpoints (#133) * Add Python 3.10 support (#133) * Add support for pre-populated Sessions (#132) * Add scaling policy endpoint (#136) +* Drop Python 3.5 support +* Up `requests` lib version +* Add support for /var and /vars endpoints () * Add support for /search endpoint (#134) diff --git a/README.md b/README.md index 9edc914..817439c 100644 --- a/README.md +++ b/README.md @@ -137,3 +137,5 @@ NOMAD_IP=127.0.0.1 NOMAD_VERSION= py.test --cov=nomad --cov-re - [x] Status [:link:](docs/api/status.md) - [x] System [:link:](docs/api/system.md) - [x] Validate [:link:](docs/api/validate.md) + - [x] Variable [:link:](docs/api/variable.md) + - [x] Variables [:link:](docs/api/variables.md) diff --git a/docs/api/variable.md b/docs/api/variable.md new file mode 100644 index 0000000..a4139bc --- /dev/null +++ b/docs/api/variable.md @@ -0,0 +1,54 @@ +## Variable + +### Read variable + +This endpoint reads a specific variable by path. This API returns the decrypted variable body. + +https://developer.hashicorp.com/nomad/api-docs/variables + +Example: + +``` +import nomad + +my_nomad = nomad.Nomad(host='192.168.33.10') + +variable = my_nomad.variable.get_variable("path_to_variable") +``` + +### Create variable + +This endpoint creates or updates a variable. + +https://developer.hashicorp.com/nomad/api-docs/variables + +Example: + +``` +import nomad + +my_nomad = nomad.Nomad(host='192.168.33.10') + +job = my_nomad.variable.create_variable("path_to_variable") + +payload = { + "Items": {"user": "test", "password": "test123"}, +} +my_nomad.variable.create_variable("variable_path", payload) +``` + +### Delete variable + +This endpoint deletes a specific variable by path. + +https://developer.hashicorp.com/nomad/api-docs/variables + +Example: + +``` +import nomad + +my_nomad = nomad.Nomad(host='192.168.33.10') + +my_nomad.variable.delete_variable("path_to_variable") +``` \ No newline at end of file diff --git a/docs/api/variables.md b/docs/api/variables.md new file mode 100644 index 0000000..b1c3abe --- /dev/null +++ b/docs/api/variables.md @@ -0,0 +1,20 @@ +## Variables + +### List variables + +This endpoint lists all known variables in the system registered with Nomad. + +https://developer.hashicorp.com/nomad/api-docs/variables + +Example: + +``` +import nomad + +my_nomad = nomad.Nomad(host='192.168.33.10') + +variables = my_nomad.variables.get_variables() + +for var in variables: + print(var) +``` diff --git a/nomad/__init__.py b/nomad/__init__.py index 6754b1f..ca5d767 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -98,6 +98,8 @@ def __init__(self, self._status = api.Status(**self.requester_settings) self._system = api.System(**self.requester_settings) self._validate = api.Validate(**self.requester_settings) + self._variable = api.Variable(**self.requester_settings) + self._variables = api.Variables(**self.requester_settings) def get_uri(self): if self.secure: @@ -211,3 +213,11 @@ def search(self): @property def metrics(self): return self._metrics + + @property + def variable(self): + return self._variable + + @property + def variables(self): + return self._variables \ No newline at end of file diff --git a/nomad/api/__init__.py b/nomad/api/__init__.py index c50314d..f326865 100644 --- a/nomad/api/__init__.py +++ b/nomad/api/__init__.py @@ -25,3 +25,5 @@ from nomad.api.status import Status from nomad.api.system import System from nomad.api.validate import Validate +from nomad.api.variable import Variable +from nomad.api.variables import Variables \ No newline at end of file diff --git a/nomad/api/base.py b/nomad/api/base.py index 71036f9..e0fae4d 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -162,6 +162,8 @@ def _request(self, method, endpoint, params=None, data=None, json=None, headers= raise nomad.api.exceptions.URLNotAuthorizedNomadException(response) elif response.status_code == 404: raise nomad.api.exceptions.URLNotFoundNomadException(response) + elif response.status_code == 409: + raise nomad.api.exceptions.VariableConflict(response) else: raise nomad.api.exceptions.BaseNomadException(response) diff --git a/nomad/api/exceptions.py b/nomad/api/exceptions.py index 99f24aa..ca5e343 100644 --- a/nomad/api/exceptions.py +++ b/nomad/api/exceptions.py @@ -25,6 +25,10 @@ class BadRequestNomadException(BaseNomadException): """Validation failure and if a parameter is modified in the request, it could potentially succeed.""" +class VariableConflict(BaseNomadException): + """In the case of a compare-and-set variable conflict""" + + class InvalidParameters(Exception): """Invalid parameters given""" diff --git a/nomad/api/variable.py b/nomad/api/variable.py new file mode 100644 index 0000000..434d543 --- /dev/null +++ b/nomad/api/variable.py @@ -0,0 +1,110 @@ +import nomad.api.exceptions + +from nomad.api.base import Requester + + +class Variable(Requester): + + """ + The /var endpoints are used to read or create variables. + https://developer.hashicorp.com/nomad/api-docs/variables + """ + + ENDPOINT = "var" + + def __init__(self, **kwargs): + super(Variable, self).__init__(**kwargs) + + def __str__(self): + return "{0}".format(self.__dict__) + + def __repr__(self): + return "{0}".format(self.__dict__) + + def __getattr__(self, item): + raise AttributeError + + def __contains__(self, item): + try: + self.get_variable(item) + return True + except nomad.api.exceptions.URLNotFoundNomadException: + return False + + def __getitem__(self, item): + try: + return self.get_variable(item) + except nomad.api.exceptions.URLNotFoundNomadException: + raise KeyError + + def get_variable(self, var_path, namespace=None): + """ + This endpoint reads a specific variable by path. This API returns the decrypted variable body. + https://developer.hashicorp.com/nomad/api-docs/variables#read-variable + + arguments: + - var_path :(str), path to variable + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + """ + params = {} + if namespace: + params["namespace"] = namespace + + return self.request(var_path, params=params, method="get").json() + + def create_variable(self, var_path, payload, namespace=None, cas=None): + """ + This endpoint creates or updates a variable. + https://developer.hashicorp.com/nomad/api-docs/variables#create-variable + + arguments: + - var_path :(str), path to variable + - payload :(dict), variable object. Example: + https://developer.hashicorp.com/nomad/api-docs/variables#sample-payload + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. + - cas :(int) optional, If set, the variable will only be deleted if the cas value matches the + current variables ModifyIndex. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.VariableConflict + """ + params = {} + if cas is not None: + params["cas"] = cas + if namespace: + params["namespace"] = namespace + + return self.request(var_path, params=params, json=payload, method="put").json() + + + def delete_variable(self, var_path, namespace=None, cas=None): + """ + This endpoint reads a specific variable by path. This API returns the decrypted variable body. + https://developer.hashicorp.com/nomad/api-docs/variables#delete-variable + + arguments: + - var_path :(str), path to variable + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. + - cas :(int) optional, If set, the variable will only be deleted if the cas value matches the + current variables ModifyIndex. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.VariableConflict + """ + params = {} + if cas is not None: + params["cas"] = cas + if namespace: + params["namespace"] = namespace + + # we need to return json here but because of bug we recieve empty response + return self.request(var_path, params=params, method="delete") diff --git a/nomad/api/variables.py b/nomad/api/variables.py new file mode 100644 index 0000000..75338ec --- /dev/null +++ b/nomad/api/variables.py @@ -0,0 +1,70 @@ +import nomad.api.exceptions + +from nomad.api.base import Requester + + +class Variables(Requester): + + """ + The /vars endpoints are used to query for and interact with variables. + https://developer.hashicorp.com/nomad/api-docs/variables + """ + + ENDPOINT = "vars" + + def __init__(self, **kwargs): + super(Variables, self).__init__(**kwargs) + + def __str__(self): + return "{0}".format(self.__dict__) + + def __repr__(self): + return "{0}".format(self.__dict__) + + def __getattr__(self, item): + raise AttributeError + + def __contains__(self, item): + variables = self.get_variables() + + for var in variables: + if var["Path"] == item: + return True + else: + return False + + def __getitem__(self, item): + variables = self.get_variables() + + for var in variables: + if var["Path"] == item: + return var + else: + raise KeyError + + def __iter__(self): + variables = self.get_variables() + return iter(variables) + + def get_variables(self, prefix="", namespace=None): + """ + This endpoint lists variables. + https://developer.hashicorp.com/nomad/api-docs/variables + + optional_arguments: + - prefix, (default "") Specifies a string to filter variables on based on an index prefix. + This is specified as a query string parameter. + - namespace :(str) optional, Specifies the target namespace. + Specifying * will return all variables across all the authorized namespaces. + returns: list of dicts + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + """ + params = {"prefix": prefix} + if namespace: + params["namespace"] = namespace + + return self.request(params=params, method="get").json() + + diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..61c8cfa --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Run tests with local Nomad binaries. +set -ueo pipefail + +if [ "${1-}" == "init" ]; then + virtualenv .venv + pip install -r requirements-dev.txt +fi + +source .venv/bin/activate +NOMAD_VERSION=`nomad --version | awk '{print $2}' | cut -c2-` + +echo "Run Nomad in dev mode" +nomad agent -dev -node pynomad1 --acl-enabled &> nomad.log & +NOMAD_PID=$! + +sleep 3 + +echo "Run tests with Nomad $NOMAD_VERSION" +NOMAD_IP=127.0.0.1 NOMAD_VERSION=$NOMAD_VERSION py.test -s --cov=nomad --cov-report=term-missing --runxfail tests/ || true + + +echo "Kill nomad in background" +kill ${NOMAD_PID} diff --git a/setup.py b/setup.py index 47e58cc..76f9e97 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name='python-nomad', - version='1.4.1', + version='1.5.0', install_requires=['requests'], packages=['nomad', 'nomad.api'], url='http://github.com/jrxfive/python-nomad', @@ -25,6 +25,7 @@ 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', ], keywords='nomad hashicorp client', ) diff --git a/tests/test_namespace.py b/tests/test_namespace.py index 0d30571..57077bf 100644 --- a/tests/test_namespace.py +++ b/tests/test_namespace.py @@ -8,15 +8,15 @@ @responses.activate def test_create_namespace(nomad_setup): - responses.add( - responses.POST, - "http://{ip}:{port}/v1/namespace".format(ip=common.IP, port=common.NOMAD_PORT), - status=200 - ) - - namespace_api = '{"Name":"api","Description":"api server namespace"}' - namespace = json.loads(namespace_api) - nomad_setup.namespace.create_namespace(namespace) + responses.add( + responses.POST, + "http://{ip}:{port}/v1/namespace".format(ip=common.IP, port=common.NOMAD_PORT), + status=200 + ) + + namespace_api = '{"Name":"api","Description":"api server namespace"}' + namespace = json.loads(namespace_api) + nomad_setup.namespace.create_namespace(namespace) @responses.activate diff --git a/tests/test_variable.py b/tests/test_variable.py new file mode 100644 index 0000000..66ff38a --- /dev/null +++ b/tests/test_variable.py @@ -0,0 +1,103 @@ +import pytest +import os +# Nomad doesn't have any variables by default +from nomad.api import exceptions + + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_create_variable(nomad_setup): + payload = { + "Items": {"user": "test", "password": "test123"}, + } + nomad_setup.variable.create_variable("example/first", payload) + assert "example/first" in nomad_setup.variables + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_create_variable_in_namespace(nomad_setup): + payload = { + "Items": {"user": "test2", "password": "321tset"}, + } + nomad_setup.variable.create_variable("example/second", payload, namespace="default") + assert "example/second" in nomad_setup.variables + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_create_variable_with_cas(nomad_setup): + payload = { + "Items": {"user": "test3", "password": "321tset123"}, + } + nomad_setup.variable.create_variable("example/third", payload, cas=0) + assert "example/third" in nomad_setup.variables + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_get_variable_and_check_value(nomad_setup): + var = nomad_setup.variable.get_variable("example/first") + assert var["Items"]["user"] == "test" + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_get_variable_in_namespace(nomad_setup): + var = nomad_setup.variable.get_variable("example/first", namespace="default") + assert var["Items"]["user"] == "test" + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_get_no_exist_variable(nomad_setup): + with pytest.raises(KeyError): + assert nomad_setup.variable["no_exist"] + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variable_getitem_exist(nomad_setup): + var = nomad_setup.variable["example/first"] + assert isinstance(var, dict) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variable_str(nomad_setup): + assert isinstance(str(nomad_setup.variable), str) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variable_repr(nomad_setup): + assert isinstance(repr(nomad_setup.variable), str) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variable_getattr(nomad_setup): + with pytest.raises(AttributeError): + nomad_setup.variable.does_not_exist + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variable_exist(nomad_setup): + assert "example/second" in nomad_setup.variable + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variable_no_exist(nomad_setup): + assert "no_exist" not in nomad_setup.variable + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variable_getitem_not_exist(nomad_setup): + with pytest.raises(KeyError): + nomad_setup.variable["no_exists"] + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_delete_variable(nomad_setup): + assert 3 == len(nomad_setup.variables.get_variables()) + nomad_setup.variable.delete_variable("example/third") + assert "example/third" not in nomad_setup.variables + assert 2 == len(nomad_setup.variables.get_variables()) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_delete_variable_in_namespace(nomad_setup): + assert 2 == len(nomad_setup.variables.get_variables()) + nomad_setup.variable.delete_variable("example/second", namespace="default") + assert "example/third" not in nomad_setup.variables + assert 1 == len(nomad_setup.variables.get_variables()) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_delete_variable_with_cas(nomad_setup): + variable_path = "variable_with_cas" + payload = { + "Items": {"user": "test4", "password": "test123567"}, + } + var = nomad_setup.variable.create_variable(variable_path, payload) + assert variable_path in nomad_setup.variables + with pytest.raises(exceptions.VariableConflict): + nomad_setup.variable.delete_variable(variable_path, cas=var["ModifyIndex"] + 1) + assert variable_path in nomad_setup.variables + nomad_setup.variable.delete_variable(variable_path, cas=var["ModifyIndex"]) + assert variable_path not in nomad_setup.variables diff --git a/tests/test_variables.py b/tests/test_variables.py new file mode 100644 index 0000000..cf096e2 --- /dev/null +++ b/tests/test_variables.py @@ -0,0 +1,55 @@ +import pytest +import os + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_get_variables(nomad_setup): + assert 1 == len(nomad_setup.variables.get_variables()) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_get_variables_with_prefix(nomad_setup): + assert 1 == len(nomad_setup.variables.get_variables("example/first")) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_get_variables_with_prefix_no_exist(nomad_setup): + assert 0 == len(nomad_setup.variables.get_variables("no_exist_var")) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_get_variables_from_namespace(nomad_setup): + assert 1 == len(nomad_setup.variables.get_variables(namespace="default")) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_iter_variables(nomad_setup): + assert hasattr(nomad_setup.variables, '__iter__') + for _ in nomad_setup.variables: + pass + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variables_str(nomad_setup): + assert isinstance(str(nomad_setup.variables), str) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variables_repr(nomad_setup): + assert isinstance(repr(nomad_setup.variables), str) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variables_not_exist(nomad_setup): + assert "no_exist" not in nomad_setup.variables + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variables_exist(nomad_setup): + assert "example/first" in nomad_setup.variables + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variables_getitem_exist(nomad_setup): + var = nomad_setup.variables["example/first"] + assert isinstance(var, dict) + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variables_getitem_not_exist(nomad_setup): + with pytest.raises(KeyError): + nomad_setup.variables["no_exists"] + +@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +def test_variables_getattr(nomad_setup): + with pytest.raises(AttributeError): + nomad_setup.variables.does_not_exist \ No newline at end of file From 490b1489f698dd7860804c77b13fe1b6a4bafee9 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Tue, 6 Dec 2022 16:16:25 +0100 Subject: [PATCH 44/69] Remove support for Python 2, Python 3.6 and upgrade deps (#142) --- .github/workflows/main.yml | 11 +++-------- CHANGELOG.md | 4 ++++ mkdocs.yml | 2 ++ requirements-dev-py27.txt | 7 ------- requirements-dev.txt | 12 ++++++------ requirements.txt | 2 +- setup.py | 2 -- 7 files changed, 16 insertions(+), 24 deletions(-) delete mode 100644 requirements-dev-py27.txt diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 99f5e25..59d98e2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,13 +20,12 @@ jobs: env: NOMAD_IP: '127.0.0.1' NOMAD_PORT: '4646' - NOMAD_LATEST: '1.1.18' strategy: fail-fast: false matrix: - python-version: ['2.7', '3.7', '3.10'] - nomad-version: ['1.0.18', '1.1.18', '1.2.14', '1.3.7', '1.4.2'] + python-version: ['3.7', '3.11'] # the oldest and newest support versions + nomad-version: ['1.0.18', '1.1.18', '1.2.15', '1.3.8', '1.4.3'] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} @@ -57,11 +56,7 @@ jobs: run: | python -m pip install --upgrade pip if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - if [ $PYTHON_VERSION = "2.7" ]; then - pip install -r requirements-dev-py27.txt - else - pip install -r requirements-dev.txt - fi + pip install -r requirements-dev.txt - name: Before Tests shell: bash run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c89101..94d2a63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.0 (unreleased) +* Up `requests` lib version to 2.28.1 +* Drop Python 2 and Python 3.6 support + ## 1.5.0 * Add `namespace` agrument support for `get_allocations` and `get_deployments` endpoints (#133) * Add Python 3.10 support (#133) diff --git a/mkdocs.yml b/mkdocs.yml index 4119c91..7f7575b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,5 +29,7 @@ pages: - Namespace: 'api/namespace.md' - Namespaces: 'api/namespaces.md' - Sentinel: 'api/sentinel.md' + - Variable: 'api/variable.md' + - Variables: 'api/variables.md' - Validate: 'api/validate.md' - Contributing: 'CONTRIBUTING.md' diff --git a/requirements-dev-py27.txt b/requirements-dev-py27.txt deleted file mode 100644 index 21de22f..0000000 --- a/requirements-dev-py27.txt +++ /dev/null @@ -1,7 +0,0 @@ -coverage==5.2.1 -pytest==4.6.11 -pytest-cov==2.12.1 -mkdocs==1.0.4 -mock==1.2.0 -flaky==3.7.0 -responses==0.13.4 diff --git a/requirements-dev.txt b/requirements-dev.txt index eac8c93..6c4caad 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,7 @@ -coverage==5.2.1 -pytest==6.2.4 -pytest-cov==2.12.1 -mkdocs==1.2.3 -mock==1.2.0 +coverage==6.5.0 +pytest==7.2.0 +pytest-cov==4.0.0 +mkdocs==1.4.2 +mock==4.0.3 flaky==3.7.0 -responses==0.13.4 +responses==0.22.0 diff --git a/requirements.txt b/requirements.txt index a743bbe..d15ce5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -requests==2.27.1 +requests==2.28.1 diff --git a/setup.py b/setup.py index 76f9e97..1d2e987 100644 --- a/setup.py +++ b/setup.py @@ -19,8 +19,6 @@ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', From 78452b63e19e9c292752386262534ed14a5c0e18 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Thu, 22 Dec 2022 17:09:06 +0100 Subject: [PATCH 45/69] Add pylint (#143) * add pylint check to repo --- .github/workflows/lint.yml | 31 ++++++++++ .github/workflows/main.yml | 3 - .pylintrc | 6 ++ CHANGELOG.md | 5 +- CONTRIBUTING.md | 6 +- nomad/__init__.py | 105 +++++++++++++++++++++++++++++-- nomad/api/__init__.py | 3 +- nomad/api/acl.py | 36 +++++------ nomad/api/agent.py | 11 ++-- nomad/api/allocation.py | 19 +++--- nomad/api/allocations.py | 8 +-- nomad/api/base.py | 108 ++++++++++++++++++++------------ nomad/api/client.py | 119 ++++++++++++++++++----------------- nomad/api/deployment.py | 87 ++++++++++++++------------ nomad/api/deployments.py | 28 ++++----- nomad/api/evaluation.py | 34 +++++----- nomad/api/evaluations.py | 26 ++++---- nomad/api/event.py | 32 +++++----- nomad/api/exceptions.py | 7 ++- nomad/api/job.py | 124 ++++++++++++++++++------------------- nomad/api/jobs.py | 21 ++++--- nomad/api/metrics.py | 10 +-- nomad/api/namespace.py | 47 +++++++------- nomad/api/namespaces.py | 27 ++++---- nomad/api/node.py | 84 ++++++++++++------------- nomad/api/nodes.py | 34 +++++----- nomad/api/operator.py | 10 +-- nomad/api/regions.py | 28 ++++----- nomad/api/scaling.py | 21 ++++--- nomad/api/search.py | 16 ++--- nomad/api/sentinel.py | 28 +++++---- nomad/api/status.py | 34 +++++----- nomad/api/system.py | 10 +-- nomad/api/validate.py | 10 +-- nomad/api/variable.py | 16 ++--- nomad/api/variables.py | 22 +++---- requirements-dev.txt | 2 +- setup.py | 3 +- tests/test_acl.py | 8 +-- tests/test_job.py | 13 ++-- tests/test_sentinel.py | 8 +-- 41 files changed, 717 insertions(+), 533 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .pylintrc diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..218d60e --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,31 @@ +name: Lint + +on: + push: + branches: + - master + pull_request: + workflow_dispatch: + + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Python 3 + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install Dependencies + shell: bash + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pylint==2.15.8 + - name: Lint + shell: bash + run: | + pylint nomad/ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 59d98e2..9a33440 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,7 +37,6 @@ jobs: NOMAD_VERSION: ${{ matrix.nomad-version }} shell: bash run: | - echo $NOMAD_VERSION echo ${NOMAD_VERSION} echo "downloading nomad" @@ -50,8 +49,6 @@ jobs: /tmp/nomad agent -dev -bind ${NOMAD_IP} -node pynomad1 --acl-enabled > /dev/null 2>&1 & sleep 30 - name: Install Dependencies - env: - PYTHON_VERSION: ${{ matrix.python-version }} shell: bash run: | python -m pip install --upgrade pip diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..279e122 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,6 @@ +[FORMAT] +# Maximum number of characters on a single line. +max-line-length=120 + +[MESSAGES CONTROL] +disable=duplicate-code \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 94d2a63..4431209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ## 2.0.0 (unreleased) -* Up `requests` lib version to 2.28.1 +### BREAKING CHANGES * Drop Python 2 and Python 3.6 support +* Rename `id` arguments to `_id` across of code base +### Other changes +* Up `requests` lib version to 2.28.1 ## 1.5.0 * Add `namespace` agrument support for `get_allocations` and `get_deployments` endpoints (#133) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fb4e933..f888149 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -180,7 +180,7 @@ class (Requester): ENDPOINT = "" def __init__(self, **kwargs): - super(, self).__init__(**kwargs) + super().__init__(**kwargs) ``` ##### Entity @@ -249,7 +249,7 @@ class cat(Requester): ENDPOINT = "client/fs/cat" def __init__(self, **kwargs): - super(cat, self).__init__(**kwargs) + super().__init__(**kwargs) def read_file(self, id=None, path="/"): """ Read contents of a file in an allocation directory. @@ -382,7 +382,7 @@ class (Requester): ENDPOINT = "" def __init__(self, **kwargs): - super(, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): return "{0}".format(self.__dict__) diff --git a/nomad/__init__.py b/nomad/__init__.py index ca5d767..be2be26 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -1,10 +1,13 @@ -import nomad.api as api +"""Nomad Python library""" import os +from nomad import api -class Nomad(object): - - def __init__(self, +class Nomad(): # pylint: disable=too-many-public-methods,too-many-instance-attributes + """ + Nomad API + """ + def __init__(self, # pylint: disable=too-many-arguments host='127.0.0.1', secure=False, port=4646, @@ -102,122 +105,212 @@ def __init__(self, self._variables = api.Variables(**self.requester_settings) def get_uri(self): + """ + Get Nomad host + """ if self.secure: protocol = "https" else: protocol = "http" - return "{protocol}://{host}".format(protocol=protocol, host=self.host) + return f"{protocol}://{self.host}" def get_namespace(self): + """ + Get Nomad namaspace + """ return self.__namespace def get_token(self): + """ + Get Nomad token + """ return self.token @property def jobs(self): + """ + Jobs API + """ return self._jobs @property def job(self): + """ + Job API + """ return self._job @property def nodes(self): + """ + Nodes API + """ return self._nodes @property def node(self): + """ + Node API + """ return self._node @property def allocations(self): + """ + Allocations API + """ return self._allocations @property def allocation(self): + """ + Allocation API + """ return self._allocation @property def evaluations(self): + """ + Evaluations API + """ return self._evaluations @property def evaluation(self): + """ + Evaluation API + """ return self._evaluation @property def event(self): + """ + Event API + """ return self._event @property def agent(self): + """ + Agent API + """ return self._agent @property def client(self): + """ + Client API + """ return self._client @property def deployments(self): + """ + Deployments API + """ return self._deployments @property def deployment(self): + """ + Deployment API + """ return self._deployment @property def regions(self): + """ + Regions API + """ return self._regions @property def scaling(self): + """ + Scaling API + """ return self._scaling @property def status(self): + """ + Status API + """ return self._status @property def system(self): + """ + System API + """ return self._system @property def operator(self): + """ + Operator API + """ return self._operator @property def validate(self): + """ + Validate API + """ return self._validate @property def namespaces(self): + """ + Namespaces API + """ return self._namespaces @property def namespace(self): + """ + Namespace API + """ return self._namespace @property def acl(self): + """ + ACL API + """ return self._acl @property def sentinel(self): + """ + Sentinel API + """ return self._sentinel @property def search(self): + """ + Search API + """ return self._search @property def metrics(self): + """ + Metrics API + """ return self._metrics @property def variable(self): + """ + Variable API + """ return self._variable @property def variables(self): - return self._variables \ No newline at end of file + """ + Variables API + """ + return self._variables diff --git a/nomad/api/__init__.py b/nomad/api/__init__.py index f326865..3886570 100644 --- a/nomad/api/__init__.py +++ b/nomad/api/__init__.py @@ -1,3 +1,4 @@ +"""Nomad Python library""" import nomad.api.exceptions from nomad.api.acl import Acl from nomad.api.agent import Agent @@ -26,4 +27,4 @@ from nomad.api.system import System from nomad.api.validate import Validate from nomad.api.variable import Variable -from nomad.api.variables import Variables \ No newline at end of file +from nomad.api.variables import Variables diff --git a/nomad/api/acl.py b/nomad/api/acl.py index af23e80..857c2dc 100644 --- a/nomad/api/acl.py +++ b/nomad/api/acl.py @@ -1,4 +1,4 @@ -import nomad.api.exceptions +"""Nomad ACL: https://developer.hashicorp.com/nomad/api-docs/acl""" from nomad.api.base import Requester @@ -13,13 +13,13 @@ class Acl(Requester): ENDPOINT = "acl" def __init__(self, **kwargs): - super(Acl, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): raise AttributeError @@ -51,7 +51,7 @@ def get_tokens(self): return self.request("tokens", method="get").json() - def get_token(self, id): + def get_token(self, _id): """ Retrieve specific token. https://www.nomadproject.io/api/acl-tokens.html @@ -62,7 +62,7 @@ def get_token(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("token", id, method="get").json() + return self.request("token", _id, method="get").json() def get_self_token(self): """ Retrieve self token used for auth. @@ -92,7 +92,7 @@ def create_token(self, token): """ return self.request("token", json=token, method="post").json() - def delete_token(self, id): + def delete_token(self, _id): """ Delete specific token. https://www.nomadproject.io/api/acl-tokens.html @@ -103,9 +103,9 @@ def delete_token(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("token", id, method="delete").ok + return self.request("token", _id, method="delete").ok - def update_token(self, id, token): + def update_token(self, _id, token): """ Update token. https://www.nomadproject.io/api/acl-tokens.html @@ -119,7 +119,7 @@ def update_token(self, id, token): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("token", id, json=token, method="post").json() + return self.request("token", _id, json=token, method="post").json() def get_policies(self): """ Get a list of policies. @@ -134,7 +134,7 @@ def get_policies(self): """ return self.request("policies", method="get").json() - def create_policy(self, id, policy): + def create_policy(self, _id, policy): """ Create policy. https://www.nomadproject.io/api/acl-policies.html @@ -147,9 +147,9 @@ def create_policy(self, id, policy): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", id, json=policy, method="post") + return self.request("policy", _id, json=policy, method="post") - def get_policy(self, id): + def get_policy(self, _id): """ Get a spacific. https://www.nomadproject.io/api/acl-policies.html @@ -160,9 +160,9 @@ def get_policy(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", id, method="get").json() + return self.request("policy", _id, method="get").json() - def update_policy(self, id, policy): + def update_policy(self, _id, policy): """ Create policy. https://www.nomadproject.io/api/acl-policies.html @@ -176,9 +176,9 @@ def update_policy(self, id, policy): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", id, json=policy, method="post") + return self.request("policy", _id, json=policy, method="post") - def delete_policy(self, id): + def delete_policy(self, _id): """ Delete specific policy. https://www.nomadproject.io/api/acl-policies.html @@ -191,4 +191,4 @@ def delete_policy(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", id, method="delete").ok + return self.request("policy", _id, method="delete").ok diff --git a/nomad/api/agent.py b/nomad/api/agent.py index c954c29..403d035 100644 --- a/nomad/api/agent.py +++ b/nomad/api/agent.py @@ -1,5 +1,4 @@ -import nomad.api.exceptions - +"""Nomad Agent: https://developer.hashicorp.com/nomad/api-docs/agent""" from nomad.api.base import Requester @@ -9,16 +8,16 @@ class Agent(Requester): ENDPOINT = "agent" def __init__(self, **kwargs): - super(Agent, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - msg = "{0} does not exist".format(item) + msg = f"{item} does not exist" raise AttributeError(msg) def get_agent(self): diff --git a/nomad/api/allocation.py b/nomad/api/allocation.py index 2967d30..c6ab949 100644 --- a/nomad/api/allocation.py +++ b/nomad/api/allocation.py @@ -1,3 +1,4 @@ +"""Nomad allocation: https://developer.hashicorp.com/nomad/api-docs/allocations""" import nomad.api.exceptions from nomad.api.base import Requester @@ -15,13 +16,13 @@ class Allocation(Requester): ENDPOINT = "allocation" def __init__(self, **kwargs): - super(Allocation, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): raise AttributeError @@ -32,6 +33,7 @@ def __contains__(self, item): if response["ID"] == item: return True + return False except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -41,10 +43,11 @@ def __getitem__(self, item): if response["ID"] == item: return response - except nomad.api.exceptions.URLNotFoundNomadException: raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc - def get_allocation(self, id): + def get_allocation(self, _id): """ Query a specific allocation. https://www.nomadproject.io/docs/http/alloc.html @@ -54,9 +57,9 @@ def get_allocation(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, method="get").json() + return self.request(_id, method="get").json() - def stop_allocation(self, id): + def stop_allocation(self, _id): """ Stop a specific allocation. https://www.nomadproject.io/api-docs/allocations/#stop-allocation @@ -66,4 +69,4 @@ def stop_allocation(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "stop", method="post").json() + return self.request(_id, "stop", method="post").json() diff --git a/nomad/api/allocations.py b/nomad/api/allocations.py index e532f0e..a89e407 100644 --- a/nomad/api/allocations.py +++ b/nomad/api/allocations.py @@ -1,8 +1,8 @@ +"""Nomad allocation: https://developer.hashicorp.com/nomad/api-docs/allocations""" from nomad.api.base import Requester class Allocations(Requester): - """ The allocations endpoint is used to query the status of allocations. By default, the agent's local region is used; another region can be @@ -13,13 +13,13 @@ class Allocations(Requester): ENDPOINT = "allocations" def __init__(self, **kwargs): - super(Allocations, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): raise AttributeError diff --git a/nomad/api/base.py b/nomad/api/base.py index e0fae4d..3fd3975 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -1,15 +1,30 @@ +"""Requester""" import requests -import nomad.api.exceptions -from requests.packages.urllib3.exceptions import InsecureRequestWarning -requests.packages.urllib3.disable_warnings(InsecureRequestWarning) +import nomad.api.exceptions -class Requester(object): +class Requester(): # pylint: disable=too-many-instance-attributes,too-few-public-methods + """ + Base object for endpoints + """ ENDPOINT = "" - def __init__(self, address=None, uri='http://127.0.0.1', port=4646, namespace=None, token=None, timeout=5, version='v1', verify=False, cert=(), region=None, session=None, **kwargs): + def __init__( # pylint: disable=too-many-arguments + self, + address=None, + uri="http://127.0.0.1", + port=4646, + namespace=None, + token=None, + timeout=5, + version="v1", + verify=False, + cert=(), + region=None, + session=None + ): self.uri = uri self.port = port self.namespace = namespace @@ -24,28 +39,32 @@ def __init__(self, address=None, uri='http://127.0.0.1', port=4646, namespace=No def _endpoint_builder(self, *args): if args: - u = "/".join(args) - return "{v}/".format(v=self.version) + u + args_str = "/".join(args) + return f"{self.version}/" + args_str + + return "/" def _required_namespace(self, endpoint): required_namespace = [ - "job", - "jobs", - "allocation", - "allocations", - "deployment", - "deployments", - "acl", - "client", - "node" - ] + "job", + "jobs", + "allocation", + "allocations", + "deployment", + "deployments", + "acl", + "client", + "node", + "variable", + "variables", + ] # split 0 -> Api Version # split 1 -> Working Endpoint - ENDPOINT_NAME = 1 endpoint_split = endpoint.split("/") try: - required = endpoint_split[ENDPOINT_NAME] in required_namespace - except: + endpoint_name = 1 + required = endpoint_split[endpoint_name] in required_namespace + except IndexError: required = False return required @@ -54,27 +73,29 @@ def _url_builder(self, endpoint): url = self.address if self.address is None: - url = "{uri}:{port}".format(uri=self.uri, port=self.port) - - url = "{url}/{endpoint}".format(url=url, endpoint=endpoint) + url = f"{self.uri}:{self.port}" + url = f"{url}/{endpoint}" return url def _query_string_builder(self, endpoint, params=None): - qs = {} + query_string = {} if not isinstance(params, dict): params = {} if ("namespace" not in params) and (self.namespace and self._required_namespace(endpoint)): - qs["namespace"] = self.namespace + query_string["namespace"] = self.namespace if "region" not in params and self.region: - qs["region"] = self.region + query_string["region"] = self.region - return qs + return query_string def request(self, *args, **kwargs): + """ + Send HTTP Request (wrapper around requests) + """ endpoint = self._endpoint_builder(self.ENDPOINT, *args) response = self._request( endpoint=endpoint, @@ -85,19 +106,30 @@ def request(self, *args, **kwargs): headers=kwargs.get("headers", None), allow_redirects=kwargs.get("allow_redirects", False), timeout=kwargs.get("timeout", self.timeout), - stream=kwargs.get("stream", False) + stream=kwargs.get("stream", False), ) return response - def _request(self, method, endpoint, params=None, data=None, json=None, headers=None, allow_redirects=None, timeout=None, stream=False): + def _request( # pylint: disable=too-many-arguments, too-many-branches + self, + method, + endpoint, + params=None, + data=None, + json=None, + headers=None, + allow_redirects=None, + timeout=None, + stream=False, + ): url = self._url_builder(endpoint) - qs = self._query_string_builder(endpoint=endpoint, params=params) + query_string = self._query_string_builder(endpoint=endpoint, params=params) if params: - params.update(qs) + params.update(query_string) else: - params = qs + params = query_string if self.token: try: @@ -156,16 +188,16 @@ def _request(self, method, endpoint, params=None, data=None, json=None, headers= if response.ok: return response - elif response.status_code == 400: + if response.status_code == 400: raise nomad.api.exceptions.BadRequestNomadException(response) - elif response.status_code == 403: + if response.status_code == 403: raise nomad.api.exceptions.URLNotAuthorizedNomadException(response) - elif response.status_code == 404: + if response.status_code == 404: raise nomad.api.exceptions.URLNotFoundNomadException(response) - elif response.status_code == 409: + if response.status_code == 409: raise nomad.api.exceptions.VariableConflict(response) - else: - raise nomad.api.exceptions.BaseNomadException(response) + + raise nomad.api.exceptions.BaseNomadException(response) except requests.exceptions.ConnectionError as error: if all([stream, timeout]): diff --git a/nomad/api/client.py b/nomad/api/client.py index 4178f57..7b495fe 100644 --- a/nomad/api/client.py +++ b/nomad/api/client.py @@ -1,8 +1,11 @@ +# we want to have backward compatibility here +# pylint: disable=invalid-name,too-many-instance-attributes,too-many-arguments +"""Nomad Client: https://developer.hashicorp.com/nomad/api-docs/client""" from nomad.api.base import Requester - - -class Client(object): - +class Client(): + """ + The /client endpoints are used to interact with the Nomad clients. + """ def __init__(self, **kwargs): self.ls = ls(**kwargs) self.cat = cat(**kwargs) @@ -16,13 +19,14 @@ def __init__(self, **kwargs): self.gc_all_allocations = gc_all_allocations(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) class ls(Requester): @@ -38,25 +42,25 @@ class ls(Requester): ENDPOINT = "client/fs/ls" def __init__(self, **kwargs): - super(ls, self).__init__(**kwargs) + super().__init__(**kwargs) - def list_files(self, id=None, path="/"): + def list_files(self, _id=None, path="/"): """ List files in an allocation directory. https://www.nomadproject.io/docs/http/client-fs-ls.html arguments: - - id - - path + - _id + - path returns: list raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - if id: - return self.request(id, params={"path": path}, method="get").json() - else: - return self.request(params={"path": path}, method="get").json() + if _id: + return self.request(_id, params={"path": path}, method="get").json() + + return self.request(params={"path": path}, method="get").json() class cat(Requester): @@ -73,25 +77,25 @@ class cat(Requester): ENDPOINT = "client/fs/cat" def __init__(self, **kwargs): - super(cat, self).__init__(**kwargs) + super().__init__(**kwargs) - def read_file(self, id=None, path="/"): + def read_file(self, _id=None, path="/"): """ Read contents of a file in an allocation directory. https://www.nomadproject.io/docs/http/client-fs-cat.html arguments: - - id + - _id - path returns: (str) text raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - if id: - return self.request(id, params={"path": path}, method="get").text - else: - return self.request(params={"path": path}, method="get").text + if _id: + return self.request(_id, params={"path": path}, method="get").text + + return self.request(params={"path": path}, method="get").text class read_at(Requester): @@ -105,15 +109,15 @@ class read_at(Requester): ENDPOINT = "client/fs/readat" def __init__(self, **kwargs): - super(read_at, self).__init__(**kwargs) + super().__init__(**kwargs) - def read_file_offset(self, id, offset, limit, path="/"): + def read_file_offset(self, _id, offset, limit, path="/"): """ Read contents of a file in an allocation directory. https://www.nomadproject.io/docs/http/client-fs-cat.html arguments: - - id: (str) allocation_id required + - _id: (str) allocation_id required - offset: (int) required - limit: (int) required - path: (str) optional @@ -127,7 +131,7 @@ def read_file_offset(self, id, offset, limit, path="/"): "offset": offset, "limit": limit } - return self.request(id, params=params, method="get").text + return self.request(_id, params=params, method="get").text class stream_file(Requester): @@ -141,15 +145,15 @@ class stream_file(Requester): ENDPOINT = "client/fs/stream" def __init__(self, **kwargs): - super(stream_file, self).__init__(**kwargs) + super().__init__(**kwargs) - def stream(self, id, offset, origin, path="/"): + def stream(self, _id, offset, origin, path="/"): """ This endpoint streams the contents of a file in an allocation directory. https://www.nomadproject.io/api/client.html#stream-file arguments: - - id: (str) allocation_id required + - _id: (str) allocation_id required - offset: (int) required - origin: (str) either start|end - path: (str) optional @@ -163,7 +167,7 @@ def stream(self, id, offset, origin, path="/"): "offset": offset, "origin": origin } - return self.request(id, params=params, method="get").text + return self.request(_id, params=params, method="get").text class stream_logs(Requester): @@ -177,17 +181,17 @@ class stream_logs(Requester): ENDPOINT = "client/fs/logs" def __init__(self, **kwargs): - super(stream_logs, self).__init__(**kwargs) + super().__init__(**kwargs) - def stream(self, id, task, type, follow=False, offset=0, origin="start", plain=False): + def stream(self, _id, task, _type, follow=False, offset=0, origin="start", plain=False): """ This endpoint streams a task's stderr/stdout logs. https://www.nomadproject.io/api/client.html#stream-logs arguments: - - id: (str) allocation_id required + - _id: (str) allocation_id required - task: (str) name of the task inside the allocation to stream logs from - - type: (str) Specifies the stream to stream. Either "stderr|stdout" + - _type: (str) Specifies the stream to stream. Either "stderr|stdout" - follow: (bool) default false - offset: (int) default 0 - origin: (str) either start|end, default "start" @@ -199,19 +203,18 @@ def stream(self, id, task, type, follow=False, offset=0, origin="start", plain=F """ params = { "task": task, - "type": type, + "type": _type, "follow": follow, "offset": offset, "origin": origin, "plain": plain } - return self.request(id, params=params, method="get").text + return self.request(_id, params=params, method="get").text class stat(Requester): - """ - The /fs/stat endpoint is used to show stat information + The /fs/stat endpoint is used to show stat information This API endpoint is hosted by the Nomad client and requests have to be made to the Nomad client where the particular allocation was placed. @@ -221,25 +224,25 @@ class stat(Requester): ENDPOINT = "client/fs/stat" def __init__(self, **kwargs): - super(stat, self).__init__(**kwargs) + super().__init__(**kwargs) - def stat_file(self, id=None, path="/"): + def stat_file(self, _id=None, path="/"): """ Stat a file in an allocation directory. https://www.nomadproject.io/docs/http/client-fs-stat.html arguments: - - id + - _id - path returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - if id: - return self.request(id, params={"path": path}, method="get").json() - else: - return self.request(params={"path": path}, method="get").json() + if _id: + return self.request(_id, params={"path": path}, method="get").json() + + return self.request(params={"path": path}, method="get").json() class stats(Requester): @@ -255,7 +258,7 @@ class stats(Requester): ENDPOINT = "client/stats" def __init__(self, **kwargs): - super(stats, self).__init__(**kwargs) + super().__init__(**kwargs) def read_stats(self, node_id=None): """ Query the actual resources consumed on a node. @@ -275,8 +278,8 @@ class allocation(Requester): """ The allocation/:alloc_id/stats endpoint is used to query the actual - resources consumed by an allocation. The API endpoint is hosted by the - Nomad client and requests have to be made to the nomad client whose + resources consumed by an allocation. The API endpoint is hosted by the + Nomad client and requests have to be made to the nomad client whose resource usage metrics are of interest. https://www.nomadproject.io/api/client.html#read-allocation @@ -285,9 +288,9 @@ class allocation(Requester): ENDPOINT = "client/allocation" def __init__(self, **kwargs): - super(allocation, self).__init__(**kwargs) + super().__init__(**kwargs) - def read_allocation_stats(self, id): + def read_allocation_stats(self, _id): """ Query the actual resources consumed by an allocation. https://www.nomadproject.io/api/client.html#read-allocation @@ -298,9 +301,9 @@ def read_allocation_stats(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "stats", method="get").json() + return self.request(_id, "stats", method="get").json() - def restart_allocation(self, id): + def restart_allocation(self, _id): """ Restart a specific allocation. https://www.nomadproject.io/api-docs/allocations/#restart-allocation @@ -310,7 +313,7 @@ def restart_allocation(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "restart", method="post").json() + return self.request(_id, "restart", method="post").json() class gc_allocation(Requester): @@ -324,20 +327,20 @@ class gc_allocation(Requester): ENDPOINT = "client/allocation" def __init__(self, **kwargs): - super(gc_allocation, self).__init__(**kwargs) + super().__init__(**kwargs) - def garbage_collect(self, id): + def garbage_collect(self, _id): """ This endpoint forces a garbage collection of a particular, stopped allocation on a node. https://www.nomadproject.io/api/client.html#gc-allocation arguments: - - id: (str) full allocation_id + - _id: (str) full allocation_id raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - self.request(id, "gc", method="get") + self.request(_id, "gc", method="get") class gc_all_allocations(Requester): @@ -351,7 +354,7 @@ class gc_all_allocations(Requester): ENDPOINT = "client/gc" def __init__(self, **kwargs): - super(gc_all_allocations, self).__init__(**kwargs) + super().__init__(**kwargs) def garbage_collect(self, node_id=None): """ This endpoint forces a garbage collection of all stopped allocations on a node. diff --git a/nomad/api/deployment.py b/nomad/api/deployment.py index 238bf2f..90e27e2 100644 --- a/nomad/api/deployment.py +++ b/nomad/api/deployment.py @@ -1,3 +1,4 @@ +"""Nomad Deployment: https://developer.hashicorp.com/nomad/api-docs/deployments""" import nomad.api.exceptions from nomad.api.base import Requester @@ -13,88 +14,86 @@ class Deployment(Requester): ENDPOINT = "deployment" def __init__(self, **kwargs): - super(Deployment, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - msg = "{0} does not exist".format(item) + msg = f"{item} does not exist" raise AttributeError(msg) def __contains__(self, item): - try: - d = self.get_deployment(item) + self.get_deployment(item) return True except nomad.api.exceptions.URLNotFoundNomadException: return False def __getitem__(self, item): - try: - d = self.get_deployment(item) - - if d["ID"] == item: - return d - except nomad.api.exceptions.URLNotFoundNomadException: + deployment = self.get_deployment(item) + if deployment["ID"] == item: + return deployment raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exp: + raise KeyError from exp - def get_deployment(self, id): + def get_deployment(self, _id): """ This endpoint reads information about a specific deployment by ID. https://www.nomadproject.io/docs/http/deployments.html arguments: - - id + - _id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, method="get").json() + return self.request(_id, method="get").json() - def get_deployment_allocations(self, id): + def get_deployment_allocations(self, _id): """ This endpoint lists the allocations created or modified for the given deployment. https://www.nomadproject.io/docs/http/deployments.html arguments: - - id + - _id returns: list of dicts raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("allocations", id, method="get").json() + return self.request("allocations", _id, method="get").json() - def fail_deployment(self, id): + def fail_deployment(self, _id): """ This endpoint is used to mark a deployment as failed. This should be done to force the scheduler to stop creating allocations as part of the deployment or to cause a rollback to a previous job version. https://www.nomadproject.io/docs/http/deployments.html arguments: - - id + - _id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - fail_json = {"DeploymentID": id} - return self.request("fail", id, json=fail_json, method="post").json() + fail_json = {"DeploymentID": _id} + return self.request("fail", _id, json=fail_json, method="post").json() - def pause_deployment(self, id, pause): + def pause_deployment(self, _id, pause): """ This endpoint is used to pause or unpause a deployment. This is done to pause a rolling upgrade or resume it. https://www.nomadproject.io/docs/http/deployments.html arguments: - - id + - _id - pause, Specifies whether to pause or resume the deployment. returns: dict raises: @@ -102,46 +101,48 @@ def pause_deployment(self, id, pause): - nomad.api.exceptions.URLNotFoundNomadException """ pause_json = {"Pause": pause, - "DeploymentID": id} - return self.request("pause", id, json=pause_json, method="post").json() + "DeploymentID": _id} + return self.request("pause", _id, json=pause_json, method="post").json() - def promote_deployment_all(self, id, all=True): + def promote_deployment_all(self, _id, _all=True): """ This endpoint is used to promote task groups that have canaries for a deployment. This should be done when the placed canaries are healthy and the rolling upgrade of the remaining allocations should begin. https://www.nomadproject.io/docs/http/deployments.html arguments: - - id - - all, Specifies whether all task groups should be promoted. + - _id + - _all, Specifies whether all task groups should be promoted. returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - promote_all_json = {"All": all, - "DeploymentID": id} - return self.request("promote", id, json=promote_all_json, method="post").json() + promote_all_json = {"All": _all, + "DeploymentID": _id} + return self.request("promote", _id, json=promote_all_json, method="post").json() - def promote_deployment_groups(self, id, groups=list()): + def promote_deployment_groups(self, _id, groups=None): """ This endpoint is used to promote task groups that have canaries for a deployment. This should be done when the placed canaries are healthy and the rolling upgrade of the remaining allocations should begin. https://www.nomadproject.io/docs/http/deployments.html arguments: - - id + - _id - groups, (list) Specifies a particular set of task groups that should be promoted returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ + if groups is None: + groups = [] promote_groups_json = {"Groups": groups, - "DeploymentID": id} - return self.request("promote", id, json=promote_groups_json, method="post").json() + "DeploymentID": _id} + return self.request("promote", _id, json=promote_groups_json, method="post").json() - def deployment_allocation_health(self, id, healthy_allocations=list(), unhealthy_allocations=list()): + def deployment_allocation_health(self, _id, healthy_allocations=None, unhealthy_allocations=None): """ This endpoint is used to set the health of an allocation that is in the deployment manually. In some use cases, automatic detection of allocation health may not be desired. As such those task groups can be marked with an upgrade policy that uses health_check = "manual". Those allocations must have their health marked @@ -151,7 +152,7 @@ def deployment_allocation_health(self, id, healthy_allocations=list(), unhealthy https://www.nomadproject.io/docs/http/deployments.html arguments: - - id + - _id - healthy_allocations, Specifies the set of allocation that should be marked as healthy. - unhealthy_allocations, Specifies the set of allocation that should be marked as unhealthy. returns: dict @@ -159,7 +160,13 @@ def deployment_allocation_health(self, id, healthy_allocations=list(), unhealthy - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ + if healthy_allocations is None: + healthy_allocations = [] + + if unhealthy_allocations is None: + unhealthy_allocations = [] + allocations = {"HealthyAllocationIDs": healthy_allocations, "UnHealthyAllocationIDs": unhealthy_allocations, - "DeploymentID": id} - return self.request("allocation-health", id, json=allocations, method="post").json() + "DeploymentID": _id} + return self.request("allocation-health", _id, json=allocations, method="post").json() diff --git a/nomad/api/deployments.py b/nomad/api/deployments.py index 92b7145..a812d3a 100644 --- a/nomad/api/deployments.py +++ b/nomad/api/deployments.py @@ -1,3 +1,4 @@ +"""Nomad Deployment: https://developer.hashicorp.com/nomad/api-docs/deployments""" import nomad.api.exceptions from nomad.api.base import Requester @@ -13,13 +14,13 @@ class Deployments(Requester): ENDPOINT = "deployments" def __init__(self, **kwargs): - super(Deployments, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): raise AttributeError @@ -35,26 +36,23 @@ def __iter__(self): def __contains__(self, item): try: deployments = self.get_deployments() - - for d in deployments: - if d["ID"] == item: + for deployment in deployments: + if deployment["ID"] == item: return True - else: - return False + + return False except nomad.api.exceptions.URLNotFoundNomadException: return False def __getitem__(self, item): try: deployments = self.get_deployments() - - for d in deployments: - if d["ID"] == item: - return d - else: - raise KeyError - except nomad.api.exceptions.URLNotFoundNomadException: + for deployment in deployments: + if deployment["ID"] == item: + return deployment raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc def get_deployments(self, prefix="", namespace=None): """ This endpoint lists all deployments. diff --git a/nomad/api/evaluation.py b/nomad/api/evaluation.py index 2aa6bb2..a9ad84c 100644 --- a/nomad/api/evaluation.py +++ b/nomad/api/evaluation.py @@ -1,3 +1,4 @@ +"""Nomad Evaluation: https://developer.hashicorp.com/nomad/api-docs/evaluations""" import nomad.api.exceptions from nomad.api.base import Requester @@ -15,22 +16,21 @@ class Evaluation(Requester): ENDPOINT = "evaluation" def __init__(self, **kwargs): - super(Evaluation, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - msg = "{0} does not exist".format(item) + msg = f"{item} does not exist" raise AttributeError(msg) def __contains__(self, item): - try: - e = self.get_evaluation(item) + self.get_evaluation(item) return True except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -38,37 +38,37 @@ def __contains__(self, item): def __getitem__(self, item): try: - e = self.get_evaluation(item) - - if e["ID"] == item: - return e - except nomad.api.exceptions.URLNotFoundNomadException: + evaluation = self.get_evaluation(item) + if evaluation["ID"] == item: + return evaluation raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc - def get_evaluation(self, id): + def get_evaluation(self, _id): """ Query a specific evaluation. https://www.nomadproject.io/docs/http/eval.html arguments: - - id + - _id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, method="get").json() + return self.request(_id, method="get").json() - def get_allocations(self, id): + def get_allocations(self, _id): """ Query the allocations created or modified by an evaluation. https://www.nomadproject.io/docs/http/eval.html arguments: - - id + - _id returns: list raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "allocations", method="get").json() + return self.request(_id, "allocations", method="get").json() diff --git a/nomad/api/evaluations.py b/nomad/api/evaluations.py index 8e581bc..8ec96e8 100644 --- a/nomad/api/evaluations.py +++ b/nomad/api/evaluations.py @@ -1,3 +1,4 @@ +"""Nomad Evaluations: https://developer.hashicorp.com/nomad/api-docs/evaluations""" import nomad.api.exceptions from nomad.api.base import Requester @@ -14,13 +15,13 @@ class Evaluations(Requester): ENDPOINT = "evaluations" def __init__(self, **kwargs): - super(Evaluations, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): raise AttributeError @@ -29,11 +30,11 @@ def __contains__(self, item): try: evaluations = self.get_evaluations() - for e in evaluations: - if e["ID"] == item: + for evaluation in evaluations: + if evaluation["ID"] == item: return True - else: - return False + + return False except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -45,13 +46,12 @@ def __getitem__(self, item): try: evaluations = self.get_evaluations() - for e in evaluations: - if e["ID"] == item: - return e - else: - raise KeyError - except nomad.api.exceptions.URLNotFoundNomadException: + for evaluation in evaluations: + if evaluation["ID"] == item: + return evaluation raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc def __iter__(self): evaluations = self.get_evaluations() diff --git a/nomad/api/event.py b/nomad/api/event.py index 658c1ca..ba001fc 100644 --- a/nomad/api/event.py +++ b/nomad/api/event.py @@ -1,24 +1,21 @@ +"""Nomad Events: https://developer.hashicorp.com/nomad/api-docs/events""" import json import threading +import queue import requests from nomad.api.base import Requester -from nomad.api.exceptions import TimeoutNomadException - -try: - import queue -except ImportError: - import Queue as queue - - -class Event(object): +class Event(): + """ + Nomad Event + """ def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): raise AttributeError @@ -26,8 +23,8 @@ def __getattr__(self, item): def __init__(self, **kwargs): self.stream = stream(**kwargs) - -class stream(Requester): +# backward compatibility +class stream(Requester): # pylint: disable=invalid-name """ The /event/stream endpoint is used to stream events generated by Nomad. @@ -37,9 +34,9 @@ class stream(Requester): ENDPOINT = "event/stream" def __init__(self, **kwargs): - super(stream, self).__init__(**kwargs) + super().__init__(**kwargs) - def _get_stream(self, method, params, timeout, event_queue, exit_event): + def _get_stream(self, method, params, timeout, event_queue, exit_event): # pylint: disable=too-many-arguments """ Used as threading target, to obtain json() value Args: @@ -66,7 +63,7 @@ def _get_stream(self, method, params, timeout, event_queue, exit_event): except requests.exceptions.ConnectionError: continue - def get_stream(self, index=0, topic=None, namespace=None, event_queue=None, timeout=None): + def get_stream(self, index=0, topic=None, namespace=None, event_queue=None, timeout=None): # pylint: disable=too-many-arguments """ Usage: stream, stream_exit_event, events = n.event.stream.get_stream() @@ -81,7 +78,8 @@ def get_stream(self, index=0, topic=None, namespace=None, event_queue=None, time index: (int), Specifies the index to start streaming events from. If the requested index is no longer in the buffer the stream will start at the next available index. - topic: (None or dict), Specifies a topic to subscribe to and filter on. The default is to subscribe to all topics. + topic: (None or dict), Specifies a topic to subscribe to and filter on. + The default is to subscribe to all topics. Multiple topics may be specified by passing multiple topic parameters. A valid topic parameter includes a topic type and an optional filter_key separated by a colon :. As an example ?topic=Deployment:redis would subscribe to all Deployment events for a job redis. diff --git a/nomad/api/exceptions.py b/nomad/api/exceptions.py index ca5e343..5f60205 100644 --- a/nomad/api/exceptions.py +++ b/nomad/api/exceptions.py @@ -1,3 +1,4 @@ +"""Internal library exceptions""" import requests @@ -8,9 +9,9 @@ def __init__(self, nomad_resp): def __str__(self): if isinstance(self.nomad_resp, requests.Response) and hasattr(self.nomad_resp, "text"): - return 'The {0} was raised with following response: {1}.'.format(self.__class__.__name__, self.nomad_resp.text) - else: - return 'The {0} was raised due to the following error: {1}'.format(self.__class__.__name__, str(self.nomad_resp)) + return f"The {self.__class__.__name__} was raised with following response: {self.nomad_resp.text}." + + return f"The {self.__class__.__name__} was raised due to the following error: {self.nomad_resp}" class URLNotFoundNomadException(BaseNomadException): diff --git a/nomad/api/job.py b/nomad/api/job.py index b1c0a3a..6d0286b 100644 --- a/nomad/api/job.py +++ b/nomad/api/job.py @@ -1,3 +1,4 @@ +"""Nomad job: https://developer.hashicorp.com/nomad/api-docs/jobs""" import nomad.api.exceptions from nomad.api.base import Requester @@ -14,47 +15,44 @@ class Job(Requester): ENDPOINT = "job" def __init__(self, **kwargs): - super(Job, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - msg = "{0} does not exist".format(item) + msg = f"{item} does not exist" raise AttributeError(msg) def __contains__(self, item): - try: - j = self.get_job(item) + self.get_job(item) return True except nomad.api.exceptions.URLNotFoundNomadException: return False def __getitem__(self, item): - try: - j = self.get_job(item) - - if j["ID"] == item: - return j - if j["Name"] == item: - return j - else: - raise KeyError - except nomad.api.exceptions.URLNotFoundNomadException: + job = self.get_job(item) + if job["ID"] == item: + return job + if job["Name"] == item: + return job + raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc - def get_job(self, id, namespace=None): + def get_job(self, _id, namespace=None): """ Query a single job for its specification and status. https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. This is specified as a querystring parameter. returns: dict @@ -67,9 +65,9 @@ def get_job(self, id, namespace=None): if namespace: params["namespace"] = namespace - return self.request(id, method="get", params=params).json() + return self.request(_id, method="get", params=params).json() - def get_versions(self, id): + def get_versions(self, _id): """ This endpoint reads information about all versions of a job. https://www.nomadproject.io/docs/http/job.html @@ -81,114 +79,114 @@ def get_versions(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "versions", method="get").json() + return self.request(_id, "versions", method="get").json() - def get_allocations(self, id): + def get_allocations(self, _id): """ Query the allocations belonging to a single job. https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id returns: list raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "allocations", method="get").json() + return self.request(_id, "allocations", method="get").json() - def get_evaluations(self, id): + def get_evaluations(self, _id): """ Query the evaluations belonging to a single job. https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "evaluations", method="get").json() + return self.request(_id, "evaluations", method="get").json() - def get_deployments(self, id): + def get_deployments(self, _id): """ This endpoint lists a single job's deployments https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "deployments", method="get").json() + return self.request(_id, "deployments", method="get").json() - def get_deployment(self, id): + def get_deployment(self, _id): """ This endpoint returns a single job's most recent deployment. https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id returns: list of dicts raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "deployment", method="get").json() + return self.request(_id, "deployment", method="get").json() - def get_summary(self, id): + def get_summary(self, _id): """ Query the summary of a job. https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "summary", method="get").json() + return self.request(_id, "summary", method="get").json() - def register_job(self, id, job): + def register_job(self, _id, job): """ Registers a new job or updates an existing job https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, json=job, method="post").json() + return self.request(_id, json=job, method="post").json() - def evaluate_job(self, id): + def evaluate_job(self, _id): """ Creates a new evaluation for the given job. This can be used to force run the scheduling logic if necessary. https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "evaluate", method="post").json() + return self.request(_id, "evaluate", method="post").json() - def plan_job(self, id, job, diff=False, policy_override=False): + def plan_job(self, _id, job, diff=False, policy_override=False): """ Invoke a dry-run of the scheduler for the job. https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id - job, dict - diff, boolean - policy_override, boolean @@ -201,9 +199,9 @@ def plan_job(self, id, job, diff=False, policy_override=False): json_dict.update(job) json_dict.setdefault('Diff', diff) json_dict.setdefault('PolicyOverride', policy_override) - return self.request(id, "plan", json=json_dict, method="post").json() + return self.request(_id, "plan", json=json_dict, method="post").json() - def periodic_job(self, id): + def periodic_job(self, _id): """ Forces a new instance of the periodic job. A new instance will be created even if it violates the job's prohibit_overlap settings. As such, this should be only used to immediately @@ -212,21 +210,21 @@ def periodic_job(self, id): https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "periodic", "force", method="post").json() + return self.request(_id, "periodic", "force", method="post").json() - def dispatch_job(self, id, payload=None, meta=None): + def dispatch_job(self, _id, payload=None, meta=None): """ Dispatches a new instance of a parameterized job. https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id - payload - meta returns: dict @@ -235,15 +233,15 @@ def dispatch_job(self, id, payload=None, meta=None): - nomad.api.exceptions.URLNotFoundNomadException """ dispatch_json = {"Meta": meta, "Payload": payload} - return self.request(id, "dispatch", json=dispatch_json, method="post").json() + return self.request(_id, "dispatch", json=dispatch_json, method="post").json() - def revert_job(self, id, version, enforce_prior_version=None): + def revert_job(self, _id, version, enforce_prior_version=None): """ This endpoint reverts the job to an older version. https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id - version, Specifies the job version to revert to. optional_arguments: - enforce_prior_version, Optional value specifying the current job's version. @@ -254,18 +252,18 @@ def revert_job(self, id, version, enforce_prior_version=None): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - revert_json = {"JobID": id, + revert_json = {"JobID": _id, "JobVersion": version, "EnforcePriorVersion": enforce_prior_version} - return self.request(id, "revert", json=revert_json, method="post").json() + return self.request(_id, "revert", json=revert_json, method="post").json() - def stable_job(self, id, version, stable): + def stable_job(self, _id, version, stable): """ This endpoint sets the job's stability. https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id - version, Specifies the job version to revert to. - stable, Specifies whether the job should be marked as stable or not. returns: dict @@ -273,18 +271,18 @@ def stable_job(self, id, version, stable): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - revert_json = {"JobID": id, + revert_json = {"JobID": _id, "JobVersion": version, "Stable": stable} - return self.request(id, "stable", json=revert_json, method="post").json() + return self.request(_id, "stable", json=revert_json, method="post").json() - def deregister_job(self, id, purge=None): + def deregister_job(self, _id, purge=None): """ Deregisters a job, and stops all allocations part of it. https://www.nomadproject.io/docs/http/job.html arguments: - - id + - _id - purge (bool), optionally specifies whether the job should be stopped and purged immediately (`purge=True`) or deferred to the Nomad garbage collector (`purge=False`). @@ -299,6 +297,6 @@ def deregister_job(self, id, purge=None): if purge is not None: if not isinstance(purge, bool): raise nomad.api.exceptions.InvalidParameters("purge is invalid " - "(expected type %s but got %s)"%(type(bool()), type(purge))) + f"(expected type {type(bool())} but got {type(purge)})") params = {"purge": purge} - return self.request(id, params=params, method="delete").json() + return self.request(_id, params=params, method="delete").json() diff --git a/nomad/api/jobs.py b/nomad/api/jobs.py index 09e6f3a..98fa2b4 100644 --- a/nomad/api/jobs.py +++ b/nomad/api/jobs.py @@ -1,3 +1,4 @@ +"""Nomad job: https://developer.hashicorp.com/nomad/api-docs/jobs""" import nomad.api.exceptions from nomad.api.base import Requester @@ -15,16 +16,16 @@ class Jobs(Requester): ENDPOINT = "jobs" def __init__(self, **kwargs): - super(Jobs, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return "{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return "{self.__dict__}" def __getattr__(self, item): - msg = "{0} does not exist".format(item) + msg = f"{item} does not exist" raise AttributeError(msg) def __contains__(self, item): @@ -36,8 +37,7 @@ def __contains__(self, item): return True if j["Name"] == item: return True - else: - return False + return False except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -54,10 +54,9 @@ def __getitem__(self, item): return j if j["Name"] == item: return j - else: - raise KeyError - except nomad.api.exceptions.URLNotFoundNomadException: raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc def __iter__(self): jobs = self.get_jobs() @@ -106,4 +105,6 @@ def parse(self, hcl, canonicalize=False): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("parse", json={"JobHCL": hcl, "Canonicalize": canonicalize}, method="post", allow_redirects=True).json() + return self.request( + "parse", json={"JobHCL": hcl, "Canonicalize": canonicalize}, method="post", allow_redirects=True + ).json() diff --git a/nomad/api/metrics.py b/nomad/api/metrics.py index beef3c3..2ecccf2 100644 --- a/nomad/api/metrics.py +++ b/nomad/api/metrics.py @@ -1,3 +1,4 @@ +"""Nomad Metrics: https://developer.hashicorp.com/nomad/api-docs/metrics""" from nomad.api.base import Requester @@ -15,16 +16,17 @@ class Metrics(Requester): ENDPOINT = "metrics" def __init__(self, **kwargs): - super(Metrics, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) def get_metrics(self): """ Get the metrics diff --git a/nomad/api/namespace.py b/nomad/api/namespace.py index 0f51b01..0860c73 100644 --- a/nomad/api/namespace.py +++ b/nomad/api/namespace.py @@ -1,3 +1,4 @@ +"""Nomad namespace: https://developer.hashicorp.com/nomad/api-docs/namespaces""" import nomad.api.exceptions from nomad.api.base import Requester @@ -14,22 +15,22 @@ class Namespace(Requester): ENDPOINT = "namespace" def __init__(self, **kwargs): - super(Namespace, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - msg = "{0} does not exist".format(item) + msg = f"{item} does not exist" raise AttributeError(msg) def __contains__(self, item): try: - j = self.get_namespace(item) + self.get_namespace(item) return True except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -37,30 +38,30 @@ def __contains__(self, item): def __getitem__(self, item): try: - j = self.get_namespace(item) - - if j["ID"] == item: - return j - if j["Name"] == item: - return j - else: - raise KeyError - except nomad.api.exceptions.URLNotFoundNomadException: + job = self.get_namespace(item) + + if job["ID"] == item: + return job + if job["Name"] == item: + return job + raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc - def get_namespace(self, id): + def get_namespace(self, _id): """ Query a single namespace. https://www.nomadproject.io/api/namespaces.html arguments: - - id + - _id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, method="get").json() + return self.request(_id, method="get").json() def create_namespace(self, namespace): """ create namespace @@ -77,31 +78,31 @@ def create_namespace(self, namespace): """ return self.request(json=namespace, method="post") - def update_namespace(self, id, namespace): + def update_namespace(self, _id, namespace): """ Update namespace https://www.nomadproject.io/api/namespaces.html arguments: - - id + - _id - namespace (dict) returns: requests.Response raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, json=namespace, method="post") + return self.request(_id, json=namespace, method="post") - def delete_namespace(self, id): + def delete_namespace(self, _id): """ delete namespace. https://www.nomadproject.io/api/namespaces.html arguments: - - id + - _id returns: requests.Response raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, method="delete") + return self.request(_id, method="delete") diff --git a/nomad/api/namespaces.py b/nomad/api/namespaces.py index 12b39a1..3fb907f 100644 --- a/nomad/api/namespaces.py +++ b/nomad/api/namespaces.py @@ -1,3 +1,4 @@ +"""Nomad namespace: https://developer.hashicorp.com/nomad/api-docs/namespaces""" import nomad.api.exceptions from nomad.api.base import Requester @@ -13,26 +14,26 @@ class Namespaces(Requester): ENDPOINT = "namespaces" def __init__(self, **kwargs): - super(Namespaces, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - msg = "{0} does not exist".format(item) + msg = f"{item} does not exist" raise AttributeError(msg) def __contains__(self, item): try: namespaces = self.get_namespaces() - for n in namespaces: - if n["Name"] == item: + for namespace in namespaces: + if namespace["Name"] == item: return True - + return False except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -45,13 +46,13 @@ def __getitem__(self, item): try: namespaces = self.get_namespaces() - for n in namespaces: - if n["Name"] == item: - return n - - raise KeyError - except nomad.api.exceptions.URLNotFoundNomadException: + for namespace in namespaces: + if namespace["Name"] == item: + return namespace + raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc def __iter__(self): namespaces = self.get_namespaces() diff --git a/nomad/api/node.py b/nomad/api/node.py index bdff1a7..46688c2 100644 --- a/nomad/api/node.py +++ b/nomad/api/node.py @@ -1,3 +1,4 @@ +"""Nomad Node: https://developer.hashicorp.com/nomad/api-docs/nodes""" import nomad.api.exceptions from nomad.api.base import Requester @@ -14,22 +15,22 @@ class Node(Requester): ENDPOINT = "node" def __init__(self, **kwargs): - super(Node, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - msg = "{0} does not exist".format(item) + msg = f"{item} does not exist" raise AttributeError(msg) def __contains__(self, item): try: - n = self.get_node(item) + self.get_node(item) return True except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -37,18 +38,18 @@ def __contains__(self, item): def __getitem__(self, item): try: - n = self.get_node(item) - - if n["ID"] == item: - return n - if n["Name"] == item: - return n - else: - raise KeyError - except nomad.api.exceptions.URLNotFoundNomadException: + node = self.get_node(item) + + if node["ID"] == item: + return node + if node["Name"] == item: + return node + raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc - def get_node(self, id): + def get_node(self, _id): """ Query the status of a client node registered with Nomad. https://www.nomadproject.io/docs/http/node.html @@ -58,9 +59,9 @@ def get_node(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, method="get").json() + return self.request(_id, method="get").json() - def get_allocations(self, id): + def get_allocations(self, _id): """ Query the allocations belonging to a single node. https://www.nomadproject.io/docs/http/node.html @@ -70,11 +71,11 @@ def get_allocations(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "allocations", method="get").json() + return self.request(_id, "allocations", method="get").json() - def evaluate_node(self, id): + def evaluate_node(self, _id): """ Creates a new evaluation for the given node. - This can be used to force run the + This can be used to force run the scheduling logic if necessary. https://www.nomadproject.io/docs/http/node.html @@ -84,9 +85,9 @@ def evaluate_node(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "evaluate", method="post").json() + return self.request(_id, "evaluate", method="post").json() - def drain_node(self, id, enable=False): + def drain_node(self, _id, enable=False): """ Toggle the drain mode of the node. When enabled, no further allocations will be assigned and existing allocations will be migrated. @@ -94,7 +95,7 @@ def drain_node(self, id, enable=False): https://www.nomadproject.io/docs/http/node.html arguments: - - id (str uuid): node id + - _id (str uuid): node id - enable (bool): enable node drain or not to enable node drain returns: dict raises: @@ -102,9 +103,9 @@ def drain_node(self, id, enable=False): - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "drain", params={"enable": enable}, method="post").json() + return self.request(_id, "drain", params={"enable": enable}, method="post").json() - def drain_node_with_spec(self, id, drain_spec, mark_eligible=None): + def drain_node_with_spec(self, _id, drain_spec, mark_eligible=None): """ This endpoint toggles the drain mode of the node. When draining is enabled, no further allocations will be assigned to this node, and existing allocations will be migrated to new nodes. @@ -114,7 +115,7 @@ def drain_node_with_spec(self, id, drain_spec, mark_eligible=None): https://www.nomadproject.io/docs/http/node.html arguments: - - id (str uuid): node id + - _id (str uuid): node id - drain_spec (dict): https://www.nomadproject.io/api/nodes.html#drainspec - mark_eligible (bool): https://www.nomadproject.io/api/nodes.html#markeligible returns: dict @@ -126,36 +127,36 @@ def drain_node_with_spec(self, id, drain_spec, mark_eligible=None): if drain_spec and mark_eligible is not None: payload = { - "NodeID": id, + "NodeID": _id, "DrainSpec": drain_spec, "MarkEligible": mark_eligible } elif drain_spec and mark_eligible is None: payload = { - "NodeID": id, + "NodeID": _id, "DrainSpec": drain_spec } elif not drain_spec and mark_eligible is not None: payload = { - "NodeID": id, + "NodeID": _id, "DrainSpec": None, "MarkEligible": mark_eligible } elif not drain_spec and mark_eligible is None: payload = { - "NodeID": id, + "NodeID": _id, "DrainSpec": None, } - return self.request(id, "drain", json=payload, method="post").json() + return self.request(_id, "drain", json=payload, method="post").json() - def eligible_node(self, id, eligible=None, ineligible=None): + def eligible_node(self, _id, eligible=None, ineligible=None): """ Toggle the eligibility of the node. https://www.nomadproject.io/docs/http/node.html arguments: - - id (str uuid): node id + - _id (str uuid): node id - eligible (bool): Set to True to mark node eligible - ineligible (bool): Set to True to mark node ineligible returns: dict @@ -171,25 +172,24 @@ def eligible_node(self, id, eligible=None, ineligible=None): raise nomad.api.exceptions.InvalidParameters if eligible is not None and eligible: - payload = {"Eligibility": "eligible", "NodeID": id} + payload = {"Eligibility": "eligible", "NodeID": _id} elif eligible is not None and not eligible: - payload = {"Eligibility": "ineligible", "NodeID": id} + payload = {"Eligibility": "ineligible", "NodeID": _id} elif ineligible is not None: - payload = {"Eligibility": "ineligible", "NodeID": id} + payload = {"Eligibility": "ineligible", "NodeID": _id} elif ineligible is not None and not ineligible: - payload = {"Eligibility": "eligible", "NodeID": id} + payload = {"Eligibility": "eligible", "NodeID": _id} - return self.request(id, "eligibility", json=payload, method="post").json() + return self.request(_id, "eligibility", json=payload, method="post").json() - def purge_node(self, id): + def purge_node(self, _id): """ This endpoint purges a node from the system. Nodes can still join the cluster if they are alive. arguments: - - id (str uuid): node id + - _id (str uuid): node id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id, "purge", method="post").json() - + return self.request(_id, "purge", method="post").json() diff --git a/nomad/api/nodes.py b/nomad/api/nodes.py index 30dd1b0..5c4def0 100644 --- a/nomad/api/nodes.py +++ b/nomad/api/nodes.py @@ -1,3 +1,4 @@ +"""Nomad Node: https://developer.hashicorp.com/nomad/api-docs/nodes""" import nomad.api.exceptions from nomad.api.base import Requester @@ -14,28 +15,28 @@ class Nodes(Requester): ENDPOINT = "nodes" def __init__(self, **kwargs): - super(Nodes, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) def __contains__(self, item): try: nodes = self.get_nodes() - for n in nodes: - if n["ID"] == item: + for node in nodes: + if node["ID"] == item: return True - if n["Name"] == item: + if node["Name"] == item: return True - else: - return False + return False except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -47,15 +48,14 @@ def __getitem__(self, item): try: nodes = self.get_nodes() - for n in nodes: - if n["ID"] == item: - return n - if n["Name"] == item: - return n - else: - raise KeyError - except nomad.api.exceptions.URLNotFoundNomadException: + for node in nodes: + if node["ID"] == item: + return node + if node["Name"] == item: + return node raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc def __iter__(self): nodes = self.get_nodes() diff --git a/nomad/api/operator.py b/nomad/api/operator.py index 52cef1f..b301c18 100644 --- a/nomad/api/operator.py +++ b/nomad/api/operator.py @@ -1,3 +1,4 @@ +"""Nomad Operator: https://developer.hashicorp.com/nomad/api-docs/operator""" from nomad.api.base import Requester @@ -13,16 +14,17 @@ class Operator(Requester): ENDPOINT = "operator" def __init__(self, **kwargs): - super(Operator, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) def get_configuration(self, stale=False): """ Query the status of a client node registered with Nomad. diff --git a/nomad/api/regions.py b/nomad/api/regions.py index 49bc765..76b5038 100644 --- a/nomad/api/regions.py +++ b/nomad/api/regions.py @@ -1,3 +1,4 @@ +"""Nomad region: https://developer.hashicorp.com/nomad/api-docs/regions""" import nomad.api.exceptions from nomad.api.base import Requester @@ -11,26 +12,26 @@ class Regions(Requester): ENDPOINT = "regions" def __init__(self, **kwargs): - super(Regions, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) def __contains__(self, item): try: regions = self.get_regions() - for r in regions: - if r == item: + for region in regions: + if region == item: return True - else: - return False + return False except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -42,13 +43,12 @@ def __getitem__(self, item): try: regions = self.get_regions() - for r in regions: - if r == item: - return r - else: - raise KeyError - except nomad.api.exceptions.URLNotFoundNomadException: + for region in regions: + if region == item: + return region raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc def __iter__(self): regions = self.get_regions() diff --git a/nomad/api/scaling.py b/nomad/api/scaling.py index 56776a2..850c76b 100644 --- a/nomad/api/scaling.py +++ b/nomad/api/scaling.py @@ -1,3 +1,4 @@ +"""Nomad Scalling API: https://developer.hashicorp.com/nomad/api-docs/scaling-policies""" import nomad.api.exceptions from nomad.api.base import Requester @@ -12,18 +13,20 @@ class Scaling(Requester): ENDPOINT = "scaling" def __init__(self, **kwargs): - super(Scaling, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) - def get_scaling_policies(self, job="", type=""): + # we want to have common arguments name with Nomad API + def get_scaling_policies(self, job="", type=""): # pylint: disable=redefined-builtin """ This endpoint returns the scaling policies from all jobs. @@ -46,23 +49,23 @@ def get_scaling_policies(self, job="", type=""): if type not in type_of_scaling_policies: raise nomad.api.exceptions.InvalidParameters("type is invalid " - "(expected values are {} but got {})".format(type_of_scaling_policies, type)) + f"(expected values are {type_of_scaling_policies} but got {type})") params = {"job": job, "type": type} return self.request("policies", method="get", params=params).json() - def get_scaling_policy(self, id): + def get_scaling_policy(self, _id): """ This endpoint reads a specific scaling policy. https://developer.hashicorp.com/nomad/api-docs/scaling-policies#read-scaling-policy arguments: - - id + - _id returns: list of dicts raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy/{}".format(id), method="get").json() + return self.request(f"policy/{_id}", method="get").json() diff --git a/nomad/api/search.py b/nomad/api/search.py index 66e4dc9..af8c3bf 100644 --- a/nomad/api/search.py +++ b/nomad/api/search.py @@ -1,3 +1,4 @@ +"""Nomad Search API: https://developer.hashicorp.com/nomad/api-docs/search""" import nomad.api.exceptions from nomad.api.base import Requester @@ -15,16 +16,17 @@ class Search(Requester): ENDPOINT = "search" def __init__(self, **kwargs): - super(Search, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) def search(self, prefix, context): """ The endpoint returns matches for a given prefix and context, where a context can be jobs, @@ -46,14 +48,14 @@ def search(self, prefix, context): accetaple_contexts = ("jobs", "evals", "allocs", "nodes", "deployment", "plugins", "volumes", "all") if context not in accetaple_contexts: raise nomad.api.exceptions.InvalidParameters("context is invalid " - "(expected values are {} but got {})".format(accetaple_contexts, context)) + f"(expected values are {accetaple_contexts} but got {context})") params = {"Prefix": prefix, "Context": context} return self.request(json=params, method="post").json() def fuzzy_search(self, text, context): """ The /search/fuzzy endpoint returns partial substring matches for a given search term and context, - where a context can be jobs, allocations, nodes, plugins, or namespaces. Additionally, + where a context can be jobs, allocations, nodes, plugins, or namespaces. Additionally, fuzzy searching can be done across all contexts. https://developer.hashicorp.com/nomad/api-docs/search#fuzzy-searching @@ -78,6 +80,6 @@ def fuzzy_search(self, text, context): accetaple_contexts = ("jobs", "allocs", "nodes", "plugins", "all") if context not in accetaple_contexts: raise nomad.api.exceptions.InvalidParameters("context is invalid " - "(expected values are {} but got {})".format(accetaple_contexts,context)) + f"(expected values are {accetaple_contexts} but got {context})") return self.request("fuzzy", json=params, method="post").json() diff --git a/nomad/api/sentinel.py b/nomad/api/sentinel.py index 29d91a4..ba0d49b 100644 --- a/nomad/api/sentinel.py +++ b/nomad/api/sentinel.py @@ -1,3 +1,4 @@ +"""Nomad Sentinel API: https://developer.hashicorp.com/nomad/api-docs/sentinel-policies""" from nomad.api.base import Requester @@ -12,16 +13,17 @@ class Sentinel(Requester): ENDPOINT = "sentinel" def __init__(self, **kwargs): - super(Sentinel, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) def get_policies(self): """ Get a list of policies. @@ -36,7 +38,7 @@ def get_policies(self): """ return self.request("policies", method="get").json() - def create_policy(self, id, policy): + def create_policy(self, _id, policy): """ Create policy. https://www.nomadproject.io/api/sentinel-policies.html @@ -49,9 +51,9 @@ def create_policy(self, id, policy): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", id, json=policy, method="post") + return self.request("policy", _id, json=policy, method="post") - def get_policy(self, id): + def get_policy(self, _id): """ Get a spacific policy. https://www.nomadproject.io/api/sentinel-policies.html @@ -62,9 +64,9 @@ def get_policy(self, id): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", id, method="get").json() + return self.request("policy", _id, method="get").json() - def update_policy(self, id, policy): + def update_policy(self, _id, policy): """ Create policy. https://www.nomadproject.io/api/sentinel-policies.html @@ -78,19 +80,19 @@ def update_policy(self, id, policy): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", id, json=policy, method="post") + return self.request("policy", _id, json=policy, method="post") - def delete_policy(self, id): + def delete_policy(self, _id): """ Delete specific policy. https://www.nomadproject.io/api/sentinel-policies.html arguments: - - id + - _id returns: Boolean raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", id, method="delete").ok + return self.request("policy", _id, method="delete").ok diff --git a/nomad/api/status.py b/nomad/api/status.py index ba598e0..3e33501 100644 --- a/nomad/api/status.py +++ b/nomad/api/status.py @@ -1,9 +1,10 @@ +"""Nomad Status API: https://developer.hashicorp.com/nomad/api-docs/status""" import nomad.api.exceptions from nomad.api.base import Requester -class Status(object): +class Status(): """ By default, the agent's local region is used @@ -16,16 +17,18 @@ def __init__(self, **kwargs): self.peers = Peers(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) class Leader(Requester): + """This endpoint returns the address of the current leader in the region.""" ENDPOINT = "status/leader" @@ -35,8 +38,8 @@ def __contains__(self, item): if leader == item: return True - else: - return False + + return False except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -58,6 +61,7 @@ def get_leader(self): class Peers(Requester): + """This endpoint returns the set of raft peers in the region.""" ENDPOINT = "status/peers" @@ -65,11 +69,10 @@ def __contains__(self, item): try: peers = self.get_peers() - for p in peers: - if p == item: + for peer in peers: + if peer == item: return True - else: - return False + return False except nomad.api.exceptions.URLNotFoundNomadException: return False @@ -81,13 +84,12 @@ def __getitem__(self, item): try: peers = self.get_peers() - for p in peers: - if p == item: - return p - else: - raise KeyError - except nomad.api.exceptions.URLNotFoundNomadException: + for peer in peers: + if peer == item: + return peer raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc def __iter__(self): peers = self.get_peers() diff --git a/nomad/api/system.py b/nomad/api/system.py index 923d587..8437821 100644 --- a/nomad/api/system.py +++ b/nomad/api/system.py @@ -1,3 +1,4 @@ +"""Nomad System API: https://developer.hashicorp.com/nomad/api-docs/system""" from nomad.api.base import Requester @@ -14,16 +15,17 @@ class System(Requester): ENDPOINT = "system" def __init__(self, **kwargs): - super(System, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) def initiate_garbage_collection(self): """ Initiate garbage collection of jobs, evals, allocations and nodes. diff --git a/nomad/api/validate.py b/nomad/api/validate.py index a0c3d7d..f0ba03a 100644 --- a/nomad/api/validate.py +++ b/nomad/api/validate.py @@ -1,3 +1,4 @@ +"""Nomad Validate API: https://developer.hashicorp.com/nomad/api-docs/validate""" from nomad.api.base import Requester @@ -14,16 +15,17 @@ class Validate(Requester): ENDPOINT = "validate" def __init__(self, **kwargs): - super(Validate, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) def validate_job(self, nomad_job_dict): """ This endpoint validates a Nomad job file. The local Nomad agent forwards the request to a server. diff --git a/nomad/api/variable.py b/nomad/api/variable.py index 434d543..2ff23a0 100644 --- a/nomad/api/variable.py +++ b/nomad/api/variable.py @@ -1,3 +1,4 @@ +"""Nomad Valiables API: https://developer.hashicorp.com/nomad/api-docs/variables""" import nomad.api.exceptions from nomad.api.base import Requester @@ -13,16 +14,17 @@ class Variable(Requester): ENDPOINT = "var" def __init__(self, **kwargs): - super(Variable, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) def __contains__(self, item): try: @@ -34,8 +36,8 @@ def __contains__(self, item): def __getitem__(self, item): try: return self.get_variable(item) - except nomad.api.exceptions.URLNotFoundNomadException: - raise KeyError + except nomad.api.exceptions.URLNotFoundNomadException as exc: + raise KeyError from exc def get_variable(self, var_path, namespace=None): """ @@ -92,7 +94,7 @@ def delete_variable(self, var_path, namespace=None, cas=None): - var_path :(str), path to variable - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. This is specified as a querystring parameter. - - cas :(int) optional, If set, the variable will only be deleted if the cas value matches the + - cas :(int) optional, If set, the variable will only be deleted if the cas value matches the current variables ModifyIndex. returns: dict raises: diff --git a/nomad/api/variables.py b/nomad/api/variables.py index 75338ec..c9d8928 100644 --- a/nomad/api/variables.py +++ b/nomad/api/variables.py @@ -1,5 +1,4 @@ -import nomad.api.exceptions - +"""Nomad Valiables API: https://developer.hashicorp.com/nomad/api-docs/variables""" from nomad.api.base import Requester @@ -13,16 +12,17 @@ class Variables(Requester): ENDPOINT = "vars" def __init__(self, **kwargs): - super(Variables, self).__init__(**kwargs) + super().__init__(**kwargs) def __str__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __repr__(self): - return "{0}".format(self.__dict__) + return f"{self.__dict__}" def __getattr__(self, item): - raise AttributeError + msg = f"{item} does not exist" + raise AttributeError(msg) def __contains__(self, item): variables = self.get_variables() @@ -30,8 +30,7 @@ def __contains__(self, item): for var in variables: if var["Path"] == item: return True - else: - return False + return False def __getitem__(self, item): variables = self.get_variables() @@ -39,15 +38,14 @@ def __getitem__(self, item): for var in variables: if var["Path"] == item: return var - else: - raise KeyError + raise KeyError def __iter__(self): variables = self.get_variables() return iter(variables) def get_variables(self, prefix="", namespace=None): - """ + """ This endpoint lists variables. https://developer.hashicorp.com/nomad/api-docs/variables @@ -66,5 +64,3 @@ def get_variables(self, prefix="", namespace=None): params["namespace"] = namespace return self.request(params=params, method="get").json() - - diff --git a/requirements-dev.txt b/requirements-dev.txt index 6c4caad..6e3596d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,4 +4,4 @@ pytest-cov==4.0.0 mkdocs==1.4.2 mock==4.0.3 flaky==3.7.0 -responses==0.22.0 +responses==0.22.0 \ No newline at end of file diff --git a/setup.py b/setup.py index 1d2e987..5fddd91 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ +"""Nomad Python Library""" import setuptools -with open("README.md", "r") as fh: +with open("README.md", "r", encoding='utf-8') as fh: long_description = fh.read() setuptools.setup( diff --git a/tests/test_acl.py b/tests/test_acl.py index ec7c151..d7936fc 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -46,7 +46,7 @@ def test_update_token(nomad_setup): token_update ='{"AccessorID":"' + created_token["AccessorID"] + '","Name": "Updated" ,"Type": "client","Policies": ["readonly"]}' json_token_update = json.loads(token_update) - update_token = nomad_setup.acl.update_token(id=created_token["AccessorID"],token=json_token_update) + update_token = nomad_setup.acl.update_token(_id=created_token["AccessorID"],token=json_token_update) assert "Updated" in update_token["Name"] @@ -89,7 +89,7 @@ def test_get_policies(nomad_setup): def test_create_policy(nomad_setup): policy_example = '{ "Name": "my-policy", "Description": "This is a great policy", "Rules": "" }' json_policy = json.loads(policy_example) - nomad_setup.acl.create_policy(id="my-policy", policy=json_policy) + nomad_setup.acl.create_policy(_id="my-policy", policy=json_policy) assert False == any("my-policy" in x for x in nomad_setup.acl.get_policies()) @@ -103,11 +103,11 @@ def test_get_policy(nomad_setup): def test_update_policy(nomad_setup): policy_update = '{"Name": "my-policy","Description": "Updated","Rules": ""}' json_policy_update = json.loads(policy_update) - nomad_setup.acl.update_policy(id="my-policy", policy=json_policy_update) + nomad_setup.acl.update_policy(_id="my-policy", policy=json_policy_update) assert False == any("Updated" in x for x in nomad_setup.acl.get_policies()) @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") def test_delete_policy(nomad_setup): - nomad_setup.acl.delete_policy(id="my-policy") + nomad_setup.acl.delete_policy(_id="my-policy") assert False == any("my-policy" in x for x in nomad_setup.acl.get_policies()) diff --git a/tests/test_job.py b/tests/test_job.py index a5019e1..992f327 100644 --- a/tests/test_job.py +++ b/tests/test_job.py @@ -36,7 +36,7 @@ def test_get_jobs_with_namespace_override_no_namespace_declared_on_create_incorr ) with pytest.raises(exceptions.BaseNomadException): - nomad_setup.job.get_job(id=str(uuid.uuid4())) + nomad_setup.job.get_job(_id=str(uuid.uuid4())) @responses.activate @@ -48,7 +48,7 @@ def test_get_jobs_with_namespace_override_no_namespace_declared_on_create(nomad_ json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] ) - nomad_setup.job.get_job(id="18a0f501-41d5-ae43-ff61-1d8ec3ec8314", namespace=common.NOMAD_NAMESPACE) + nomad_setup.job.get_job(_id="18a0f501-41d5-ae43-ff61-1d8ec3ec8314", namespace=common.NOMAD_NAMESPACE) @responses.activate @@ -60,7 +60,7 @@ def test_get_jobs_with_namespace_override_namespace_declared_on_create(nomad_set json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] ) - nomad_setup_with_namespace.job.get_job(id="18a0f501-41d5-ae43-ff61-1d8ec3ec8314", namespace="override-namespace") + nomad_setup_with_namespace.job.get_job(_id="18a0f501-41d5-ae43-ff61-1d8ec3ec8314", namespace="override-namespace") def test_get_allocations(nomad_setup): @@ -137,7 +137,7 @@ def test_get_job_deployment(nomad_setup): assert "example" == nomad_setup.job.get_deployment("example")["JobID"] @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") -def test_get_job_deployment(nomad_setup): +def test_get_summary(nomad_setup): assert "JobID" in nomad_setup.job.get_summary("example") assert isinstance(nomad_setup.job.get_summary("example"), dict) assert "example" == nomad_setup.job.get_summary("example")["JobID"] @@ -148,11 +148,6 @@ def test_revert_job(nomad_setup): prior_job_version = current_job_version - 1 nomad_setup.job.revert_job("example", prior_job_version, current_job_version) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") -def test_revert_job(nomad_setup): - current_job_version = nomad_setup.job.get_deployment("example")["JobVersion"] - prior_job_version = current_job_version - 1 - nomad_setup.job.revert_job("example", prior_job_version, current_job_version) @pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") def test_stable_job(nomad_setup): diff --git a/tests/test_sentinel.py b/tests/test_sentinel.py index 46dc07a..5d26057 100644 --- a/tests/test_sentinel.py +++ b/tests/test_sentinel.py @@ -40,7 +40,7 @@ def test_create_policy(nomad_setup): policy_example = '{"Name": "my-policy", "Description": "This is a great policy", "Scope": "submit-job", "EnforcementLevel": "advisory", "Policy": "main = rule { true }"}' json_policy = json.loads(policy_example) - nomad_setup.sentinel.create_policy(id="my-policy", policy=json_policy) + nomad_setup.sentinel.create_policy(_id="my-policy", policy=json_policy) @responses.activate @@ -48,13 +48,13 @@ def test_update_policy(nomad_setup): responses.add( responses.POST, - "http://{ip}:{port}/v1/sentinel/policy/my-policy".format(ip=common.IP, port=common.NOMAD_PORT), + f"http://{common.IP}:{common.NOMAD_PORT}/v1/sentinel/policy/my-policy", status=200 ) policy_example = '{"Name": "my-policy", "Description": "Update", "Scope": "submit-job", "EnforcementLevel": "advisory", "Policy": "main = rule { true }"}' json_policy = json.loads(policy_example) - nomad_setup.sentinel.update_policy(id="my-policy", policy=json_policy) + nomad_setup.sentinel.update_policy(_id="my-policy", policy=json_policy) @responses.activate @@ -97,4 +97,4 @@ def test_delete_policy(nomad_setup): } ) - nomad_setup.sentinel.delete_policy(id="my-policy") + nomad_setup.sentinel.delete_policy(_id="my-policy") From fb8afc229cdf51f8b94d576d2b10d5cf080c9ba9 Mon Sep 17 00:00:00 2001 From: KamilCuk Date: Mon, 16 Jan 2023 16:12:34 +0100 Subject: [PATCH 46/69] Add missing parameters, do not pass None (#144) * jobs: add additional parameters for job listing * allocations: add additional parameters to get_allocations * api/base: omit exception, check explicitly * api/base: cleanup parameters that are none * allocations,jobs: Add trailing underscore to filter not to conflict with builtin filter function Co-authored-by: Kamil Cukrowski --- nomad/api/allocations.py | 51 ++++++++++++++++++++++++++-------------- nomad/api/base.py | 7 ++++-- nomad/api/jobs.py | 47 ++++++++++++++++++++++-------------- 3 files changed, 69 insertions(+), 36 deletions(-) diff --git a/nomad/api/allocations.py b/nomad/api/allocations.py index a89e407..edd6229 100644 --- a/nomad/api/allocations.py +++ b/nomad/api/allocations.py @@ -1,4 +1,6 @@ """Nomad allocation: https://developer.hashicorp.com/nomad/api-docs/allocations""" +from typing import Optional + from nomad.api.base import Requester @@ -10,6 +12,7 @@ class Allocations(Requester): https://www.nomadproject.io/docs/http/allocs.html """ + ENDPOINT = "allocations" def __init__(self, **kwargs): @@ -32,22 +35,36 @@ def __iter__(self): response = self.get_allocations() return iter(response) - def get_allocations(self, prefix=None, namespace=None): - """ Lists all the allocations. - - https://www.nomadproject.io/docs/http/allocs.html - arguments: - - prefix :(str) optional, specifies a string to filter allocations on based on an prefix. - This is specified as a querystring parameter. - - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. - This is specified as a querystring parameter. - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException - """ - params = {"prefix": prefix} - if namespace: - params["namespace"] = namespace + def get_allocations( # pylint: disable=too-many-arguments + self, + prefix: Optional[str] = None, + filter_: Optional[str] = None, + namespace: Optional[str] = None, + resources: Optional[bool] = None, + task_states: Optional[bool] = None, + ): + """Lists all the allocations. + https://www.nomadproject.io/docs/http/allocs.html + arguments: + - prefix :(str) optional, specifies a string to filter allocations on based on an prefix. + This is specified as a querystring parameter. + - filter_ :(str) optional + Name has a trailing underscore not to conflict with builtin function. + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. + - resources :(bool) optional + - task_states :(bool) optional + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + """ + params = { + "prefix": prefix, + "filter": filter_, + "namespace": namespace, + "resources": resources, + "task_states": task_states, + } return self.request(method="get", params=params).json() diff --git a/nomad/api/base.py b/nomad/api/base.py index 3fd3975..22f6e3a 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -84,6 +84,9 @@ def _query_string_builder(self, endpoint, params=None): if not isinstance(params, dict): params = {} + # Remove parameters that are None + params = {key: val for key, val in params.items() if val is not None} + if ("namespace" not in params) and (self.namespace and self._required_namespace(endpoint)): query_string["namespace"] = self.namespace @@ -132,9 +135,9 @@ def _request( # pylint: disable=too-many-arguments, too-many-branches params = query_string if self.token: - try: + if headers is not None: headers["X-Nomad-Token"] = self.token - except TypeError: + else: headers = {"X-Nomad-Token": self.token} response = None diff --git a/nomad/api/jobs.py b/nomad/api/jobs.py index 98fa2b4..3d8793b 100644 --- a/nomad/api/jobs.py +++ b/nomad/api/jobs.py @@ -1,4 +1,5 @@ """Nomad job: https://developer.hashicorp.com/nomad/api-docs/jobs""" +from typing import Optional import nomad.api.exceptions from nomad.api.base import Requester @@ -62,24 +63,36 @@ def __iter__(self): jobs = self.get_jobs() return iter(jobs) - def get_jobs(self, prefix=None, namespace=None): - """ Lists all the jobs registered with Nomad. - - https://www.nomadproject.io/docs/http/jobs.html - arguments: - - prefix :(str) optional, specifies a string to filter jobs on based on an prefix. - This is specified as a querystring parameter. - - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. - This is specified as a querystring parameter. - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + def get_jobs( + self, + prefix: Optional[str] = None, + namespace: Optional[str] = None, + filter_: Optional[str] = None, + meta: Optional[bool] = None, + ): + """Lists all the jobs registered with Nomad. + + https://www.nomadproject.io/docs/http/jobs.html + arguments: + - prefix :(str) optional, specifies a string to filter jobs on based on an prefix. + This is specified as a querystring parameter. + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. + - filter_ :(str) optional, specifies the expression used to filter the result. + Name has a trailing underscore not to conflict with builtin function. + - meta :(bool) optional, if set, jobs returned will include a meta field containing + key-value pairs provided in the job specification's meta block. + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - params = {"prefix": prefix} - if namespace: - params["namespace"] = namespace - + params = { + "prefix": prefix, + "namespace": namespace, + "filter": filter_, + "meta": meta, + } return self.request(method="get", params=params).json() def register_job(self, job): From 94225917ef99d14a23f952f11b43edb2063cc520 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Tue, 31 Jan 2023 12:24:32 +0100 Subject: [PATCH 47/69] Add code formatter and change _id to id_ (#146) * replace _id to id_ * add black to format files * change changelog * fix linter * add missing record to CHANGELOG.md --- .github/workflows/lint.yml | 3 +- CHANGELOG.md | 4 +- nomad/__init__.py | 87 ++++---- nomad/api/acl.py | 196 +++++++++--------- nomad/api/agent.py | 68 +++---- nomad/api/allocation.py | 32 +-- nomad/api/allocations.py | 26 +-- nomad/api/base.py | 6 +- nomad/api/client.py | 268 ++++++++++++------------- nomad/api/deployment.py | 210 ++++++++++---------- nomad/api/deployments.py | 24 +-- nomad/api/evaluation.py | 42 ++-- nomad/api/evaluations.py | 23 +-- nomad/api/event.py | 13 +- nomad/api/exceptions.py | 1 + nomad/api/job.py | 395 ++++++++++++++++++------------------- nomad/api/jobs.py | 54 ++--- nomad/api/metrics.py | 14 +- nomad/api/namespace.py | 82 ++++---- nomad/api/namespaces.py | 22 +-- nomad/api/node.py | 175 ++++++++-------- nomad/api/nodes.py | 22 +-- nomad/api/operator.py | 47 +++-- nomad/api/regions.py | 14 +- nomad/api/scaling.py | 32 +-- nomad/api/search.py | 79 ++++---- nomad/api/sentinel.py | 91 +++++---- nomad/api/status.py | 27 ++- nomad/api/system.py | 25 ++- nomad/api/validate.py | 16 +- nomad/api/variable.py | 42 ++-- nomad/api/variables.py | 13 +- requirements-lint.txt | 2 + tests/conftest.py | 6 +- tests/test_acl.py | 75 ++++--- tests/test_allocation.py | 24 ++- tests/test_allocations.py | 39 +++- tests/test_base.py | 70 ++++--- tests/test_client.py | 26 ++- tests/test_deployment.py | 69 +++++-- tests/test_deployments.py | 50 ++++- tests/test_evaluation.py | 3 +- tests/test_evaluations.py | 2 +- tests/test_event.py | 8 +- tests/test_job.py | 110 ++++++++--- tests/test_jobs.py | 72 +++++-- tests/test_metrics.py | 6 +- tests/test_namespace.py | 11 +- tests/test_namespaces.py | 57 ++---- tests/test_node.py | 12 +- tests/test_nodes.py | 2 +- tests/test_operator.py | 12 +- tests/test_regions.py | 2 +- tests/test_scaling.py | 2 + tests/test_search.py | 34 +++- tests/test_sentinel.py | 26 ++- tests/test_status.py | 11 +- tests/test_validate.py | 8 +- tests/test_variable.py | 80 ++++++-- tests/test_variables.py | 64 ++++-- 60 files changed, 1698 insertions(+), 1338 deletions(-) create mode 100644 requirements-lint.txt diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 218d60e..095b56c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -24,8 +24,9 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements.txt - pip install pylint==2.15.8 + pip install -r requirements-lint.txt - name: Lint shell: bash run: | pylint nomad/ + black --check -l 120 -t py311 nomad/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 4431209..831e35f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,11 @@ ## 2.0.0 (unreleased) ### BREAKING CHANGES * Drop Python 2 and Python 3.6 support -* Rename `id` arguments to `_id` across of code base +* Rename `id` arguments to `id_` across of code base +* Rename `type` arguments to `type_` across of code base ### Other changes * Up `requests` lib version to 2.28.1 +* Add migging parameters to allocations.get_allocations and jobs.get_jobs (#144). Thanks @Kamilcuk ## 1.5.0 * Add `namespace` agrument support for `get_allocations` and `get_deployments` endpoints (#133) diff --git a/nomad/__init__.py b/nomad/__init__.py index be2be26..0ad41b9 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -3,51 +3,54 @@ from nomad import api -class Nomad(): # pylint: disable=too-many-public-methods,too-many-instance-attributes + +class Nomad: # pylint: disable=too-many-public-methods,too-many-instance-attributes """ Nomad API """ - def __init__(self, # pylint: disable=too-many-arguments - host='127.0.0.1', - secure=False, - port=4646, - address=os.getenv('NOMAD_ADDR', None), - namespace=os.getenv('NOMAD_NAMESPACE', None), - token=os.getenv('NOMAD_TOKEN', None), - timeout=5, - region=os.getenv('NOMAD_REGION', None), - version='v1', - verify=False, - cert=(os.getenv('NOMAD_CLIENT_CERT', None), - os.getenv('NOMAD_CLIENT_KEY', None)), - session=None): - """ Nomad api client - - https://github.com/jrxFive/python-nomad/ - - optional arguments: - - host (defaults 127.0.0.1), string ip or name of the nomad api server/agent that will be used. - - port (defaults 4646), integer port that will be used to connect. - - secure (defaults False), define if the protocol is secured or not (https or http) - - version (defaults v1), vesion of the api of nomad. - - verify (defaults False), verify the certificate when tls/ssl is enabled - at nomad. - - cert (defaults empty), cert, or key and cert file to validate the certificate - configured at nomad. - - region (defaults None), version of the region to use. It will be used then - regions of the current agent of the connection. - - namespace (defaults to None), Specifies the enterpise namespace that will - be use to deploy or to ask info to nomad. - - token (defaults to None), Specifies to append ACL token to the headers to - make authentication on secured based nomad environemnts. - - session (defaults to None), allows for injecting a prepared requests.Session object that - all requests to Nomad should use. - returns: Nomad api client object - - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException - - nomad.api.exceptions.URLNotAuthorizedNomadException + + def __init__( # pylint: disable=too-many-arguments + self, + host="127.0.0.1", + secure=False, + port=4646, + address=os.getenv("NOMAD_ADDR", None), + namespace=os.getenv("NOMAD_NAMESPACE", None), + token=os.getenv("NOMAD_TOKEN", None), + timeout=5, + region=os.getenv("NOMAD_REGION", None), + version="v1", + verify=False, + cert=(os.getenv("NOMAD_CLIENT_CERT", None), os.getenv("NOMAD_CLIENT_KEY", None)), + session=None, + ): + """Nomad api client + + https://github.com/jrxFive/python-nomad/ + + optional arguments: + - host (defaults 127.0.0.1), string ip or name of the nomad api server/agent that will be used. + - port (defaults 4646), integer port that will be used to connect. + - secure (defaults False), define if the protocol is secured or not (https or http) + - version (defaults v1), vesion of the api of nomad. + - verify (defaults False), verify the certificate when tls/ssl is enabled + at nomad. + - cert (defaults empty), cert, or key and cert file to validate the certificate + configured at nomad. + - region (defaults None), version of the region to use. It will be used then + regions of the current agent of the connection. + - namespace (defaults to None), Specifies the enterpise namespace that will + be use to deploy or to ask info to nomad. + - token (defaults to None), Specifies to append ACL token to the headers to + make authentication on secured based nomad environemnts. + - session (defaults to None), allows for injecting a prepared requests.Session object that + all requests to Nomad should use. + returns: Nomad api client object + + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.URLNotAuthorizedNomadException """ self.host = host self.secure = secure diff --git a/nomad/api/acl.py b/nomad/api/acl.py index 857c2dc..9b3b024 100644 --- a/nomad/api/acl.py +++ b/nomad/api/acl.py @@ -25,170 +25,170 @@ def __getattr__(self, item): raise AttributeError def generate_bootstrap(self): - """ Activate bootstrap token. + """Activate bootstrap token. - https://www.nomadproject.io/api/acl-tokens.html + https://www.nomadproject.io/api/acl-tokens.html - returns: dict + returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("bootstrap", method="post").json() def get_tokens(self): - """ Get a list of tokens. + """Get a list of tokens. - https://www.nomadproject.io/api/acl-tokens.html + https://www.nomadproject.io/api/acl-tokens.html - returns: list + returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("tokens", method="get").json() - def get_token(self, _id): - """ Retrieve specific token. + def get_token(self, id_): + """Retrieve specific token. - https://www.nomadproject.io/api/acl-tokens.html + https://www.nomadproject.io/api/acl-tokens.html - returns: dict + returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("token", _id, method="get").json() + return self.request("token", id_, method="get").json() def get_self_token(self): - """ Retrieve self token used for auth. + """Retrieve self token used for auth. - https://www.nomadproject.io/api/acl-tokens.html + https://www.nomadproject.io/api/acl-tokens.html - returns: dict + returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("token", "self", method="get").json() def create_token(self, token): - """ Create token. + """Create token. - https://www.nomadproject.io/api/acl-tokens.html + https://www.nomadproject.io/api/acl-tokens.html - arguments: - token - returns: dict + arguments: + token + returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("token", json=token, method="post").json() - def delete_token(self, _id): - """ Delete specific token. + def delete_token(self, id_): + """Delete specific token. - https://www.nomadproject.io/api/acl-tokens.html + https://www.nomadproject.io/api/acl-tokens.html - returns: Boolean + returns: Boolean - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("token", _id, method="delete").ok + return self.request("token", id_, method="delete").ok - def update_token(self, _id, token): - """ Update token. + def update_token(self, id_, token): + """Update token. - https://www.nomadproject.io/api/acl-tokens.html + https://www.nomadproject.io/api/acl-tokens.html - arguments: - - AccdesorID - - token - returns: dict + arguments: + - AccdesorID + - token + returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("token", _id, json=token, method="post").json() + return self.request("token", id_, json=token, method="post").json() def get_policies(self): - """ Get a list of policies. + """Get a list of policies. - https://www.nomadproject.io/api/acl-policies.html + https://www.nomadproject.io/api/acl-policies.html - returns: list + returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("policies", method="get").json() - def create_policy(self, _id, policy): - """ Create policy. + def create_policy(self, id_, policy): + """Create policy. - https://www.nomadproject.io/api/acl-policies.html + https://www.nomadproject.io/api/acl-policies.html - arguments: - - policy - returns: request.Response + arguments: + - policy + returns: request.Response - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", _id, json=policy, method="post") + return self.request("policy", id_, json=policy, method="post") - def get_policy(self, _id): - """ Get a spacific. + def get_policy(self, id_): + """Get a spacific. - https://www.nomadproject.io/api/acl-policies.html + https://www.nomadproject.io/api/acl-policies.html - returns: dict + returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", _id, method="get").json() + return self.request("policy", id_, method="get").json() - def update_policy(self, _id, policy): - """ Create policy. + def update_policy(self, id_, policy): + """Create policy. - https://www.nomadproject.io/api/acl-policies.html + https://www.nomadproject.io/api/acl-policies.html - arguments: - - name - - policy - returns: request.Response + arguments: + - name + - policy + returns: request.Response - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", _id, json=policy, method="post") + return self.request("policy", id_, json=policy, method="post") - def delete_policy(self, _id): - """ Delete specific policy. + def delete_policy(self, id_): + """Delete specific policy. - https://www.nomadproject.io/api/acl-policies.html + https://www.nomadproject.io/api/acl-policies.html - arguments: - - id - returns: Boolean + arguments: + - id + returns: Boolean - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", _id, method="delete").ok + return self.request("policy", id_, method="delete").ok diff --git a/nomad/api/agent.py b/nomad/api/agent.py index 403d035..7ad412c 100644 --- a/nomad/api/agent.py +++ b/nomad/api/agent.py @@ -3,8 +3,8 @@ class Agent(Requester): - """The self endpoint is used to query the state of the target agent.""" + ENDPOINT = "agent" def __init__(self, **kwargs): @@ -21,64 +21,64 @@ def __getattr__(self, item): raise AttributeError(msg) def get_agent(self): - """ Query the state of the target agent. + """Query the state of the target agent. - https://www.nomadproject.io/docs/http/agent-self.html + https://www.nomadproject.io/docs/http/agent-self.html - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("self", method="get").json() def get_members(self): """Lists the known members of the gossip pool. - https://www.nomadproject.io/docs/http/agent-members.html + https://www.nomadproject.io/docs/http/agent-members.html - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("members", method="get").json() def get_servers(self): - """ Lists the known members of the gossip pool. + """Lists the known members of the gossip pool. - https://www.nomadproject.io/docs/http/agent-servers.html + https://www.nomadproject.io/docs/http/agent-servers.html - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("servers", method="get").json() def join_agent(self, addresses): """Initiate a join between the agent and target peers. - https://www.nomadproject.io/docs/http/agent-join.html + https://www.nomadproject.io/docs/http/agent-join.html - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {"address": addresses} return self.request("join", params=params, method="post").json() def update_servers(self, addresses): """Updates the list of known servers to the provided list. - Replaces all previous server addresses with the new list. + Replaces all previous server addresses with the new list. - https://www.nomadproject.io/docs/http/agent-servers.html + https://www.nomadproject.io/docs/http/agent-servers.html - returns: 200 status code - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: 200 status code + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {"address": addresses} return self.request("servers", params=params, method="post").status_code @@ -86,12 +86,12 @@ def update_servers(self, addresses): def force_leave(self, node): """Force a failed gossip member into the left state. - https://www.nomadproject.io/docs/http/agent-force-leave.html + https://www.nomadproject.io/docs/http/agent-force-leave.html - returns: 200 status code - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: 200 status code + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {"node": node} return self.request("force-leave", params=params, method="post").status_code diff --git a/nomad/api/allocation.py b/nomad/api/allocation.py index c6ab949..6ca74a4 100644 --- a/nomad/api/allocation.py +++ b/nomad/api/allocation.py @@ -47,26 +47,26 @@ def __getitem__(self, item): except nomad.api.exceptions.URLNotFoundNomadException as exc: raise KeyError from exc - def get_allocation(self, _id): - """ Query a specific allocation. + def get_allocation(self, id_): + """Query a specific allocation. - https://www.nomadproject.io/docs/http/alloc.html + https://www.nomadproject.io/docs/http/alloc.html - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, method="get").json() + return self.request(id_, method="get").json() - def stop_allocation(self, _id): - """ Stop a specific allocation. + def stop_allocation(self, id_): + """Stop a specific allocation. - https://www.nomadproject.io/api-docs/allocations/#stop-allocation + https://www.nomadproject.io/api-docs/allocations/#stop-allocation - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "stop", method="post").json() + return self.request(id_, "stop", method="post").json() diff --git a/nomad/api/allocations.py b/nomad/api/allocations.py index edd6229..c776772 100644 --- a/nomad/api/allocations.py +++ b/nomad/api/allocations.py @@ -46,19 +46,19 @@ def get_allocations( # pylint: disable=too-many-arguments """Lists all the allocations. https://www.nomadproject.io/docs/http/allocs.html - arguments: - - prefix :(str) optional, specifies a string to filter allocations on based on an prefix. - This is specified as a querystring parameter. - - filter_ :(str) optional - Name has a trailing underscore not to conflict with builtin function. - - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. - This is specified as a querystring parameter. - - resources :(bool) optional - - task_states :(bool) optional - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - prefix :(str) optional, specifies a string to filter allocations on based on an prefix. + This is specified as a querystring parameter. + - filter_ :(str) optional + Name has a trailing underscore not to conflict with builtin function. + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. + - resources :(bool) optional + - task_states :(bool) optional + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = { "prefix": prefix, diff --git a/nomad/api/base.py b/nomad/api/base.py index 22f6e3a..a4c7329 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -4,7 +4,7 @@ import nomad.api.exceptions -class Requester(): # pylint: disable=too-many-instance-attributes,too-few-public-methods +class Requester: # pylint: disable=too-many-instance-attributes,too-few-public-methods """ Base object for endpoints """ @@ -23,7 +23,7 @@ def __init__( # pylint: disable=too-many-arguments verify=False, cert=(), region=None, - session=None + session=None, ): self.uri = uri self.port = port @@ -114,7 +114,7 @@ def request(self, *args, **kwargs): return response - def _request( # pylint: disable=too-many-arguments, too-many-branches + def _request( # pylint: disable=too-many-arguments, too-many-branches self, method, endpoint, diff --git a/nomad/api/client.py b/nomad/api/client.py index 7b495fe..cefbd33 100644 --- a/nomad/api/client.py +++ b/nomad/api/client.py @@ -2,10 +2,13 @@ # pylint: disable=invalid-name,too-many-instance-attributes,too-many-arguments """Nomad Client: https://developer.hashicorp.com/nomad/api-docs/client""" from nomad.api.base import Requester -class Client(): + + +class Client: """ The /client endpoints are used to interact with the Nomad clients. """ + def __init__(self, **kwargs): self.ls = ls(**kwargs) self.cat = cat(**kwargs) @@ -44,21 +47,21 @@ class ls(Requester): def __init__(self, **kwargs): super().__init__(**kwargs) - def list_files(self, _id=None, path="/"): - """ List files in an allocation directory. + def list_files(self, id_=None, path="/"): + """List files in an allocation directory. - https://www.nomadproject.io/docs/http/client-fs-ls.html + https://www.nomadproject.io/docs/http/client-fs-ls.html - arguments: - - _id - - path - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + - path + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - if _id: - return self.request(_id, params={"path": path}, method="get").json() + if id_: + return self.request(id_, params={"path": path}, method="get").json() return self.request(params={"path": path}, method="get").json() @@ -79,21 +82,21 @@ class cat(Requester): def __init__(self, **kwargs): super().__init__(**kwargs) - def read_file(self, _id=None, path="/"): - """ Read contents of a file in an allocation directory. + def read_file(self, id_=None, path="/"): + """Read contents of a file in an allocation directory. - https://www.nomadproject.io/docs/http/client-fs-cat.html + https://www.nomadproject.io/docs/http/client-fs-cat.html - arguments: - - _id - - path - returns: (str) text - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + - path + returns: (str) text + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - if _id: - return self.request(_id, params={"path": path}, method="get").text + if id_: + return self.request(id_, params={"path": path}, method="get").text return self.request(params={"path": path}, method="get").text @@ -111,27 +114,23 @@ class read_at(Requester): def __init__(self, **kwargs): super().__init__(**kwargs) - def read_file_offset(self, _id, offset, limit, path="/"): - """ Read contents of a file in an allocation directory. + def read_file_offset(self, id_, offset, limit, path="/"): + """Read contents of a file in an allocation directory. - https://www.nomadproject.io/docs/http/client-fs-cat.html + https://www.nomadproject.io/docs/http/client-fs-cat.html - arguments: - - _id: (str) allocation_id required - - offset: (int) required - - limit: (int) required - - path: (str) optional - returns: (str) text - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.BadRequestNomadException + arguments: + - id_: (str) allocation_id required + - offset: (int) required + - limit: (int) required + - path: (str) optional + returns: (str) text + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.BadRequestNomadException """ - params = { - "path": path, - "offset": offset, - "limit": limit - } - return self.request(_id, params=params, method="get").text + params = {"path": path, "offset": offset, "limit": limit} + return self.request(id_, params=params, method="get").text class stream_file(Requester): @@ -147,27 +146,23 @@ class stream_file(Requester): def __init__(self, **kwargs): super().__init__(**kwargs) - def stream(self, _id, offset, origin, path="/"): - """ This endpoint streams the contents of a file in an allocation directory. + def stream(self, id_, offset, origin, path="/"): + """This endpoint streams the contents of a file in an allocation directory. - https://www.nomadproject.io/api/client.html#stream-file + https://www.nomadproject.io/api/client.html#stream-file - arguments: - - _id: (str) allocation_id required - - offset: (int) required - - origin: (str) either start|end - - path: (str) optional - returns: (str) text - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.BadRequestNomadException + arguments: + - id_: (str) allocation_id required + - offset: (int) required + - origin: (str) either start|end + - path: (str) optional + returns: (str) text + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.BadRequestNomadException """ - params = { - "path": path, - "offset": offset, - "origin": origin - } - return self.request(_id, params=params, method="get").text + params = {"path": path, "offset": offset, "origin": origin} + return self.request(id_, params=params, method="get").text class stream_logs(Requester): @@ -183,33 +178,26 @@ class stream_logs(Requester): def __init__(self, **kwargs): super().__init__(**kwargs) - def stream(self, _id, task, _type, follow=False, offset=0, origin="start", plain=False): - """ This endpoint streams a task's stderr/stdout logs. - - https://www.nomadproject.io/api/client.html#stream-logs - - arguments: - - _id: (str) allocation_id required - - task: (str) name of the task inside the allocation to stream logs from - - _type: (str) Specifies the stream to stream. Either "stderr|stdout" - - follow: (bool) default false - - offset: (int) default 0 - - origin: (str) either start|end, default "start" - - plain: (bool) Return just the plain text without framing. default False - returns: (str) text - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.BadRequestNomadException + def stream(self, id_, task, type_, follow=False, offset=0, origin="start", plain=False): + """This endpoint streams a task's stderr/stdout logs. + + https://www.nomadproject.io/api/client.html#stream-logs + + arguments: + - id_: (str) allocation_id required + - task: (str) name of the task inside the allocation to stream logs from + - type_: (str) Specifies the stream to stream. Either "stderr|stdout" + - follow: (bool) default false + - offset: (int) default 0 + - origin: (str) either start|end, default "start" + - plain: (bool) Return just the plain text without framing. default False + returns: (str) text + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.BadRequestNomadException """ - params = { - "task": task, - "type": _type, - "follow": follow, - "offset": offset, - "origin": origin, - "plain": plain - } - return self.request(_id, params=params, method="get").text + params = {"task": task, "type": type_, "follow": follow, "offset": offset, "origin": origin, "plain": plain} + return self.request(id_, params=params, method="get").text class stat(Requester): @@ -226,27 +214,26 @@ class stat(Requester): def __init__(self, **kwargs): super().__init__(**kwargs) - def stat_file(self, _id=None, path="/"): - """ Stat a file in an allocation directory. + def stat_file(self, id_=None, path="/"): + """Stat a file in an allocation directory. - https://www.nomadproject.io/docs/http/client-fs-stat.html + https://www.nomadproject.io/docs/http/client-fs-stat.html - arguments: - - _id - - path - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + - path + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - if _id: - return self.request(_id, params={"path": path}, method="get").json() + if id_: + return self.request(id_, params={"path": path}, method="get").json() return self.request(params={"path": path}, method="get").json() class stats(Requester): - """ The /stats endpoint queries the actual resources consumed on a node. The API endpoint is hosted by the Nomad client and requests have to @@ -261,21 +248,21 @@ def __init__(self, **kwargs): super().__init__(**kwargs) def read_stats(self, node_id=None): - """ Query the actual resources consumed on a node. + """ + Query the actual resources consumed on a node. - https://www.nomadproject.io/api/client.html#read-stats + https://www.nomadproject.io/api/client.html#read-stats - arguments: - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request(params={"node_id": node_id}, method="get").json() class allocation(Requester): - """ The allocation/:alloc_id/stats endpoint is used to query the actual resources consumed by an allocation. The API endpoint is hosted by the @@ -290,30 +277,30 @@ class allocation(Requester): def __init__(self, **kwargs): super().__init__(**kwargs) - def read_allocation_stats(self, _id): - """ Query the actual resources consumed by an allocation. + def read_allocation_stats(self, id_): + """Query the actual resources consumed by an allocation. - https://www.nomadproject.io/api/client.html#read-allocation + https://www.nomadproject.io/api/client.html#read-allocation - arguments: - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "stats", method="get").json() + return self.request(id_, "stats", method="get").json() - def restart_allocation(self, _id): - """ Restart a specific allocation. + def restart_allocation(self, id_): + """Restart a specific allocation. - https://www.nomadproject.io/api-docs/allocations/#restart-allocation + https://www.nomadproject.io/api-docs/allocations/#restart-allocation - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "restart", method="post").json() + return self.request(id_, "restart", method="post").json() class gc_allocation(Requester): @@ -329,22 +316,21 @@ class gc_allocation(Requester): def __init__(self, **kwargs): super().__init__(**kwargs) - def garbage_collect(self, _id): - """ This endpoint forces a garbage collection of a particular, stopped allocation on a node. + def garbage_collect(self, id_): + """This endpoint forces a garbage collection of a particular, stopped allocation on a node. - https://www.nomadproject.io/api/client.html#gc-allocation + https://www.nomadproject.io/api/client.html#gc-allocation - arguments: - - _id: (str) full allocation_id - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_: (str) full allocation_id + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - self.request(_id, "gc", method="get") + self.request(id_, "gc", method="get") class gc_all_allocations(Requester): - """ This endpoint forces a garbage collection of all stopped allocations on a node. @@ -357,13 +343,13 @@ def __init__(self, **kwargs): super().__init__(**kwargs) def garbage_collect(self, node_id=None): - """ This endpoint forces a garbage collection of all stopped allocations on a node. + """This endpoint forces a garbage collection of all stopped allocations on a node. - https://www.nomadproject.io/api/client.html#gc-all-allocation + https://www.nomadproject.io/api/client.html#gc-all-allocation - arguments: - - node_id: (str) full allocation_id - raises: - - nomad.api.exceptions.BaseNomadException + arguments: + - node_id: (str) full allocation_id + raises: + - nomad.api.exceptions.BaseNomadException """ self.request(params={"node_id": node_id}, method="get") diff --git a/nomad/api/deployment.py b/nomad/api/deployment.py index 90e27e2..2b2432c 100644 --- a/nomad/api/deployment.py +++ b/nomad/api/deployment.py @@ -11,6 +11,7 @@ class Deployment(Requester): https://www.nomadproject.io/docs/http/deployments.html """ + ENDPOINT = "deployment" def __init__(self, **kwargs): @@ -42,123 +43,120 @@ def __getitem__(self, item): except nomad.api.exceptions.URLNotFoundNomadException as exp: raise KeyError from exp - def get_deployment(self, _id): - """ This endpoint reads information about a specific deployment by ID. + def get_deployment(self, id_): + """This endpoint reads information about a specific deployment by ID. - https://www.nomadproject.io/docs/http/deployments.html + https://www.nomadproject.io/docs/http/deployments.html - arguments: - - _id - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, method="get").json() + return self.request(id_, method="get").json() - def get_deployment_allocations(self, _id): - """ This endpoint lists the allocations created or modified for the given deployment. + def get_deployment_allocations(self, id_): + """This endpoint lists the allocations created or modified for the given deployment. - https://www.nomadproject.io/docs/http/deployments.html + https://www.nomadproject.io/docs/http/deployments.html - arguments: - - _id - returns: list of dicts - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: list of dicts + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("allocations", _id, method="get").json() + return self.request("allocations", id_, method="get").json() - def fail_deployment(self, _id): - """ This endpoint is used to mark a deployment as failed. This should be done to force the scheduler to stop - creating allocations as part of the deployment or to cause a rollback to a previous job version. + def fail_deployment(self, id_): + """This endpoint is used to mark a deployment as failed. This should be done to force the scheduler to stop + creating allocations as part of the deployment or to cause a rollback to a previous job version. - https://www.nomadproject.io/docs/http/deployments.html + https://www.nomadproject.io/docs/http/deployments.html - arguments: - - _id - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - fail_json = {"DeploymentID": _id} - return self.request("fail", _id, json=fail_json, method="post").json() - - def pause_deployment(self, _id, pause): - """ This endpoint is used to pause or unpause a deployment. - This is done to pause a rolling upgrade or resume it. - - https://www.nomadproject.io/docs/http/deployments.html - - arguments: - - _id - - pause, Specifies whether to pause or resume the deployment. - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + fail_json = {"DeploymentID": id_} + return self.request("fail", id_, json=fail_json, method="post").json() + + def pause_deployment(self, id_, pause): + """This endpoint is used to pause or unpause a deployment. + This is done to pause a rolling upgrade or resume it. + + https://www.nomadproject.io/docs/http/deployments.html + + arguments: + - id_ + - pause, Specifies whether to pause or resume the deployment. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - pause_json = {"Pause": pause, - "DeploymentID": _id} - return self.request("pause", _id, json=pause_json, method="post").json() - - def promote_deployment_all(self, _id, _all=True): - """ This endpoint is used to promote task groups that have canaries for a deployment. This should be done when - the placed canaries are healthy and the rolling upgrade of the remaining allocations should begin. - - https://www.nomadproject.io/docs/http/deployments.html - - arguments: - - _id - - _all, Specifies whether all task groups should be promoted. - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + pause_json = {"Pause": pause, "DeploymentID": id_} + return self.request("pause", id_, json=pause_json, method="post").json() + + def promote_deployment_all(self, id_, _all=True): + """This endpoint is used to promote task groups that have canaries for a deployment. This should be done when + the placed canaries are healthy and the rolling upgrade of the remaining allocations should begin. + + https://www.nomadproject.io/docs/http/deployments.html + + arguments: + - id_ + - _all, Specifies whether all task groups should be promoted. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - promote_all_json = {"All": _all, - "DeploymentID": _id} - return self.request("promote", _id, json=promote_all_json, method="post").json() - - def promote_deployment_groups(self, _id, groups=None): - """ This endpoint is used to promote task groups that have canaries for a deployment. This should be done when - the placed canaries are healthy and the rolling upgrade of the remaining allocations should begin. - - https://www.nomadproject.io/docs/http/deployments.html - - arguments: - - _id - - groups, (list) Specifies a particular set of task groups that should be promoted - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + promote_all_json = {"All": _all, "DeploymentID": id_} + return self.request("promote", id_, json=promote_all_json, method="post").json() + + def promote_deployment_groups(self, id_, groups=None): + """This endpoint is used to promote task groups that have canaries for a deployment. This should be done when + the placed canaries are healthy and the rolling upgrade of the remaining allocations should begin. + + https://www.nomadproject.io/docs/http/deployments.html + + arguments: + - id_ + - groups, (list) Specifies a particular set of task groups that should be promoted + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ if groups is None: groups = [] - promote_groups_json = {"Groups": groups, - "DeploymentID": _id} - return self.request("promote", _id, json=promote_groups_json, method="post").json() - - def deployment_allocation_health(self, _id, healthy_allocations=None, unhealthy_allocations=None): - """ This endpoint is used to set the health of an allocation that is in the deployment manually. In some use - cases, automatic detection of allocation health may not be desired. As such those task groups can be marked - with an upgrade policy that uses health_check = "manual". Those allocations must have their health marked - manually using this endpoint. Marking an allocation as healthy will allow the rolling upgrade to proceed. - Marking it as failed will cause the deployment to fail. - - https://www.nomadproject.io/docs/http/deployments.html - - arguments: - - _id - - healthy_allocations, Specifies the set of allocation that should be marked as healthy. - - unhealthy_allocations, Specifies the set of allocation that should be marked as unhealthy. - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + promote_groups_json = {"Groups": groups, "DeploymentID": id_} + return self.request("promote", id_, json=promote_groups_json, method="post").json() + + def deployment_allocation_health(self, id_, healthy_allocations=None, unhealthy_allocations=None): + """This endpoint is used to set the health of an allocation that is in the deployment manually. In some use + cases, automatic detection of allocation health may not be desired. As such those task groups can be marked + with an upgrade policy that uses health_check = "manual". Those allocations must have their health marked + manually using this endpoint. Marking an allocation as healthy will allow the rolling upgrade to proceed. + Marking it as failed will cause the deployment to fail. + + https://www.nomadproject.io/docs/http/deployments.html + + arguments: + - id_ + - healthy_allocations, Specifies the set of allocation that should be marked as healthy. + - unhealthy_allocations, Specifies the set of allocation that should be marked as unhealthy. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ if healthy_allocations is None: healthy_allocations = [] @@ -166,7 +164,9 @@ def deployment_allocation_health(self, _id, healthy_allocations=None, unhealthy_ if unhealthy_allocations is None: unhealthy_allocations = [] - allocations = {"HealthyAllocationIDs": healthy_allocations, - "UnHealthyAllocationIDs": unhealthy_allocations, - "DeploymentID": _id} - return self.request("allocation-health", _id, json=allocations, method="post").json() + allocations = { + "HealthyAllocationIDs": healthy_allocations, + "UnHealthyAllocationIDs": unhealthy_allocations, + "DeploymentID": id_, + } + return self.request("allocation-health", id_, json=allocations, method="post").json() diff --git a/nomad/api/deployments.py b/nomad/api/deployments.py index a812d3a..91dd335 100644 --- a/nomad/api/deployments.py +++ b/nomad/api/deployments.py @@ -5,12 +5,12 @@ class Deployments(Requester): - """ The /deployment endpoints are used to query for and interact with deployments. https://www.nomadproject.io/docs/http/deployments.html """ + ENDPOINT = "deployments" def __init__(self, **kwargs): @@ -55,20 +55,20 @@ def __getitem__(self, item): raise KeyError from exc def get_deployments(self, prefix="", namespace=None): - """ This endpoint lists all deployments. + """This endpoint lists all deployments. - https://www.nomadproject.io/docs/http/deployments.html + https://www.nomadproject.io/docs/http/deployments.html - optional_arguments: - - prefix, (default "") Specifies a string to filter deployments on based on an index prefix. - This is specified as a querystring parameter. - - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. - This is specified as a querystring parameter. + optional_arguments: + - prefix, (default "") Specifies a string to filter deployments on based on an index prefix. + This is specified as a querystring parameter. + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. - returns: list of dicts - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: list of dicts + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {"prefix": prefix} if namespace: diff --git a/nomad/api/evaluation.py b/nomad/api/evaluation.py index a9ad84c..e7bbf9b 100644 --- a/nomad/api/evaluation.py +++ b/nomad/api/evaluation.py @@ -5,7 +5,6 @@ class Evaluation(Requester): - """ The evaluation endpoint is used to query a specific evaluations. By default, the agent's local region is used; another region can @@ -13,6 +12,7 @@ class Evaluation(Requester): https://www.nomadproject.io/docs/http/eval.html """ + ENDPOINT = "evaluation" def __init__(self, **kwargs): @@ -45,30 +45,30 @@ def __getitem__(self, item): except nomad.api.exceptions.URLNotFoundNomadException as exc: raise KeyError from exc - def get_evaluation(self, _id): - """ Query a specific evaluation. + def get_evaluation(self, id_): + """Query a specific evaluation. - https://www.nomadproject.io/docs/http/eval.html + https://www.nomadproject.io/docs/http/eval.html - arguments: - - _id - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, method="get").json() + return self.request(id_, method="get").json() - def get_allocations(self, _id): - """ Query the allocations created or modified by an evaluation. + def get_allocations(self, id_): + """Query the allocations created or modified by an evaluation. - https://www.nomadproject.io/docs/http/eval.html + https://www.nomadproject.io/docs/http/eval.html - arguments: - - _id - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "allocations", method="get").json() + return self.request(id_, "allocations", method="get").json() diff --git a/nomad/api/evaluations.py b/nomad/api/evaluations.py index 8ec96e8..dae0f61 100644 --- a/nomad/api/evaluations.py +++ b/nomad/api/evaluations.py @@ -3,8 +3,8 @@ from nomad.api.base import Requester -class Evaluations(Requester): +class Evaluations(Requester): """ The evaluations endpoint is used to query the status of evaluations. By default, the agent's local region is used; another region can @@ -12,6 +12,7 @@ class Evaluations(Requester): https://www.nomadproject.io/docs/http/evals.html """ + ENDPOINT = "evaluations" def __init__(self, **kwargs): @@ -58,16 +59,16 @@ def __iter__(self): return iter(evaluations) def get_evaluations(self, prefix=None): - """ Lists all the evaluations. - - https://www.nomadproject.io/docs/http/evals.html - arguments: - - prefix :(str) optional, specifies a string to filter evaluations on based on an prefix. - This is specified as a querystring parameter. - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + """Lists all the evaluations. + + https://www.nomadproject.io/docs/http/evals.html + arguments: + - prefix :(str) optional, specifies a string to filter evaluations on based on an prefix. + This is specified as a querystring parameter. + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {"prefix": prefix} return self.request(method="get", params=params).json() diff --git a/nomad/api/event.py b/nomad/api/event.py index ba001fc..cafadb2 100644 --- a/nomad/api/event.py +++ b/nomad/api/event.py @@ -7,10 +7,12 @@ from nomad.api.base import Requester -class Event(): + +class Event: """ Nomad Event """ + def __str__(self): return f"{self.__dict__}" @@ -23,6 +25,7 @@ def __getattr__(self, item): def __init__(self, **kwargs): self.stream = stream(**kwargs) + # backward compatibility class stream(Requester): # pylint: disable=invalid-name """ @@ -63,7 +66,9 @@ def _get_stream(self, method, params, timeout, event_queue, exit_event): # pyli except requests.exceptions.ConnectionError: continue - def get_stream(self, index=0, topic=None, namespace=None, event_queue=None, timeout=None): # pylint: disable=too-many-arguments + def get_stream( + self, index=0, topic=None, namespace=None, event_queue=None, timeout=None + ): # pylint: disable=too-many-arguments """ Usage: stream, stream_exit_event, events = n.event.stream.get_stream() @@ -120,8 +125,8 @@ def get_stream(self, index=0, topic=None, namespace=None, event_queue=None, time "params": params, "timeout": timeout, "event_queue": event_queue, - "exit_event": stream_exit_event - } + "exit_event": stream_exit_event, + }, ) return _stream, stream_exit_event, event_queue diff --git a/nomad/api/exceptions.py b/nomad/api/exceptions.py index 5f60205..578c16f 100644 --- a/nomad/api/exceptions.py +++ b/nomad/api/exceptions.py @@ -4,6 +4,7 @@ class BaseNomadException(Exception): """General Error occurred when interacting with nomad API""" + def __init__(self, nomad_resp): self.nomad_resp = nomad_resp diff --git a/nomad/api/job.py b/nomad/api/job.py index 6d0286b..03288db 100644 --- a/nomad/api/job.py +++ b/nomad/api/job.py @@ -5,13 +5,13 @@ class Job(Requester): - """ The job endpoint is used for CRUD on a single job. By default, the agent's local region is used. https://www.nomadproject.io/docs/http/job.html """ + ENDPOINT = "job" def __init__(self, **kwargs): @@ -46,257 +46,254 @@ def __getitem__(self, item): except nomad.api.exceptions.URLNotFoundNomadException as exc: raise KeyError from exc - def get_job(self, _id, namespace=None): - """ Query a single job for its specification and status. + def get_job(self, id_, namespace=None): + """Query a single job for its specification and status. - https://www.nomadproject.io/docs/http/job.html + https://www.nomadproject.io/docs/http/job.html - arguments: - - _id - - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. - This is specified as a querystring parameter. - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {} if namespace: params["namespace"] = namespace - return self.request(_id, method="get", params=params).json() + return self.request(id_, method="get", params=params).json() - def get_versions(self, _id): - """ This endpoint reads information about all versions of a job. + def get_versions(self, id_): + """This endpoint reads information about all versions of a job. - https://www.nomadproject.io/docs/http/job.html + https://www.nomadproject.io/docs/http/job.html - arguments: - - id - returns: list of dicts - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id + returns: list of dicts + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "versions", method="get").json() + return self.request(id_, "versions", method="get").json() - def get_allocations(self, _id): - """ Query the allocations belonging to a single job. + def get_allocations(self, id_): + """Query the allocations belonging to a single job. - https://www.nomadproject.io/docs/http/job.html + https://www.nomadproject.io/docs/http/job.html - arguments: - - _id - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "allocations", method="get").json() + return self.request(id_, "allocations", method="get").json() - def get_evaluations(self, _id): - """ Query the evaluations belonging to a single job. + def get_evaluations(self, id_): + """Query the evaluations belonging to a single job. - https://www.nomadproject.io/docs/http/job.html + https://www.nomadproject.io/docs/http/job.html - arguments: - - _id - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "evaluations", method="get").json() + return self.request(id_, "evaluations", method="get").json() - def get_deployments(self, _id): - """ This endpoint lists a single job's deployments + def get_deployments(self, id_): + """This endpoint lists a single job's deployments - https://www.nomadproject.io/docs/http/job.html + https://www.nomadproject.io/docs/http/job.html - arguments: - - _id - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "deployments", method="get").json() + return self.request(id_, "deployments", method="get").json() - def get_deployment(self, _id): - """ This endpoint returns a single job's most recent deployment. + def get_deployment(self, id_): + """This endpoint returns a single job's most recent deployment. - https://www.nomadproject.io/docs/http/job.html + https://www.nomadproject.io/docs/http/job.html - arguments: - - _id - returns: list of dicts - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: list of dicts + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "deployment", method="get").json() + return self.request(id_, "deployment", method="get").json() - def get_summary(self, _id): - """ Query the summary of a job. + def get_summary(self, id_): + """Query the summary of a job. - https://www.nomadproject.io/docs/http/job.html + https://www.nomadproject.io/docs/http/job.html - arguments: - - _id - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "summary", method="get").json() + return self.request(id_, "summary", method="get").json() - def register_job(self, _id, job): - """ Registers a new job or updates an existing job + def register_job(self, id_, job): + """Registers a new job or updates an existing job - https://www.nomadproject.io/docs/http/job.html + https://www.nomadproject.io/docs/http/job.html - arguments: - - _id - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, json=job, method="post").json() + return self.request(id_, json=job, method="post").json() - def evaluate_job(self, _id): - """ Creates a new evaluation for the given job. - This can be used to force run the scheduling logic if necessary. + def evaluate_job(self, id_): + """Creates a new evaluation for the given job. + This can be used to force run the scheduling logic if necessary. - https://www.nomadproject.io/docs/http/job.html + https://www.nomadproject.io/docs/http/job.html - arguments: - - _id - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "evaluate", method="post").json() - - def plan_job(self, _id, job, diff=False, policy_override=False): - """ Invoke a dry-run of the scheduler for the job. - - https://www.nomadproject.io/docs/http/job.html - - arguments: - - _id - - job, dict - - diff, boolean - - policy_override, boolean - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + return self.request(id_, "evaluate", method="post").json() + + def plan_job(self, id_, job, diff=False, policy_override=False): + """Invoke a dry-run of the scheduler for the job. + + https://www.nomadproject.io/docs/http/job.html + + arguments: + - id_ + - job, dict + - diff, boolean + - policy_override, boolean + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ json_dict = {} json_dict.update(job) - json_dict.setdefault('Diff', diff) - json_dict.setdefault('PolicyOverride', policy_override) - return self.request(_id, "plan", json=json_dict, method="post").json() - - def periodic_job(self, _id): - """ Forces a new instance of the periodic job. A new instance will be - created even if it violates the job's prohibit_overlap settings. - As such, this should be only used to immediately - run a periodic job. - - https://www.nomadproject.io/docs/http/job.html - - arguments: - - _id - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + json_dict.setdefault("Diff", diff) + json_dict.setdefault("PolicyOverride", policy_override) + return self.request(id_, "plan", json=json_dict, method="post").json() + + def periodic_job(self, id_): + """Forces a new instance of the periodic job. A new instance will be + created even if it violates the job's prohibit_overlap settings. + As such, this should be only used to immediately + run a periodic job. + + https://www.nomadproject.io/docs/http/job.html + + arguments: + - id_ + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "periodic", "force", method="post").json() + return self.request(id_, "periodic", "force", method="post").json() - def dispatch_job(self, _id, payload=None, meta=None): - """ Dispatches a new instance of a parameterized job. + def dispatch_job(self, id_, payload=None, meta=None): + """Dispatches a new instance of a parameterized job. - https://www.nomadproject.io/docs/http/job.html + https://www.nomadproject.io/docs/http/job.html - arguments: - - _id - - payload - - meta - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + - payload + - meta + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ dispatch_json = {"Meta": meta, "Payload": payload} - return self.request(_id, "dispatch", json=dispatch_json, method="post").json() - - def revert_job(self, _id, version, enforce_prior_version=None): - """ This endpoint reverts the job to an older version. - - https://www.nomadproject.io/docs/http/job.html - - arguments: - - _id - - version, Specifies the job version to revert to. - optional_arguments: - - enforce_prior_version, Optional value specifying the current job's version. - This is checked and acts as a check-and-set value before reverting to the - specified job. - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + return self.request(id_, "dispatch", json=dispatch_json, method="post").json() + + def revert_job(self, id_, version, enforce_prior_version=None): + """This endpoint reverts the job to an older version. + + https://www.nomadproject.io/docs/http/job.html + + arguments: + - id_ + - version, Specifies the job version to revert to. + optional_arguments: + - enforce_prior_version, Optional value specifying the current job's version. + This is checked and acts as a check-and-set value before reverting to the + specified job. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - revert_json = {"JobID": _id, - "JobVersion": version, - "EnforcePriorVersion": enforce_prior_version} - return self.request(_id, "revert", json=revert_json, method="post").json() - - def stable_job(self, _id, version, stable): - """ This endpoint sets the job's stability. - - https://www.nomadproject.io/docs/http/job.html - - arguments: - - _id - - version, Specifies the job version to revert to. - - stable, Specifies whether the job should be marked as stable or not. - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + revert_json = {"JobID": id_, "JobVersion": version, "EnforcePriorVersion": enforce_prior_version} + return self.request(id_, "revert", json=revert_json, method="post").json() + + def stable_job(self, id_, version, stable): + """This endpoint sets the job's stability. + + https://www.nomadproject.io/docs/http/job.html + + arguments: + - id_ + - version, Specifies the job version to revert to. + - stable, Specifies whether the job should be marked as stable or not. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - revert_json = {"JobID": _id, - "JobVersion": version, - "Stable": stable} - return self.request(_id, "stable", json=revert_json, method="post").json() - - def deregister_job(self, _id, purge=None): - """ Deregisters a job, and stops all allocations part of it. - - https://www.nomadproject.io/docs/http/job.html - - arguments: - - _id - - purge (bool), optionally specifies whether the job should be - stopped and purged immediately (`purge=True`) or deferred to the - Nomad garbage collector (`purge=False`). - - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException - - nomad.api.exceptions.InvalidParameters + revert_json = {"JobID": id_, "JobVersion": version, "Stable": stable} + return self.request(id_, "stable", json=revert_json, method="post").json() + + def deregister_job(self, id_, purge=None): + """Deregisters a job, and stops all allocations part of it. + + https://www.nomadproject.io/docs/http/job.html + + arguments: + - id_ + - purge (bool), optionally specifies whether the job should be + stopped and purged immediately (`purge=True`) or deferred to the + Nomad garbage collector (`purge=False`). + + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.InvalidParameters """ params = None if purge is not None: if not isinstance(purge, bool): - raise nomad.api.exceptions.InvalidParameters("purge is invalid " - f"(expected type {type(bool())} but got {type(purge)})") + raise nomad.api.exceptions.InvalidParameters( + "purge is invalid " f"(expected type {type(bool())} but got {type(purge)})" + ) params = {"purge": purge} - return self.request(_id, params=params, method="delete").json() + return self.request(id_, params=params, method="delete").json() diff --git a/nomad/api/jobs.py b/nomad/api/jobs.py index 3d8793b..c57f31c 100644 --- a/nomad/api/jobs.py +++ b/nomad/api/jobs.py @@ -6,7 +6,6 @@ class Jobs(Requester): - """ The jobs endpoint is used to query the status of existing jobs in Nomad and to register new jobs. @@ -14,6 +13,7 @@ class Jobs(Requester): https://www.nomadproject.io/docs/http/jobs.html """ + ENDPOINT = "jobs" def __init__(self, **kwargs): @@ -73,19 +73,19 @@ def get_jobs( """Lists all the jobs registered with Nomad. https://www.nomadproject.io/docs/http/jobs.html - arguments: - - prefix :(str) optional, specifies a string to filter jobs on based on an prefix. - This is specified as a querystring parameter. - - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. - This is specified as a querystring parameter. - - filter_ :(str) optional, specifies the expression used to filter the result. - Name has a trailing underscore not to conflict with builtin function. - - meta :(bool) optional, if set, jobs returned will include a meta field containing - key-value pairs provided in the job specification's meta block. - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - prefix :(str) optional, specifies a string to filter jobs on based on an prefix. + This is specified as a querystring parameter. + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. + - filter_ :(str) optional, specifies the expression used to filter the result. + Name has a trailing underscore not to conflict with builtin function. + - meta :(bool) optional, if set, jobs returned will include a meta field containing + key-value pairs provided in the job specification's meta block. + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = { "prefix": prefix, @@ -96,27 +96,27 @@ def get_jobs( return self.request(method="get", params=params).json() def register_job(self, job): - """ Register a job with Nomad. + """Register a job with Nomad. - https://www.nomadproject.io/docs/http/jobs.html + https://www.nomadproject.io/docs/http/jobs.html - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request(json=job, method="post").json() def parse(self, hcl, canonicalize=False): - """ Parse a HCL Job file. Returns a dict with the JSON formatted job. - This API endpoint is only supported from Nomad version 0.8.3. + """Parse a HCL Job file. Returns a dict with the JSON formatted job. + This API endpoint is only supported from Nomad version 0.8.3. - https://www.nomadproject.io/api/jobs.html#parse-job + https://www.nomadproject.io/api/jobs.html#parse-job - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request( "parse", json={"JobHCL": hcl, "Canonicalize": canonicalize}, method="post", allow_redirects=True diff --git a/nomad/api/metrics.py b/nomad/api/metrics.py index 2ecccf2..0575eff 100644 --- a/nomad/api/metrics.py +++ b/nomad/api/metrics.py @@ -3,7 +3,6 @@ class Metrics(Requester): - """ The /metrics endpoint returns metrics for the current Nomad process. @@ -13,6 +12,7 @@ class Metrics(Requester): https://www.nomadproject.io/docs/agent/telemetry.html """ + ENDPOINT = "metrics" def __init__(self, **kwargs): @@ -29,13 +29,13 @@ def __getattr__(self, item): raise AttributeError(msg) def get_metrics(self): - """ Get the metrics + """Get the metrics - https://www.nomadproject.io/api/metrics.html + https://www.nomadproject.io/api/metrics.html - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request(method="get").json() diff --git a/nomad/api/namespace.py b/nomad/api/namespace.py index 0860c73..d494449 100644 --- a/nomad/api/namespace.py +++ b/nomad/api/namespace.py @@ -5,13 +5,13 @@ class Namespace(Requester): - """ The job endpoint is used for CRUD on a single namespace. By default, the agent's local region is used. https://www.nomadproject.io/api/namespaces.html """ + ENDPOINT = "namespace" def __init__(self, **kwargs): @@ -49,60 +49,60 @@ def __getitem__(self, item): except nomad.api.exceptions.URLNotFoundNomadException as exc: raise KeyError from exc - def get_namespace(self, _id): - """ Query a single namespace. + def get_namespace(self, id_): + """Query a single namespace. - https://www.nomadproject.io/api/namespaces.html + https://www.nomadproject.io/api/namespaces.html - arguments: - - _id - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, method="get").json() + return self.request(id_, method="get").json() def create_namespace(self, namespace): - """ create namespace + """create namespace - https://www.nomadproject.io/api/namespaces.html + https://www.nomadproject.io/api/namespaces.html - arguments: - - id - - namespace (dict) - returns: requests.Response - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id + - namespace (dict) + returns: requests.Response + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request(json=namespace, method="post") - def update_namespace(self, _id, namespace): - """ Update namespace + def update_namespace(self, id_, namespace): + """Update namespace - https://www.nomadproject.io/api/namespaces.html + https://www.nomadproject.io/api/namespaces.html - arguments: - - _id - - namespace (dict) - returns: requests.Response - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + - namespace (dict) + returns: requests.Response + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, json=namespace, method="post") + return self.request(id_, json=namespace, method="post") - def delete_namespace(self, _id): - """ delete namespace. + def delete_namespace(self, id_): + """delete namespace. - https://www.nomadproject.io/api/namespaces.html + https://www.nomadproject.io/api/namespaces.html - arguments: - - _id - returns: requests.Response - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ + returns: requests.Response + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, method="delete") + return self.request(id_, method="delete") diff --git a/nomad/api/namespaces.py b/nomad/api/namespaces.py index 3fb907f..a87108f 100644 --- a/nomad/api/namespaces.py +++ b/nomad/api/namespaces.py @@ -5,12 +5,12 @@ class Namespaces(Requester): - """ The namespaces from enterprise solution https://www.nomadproject.io/docs/enterprise/namespaces/index.html """ + ENDPOINT = "namespaces" def __init__(self, **kwargs): @@ -59,16 +59,16 @@ def __iter__(self): return iter(namespaces) def get_namespaces(self, prefix=None): - """ Lists all the namespaces registered with Nomad. - - https://www.nomadproject.io/docs/enterprise/namespaces/index.html - arguments: - - prefix :(str) optional, specifies a string to filter namespaces on based on an prefix. - This is specified as a querystring parameter. - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + """Lists all the namespaces registered with Nomad. + + https://www.nomadproject.io/docs/enterprise/namespaces/index.html + arguments: + - prefix :(str) optional, specifies a string to filter namespaces on based on an prefix. + This is specified as a querystring parameter. + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {"prefix": prefix} return self.request(method="get", params=params).json() diff --git a/nomad/api/node.py b/nomad/api/node.py index 46688c2..48c3faf 100644 --- a/nomad/api/node.py +++ b/nomad/api/node.py @@ -5,13 +5,13 @@ class Node(Requester): - """ The node endpoint is used to query the a specific client node. By default, the agent's local region is used. https://www.nomadproject.io/docs/http/node.html """ + ENDPOINT = "node" def __init__(self, **kwargs): @@ -49,120 +49,109 @@ def __getitem__(self, item): except nomad.api.exceptions.URLNotFoundNomadException as exc: raise KeyError from exc - def get_node(self, _id): - """ Query the status of a client node registered with Nomad. + def get_node(self, id_): + """Query the status of a client node registered with Nomad. - https://www.nomadproject.io/docs/http/node.html + https://www.nomadproject.io/docs/http/node.html - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, method="get").json() + return self.request(id_, method="get").json() - def get_allocations(self, _id): - """ Query the allocations belonging to a single node. + def get_allocations(self, id_): + """Query the allocations belonging to a single node. - https://www.nomadproject.io/docs/http/node.html + https://www.nomadproject.io/docs/http/node.html - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "allocations", method="get").json() + return self.request(id_, "allocations", method="get").json() - def evaluate_node(self, _id): - """ Creates a new evaluation for the given node. - This can be used to force run the - scheduling logic if necessary. + def evaluate_node(self, id_): + """Creates a new evaluation for the given node. + This can be used to force run the + scheduling logic if necessary. - https://www.nomadproject.io/docs/http/node.html + https://www.nomadproject.io/docs/http/node.html - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "evaluate", method="post").json() - - def drain_node(self, _id, enable=False): - """ Toggle the drain mode of the node. - When enabled, no further allocations will be - assigned and existing allocations will be migrated. - - https://www.nomadproject.io/docs/http/node.html - - arguments: - - _id (str uuid): node id - - enable (bool): enable node drain or not to enable node drain - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + return self.request(id_, "evaluate", method="post").json() + + def drain_node(self, id_, enable=False): + """Toggle the drain mode of the node. + When enabled, no further allocations will be + assigned and existing allocations will be migrated. + + https://www.nomadproject.io/docs/http/node.html + + arguments: + - id_ (str uuid): node id + - enable (bool): enable node drain or not to enable node drain + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "drain", params={"enable": enable}, method="post").json() + return self.request(id_, "drain", params={"enable": enable}, method="post").json() - def drain_node_with_spec(self, _id, drain_spec, mark_eligible=None): - """ This endpoint toggles the drain mode of the node. When draining is enabled, - no further allocations will be assigned to this node, and existing allocations - will be migrated to new nodes. + def drain_node_with_spec(self, id_, drain_spec, mark_eligible=None): + """This endpoint toggles the drain mode of the node. When draining is enabled, + no further allocations will be assigned to this node, and existing allocations + will be migrated to new nodes. - If an empty dictionary is given as drain_spec this will disable/toggle the drain. + If an empty dictionary is given as drain_spec this will disable/toggle the drain. - https://www.nomadproject.io/docs/http/node.html + https://www.nomadproject.io/docs/http/node.html - arguments: - - _id (str uuid): node id - - drain_spec (dict): https://www.nomadproject.io/api/nodes.html#drainspec - - mark_eligible (bool): https://www.nomadproject.io/api/nodes.html#markeligible - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ (str uuid): node id + - drain_spec (dict): https://www.nomadproject.io/api/nodes.html#drainspec + - mark_eligible (bool): https://www.nomadproject.io/api/nodes.html#markeligible + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ payload = {} if drain_spec and mark_eligible is not None: - payload = { - "NodeID": _id, - "DrainSpec": drain_spec, - "MarkEligible": mark_eligible - } + payload = {"NodeID": id_, "DrainSpec": drain_spec, "MarkEligible": mark_eligible} elif drain_spec and mark_eligible is None: - payload = { - "NodeID": _id, - "DrainSpec": drain_spec - } + payload = {"NodeID": id_, "DrainSpec": drain_spec} elif not drain_spec and mark_eligible is not None: - payload = { - "NodeID": _id, - "DrainSpec": None, - "MarkEligible": mark_eligible - } + payload = {"NodeID": id_, "DrainSpec": None, "MarkEligible": mark_eligible} elif not drain_spec and mark_eligible is None: payload = { - "NodeID": _id, + "NodeID": id_, "DrainSpec": None, } - return self.request(_id, "drain", json=payload, method="post").json() + return self.request(id_, "drain", json=payload, method="post").json() - def eligible_node(self, _id, eligible=None, ineligible=None): - """ Toggle the eligibility of the node. + def eligible_node(self, id_, eligible=None, ineligible=None): + """Toggle the eligibility of the node. - https://www.nomadproject.io/docs/http/node.html + https://www.nomadproject.io/docs/http/node.html - arguments: - - _id (str uuid): node id - - eligible (bool): Set to True to mark node eligible - - ineligible (bool): Set to True to mark node ineligible - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - id_ (str uuid): node id + - eligible (bool): Set to True to mark node eligible + - ineligible (bool): Set to True to mark node ineligible + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ payload = {} @@ -172,24 +161,24 @@ def eligible_node(self, _id, eligible=None, ineligible=None): raise nomad.api.exceptions.InvalidParameters if eligible is not None and eligible: - payload = {"Eligibility": "eligible", "NodeID": _id} + payload = {"Eligibility": "eligible", "NodeID": id_} elif eligible is not None and not eligible: - payload = {"Eligibility": "ineligible", "NodeID": _id} + payload = {"Eligibility": "ineligible", "NodeID": id_} elif ineligible is not None: - payload = {"Eligibility": "ineligible", "NodeID": _id} + payload = {"Eligibility": "ineligible", "NodeID": id_} elif ineligible is not None and not ineligible: - payload = {"Eligibility": "eligible", "NodeID": _id} + payload = {"Eligibility": "eligible", "NodeID": id_} - return self.request(_id, "eligibility", json=payload, method="post").json() + return self.request(id_, "eligibility", json=payload, method="post").json() - def purge_node(self, _id): - """ This endpoint purges a node from the system. Nodes can still join the cluster if they are alive. + def purge_node(self, id_): + """This endpoint purges a node from the system. Nodes can still join the cluster if they are alive. arguments: - - _id (str uuid): node id + - id_ (str uuid): node id returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(_id, "purge", method="post").json() + return self.request(id_, "purge", method="post").json() diff --git a/nomad/api/nodes.py b/nomad/api/nodes.py index 5c4def0..4b02392 100644 --- a/nomad/api/nodes.py +++ b/nomad/api/nodes.py @@ -5,13 +5,13 @@ class Nodes(Requester): - """ The nodes endpoint is used to query the status of client nodes. By default, the agent's local region is used https://www.nomadproject.io/docs/http/nodes.html """ + ENDPOINT = "nodes" def __init__(self, **kwargs): @@ -62,16 +62,16 @@ def __iter__(self): return iter(nodes) def get_nodes(self, prefix=None): - """ Lists all the client nodes registered with Nomad. - - https://www.nomadproject.io/docs/http/nodes.html - arguments: - - prefix :(str) optional, specifies a string to filter nodes on based on an prefix. - This is specified as a querystring parameter. - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + """Lists all the client nodes registered with Nomad. + + https://www.nomadproject.io/docs/http/nodes.html + arguments: + - prefix :(str) optional, specifies a string to filter nodes on based on an prefix. + This is specified as a querystring parameter. + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {"prefix": prefix} return self.request(method="get", params=params).json() diff --git a/nomad/api/operator.py b/nomad/api/operator.py index b301c18..8fa2146 100644 --- a/nomad/api/operator.py +++ b/nomad/api/operator.py @@ -3,7 +3,6 @@ class Operator(Requester): - """ The Operator endpoint provides cluster-level tools for Nomad operators, such as interacting with the Raft subsystem. @@ -27,37 +26,37 @@ def __getattr__(self, item): raise AttributeError(msg) def get_configuration(self, stale=False): - """ Query the status of a client node registered with Nomad. + """Query the status of a client node registered with Nomad. - https://www.nomadproject.io/docs/http/operator.html + https://www.nomadproject.io/docs/http/operator.html - returns: dict - optional arguments: - - stale, (defaults to False), Specifies if the cluster should respond without an active leader. - This is specified as a querystring parameter. - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: dict + optional arguments: + - stale, (defaults to False), Specifies if the cluster should respond without an active leader. + This is specified as a querystring parameter. + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {"stale": stale} return self.request("raft", "configuration", params=params, method="get").json() def delete_peer(self, peer_address, stale=False): - """ Remove the Nomad server with given address from the Raft configuration. - The return code signifies success or failure. - - https://www.nomadproject.io/docs/http/operator.html - - arguments: - - peer_address, The address specifies the server to remove and is given as an IP:port - optional arguments: - - stale, (defaults to False), Specifies if the cluster should respond without an active leader. - This is specified as a querystring parameter. - returns: Boolean - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + """Remove the Nomad server with given address from the Raft configuration. + The return code signifies success or failure. + + https://www.nomadproject.io/docs/http/operator.html + + arguments: + - peer_address, The address specifies the server to remove and is given as an IP:port + optional arguments: + - stale, (defaults to False), Specifies if the cluster should respond without an active leader. + This is specified as a querystring parameter. + returns: Boolean + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {"address": peer_address, "stale": stale} diff --git a/nomad/api/regions.py b/nomad/api/regions.py index 76b5038..bc89989 100644 --- a/nomad/api/regions.py +++ b/nomad/api/regions.py @@ -5,10 +5,10 @@ class Regions(Requester): - """ https://www.nomadproject.io/docs/http/regions.html """ + ENDPOINT = "regions" def __init__(self, **kwargs): @@ -55,13 +55,13 @@ def __iter__(self): return iter(regions) def get_regions(self): - """ Returns the known region names. + """Returns the known region names. - https://www.nomadproject.io/docs/http/regions.html + https://www.nomadproject.io/docs/http/regions.html - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request(method="get").json() diff --git a/nomad/api/scaling.py b/nomad/api/scaling.py index 850c76b..a05fa8a 100644 --- a/nomad/api/scaling.py +++ b/nomad/api/scaling.py @@ -10,6 +10,7 @@ class Scaling(Requester): https://developer.hashicorp.com/nomad/api-docs/scaling-policies """ + ENDPOINT = "scaling" def __init__(self, **kwargs): @@ -26,46 +27,47 @@ def __getattr__(self, item): raise AttributeError(msg) # we want to have common arguments name with Nomad API - def get_scaling_policies(self, job="", type=""): # pylint: disable=redefined-builtin + def get_scaling_policies(self, job="", type_=""): # pylint: disable=redefined-builtin """ This endpoint returns the scaling policies from all jobs. https://developer.hashicorp.com/nomad/api-docs/scaling-policies#list-scaling-policies arguments: - - job - - type + - job + - type_ returns: list of dicts raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ type_of_scaling_policies = [ "horizontal", "vertical_mem", "vertical_cpu", "", - ] # we have only horizontal in OSS + ] # we have only horizontal in OSS - if type not in type_of_scaling_policies: - raise nomad.api.exceptions.InvalidParameters("type is invalid " - f"(expected values are {type_of_scaling_policies} but got {type})") + if type_ not in type_of_scaling_policies: + raise nomad.api.exceptions.InvalidParameters( + "type is invalid " f"(expected values are {type_of_scaling_policies} but got {type_})" + ) - params = {"job": job, "type": type} + params = {"job": job, "type": type_} return self.request("policies", method="get", params=params).json() - def get_scaling_policy(self, _id): + def get_scaling_policy(self, id_): """ This endpoint reads a specific scaling policy. https://developer.hashicorp.com/nomad/api-docs/scaling-policies#read-scaling-policy arguments: - - _id + - id_ returns: list of dicts raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(f"policy/{_id}", method="get").json() + return self.request(f"policy/{id_}", method="get").json() diff --git a/nomad/api/search.py b/nomad/api/search.py index af8c3bf..e373ee8 100644 --- a/nomad/api/search.py +++ b/nomad/api/search.py @@ -13,6 +13,7 @@ class Search(Requester): https://developer.hashicorp.com/nomad/api-docs/search """ + ENDPOINT = "search" def __init__(self, **kwargs): @@ -29,57 +30,59 @@ def __getattr__(self, item): raise AttributeError(msg) def search(self, prefix, context): - """ The endpoint returns matches for a given prefix and context, where a context can be jobs, - allocations, evaluations, nodes, deployments, plugins, namespaces, or volumes. - - https://developer.hashicorp.com/nomad/api-docs/search - arguments: - - prefix:(str) required, specifies the identifier against which matches will be found. - For example, if the given prefix were "a", potential matches might be "abcd", or "aabb". - - context:(str) defines the scope in which a search for a prefix operates. - Contexts can be: "jobs", "evals", "allocs", "nodes", "deployment", "plugins", - "volumes" or "all", where "all" means every context will be searched. - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException - - nomad.api.exceptions.InvalidParameters + """The endpoint returns matches for a given prefix and context, where a context can be jobs, + allocations, evaluations, nodes, deployments, plugins, namespaces, or volumes. + + https://developer.hashicorp.com/nomad/api-docs/search + arguments: + - prefix:(str) required, specifies the identifier against which matches will be found. + For example, if the given prefix were "a", potential matches might be "abcd", or "aabb". + - context:(str) defines the scope in which a search for a prefix operates. + Contexts can be: "jobs", "evals", "allocs", "nodes", "deployment", "plugins", + "volumes" or "all", where "all" means every context will be searched. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.InvalidParameters """ accetaple_contexts = ("jobs", "evals", "allocs", "nodes", "deployment", "plugins", "volumes", "all") if context not in accetaple_contexts: - raise nomad.api.exceptions.InvalidParameters("context is invalid " - f"(expected values are {accetaple_contexts} but got {context})") + raise nomad.api.exceptions.InvalidParameters( + "context is invalid " f"(expected values are {accetaple_contexts} but got {context})" + ) params = {"Prefix": prefix, "Context": context} return self.request(json=params, method="post").json() def fuzzy_search(self, text, context): - """ The /search/fuzzy endpoint returns partial substring matches for a given search term and context, - where a context can be jobs, allocations, nodes, plugins, or namespaces. Additionally, - fuzzy searching can be done across all contexts. - - https://developer.hashicorp.com/nomad/api-docs/search#fuzzy-searching - arguments: - - text:(str) required, specifies the identifier against which matches will be found. - For example, if the given text were "py", potential fuzzy matches might be "python", "spying", - or "happy". - - context:(str) defines the scope in which a search for a prefix operates. Contexts can be: - "jobs", "allocs", "nodes", "plugins", or "all", where "all" means every context will - be searched. - When "all" is selected, additional prefix matches will be included for the "deployments", - "evals", and "volumes" types. When searching in the "jobs" context, results that fuzzy match - "groups", "services", "tasks", "images", "commands", and "classes" are also included in the results. - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + """The /search/fuzzy endpoint returns partial substring matches for a given search term and context, + where a context can be jobs, allocations, nodes, plugins, or namespaces. Additionally, + fuzzy searching can be done across all contexts. + + https://developer.hashicorp.com/nomad/api-docs/search#fuzzy-searching + arguments: + - text:(str) required, specifies the identifier against which matches will be found. + For example, if the given text were "py", potential fuzzy matches might be "python", "spying", + or "happy". + - context:(str) defines the scope in which a search for a prefix operates. Contexts can be: + "jobs", "allocs", "nodes", "plugins", or "all", where "all" means every context will + be searched. + When "all" is selected, additional prefix matches will be included for the "deployments", + "evals", and "volumes" types. When searching in the "jobs" context, results that fuzzy match + "groups", "services", "tasks", "images", "commands", and "classes" are also included in the results. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {"Text": text, "Context": context} accetaple_contexts = ("jobs", "allocs", "nodes", "plugins", "all") if context not in accetaple_contexts: - raise nomad.api.exceptions.InvalidParameters("context is invalid " - f"(expected values are {accetaple_contexts} but got {context})") + raise nomad.api.exceptions.InvalidParameters( + "context is invalid " f"(expected values are {accetaple_contexts} but got {context})" + ) return self.request("fuzzy", json=params, method="post").json() diff --git a/nomad/api/sentinel.py b/nomad/api/sentinel.py index ba0d49b..eaf7c20 100644 --- a/nomad/api/sentinel.py +++ b/nomad/api/sentinel.py @@ -3,7 +3,6 @@ class Sentinel(Requester): - """ The endpoint manage sentinel policies (Enterprise Only) @@ -26,73 +25,73 @@ def __getattr__(self, item): raise AttributeError(msg) def get_policies(self): - """ Get a list of policies. + """Get a list of policies. - https://www.nomadproject.io/api/sentinel-policies.html + https://www.nomadproject.io/api/sentinel-policies.html - returns: list + returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("policies", method="get").json() - def create_policy(self, _id, policy): - """ Create policy. + def create_policy(self, id_, policy): + """Create policy. - https://www.nomadproject.io/api/sentinel-policies.html + https://www.nomadproject.io/api/sentinel-policies.html - arguments: - - policy - returns: requests.Response + arguments: + - policy + returns: requests.Response - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", _id, json=policy, method="post") + return self.request("policy", id_, json=policy, method="post") - def get_policy(self, _id): - """ Get a spacific policy. + def get_policy(self, id_): + """Get a spacific policy. - https://www.nomadproject.io/api/sentinel-policies.html + https://www.nomadproject.io/api/sentinel-policies.html - returns: dict + returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", _id, method="get").json() + return self.request("policy", id_, method="get").json() - def update_policy(self, _id, policy): - """ Create policy. + def update_policy(self, id_, policy): + """Create policy. - https://www.nomadproject.io/api/sentinel-policies.html + https://www.nomadproject.io/api/sentinel-policies.html - arguments: - - name - - policy - returns: requests.Response + arguments: + - name + - policy + returns: requests.Response - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", _id, json=policy, method="post") + return self.request("policy", id_, json=policy, method="post") - def delete_policy(self, _id): - """ Delete specific policy. + def delete_policy(self, id_): + """Delete specific policy. - https://www.nomadproject.io/api/sentinel-policies.html + https://www.nomadproject.io/api/sentinel-policies.html - arguments: - - _id - returns: Boolean + arguments: + - id_ + returns: Boolean - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request("policy", _id, method="delete").ok + return self.request("policy", id_, method="delete").ok diff --git a/nomad/api/status.py b/nomad/api/status.py index 3e33501..85d8a42 100644 --- a/nomad/api/status.py +++ b/nomad/api/status.py @@ -4,8 +4,7 @@ from nomad.api.base import Requester -class Status(): - +class Status: """ By default, the agent's local region is used @@ -48,14 +47,14 @@ def __len__(self): return len(leader) def get_leader(self): - """ Returns the address of the current leader in the region. + """Returns the address of the current leader in the region. - https://www.nomadproject.io/docs/http/status.html + https://www.nomadproject.io/docs/http/status.html - returns: string - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: string + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request(method="get").json() @@ -96,13 +95,13 @@ def __iter__(self): return iter(peers) def get_peers(self): - """ Returns the set of raft peers in the region. + """Returns the set of raft peers in the region. - https://www.nomadproject.io/docs/http/status.html + https://www.nomadproject.io/docs/http/status.html - returns: list - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: list + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request(method="get").json() diff --git a/nomad/api/system.py b/nomad/api/system.py index 8437821..4255ab4 100644 --- a/nomad/api/system.py +++ b/nomad/api/system.py @@ -3,7 +3,6 @@ class System(Requester): - """ The system endpoint is used to for system maintenance and should not be necessary for most users. @@ -28,25 +27,25 @@ def __getattr__(self, item): raise AttributeError(msg) def initiate_garbage_collection(self): - """ Initiate garbage collection of jobs, evals, allocations and nodes. + """Initiate garbage collection of jobs, evals, allocations and nodes. - https://www.nomadproject.io/docs/http/system.html + https://www.nomadproject.io/docs/http/system.html - returns: Boolean - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: Boolean + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("gc", method="put").ok def reconcile_summaries(self): - """ This endpoint reconciles the summaries of all registered jobs. + """This endpoint reconciles the summaries of all registered jobs. - https://www.nomadproject.io/docs/http/system.html + https://www.nomadproject.io/docs/http/system.html - returns: Boolean - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + returns: Boolean + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("reconcile", "summaries", method="put").ok diff --git a/nomad/api/validate.py b/nomad/api/validate.py index f0ba03a..ea17fda 100644 --- a/nomad/api/validate.py +++ b/nomad/api/validate.py @@ -28,17 +28,17 @@ def __getattr__(self, item): raise AttributeError(msg) def validate_job(self, nomad_job_dict): - """ This endpoint validates a Nomad job file. The local Nomad agent forwards the request to a server. + """This endpoint validates a Nomad job file. The local Nomad agent forwards the request to a server. In the event a server can't be reached the agent verifies the job file locally but skips validating driver configurations. - https://www.nomadproject.io/api/validate.html + https://www.nomadproject.io/api/validate.html - arguments: - - nomad_job_json, any valid nomad job IN dict FORMAT - returns: dict - raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + arguments: + - nomad_job_json, any valid nomad job IN dict FORMAT + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ return self.request("job", json=nomad_job_dict, method="post") diff --git a/nomad/api/variable.py b/nomad/api/variable.py index 2ff23a0..7df2446 100644 --- a/nomad/api/variable.py +++ b/nomad/api/variable.py @@ -5,7 +5,6 @@ class Variable(Requester): - """ The /var endpoints are used to read or create variables. https://developer.hashicorp.com/nomad/api-docs/variables @@ -45,11 +44,11 @@ def get_variable(self, var_path, namespace=None): https://developer.hashicorp.com/nomad/api-docs/variables#read-variable arguments: - - var_path :(str), path to variable + - var_path :(str), path to variable returns: dict raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {} if namespace: @@ -63,18 +62,18 @@ def create_variable(self, var_path, payload, namespace=None, cas=None): https://developer.hashicorp.com/nomad/api-docs/variables#create-variable arguments: - - var_path :(str), path to variable - - payload :(dict), variable object. Example: + - var_path :(str), path to variable + - payload :(dict), variable object. Example: https://developer.hashicorp.com/nomad/api-docs/variables#sample-payload - - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. - This is specified as a querystring parameter. - - cas :(int) optional, If set, the variable will only be deleted if the cas value matches the - current variables ModifyIndex. + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. + - cas :(int) optional, If set, the variable will only be deleted if the cas value matches the + current variables ModifyIndex. returns: dict raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException - - nomad.api.exceptions.VariableConflict + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.VariableConflict """ params = {} if cas is not None: @@ -84,23 +83,22 @@ def create_variable(self, var_path, payload, namespace=None, cas=None): return self.request(var_path, params=params, json=payload, method="put").json() - def delete_variable(self, var_path, namespace=None, cas=None): """ This endpoint reads a specific variable by path. This API returns the decrypted variable body. https://developer.hashicorp.com/nomad/api-docs/variables#delete-variable arguments: - - var_path :(str), path to variable - - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. - This is specified as a querystring parameter. - - cas :(int) optional, If set, the variable will only be deleted if the cas value matches the - current variables ModifyIndex. + - var_path :(str), path to variable + - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. + This is specified as a querystring parameter. + - cas :(int) optional, If set, the variable will only be deleted if the cas value matches the + current variables ModifyIndex. returns: dict raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException - - nomad.api.exceptions.VariableConflict + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.VariableConflict """ params = {} if cas is not None: diff --git a/nomad/api/variables.py b/nomad/api/variables.py index c9d8928..d75f482 100644 --- a/nomad/api/variables.py +++ b/nomad/api/variables.py @@ -3,7 +3,6 @@ class Variables(Requester): - """ The /vars endpoints are used to query for and interact with variables. https://developer.hashicorp.com/nomad/api-docs/variables @@ -50,14 +49,14 @@ def get_variables(self, prefix="", namespace=None): https://developer.hashicorp.com/nomad/api-docs/variables optional_arguments: - - prefix, (default "") Specifies a string to filter variables on based on an index prefix. - This is specified as a query string parameter. - - namespace :(str) optional, Specifies the target namespace. - Specifying * will return all variables across all the authorized namespaces. + - prefix, (default "") Specifies a string to filter variables on based on an index prefix. + This is specified as a query string parameter. + - namespace :(str) optional, Specifies the target namespace. + Specifying * will return all variables across all the authorized namespaces. returns: list of dicts raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ params = {"prefix": prefix} if namespace: diff --git a/requirements-lint.txt b/requirements-lint.txt new file mode 100644 index 0000000..ad9fd7e --- /dev/null +++ b/requirements-lint.txt @@ -0,0 +1,2 @@ +pylint==2.15.10 +black==22.12.0 \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index a871f3b..8630b01 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,12 +2,16 @@ import pytest import tests.common as common + @pytest.fixture def nomad_setup(): n = nomad.Nomad(host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN) return n + @pytest.fixture def nomad_setup_with_namespace(): - n = nomad.Nomad(host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN, namespace=common.NOMAD_NAMESPACE) + n = nomad.Nomad( + host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN, namespace=common.NOMAD_NAMESPACE + ) return n diff --git a/tests/test_acl.py b/tests/test_acl.py index d7936fc..863959d 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -7,7 +7,10 @@ # integration tests requires nomad Vagrant VM or Binary running # IMPORTANT: without token activated -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) @pytest.mark.run(order=0) def test_create_bootstrap(nomad_setup): bootstrap = nomad_setup.acl.generate_bootstrap() @@ -15,45 +18,59 @@ def test_create_bootstrap(nomad_setup): common.NOMAD_TOKEN = bootstrap["SecretID"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) @pytest.mark.run(order=1) def test_list_tokens(nomad_setup): assert "Bootstrap Token" in nomad_setup.acl.get_tokens()[0]["Name"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) @pytest.mark.run(order=2) def test_create_token(nomad_setup): - token_example='{"Name": "Readonly token","Type": "client","Policies": ["readonly"],"Global": false}' + token_example = '{"Name": "Readonly token","Type": "client","Policies": ["readonly"],"Global": false}' json_token = json.loads(token_example) created_token = nomad_setup.acl.create_token(json_token) assert "Readonly token" in created_token["Name"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) @pytest.mark.run(order=3) def test_list_all_tokens(nomad_setup): tokens = nomad_setup.acl.get_tokens() assert isinstance(tokens, list) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) @pytest.mark.run(order=4) def test_update_token(nomad_setup): - token_example='{"Name": "CreatedForUpdate","Type": "client","Policies": ["readonly"],"Global": false}' + token_example = '{"Name": "CreatedForUpdate","Type": "client","Policies": ["readonly"],"Global": false}' json_token = json.loads(token_example) created_token = nomad_setup.acl.create_token(json_token) - token_update ='{"AccessorID":"' + created_token["AccessorID"] + '","Name": "Updated" ,"Type": "client","Policies": ["readonly"]}' + token_update = ( + '{"AccessorID":"' + + created_token["AccessorID"] + + '","Name": "Updated" ,"Type": "client","Policies": ["readonly"]}' + ) json_token_update = json.loads(token_update) - update_token = nomad_setup.acl.update_token(_id=created_token["AccessorID"],token=json_token_update) + update_token = nomad_setup.acl.update_token(id_=created_token["AccessorID"], token=json_token_update) assert "Updated" in update_token["Name"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) @pytest.mark.run(order=5) def test_get_token(nomad_setup): - token_example='{"Name": "GetToken","Type": "client","Policies": ["readonly"],"Global": false}' + token_example = '{"Name": "GetToken","Type": "client","Policies": ["readonly"],"Global": false}' json_token = json.loads(token_example) created_token = nomad_setup.acl.create_token(json_token) @@ -61,10 +78,12 @@ def test_get_token(nomad_setup): assert "GetToken" in created_token["Name"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) @pytest.mark.run(order=6) def test_delete_token(nomad_setup): - token_example='{"Name": "DeleteToken","Type": "client","Policies": ["readonly"],"Global": false}' + token_example = '{"Name": "DeleteToken","Type": "client","Policies": ["readonly"],"Global": false}' json_token = json.loads(token_example) created_token = nomad_setup.acl.create_token(json_token) assert "DeleteToken" in created_token["Name"] @@ -73,41 +92,53 @@ def test_delete_token(nomad_setup): assert False == any("DeleteToken" in x for x in nomad_setup.acl.get_tokens()) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) def test_get_self_token(nomad_setup): current_token = nomad_setup.acl.get_self_token() assert nomad_setup.get_token() in current_token["SecretID"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) def test_get_policies(nomad_setup): policies = nomad_setup.acl.get_policies() assert isinstance(policies, list) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) def test_create_policy(nomad_setup): policy_example = '{ "Name": "my-policy", "Description": "This is a great policy", "Rules": "" }' json_policy = json.loads(policy_example) - nomad_setup.acl.create_policy(_id="my-policy", policy=json_policy) + nomad_setup.acl.create_policy(id_="my-policy", policy=json_policy) assert False == any("my-policy" in x for x in nomad_setup.acl.get_policies()) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) def test_get_policy(nomad_setup): policy = nomad_setup.acl.get_policy("my-policy") assert "This is a great policy" in policy["Description"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) def test_update_policy(nomad_setup): policy_update = '{"Name": "my-policy","Description": "Updated","Rules": ""}' json_policy_update = json.loads(policy_update) - nomad_setup.acl.update_policy(_id="my-policy", policy=json_policy_update) + nomad_setup.acl.update_policy(id_="my-policy", policy=json_policy_update) assert False == any("Updated" in x for x in nomad_setup.acl.get_policies()) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) def test_delete_policy(nomad_setup): - nomad_setup.acl.delete_policy(_id="my-policy") + nomad_setup.acl.delete_policy(id_="my-policy") assert False == any("my-policy" in x for x in nomad_setup.acl.get_policies()) diff --git a/tests/test_allocation.py b/tests/test_allocation.py index 0d02137..1aca38c 100644 --- a/tests/test_allocation.py +++ b/tests/test_allocation.py @@ -20,7 +20,10 @@ def test_get_allocation(nomad_setup): assert isinstance(nomad_setup.allocation.get_allocation(id), dict) == True -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 9, 2), reason="Nomad alloc stop not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 9, 2), + reason="Nomad alloc stop not supported", +) def test_stop_allocation(nomad_setup): id = nomad_setup.job.get_allocations("example")[0]["ID"] assert isinstance(nomad_setup.allocation.stop_allocation(id), dict) == True @@ -69,8 +72,21 @@ def test_dunder_getattr(nomad_setup): def test_get_allocation_with_namespace(nomad_setup_with_namespace): responses.add( responses.GET, - "http://{ip}:{port}/v1/allocation/a8198d79-cfdb-6593-a999-1e9adabcba2e?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + "http://{ip}:{port}/v1/allocation/a8198d79-cfdb-6593-a999-1e9adabcba2e?namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE + ), status=200, - json={"ID": "a8198d79-cfdb-6593-a999-1e9adabcba2e","EvalID": "5456bd7a-9fc0-c0dd-6131-cbee77f57577","Namespace": common.NOMAD_NAMESPACE, "Name": "example.cache[0]","NodeID": "fb2170a8-257d-3c64-b14d-bc06cc94e34c","PreviousAllocation": "516d2753-0513-cfc7-57ac-2d6fac18b9dc","NextAllocation": "cd13d9b9-4f97-7184-c88b-7b451981616b"} + json={ + "ID": "a8198d79-cfdb-6593-a999-1e9adabcba2e", + "EvalID": "5456bd7a-9fc0-c0dd-6131-cbee77f57577", + "Namespace": common.NOMAD_NAMESPACE, + "Name": "example.cache[0]", + "NodeID": "fb2170a8-257d-3c64-b14d-bc06cc94e34c", + "PreviousAllocation": "516d2753-0513-cfc7-57ac-2d6fac18b9dc", + "NextAllocation": "cd13d9b9-4f97-7184-c88b-7b451981616b", + }, + ) + assert ( + common.NOMAD_NAMESPACE + in nomad_setup_with_namespace.allocation.get_allocation("a8198d79-cfdb-6593-a999-1e9adabcba2e")["Namespace"] ) - assert common.NOMAD_NAMESPACE in nomad_setup_with_namespace.allocation.get_allocation("a8198d79-cfdb-6593-a999-1e9adabcba2e")["Namespace"] diff --git a/tests/test_allocations.py b/tests/test_allocations.py index b35c445..b618786 100644 --- a/tests/test_allocations.py +++ b/tests/test_allocations.py @@ -38,7 +38,7 @@ def test_dunder_getattr(nomad_setup): def test_dunder_iter(nomad_setup): - assert hasattr(nomad_setup.allocations, '__iter__') + assert hasattr(nomad_setup.allocations, "__iter__") for j in nomad_setup.allocations: pass @@ -54,20 +54,47 @@ def test_dunder_len(nomad_setup): def test_get_allocations_with_namespace(nomad_setup_with_namespace): responses.add( responses.GET, - "http://{ip}:{port}/v1/allocations?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + "http://{ip}:{port}/v1/allocations?namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE + ), status=200, - json=[{"ID": "a8198d79-cfdb-6593-a999-1e9adabcba2e","EvalID": "5456bd7a-9fc0-c0dd-6131-cbee77f57577","Namespace": common.NOMAD_NAMESPACE, "Name": "example.cache[0]","NodeID": "fb2170a8-257d-3c64-b14d-bc06cc94e34c","PreviousAllocation": "516d2753-0513-cfc7-57ac-2d6fac18b9dc","NextAllocation": "cd13d9b9-4f97-7184-c88b-7b451981616b"}] + json=[ + { + "ID": "a8198d79-cfdb-6593-a999-1e9adabcba2e", + "EvalID": "5456bd7a-9fc0-c0dd-6131-cbee77f57577", + "Namespace": common.NOMAD_NAMESPACE, + "Name": "example.cache[0]", + "NodeID": "fb2170a8-257d-3c64-b14d-bc06cc94e34c", + "PreviousAllocation": "516d2753-0513-cfc7-57ac-2d6fac18b9dc", + "NextAllocation": "cd13d9b9-4f97-7184-c88b-7b451981616b", + } + ], ) assert common.NOMAD_NAMESPACE in nomad_setup_with_namespace.allocations.get_allocations()[0]["Namespace"] + @responses.activate def test_get_allocations_with_namespace_override_namespace_declared_on_create(nomad_setup_with_namespace): override_namespace_name = "namespace=override-namespace" responses.add( responses.GET, - "http://{ip}:{port}/v1/allocations?prefix=a8198d79-cfdb-6593-a999-1e9adabcba2e&namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=override_namespace_name), + "http://{ip}:{port}/v1/allocations?prefix=a8198d79-cfdb-6593-a999-1e9adabcba2e&namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace=override_namespace_name + ), status=200, - json=[{"ID": "a8198d79-cfdb-6593-a999-1e9adabcba2e","EvalID": "5456bd7a-9fc0-c0dd-6131-cbee77f57577","Namespace": override_namespace_name, "Name": "example.cache[0]","NodeID": "fb2170a8-257d-3c64-b14d-bc06cc94e34c","PreviousAllocation": "516d2753-0513-cfc7-57ac-2d6fac18b9dc","NextAllocation": "cd13d9b9-4f97-7184-c88b-7b451981616b"}] + json=[ + { + "ID": "a8198d79-cfdb-6593-a999-1e9adabcba2e", + "EvalID": "5456bd7a-9fc0-c0dd-6131-cbee77f57577", + "Namespace": override_namespace_name, + "Name": "example.cache[0]", + "NodeID": "fb2170a8-257d-3c64-b14d-bc06cc94e34c", + "PreviousAllocation": "516d2753-0513-cfc7-57ac-2d6fac18b9dc", + "NextAllocation": "cd13d9b9-4f97-7184-c88b-7b451981616b", + } + ], ) - nomad_setup_with_namespace.allocations.get_allocations("a8198d79-cfdb-6593-a999-1e9adabcba2e", namespace=override_namespace_name) \ No newline at end of file + nomad_setup_with_namespace.allocations.get_allocations( + "a8198d79-cfdb-6593-a999-1e9adabcba2e", namespace=override_namespace_name + ) diff --git a/tests/test_base.py b/tests/test_base.py index 08b5711..59b9be3 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -18,7 +18,14 @@ def test_base_region_qs(): def test_base_region_and_namespace_qs(): - n = nomad.Nomad(host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN, region="random", namespace="test") + n = nomad.Nomad( + host=common.IP, + port=common.NOMAD_PORT, + verify=False, + token=common.NOMAD_TOKEN, + region="random", + namespace="test", + ) qs = n.jobs._query_string_builder("v1/jobs") assert "region" in qs @@ -29,7 +36,14 @@ def test_base_region_and_namespace_qs(): def test_base_region_and_namespace_qs_namespace_override(): - n = nomad.Nomad(host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN, region="random", namespace="test") + n = nomad.Nomad( + host=common.IP, + port=common.NOMAD_PORT, + verify=False, + token=common.NOMAD_TOKEN, + region="random", + namespace="test", + ) qs = n.jobs._query_string_builder("v1/jobs", {"namespace": "new-namespace"}) assert "namespace" not in qs @@ -38,7 +52,14 @@ def test_base_region_and_namespace_qs_namespace_override(): def test_base_region_and_namespace_qs_region_override(): - n = nomad.Nomad(host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN, region="random", namespace="test") + n = nomad.Nomad( + host=common.IP, + port=common.NOMAD_PORT, + verify=False, + token=common.NOMAD_TOKEN, + region="random", + namespace="test", + ) qs = n.jobs._query_string_builder("v1/jobs", {"region": "new-region"}) assert "region" not in qs @@ -47,7 +68,14 @@ def test_base_region_and_namespace_qs_region_override(): def test_base_region_and_namespace_qs_overrides_via_params(): - n = nomad.Nomad(host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN, region="random", namespace="test") + n = nomad.Nomad( + host=common.IP, + port=common.NOMAD_PORT, + verify=False, + token=common.NOMAD_TOKEN, + region="random", + namespace="test", + ) qs = n.jobs._query_string_builder("v1/jobs", {"namespace": "new-namespace", "region": "new-region"}) assert qs == {} @@ -55,22 +83,19 @@ def test_base_region_and_namespace_qs_overrides_via_params(): # integration tests requires nomad Vagrant VM or Binary running def test_base_get_connection_error(): - n = nomad.Nomad( - host="162.16.10.102", port=common.NOMAD_PORT, timeout=0.001, verify=False) + n = nomad.Nomad(host="162.16.10.102", port=common.NOMAD_PORT, timeout=0.001, verify=False) with pytest.raises(nomad.api.exceptions.BaseNomadException): j = n.evaluations["nope"] def test_base_put_connection_error(): - n = nomad.Nomad( - host="162.16.10.102", port=common.NOMAD_PORT, timeout=0.001, verify=False) + n = nomad.Nomad(host="162.16.10.102", port=common.NOMAD_PORT, timeout=0.001, verify=False) with pytest.raises(nomad.api.exceptions.BaseNomadException): j = n.system.initiate_garbage_collection() def test_base_delete_connection_error(): - n = nomad.Nomad( - host="162.16.10.102", port=common.NOMAD_PORT, timeout=0.001, verify=False) + n = nomad.Nomad(host="162.16.10.102", port=common.NOMAD_PORT, timeout=0.001, verify=False) with pytest.raises(nomad.api.exceptions.BaseNomadException): j = n.job.deregister_job("example") @@ -80,12 +105,7 @@ def test_base_raise_exception_not_requests_response_object(mock_requests): mock_requests().delete.side_effect = [requests.RequestException()] with pytest.raises(nomad.api.exceptions.BaseNomadException) as excinfo: - n = nomad.Nomad( - host="162.16.10.102", - port=common.NOMAD_PORT, - timeout=0.001, - verify=False - ) + n = nomad.Nomad(host="162.16.10.102", port=common.NOMAD_PORT, timeout=0.001, verify=False) _ = n.job.deregister_job("example") @@ -109,23 +129,21 @@ def test_base_raise_exception_is_requests_response_object(nomad_setup): assert "raised with" in str(excinfo.value) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 0), reason="Nomad dispatch not supported" +) def test_base_get_connnection_not_authorized(): - n = nomad.Nomad( - host=common.IP, port=common.NOMAD_PORT, token='aed2fc63-c155-40d5-b58a-18deed4b73e5', verify=False) + n = nomad.Nomad(host=common.IP, port=common.NOMAD_PORT, token="aed2fc63-c155-40d5-b58a-18deed4b73e5", verify=False) with pytest.raises(nomad.api.exceptions.URLNotAuthorizedNomadException): j = n.job.get_job("example") @responses.activate def test_base_use_address_instead_on_host_port(): - responses.add( - responses.GET, - 'https://nomad.service.consul:4646/v1/jobs', - status=200, - json=[] - ) + responses.add(responses.GET, "https://nomad.service.consul:4646/v1/jobs", status=200, json=[]) nomad_address = "https://nomad.service.consul:4646" - n = nomad.Nomad(address=nomad_address, host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN) + n = nomad.Nomad( + address=nomad_address, host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN + ) n.jobs.get_jobs() diff --git a/tests/test_client.py b/tests/test_client.py index 19c24a1..84bd076 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -26,41 +26,53 @@ def test_register_job(nomad_setup): max_iterations -= 1 -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 6), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 6), reason="Not supported in version" +) def test_ls_list_files(nomad_setup): a = nomad_setup.allocations.get_allocations()[0]["ID"] f = nomad_setup.client.ls.list_files(a) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 6), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 6), reason="Not supported in version" +) def test_stat_stat_file(nomad_setup): a = nomad_setup.allocations.get_allocations()[0]["ID"] f = nomad_setup.client.stat.stat_file(a) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version" +) def test_streamfile_fail(nomad_setup): with pytest.raises(nomad.api.exceptions.BadRequestNomadException): a = nomad_setup.allocations.get_allocations()[0]["ID"] - _ = nomad_setup.client.stream_file.stream(a, 1, "start", "/redis/executor") #invalid file name + _ = nomad_setup.client.stream_file.stream(a, 1, "start", "/redis/executor") # invalid file name -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 6), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 6), reason="Not supported in version" +) def test_read_stats(nomad_setup): f = nomad_setup.client.stats.read_stats() -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 6), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 6), reason="Not supported in version" +) def test_read_allocation_stats(nomad_setup): a = nomad_setup.allocations.get_allocations()[0]["ID"] f = nomad_setup.client.allocation.read_allocation_stats(a) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version" +) def test_gc_all_allocations(nomad_setup): node_id = nomad_setup.nodes.get_nodes()[0]["ID"] diff --git a/tests/test_deployment.py b/tests/test_deployment.py index d27bab8..8e0d1ca 100644 --- a/tests/test_deployment.py +++ b/tests/test_deployment.py @@ -16,14 +16,18 @@ def test_register_job(nomad_setup): # integration tests requires nomad Vagrant VM or Binary running -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_get_deployment(nomad_setup): deploymentID = nomad_setup.deployments.get_deployments()[0]["ID"] assert isinstance(nomad_setup.deployment.get_deployment(deploymentID), dict) assert deploymentID == nomad_setup.deployment.get_deployment(deploymentID)["ID"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_get_deployment_allocations(nomad_setup): deploymentID = nomad_setup.deployments.get_deployments()[0]["ID"] assert isinstance(nomad_setup.deployment.get_deployment_allocations(deploymentID), list) @@ -31,7 +35,9 @@ def test_get_deployment_allocations(nomad_setup): assert "example" == nomad_setup.deployment.get_deployment_allocations(deploymentID)[0]["JobID"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_fail_deployment(nomad_setup): deploymentID = nomad_setup.deployments.get_deployments()[0]["ID"] try: @@ -40,7 +46,9 @@ def test_fail_deployment(nomad_setup): assert err.nomad_resp.text == "can't fail terminal deployment" -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_pause_deployment(nomad_setup): deploymentID = nomad_setup.deployments.get_deployments()[0]["ID"] try: @@ -49,7 +57,9 @@ def test_pause_deployment(nomad_setup): assert err.nomad_resp.text == "can't resume terminal deployment" -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_promote_all_deployment(nomad_setup): deploymentID = nomad_setup.deployments.get_deployments()[0]["ID"] try: @@ -58,7 +68,9 @@ def test_promote_all_deployment(nomad_setup): assert err.nomad_resp.text == "can't promote terminal deployment" -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_promote_all_deployment(nomad_setup): deploymentID = nomad_setup.deployments.get_deployments()[0]["ID"] try: @@ -67,7 +79,9 @@ def test_promote_all_deployment(nomad_setup): assert err.nomad_resp.text == "can't promote terminal deployment" -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_deployment_allocation_health(nomad_setup): deploymentID = nomad_setup.deployments.get_deployments()[0]["ID"] allocationID = nomad_setup.deployment.get_deployment(deploymentID)["ID"] @@ -111,6 +125,7 @@ def test_dunder_getattr(nomad_setup): with pytest.raises(AttributeError): _ = nomad_setup.deployment.does_not_exist + @responses.activate # # fix No data when you are using namespaces #82 @@ -118,20 +133,48 @@ def test_dunder_getattr(nomad_setup): def test_get_deployment_with_namespace(nomad_setup_with_namespace): responses.add( responses.GET, - "http://{ip}:{port}/v1/deployment/a8198d79-cfdb-6593-a999-1e9adabcba2e?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + "http://{ip}:{port}/v1/deployment/a8198d79-cfdb-6593-a999-1e9adabcba2e?namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE + ), status=200, - json={"ID": "70638f62-5c19-193e-30d6-f9d6e689ab8e","JobID": "example", "JobVersion": 1, "JobModifyIndex": 17, "JobSpecModifyIndex": 17, "JobCreateIndex": 7,"Namespace": common.NOMAD_NAMESPACE, "Name": "example.cache[0]"} + json={ + "ID": "70638f62-5c19-193e-30d6-f9d6e689ab8e", + "JobID": "example", + "JobVersion": 1, + "JobModifyIndex": 17, + "JobSpecModifyIndex": 17, + "JobCreateIndex": 7, + "Namespace": common.NOMAD_NAMESPACE, + "Name": "example.cache[0]", + }, + ) + assert ( + common.NOMAD_NAMESPACE + in nomad_setup_with_namespace.deployment.get_deployment("a8198d79-cfdb-6593-a999-1e9adabcba2e")["Namespace"] ) - assert common.NOMAD_NAMESPACE in nomad_setup_with_namespace.deployment.get_deployment("a8198d79-cfdb-6593-a999-1e9adabcba2e")["Namespace"] + @responses.activate def test_get_deployments_with_namespace_override_namespace_declared_on_create(nomad_setup_with_namespace): override_namespace_name = "override-namespace" responses.add( responses.GET, - "http://{ip}:{port}/v1/deployments?prefix=a8198d79-cfdb-6593-a999-1e9adabcba2e&namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=override_namespace_name), + "http://{ip}:{port}/v1/deployments?prefix=a8198d79-cfdb-6593-a999-1e9adabcba2e&namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace=override_namespace_name + ), status=200, - json={"ID": "70638f62-5c19-193e-30d6-f9d6e689ab8e","JobID": "example", "JobVersion": 1, "JobModifyIndex": 17, "JobSpecModifyIndex": 17, "JobCreateIndex": 7,"Namespace": override_namespace_name, "Name": "example.cache[0]"} + json={ + "ID": "70638f62-5c19-193e-30d6-f9d6e689ab8e", + "JobID": "example", + "JobVersion": 1, + "JobModifyIndex": 17, + "JobSpecModifyIndex": 17, + "JobCreateIndex": 7, + "Namespace": override_namespace_name, + "Name": "example.cache[0]", + }, ) - nomad_setup_with_namespace.deployments.get_deployments("a8198d79-cfdb-6593-a999-1e9adabcba2e", namespace=override_namespace_name) \ No newline at end of file + nomad_setup_with_namespace.deployments.get_deployments( + "a8198d79-cfdb-6593-a999-1e9adabcba2e", namespace=override_namespace_name + ) diff --git a/tests/test_deployments.py b/tests/test_deployments.py index b361d71..258aedd 100644 --- a/tests/test_deployments.py +++ b/tests/test_deployments.py @@ -14,48 +14,64 @@ def test_register_job(nomad_setup): # integration tests requires nomad Vagrant VM or Binary running -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_get_evaluation(nomad_setup): assert "example" == nomad_setup.deployments.get_deployments()[0]["JobID"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_get_deployments_prefix(nomad_setup): deployments = nomad_setup.deployments.get_deployments() prefix = deployments[0]["ID"][:4] nomad_setup.deployments.get_deployments(prefix=prefix) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_dunder_getitem_exist(nomad_setup): jobID = nomad_setup.deployments.get_deployments()[0]["ID"] d = nomad_setup.deployment[jobID] assert isinstance(d, dict) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_dunder_getitem_not_exist(nomad_setup): with pytest.raises(KeyError): _ = nomad_setup.deployments["nope"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_dunder_contain_exists(nomad_setup): jobID = nomad_setup.deployments.get_deployments()[0]["ID"] assert jobID in nomad_setup.deployments -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_dunder_contain_not_exist(nomad_setup): assert "nope" not in nomad_setup.deployments -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_dunder_len(nomad_setup): assert len(nomad_setup.deployments) == 1 -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_dunder_iter(nomad_setup): for d in nomad_setup.deployments: pass @@ -74,6 +90,7 @@ def test_dunder_getattr(nomad_setup): with pytest.raises(AttributeError): _ = nomad_setup.deployments.does_not_exist + @responses.activate # # fix No data when you are using namespaces #82 @@ -81,8 +98,21 @@ def test_dunder_getattr(nomad_setup): def test_get_deployments_with_namespace(nomad_setup_with_namespace): responses.add( responses.GET, - "http://{ip}:{port}/v1/deployments?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + "http://{ip}:{port}/v1/deployments?namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE + ), status=200, - json=[{"ID": "70638f62-5c19-193e-30d6-f9d6e689ab8e","JobID": "example", "JobVersion": 1, "JobModifyIndex": 17, "JobSpecModifyIndex": 17, "JobCreateIndex": 7,"Namespace": common.NOMAD_NAMESPACE, "Name": "example.cache[0]"}] + json=[ + { + "ID": "70638f62-5c19-193e-30d6-f9d6e689ab8e", + "JobID": "example", + "JobVersion": 1, + "JobModifyIndex": 17, + "JobSpecModifyIndex": 17, + "JobCreateIndex": 7, + "Namespace": common.NOMAD_NAMESPACE, + "Name": "example.cache[0]", + } + ], ) assert common.NOMAD_NAMESPACE in nomad_setup_with_namespace.deployments.get_deployments()[0]["Namespace"] diff --git a/tests/test_evaluation.py b/tests/test_evaluation.py index 8a80e43..ec16dac 100644 --- a/tests/test_evaluation.py +++ b/tests/test_evaluation.py @@ -14,8 +14,7 @@ def test_register_job(nomad_setup): # integration tests requires nomad Vagrant VM or Binary running def test_get_evaluation(nomad_setup): evalID = nomad_setup.job.get_allocations("example")[0]["EvalID"] - assert isinstance( - nomad_setup.evaluation.get_evaluation(evalID), dict) == True + assert isinstance(nomad_setup.evaluation.get_evaluation(evalID), dict) == True def test_get_allocations(nomad_setup): diff --git a/tests/test_evaluations.py b/tests/test_evaluations.py index 4ca4362..e3befa8 100644 --- a/tests/test_evaluations.py +++ b/tests/test_evaluations.py @@ -57,7 +57,7 @@ def test_dunder_getattr(nomad_setup): def test_dunder_iter(nomad_setup): - assert hasattr(nomad_setup.evaluations, '__iter__') + assert hasattr(nomad_setup.evaluations, "__iter__") for j in nomad_setup.evaluations: pass diff --git a/tests/test_event.py b/tests/test_event.py index 33a805f..3a2fd3b 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -39,6 +39,12 @@ def test_get_event_stream_with_customized_topic(nomad_setup): event = events.get(timeout=5) assert event assert "Index" in event - assert event["Events"][0]["Type"] in ("NodeRegistration", "NodeDeregistration", "NodeEligibility", "NodeDrain", "NodeEvent") + assert event["Events"][0]["Type"] in ( + "NodeRegistration", + "NodeDeregistration", + "NodeEligibility", + "NodeDrain", + "NodeEvent", + ) stream_exit.set() diff --git a/tests/test_job.py b/tests/test_job.py index 992f327..486a101 100644 --- a/tests/test_job.py +++ b/tests/test_job.py @@ -30,37 +30,73 @@ def test_get_job(nomad_setup): def test_get_jobs_with_namespace_override_no_namespace_declared_on_create_incorrect_declared_namespace(nomad_setup): responses.add( responses.GET, - "http://{ip}:{port}/v1/job/18a0f501-41d5-ae43-ff61-1d8ec3ec8314?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + "http://{ip}:{port}/v1/job/18a0f501-41d5-ae43-ff61-1d8ec3ec8314?namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE + ), status=200, - json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + json=[ + { + "Region": "global", + "ID": "my-job", + "ParentID": "", + "Name": "my-job", + "Namespace": common.NOMAD_NAMESPACE, + "Type": "batch", + "Priority": 50, + } + ], ) with pytest.raises(exceptions.BaseNomadException): - nomad_setup.job.get_job(_id=str(uuid.uuid4())) + nomad_setup.job.get_job(id_=str(uuid.uuid4())) @responses.activate def test_get_jobs_with_namespace_override_no_namespace_declared_on_create(nomad_setup): responses.add( responses.GET, - "http://{ip}:{port}/v1/job/18a0f501-41d5-ae43-ff61-1d8ec3ec8314?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + "http://{ip}:{port}/v1/job/18a0f501-41d5-ae43-ff61-1d8ec3ec8314?namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE + ), status=200, - json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + json=[ + { + "Region": "global", + "ID": "my-job", + "ParentID": "", + "Name": "my-job", + "Namespace": common.NOMAD_NAMESPACE, + "Type": "batch", + "Priority": 50, + } + ], ) - nomad_setup.job.get_job(_id="18a0f501-41d5-ae43-ff61-1d8ec3ec8314", namespace=common.NOMAD_NAMESPACE) + nomad_setup.job.get_job(id_="18a0f501-41d5-ae43-ff61-1d8ec3ec8314", namespace=common.NOMAD_NAMESPACE) @responses.activate def test_get_jobs_with_namespace_override_namespace_declared_on_create(nomad_setup_with_namespace): responses.add( responses.GET, - "http://{ip}:{port}/v1/job/18a0f501-41d5-ae43-ff61-1d8ec3ec8314?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace="override-namespace"), + "http://{ip}:{port}/v1/job/18a0f501-41d5-ae43-ff61-1d8ec3ec8314?namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace="override-namespace" + ), status=200, - json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + json=[ + { + "Region": "global", + "ID": "my-job", + "ParentID": "", + "Name": "my-job", + "Namespace": common.NOMAD_NAMESPACE, + "Type": "batch", + "Priority": 50, + } + ], ) - nomad_setup_with_namespace.job.get_job(_id="18a0f501-41d5-ae43-ff61-1d8ec3ec8314", namespace="override-namespace") + nomad_setup_with_namespace.job.get_job(id_="18a0f501-41d5-ae43-ff61-1d8ec3ec8314", namespace="override-namespace") def test_get_allocations(nomad_setup): @@ -78,6 +114,7 @@ def test_get_evaluations(nomad_setup): def test_evaluate_job(nomad_setup): assert "EvalID" in nomad_setup.job.evaluate_job("example") + # def test_periodic_job(nomad_setup): # assert "EvalID" in nomad_setup.job.periodic_job("example") @@ -88,68 +125,93 @@ def test_delete_job(nomad_setup): @flaky(max_runs=5, min_passes=1) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 3), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 3), reason="Nomad dispatch not supported" +) def test_dispatch_job(nomad_setup): with open("example_batch_parameterized.json") as fh: job = json.loads(fh.read()) nomad_setup.job.register_job("example-batch", job) try: nomad_setup.job.dispatch_job("example-batch", meta={"time": "500"}) - except (exceptions.URLNotFoundNomadException, - exceptions.BaseNomadException) as e: + except (exceptions.URLNotFoundNomadException, exceptions.BaseNomadException) as e: print(e.nomad_resp.text) raise e assert "example-batch" in nomad_setup.job -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 3), reason="Nomad dispatch not supported") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 3), reason="Nomad dispatch not supported" +) def test_summary_job(nomad_setup): j = nomad_setup.job["example"] assert "JobID" in nomad_setup.job.get_summary(j["ID"]) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 4, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 4, 0), reason="Not supported in version" +) def test_plan_job(nomad_setup): with open("example.json") as fh: job = json.loads(fh.read()) - assert "Index" in nomad_setup.job.plan_job(nomad_setup.job["example"]["ID"],job) + assert "Index" in nomad_setup.job.plan_job(nomad_setup.job["example"]["ID"], job) + -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_versions_job(nomad_setup): assert "Versions" in nomad_setup.job.get_versions("example") -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_versions_job_missing(nomad_setup): with pytest.raises(nomad.api.exceptions.URLNotFoundNomadException): assert "Versions" in nomad_setup.job.get_versions("example1") -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_get_job_deployments(nomad_setup): assert "JobID" in nomad_setup.job.get_deployments("example")[0] assert isinstance(nomad_setup.job.get_deployments("example"), list) assert isinstance(nomad_setup.job.get_deployments("example")[0], dict) assert "example" == nomad_setup.job.get_deployments("example")[0]["JobID"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_get_job_deployment(nomad_setup): assert "JobID" in nomad_setup.job.get_deployment("example") assert isinstance(nomad_setup.job.get_deployment("example"), dict) assert "example" == nomad_setup.job.get_deployment("example")["JobID"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_get_summary(nomad_setup): assert "JobID" in nomad_setup.job.get_summary("example") assert isinstance(nomad_setup.job.get_summary("example"), dict) assert "example" == nomad_setup.job.get_summary("example")["JobID"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_revert_job(nomad_setup): current_job_version = nomad_setup.job.get_deployment("example")["JobVersion"] prior_job_version = current_job_version - 1 nomad_setup.job.revert_job("example", prior_job_version, current_job_version) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_stable_job(nomad_setup): current_job_version = nomad_setup.job.get_deployment("example")["JobVersion"] nomad_setup.job.stable_job("example", current_job_version, True) @@ -187,9 +249,11 @@ def test_dunder_getattr(nomad_setup): with pytest.raises(AttributeError): d = nomad_setup.job.does_not_exist + def test_delete_job_with_invalid_purge_param_raises(nomad_setup): with pytest.raises(exceptions.InvalidParameters): - nomad_setup.job.deregister_job("example", purge='True') + nomad_setup.job.deregister_job("example", purge="True") + def test_delete_job_with_purge(nomad_setup): # Run this last since it will purge the job completely, resetting things like diff --git a/tests/test_jobs.py b/tests/test_jobs.py index 860bf58..05d5895 100644 --- a/tests/test_jobs.py +++ b/tests/test_jobs.py @@ -25,7 +25,9 @@ def test_get_jobs_prefix(nomad_setup): nomad_setup.jobs.get_jobs(prefix="ex") -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 3), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 3), reason="Not supported in version" +) def test_parse_job(nomad_setup): with open("example.nomad") as fh: hcl = fh.read() @@ -68,7 +70,7 @@ def test_dunder_getattr(nomad_setup): def test_dunder_iter(nomad_setup): - assert hasattr(nomad_setup.jobs, '__iter__') + assert hasattr(nomad_setup.jobs, "__iter__") for j in nomad_setup.jobs: pass @@ -82,9 +84,21 @@ def test_dunder_len(nomad_setup): def test_get_jobs_with_namespace(nomad_setup_with_namespace): responses.add( responses.GET, - "http://{ip}:{port}/v1/jobs?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + "http://{ip}:{port}/v1/jobs?namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE + ), status=200, - json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + json=[ + { + "Region": "global", + "ID": "my-job", + "ParentID": "", + "Name": "my-job", + "Namespace": common.NOMAD_NAMESPACE, + "Type": "batch", + "Priority": 50, + } + ], ) assert common.NOMAD_NAMESPACE in nomad_setup_with_namespace.jobs.get_jobs()[0]["Namespace"] @@ -93,9 +107,21 @@ def test_get_jobs_with_namespace(nomad_setup_with_namespace): def test_get_jobs_with_namespace_override_no_namespace_declared_on_create_incorrect_declared_namespace(nomad_setup): responses.add( responses.GET, - "http://{ip}:{port}/v1/jobs?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + "http://{ip}:{port}/v1/jobs?namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE + ), status=200, - json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + json=[ + { + "Region": "global", + "ID": "my-job", + "ParentID": "", + "Name": "my-job", + "Namespace": common.NOMAD_NAMESPACE, + "Type": "batch", + "Priority": 50, + } + ], ) with pytest.raises(BaseNomadException): @@ -106,9 +132,21 @@ def test_get_jobs_with_namespace_override_no_namespace_declared_on_create_incorr def test_get_jobs_with_namespace_override_no_namespace_declared_on_create(nomad_setup): responses.add( responses.GET, - "http://{ip}:{port}/v1/jobs?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE), + "http://{ip}:{port}/v1/jobs?namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace=common.NOMAD_NAMESPACE + ), status=200, - json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + json=[ + { + "Region": "global", + "ID": "my-job", + "ParentID": "", + "Name": "my-job", + "Namespace": common.NOMAD_NAMESPACE, + "Type": "batch", + "Priority": 50, + } + ], ) nomad_setup.jobs.get_jobs(namespace=common.NOMAD_NAMESPACE) @@ -118,9 +156,21 @@ def test_get_jobs_with_namespace_override_no_namespace_declared_on_create(nomad_ def test_get_jobs_with_namespace_override_namespace_declared_on_create(nomad_setup_with_namespace): responses.add( responses.GET, - "http://{ip}:{port}/v1/jobs?namespace={namespace}".format(ip=common.IP, port=common.NOMAD_PORT, namespace="override-namespace"), + "http://{ip}:{port}/v1/jobs?namespace={namespace}".format( + ip=common.IP, port=common.NOMAD_PORT, namespace="override-namespace" + ), status=200, - json=[{"Region": "global","ID": "my-job", "ParentID": "", "Name": "my-job","Namespace": common.NOMAD_NAMESPACE, "Type": "batch", "Priority": 50}] + json=[ + { + "Region": "global", + "ID": "my-job", + "ParentID": "", + "Name": "my-job", + "Namespace": common.NOMAD_NAMESPACE, + "Type": "batch", + "Priority": 50, + } + ], ) - nomad_setup_with_namespace.jobs.get_jobs(namespace="override-namespace") \ No newline at end of file + nomad_setup_with_namespace.jobs.get_jobs(namespace="override-namespace") diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 9d7bc00..618db14 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -3,7 +3,9 @@ # integration tests requires nomad Vagrant VM or Binary running -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 1), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 7, 1), reason="Not supported in version" +) def test_metrics(nomad_setup): nomad_setup.metrics.get_metrics() @@ -19,4 +21,4 @@ def test_dunder_repr(nomad_setup): def test_dunder_getattr(nomad_setup): with pytest.raises(AttributeError): - d = nomad_setup.metrics.does_not_exist \ No newline at end of file + d = nomad_setup.metrics.does_not_exist diff --git a/tests/test_namespace.py b/tests/test_namespace.py index 57077bf..53a3924 100644 --- a/tests/test_namespace.py +++ b/tests/test_namespace.py @@ -9,9 +9,7 @@ def test_create_namespace(nomad_setup): responses.add( - responses.POST, - "http://{ip}:{port}/v1/namespace".format(ip=common.IP, port=common.NOMAD_PORT), - status=200 + responses.POST, "http://{ip}:{port}/v1/namespace".format(ip=common.IP, port=common.NOMAD_PORT), status=200 ) namespace_api = '{"Name":"api","Description":"api server namespace"}' @@ -23,9 +21,7 @@ def test_create_namespace(nomad_setup): def test_update_namespace(nomad_setup): responses.add( - responses.POST, - "http://{ip}:{port}/v1/namespace/api".format(ip=common.IP, port=common.NOMAD_PORT), - status=200 + responses.POST, "http://{ip}:{port}/v1/namespace/api".format(ip=common.IP, port=common.NOMAD_PORT), status=200 ) namespace_api = '{"Name":"api","Description":"updated namespace"}' @@ -40,7 +36,7 @@ def test_get_namespace(nomad_setup): responses.GET, "http://{ip}:{port}/v1/namespace/api".format(ip=common.IP, port=common.NOMAD_PORT), status=200, - json={"Name": "api", "Description": "api server namespace"} + json={"Name": "api", "Description": "api server namespace"}, ) assert "api" in nomad_setup.namespace.get_namespace("api")["Name"] @@ -57,7 +53,6 @@ def test_delete_namespace(nomad_setup): nomad_setup.namespace.delete_namespace("api") - ######### ENTERPRISE TEST ########### # def test_apply_namespace(nomad_setup): # namespace_api='{"Name":"api","Description":"api server namespace"}' diff --git a/tests/test_namespaces.py b/tests/test_namespaces.py index 393baa8..46e51f2 100644 --- a/tests/test_namespaces.py +++ b/tests/test_namespaces.py @@ -12,19 +12,9 @@ def test_get_namespaces(nomad_setup): "http://{ip}:{port}/v1/namespaces".format(ip=common.IP, port=common.NOMAD_PORT), status=200, json=[ - { - "CreateIndex": 31, - "Description": "Production API Servers", - "ModifyIndex": 31, - "Name": "api-prod" - }, - { - "CreateIndex": 5, - "Description": "Default shared namespace", - "ModifyIndex": 5, - "Name": "default" - } - ] + {"CreateIndex": 31, "Description": "Production API Servers", "ModifyIndex": 31, "Name": "api-prod"}, + {"CreateIndex": 5, "Description": "Default shared namespace", "ModifyIndex": 5, "Name": "default"}, + ], ) assert isinstance(nomad_setup.namespaces.get_namespaces(), list) == True @@ -37,13 +27,8 @@ def test_get_namespaces_prefix(nomad_setup): "http://{ip}:{port}/v1/namespaces?prefix=api-".format(ip=common.IP, port=common.NOMAD_PORT), status=200, json=[ - { - "CreateIndex": 31, - "Description": "Production API Servers", - "ModifyIndex": 31, - "Name": "api-prod" - }, - ] + {"CreateIndex": 31, "Description": "Production API Servers", "ModifyIndex": 31, "Name": "api-prod"}, + ], ) assert isinstance(nomad_setup.namespaces.get_namespaces(prefix="api-"), list) == True @@ -56,19 +41,9 @@ def test_namespaces_iter(nomad_setup): "http://{ip}:{port}/v1/namespaces".format(ip=common.IP, port=common.NOMAD_PORT), status=200, json=[ - { - "CreateIndex": 31, - "Description": "Production API Servers", - "ModifyIndex": 31, - "Name": "api-prod" - }, - { - "CreateIndex": 5, - "Description": "Default shared namespace", - "ModifyIndex": 5, - "Name": "default" - } - ] + {"CreateIndex": 31, "Description": "Production API Servers", "ModifyIndex": 31, "Name": "api-prod"}, + {"CreateIndex": 5, "Description": "Default shared namespace", "ModifyIndex": 5, "Name": "default"}, + ], ) assert "api-prod" in nomad_setup.namespaces @@ -81,19 +56,9 @@ def test_namespaces_len(nomad_setup): "http://{ip}:{port}/v1/namespaces".format(ip=common.IP, port=common.NOMAD_PORT), status=200, json=[ - { - "CreateIndex": 31, - "Description": "Production API Servers", - "ModifyIndex": 31, - "Name": "api-prod" - }, - { - "CreateIndex": 5, - "Description": "Default shared namespace", - "ModifyIndex": 5, - "Name": "default" - } - ] + {"CreateIndex": 31, "Description": "Production API Servers", "ModifyIndex": 31, "Name": "api-prod"}, + {"CreateIndex": 5, "Description": "Default shared namespace", "ModifyIndex": 5, "Name": "default"}, + ], ) assert 2 == len(nomad_setup.namespaces) diff --git a/tests/test_node.py b/tests/test_node.py index d63274b..055b1f5 100644 --- a/tests/test_node.py +++ b/tests/test_node.py @@ -23,7 +23,9 @@ def test_evaluate_node(nomad_setup): assert "EvalIDs" in nomad_setup.node.evaluate_node(nodeID) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) > (1, 1, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) > (1, 1, 0), reason="Not supported in version" +) def test_drain_node(nomad_setup): nodeID = nomad_setup.nodes["pynomad1"]["ID"] assert "EvalIDs" in nomad_setup.node.drain_node(nodeID) @@ -33,7 +35,9 @@ def test_drain_node(nomad_setup): assert nomad_setup.node[nodeID]["Drain"] is False -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version" +) def test_drain_node_with_spec(nomad_setup): nodeID = nomad_setup.nodes["pynomad1"]["ID"] assert "EvalIDs" in nomad_setup.node.drain_node_with_spec(nodeID, drain_spec={"Duration": "-100000000"}) @@ -42,7 +46,9 @@ def test_drain_node_with_spec(nomad_setup): assert nomad_setup.node[nodeID]["Drain"] is False -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version" +) def test_eligible_node(nomad_setup): nodeID = nomad_setup.nodes["pynomad1"]["ID"] diff --git a/tests/test_nodes.py b/tests/test_nodes.py index f708682..0214902 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -46,7 +46,7 @@ def test_dunder_getattr(nomad_setup): def test_dunder_iter(nomad_setup): - assert hasattr(nomad_setup.nodes, '__iter__') + assert hasattr(nomad_setup.nodes, "__iter__") for j in nomad_setup.nodes: pass diff --git a/tests/test_operator.py b/tests/test_operator.py index 85747e4..85d94f3 100644 --- a/tests/test_operator.py +++ b/tests/test_operator.py @@ -4,17 +4,23 @@ # integration tests requires nomad Vagrant VM or Binary running -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 5), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 5), reason="Not supported in version" +) def test_get_configuration_default(nomad_setup): assert isinstance(nomad_setup.operator.get_configuration(), dict) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 5), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 5), reason="Not supported in version" +) def test_get_configuration_stale(nomad_setup): assert isinstance(nomad_setup.operator.get_configuration(stale=True), dict) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 5), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 5), reason="Not supported in version" +) def test_delete_peer(nomad_setup): with pytest.raises(exceptions.BaseNomadException): nomad_setup.operator.delete_peer("192.168.33.10:4646") diff --git a/tests/test_regions.py b/tests/test_regions.py index 4a16139..5654419 100644 --- a/tests/test_regions.py +++ b/tests/test_regions.py @@ -44,7 +44,7 @@ def test_dunder_getattr(nomad_setup): def test_dunder_iter(nomad_setup): - assert hasattr(nomad_setup.regions, '__iter__') + assert hasattr(nomad_setup.regions, "__iter__") for j in nomad_setup.regions: pass diff --git a/tests/test_scaling.py b/tests/test_scaling.py index 686ad3b..ffc3ff2 100644 --- a/tests/test_scaling.py +++ b/tests/test_scaling.py @@ -2,10 +2,12 @@ from nomad.api import exceptions + def test_scaling_list(nomad_setup): result = nomad_setup.scaling.get_scaling_policies() assert not result + def test_scaling_policy_not_exist(nomad_setup): with pytest.raises(exceptions.URLNotFoundNomadException): nomad_setup.scaling.get_scaling_policy("example") diff --git a/tests/test_search.py b/tests/test_search.py index 6b291ae..04812b3 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -3,32 +3,50 @@ from nomad.api import exceptions -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version" +) def test_search(nomad_setup): result = nomad_setup.search.search("example", "jobs") assert "example" in result["Matches"]["jobs"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version" +) def test_search_incorrect_context(nomad_setup): # job context doesn't exist with pytest.raises(exceptions.InvalidParameters): nomad_setup.search.search("example", "job") -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version" +) def test_search_fuzzy(nomad_setup): result = nomad_setup.search.fuzzy_search("example", "jobs") - assert any(r['ID'] == 'example' for r in result["Matches"]["jobs"]) + assert any(r["ID"] == "example" for r in result["Matches"]["jobs"]) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version" +) def test_search_fuzzy_incorrect_context(nomad_setup): # job context doesn't exist with pytest.raises(exceptions.InvalidParameters): nomad_setup.search.fuzzy_search("example", "job") -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version" +) def test_search_str(nomad_setup): assert isinstance(str(nomad_setup.search), str) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 1, 0), reason="Not supported in version" +) def test_search_repr(nomad_setup): - assert isinstance(repr(nomad_setup.search), str) \ No newline at end of file + assert isinstance(repr(nomad_setup.search), str) diff --git a/tests/test_sentinel.py b/tests/test_sentinel.py index 5d26057..99de2fb 100644 --- a/tests/test_sentinel.py +++ b/tests/test_sentinel.py @@ -19,9 +19,9 @@ def test_list_policies(nomad_setup): "EnforcementLevel": "advisory", "Hash": "CIs8aNX5OfFvo4D7ihWcQSexEJpHp+Za+dHSncVx5+8=", "CreateIndex": 8, - "ModifyIndex": 8 + "ModifyIndex": 8, } - ] + ], ) policies = nomad_setup.sentinel.get_policies() @@ -35,26 +35,22 @@ def test_create_policy(nomad_setup): responses.add( responses.POST, "http://{ip}:{port}/v1/sentinel/policy/my-policy".format(ip=common.IP, port=common.NOMAD_PORT), - status=200 + status=200, ) policy_example = '{"Name": "my-policy", "Description": "This is a great policy", "Scope": "submit-job", "EnforcementLevel": "advisory", "Policy": "main = rule { true }"}' json_policy = json.loads(policy_example) - nomad_setup.sentinel.create_policy(_id="my-policy", policy=json_policy) + nomad_setup.sentinel.create_policy(id_="my-policy", policy=json_policy) @responses.activate def test_update_policy(nomad_setup): - responses.add( - responses.POST, - f"http://{common.IP}:{common.NOMAD_PORT}/v1/sentinel/policy/my-policy", - status=200 - ) + responses.add(responses.POST, f"http://{common.IP}:{common.NOMAD_PORT}/v1/sentinel/policy/my-policy", status=200) policy_example = '{"Name": "my-policy", "Description": "Update", "Scope": "submit-job", "EnforcementLevel": "advisory", "Policy": "main = rule { true }"}' json_policy = json.loads(policy_example) - nomad_setup.sentinel.update_policy(_id="my-policy", policy=json_policy) + nomad_setup.sentinel.update_policy(id_="my-policy", policy=json_policy) @responses.activate @@ -71,8 +67,8 @@ def test_get_policy(nomad_setup): "Policy": "main = rule { true }\n", "Hash": "CIs8aNX5OfFvo4D7ihWcQSexEJpHp+Za+dHSncVx5+8=", "CreateIndex": 8, - "ModifyIndex": 8 - } + "ModifyIndex": 8, + }, ) policy = nomad_setup.sentinel.get_policy("foo") @@ -93,8 +89,8 @@ def test_delete_policy(nomad_setup): "Policy": "main = rule { true }\n", "Hash": "CIs8aNX5OfFvo4D7ihWcQSexEJpHp+Za+dHSncVx5+8=", "CreateIndex": 8, - "ModifyIndex": 8 - } + "ModifyIndex": 8, + }, ) - nomad_setup.sentinel.delete_policy(_id="my-policy") + nomad_setup.sentinel.delete_policy(id_="my-policy") diff --git a/tests/test_status.py b/tests/test_status.py index f29494b..dde1a39 100644 --- a/tests/test_status.py +++ b/tests/test_status.py @@ -8,8 +8,7 @@ def test_get_leader(nomad_setup): if int(sys.version[0]) == 3: assert isinstance(nomad_setup.status.leader.get_leader(), str) == True else: - assert isinstance( - nomad_setup.status.leader.get_leader(), unicode) == True + assert isinstance(nomad_setup.status.leader.get_leader(), unicode) == True def test_get_peers(nomad_setup): @@ -35,8 +34,7 @@ def test_peers_dunder_contain_exists(nomad_setup): def test_peers_dunder_contain_not_exist(nomad_setup): - assert "{IP}:4647".format( - IP="172.16.10.100") not in nomad_setup.status.peers + assert "{IP}:4647".format(IP="172.16.10.100") not in nomad_setup.status.peers def test_leader_dunder_contain_exists(nomad_setup): @@ -44,8 +42,7 @@ def test_leader_dunder_contain_exists(nomad_setup): def test_leader_dunder_contain_not_exist(nomad_setup): - assert "{IP}:4647".format( - IP="172.16.10.100") not in nomad_setup.status.leader + assert "{IP}:4647".format(IP="172.16.10.100") not in nomad_setup.status.leader def test_dunder_str(nomad_setup): @@ -67,7 +64,7 @@ def test_dunder_getattr(nomad_setup): def test_peers_dunder_iter(nomad_setup): - assert hasattr(nomad_setup.status.peers, '__iter__') + assert hasattr(nomad_setup.status.peers, "__iter__") for p in nomad_setup.status.peers: pass diff --git a/tests/test_validate.py b/tests/test_validate.py index 1efde82..9888369 100644 --- a/tests/test_validate.py +++ b/tests/test_validate.py @@ -5,14 +5,18 @@ # integration tests requires nomad Vagrant VM or Binary running -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_validate_job(nomad_setup): with open("example.json") as job: nomad_setup.validate.validate_job(json.loads(job.read())) # integration tests requires nomad Vagrant VM or Binary running -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" +) def test_invalid_job(nomad_setup): with pytest.raises(nomad.api.exceptions.BadRequestNomadException): nomad_setup.validate.validate_job({}) diff --git a/tests/test_variable.py b/tests/test_variable.py index 66ff38a..a1d1056 100644 --- a/tests/test_variable.py +++ b/tests/test_variable.py @@ -1,10 +1,13 @@ import pytest import os + # Nomad doesn't have any variables by default from nomad.api import exceptions -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_create_variable(nomad_setup): payload = { "Items": {"user": "test", "password": "test123"}, @@ -12,7 +15,10 @@ def test_create_variable(nomad_setup): nomad_setup.variable.create_variable("example/first", payload) assert "example/first" in nomad_setup.variables -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_create_variable_in_namespace(nomad_setup): payload = { "Items": {"user": "test2", "password": "321tset"}, @@ -20,7 +26,10 @@ def test_create_variable_in_namespace(nomad_setup): nomad_setup.variable.create_variable("example/second", payload, namespace="default") assert "example/second" in nomad_setup.variables -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_create_variable_with_cas(nomad_setup): payload = { "Items": {"user": "test3", "password": "321tset123"}, @@ -28,67 +37,106 @@ def test_create_variable_with_cas(nomad_setup): nomad_setup.variable.create_variable("example/third", payload, cas=0) assert "example/third" in nomad_setup.variables -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_get_variable_and_check_value(nomad_setup): var = nomad_setup.variable.get_variable("example/first") assert var["Items"]["user"] == "test" -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_get_variable_in_namespace(nomad_setup): var = nomad_setup.variable.get_variable("example/first", namespace="default") assert var["Items"]["user"] == "test" -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_get_no_exist_variable(nomad_setup): with pytest.raises(KeyError): assert nomad_setup.variable["no_exist"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variable_getitem_exist(nomad_setup): var = nomad_setup.variable["example/first"] assert isinstance(var, dict) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variable_str(nomad_setup): assert isinstance(str(nomad_setup.variable), str) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variable_repr(nomad_setup): assert isinstance(repr(nomad_setup.variable), str) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variable_getattr(nomad_setup): with pytest.raises(AttributeError): nomad_setup.variable.does_not_exist -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variable_exist(nomad_setup): assert "example/second" in nomad_setup.variable -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variable_no_exist(nomad_setup): assert "no_exist" not in nomad_setup.variable -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variable_getitem_not_exist(nomad_setup): with pytest.raises(KeyError): nomad_setup.variable["no_exists"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_delete_variable(nomad_setup): assert 3 == len(nomad_setup.variables.get_variables()) nomad_setup.variable.delete_variable("example/third") assert "example/third" not in nomad_setup.variables assert 2 == len(nomad_setup.variables.get_variables()) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_delete_variable_in_namespace(nomad_setup): assert 2 == len(nomad_setup.variables.get_variables()) nomad_setup.variable.delete_variable("example/second", namespace="default") assert "example/third" not in nomad_setup.variables assert 1 == len(nomad_setup.variables.get_variables()) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_delete_variable_with_cas(nomad_setup): variable_path = "variable_with_cas" payload = { diff --git a/tests/test_variables.py b/tests/test_variables.py index cf096e2..c39d31b 100644 --- a/tests/test_variables.py +++ b/tests/test_variables.py @@ -1,55 +1,91 @@ import pytest import os -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_get_variables(nomad_setup): assert 1 == len(nomad_setup.variables.get_variables()) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_get_variables_with_prefix(nomad_setup): assert 1 == len(nomad_setup.variables.get_variables("example/first")) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_get_variables_with_prefix_no_exist(nomad_setup): assert 0 == len(nomad_setup.variables.get_variables("no_exist_var")) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_get_variables_from_namespace(nomad_setup): assert 1 == len(nomad_setup.variables.get_variables(namespace="default")) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_iter_variables(nomad_setup): - assert hasattr(nomad_setup.variables, '__iter__') + assert hasattr(nomad_setup.variables, "__iter__") for _ in nomad_setup.variables: pass -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variables_str(nomad_setup): assert isinstance(str(nomad_setup.variables), str) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variables_repr(nomad_setup): assert isinstance(repr(nomad_setup.variables), str) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variables_not_exist(nomad_setup): assert "no_exist" not in nomad_setup.variables -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variables_exist(nomad_setup): assert "example/first" in nomad_setup.variables -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variables_getitem_exist(nomad_setup): var = nomad_setup.variables["example/first"] assert isinstance(var, dict) -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variables_getitem_not_exist(nomad_setup): with pytest.raises(KeyError): nomad_setup.variables["no_exists"] -@pytest.mark.skipif(tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version") + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 4, 0), reason="Not supported in version" +) def test_variables_getattr(nomad_setup): with pytest.raises(AttributeError): - nomad_setup.variables.does_not_exist \ No newline at end of file + nomad_setup.variables.does_not_exist From 975b9027b97630920ee8a838838dd63b13775774 Mon Sep 17 00:00:00 2001 From: El Nerdo <10955996+elnerd@users.noreply.github.com> Date: Wed, 22 Feb 2023 18:48:05 +0100 Subject: [PATCH 48/69] Fixed ${NOMAD_META_TIME -> ${NOMAD_META_TIME} (#147) Corrected small typo in example --- docs/api/job.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/job.md b/docs/api/job.md index 9e7d39d..4008d4d 100644 --- a/docs/api/job.md +++ b/docs/api/job.md @@ -287,7 +287,7 @@ parametrize_job = { "Name": "example-task", "Driver": "docker", "Config": { - "args": ["${NOMAD_META_TIME"], + "args": ["${NOMAD_META_TIME}"], "command": "sleep", "image": "scratch", "logging": [], From d65f5daaa75154ef4d4cbd2498aa5e5a5b4a87c1 Mon Sep 17 00:00:00 2001 From: Alex K Date: Mon, 27 Mar 2023 09:57:36 -0400 Subject: [PATCH 49/69] chore(docs): update link to the docs (#148) Co-authored-by: Alex Konyukov --- docs/api/acl.md | 24 ++++++++++++------------ docs/api/agent.md | 14 +++++++------- docs/api/allocation.md | 2 +- docs/api/allocations.md | 2 +- docs/api/client.md | 2 +- docs/api/deployment.md | 12 ++++++------ docs/api/deployments.md | 2 +- docs/api/evaluation.md | 4 ++-- docs/api/evaluations.md | 2 +- docs/api/job.md | 30 +++++++++++++++--------------- docs/api/jobs.md | 6 +++--- docs/api/metrics.md | 2 +- docs/api/namespace.md | 8 ++++---- docs/api/namespaces.md | 2 +- docs/api/node.md | 14 +++++++------- docs/api/nodes.md | 2 +- docs/api/regions.md | 2 +- docs/api/sentinel.md | 10 +++++----- docs/api/status.md | 4 ++-- docs/api/system.md | 4 ++-- docs/api/validate.md | 2 +- 21 files changed, 75 insertions(+), 75 deletions(-) diff --git a/docs/api/acl.md b/docs/api/acl.md index 34ec265..90fe636 100644 --- a/docs/api/acl.md +++ b/docs/api/acl.md @@ -8,7 +8,7 @@ Nomad must be running with ACL mode enabled. This endpoint is used to bootstrap the ACL system and provide the initial management token. This request is always forwarded to the authoritative region. It can only be invoked once until a bootstrap reset is performed. -https://www.nomadproject.io/api/acl-tokens.html#bootstrap-token +https://developer.hashicorp.com/nomad/api-docs/acl/tokens#bootstrap-token Example: @@ -48,7 +48,7 @@ print (my_nomad.get_token()) This endpoint lists all ACL tokens. This lists the local tokens and the global tokens which have been replicated to the region, and may lag behind the authoritative region. -https://www.nomadproject.io/api/acl-tokens.html#list-tokens +https://developer.hashicorp.com/nomad/api-docs/acl/tokens#list-tokens Exmaple: @@ -68,7 +68,7 @@ for token in tokens: This endpoint creates an ACL Token. If the token is a global token, the request is forwarded to the authoritative region. -https://www.nomadproject.io/api/acl-tokens.html#create-token +https://developer.hashicorp.com/nomad/api-docs/acl/tokens#create-token Exmample: @@ -91,7 +91,7 @@ created_token = my_nomad.acl.create_token(new_token) This endpoint updates an existing ACL Token. If the token is a global token, the request is forwarded to the authoritative region. Note that a token cannot be switched from global to local or vice versa. -https://www.nomadproject.io/api/acl-tokens.html#update-token +https://developer.hashicorp.com/nomad/api-docs/acl/tokens#update-token Example: @@ -115,7 +115,7 @@ updated_token = my_nomad.acl.update_token('377ba749-8b0e-c7fd-c0c0-9da5bb943088' This endpoint reads an ACL token with the given accessor. If the token is a global token which has been replicated to the region it may lag behind the authoritative region. -https://www.nomadproject.io/api/acl-tokens.html#read-token +https://developer.hashicorp.com/nomad/api-docs/acl/tokens#read-token Exmaple: @@ -131,7 +131,7 @@ token = my_nomad.acl.get_token("377ba749-8b0e-c7fd-c0c0-9da5bb943088") This endpoint reads the ACL token given by the passed SecretID. If the token is a global token which has been replicated to the region it may lag behind the authoritative region. -https://www.nomadproject.io/api/acl-tokens.html#read-self-token +https://developer.hashicorp.com/nomad/api-docs/acl/tokens#read-self-token Exmaple: @@ -147,7 +147,7 @@ self_token = my_nomad.acl.get_self_token() This endpoint deletes the ACL token by accessor. This request is forwarded to the authoritative region for global tokens. -https://www.nomadproject.io/api/acl-tokens.html#delete-token +https://developer.hashicorp.com/nomad/api-docs/acl/tokens#delete-token Example: @@ -164,13 +164,13 @@ my_nomad.acl.delete_token("377ba749-8b0e-c7fd-c0c0-9da5bb943088") Manage acl Policies -https://www.nomadproject.io/api/acl-policies.html +https://developer.hashicorp.com/nomad/api-docs/acl-policies.html ### List policies This endpoint lists all ACL policies. This lists the policies that have been replicated to the region, and may lag behind the authoritative region. -https://www.nomadproject.io/api/acl-policies.html#list-policies +https://developer.hashicorp.com/nomad/api-docs/acl-policies#list-policies Example: @@ -186,7 +186,7 @@ policies = my_nomad.acl.get_policies() This endpoint creates an ACL Policy. This request is always forwarded to the authoritative region. -https://www.nomadproject.io/api/acl-policies.html#create-or-update-policy +https://developer.hashicorp.com/nomad/api-docs/acl-policies#create-or-update-policy Example: ``` @@ -207,7 +207,7 @@ my_nomad.acl.create_policy("my-policy", policy) This endpoint update an ACL Policy. This request is always forwarded to the authoritative region. -https://www.nomadproject.io/api/acl-policies.html#create-or-update-policy +https://developer.hashicorp.com/nomad/api-docs/acl-policies#create-or-update-policy Example: @@ -229,7 +229,7 @@ my_nomad.acl.update_policy("my-policy", policy) This endpoint reads an ACL policy with the given name. This queries the policy that have been replicated to the region, and may lag behind the authoritative region. -https://www.nomadproject.io/api/acl-policies.html#read-policy +https://developer.hashicorp.com/nomad/api-docs/acl-policies#read-policy Example: diff --git a/docs/api/agent.md b/docs/api/agent.md index 32da2d2..81f623f 100644 --- a/docs/api/agent.md +++ b/docs/api/agent.md @@ -4,7 +4,7 @@ This endpoint queries the agent for the known peers in the gossip pool. This endpoint is only applicable to servers. Due to the nature of gossip, this is eventually consistent. -https://www.nomadproject.io/api/agent.html#list-members +https://developer.hashicorp.com/nomad/api-docs/agent#list-members Example: @@ -23,7 +23,7 @@ for member in members["Members"]: This endpoint lists the known server nodes. The servers endpoint is used to query an agent in client mode for its list of known servers. Client nodes register themselves with these server addresses so that they may dequeue work. The servers endpoint can be used to keep this configuration up to date if there are changes in the cluster -https://www.nomadproject.io/api/agent.html#list-servers +https://developer.hashicorp.com/nomad/api-docs/agent#list-servers Example: @@ -42,7 +42,7 @@ for server in servers: This endpoint queries the state of the target agent (self). -https://www.nomadproject.io/api/agent.html#query-self +https://developer.hashicorp.com/nomad/api-docs/agent#query-self Example: @@ -60,7 +60,7 @@ print (agent) This endpoint updates the list of known servers to the provided list. This replaces all previous server addresses with the new list. -https://www.nomadproject.io/api/agent.html#update-servers +https://developer.hashicorp.com/nomad/api-docs/agent#update-servers Example: @@ -76,7 +76,7 @@ r = my_nomad.agent.update_servers(['192.168.33.11', '10.1.10.200:4829']) This endpoint introduces a new member to the gossip pool. This endpoint is only eligible for servers. -https://www.nomadproject.io/api/agent.html#join-agent +https://developer.hashicorp.com/nomad/api-docs/agent#join-agent Example: @@ -92,7 +92,7 @@ r = my_nomad.agent.join_agent("server02") This endpoint forces a member of the gossip pool from the "failed" state to the "left" state. This allows the consensus protocol to remove the peer and stop attempting replication. This is only applicable for servers. -https://www.nomadproject.io/api/agent.html#force-leave-agent +https://developer.hashicorp.com/nomad/api-docs/agent#force-leave-agent Exmaple: @@ -110,7 +110,7 @@ This endpoint returns whether or not the agent is healthy. When using Consul it When the agent is unhealthy 500 will be returned along with JSON response containing an error message. -https://www.nomadproject.io/api/agent.html#health +https://developer.hashicorp.com/nomad/api-docs/agent#health Example: diff --git a/docs/api/allocation.md b/docs/api/allocation.md index 2c331d2..02efd9d 100644 --- a/docs/api/allocation.md +++ b/docs/api/allocation.md @@ -4,7 +4,7 @@ This endpoint reads information about a specific allocation. -https://www.nomadproject.io/api/allocations.html#read-allocation +https://developer.hashicorp.com/nomad/api-docs/allocations#read-allocation ``` import nomad diff --git a/docs/api/allocations.md b/docs/api/allocations.md index 8c7ef2f..4f525af 100644 --- a/docs/api/allocations.md +++ b/docs/api/allocations.md @@ -4,7 +4,7 @@ This endpoint lists all allocations. -https://www.nomadproject.io/api/allocations.html#list-allocations +https://developer.hashicorp.com/nomad/api-docs/allocations#list-allocations Example: diff --git a/docs/api/client.md b/docs/api/client.md index da9ca15..bd7746f 100644 --- a/docs/api/client.md +++ b/docs/api/client.md @@ -4,7 +4,7 @@ This endpoint queries the actual resources consumed on a node. The API endpoint is hosted by the Nomad client and requests have to be made to the nomad client whose resource usage metrics are of interest. -https://www.nomadproject.io/api/client.html#read-stats +https://developer.hashicorp.com/nomad/api-docs/client#read-stats Example: diff --git a/docs/api/deployment.md b/docs/api/deployment.md index 9445cd8..919bbcd 100644 --- a/docs/api/deployment.md +++ b/docs/api/deployment.md @@ -6,7 +6,7 @@ The deployment endpoints are used to query for and interact with deployments. This endpoint reads information about a specific deployment by ID. -https://www.nomadproject.io/api/deployments.html#read-deployment +https://developer.hashicorp.com/nomad/api-docs/deployments#read-deployment Example: @@ -25,7 +25,7 @@ print (deployment) This endpoint lists the allocations created or modified for the given deployment. -https://www.nomadproject.io/api/deployments.html#list-allocations-for-deployment +https://developer.hashicorp.com/nomad/api-docs/deployments#list-allocations-for-deployment Example: @@ -44,7 +44,7 @@ for allocation in allocations: This endpoint is used to mark a deployment as failed. This should be done to force the scheduler to stop creating allocations as part of the deployment or to cause a rollback to a previous job version. This endpoint only triggers a rollback if the most recent stable version of the job has a different specification than the job being reverted. -https://www.nomadproject.io/api/deployments.html#fail-deployment +https://developer.hashicorp.com/nomad/api-docs/deployments#fail-deployment example: @@ -69,7 +69,7 @@ fail_deployment = my_nomad.deployment.fail_deployment('a8061a1c-d4c9-2a7d-a4b2-9 This endpoint is used to pause or unpause a deployment. This is done to pause a rolling upgrade or resume it. -https://www.nomadproject.io/api/deployments.html#pause-deployment +https://developer.hashicorp.com/nomad/api-docs/deployments#pause-deployment example: @@ -103,7 +103,7 @@ pause = my_nomad.deployment.pause_deployment("52c47d49-eefa-540f-f0f1-d25ba298c8 This endpoint is used to promote task groups that have canaries for a deployment. This should be done when the placed canaries are healthy and the rolling upgrade of the remaining allocations should begin. -https://www.nomadproject.io/api/deployments.html#promote-deployment +https://developer.hashicorp.com/nomad/api-docs/deployments#promote-deployment #### Promote All @@ -138,7 +138,7 @@ promote = my_nomad.deployment.promote_deployment_groups("52c47d49-eefa-540f-f0f1 This endpoint is used to set the health of an allocation that is in the deployment manually. In some use cases, automatic detection of allocation health may not be desired. As such those task groups can be marked with an upgrade policy that uses health_check = "manual". Those allocations must have their health marked manually using this endpoint. Marking an allocation as healthy will allow the rolling upgrade to proceed. Marking it as failed will cause the deployment to fail. This endpoint only triggers a rollback if the most recent stable version of the job has a different specification than the job being reverted. -https://www.nomadproject.io/api/deployments.html#set-allocation-health-in-deployment +https://developer.hashicorp.com/nomad/api-docs/deployments#set-allocation-health-in-deployment example: diff --git a/docs/api/deployments.md b/docs/api/deployments.md index ab5bdcf..4cc0a03 100644 --- a/docs/api/deployments.md +++ b/docs/api/deployments.md @@ -6,7 +6,7 @@ The deployment endpoints are used to query for and interact with deployments. This endpoint lists all deployments. -https://www.nomadproject.io/api/deployments.html#list-deployments +https://developer.hashicorp.com/nomad/api-docs/deployments#list-deployments Example: diff --git a/docs/api/evaluation.md b/docs/api/evaluation.md index e035a83..561dc98 100644 --- a/docs/api/evaluation.md +++ b/docs/api/evaluation.md @@ -4,7 +4,7 @@ This endpoint reads information about a specific evaluation by ID. -https://www.nomadproject.io/api/evaluations.html#read-evaluation +https://developer.hashicorp.com/nomad/api-docs/evaluations#read-evaluation Example: @@ -22,7 +22,7 @@ print (evaluation) This endpoint lists the allocations created or modified for the given evaluation. -https://www.nomadproject.io/api/evaluations.html#list-allocations-for-evaluation +https://developer.hashicorp.com/nomad/api-docs/evaluations#list-allocations-for-evaluation Example: diff --git a/docs/api/evaluations.md b/docs/api/evaluations.md index f0745cd..fcf48b3 100644 --- a/docs/api/evaluations.md +++ b/docs/api/evaluations.md @@ -4,7 +4,7 @@ This endpoint lists all evaluations. -https://www.nomadproject.io/api/evaluations.html#list-evaluations +https://developer.hashicorp.com/nomad/api-docs/evaluations#list-evaluations Example: diff --git a/docs/api/job.md b/docs/api/job.md index 4008d4d..af6f77f 100644 --- a/docs/api/job.md +++ b/docs/api/job.md @@ -4,7 +4,7 @@ This endpoint creates (aka "registers") a new job in the system. -https://www.nomadproject.io/api/jobs.html#create-job +https://developer.hashicorp.com/nomad/api-docs/jobs#create-job Example: @@ -109,7 +109,7 @@ response = my_nomad.job.register_job("example", job) This endpoint reads information about a single job for its specification and status. -https://www.nomadproject.io/api/jobs.html#read-job +https://developer.hashicorp.com/nomad/api-docs/jobs#read-job Example: @@ -126,7 +126,7 @@ job = my_nomad.job.get_job("example") This endpoint reads information about all versions of a job. -https://www.nomadproject.io/api/jobs.html#list-job-versions +https://developer.hashicorp.com/nomad/api-docs/jobs#list-job-versions Example: @@ -145,7 +145,7 @@ for version in versions["Versions"]: This endpoint reads information about a single job's allocations. -https://www.nomadproject.io/api/jobs.html#list-job-allocations +https://developer.hashicorp.com/nomad/api-docs/jobs#list-job-allocations Example: @@ -164,7 +164,7 @@ for allocation in allocations: This endpoint reads information about a single job's evaluations -https://www.nomadproject.io/api/jobs.html#list-job-evaluations +https://developer.hashicorp.com/nomad/api-docs/jobs#list-job-evaluations Example: @@ -184,7 +184,7 @@ for evaluation in evaluations: This endpoint lists a single job's deployments -https://www.nomadproject.io/api/jobs.html#list-job-deployments +https://developer.hashicorp.com/nomad/api-docs/jobs#list-job-deployments Example: @@ -204,7 +204,7 @@ for deployment in deployments: This endpoint returns a single job's most recent deployment. -https://www.nomadproject.io/api/jobs.html#read-job-39-s-most-recent-deployment +https://developer.hashicorp.com/nomad/api-docs/jobs#read-job-39-s-most-recent-deployment Example: @@ -221,7 +221,7 @@ deployment = my_nomad.job.get_deployment("example") This endpoint reads summary information about a job. -https://www.nomadproject.io/api/jobs.html#read-job-summary +https://developer.hashicorp.com/nomad/api-docs/jobs#read-job-summary Example: @@ -238,7 +238,7 @@ summary = my_nomad.job.get_summary("example") This endpoint registers a new job or updates an existing job. -https://www.nomadproject.io/api/jobs.html#update-existing-job +https://developer.hashicorp.com/nomad/api-docs/jobs#update-existing-job Example: @@ -249,7 +249,7 @@ See create new job This endpoint dispatches a new instance of a parameterized job. -https://www.nomadproject.io/api/jobs.html#dispatch-job +https://developer.hashicorp.com/nomad/api-docs/jobs#dispatch-job Example: @@ -340,7 +340,7 @@ my_nomad.job.dispatch_job("example-batch", meta={"time": "500"}) This endpoint reverts the job to an older version. -https://www.nomadproject.io/api/jobs.html#revert-to-older-job-version +https://developer.hashicorp.com/nomad/api-docs/jobs#revert-to-older-job-version Example: @@ -361,7 +361,7 @@ my_nomad.job.revert_job("example", prior_job_version, current_job_version) This endpoint sets the job's stability. -https://www.nomadproject.io/api/jobs.html#set-job-stability +https://developer.hashicorp.com/nomad/api-docs/jobs#set-job-stability Example: @@ -380,7 +380,7 @@ my_nomad.job.stable_job("example", current_job_version, True) This endpoint creates a new evaluation for the given job. This can be used to force run the scheduling logic if necessary. -https://www.nomadproject.io/api/jobs.html#create-job-evaluation +https://developer.hashicorp.com/nomad/api-docs/jobs#create-job-evaluation Example: @@ -396,7 +396,7 @@ my_nomad.job.evaluate_job("example") This endpoint invokes a dry-run of the scheduler for the job. -https://www.nomadproject.io/api/jobs.html#create-job-plan +https://developer.hashicorp.com/nomad/api-docs/jobs#create-job-plan Example: @@ -500,7 +500,7 @@ plan = my_nomad.job.plan_job("example", job) This endpoint deregisters a job, and stops all allocations part of it. -https://www.nomadproject.io/api/jobs.html#stop-a-job +https://developer.hashicorp.com/nomad/api-docs/jobs#stop-a-job Example of deferred removal of job (performed by Nomad garbage collector): diff --git a/docs/api/jobs.md b/docs/api/jobs.md index 08fb1f6..c1a94a5 100644 --- a/docs/api/jobs.md +++ b/docs/api/jobs.md @@ -4,7 +4,7 @@ This endpoint lists all known jobs in the system registered with Nomad. -https://www.nomadproject.io/api/jobs.html#list-jobs +https://developer.hashicorp.com/nomad/api-docs/jobs#list-jobs Example: @@ -23,7 +23,7 @@ for job in jobs: This endpoint creates (aka "registers") a new job in the system. -https://www.nomadproject.io/api/jobs.html#create-job +https://developer.hashicorp.com/nomad/api-docs/jobs#create-job Example: @@ -129,7 +129,7 @@ To convert to python dict and verify for correctness a hcl/nomad file. The examp `nomad job init` and it will assume this file is in the current working directory. In practice this file should already be read and used as the parameter hcl. -https://www.nomadproject.io/api/jobs.html#parse-job +https://developer.hashicorp.com/nomad/api-docs/jobs#parse-job ```python diff --git a/docs/api/metrics.md b/docs/api/metrics.md index f797525..5c5fe0c 100644 --- a/docs/api/metrics.md +++ b/docs/api/metrics.md @@ -2,7 +2,7 @@ ### Get node metrics -https://www.nomadproject.io/api/metrics.html +https://developer.hashicorp.com/nomad/api-docs/metrics.html Example: diff --git a/docs/api/namespace.md b/docs/api/namespace.md index c4d8a42..52d126c 100644 --- a/docs/api/namespace.md +++ b/docs/api/namespace.md @@ -8,7 +8,7 @@ You must have nomad **ENTERPRISE Edition** Create new namespace -https://www.nomadproject.io/api/namespaces.html#create-or-update-namespace +https://developer.hashicorp.com/nomad/api-docs/namespaces#create-or-update-namespace Exmample: @@ -52,7 +52,7 @@ print (my_nomad.get_namespace()) This endpoint reads information about a specific namespace. -https://www.nomadproject.io/api/namespaces.html#read-namespace +https://developer.hashicorp.com/nomad/api-docs/namespaces#read-namespace Exmample: @@ -69,7 +69,7 @@ namespace = my_nomad.namespace.get_namespace("api-prod") Update existing namespace -https://www.nomadproject.io/api/namespaces.html#create-or-update-namespace +https://developer.hashicorp.com/nomad/api-docs/namespaces#create-or-update-namespace Example: @@ -89,7 +89,7 @@ my_nomad.namespace.create_namespace("api-prod", namespace) Delete namespace -https://www.nomadproject.io/api/namespaces.html#create-or-update-namespace +https://developer.hashicorp.com/nomad/api-docs/namespaces#create-or-update-namespace Exmaple: diff --git a/docs/api/namespaces.md b/docs/api/namespaces.md index bb2595b..359ced3 100644 --- a/docs/api/namespaces.md +++ b/docs/api/namespaces.md @@ -8,7 +8,7 @@ You must have nomad **ENTERPRISE Edition** This endpoint lists all namespaces. -https://www.nomadproject.io/api/namespaces.html#list-namespaces +https://developer.hashicorp.com/nomad/api-docs/namespaces#list-namespaces Exmaple: diff --git a/docs/api/node.md b/docs/api/node.md index 9accd9d..f89ef69 100644 --- a/docs/api/node.md +++ b/docs/api/node.md @@ -4,7 +4,7 @@ This endpoint queries the status of a client node. -https://www.nomadproject.io/api/nodes.html#read-node +https://developer.hashicorp.com/nomad/api-docs/nodes#read-node Example: @@ -20,7 +20,7 @@ node = my_nomad.node.get_node('ed1bbae7-c38a-df2d-1de7-50dbc753fc98') This endpoint lists all of the allocations for the given node. This can be used to determine what allocations have been scheduled on the node, their current status, and the values of dynamically assigned resources, like ports. -https://www.nomadproject.io/api/nodes.html#list-node-allocations +https://developer.hashicorp.com/nomad/api-docs/nodes#list-node-allocations Example: @@ -40,7 +40,7 @@ for allocation in allocations: This endpoint creates a new evaluation for the given node. This can be used to force a run of the scheduling logic. -https://www.nomadproject.io/api/nodes.html#create-node-evaluation +https://developer.hashicorp.com/nomad/api-docs/nodes#create-node-evaluation Example: @@ -56,7 +56,7 @@ my_nomad.node.evaluate_node('ed1bbae7-c38a-df2d-1de7-50dbc753fc98') This endpoint toggles the drain mode of the node. When draining is enabled, no further allocations will be assigned to this node, and existing allocations will be migrated to new nodes. -https://www.nomadproject.io/api/nodes.html#drain-node +https://developer.hashicorp.com/nomad/api-docs/nodes#drain-node Example: @@ -76,7 +76,7 @@ my_nomad.node.drain_node('ed1bbae7-c38a-df2d-1de7-50dbc753fc98', enable=False) This endpoint toggles the drain mode of the node. When draining is enabled, no further allocations will be assigned to this node, and existing allocations will be migrated to new nodes. -https://www.nomadproject.io/api/nodes.html#drain-node +https://developer.hashicorp.com/nomad/api-docs/nodes#drain-node Example: @@ -102,7 +102,7 @@ my_nomad.node.drain_node_with_spec('ed1bbae7-c38a-df2d-1de7-50dbc753fc98', drain This endpoint toggles the eligibility of the node. When a node's "SchedulingEligibility" is ineligible the scheduler will not consider it for new placements. -https://www.nomadproject.io/api/nodes.html#toggle-node-eligibility +https://developer.hashicorp.com/nomad/api-docs/nodes#toggle-node-eligibility Example: @@ -122,7 +122,7 @@ my_nomad.node.eligible_node('ed1bbae7-c38a-df2d-1de7-50dbc753fc98', eligible=Tru This endpoint purges a node from the system. Nodes can still join the cluster if they are alive. -https://www.nomadproject.io/api/nodes.html#purge-node +https://developer.hashicorp.com/nomad/api-docs/nodes#purge-node Example: diff --git a/docs/api/nodes.md b/docs/api/nodes.md index 03cbc78..825957e 100644 --- a/docs/api/nodes.md +++ b/docs/api/nodes.md @@ -4,7 +4,7 @@ This endpoint lists all nodes registered with Nomad. -https://www.nomadproject.io/api/nodes.html#list-nodes +https://developer.hashicorp.com/nomad/api-docs/nodes#list-nodes Example: diff --git a/docs/api/regions.md b/docs/api/regions.md index 54b7754..f68c230 100644 --- a/docs/api/regions.md +++ b/docs/api/regions.md @@ -2,7 +2,7 @@ ### List regions -https://www.nomadproject.io/api/regions.html#list-regions +https://developer.hashicorp.com/nomad/api-docs/regions#list-regions Example: diff --git a/docs/api/sentinel.md b/docs/api/sentinel.md index 2693c28..1fe7148 100644 --- a/docs/api/sentinel.md +++ b/docs/api/sentinel.md @@ -8,7 +8,7 @@ You must have nomad **ENTERPRISE Edition** Get all policies -https://www.nomadproject.io/api/sentinel-policies.html#list-policies +https://developer.hashicorp.com/nomad/api-docs/sentinel-policies#list-policies Example: @@ -24,7 +24,7 @@ policies = my_nomad.sentinel.get_policies() Create a policy -https://www.nomadproject.io/api/sentinel-policies.html#create-or-update-policy +https://developer.hashicorp.com/nomad/api-docs/sentinel-policies#create-or-update-policy Example: ``` @@ -47,7 +47,7 @@ my_nomad.sentinel.create_policy("my-policy", policy) Update specific policy -https://www.nomadproject.io/api/sentinel-policies.html#create-or-update-policy +https://developer.hashicorp.com/nomad/api-docs/sentinel-policies#create-or-update-policy Example: @@ -71,7 +71,7 @@ my_nomad.sentinel.update_policy("my-policy", policy) Get specific policy -https://www.nomadproject.io/api/sentinel-policies.html#read-policy +https://developer.hashicorp.com/nomad/api-docs/sentinel-policies#read-policy Example: @@ -87,7 +87,7 @@ policy = my_nomad.sentinel.get_policy("my-policy") Delete specific policy -https://www.nomadproject.io/api/sentinel-policies.html#delete-policy +https://developer.hashicorp.com/nomad/api-docs/sentinel-policies#delete-policy Example: diff --git a/docs/api/status.md b/docs/api/status.md index ed5cc97..6e30d5d 100644 --- a/docs/api/status.md +++ b/docs/api/status.md @@ -4,7 +4,7 @@ This endpoint returns the address of the current leader in the region. -https://www.nomadproject.io/api/status.html#read-leader +https://developer.hashicorp.com/nomad/api-docs/status#read-leader Example: @@ -20,7 +20,7 @@ leader = my_nomad.status.leader.get_leader() This endpoint returns the set of raft peers in the region. -https://www.nomadproject.io/api/status.html#list-peers +https://developer.hashicorp.com/nomad/api-docs/status#list-peers Example: diff --git a/docs/api/system.md b/docs/api/system.md index ead5bc0..f836fe6 100644 --- a/docs/api/system.md +++ b/docs/api/system.md @@ -4,7 +4,7 @@ This endpoint initializes a garbage collection of jobs, evaluations, allocations, and nodes. This is an asynchronous operation. -https://www.nomadproject.io/api/system.html#force-gc +https://developer.hashicorp.com/nomad/api-docs/system#force-gc Example: @@ -20,7 +20,7 @@ my_nomad.system.initiate_garbage_collection() This endpoint reconciles the summaries of all registered jobs. -https://www.nomadproject.io/api/system.html#reconcile-summaries +https://developer.hashicorp.com/nomad/api-docs/system#reconcile-summaries Example: diff --git a/docs/api/validate.md b/docs/api/validate.md index 8b84022..ce05d91 100644 --- a/docs/api/validate.md +++ b/docs/api/validate.md @@ -4,7 +4,7 @@ This endpoint validates a Nomad job file. The local Nomad agent forwards the request to a server. In the event a server can't be reached the agent verifies the job file locally but skips validating driver configurations. -https://www.nomadproject.io/api/validate.html#validate-job +https://developer.hashicorp.com/nomad/api-docs/validate#validate-job Example: From ef6bc84c6ea633911f3a58797e8f017eba65859b Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Wed, 12 Apr 2023 13:19:49 +0200 Subject: [PATCH 50/69] add option for custom user agent (#150) * add option for custom user agent * add tests --- CHANGELOG.md | 2 +- nomad/__init__.py | 37 ++++++++++++++++++++++--------------- nomad/api/base.py | 29 ++++++++++++++++++----------- tests/test_base.py | 14 ++++++++++++++ 4 files changed, 55 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 831e35f..71144a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ ### Other changes * Up `requests` lib version to 2.28.1 * Add migging parameters to allocations.get_allocations and jobs.get_jobs (#144). Thanks @Kamilcuk - +* Add option for custom user agent ## 1.5.0 * Add `namespace` agrument support for `get_allocations` and `get_deployments` endpoints (#133) * Add Python 3.10 support (#133) diff --git a/nomad/__init__.py b/nomad/__init__.py index 0ad41b9..fdb4f16 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -1,5 +1,8 @@ """Nomad Python library""" import os +from typing import Optional + +import requests from nomad import api @@ -11,18 +14,19 @@ class Nomad: # pylint: disable=too-many-public-methods,too-many-instance-attrib def __init__( # pylint: disable=too-many-arguments self, - host="127.0.0.1", - secure=False, - port=4646, - address=os.getenv("NOMAD_ADDR", None), - namespace=os.getenv("NOMAD_NAMESPACE", None), - token=os.getenv("NOMAD_TOKEN", None), - timeout=5, - region=os.getenv("NOMAD_REGION", None), - version="v1", - verify=False, - cert=(os.getenv("NOMAD_CLIENT_CERT", None), os.getenv("NOMAD_CLIENT_KEY", None)), - session=None, + host: str = "127.0.0.1", + secure: bool = False, + port: int = 4646, + address: Optional[str] = os.getenv("NOMAD_ADDR", None), + user_agent: Optional[str] = None, + namespace: Optional[str] = os.getenv("NOMAD_NAMESPACE", None), + token: Optional[str] = os.getenv("NOMAD_TOKEN", None), + timeout: int = 5, + region: Optional[str] = os.getenv("NOMAD_REGION", None), + version: str = "v1", + verify: bool = False, + cert: tuple = (os.getenv("NOMAD_CLIENT_CERT", None), os.getenv("NOMAD_CLIENT_KEY", None)), + session: requests.Session = None, ): """Nomad api client @@ -31,18 +35,19 @@ def __init__( # pylint: disable=too-many-arguments optional arguments: - host (defaults 127.0.0.1), string ip or name of the nomad api server/agent that will be used. - port (defaults 4646), integer port that will be used to connect. + - user_agent (defaults None), custom user agent for requests to Nomad. - secure (defaults False), define if the protocol is secured or not (https or http) - - version (defaults v1), vesion of the api of nomad. + - version (defaults v1), version of the api of nomad. - verify (defaults False), verify the certificate when tls/ssl is enabled at nomad. - cert (defaults empty), cert, or key and cert file to validate the certificate configured at nomad. - region (defaults None), version of the region to use. It will be used then regions of the current agent of the connection. - - namespace (defaults to None), Specifies the enterpise namespace that will + - namespace (defaults to None), Specifies the enterprise namespace that will be use to deploy or to ask info to nomad. - token (defaults to None), Specifies to append ACL token to the headers to - make authentication on secured based nomad environemnts. + make authentication on secured based nomad environments. - session (defaults to None), allows for injecting a prepared requests.Session object that all requests to Nomad should use. returns: Nomad api client object @@ -56,6 +61,7 @@ def __init__( # pylint: disable=too-many-arguments self.secure = secure self.port = port self.address = address + self.user_agent = user_agent self.region = region self.timeout = timeout self.version = version @@ -69,6 +75,7 @@ def __init__( # pylint: disable=too-many-arguments "address": self.address, "uri": self.get_uri(), "port": self.port, + "user_agent": self.user_agent, "namespace": self.__namespace, "token": self.token, "timeout": self.timeout, diff --git a/nomad/api/base.py b/nomad/api/base.py index a4c7329..87f3714 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -1,4 +1,6 @@ """Requester""" +from typing import Optional + import requests import nomad.api.exceptions @@ -13,20 +15,22 @@ class Requester: # pylint: disable=too-many-instance-attributes,too-few-public- def __init__( # pylint: disable=too-many-arguments self, - address=None, - uri="http://127.0.0.1", - port=4646, - namespace=None, - token=None, - timeout=5, - version="v1", - verify=False, - cert=(), - region=None, - session=None, + address: Optional[str] = None, + uri: Optional[str] = "http://127.0.0.1", + port: int = 4646, + user_agent: Optional[str] = None, + namespace: Optional[str] = None, + token: Optional[str] = None, + timeout: int = 5, + version: str = "v1", + verify: bool = False, + cert: tuple = (), + region: Optional[str] = None, + session: requests.Session = None, ): self.uri = uri self.port = port + self.user_agent = user_agent self.namespace = namespace self.token = token self.timeout = timeout @@ -140,6 +144,9 @@ def _request( # pylint: disable=too-many-arguments, too-many-branches else: headers = {"X-Nomad-Token": self.token} + if self.user_agent: + headers["User-Agent"] = self.user_agent + response = None try: diff --git a/tests/test_base.py b/tests/test_base.py index 59b9be3..d1501a0 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -147,3 +147,17 @@ def test_base_use_address_instead_on_host_port(): address=nomad_address, host=common.IP, port=common.NOMAD_PORT, verify=False, token=common.NOMAD_TOKEN ) n.jobs.get_jobs() + +@responses.activate +def test_use_custom_user_agent(): + custom_agent_name = "custom_agent" + responses.add(responses.GET, "https://nomad.service.consul:4646/v1/jobs", status=200, json=[]) + + nomad_address = "https://nomad.service.consul:4646" + n = nomad.Nomad( + address=nomad_address, host=common.IP, port=common.NOMAD_PORT, verify=False, + token=common.NOMAD_TOKEN, user_agent=custom_agent_name + ) + n.jobs.get_jobs() + + assert responses.calls[0].request.headers["User-Agent"] == custom_agent_name From 364f063a061d95a4ae4500580930bb0bb42ba134 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Mon, 17 Apr 2023 20:02:32 +0200 Subject: [PATCH 51/69] Add missing arguments to get_allocations() (#149) * replace _id to id_ * add black to format files * change changelog * fix linter * add missing record to CHANGELOG.md * add parameters to allocations endpoint --- CHANGELOG.md | 5 +++-- nomad/api/allocations.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71144a2..6b6cc6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,12 @@ * Rename `id` arguments to `id_` across of code base * Rename `type` arguments to `type_` across of code base ### Other changes +* Add more missing parameters to allocations.get_allocations() * Up `requests` lib version to 2.28.1 -* Add migging parameters to allocations.get_allocations and jobs.get_jobs (#144). Thanks @Kamilcuk +* Add missing parameters to allocations.get_allocations and jobs.get_jobs (#144). Thanks @Kamilcuk * Add option for custom user agent ## 1.5.0 -* Add `namespace` agrument support for `get_allocations` and `get_deployments` endpoints (#133) +* Add `namespace` argument support for `get_allocations` and `get_deployments` endpoints (#133) * Add Python 3.10 support (#133) * Add support for pre-populated Sessions (#132) * Add scaling policy endpoint (#136) diff --git a/nomad/api/allocations.py b/nomad/api/allocations.py index c776772..128b072 100644 --- a/nomad/api/allocations.py +++ b/nomad/api/allocations.py @@ -38,10 +38,13 @@ def __iter__(self): def get_allocations( # pylint: disable=too-many-arguments self, prefix: Optional[str] = None, + next_token: Optional[str] = None, + per_page: Optional[int] = None, filter_: Optional[str] = None, namespace: Optional[str] = None, resources: Optional[bool] = None, task_states: Optional[bool] = None, + reverse: Optional[bool] = None, ): """Lists all the allocations. @@ -49,12 +52,18 @@ def get_allocations( # pylint: disable=too-many-arguments arguments: - prefix :(str) optional, specifies a string to filter allocations on based on an prefix. This is specified as a querystring parameter. + - next_token :(str) optional. + This endpoint supports paging. The next_token parameter accepts a string which identifies the next + expected allocation. This value can be obtained from the X-Nomad-NextToken header from the previous + response. + - per_page :(int) optional - filter_ :(str) optional Name has a trailing underscore not to conflict with builtin function. - namespace :(str) optional, specifies the target namespace. Specifying * would return all jobs. This is specified as a querystring parameter. - resources :(bool) optional - task_states :(bool) optional + - reverse :(bool) optional returns: list raises: - nomad.api.exceptions.BaseNomadException @@ -62,9 +71,12 @@ def get_allocations( # pylint: disable=too-many-arguments """ params = { "prefix": prefix, + "next_token": next_token, + "per_page": per_page, "filter": filter_, "namespace": namespace, "resources": resources, "task_states": task_states, + "reverse": reverse, } return self.request(method="get", params=params).json() From f5dcfd119fb63df3d27098dfeaca641861e87184 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Tue, 23 May 2023 18:42:37 +0200 Subject: [PATCH 52/69] add missing arguments to node endpoints (#152) * add missing arguments to node endpoints * add tests * fix * add more tests * add CHANGELOG --- CHANGELOG.md | 3 ++- nomad/api/allocation.py | 4 ++-- nomad/api/evaluation.py | 1 - nomad/api/namespace.py | 2 -- nomad/api/node.py | 17 ++++++++--------- nomad/api/nodes.py | 21 +++++++++++++++++++-- tests/test_allocations.py | 3 +++ tests/test_nodes.py | 18 ++++++++++++++++-- 8 files changed, 50 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b6cc6a..1ac2a9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ * Add more missing parameters to allocations.get_allocations() * Up `requests` lib version to 2.28.1 * Add missing parameters to allocations.get_allocations and jobs.get_jobs (#144). Thanks @Kamilcuk -* Add option for custom user agent +* Add option for custom user agent (#150) +* Add missing parameters to nodes.get_nodes (#152). ## 1.5.0 * Add `namespace` argument support for `get_allocations` and `get_deployments` endpoints (#133) * Add Python 3.10 support (#133) diff --git a/nomad/api/allocation.py b/nomad/api/allocation.py index 6ca74a4..87d64d9 100644 --- a/nomad/api/allocation.py +++ b/nomad/api/allocation.py @@ -47,7 +47,7 @@ def __getitem__(self, item): except nomad.api.exceptions.URLNotFoundNomadException as exc: raise KeyError from exc - def get_allocation(self, id_): + def get_allocation(self, id_: str): """Query a specific allocation. https://www.nomadproject.io/docs/http/alloc.html @@ -59,7 +59,7 @@ def get_allocation(self, id_): """ return self.request(id_, method="get").json() - def stop_allocation(self, id_): + def stop_allocation(self, id_: str): """Stop a specific allocation. https://www.nomadproject.io/api-docs/allocations/#stop-allocation diff --git a/nomad/api/evaluation.py b/nomad/api/evaluation.py index e7bbf9b..2f1546a 100644 --- a/nomad/api/evaluation.py +++ b/nomad/api/evaluation.py @@ -36,7 +36,6 @@ def __contains__(self, item): return False def __getitem__(self, item): - try: evaluation = self.get_evaluation(item) if evaluation["ID"] == item: diff --git a/nomad/api/namespace.py b/nomad/api/namespace.py index d494449..4044e5b 100644 --- a/nomad/api/namespace.py +++ b/nomad/api/namespace.py @@ -28,7 +28,6 @@ def __getattr__(self, item): raise AttributeError(msg) def __contains__(self, item): - try: self.get_namespace(item) return True @@ -36,7 +35,6 @@ def __contains__(self, item): return False def __getitem__(self, item): - try: job = self.get_namespace(item) diff --git a/nomad/api/node.py b/nomad/api/node.py index 48c3faf..86a7462 100644 --- a/nomad/api/node.py +++ b/nomad/api/node.py @@ -1,4 +1,5 @@ """Nomad Node: https://developer.hashicorp.com/nomad/api-docs/nodes""" +from typing import Optional import nomad.api.exceptions from nomad.api.base import Requester @@ -28,7 +29,6 @@ def __getattr__(self, item): raise AttributeError(msg) def __contains__(self, item): - try: self.get_node(item) return True @@ -36,7 +36,6 @@ def __contains__(self, item): return False def __getitem__(self, item): - try: node = self.get_node(item) @@ -49,7 +48,7 @@ def __getitem__(self, item): except nomad.api.exceptions.URLNotFoundNomadException as exc: raise KeyError from exc - def get_node(self, id_): + def get_node(self, id_: str): """Query the status of a client node registered with Nomad. https://www.nomadproject.io/docs/http/node.html @@ -61,7 +60,7 @@ def get_node(self, id_): """ return self.request(id_, method="get").json() - def get_allocations(self, id_): + def get_allocations(self, id_: str): """Query the allocations belonging to a single node. https://www.nomadproject.io/docs/http/node.html @@ -73,7 +72,7 @@ def get_allocations(self, id_): """ return self.request(id_, "allocations", method="get").json() - def evaluate_node(self, id_): + def evaluate_node(self, id_: str): """Creates a new evaluation for the given node. This can be used to force run the scheduling logic if necessary. @@ -87,7 +86,7 @@ def evaluate_node(self, id_): """ return self.request(id_, "evaluate", method="post").json() - def drain_node(self, id_, enable=False): + def drain_node(self, id_, enable: bool = False): """Toggle the drain mode of the node. When enabled, no further allocations will be assigned and existing allocations will be migrated. @@ -105,7 +104,7 @@ def drain_node(self, id_, enable=False): return self.request(id_, "drain", params={"enable": enable}, method="post").json() - def drain_node_with_spec(self, id_, drain_spec, mark_eligible=None): + def drain_node_with_spec(self, id_, drain_spec: Optional[dict], mark_eligible: Optional[bool] = None): """This endpoint toggles the drain mode of the node. When draining is enabled, no further allocations will be assigned to this node, and existing allocations will be migrated to new nodes. @@ -139,7 +138,7 @@ def drain_node_with_spec(self, id_, drain_spec, mark_eligible=None): return self.request(id_, "drain", json=payload, method="post").json() - def eligible_node(self, id_, eligible=None, ineligible=None): + def eligible_node(self, id_: str, eligible: Optional[bool] = None, ineligible: Optional[bool] = None): """Toggle the eligibility of the node. https://www.nomadproject.io/docs/http/node.html @@ -171,7 +170,7 @@ def eligible_node(self, id_, eligible=None, ineligible=None): return self.request(id_, "eligibility", json=payload, method="post").json() - def purge_node(self, id_): + def purge_node(self, id_: str): """This endpoint purges a node from the system. Nodes can still join the cluster if they are alive. arguments: - id_ (str uuid): node id diff --git a/nomad/api/nodes.py b/nomad/api/nodes.py index 4b02392..28949e4 100644 --- a/nomad/api/nodes.py +++ b/nomad/api/nodes.py @@ -1,4 +1,6 @@ """Nomad Node: https://developer.hashicorp.com/nomad/api-docs/nodes""" +from typing import Optional + import nomad.api.exceptions from nomad.api.base import Requester @@ -61,7 +63,15 @@ def __iter__(self): nodes = self.get_nodes() return iter(nodes) - def get_nodes(self, prefix=None): + def get_nodes( # pylint: disable=too-many-arguments + self, + prefix: Optional[str] = None, + next_token: Optional[str] = None, + per_page: Optional[str] = None, + filter_: Optional[str] = None, + resources: Optional[bool] = None, + os: Optional[bool] = None, # pylint: disable=invalid-name + ): """Lists all the client nodes registered with Nomad. https://www.nomadproject.io/docs/http/nodes.html @@ -73,5 +83,12 @@ def get_nodes(self, prefix=None): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - params = {"prefix": prefix} + params = { + "prefix": prefix, + "next_token": next_token, + "per_page": per_page, + "filter_": filter_, + "resources": resources, + "os": os, + } return self.request(method="get", params=params).json() diff --git a/tests/test_allocations.py b/tests/test_allocations.py index b618786..186a41d 100644 --- a/tests/test_allocations.py +++ b/tests/test_allocations.py @@ -22,6 +22,9 @@ def test_get_allocations_prefix(nomad_setup): prefix = allocations[0]["ID"][:4] nomad_setup.allocations.get_allocations(prefix=prefix) +def test_get_allocations_resouces(nomad_setup): + allocations = nomad_setup.allocations.get_allocations(resources=True) + assert "AllocatedResources" in allocations[0] def test_dunder_str(nomad_setup): assert isinstance(str(nomad_setup.allocations), str) diff --git a/tests/test_nodes.py b/tests/test_nodes.py index 0214902..96087e4 100644 --- a/tests/test_nodes.py +++ b/tests/test_nodes.py @@ -1,16 +1,30 @@ import pytest +import os # integration tests requires nomad Vagrant VM or Binary running def test_get_nodes(nomad_setup): assert isinstance(nomad_setup.nodes.get_nodes(), list) == True - +def test_get_node(nomad_setup): + node = nomad_setup.nodes.get_nodes()[0] + print(node) + assert node["ID"] in nomad_setup.nodes def test_get_nodes_prefix(nomad_setup): nodes = nomad_setup.nodes.get_nodes() prefix = nodes[0]["ID"][:4] nomad_setup.nodes.get_nodes(prefix=prefix) - +def test_get_nodes_resouces(nomad_setup): + nodes = nomad_setup.nodes.get_nodes(resources=True) + print(nodes) + assert "NodeResources" in nodes[0] + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (1, 3, 0), reason="Not supported in version" +) +def test_get_nodes_os(nomad_setup): + nodes = nomad_setup.nodes.get_nodes(os=True) + assert "os.name" in nodes[0]["Attributes"] def test_dunder_getitem_exist(nomad_setup): n = nomad_setup.nodes["pynomad1"] From 5cc2beb64241408b23839f4b24a02af6d440af40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 18:43:27 +0200 Subject: [PATCH 53/69] Bump requests from 2.28.1 to 2.31.0 (#154) Bumps [requests](https://github.com/psf/requests) from 2.28.1 to 2.31.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.28.1...v2.31.0) --- updated-dependencies: - dependency-name: requests dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d15ce5a..2c24336 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -requests==2.28.1 +requests==2.31.0 From 6b9ebd61d720c84bab74d4decb7999342e36a7e3 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Tue, 23 May 2023 18:43:53 +0200 Subject: [PATCH 54/69] add nomad 1.5.x to tests (#151) * add nomad 1.5.x to tests * fix nomad file path * modern versions * fix * fix * fix * fix * fix * fix * fxi --- .github/workflows/main.yml | 14 +++++--------- run_tests.sh | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9a33440..c1de942 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,7 +25,7 @@ jobs: fail-fast: false matrix: python-version: ['3.7', '3.11'] # the oldest and newest support versions - nomad-version: ['1.0.18', '1.1.18', '1.2.15', '1.3.8', '1.4.3'] + nomad-version: ['1.0.18', '1.1.18', '1.2.15', '1.3.14', '1.4.9', '1.5.5'] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} @@ -43,11 +43,7 @@ jobs: curl -L -o /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip echo "unzip nomad" - unzip -d /tmp /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip - - echo "starting nomad server" - /tmp/nomad agent -dev -bind ${NOMAD_IP} -node pynomad1 --acl-enabled > /dev/null 2>&1 & - sleep 30 + unzip -d /usr/local/bin/ /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip - name: Install Dependencies shell: bash run: | @@ -57,14 +53,14 @@ jobs: - name: Before Tests shell: bash run: | - /tmp/nomad init - /tmp/nomad run -output example.nomad > example.json + nomad init example.nomad + nomad run -output example.nomad > example.json - name: Unit and Integration Tests env: NOMAD_VERSION: ${{ matrix.nomad-version }} shell: bash run: | - py.test --cov=nomad --cov-report=term-missing --runxfail tests/ + ./run_tests.sh - name: Upload coverage to Codecov uses: codecov/codecov-action@v2 - name: Create Package diff --git a/run_tests.sh b/run_tests.sh index 61c8cfa..0318587 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -6,9 +6,9 @@ set -ueo pipefail if [ "${1-}" == "init" ]; then virtualenv .venv pip install -r requirements-dev.txt + source .venv/bin/activate fi -source .venv/bin/activate NOMAD_VERSION=`nomad --version | awk '{print $2}' | cut -c2-` echo "Run Nomad in dev mode" From 2b02b70d63d97c16933adea58ee14796095be241 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Fri, 26 May 2023 17:06:42 +0200 Subject: [PATCH 55/69] 2.0.0 release (#155) --- CHANGELOG.md | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ac2a9d..5c452ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 2.0.0 (unreleased) +## 2.0.0 ### BREAKING CHANGES * Drop Python 2 and Python 3.6 support * Rename `id` arguments to `id_` across of code base diff --git a/setup.py b/setup.py index 5fddd91..88d6a72 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name='python-nomad', - version='1.5.0', + version='2.0.0', install_requires=['requests'], packages=['nomad', 'nomad.api'], url='http://github.com/jrxfive/python-nomad', From 41df8692ed3fca69ff92cf5e806ad969fe0e0ffa Mon Sep 17 00:00:00 2001 From: Ignacio Heredia Date: Fri, 2 Jun 2023 23:59:17 +0200 Subject: [PATCH 56/69] Update parameters in `Job` functions (#153) * update parameters in `deregister_job()` * update parameters in `get_allocations()` * update parameters in `get_evaluations()` * fix pylint warnings * fix black --- nomad/api/job.py | 106 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 28 deletions(-) diff --git a/nomad/api/job.py b/nomad/api/job.py index 03288db..b8beba8 100644 --- a/nomad/api/job.py +++ b/nomad/api/job.py @@ -1,7 +1,9 @@ """Nomad job: https://developer.hashicorp.com/nomad/api-docs/jobs""" -import nomad.api.exceptions + +from typing import Union from nomad.api.base import Requester +import nomad.api.exceptions class Job(Requester): @@ -81,33 +83,58 @@ def get_versions(self, id_): """ return self.request(id_, "versions", method="get").json() - def get_allocations(self, id_): + def get_allocations( + self, + id_: str, + all_: Union[bool, None] = None, + namespace: Union[str, None] = None, + ): """Query the allocations belonging to a single job. https://www.nomadproject.io/docs/http/job.html arguments: - - id_ + - id_ + - all (bool optional) + - namespace (str) optional. + Specifies the target namespace. If ACL is enabled, this value + must match a namespace that the token is allowed to access. + This is specified as a query string parameter. returns: list raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id_, "allocations", method="get").json() - - def get_evaluations(self, id_): + params = { + "all": all_, + "namespace": namespace, + } + return self.request(id_, "allocations", params=params, method="get").json() + + def get_evaluations( + self, + id_: str, + namespace: Union[str, None] = None, + ): """Query the evaluations belonging to a single job. https://www.nomadproject.io/docs/http/job.html arguments: - - id_ + - id_ + - namespace (str) optional. + Specifies the target namespace. If ACL is enabled, this value + must match a namespace that the token is allowed to access. + This is specified as a query string parameter. returns: dict raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException """ - return self.request(id_, "evaluations", method="get").json() + params = { + "namespace": namespace, + } + return self.request(id_, "evaluations", params=params, method="get").json() def get_deployments(self, id_): """This endpoint lists a single job's deployments @@ -252,7 +279,11 @@ def revert_job(self, id_, version, enforce_prior_version=None): - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - revert_json = {"JobID": id_, "JobVersion": version, "EnforcePriorVersion": enforce_prior_version} + revert_json = { + "JobID": id_, + "JobVersion": version, + "EnforcePriorVersion": enforce_prior_version, + } return self.request(id_, "revert", json=revert_json, method="post").json() def stable_job(self, id_, version, stable): @@ -272,28 +303,47 @@ def stable_job(self, id_, version, stable): revert_json = {"JobID": id_, "JobVersion": version, "Stable": stable} return self.request(id_, "stable", json=revert_json, method="post").json() - def deregister_job(self, id_, purge=None): + def deregister_job( + self, + id_: str, + eval_priority: Union[int, None] = None, + global_: Union[bool, None] = None, + namespace: Union[str, None] = None, + purge: Union[bool, None] = None, + ): # pylint: disable=too-many-arguments """Deregisters a job, and stops all allocations part of it. https://www.nomadproject.io/docs/http/job.html arguments: - - id_ - - purge (bool), optionally specifies whether the job should be - stopped and purged immediately (`purge=True`) or deferred to the - Nomad garbage collector (`purge=False`). + - id_ + - eval_priority (int) optional. + Override the priority of the evaluations produced as a result + of this job deregistration. By default, this is set to the + priority of the job. + - global_ (bool) optional. + Stop a multi-region job in all its regions. By default, job + stop will stop only a single region at a time. Ignored for + single-region jobs. + - purge (bool) optional. + Specifies that the job should be stopped and purged immediately. + This means the job will not be queryable after being stopped. + If not set, the job will be purged by the garbage collector. + - namespace (str) optional. + Specifies the target namespace. If ACL is enabled, this value + must match a namespace that the token is allowed to access. + This is specified as a query string parameter. returns: dict raises: - - nomad.api.exceptions.BaseNomadException - - nomad.api.exceptions.URLNotFoundNomadException - - nomad.api.exceptions.InvalidParameters + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + - nomad.api.exceptions.InvalidParameters """ - params = None - if purge is not None: - if not isinstance(purge, bool): - raise nomad.api.exceptions.InvalidParameters( - "purge is invalid " f"(expected type {type(bool())} but got {type(purge)})" - ) - params = {"purge": purge} + params = { + "eval_priority": eval_priority, + "global": global_, + "namespace": namespace, + "purge": purge, + } return self.request(id_, params=params, method="delete").json() From d5d5c4c7b383eea87030d95211f43f81d778d38b Mon Sep 17 00:00:00 2001 From: Bernardo Vale Date: Mon, 22 Apr 2024 16:50:59 -0300 Subject: [PATCH 57/69] fix node filter (#162) --- nomad/api/nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nomad/api/nodes.py b/nomad/api/nodes.py index 28949e4..444f32e 100644 --- a/nomad/api/nodes.py +++ b/nomad/api/nodes.py @@ -87,7 +87,7 @@ def get_nodes( # pylint: disable=too-many-arguments "prefix": prefix, "next_token": next_token, "per_page": per_page, - "filter_": filter_, + "filter": filter_, "resources": resources, "os": os, } From 7239fecd6a451f654642ba593fd02f4371307815 Mon Sep 17 00:00:00 2001 From: Davide Villa Date: Mon, 22 Apr 2024 15:51:59 -0400 Subject: [PATCH 58/69] Fix errror in "Namespace" docs (#161) The json to create or update a namespace is wrong, since it needs "Name", not "Namespace". --- docs/api/namespace.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/namespace.md b/docs/api/namespace.md index 52d126c..57ea1ed 100644 --- a/docs/api/namespace.md +++ b/docs/api/namespace.md @@ -18,7 +18,7 @@ import nomad my_nomad = nomad.Nomad(host='192.168.33.10') namespace = { - "Namespace": "api-prod", + "Name": "api-prod", "Description": "Production API Servers" } my_nomad.namespace.create_namespace(namespace) @@ -36,7 +36,7 @@ import nomad my_nomad = nomad.Nomad(host='192.168.33.10') namespace = { - "Namespace": "api-prod", + "Name": "api-prod", "Description": "Production API Servers" } my_nomad.namespace.create_namespace(namespace) @@ -79,7 +79,7 @@ import nomad my_nomad = nomad.Nomad(host='192.168.33.10') namespace = { - "Namespace": "api-prod", + "Name": "api-prod", "Description": "Production API Servers" } my_nomad.namespace.create_namespace("api-prod", namespace) From 35556fa3ea5787ded6e6991157a9676d8c62f484 Mon Sep 17 00:00:00 2001 From: Atwam Date: Mon, 22 Apr 2024 21:01:04 +0100 Subject: [PATCH 59/69] Only read env variables on __init__ (#157) * run_tests.sh: Handle multi-line nomad --version output * Nomad(): Evaluate env variables on __init__() call, not before --------- Co-authored-by: Damien Couture --- nomad/__init__.py | 20 +++++++++++--------- run_tests.sh | 2 +- tests/test_base.py | 7 +++++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/nomad/__init__.py b/nomad/__init__.py index fdb4f16..2371b61 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -17,15 +17,15 @@ def __init__( # pylint: disable=too-many-arguments host: str = "127.0.0.1", secure: bool = False, port: int = 4646, - address: Optional[str] = os.getenv("NOMAD_ADDR", None), + address: Optional[str] = None, user_agent: Optional[str] = None, - namespace: Optional[str] = os.getenv("NOMAD_NAMESPACE", None), - token: Optional[str] = os.getenv("NOMAD_TOKEN", None), + namespace: Optional[str] = None, + token: Optional[str] = None, timeout: int = 5, - region: Optional[str] = os.getenv("NOMAD_REGION", None), + region: Optional[str] = None, version: str = "v1", verify: bool = False, - cert: tuple = (os.getenv("NOMAD_CLIENT_CERT", None), os.getenv("NOMAD_CLIENT_KEY", None)), + cert: tuple = (), session: requests.Session = None, ): """Nomad api client @@ -57,19 +57,21 @@ def __init__( # pylint: disable=too-many-arguments - nomad.api.exceptions.URLNotFoundNomadException - nomad.api.exceptions.URLNotAuthorizedNomadException """ + cert = cert or (os.getenv("NOMAD_CLIENT_CERT", None), os.getenv("NOMAD_CLIENT_KEY", None)) + self.host = host self.secure = secure self.port = port - self.address = address + self.address = address or os.getenv("NOMAD_ADDR", None) self.user_agent = user_agent - self.region = region + self.region = region or os.getenv("NOMAD_REGION", None) self.timeout = timeout self.version = version - self.token = token + self.token = token or os.getenv("NOMAD_TOKEN", None) self.verify = verify self.cert = cert if all(cert) else () self.session = session - self.__namespace = namespace + self.__namespace = namespace or os.getenv("NOMAD_NAMESPACE", None) self.requester_settings = { "address": self.address, diff --git a/run_tests.sh b/run_tests.sh index 0318587..eea22cf 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -9,7 +9,7 @@ if [ "${1-}" == "init" ]; then source .venv/bin/activate fi -NOMAD_VERSION=`nomad --version | awk '{print $2}' | cut -c2-` +NOMAD_VERSION=`nomad --version | awk 'NR==1{print $2}' | cut -c2-` echo "Run Nomad in dev mode" nomad agent -dev -node pynomad1 --acl-enabled &> nomad.log & diff --git a/tests/test_base.py b/tests/test_base.py index d1501a0..df8f44a 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -161,3 +161,10 @@ def test_use_custom_user_agent(): n.jobs.get_jobs() assert responses.calls[0].request.headers["User-Agent"] == custom_agent_name + +def test_env_variables(): + # This ensures that the env variables are only read upon initialization of Nomad() instance, + # and not before. + with mock.patch.dict(os.environ, {"NOMAD_ADDR": "https://foo"}): + n = nomad.Nomad() + assert n.address == os.environ["NOMAD_ADDR"] From ad83fff90a3a235faf958e71c0cb0cd003a9b5a2 Mon Sep 17 00:00:00 2001 From: j-aub <37088847+j-aub@users.noreply.github.com> Date: Mon, 22 Apr 2024 16:01:46 -0400 Subject: [PATCH 60/69] include event api docs (#160) --- docs/index.md | 3 ++- mkdocs.yml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index de10bc5..af1fad4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -57,6 +57,7 @@ NOMAD_REGION=us-east-1a |client|N|N|N|N |evaluation|Y|N|Y|N |evaluations|Y|Y|Y|Y +|event|N|N|N|N |job|Y|N|Y|N |jobs|Y|Y|Y|Y |node|Y|N|Y|N @@ -88,4 +89,4 @@ pip install -r requirements-dev.txt ``` vagrant up --provider virtualbox py.test --cov=nomad --cov-report=term-missing --runxfail tests/ -``` \ No newline at end of file +``` diff --git a/mkdocs.yml b/mkdocs.yml index 7f7575b..cf1f47a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -16,6 +16,7 @@ pages: - Deployments: 'api/deployments.md' - Evaluations: 'api/evaluations.md' - Evaluation: 'api/evaluation.md' + - Event: 'api/event.md' - Job: 'api/job.md' - Jobs: 'api/jobs.md' - Metrics: 'api/metrics.md' From 2aca1a4e82358745cb502793bea9c1c177a883e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 16:35:55 -0400 Subject: [PATCH 61/69] Bump black from 22.12.0 to 24.3.0 and fix format (#163) * Bump black from 22.12.0 to 24.3.0 Bumps [black](https://github.com/psf/black) from 22.12.0 to 24.3.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/22.12.0...24.3.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:production ... Signed-off-by: dependabot[bot] * fix syntax * upgrade to the latest version * fix syntax more * fix --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Nikita Beletskii Co-authored-by: Nikita Beletskii <2nikita.b@gmail.com> --- nomad/__init__.py | 6 +++++- nomad/api/__init__.py | 1 + nomad/api/agent.py | 1 + nomad/api/allocation.py | 1 + nomad/api/allocations.py | 1 + nomad/api/base.py | 1 + nomad/api/client.py | 15 ++++++++------- nomad/api/deployment.py | 2 +- nomad/api/deployments.py | 1 + nomad/api/evaluation.py | 1 + nomad/api/evaluations.py | 1 + nomad/api/event.py | 1 + nomad/api/exceptions.py | 1 + nomad/api/jobs.py | 6 +++++- nomad/api/metrics.py | 1 + nomad/api/namespace.py | 1 + nomad/api/namespaces.py | 1 + nomad/api/node.py | 14 ++++++++++++-- nomad/api/nodes.py | 1 + nomad/api/operator.py | 1 + nomad/api/regions.py | 1 + nomad/api/scaling.py | 1 + nomad/api/search.py | 12 +++++++++++- nomad/api/sentinel.py | 1 + nomad/api/status.py | 1 + nomad/api/system.py | 1 + nomad/api/validate.py | 2 +- nomad/api/variable.py | 1 + nomad/api/variables.py | 1 + requirements-lint.txt | 2 +- 30 files changed, 66 insertions(+), 15 deletions(-) diff --git a/nomad/__init__.py b/nomad/__init__.py index 2371b61..406d2db 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -1,4 +1,5 @@ """Nomad Python library""" + import os from typing import Optional @@ -57,7 +58,10 @@ def __init__( # pylint: disable=too-many-arguments - nomad.api.exceptions.URLNotFoundNomadException - nomad.api.exceptions.URLNotAuthorizedNomadException """ - cert = cert or (os.getenv("NOMAD_CLIENT_CERT", None), os.getenv("NOMAD_CLIENT_KEY", None)) + cert = cert or ( + os.getenv("NOMAD_CLIENT_CERT", None), + os.getenv("NOMAD_CLIENT_KEY", None), + ) self.host = host self.secure = secure diff --git a/nomad/api/__init__.py b/nomad/api/__init__.py index 3886570..0c3f17e 100644 --- a/nomad/api/__init__.py +++ b/nomad/api/__init__.py @@ -1,4 +1,5 @@ """Nomad Python library""" + import nomad.api.exceptions from nomad.api.acl import Acl from nomad.api.agent import Agent diff --git a/nomad/api/agent.py b/nomad/api/agent.py index 7ad412c..c62babe 100644 --- a/nomad/api/agent.py +++ b/nomad/api/agent.py @@ -1,4 +1,5 @@ """Nomad Agent: https://developer.hashicorp.com/nomad/api-docs/agent""" + from nomad.api.base import Requester diff --git a/nomad/api/allocation.py b/nomad/api/allocation.py index 87d64d9..9e9da20 100644 --- a/nomad/api/allocation.py +++ b/nomad/api/allocation.py @@ -1,4 +1,5 @@ """Nomad allocation: https://developer.hashicorp.com/nomad/api-docs/allocations""" + import nomad.api.exceptions from nomad.api.base import Requester diff --git a/nomad/api/allocations.py b/nomad/api/allocations.py index 128b072..920beaa 100644 --- a/nomad/api/allocations.py +++ b/nomad/api/allocations.py @@ -1,4 +1,5 @@ """Nomad allocation: https://developer.hashicorp.com/nomad/api-docs/allocations""" + from typing import Optional from nomad.api.base import Requester diff --git a/nomad/api/base.py b/nomad/api/base.py index 87f3714..209899b 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -1,4 +1,5 @@ """Requester""" + from typing import Optional import requests diff --git a/nomad/api/client.py b/nomad/api/client.py index cefbd33..ebf5991 100644 --- a/nomad/api/client.py +++ b/nomad/api/client.py @@ -33,7 +33,6 @@ def __getattr__(self, item): class ls(Requester): - """ The /fs/ls endpoint is used to list files in an allocation directory. This API endpoint is hosted by the Nomad client and requests have to be @@ -67,7 +66,6 @@ def list_files(self, id_=None, path="/"): class cat(Requester): - """ The /fs/cat endpoint is used to read the contents of a file in an allocation directory. This API endpoint is hosted by the Nomad @@ -102,7 +100,6 @@ def read_file(self, id_=None, path="/"): class read_at(Requester): - """ This endpoint reads the contents of a file in an allocation directory at a particular offset and limit. @@ -134,7 +131,6 @@ def read_file_offset(self, id_, offset, limit, path="/"): class stream_file(Requester): - """ This endpoint streams the contents of a file in an allocation directory. @@ -166,7 +162,6 @@ def stream(self, id_, offset, origin, path="/"): class stream_logs(Requester): - """ This endpoint streams a task's stderr/stdout logs. @@ -196,7 +191,14 @@ def stream(self, id_, task, type_, follow=False, offset=0, origin="start", plain - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.BadRequestNomadException """ - params = {"task": task, "type": type_, "follow": follow, "offset": offset, "origin": origin, "plain": plain} + params = { + "task": task, + "type": type_, + "follow": follow, + "offset": offset, + "origin": origin, + "plain": plain, + } return self.request(id_, params=params, method="get").text @@ -304,7 +306,6 @@ def restart_allocation(self, id_): class gc_allocation(Requester): - """ This endpoint forces a garbage collection of a particular, stopped allocation on a node. diff --git a/nomad/api/deployment.py b/nomad/api/deployment.py index 2b2432c..ff7ac3b 100644 --- a/nomad/api/deployment.py +++ b/nomad/api/deployment.py @@ -1,11 +1,11 @@ """Nomad Deployment: https://developer.hashicorp.com/nomad/api-docs/deployments""" + import nomad.api.exceptions from nomad.api.base import Requester class Deployment(Requester): - """ The /deployment endpoints are used to query for and interact with deployments. diff --git a/nomad/api/deployments.py b/nomad/api/deployments.py index 91dd335..54d1012 100644 --- a/nomad/api/deployments.py +++ b/nomad/api/deployments.py @@ -1,4 +1,5 @@ """Nomad Deployment: https://developer.hashicorp.com/nomad/api-docs/deployments""" + import nomad.api.exceptions from nomad.api.base import Requester diff --git a/nomad/api/evaluation.py b/nomad/api/evaluation.py index 2f1546a..6f8fcad 100644 --- a/nomad/api/evaluation.py +++ b/nomad/api/evaluation.py @@ -1,4 +1,5 @@ """Nomad Evaluation: https://developer.hashicorp.com/nomad/api-docs/evaluations""" + import nomad.api.exceptions from nomad.api.base import Requester diff --git a/nomad/api/evaluations.py b/nomad/api/evaluations.py index dae0f61..f91c9d2 100644 --- a/nomad/api/evaluations.py +++ b/nomad/api/evaluations.py @@ -1,4 +1,5 @@ """Nomad Evaluations: https://developer.hashicorp.com/nomad/api-docs/evaluations""" + import nomad.api.exceptions from nomad.api.base import Requester diff --git a/nomad/api/event.py b/nomad/api/event.py index cafadb2..e812b42 100644 --- a/nomad/api/event.py +++ b/nomad/api/event.py @@ -1,4 +1,5 @@ """Nomad Events: https://developer.hashicorp.com/nomad/api-docs/events""" + import json import threading import queue diff --git a/nomad/api/exceptions.py b/nomad/api/exceptions.py index 578c16f..dcc0d66 100644 --- a/nomad/api/exceptions.py +++ b/nomad/api/exceptions.py @@ -1,4 +1,5 @@ """Internal library exceptions""" + import requests diff --git a/nomad/api/jobs.py b/nomad/api/jobs.py index c57f31c..1b3488c 100644 --- a/nomad/api/jobs.py +++ b/nomad/api/jobs.py @@ -1,4 +1,5 @@ """Nomad job: https://developer.hashicorp.com/nomad/api-docs/jobs""" + from typing import Optional import nomad.api.exceptions @@ -119,5 +120,8 @@ def parse(self, hcl, canonicalize=False): - nomad.api.exceptions.URLNotFoundNomadException """ return self.request( - "parse", json={"JobHCL": hcl, "Canonicalize": canonicalize}, method="post", allow_redirects=True + "parse", + json={"JobHCL": hcl, "Canonicalize": canonicalize}, + method="post", + allow_redirects=True, ).json() diff --git a/nomad/api/metrics.py b/nomad/api/metrics.py index 0575eff..acc5fab 100644 --- a/nomad/api/metrics.py +++ b/nomad/api/metrics.py @@ -1,4 +1,5 @@ """Nomad Metrics: https://developer.hashicorp.com/nomad/api-docs/metrics""" + from nomad.api.base import Requester diff --git a/nomad/api/namespace.py b/nomad/api/namespace.py index 4044e5b..66f096c 100644 --- a/nomad/api/namespace.py +++ b/nomad/api/namespace.py @@ -1,4 +1,5 @@ """Nomad namespace: https://developer.hashicorp.com/nomad/api-docs/namespaces""" + import nomad.api.exceptions from nomad.api.base import Requester diff --git a/nomad/api/namespaces.py b/nomad/api/namespaces.py index a87108f..6017016 100644 --- a/nomad/api/namespaces.py +++ b/nomad/api/namespaces.py @@ -1,4 +1,5 @@ """Nomad namespace: https://developer.hashicorp.com/nomad/api-docs/namespaces""" + import nomad.api.exceptions from nomad.api.base import Requester diff --git a/nomad/api/node.py b/nomad/api/node.py index 86a7462..6b4099c 100644 --- a/nomad/api/node.py +++ b/nomad/api/node.py @@ -1,4 +1,5 @@ """Nomad Node: https://developer.hashicorp.com/nomad/api-docs/nodes""" + from typing import Optional import nomad.api.exceptions @@ -125,7 +126,11 @@ def drain_node_with_spec(self, id_, drain_spec: Optional[dict], mark_eligible: O payload = {} if drain_spec and mark_eligible is not None: - payload = {"NodeID": id_, "DrainSpec": drain_spec, "MarkEligible": mark_eligible} + payload = { + "NodeID": id_, + "DrainSpec": drain_spec, + "MarkEligible": mark_eligible, + } elif drain_spec and mark_eligible is None: payload = {"NodeID": id_, "DrainSpec": drain_spec} elif not drain_spec and mark_eligible is not None: @@ -138,7 +143,12 @@ def drain_node_with_spec(self, id_, drain_spec: Optional[dict], mark_eligible: O return self.request(id_, "drain", json=payload, method="post").json() - def eligible_node(self, id_: str, eligible: Optional[bool] = None, ineligible: Optional[bool] = None): + def eligible_node( + self, + id_: str, + eligible: Optional[bool] = None, + ineligible: Optional[bool] = None, + ): """Toggle the eligibility of the node. https://www.nomadproject.io/docs/http/node.html diff --git a/nomad/api/nodes.py b/nomad/api/nodes.py index 444f32e..654fde9 100644 --- a/nomad/api/nodes.py +++ b/nomad/api/nodes.py @@ -1,4 +1,5 @@ """Nomad Node: https://developer.hashicorp.com/nomad/api-docs/nodes""" + from typing import Optional import nomad.api.exceptions diff --git a/nomad/api/operator.py b/nomad/api/operator.py index 8fa2146..777d5d8 100644 --- a/nomad/api/operator.py +++ b/nomad/api/operator.py @@ -1,4 +1,5 @@ """Nomad Operator: https://developer.hashicorp.com/nomad/api-docs/operator""" + from nomad.api.base import Requester diff --git a/nomad/api/regions.py b/nomad/api/regions.py index bc89989..0b22a0d 100644 --- a/nomad/api/regions.py +++ b/nomad/api/regions.py @@ -1,4 +1,5 @@ """Nomad region: https://developer.hashicorp.com/nomad/api-docs/regions""" + import nomad.api.exceptions from nomad.api.base import Requester diff --git a/nomad/api/scaling.py b/nomad/api/scaling.py index a05fa8a..3adf426 100644 --- a/nomad/api/scaling.py +++ b/nomad/api/scaling.py @@ -1,4 +1,5 @@ """Nomad Scalling API: https://developer.hashicorp.com/nomad/api-docs/scaling-policies""" + import nomad.api.exceptions from nomad.api.base import Requester diff --git a/nomad/api/search.py b/nomad/api/search.py index e373ee8..e104e41 100644 --- a/nomad/api/search.py +++ b/nomad/api/search.py @@ -1,4 +1,5 @@ """Nomad Search API: https://developer.hashicorp.com/nomad/api-docs/search""" + import nomad.api.exceptions from nomad.api.base import Requester @@ -46,7 +47,16 @@ def search(self, prefix, context): - nomad.api.exceptions.URLNotFoundNomadException - nomad.api.exceptions.InvalidParameters """ - accetaple_contexts = ("jobs", "evals", "allocs", "nodes", "deployment", "plugins", "volumes", "all") + accetaple_contexts = ( + "jobs", + "evals", + "allocs", + "nodes", + "deployment", + "plugins", + "volumes", + "all", + ) if context not in accetaple_contexts: raise nomad.api.exceptions.InvalidParameters( "context is invalid " f"(expected values are {accetaple_contexts} but got {context})" diff --git a/nomad/api/sentinel.py b/nomad/api/sentinel.py index eaf7c20..ef2a0f0 100644 --- a/nomad/api/sentinel.py +++ b/nomad/api/sentinel.py @@ -1,4 +1,5 @@ """Nomad Sentinel API: https://developer.hashicorp.com/nomad/api-docs/sentinel-policies""" + from nomad.api.base import Requester diff --git a/nomad/api/status.py b/nomad/api/status.py index 85d8a42..db52510 100644 --- a/nomad/api/status.py +++ b/nomad/api/status.py @@ -1,4 +1,5 @@ """Nomad Status API: https://developer.hashicorp.com/nomad/api-docs/status""" + import nomad.api.exceptions from nomad.api.base import Requester diff --git a/nomad/api/system.py b/nomad/api/system.py index 4255ab4..daf119f 100644 --- a/nomad/api/system.py +++ b/nomad/api/system.py @@ -1,4 +1,5 @@ """Nomad System API: https://developer.hashicorp.com/nomad/api-docs/system""" + from nomad.api.base import Requester diff --git a/nomad/api/validate.py b/nomad/api/validate.py index ea17fda..5372905 100644 --- a/nomad/api/validate.py +++ b/nomad/api/validate.py @@ -1,9 +1,9 @@ """Nomad Validate API: https://developer.hashicorp.com/nomad/api-docs/validate""" + from nomad.api.base import Requester class Validate(Requester): - """ The system endpoint is used to for system maintenance and should not be necessary for most users. diff --git a/nomad/api/variable.py b/nomad/api/variable.py index 7df2446..e4bcdf1 100644 --- a/nomad/api/variable.py +++ b/nomad/api/variable.py @@ -1,4 +1,5 @@ """Nomad Valiables API: https://developer.hashicorp.com/nomad/api-docs/variables""" + import nomad.api.exceptions from nomad.api.base import Requester diff --git a/nomad/api/variables.py b/nomad/api/variables.py index d75f482..2d9d972 100644 --- a/nomad/api/variables.py +++ b/nomad/api/variables.py @@ -1,4 +1,5 @@ """Nomad Valiables API: https://developer.hashicorp.com/nomad/api-docs/variables""" + from nomad.api.base import Requester diff --git a/requirements-lint.txt b/requirements-lint.txt index ad9fd7e..2855e0a 100644 --- a/requirements-lint.txt +++ b/requirements-lint.txt @@ -1,2 +1,2 @@ pylint==2.15.10 -black==22.12.0 \ No newline at end of file +black==24.4.0 \ No newline at end of file From 816100effd5634980703bac0cc7aae0f867f8303 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Mon, 22 Apr 2024 18:38:47 -0400 Subject: [PATCH 62/69] add release workflow (#156) * add release workflow * remove merge request trigger --- .github/workflows/main.yml | 15 +-------------- .github/workflows/publish.yml | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c1de942..4d49f39 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,8 +10,6 @@ on: branches: [ master ] registry_package: types: [published] - release: - types: [created, published, released] workflow_dispatch: jobs: @@ -62,15 +60,4 @@ jobs: run: | ./run_tests.sh - name: Upload coverage to Codecov - uses: codecov/codecov-action@v2 - - name: Create Package - shell: bash - run: | - python -m pip install build - python -m build --sdist --wheel - - name: Publish/Release Package - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} + uses: codecov/codecov-action@v3 \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..9fcc9ed --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,27 @@ +name: Publish + +on: + release: + types: + - created + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: 3.11 + - name: Install Dependencies + shell: bash + run: pip install build + - name: Create Package + shell: bash + run: python -m build --sdist --wheel + - name: Publish/Release Package + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} \ No newline at end of file From 17df29470bc358c4bf094083c620c6c4432d9237 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Tue, 23 Apr 2024 15:11:17 -0400 Subject: [PATCH 63/69] Prepare 2.0.1 release (#164) * create 2.0.1 release * add CHANGELOG --- CHANGELOG.md | 7 +++++++ setup.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c452ec..56ebcdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +**Changes of the next versions will be published in the [releases](https://github.com/jrxFive/python-nomad/releases) section of the repository** + +## 2.0.1 +* update parameters in `Job` functions by @IgnacioHeredia #153 +* Fix nodes API filter by @bernardoVale #162 +* Read env variables on __init__ by @atwam #157 + ## 2.0.0 ### BREAKING CHANGES * Drop Python 2 and Python 3.6 support diff --git a/setup.py b/setup.py index 88d6a72..1baaee4 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name='python-nomad', - version='2.0.0', + version='2.0.1', install_requires=['requests'], packages=['nomad', 'nomad.api'], url='http://github.com/jrxfive/python-nomad', From 060ced3406bc60e0c8afaae14b13e9271a791ed6 Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Tue, 23 Apr 2024 17:03:39 -0400 Subject: [PATCH 64/69] add latest Nomad and Python to tests (#165) * add latest Nomad and Python to tests * upgrade pylint --- .github/workflows/lint.yml | 4 ++-- .github/workflows/main.yml | 10 +++++----- .github/workflows/publish.yml | 8 ++++---- requirements-lint.txt | 2 +- setup.py | 1 + 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 095b56c..cb822a0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Python 3 uses: actions/setup-python@v4 with: - python-version: '3.11' + python-version: '3.12' - name: Install Dependencies shell: bash @@ -29,4 +29,4 @@ jobs: shell: bash run: | pylint nomad/ - black --check -l 120 -t py311 nomad/ + black --check -l 120 -t py312 nomad/ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4d49f39..9bcdfe9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,12 +22,12 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.7', '3.11'] # the oldest and newest support versions - nomad-version: ['1.0.18', '1.1.18', '1.2.15', '1.3.14', '1.4.9', '1.5.5'] + python-version: ['3.7', '3.12'] # the oldest and newest support versions + nomad-version: ['1.2.16', '1.3.16', '1.4.14', '1.5.17', '1.6.10', '1.7.7'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Setup Nomad ${{ matrix.nomad-version }} @@ -60,4 +60,4 @@ jobs: run: | ./run_tests.sh - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 \ No newline at end of file + uses: codecov/codecov-action@v4 \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9fcc9ed..b40fa75 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,11 +9,11 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.11 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: 3.11 + python-version: 3.12 - name: Install Dependencies shell: bash run: pip install build diff --git a/requirements-lint.txt b/requirements-lint.txt index 2855e0a..5d0c3b3 100644 --- a/requirements-lint.txt +++ b/requirements-lint.txt @@ -1,2 +1,2 @@ -pylint==2.15.10 +pylint==3.1.0 black==24.4.0 \ No newline at end of file diff --git a/setup.py b/setup.py index 1baaee4..9c97309 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', ], keywords='nomad hashicorp client', ) From 79ae9d9a42d0b24b35f0df936eca53e42137b0d5 Mon Sep 17 00:00:00 2001 From: aas47 <179645738+aas47@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:21:10 -0500 Subject: [PATCH 65/69] feat: Update dispatch jobs to support `templateprefix` and `idempotencytoken` (#171) * Update `dispatch_jobs` to support `idempotency_token` and `IdPrefixTemplate` * bump action versions * black formatting --- .github/workflows/lint.yml | 6 ++--- .github/workflows/main.yml | 10 +++++--- nomad/api/job.py | 18 ++++++++++++-- tests/test_job.py | 51 +++++++++++++++----------------------- 4 files changed, 45 insertions(+), 40 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cb822a0..ee8f1b9 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,12 +12,12 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '3.13' - name: Install Dependencies shell: bash diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9bcdfe9..52f78cb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,8 +22,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.7', '3.12'] # the oldest and newest support versions - nomad-version: ['1.2.16', '1.3.16', '1.4.14', '1.5.17', '1.6.10', '1.7.7'] + python-version: ['3.8', '3.13'] # the oldest and newest support versions + nomad-version: ['1.4.14', '1.5.17', '1.6.10', '1.7.7', '1.8.4', '1.9.5'] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} @@ -41,7 +41,9 @@ jobs: curl -L -o /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip echo "unzip nomad" - unzip -d /usr/local/bin/ /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip + unzip -o -d /usr/local/bin/ /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip + chmod +x /usr/local/bin/nomad + /usr/local/bin/nomad version - name: Install Dependencies shell: bash run: | @@ -60,4 +62,4 @@ jobs: run: | ./run_tests.sh - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 \ No newline at end of file + uses: codecov/codecov-action@v5 diff --git a/nomad/api/job.py b/nomad/api/job.py index b8beba8..5ba2502 100644 --- a/nomad/api/job.py +++ b/nomad/api/job.py @@ -245,7 +245,14 @@ def periodic_job(self, id_): """ return self.request(id_, "periodic", "force", method="post").json() - def dispatch_job(self, id_, payload=None, meta=None): + def dispatch_job( + self, + id_, + payload=None, + meta=None, + id_prefix_template=None, + idempotency_token=None, + ): # pylint: disable=too-many-arguments """Dispatches a new instance of a parameterized job. https://www.nomadproject.io/docs/http/job.html @@ -254,12 +261,19 @@ def dispatch_job(self, id_, payload=None, meta=None): - id_ - payload - meta + - id_prefix_template + - idempotency_token returns: dict raises: - nomad.api.exceptions.BaseNomadException - nomad.api.exceptions.URLNotFoundNomadException """ - dispatch_json = {"Meta": meta, "Payload": payload} + dispatch_json = { + "Meta": meta, + "Payload": payload, + "idempotency_token": idempotency_token, + "IdPrefixTemplate": id_prefix_template, + } return self.request(id_, "dispatch", json=dispatch_json, method="post").json() def revert_job(self, id_, version, enforce_prior_version=None): diff --git a/tests/test_job.py b/tests/test_job.py index 486a101..ae1eb36 100644 --- a/tests/test_job.py +++ b/tests/test_job.py @@ -125,56 +125,57 @@ def test_delete_job(nomad_setup): @flaky(max_runs=5, min_passes=1) -@pytest.mark.skipif( - tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 3), reason="Nomad dispatch not supported" -) def test_dispatch_job(nomad_setup): with open("example_batch_parameterized.json") as fh: job = json.loads(fh.read()) nomad_setup.job.register_job("example-batch", job) try: - nomad_setup.job.dispatch_job("example-batch", meta={"time": "500"}) + nomad_setup.job.dispatch_job("example-batch", meta={"time": "500"}, id_prefix_template="run1") except (exceptions.URLNotFoundNomadException, exceptions.BaseNomadException) as e: print(e.nomad_resp.text) raise e assert "example-batch" in nomad_setup.job -@pytest.mark.skipif( - tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 3), reason="Nomad dispatch not supported" -) +@flaky(max_runs=5, min_passes=1) +def test_dispatch_job_idempotency(nomad_setup): + with open("example_batch_parameterized.json") as fh: + job = json.loads(fh.read()) + nomad_setup.job.register_job("example-batch-idempotent", job) + + # First dispatch should succeed + try: + nomad_setup.job.dispatch_job("example-batch-idempotent", meta={"time": "500"}, id_prefix_template="run1", idempotency_token="737ae8cd-f237-43a5-8fad-0e6a3f94ad55") + except (exceptions.URLNotFoundNomadException, exceptions.BaseNomadException) as e: + print(e.nomad_resp.text) + raise e + assert "example-batch-idempotent" in nomad_setup.job + + # Second dispatch with the same idempotency token should fail + with pytest.raises(exceptions.BaseNomadException): + nomad_setup.job.dispatch_job("example-batch-idempotent", meta={"time": "500"}, id_prefix_template="run2", idempotency_token="737ae8cd-f237-43a5-8fad-0e6a3f94ad55") + + def test_summary_job(nomad_setup): j = nomad_setup.job["example"] assert "JobID" in nomad_setup.job.get_summary(j["ID"]) -@pytest.mark.skipif( - tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 4, 0), reason="Not supported in version" -) def test_plan_job(nomad_setup): with open("example.json") as fh: job = json.loads(fh.read()) assert "Index" in nomad_setup.job.plan_job(nomad_setup.job["example"]["ID"], job) -@pytest.mark.skipif( - tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" -) def test_versions_job(nomad_setup): assert "Versions" in nomad_setup.job.get_versions("example") -@pytest.mark.skipif( - tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" -) def test_versions_job_missing(nomad_setup): with pytest.raises(nomad.api.exceptions.URLNotFoundNomadException): assert "Versions" in nomad_setup.job.get_versions("example1") -@pytest.mark.skipif( - tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" -) def test_get_job_deployments(nomad_setup): assert "JobID" in nomad_setup.job.get_deployments("example")[0] assert isinstance(nomad_setup.job.get_deployments("example"), list) @@ -182,36 +183,24 @@ def test_get_job_deployments(nomad_setup): assert "example" == nomad_setup.job.get_deployments("example")[0]["JobID"] -@pytest.mark.skipif( - tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" -) def test_get_job_deployment(nomad_setup): assert "JobID" in nomad_setup.job.get_deployment("example") assert isinstance(nomad_setup.job.get_deployment("example"), dict) assert "example" == nomad_setup.job.get_deployment("example")["JobID"] -@pytest.mark.skipif( - tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" -) def test_get_summary(nomad_setup): assert "JobID" in nomad_setup.job.get_summary("example") assert isinstance(nomad_setup.job.get_summary("example"), dict) assert "example" == nomad_setup.job.get_summary("example")["JobID"] -@pytest.mark.skipif( - tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" -) def test_revert_job(nomad_setup): current_job_version = nomad_setup.job.get_deployment("example")["JobVersion"] prior_job_version = current_job_version - 1 nomad_setup.job.revert_job("example", prior_job_version, current_job_version) -@pytest.mark.skipif( - tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version" -) def test_stable_job(nomad_setup): current_job_version = nomad_setup.job.get_deployment("example")["JobVersion"] nomad_setup.job.stable_job("example", current_job_version, True) From 1befcb46eefb7b0bc3718f879f1ab599c156d0d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:36:46 -0500 Subject: [PATCH 66/69] Bump requests from 2.31.0 to 2.32.2 (#172) Bumps [requests](https://github.com/psf/requests) from 2.31.0 to 2.32.2. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.31.0...v2.32.2) --- updated-dependencies: - dependency-name: requests dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2c24336..6e42168 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -requests==2.31.0 +requests==2.32.2 From ff1f1db58fc6cc21270f14ce451377ee8c2b4a99 Mon Sep 17 00:00:00 2001 From: Matias <15092815+Parselon@users.noreply.github.com> Date: Mon, 3 Feb 2025 16:16:57 -0300 Subject: [PATCH 67/69] Feature: signal allocations (#169) * Add signal allocation method to requester * Add tests for signal allocation * Add documentation * Update signal docstring --- docs/api/allocations.md | 23 +++++++++++++++++++++ nomad/api/client.py | 16 +++++++++++++++ tests/test_client.py | 45 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/docs/api/allocations.md b/docs/api/allocations.md index 4f525af..1e6ac67 100644 --- a/docs/api/allocations.md +++ b/docs/api/allocations.md @@ -18,3 +18,26 @@ allocations = my_nomad.allocations.get_allocations() for allocation in allocations: print (allocation) ``` + +### Signal allocation + +This endpoint sends a signal to an allocation or task. + +https://developer.hashicorp.com/nomad/api-docs/allocations#signal-allocation + +Example: + +``` +import signal +import nomad + +my_nomad = nomad.Nomad(host='192.168.33.10') + +alloc_id = nomad_setup.allocations.get_allocations()[0]["ID"] + +# Send signal to an allocation +my_nomad.client.allocation.signal_allocation(alloc_id, signal.SIGUSR1.name) + +# Send signal to a task in allocation +my_nomad.client.allocation.signal_allocation(alloc_id, signal.SIGUSR1.name, task="my_task") +``` diff --git a/nomad/api/client.py b/nomad/api/client.py index ebf5991..9f4374c 100644 --- a/nomad/api/client.py +++ b/nomad/api/client.py @@ -304,6 +304,22 @@ def restart_allocation(self, id_): """ return self.request(id_, "restart", method="post").json() + def signal_allocation(self, id_, signal, task=None): + """Send a signal to an allocation or task. + https://www.nomadproject.io/api-docs/allocations#signal-allocation + arguments: + - id_ + - signal (str) + optional_arguments: + - task: (str) Optional, if omitted, the signal will be sent to all tasks in the allocation. + returns: dict + raises: + - nomad.api.exceptions.BaseNomadException + - nomad.api.exceptions.URLNotFoundNomadException + """ + payload = {"Signal": signal, "Task": task} + return self.request(id_, "signal", json=payload, method="post").json() + class gc_allocation(Requester): """ diff --git a/tests/test_client.py b/tests/test_client.py index 84bd076..5330869 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,5 +1,6 @@ import pytest import json +import signal import time import os @@ -8,6 +9,21 @@ from flaky import flaky +def get_running_allocation(nomad_setup): + max_iterations = 6 + for _ in range(max_iterations): + try: + return next( + alloc + for alloc in nomad_setup.allocations.get_allocations() + if alloc["ClientStatus"] == "running" + ) + except StopIteration: + # No alloc running + time.sleep(5) + raise ValueError("No allocations running") + + # integration tests requires nomad Vagrant VM or Binary running def test_register_job(nomad_setup): @@ -17,7 +33,6 @@ def test_register_job(nomad_setup): assert "example" in nomad_setup.job max_iterations = 6 - while nomad_setup.job["example"]["Status"] != "running": time.sleep(5) if max_iterations == 0: @@ -70,11 +85,37 @@ def test_read_allocation_stats(nomad_setup): f = nomad_setup.client.allocation.read_allocation_stats(a) +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 9, 1), reason="Not supported in version" +) +def test_signal_allocation(nomad_setup): + alloc_id = get_running_allocation(nomad_setup)["ID"] + nomad_setup.client.allocation.signal_allocation(alloc_id, signal.SIGUSR1.name) + + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 9, 1), reason="Not supported in version" +) +def test_signal_allocation_task(nomad_setup): + allocation = get_running_allocation(nomad_setup) + alloc_id = allocation["ID"] + task = list(allocation["TaskStates"].keys())[0] + nomad_setup.client.allocation.signal_allocation(alloc_id, signal.SIGUSR1.name, task) + + +@pytest.mark.skipif( + tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 9, 1), reason="Not supported in version" +) +def test_signal_allocation_invalid_signal(nomad_setup): + alloc_id = get_running_allocation(nomad_setup)["ID"] + with pytest.raises(nomad.api.exceptions.BaseNomadException, match="invalid signal"): + nomad_setup.client.allocation.signal_allocation(alloc_id, "INVALID-SIGNAL") + + @pytest.mark.skipif( tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 8, 1), reason="Not supported in version" ) def test_gc_all_allocations(nomad_setup): - node_id = nomad_setup.nodes.get_nodes()[0]["ID"] nomad_setup.client.gc_all_allocations.garbage_collect(node_id) nomad_setup.client.gc_all_allocations.garbage_collect() From db31eaadd02cc562c4f0fb55da06ab2ac800665b Mon Sep 17 00:00:00 2001 From: Nikita Beletskii <2nikita.b@gmail.com> Date: Tue, 4 Feb 2025 13:29:43 -0500 Subject: [PATCH 68/69] update version (#173) --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9c97309..c44d07b 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name='python-nomad', - version='2.0.1', + version='2.1.0', install_requires=['requests'], packages=['nomad', 'nomad.api'], url='http://github.com/jrxfive/python-nomad', @@ -26,6 +26,7 @@ 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', ], keywords='nomad hashicorp client', ) From 45c47732bd8ceaddfd967fefaee16c7d075883ba Mon Sep 17 00:00:00 2001 From: JannikSt Date: Thu, 1 May 2025 13:16:21 -0700 Subject: [PATCH 69/69] Fix: Update Requester.verify typing to support TLS CA certificate paths (#170) * Fix Requester verify typing to allow setting CA path * fix lint --- README.md | 3 +++ nomad/__init__.py | 8 ++++---- nomad/api/base.py | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 817439c..05f7815 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,9 @@ n = nomad.Nomad(host="172.16.100.10", secure=True, timeout=5, verify=True, cert= # For HTTPS Nomad instances with cert file and key n = nomad.Nomad(host="https://172.16.100.10", secure=True, timeout=5, verify=True, cert=("/path/to/certfile", "/path/to/key")) # See http://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification +# For HTTPS Nomad instance with cert file and key and CA file +n = nomad.Nomad(host="https://172.16.100.10", secure=True, timeout=5, verify="/path/to/cacert", cert=("/path/to/certfile", "/path/to/key")) + # For HTTPS Nomad instances with namespace and acl token n = nomad.Nomad(host="172.16.100.10", secure=True, timeout=5, verify=False, namespace='Namespace-example',token='3f4a0fcd-7c42-773c-25db-2d31ba0c05fe') diff --git a/nomad/__init__.py b/nomad/__init__.py index 406d2db..43437c5 100644 --- a/nomad/__init__.py +++ b/nomad/__init__.py @@ -1,7 +1,7 @@ """Nomad Python library""" import os -from typing import Optional +from typing import Optional, Union import requests @@ -25,7 +25,7 @@ def __init__( # pylint: disable=too-many-arguments timeout: int = 5, region: Optional[str] = None, version: str = "v1", - verify: bool = False, + verify: Union[bool, str] = False, cert: tuple = (), session: requests.Session = None, ): @@ -39,8 +39,8 @@ def __init__( # pylint: disable=too-many-arguments - user_agent (defaults None), custom user agent for requests to Nomad. - secure (defaults False), define if the protocol is secured or not (https or http) - version (defaults v1), version of the api of nomad. - - verify (defaults False), verify the certificate when tls/ssl is enabled - at nomad. + - verify (defaults False), verify SSL certificates for HTTPS requests. Can be a boolean to enable/disable + verification, or a string path to a CA certificate file. - cert (defaults empty), cert, or key and cert file to validate the certificate configured at nomad. - region (defaults None), version of the region to use. It will be used then diff --git a/nomad/api/base.py b/nomad/api/base.py index 209899b..ef340fe 100644 --- a/nomad/api/base.py +++ b/nomad/api/base.py @@ -1,6 +1,6 @@ """Requester""" -from typing import Optional +from typing import Optional, Union import requests @@ -24,7 +24,7 @@ def __init__( # pylint: disable=too-many-arguments token: Optional[str] = None, timeout: int = 5, version: str = "v1", - verify: bool = False, + verify: Union[bool, str] = False, cert: tuple = (), region: Optional[str] = None, session: requests.Session = None,