Skip to content

Commit 45ad784

Browse files
authored
Merge pull request #5635 from chu11/job_info_update_jobspec_remove_manual
job-info: support lookup of updated jobspec, remove manual construction of updated R / jobspec
2 parents 8190b78 + d5b38ff commit 45ad784

File tree

9 files changed

+134
-129
lines changed

9 files changed

+134
-129
lines changed

src/bindings/python/flux/job/kvslookup.py

Lines changed: 24 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
import errno
1111
import json
1212

13+
import flux.constants
1314
from _flux._core import ffi, lib
1415
from flux.future import WaitAllFuture
15-
from flux.job import JobID, JobspecV1
16-
from flux.job.event import EventLogEvent
16+
from flux.job import JobID
1717
from flux.rpc import RPC
18-
from flux.util import set_treedict
1918

2019

2120
def _decode_field(data, key):
@@ -48,26 +47,19 @@ def get_decode(self):
4847
return data
4948

5049

51-
def job_info_lookup(flux_handle, jobid, keys=["jobspec"]):
52-
payload = {"id": int(jobid), "keys": keys, "flags": 0}
50+
def job_info_lookup(flux_handle, jobid, keys=["jobspec"], flags=0):
51+
payload = {"id": int(jobid), "keys": keys, "flags": flags}
5352
rpc = JobInfoLookupRPC(flux_handle, "job-info.lookup", payload)
5453
rpc.jobid = jobid
5554
return rpc
5655

5756

58-
def _setup_lookup_keys(keys, original, base):
57+
def _setup_lookup_keys(keys, original):
5958
if "jobspec" in keys:
6059
if original:
6160
keys.remove("jobspec")
6261
if "J" not in keys:
6362
keys.append("J")
64-
elif not base:
65-
if "eventlog" not in keys:
66-
keys.append("eventlog")
67-
if "R" in keys:
68-
if not base:
69-
if "eventlog" not in keys:
70-
keys.append("eventlog")
7163

7264

7365
def _get_original_jobspec(job_data):
@@ -78,58 +70,14 @@ def _get_original_jobspec(job_data):
7870
return result.decode("utf-8")
7971

8072

81-
def _get_updated_jobspec(job_data):
82-
if isinstance(job_data["jobspec"], str):
83-
data = json.loads(job_data["jobspec"])
84-
else:
85-
data = job_data["jobspec"]
86-
jobspec = JobspecV1(**data)
87-
for entry in job_data["eventlog"].splitlines():
88-
event = EventLogEvent(entry)
89-
if event.name == "jobspec-update":
90-
for key, value in event.context.items():
91-
jobspec.setattr(key, value)
92-
return jobspec.dumps()
93-
94-
95-
def _get_updated_R(job_data):
96-
if isinstance(job_data["R"], str):
97-
R = json.loads(job_data["R"])
98-
else:
99-
R = job_data["R"]
100-
for entry in job_data["eventlog"].splitlines():
101-
event = EventLogEvent(entry)
102-
if event.name == "resource-update":
103-
for key, value in event.context.items():
104-
if key == "expiration":
105-
set_treedict(R, "execution.expiration", value)
106-
return json.dumps(R, ensure_ascii=False)
107-
108-
109-
def _update_keys(job_data, decode, keys, original, base):
110-
remove_eventlog = False
73+
def _update_keys(job_data, decode, keys, original):
11174
if "jobspec" in keys:
11275
if original:
11376
job_data["jobspec"] = _get_original_jobspec(job_data)
11477
if decode:
11578
_decode_field(job_data, "jobspec")
11679
if "J" not in keys:
11780
job_data.pop("J")
118-
elif not base:
119-
job_data["jobspec"] = _get_updated_jobspec(job_data)
120-
if decode:
121-
_decode_field(job_data, "jobspec")
122-
if "eventlog" not in keys:
123-
remove_eventlog = True
124-
if "R" in keys:
125-
if not base:
126-
job_data["R"] = _get_updated_R(job_data)
127-
if decode:
128-
_decode_field(job_data, "R")
129-
if "eventlog" not in keys:
130-
remove_eventlog = True
131-
if remove_eventlog:
132-
job_data.pop("eventlog")
13381

13482

13583
# jobs_kvs_lookup simple variant for one jobid
@@ -153,15 +101,21 @@ def job_kvs_lookup(
153101
:base: For 'jobspec' or 'R', get base value, do not apply updates from eventlog
154102
"""
155103
keyslookup = list(keys)
156-
_setup_lookup_keys(keyslookup, original, base)
157-
payload = {"id": int(jobid), "keys": keyslookup, "flags": 0}
104+
_setup_lookup_keys(keyslookup, original)
105+
# N.B. JobInfoLookupRPC() has a "get_decode()" member
106+
# function, so we will always get the non-decoded result from
107+
# job-info.
108+
flags = 0
109+
if not base:
110+
flags |= flux.constants.FLUX_JOB_LOOKUP_CURRENT
111+
payload = {"id": int(jobid), "keys": keyslookup, "flags": flags}
158112
rpc = JobInfoLookupRPC(flux_handle, "job-info.lookup", payload)
159113
try:
160114
if decode:
161115
rsp = rpc.get_decode()
162116
else:
163117
rsp = rpc.get()
164-
_update_keys(rsp, decode, keys, original, base)
118+
_update_keys(rsp, decode, keys, original)
165119
# The job does not exist!
166120
except FileNotFoundError:
167121
return None
@@ -242,7 +196,7 @@ def __init__(
242196
self.original = original
243197
self.base = base
244198
self.errors = []
245-
_setup_lookup_keys(self.keyslookup, self.original, self.base)
199+
_setup_lookup_keys(self.keyslookup, self.original)
246200

247201
def fetch_data(self):
248202
"""Initiate the job info lookup to the Flux job-info module
@@ -255,9 +209,15 @@ def fetch_data(self):
255209
JobKVSLookupFuture.errors is non-empty, then it will contain a
256210
list of errors returned via the query.
257211
"""
212+
flags = 0
213+
# N.B. JobInfoLookupRPC() has a "get_decode()" member
214+
# function, so we will always get the non-decoded result from
215+
# job-info.
216+
if not self.base:
217+
flags |= flux.constants.FLUX_JOB_LOOKUP_CURRENT
258218
listids = JobKVSLookupFuture()
259219
for jobid in self.ids:
260-
listids.push(job_info_lookup(self.handle, jobid, self.keyslookup))
220+
listids.push(job_info_lookup(self.handle, jobid, self.keyslookup, flags))
261221
return listids
262222

263223
def data(self):
@@ -276,5 +236,5 @@ def data(self):
276236
if hasattr(rpc, "errors"):
277237
self.errors = rpc.errors
278238
for job_data in data:
279-
_update_keys(job_data, self.decode, self.keys, self.original, self.base)
239+
_update_keys(job_data, self.decode, self.keys, self.original)
280240
return data

src/cmd/flux-job.c

Lines changed: 3 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3308,42 +3308,6 @@ int cmd_wait_event (optparse_t *p, int argc, char **argv)
33083308
return (0);
33093309
}
33103310

3311-
char *reconstruct_current_jobspec (const char *jobspec_str,
3312-
const char *eventlog_str)
3313-
{
3314-
json_t *jobspec;
3315-
json_t *eventlog;
3316-
size_t index;
3317-
json_t *entry;
3318-
char *result;
3319-
3320-
if (!(jobspec = json_loads (jobspec_str, 0, NULL)))
3321-
log_msg_exit ("error decoding jobspec");
3322-
if (!(eventlog = eventlog_decode (eventlog_str)))
3323-
log_msg_exit ("error decoding eventlog");
3324-
json_array_foreach (eventlog, index, entry) {
3325-
const char *name;
3326-
json_t *context;
3327-
3328-
if (eventlog_entry_parse (entry, NULL, &name, &context) < 0)
3329-
log_msg_exit ("error decoding eventlog entry");
3330-
if (streq (name, "jobspec-update")) {
3331-
const char *path;
3332-
json_t *value;
3333-
3334-
json_object_foreach (context, path, value) {
3335-
if (jpath_set (jobspec, path, value) < 0)
3336-
log_err_exit ("failed to update jobspec");
3337-
}
3338-
}
3339-
}
3340-
if (!(result = json_dumps (jobspec, JSON_COMPACT)))
3341-
log_msg_exit ("failed to encode jobspec object");
3342-
json_decref (jobspec);
3343-
json_decref (eventlog);
3344-
return result;
3345-
}
3346-
33473311
void info_usage (void)
33483312
{
33493313
fprintf (stderr,
@@ -3403,33 +3367,11 @@ int cmd_info (optparse_t *p, int argc, char **argv)
34033367
log_msg_exit ("Failed to unwrap J to get jobspec: %s", error.text);
34043368
val = new_val;
34053369
}
3406-
/* The current (non --base) jobspec has to be reconstructed by fetching
3407-
* the jobspec and the eventlog and updating the former with the latter.
3408-
*/
3409-
else if (!optparse_hasopt (p, "base") && streq (key, "jobspec")) {
3410-
const char *jobspec;
3411-
const char *eventlog;
3412-
3413-
// fetch the two keys in parallel
3414-
if (!(f = flux_rpc_pack (h,
3415-
"job-info.lookup",
3416-
FLUX_NODEID_ANY,
3417-
0,
3418-
"{s:I s:[ss] s:i}",
3419-
"id", id,
3420-
"keys", "jobspec", "eventlog",
3421-
"flags", 0))
3422-
|| flux_rpc_get_unpack (f,
3423-
"{s:s s:s}",
3424-
"jobspec", &jobspec,
3425-
"eventlog", &eventlog) < 0)
3426-
log_msg_exit ("%s", future_strerror (f, errno));
3427-
val = new_val = reconstruct_current_jobspec (jobspec, eventlog);
3428-
}
3429-
/* The current (non --base) R is obtained through the
3370+
/* The current (non --base) R and jobspec are obtained through the
34303371
* job-info.lookup RPC w/ the CURRENT flag.
34313372
*/
3432-
else if (!optparse_hasopt (p, "base") && streq (key, "R")) {
3373+
else if (!optparse_hasopt (p, "base")
3374+
&& (streq (key, "R") || streq (key, "jobspec"))) {
34333375
if (!(f = flux_rpc_pack (h,
34343376
"job-info.lookup",
34353377
FLUX_NODEID_ANY,

src/common/libjob/job.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ enum job_lookup_flags {
3737
FLUX_JOB_LOOKUP_JSON_DECODE = 1,
3838
/* get current value of special fields by applying eventlog
3939
* updates for those fields
40-
* - currently works for R
40+
* - currently works for jobspec and R
4141
*/
4242
FLUX_JOB_LOOKUP_CURRENT = 2,
4343
};

src/modules/job-info/lookup.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ static int lookup_current (struct lookup_ctx *l,
173173

174174
if (streq (key, "R"))
175175
update_event_name = "resource-update";
176+
else if (streq (key, "jobspec"))
177+
update_event_name = "jobspec-update";
176178

177179
if (!(value_object = json_loads (value, 0, NULL))) {
178180
errno = EINVAL;
@@ -205,6 +207,12 @@ static int lookup_current (struct lookup_ctx *l,
205207
if (streq (name, update_event_name)) {
206208
if (streq (key, "R"))
207209
apply_updates_R (l->ctx->h, l->id, key, value_object, context);
210+
else if (streq (key, "jobspec"))
211+
apply_updates_jobspec (l->ctx->h,
212+
l->id,
213+
key,
214+
value_object,
215+
context);
208216
}
209217
}
210218

@@ -288,7 +296,8 @@ static void info_lookup_continuation (flux_future_t *fall, void *arg)
288296
}
289297

290298
if ((l->flags & FLUX_JOB_LOOKUP_CURRENT)
291-
&& streq (keystr, "R")) {
299+
&& (streq (keystr, "R")
300+
|| streq (keystr, "jobspec"))) {
292301
if (lookup_current (l, fall, keystr, s, &current_value) < 0)
293302
goto error;
294303
s = current_value;
@@ -442,7 +451,7 @@ static int lookup_cached (struct lookup_ctx *l)
442451

443452
key_str = json_string_value (key);
444453

445-
if (!streq (key_str, "R"))
454+
if (!streq (key_str, "R") && !streq (key_str, "jobspec"))
446455
return 0;
447456

448457
if ((ret = update_watch_get_cached (l->ctx,

src/modules/job-info/update.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ static struct update_ctx *update_ctx_create (struct info_ctx *ctx,
8383
goto error;
8484
if (streq (key, "R"))
8585
uc->update_name = "resource-update";
86+
else if (streq (key, "jobspec"))
87+
uc->update_name = "jobspec-update";
8688
else {
8789
errno = EINVAL;
8890
goto error;
@@ -180,6 +182,12 @@ static void eventlog_continuation (flux_future_t *f, void *arg)
180182
uc->key,
181183
uc->update_object,
182184
context);
185+
else if (streq (uc->key, "jobspec"))
186+
apply_updates_jobspec (uc->ctx->h,
187+
uc->id,
188+
uc->key,
189+
uc->update_object,
190+
context);
183191

184192
msg = flux_msglist_first (uc->msglist);
185193
while (msg) {
@@ -315,6 +323,12 @@ static void lookup_continuation (flux_future_t *f, void *arg)
315323
uc->key,
316324
uc->update_object,
317325
context);
326+
else if (streq (uc->key, "jobspec"))
327+
apply_updates_jobspec (uc->ctx->h,
328+
uc->id,
329+
uc->key,
330+
uc->update_object,
331+
context);
318332
uc->initial_update_count++;
319333
}
320334
else if (streq (name, "clean"))
@@ -474,7 +488,7 @@ void update_watch_cb (flux_t *h, flux_msg_handler_t *mh,
474488
errmsg = "update-watch request rejected without streaming RPC flag";
475489
goto error;
476490
}
477-
if (!streq (key, "R")) {
491+
if (!streq (key, "R") && !streq (key, "jobspec")) {
478492
errno = EINVAL;
479493
errmsg = "update-watch unsupported key specified";
480494
goto error;

src/modules/job-info/util.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,25 @@ void apply_updates_R (flux_t *h,
125125
}
126126
}
127127

128+
void apply_updates_jobspec (flux_t *h,
129+
flux_jobid_t id,
130+
const char *key,
131+
json_t *jobspec,
132+
json_t *context)
133+
{
134+
const char *ckey;
135+
json_t *value;
136+
137+
json_object_foreach (context, ckey, value) {
138+
if (jpath_set (jobspec,
139+
ckey,
140+
value) < 0)
141+
flux_log (h, LOG_INFO,
142+
"%s: failed to update job %s %s",
143+
__FUNCTION__, idf58 (id), key);
144+
}
145+
}
146+
128147
/*
129148
* vi:tabstop=4 shiftwidth=4 expandtab
130149
*/

src/modules/job-info/util.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ void apply_updates_R (flux_t *h,
4242
json_t *R,
4343
json_t *context);
4444

45+
/* apply context updates to the jobspec object */
46+
void apply_updates_jobspec (flux_t *h,
47+
flux_jobid_t id,
48+
const char *key,
49+
json_t *jobspec,
50+
json_t *context);
51+
4552
#endif /* ! _FLUX_JOB_INFO_UTIL_H */
4653

4754
/*

t/python/t0014-job-kvslookup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ def test_info_00_job_info_lookup(self):
159159
data = rpc.get()
160160
self.check_jobspec_str(data, self.jobid1, 0)
161161
data = rpc.get_decode()
162+
self.check_jobspec_decoded(data, self.jobid1, 0)
162163
self.assertEqual(data["id"], self.jobid1, 0)
163164

164165
def test_info_01_job_info_lookup_keys(self):

0 commit comments

Comments
 (0)