view scictl/scictl.c @ 24:34cbbd215ef7

misc: add basic support for jobresults
author David Demelier <markand@malikania.fr>
date Mon, 25 Jul 2022 21:11:23 +0200
parents 2cb228f23f53
children dae2de19ca5d
line wrap: on
line source

/*
 * scictl.c -- main scictl(8) utility file
 *
 * Copyright (c) 2021 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <limits.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "apic.h"
#include "config.h"
#include "types.h"
#include "util.h"

static void
usage(void)
{
	fprintf(stderr, "usage: %s [-u baseurl] command [args...]\n", getprogname());
	exit(1);
}

static void
help(void)
{
	fprintf(stderr, "usage: scictl job-add project tag\n");
	fprintf(stderr, "       scictl job-todo worker\n");
	fprintf(stderr, "       scictl jobresult-add id worker exitcode console\n");
	fprintf(stderr, "       scictl project-add name desc url script\n");
	fprintf(stderr, "       scictl project-info name\n");
	fprintf(stderr, "       scictl project-list\n");
	fprintf(stderr, "       scictl project-update name key value\n");
	fprintf(stderr, "       scictl worker-add name desc\n");
	fprintf(stderr, "       scictl worker-list\n");
	exit(0);
}

static inline void
replace(char **str, const char *new)
{
	free(*str);
	*str = util_strdup(new);
}

long long
toint(const char *s)
{
	long long v;
	const char *err;

	v = util_strtonum(s, 0, LLONG_MAX, &err);

	if (err)
		util_die("abort: %s\n", err);

	return v;
}

static char *
readfile(const char *path)
{
	FILE *fp, *str;
	char buf[BUFSIZ], *console = NULL;
	size_t nr, consolesz = 0;

	if (strcmp(path, "-") == 0)
		fp = stdin;
	else if (!(fp = fopen(path, "r")))
		util_die("abort: %s: %s\n", path, strerror(errno));

	if (!(str = open_memstream(&console, &consolesz)))
		util_die("abort: open_memstream: %s\n", strerror(errno));

	while ((nr = fread(buf, 1, sizeof (buf), fp)) > 0)
		fwrite(buf, 1, nr, str);

	if ((ferror(fp) && !feof(fp)) || (ferror(str) && !feof(str)))
		util_die("abort: %s\n", strerror(errno));

	fclose(str);
	fclose(fp);

	return console;
}

static void
cmd_job_add(int argc, char **argv)
{
	struct job job = {0};
	struct apic req;

	if (argc < 3)
		usage();

	job.project_name = util_strdup(argv[1]);
	job.tag = util_strdup(argv[2]);

	if (apic_job_add(&req, &job) < 0)
		util_die("abort: %s\n", req.error);

	apic_finish(&req);
	job_finish(&job);
}

static void
cmd_job_todo(int argc, char **argv)
{
	struct job jobs[SCI_JOB_LIST_MAX] = {0};
	size_t jobsz;
	struct apic req;

	if (argc < 2)
		usage();

	if ((jobsz = apic_job_todo(&req, jobs, UTIL_SIZE(jobs), argv[1])))
		util_die("abort: %s\n", req.error);

	for (size_t i = 0; i < jobsz; ++i) {
		printf("%-16s%jd\n", "id:", jobs[i].id);
		printf("%-16s%s\n", "tag:", jobs[i].tag);
		printf("%-16s%s\n", "project:", jobs[i].project_name);

		if (i + 1 < jobsz)
			printf("\n");

		job_finish(&jobs[i]);
	}

	apic_finish(&req);
}

static void
cmd_jobresult_add(int argc, char **argv)
{
	struct jobresult res = {0};
	struct apic req;

	if (argc < 5)
		usage();

	res.job_id = toint(argv[1]);
	res.worker_name = util_strdup(argv[2]);
	res.exitcode = toint(argv[3]);
	res.log = readfile(argv[4]);

	if (apic_jobresult_add(&req, &res) < 0)
		util_die("abort: unable to add job result: %s\n", req.error);

	apic_finish(&req);
	jobresult_finish(&res);
}

static void
cmd_project_add(int argc, char **argv)
{
	struct project pc = {0};
	struct apic req;

	if (argc < 5)
		usage();

	pc.name = util_strdup(argv[1]);
	pc.desc = util_strdup(argv[2]);
	pc.url = util_strdup(argv[3]);
	pc.script = readfile(argv[4]);

	if (apic_project_save(&req, &pc) < 0)
		util_die("abort: unable to create project: %s\n", req.error);

	apic_finish(&req);
	project_finish(&pc);
}

static void
cmd_project_update(int argc, char **argv)
{
	struct project pc;
	struct apic req;

	if (argc < 4)
		usage();

	if (apic_project_find(&req, &pc, argv[1]) < 0)
		util_die("abort: unable to find project: %s\n", req.error);

	if (strcmp(argv[2], "name") == 0)
		replace(&pc.name, argv[3]);
	else if (strcmp(argv[2], "desc") == 0)
		replace(&pc.desc, argv[3]);
	else if (strcmp(argv[2], "url") == 0)
		replace(&pc.url, argv[3]);
	else if (strcmp(argv[2], "script") == 0)
		replace(&pc.script, readfile(argv[3]));

	if (apic_project_save(&req, &pc) < 0)
		util_die("abort: unable to save project: %s\n", req.error);

	apic_finish(&req);
	project_finish(&pc);
}

static void
cmd_project_info(int argc, char **argv)
{
	struct project project = {0};
	struct apic req;

	if (argc < 2)
		usage();
	if (apic_project_find(&req, &project, argv[1]) < 0)
		util_die("abort: unable to find project: %s\n", req.error);

	printf("%-16s%s\n", "name:", project.name);
	printf("%-16s%s\n", "desc:", project.desc);
	printf("%-16s%s\n", "url:", project.url);
	printf("\n");
	printf("%s", project.script);

	apic_finish(&req);
	project_finish(&project);
}

static void
cmd_project_list(int argc, char **argv)
{
	(void)argc;
	(void)argv;

	struct project projects[SCI_PROJECT_MAX] = {0};
	struct apic req;
	ssize_t projectsz;

	if ((projectsz = apic_project_list(&req, projects, UTIL_SIZE(projects))) < 0)
		util_die("abort: unable to list projects: %s\n", req.error);

	for (ssize_t i = 0; i < projectsz; ++i) {
		printf("%-16s%s\n", "name:", projects[i].name);
		printf("%-16s%s\n", "desc:", projects[i].desc);
		printf("%-16s%s\n", "url:", projects[i].url);

		if (i + 1 < projectsz)
			printf("\n");

		project_finish(&projects[i]);
	}

	apic_finish(&req);
}

static void
cmd_worker_add(int argc, char **argv)
{
	struct worker wk = {0};
	struct apic req;

	if (argc < 3)
		usage();

	wk.name = util_strdup(argv[1]);
	wk.desc = util_strdup(argv[2]);

	if (apic_worker_save(&req, &wk) < 0)
		util_die("abort: unable to save worker: %s\n", req.error);

	worker_finish(&wk);
	apic_finish(&req);
}

static void
cmd_worker_list(int argc, char **argv)
{
	(void)argc;
	(void)argv;

	struct worker wk[SCI_WORKER_MAX];
	struct apic req;
	ssize_t wksz;

	if ((wksz = apic_worker_list(&req, wk, UTIL_SIZE(wk))) < 0)
		util_die("abort: unable to list worker: %s\n", req.error);

	for (ssize_t i = 0; i < wksz; ++i) {
		printf("%-16s%s\n", "name:", wk[i].name);
		printf("%-16s%s\n", "desc:", wk[i].desc);

		if (i + 1 < wksz)
			printf("\n");

		worker_finish(&wk[i]);
	}

	apic_finish(&req);
}

static struct {
	const char *name;
	void (*exec)(int, char **);
} commands[] = {
	{ "job-add",            cmd_job_add             },
	{ "job-todo",           cmd_job_todo            },
	{ "jobresult-add",      cmd_jobresult_add       },
	{ "project-add",        cmd_project_add         },
	{ "project-info",       cmd_project_info        },
	{ "project-list",       cmd_project_list        },
	{ "project-update",     cmd_project_update      },
	{ "worker-add",         cmd_worker_add          },
	{ "worker-list",        cmd_worker_list         },
	{ NULL,                 NULL                    }
};

int
main(int argc, char **argv)
{
	int ch;

	opterr = 0;
	setenv("POSIXLY_CORRECT", "1", 1);

	while ((ch = getopt(argc, argv, "u:")) != -1) {
		switch (ch) {
		case 'u':
			util_strlcpy(apiconf.baseurl, optarg, sizeof (apiconf.baseurl));
			break;
		case '?':
			util_die("abort: invalid option: %c\n", ch);
			break;
		case ':':
			util_die("abort: missing value for option %c\n", ch);
			break;
		default:
			break;
		}
	}

	argc -= optind;
	argv += optind;

	optind = 1;

	if (argc < 1)
		usage();
	if (strcmp(argv[0], "help") == 0)
		help();

	for (size_t i = 0; commands[i].name; ++i) {
		if (strcmp(commands[i].name, argv[0]) == 0) {
			commands[i].exec(argc, argv);
			return 0;
		}
	}

	util_die("abort: invalid command: %s\n", argv[0]);
}