comparison scictl/scictl.c @ 22:dd078aea5d02

misc: use project/worker name as primary key
author David Demelier <markand@malikania.fr>
date Thu, 21 Jul 2022 20:23:22 +0200
parents de4bf839b565
children 2cb228f23f53
comparison
equal deleted inserted replaced
21:ec30e1b078a9 22:dd078aea5d02
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */ 17 */
18 18
19 #include <err.h> 19 #include <limits.h>
20 #include <errno.h>
20 #include <stdio.h> 21 #include <stdio.h>
21 #include <stdlib.h> 22 #include <stdlib.h>
22 #include <string.h> 23 #include <string.h>
23 #include <stdnoreturn.h>
24 #include <unistd.h> 24 #include <unistd.h>
25 25
26 #include "apic.h"
26 #include "config.h" 27 #include "config.h"
27 #include "req.h"
28 #include "types.h" 28 #include "types.h"
29 #include "util.h" 29 #include "util.h"
30 30
31 noreturn static void 31 static void
32 usage(void) 32 usage(void)
33 { 33 {
34 fprintf(stderr, "usage: %s [-s sock] command [args...]\n", getprogname()); 34 fprintf(stderr, "usage: %s [-u baseurl] command [args...]\n", getprogname());
35 exit(1); 35 exit(1);
36 } 36 }
37 37
38 noreturn static void 38 static void
39 help(void) 39 help(void)
40 { 40 {
41 fprintf(stderr, "usage: %s job-add project tag\n", getprogname()); 41 fprintf(stderr, "usage: scictl job-add project tag\n");
42 fprintf(stderr, " %s job-todo worker\n", getprogname()); 42 fprintf(stderr, " scictl job-todo worker\n");
43 fprintf(stderr, " %s jobresult-add id worker exitcode console\n", getprogname()); 43 fprintf(stderr, " scictl jobresult-add id worker exitcode console\n");
44 fprintf(stderr, " %s project-add name desc url script\n", getprogname()); 44 fprintf(stderr, " scictl project-add name desc url script\n");
45 fprintf(stderr, " %s project-info name\n", getprogname()); 45 fprintf(stderr, " scictl project-info name\n");
46 fprintf(stderr, " %s project-list\n", getprogname()); 46 fprintf(stderr, " scictl project-list\n");
47 fprintf(stderr, " %s project-update name key value\n", getprogname()); 47 fprintf(stderr, " scictl project-update name key value\n");
48 fprintf(stderr, " %s worker-add name desc\n", getprogname()); 48 fprintf(stderr, " scictl worker-add name desc\n");
49 fprintf(stderr, " %s worker-list\n", getprogname()); 49 fprintf(stderr, " scictl worker-list\n");
50 exit(0); 50 exit(0);
51 }
52
53 static inline void
54 replace(char **str, const char *new)
55 {
56 free(*str);
57 *str = util_strdup(new);
58 }
59
60 long long
61 toint(const char *s)
62 {
63 long long v;
64 const char *err;
65
66 v = util_strtonum(s, 0, LLONG_MAX, &err);
67
68 if (err)
69 util_die("abort: %s\n", err);
70
71 return v;
51 } 72 }
52 73
53 static char * 74 static char *
54 readfile(const char *path) 75 readfile(const char *path)
55 { 76 {
56 FILE *fp, *str; 77 FILE *fp, *str;
57 char buf[BUFSIZ], *console; 78 char buf[BUFSIZ], *console = NULL;
58 size_t nr; 79 size_t nr, consolesz = 0;
59 80
60 if (strcmp(path, "-") == 0) 81 if (strcmp(path, "-") == 0)
61 fp = stdin; 82 fp = stdin;
62 else if (!(fp = fopen(path, "r"))) 83 else if (!(fp = fopen(path, "r")))
63 err(1, "%s", path); 84 util_die("abort: %s: %s\n", path, strerror(errno));
64 85
65 console = util_calloc(1, SCI_MSG_MAX); 86 if (!(str = open_memstream(&console, &consolesz)))
66 87 util_die("abort: open_memstream: %s\n", strerror(errno));
67 if (!(str = fmemopen(console, SCI_MSG_MAX, "w")))
68 err(1, "fmemopen");
69 88
70 while ((nr = fread(buf, 1, sizeof (buf), fp)) > 0) 89 while ((nr = fread(buf, 1, sizeof (buf), fp)) > 0)
71 fwrite(buf, 1, nr, str); 90 fwrite(buf, 1, nr, str);
72 91
73 if ((ferror(fp) && !feof(fp)) || (ferror(str) && !feof(str))) { 92 if ((ferror(fp) && !feof(fp)) || (ferror(str) && !feof(str)))
74 free(console); 93 util_die("abort: %s\n", strerror(errno));
75 console = NULL;
76 }
77 94
78 fclose(str); 95 fclose(str);
79 fclose(fp); 96 fclose(fp);
80 97
81 return console; 98 return console;
85 extract(char *s, size_t w, size_t n, void *data) 102 extract(char *s, size_t w, size_t n, void *data)
86 { 103 {
87 return fwrite(s, w, n, data); 104 return fwrite(s, w, n, data);
88 } 105 }
89 106
90 static json_t * 107 static void
91 parse(const char *data)
92 {
93 json_t *doc;
94 json_error_t err;
95
96 if (!(json_loads(doc, 0, &err)))
97 die("abort: unable to parse JSON: %s\n", err.text);
98
99 return doc;
100 }
101
102 static json_t *
103 get(const char *url)
104 {
105 CURL *curl;
106 CURLcode code;
107 FILE *fp;
108 char buf[HTTP_BUF_MAX];
109 long ret;
110
111 if (!(fp = fmemopen(buf, sizeof (buf), "w")))
112 die("abort: %s", strerror(errno));
113
114 curl = curl_easy_init();
115 curl_easy_setopt(curl, CURLOPT_URL, url);
116 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, extract);
117 curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
118 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3L);
119 code = curl_easy_perform(curl);
120
121 if (code != CURLE_OK)
122 die("abort: %s", curl_easy_strerror(code));
123
124 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &ret);
125
126 if (ret != 200)
127 die("abort: HTTP %ld\n", ret);
128
129 curl_easy_cleanup(curl);
130 fclose(fp);
131
132 return parse(buf);
133 }
134
135 static struct req
136 cmd_job_add(int argc, char **argv) 108 cmd_job_add(int argc, char **argv)
137 { 109 {
138 struct job job = {0}; 110 struct job job = {0};
139 struct project project = {0}; 111 struct apic req;
112
113 if (argc < 3)
114 usage();
115
116 job.project_name = util_strdup(argv[1]);
117 job.tag = util_strdup(argv[2]);
118
119 if (apic_job_add(&req, &job) < 0)
120 util_die("abort: %s\n", req.error);
121
122 apic_finish(&req);
123 job_finish(&job);
124 }
125
126 static void
127 cmd_job_todo(int argc, char **argv)
128 {
129 struct job jobs[SCI_JOB_LIST_MAX] = {0};
130 size_t jobsz;
131 struct apic req;
140 132
141 if (argc < 2) 133 if (argc < 2)
142 usage(); 134 usage();
143 135
144 project.name = argv[0]; 136 if ((jobsz = apic_job_todo(&req, jobs, UTIL_SIZE(jobs), toint(argv[1]))))
145 137 util_die("abort: %s\n", req.error);
146 if (_project_find(&project, argv[0])).status)
147 return rp;
148
149 job.project_id = project.id;
150 job.tag = argv[1];
151
152 rj = req_job_add(&job);
153 req_finish(&rp);
154
155 return rj;
156 }
157
158 static struct req
159 cmd_job_todo(int argc, char **argv)
160 {
161 struct project projects[SCI_PROJECT_MAX] = {0};
162 struct job jobs[SCI_JOB_LIST_MAX] = {0};
163 struct req rp, rj;
164 size_t projectsz = UTIL_SIZE(projects), jobsz = UTIL_SIZE(jobs);
165
166 if (argc < 1)
167 usage();
168
169 /* First retrieve projects for a better listing. */
170 if ((rp = req_project_list(projects, &projectsz)).status)
171 return rp;
172
173 if ((rj = req_job_todo(jobs, &jobsz, argv[0])).status) {
174 req_finish(&rp);
175 return rj;
176 }
177 138
178 for (size_t i = 0; i < jobsz; ++i) { 139 for (size_t i = 0; i < jobsz; ++i) {
179 const char *project = "unknown"; 140 printf("%-16s%jd\n", "id:", jobs[i].id);
180
181 /* Find project if exists (it should). */
182 for (size_t p = 0; p < projectsz; ++p) {
183 if (projects[p].id == jobs[i].project_id) {
184 project = projects[p].name;
185 break;
186 }
187 }
188
189 printf("%-16s%d\n", "id:", jobs[i].id);
190 printf("%-16s%s\n", "tag:", jobs[i].tag); 141 printf("%-16s%s\n", "tag:", jobs[i].tag);
191 printf("%-16s%s\n", "project:", project); 142 printf("%-16s%s\n", "project:", jobs[i].project_name);
192 143
193 if (i + 1 < jobsz) 144 if (i + 1 < jobsz)
194 printf("\n"); 145 printf("\n");
195 } 146
196 147 job_finish(&jobs[i]);
197 req_finish(&rp); 148 }
198 149
199 return rj; 150 apic_finish(&req);
200 } 151 }
201 152
202 static struct req 153 static void
203 cmd_jobresult_add(int argc, char **argv) 154 cmd_jobresult_add(int argc, char **argv)
204 { 155 {
205 struct jobresult res = {0}; 156 struct jobresult res = {0};
206 struct worker wk = {0}; 157 struct apic req;
207 struct req rw, rj;
208 char *log;
209 158
210 if (argc < 5) 159 if (argc < 5)
211 usage(); 160 usage();
212 161
213 /* Find worker id. */ 162 res.job_id = toint(argv[1]);
214 if ((rw = req_worker_find(&wk, argv[1])).status) 163 res.worker_name = util_strdup(argv[2]);
215 return rw; 164 res.exitcode = toint(argv[3]);
216 165 res.log = readfile(argv[4]);
217 res.job_id = strtoll(argv[0], NULL, 10); 166
218 res.exitcode = strtoll(argv[2], NULL, 10); 167 if (apic_jobresult_add(&req, &res) < 0)
219 res.worker_id = wk.id; 168 util_die("abort: unable to add job result: %s\n", req.error);
220 res.log = log = readfile(argv[3]); 169
221 rj = req_jobresult_add(&res); 170 apic_finish(&req);
222 171 jobresult_finish(&res);
223 free(log); 172 }
224 req_finish(&rw); 173
225 174 static void
226 return rj;
227 }
228
229 static struct req
230 cmd_project_add(int argc, char **argv) 175 cmd_project_add(int argc, char **argv)
231 { 176 {
232 struct project pc = {0}; 177 struct project pc = {0};
233 struct req res; 178 struct apic req;
234 char *script; 179
180 if (argc < 5)
181 usage();
182
183 pc.name = util_strdup(argv[1]);
184 pc.desc = util_strdup(argv[2]);
185 pc.url = util_strdup(argv[3]);
186 pc.script = readfile(argv[4]);
187
188 if (apic_project_save(&req, &pc) < 0)
189 util_die("abort: unable to create project: %s\n", req.error);
190
191 apic_finish(&req);
192 project_finish(&pc);
193 }
194
195 static void
196 cmd_project_update(int argc, char **argv)
197 {
198 struct project pc;
199 struct apic req;
235 200
236 if (argc < 4) 201 if (argc < 4)
237 usage(); 202 usage();
238 203
239 pc.name = argv[0]; 204 if (apic_project_find(&req, &pc, argv[1]) < 0)
240 pc.desc = argv[1]; 205 util_die("abort: unable to find project: %s\n", req.error);
241 pc.url = argv[2]; 206
242 pc.script = script = readfile(argv[3]); 207 if (strcmp(argv[2], "name") == 0)
243 res = req_project_add(&pc); 208 replace(&pc.name, argv[3]);
244 209 else if (strcmp(argv[2], "desc") == 0)
245 free(script); 210 replace(&pc.desc, argv[3]);
246 211 else if (strcmp(argv[2], "url") == 0)
247 return res; 212 replace(&pc.url, argv[3]);
248 } 213 else if (strcmp(argv[2], "script") == 0)
249 214 replace(&pc.script, readfile(argv[3]));
250 static struct req 215
251 cmd_project_update(int argc, char **argv) 216 if (apic_project_save(&req, &pc) < 0)
252 { 217 util_die("abort: unable to save project: %s\n", req.error);
253 struct project pc; 218
254 struct req rget, rsend; 219 apic_finish(&req);
255 char *script = NULL; 220 project_finish(&pc);
256 221 }
257 if (argc < 3) 222
258 help(); 223 static void
259
260 if ((rget = req_project_find(&pc, argv[0])).status)
261 return rget;
262
263 if (strcmp(argv[1], "name") == 0)
264 pc.name = argv[2];
265 else if (strcmp(argv[1], "desc") == 0)
266 pc.desc = argv[2];
267 else if (strcmp(argv[1], "url") == 0)
268 pc.url = argv[2];
269 else if (strcmp(argv[1], "script") == 0)
270 pc.script = script = readfile(argv[2]);
271
272 rsend = req_project_update(&pc);
273
274 req_finish(&rget);
275 free(script);
276
277 return rsend;
278 }
279
280 static struct req
281 cmd_project_info(int argc, char **argv) 224 cmd_project_info(int argc, char **argv)
282 { 225 {
283 struct project project = {0}; 226 struct project project = {0};
284 struct req req; 227 struct apic req;
285 228
286 if (argc < 1) 229 if (argc < 2)
287 usage(); 230 usage();
288 if ((req = req_project_find(&project, argv[0])).status) 231 if (apic_project_find(&req, &project, argv[1]) < 0)
289 return req; 232 util_die("abort: unable to find project: %s\n", req.error);
290 233
291 printf("%-16s%d\n", "id:", project.id);
292 printf("%-16s%s\n", "name:", project.name); 234 printf("%-16s%s\n", "name:", project.name);
293 printf("%-16s%s\n", "desc:", project.desc); 235 printf("%-16s%s\n", "desc:", project.desc);
294 printf("%-16s%s\n", "url:", project.url); 236 printf("%-16s%s\n", "url:", project.url);
295 printf("\n"); 237 printf("\n");
296 printf("%s", project.script); 238 printf("%s", project.script);
297 239
298 return req; 240 apic_finish(&req);
299 } 241 project_finish(&project);
300 242 }
301 static struct req 243
244 static void
302 cmd_project_list(int argc, char **argv) 245 cmd_project_list(int argc, char **argv)
303 { 246 {
304 (void)argc; 247 (void)argc;
305 (void)argv; 248 (void)argv;
306 249
307 struct project projects[SCI_PROJECT_MAX] = {0}; 250 struct project projects[SCI_PROJECT_MAX] = {0};
308 struct req req; 251 struct apic req;
309 size_t projectsz = UTIL_SIZE(projects); 252 ssize_t projectsz;
310 253
311 if ((req = req_project_list(projects, &projectsz)).status) 254 if ((projectsz = apic_project_list(&req, projects, UTIL_SIZE(projects))) < 0)
312 return req; 255 util_die("abort: unable to list projects: %s\n", req.error);
313 256
314 for (size_t i = 0; i < projectsz; ++i) { 257 for (size_t i = 0; i < projectsz; ++i) {
315 printf("%-16s%d\n", "id:", projects[i].id);
316 printf("%-16s%s\n", "name:", projects[i].name); 258 printf("%-16s%s\n", "name:", projects[i].name);
317 printf("%-16s%s\n", "desc:", projects[i].desc); 259 printf("%-16s%s\n", "desc:", projects[i].desc);
318 printf("%-16s%s\n", "url:", projects[i].url); 260 printf("%-16s%s\n", "url:", projects[i].url);
319 261
320 if (i + 1 < projectsz) 262 if (i + 1 < projectsz)
321 printf("\n"); 263 printf("\n");
322 } 264
323 265 project_finish(&projects[i]);
324 return req; 266 }
325 } 267
326 268 apic_finish(&req);
327 static struct req 269 }
270
271 static void
328 cmd_worker_add(int argc, char **argv) 272 cmd_worker_add(int argc, char **argv)
329 { 273 {
330 struct worker wk = {0}; 274 struct worker wk = {0};
331 275 struct apic req;
332 if (argc < 2) 276
333 usage(); 277 if (argc < 3)
334 278 usage();
335 wk.name = argv[0]; 279
336 wk.desc = argv[1]; 280 wk.name = util_strdup(argv[1]);
337 281 wk.desc = util_strdup(argv[2]);
338 return req_worker_add(&wk); 282
339 } 283 if (apic_worker_save(&req, &wk) < 0)
340 284 util_die("abort: unable to save worker: %s\n", req.error);
341 static struct req 285
286 worker_finish(&wk);
287 apic_finish(&req);
288 }
289
290 static void
342 cmd_worker_list(int argc, char **argv) 291 cmd_worker_list(int argc, char **argv)
343 { 292 {
344 (void)argc; 293 (void)argc;
345 (void)argv; 294 (void)argv;
346 295
347 struct worker wk[SCI_WORKER_MAX]; 296 struct worker wk[SCI_WORKER_MAX];
348 struct req req; 297 struct apic req;
349 size_t wksz = UTIL_SIZE(wk); 298 ssize_t wksz;
350 299
351 if ((req = req_worker_list(wk, &wksz)).status) 300 if ((wksz = apic_worker_list(&req, wk, UTIL_SIZE(wk))) < 0)
352 return req; 301 util_die("abort: unable to list worker: %s\n", req.error);
353 302
354 for (size_t i = 0; i < wksz; ++i) { 303 for (size_t i = 0; i < wksz; ++i) {
355 printf("%-16s%d\n", "id:", wk[i].id);
356 printf("%-16s%s\n", "name:", wk[i].name); 304 printf("%-16s%s\n", "name:", wk[i].name);
357 printf("%-16s%s\n", "desc:", wk[i].desc); 305 printf("%-16s%s\n", "desc:", wk[i].desc);
358 306
359 if (i + 1 < wksz) 307 if (i + 1 < wksz)
360 printf("\n"); 308 printf("\n");
361 } 309
362 310 worker_finish(&wk[i]);
363 return req; 311 }
312
313 apic_finish(&req);
364 } 314 }
365 315
366 static struct { 316 static struct {
367 const char *name; 317 const char *name;
368 struct req (*exec)(int, char **); 318 void (*exec)(int, char **);
369 } commands[] = { 319 } commands[] = {
370 { "job-add", cmd_job_add }, 320 { "job-add", cmd_job_add },
371 { "job-todo", cmd_job_todo }, 321 { "job-todo", cmd_job_todo },
372 { "jobresult-add", cmd_jobresult_add }, 322 { "jobresult-add", cmd_jobresult_add },
373 { "project-add", cmd_project_add }, 323 { "project-add", cmd_project_add },
380 }; 330 };
381 331
382 int 332 int
383 main(int argc, char **argv) 333 main(int argc, char **argv)
384 { 334 {
385 int ch, cmdfound = 0; 335 int ch;
386 336
387 setprogname("scictl"); 337 opterr = 0;
388 338 setenv("POSIXLY_CORRECT", "1", 1);
389 while ((ch = getopt(argc, argv, "s:")) != -1) { 339
340 while ((ch = getopt(argc, argv, "u:")) != -1) {
390 switch (ch) { 341 switch (ch) {
391 case 's': 342 case 'u':
392 req_set_path(optarg); 343 util_strlcpy(apiconf.baseurl, optarg, sizeof (apiconf.baseurl));
344 break;
345 case '?':
346 util_die("abort: invalid option: %c\n", ch);
347 break;
348 case ':':
349 util_die("abort: missing value for option %c\n", ch);
393 break; 350 break;
394 default: 351 default:
395 break; 352 break;
396 } 353 }
397 } 354 }
398 355
399 argc -= optind; 356 argc -= optind;
400 argv += optind; 357 argv += optind;
401 358
402 if (argc <= 0) 359 optind = 1;
360
361 if (argc < 1)
403 usage(); 362 usage();
404 if (strcmp(argv[0], "help") == 0) 363 if (strcmp(argv[0], "help") == 0)
405 help(); 364 help();
406 365
407 for (size_t i = 0; commands[i].name; ++i) { 366 for (size_t i = 0; commands[i].name; ++i) {
408 struct req res;
409
410 if (strcmp(commands[i].name, argv[0]) == 0) { 367 if (strcmp(commands[i].name, argv[0]) == 0) {
411 res = commands[i].exec(--argc, ++argv); 368 commands[i].exec(argc, argv);
412 cmdfound = 1; 369 return 0;
413
414 if (res.status)
415 warnx("%s", json_string_value(json_object_get(res.msg, "error")));
416
417 req_finish(&res);
418 break;
419 } 370 }
420 } 371 }
421 372
422 if (!cmdfound) 373 util_die("abort: invalid command: %s\n", argv[0]);
423 errx(1, "abort: command %s not found", argv[0]); 374 }
424 }