Skip to content

Commit 79ae9d9

Browse files
authored
feat: Update dispatch jobs to support templateprefix and idempotencytoken (#171)
* Update `dispatch_jobs` to support `idempotency_token` and `IdPrefixTemplate` * bump action versions * black formatting
1 parent 060ced3 commit 79ae9d9

4 files changed

Lines changed: 45 additions & 40 deletions

File tree

.github/workflows/lint.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ jobs:
1212
build:
1313
runs-on: ubuntu-latest
1414
steps:
15-
- uses: actions/checkout@v3
15+
- uses: actions/checkout@v4
1616

1717
- name: Set up Python 3
18-
uses: actions/setup-python@v4
18+
uses: actions/setup-python@v5
1919
with:
20-
python-version: '3.12'
20+
python-version: '3.13'
2121

2222
- name: Install Dependencies
2323
shell: bash

.github/workflows/main.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ jobs:
2222
strategy:
2323
fail-fast: false
2424
matrix:
25-
python-version: ['3.7', '3.12'] # the oldest and newest support versions
26-
nomad-version: ['1.2.16', '1.3.16', '1.4.14', '1.5.17', '1.6.10', '1.7.7']
25+
python-version: ['3.8', '3.13'] # the oldest and newest support versions
26+
nomad-version: ['1.4.14', '1.5.17', '1.6.10', '1.7.7', '1.8.4', '1.9.5']
2727
steps:
2828
- uses: actions/checkout@v4
2929
- name: Set up Python ${{ matrix.python-version }}
@@ -41,7 +41,9 @@ jobs:
4141
curl -L -o /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip
4242
4343
echo "unzip nomad"
44-
unzip -d /usr/local/bin/ /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip
44+
unzip -o -d /usr/local/bin/ /tmp/nomad_${NOMAD_VERSION}_linux_amd64.zip
45+
chmod +x /usr/local/bin/nomad
46+
/usr/local/bin/nomad version
4547
- name: Install Dependencies
4648
shell: bash
4749
run: |
@@ -60,4 +62,4 @@ jobs:
6062
run: |
6163
./run_tests.sh
6264
- name: Upload coverage to Codecov
63-
uses: codecov/codecov-action@v4
65+
uses: codecov/codecov-action@v5

nomad/api/job.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,14 @@ def periodic_job(self, id_):
245245
"""
246246
return self.request(id_, "periodic", "force", method="post").json()
247247

248-
def dispatch_job(self, id_, payload=None, meta=None):
248+
def dispatch_job(
249+
self,
250+
id_,
251+
payload=None,
252+
meta=None,
253+
id_prefix_template=None,
254+
idempotency_token=None,
255+
): # pylint: disable=too-many-arguments
249256
"""Dispatches a new instance of a parameterized job.
250257
251258
https://www.nomadproject.io/docs/http/job.html
@@ -254,12 +261,19 @@ def dispatch_job(self, id_, payload=None, meta=None):
254261
- id_
255262
- payload
256263
- meta
264+
- id_prefix_template
265+
- idempotency_token
257266
returns: dict
258267
raises:
259268
- nomad.api.exceptions.BaseNomadException
260269
- nomad.api.exceptions.URLNotFoundNomadException
261270
"""
262-
dispatch_json = {"Meta": meta, "Payload": payload}
271+
dispatch_json = {
272+
"Meta": meta,
273+
"Payload": payload,
274+
"idempotency_token": idempotency_token,
275+
"IdPrefixTemplate": id_prefix_template,
276+
}
263277
return self.request(id_, "dispatch", json=dispatch_json, method="post").json()
264278

265279
def revert_job(self, id_, version, enforce_prior_version=None):

tests/test_job.py

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -125,93 +125,82 @@ def test_delete_job(nomad_setup):
125125

126126

127127
@flaky(max_runs=5, min_passes=1)
128-
@pytest.mark.skipif(
129-
tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 3), reason="Nomad dispatch not supported"
130-
)
131128
def test_dispatch_job(nomad_setup):
132129
with open("example_batch_parameterized.json") as fh:
133130
job = json.loads(fh.read())
134131
nomad_setup.job.register_job("example-batch", job)
135132
try:
136-
nomad_setup.job.dispatch_job("example-batch", meta={"time": "500"})
133+
nomad_setup.job.dispatch_job("example-batch", meta={"time": "500"}, id_prefix_template="run1")
137134
except (exceptions.URLNotFoundNomadException, exceptions.BaseNomadException) as e:
138135
print(e.nomad_resp.text)
139136
raise e
140137
assert "example-batch" in nomad_setup.job
141138

142139

143-
@pytest.mark.skipif(
144-
tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 5, 3), reason="Nomad dispatch not supported"
145-
)
140+
@flaky(max_runs=5, min_passes=1)
141+
def test_dispatch_job_idempotency(nomad_setup):
142+
with open("example_batch_parameterized.json") as fh:
143+
job = json.loads(fh.read())
144+
nomad_setup.job.register_job("example-batch-idempotent", job)
145+
146+
# First dispatch should succeed
147+
try:
148+
nomad_setup.job.dispatch_job("example-batch-idempotent", meta={"time": "500"}, id_prefix_template="run1", idempotency_token="737ae8cd-f237-43a5-8fad-0e6a3f94ad55")
149+
except (exceptions.URLNotFoundNomadException, exceptions.BaseNomadException) as e:
150+
print(e.nomad_resp.text)
151+
raise e
152+
assert "example-batch-idempotent" in nomad_setup.job
153+
154+
# Second dispatch with the same idempotency token should fail
155+
with pytest.raises(exceptions.BaseNomadException):
156+
nomad_setup.job.dispatch_job("example-batch-idempotent", meta={"time": "500"}, id_prefix_template="run2", idempotency_token="737ae8cd-f237-43a5-8fad-0e6a3f94ad55")
157+
158+
146159
def test_summary_job(nomad_setup):
147160
j = nomad_setup.job["example"]
148161
assert "JobID" in nomad_setup.job.get_summary(j["ID"])
149162

150163

151-
@pytest.mark.skipif(
152-
tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 4, 0), reason="Not supported in version"
153-
)
154164
def test_plan_job(nomad_setup):
155165
with open("example.json") as fh:
156166
job = json.loads(fh.read())
157167
assert "Index" in nomad_setup.job.plan_job(nomad_setup.job["example"]["ID"], job)
158168

159169

160-
@pytest.mark.skipif(
161-
tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version"
162-
)
163170
def test_versions_job(nomad_setup):
164171
assert "Versions" in nomad_setup.job.get_versions("example")
165172

166173

167-
@pytest.mark.skipif(
168-
tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version"
169-
)
170174
def test_versions_job_missing(nomad_setup):
171175
with pytest.raises(nomad.api.exceptions.URLNotFoundNomadException):
172176
assert "Versions" in nomad_setup.job.get_versions("example1")
173177

174178

175-
@pytest.mark.skipif(
176-
tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version"
177-
)
178179
def test_get_job_deployments(nomad_setup):
179180
assert "JobID" in nomad_setup.job.get_deployments("example")[0]
180181
assert isinstance(nomad_setup.job.get_deployments("example"), list)
181182
assert isinstance(nomad_setup.job.get_deployments("example")[0], dict)
182183
assert "example" == nomad_setup.job.get_deployments("example")[0]["JobID"]
183184

184185

185-
@pytest.mark.skipif(
186-
tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version"
187-
)
188186
def test_get_job_deployment(nomad_setup):
189187
assert "JobID" in nomad_setup.job.get_deployment("example")
190188
assert isinstance(nomad_setup.job.get_deployment("example"), dict)
191189
assert "example" == nomad_setup.job.get_deployment("example")["JobID"]
192190

193191

194-
@pytest.mark.skipif(
195-
tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version"
196-
)
197192
def test_get_summary(nomad_setup):
198193
assert "JobID" in nomad_setup.job.get_summary("example")
199194
assert isinstance(nomad_setup.job.get_summary("example"), dict)
200195
assert "example" == nomad_setup.job.get_summary("example")["JobID"]
201196

202197

203-
@pytest.mark.skipif(
204-
tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version"
205-
)
206198
def test_revert_job(nomad_setup):
207199
current_job_version = nomad_setup.job.get_deployment("example")["JobVersion"]
208200
prior_job_version = current_job_version - 1
209201
nomad_setup.job.revert_job("example", prior_job_version, current_job_version)
210202

211203

212-
@pytest.mark.skipif(
213-
tuple(int(i) for i in os.environ.get("NOMAD_VERSION").split(".")) < (0, 6, 0), reason="Not supported in version"
214-
)
215204
def test_stable_job(nomad_setup):
216205
current_job_version = nomad_setup.job.get_deployment("example")["JobVersion"]
217206
nomad_setup.job.stable_job("example", current_job_version, True)

0 commit comments

Comments
 (0)