diff mbox series

docker: rework and add support for network / volumes

Message ID 20240129084139.13063-1-stefano.babic@swupdate.org
State Accepted
Headers show
Series docker: rework and add support for network / volumes | expand

Commit Message

Stefano Babic Jan. 29, 2024, 8:41 a.m. UTC
Simplify the interface between handler and docker client. The client
functions are hidden and the handler just asks for a docker service.

Extend the API to support:
	- network: creation / deletion
	- volume : creation / deletion

Signed-off-by: Stefano Babic <stefano.babic@swupdate.org>
---
 containers/docker.c        | 188 +++++++++++++++++++++++++------------
 containers/docker.h        |  10 --
 handlers/docker_handler.c  | 149 +++++++++++++++++++++--------
 include/docker_interface.h |  26 +++--
 4 files changed, 254 insertions(+), 119 deletions(-)
diff mbox series

Patch

diff --git a/containers/docker.c b/containers/docker.c
index 4250bebf..dff19e17 100644
--- a/containers/docker.c
+++ b/containers/docker.c
@@ -27,16 +27,33 @@ 
 typedef struct {
 	const char *url;
 	channel_method_t method;
+	docker_fn func;
+	const char *desc;
 } docker_api_t;
 
+static server_op_res_t docker_container_create(const char *name, const char *setup);
+static server_op_res_t docker_image_remove(const char *name, const char *setup);
+static server_op_res_t docker_image_prune(const char *name, const char *setup);
+static server_op_res_t docker_container_remove(const char *name, const char *setup);
+static server_op_res_t docker_container_start(const char *name, const char *setup);
+static server_op_res_t docker_container_stop(const char *name, const char *setup);
+static server_op_res_t docker_volumes_create(const char *name, const char *setup);
+static server_op_res_t docker_volumes_remove(const char *name, const char *setup);
+static server_op_res_t docker_networks_create(const char *name, const char *setup);
+static server_op_res_t docker_networks_remove(const char *name, const char *setup);
+
 docker_api_t docker_api[] = {
-	[DOCKER_IMAGE_LOAD] = {"/images/load", CHANNEL_POST},
-	[DOCKER_IMAGE_DELETE] = {"/images/%s", CHANNEL_DELETE},
-	[DOCKER_IMAGE_PRUNE] = {"/images/prune", CHANNEL_POST},
-	[DOCKER_CONTAINER_CREATE] = {"/containers/create", CHANNEL_POST},
-	[DOCKER_CONTAINER_DELETE] = {"/containers/%s", CHANNEL_DELETE},
-	[DOCKER_CONTAINER_START] = {"/containers/%s/start", CHANNEL_POST},
-	[DOCKER_CONTAINER_STOP] = {"/containers/%s/stop", CHANNEL_POST},
+	[DOCKER_IMAGE_LOAD] = {"/images/load", CHANNEL_POST, NULL, "load image"},
+	[DOCKER_IMAGE_DELETE] = {"/images/%s", CHANNEL_DELETE, docker_image_remove, "remove image"},
+	[DOCKER_IMAGE_PRUNE] = {"/images/prune", CHANNEL_POST, docker_image_prune, "prune images"},
+	[DOCKER_CONTAINER_CREATE] = {"/containers/create", CHANNEL_POST, docker_container_create, "create container"},
+	[DOCKER_CONTAINER_DELETE] = {"/containers/%s", CHANNEL_DELETE, docker_container_remove, "remove container"},
+	[DOCKER_CONTAINER_START] = {"/containers/%s/start", CHANNEL_POST, docker_container_start, "start container"},
+	[DOCKER_CONTAINER_STOP] = {"/containers/%s/stop", CHANNEL_POST, docker_container_stop, "stop container"},
+	[DOCKER_VOLUMES_CREATE] = {"/volumes/create", CHANNEL_POST, docker_volumes_create, "create volume"},
+	[DOCKER_VOLUMES_DELETE] = {"/volumes/%s", CHANNEL_DELETE, docker_volumes_remove, "remove volume"},
+	[DOCKER_NETWORKS_CREATE] = {"/networks/create", CHANNEL_POST, docker_networks_create, "create network"},
+	[DOCKER_NETWORKS_DELETE] = {"/networks/%s", CHANNEL_DELETE, docker_networks_remove, "remove network"},
 };
 
 static channel_data_t channel_data_defaults = {.debug = true,
@@ -110,6 +127,53 @@  static server_op_res_t evaluate_docker_answer(json_object *json_reply)
 	return SERVER_EBADMSG;
 }
 
+static server_op_res_t docker_send_request(docker_services_t service, char *url, const char *setup)
+{
+	channel_t *channel;
+	channel_op_res_t ch_response;
+	server_op_res_t result = SERVER_OK;
+	channel_data_t channel_data = channel_data_defaults;
+
+	channel_data.url = url;
+	channel_data.method = docker_api[service].method;
+
+	channel = docker_prepare_channel(&channel_data);
+	if (!channel) {
+		return SERVER_EERR;
+	}
+
+	if (setup)
+		channel_data.request_body = (char *)setup;
+
+	ch_response = channel->put(channel, &channel_data);
+
+	if ((result = map_channel_retcode(ch_response)) !=
+	    SERVER_OK) {
+		channel->close(channel);
+		free(channel);
+		return SERVER_EERR;
+	}
+
+	channel->close(channel);
+	free(channel);
+
+	return result;
+}
+
+static server_op_res_t docker_simple_post(docker_services_t service, const char *name, const char *setup)
+{
+	char url[256];
+
+	docker_prepare_url(service, url, sizeof(url));
+	if (name) {
+		char *tmp=strdup(url);
+		snprintf(url, sizeof(url), tmp, name);
+		free(tmp);
+	}
+
+	return docker_send_request(service, url, setup);
+}
+
 server_op_res_t docker_image_load(int fd, size_t len)
 {
 	channel_t *channel;
@@ -157,87 +221,87 @@  server_op_res_t docker_image_load(int fd, size_t len)
 	return evaluate_docker_answer(channel_data.json_reply);
 }
 
-static server_op_res_t docker_send_request(docker_services_t service, char *url, char *setup)
-{
-	channel_t *channel;
-	channel_op_res_t ch_response;
-	server_op_res_t result = SERVER_OK;
-	channel_data_t channel_data = channel_data_defaults;
-
-	channel_data.url = url;
-	channel_data.method = docker_api[service].method;
-
-	channel = docker_prepare_channel(&channel_data);
-	if (!channel) {
-		return SERVER_EERR;
+docker_fn docker_fn_lookup(docker_services_t service) {
+
+	switch (service) {
+		case DOCKER_IMAGE_LOAD:
+		case DOCKER_IMAGE_DELETE:
+		case DOCKER_IMAGE_PRUNE:
+		case DOCKER_CONTAINER_CREATE:
+		case DOCKER_CONTAINER_DELETE:
+		case DOCKER_CONTAINER_START:
+		case DOCKER_CONTAINER_STOP:
+		case DOCKER_VOLUMES_CREATE:
+		case DOCKER_VOLUMES_DELETE:
+		case DOCKER_NETWORKS_CREATE:
+		case DOCKER_NETWORKS_DELETE:
+			break;
+		default:
+			return NULL;
 	}
 
-	if (setup)
-		channel_data.request_body = setup;
-
-	ch_response = channel->put(channel, &channel_data);
-
-	if ((result = map_channel_retcode(ch_response)) !=
-	    SERVER_OK) {
-		channel->close(channel);
-		free(channel);
-		return SERVER_EERR;
-	}
+	return docker_api[service].func;
+}
 
-	channel->close(channel);
-	free(channel);
+static server_op_res_t docker_send_with_parms(docker_services_t service, const char *name, const char *setup)
+{
+	char url[256];
+	
+	if (name) {
+		snprintf(url, sizeof(url), "%s%s?name=%s",
+			 docker_base_url(), docker_api[service].url, name);
+	} else
+		docker_prepare_url(service, url, sizeof(url));
 
-	return result;
+	return docker_send_request(service, url, setup);
 }
 
-static server_op_res_t docker_simple_post(docker_services_t service, const char *name)
+static server_op_res_t docker_container_create(const char *name, const char *setup)
 {
-	char url[256];
+	return docker_send_with_parms(DOCKER_CONTAINER_CREATE, name, setup);
+}
 
-	docker_prepare_url(service, url, sizeof(url));
-	if (name) {
-		char *tmp=strdup(url);
-		snprintf(url, sizeof(url), tmp, name);
-		free(tmp);
-	}
+static server_op_res_t docker_container_remove(const char *name, const char *setup)
+{
+	return docker_simple_post(DOCKER_CONTAINER_DELETE, name, setup);
+}
 
-	return docker_send_request(service, url, NULL);
+static server_op_res_t docker_container_start(const char *name, const char *setup)
+{
+	return docker_simple_post(DOCKER_CONTAINER_START, name, setup);
 }
 
-server_op_res_t docker_container_create(const char *name, char *setup)
+static server_op_res_t docker_container_stop(const char *name, const char *setup)
 {
-	char url[256];
-	
-	if (name) {
-		snprintf(url, sizeof(url), "%s%s?name=%s",
-			 docker_base_url(), docker_api[DOCKER_CONTAINER_CREATE].url, name);
-	} else
-		docker_prepare_url(DOCKER_CONTAINER_CREATE, url, sizeof(url));
+	return docker_simple_post(DOCKER_CONTAINER_STOP, name, setup);
+}
 
-	return docker_send_request(DOCKER_CONTAINER_CREATE, url, setup);
+static server_op_res_t docker_image_remove(const char *name, const char *setup)
+{
+	return docker_simple_post(DOCKER_IMAGE_DELETE, name, setup);
 }
 
-server_op_res_t docker_container_remove(const char *name)
+static server_op_res_t docker_image_prune(const char *name, const char *setup)
 {
-	return docker_simple_post(DOCKER_CONTAINER_DELETE, name);
+	return docker_simple_post(DOCKER_IMAGE_PRUNE, name, setup);
 }
 
-server_op_res_t docker_container_start(const char *name)
+static server_op_res_t docker_volumes_create(const char __attribute__ ((__unused__)) *name, const char *setup)
 {
-	return docker_simple_post(DOCKER_CONTAINER_START, name);
+	return docker_send_with_parms(DOCKER_VOLUMES_CREATE, NULL, setup);
 }
 
-server_op_res_t docker_container_stop(const char *name)
+static server_op_res_t docker_volumes_remove(const char *name, const char *setup)
 {
-	return docker_simple_post(DOCKER_CONTAINER_STOP, name);
+	return docker_simple_post(DOCKER_VOLUMES_DELETE, name, setup);
 }
 
-server_op_res_t docker_image_remove(const char *name)
+static server_op_res_t docker_networks_create(const char __attribute__ ((__unused__)) *name, const char *setup)
 {
-	return docker_simple_post(DOCKER_IMAGE_DELETE, name);
+	return docker_send_with_parms(DOCKER_NETWORKS_CREATE, NULL, setup);
 }
 
-server_op_res_t docker_image_prune(const char *name)
+static server_op_res_t docker_networks_remove(const char *name, const char *setup)
 {
-	return docker_simple_post(DOCKER_IMAGE_PRUNE, name);
+	return docker_simple_post(DOCKER_NETWORKS_DELETE, name, setup);
 }
diff --git a/containers/docker.h b/containers/docker.h
index 84ad3a0d..9d72db17 100644
--- a/containers/docker.h
+++ b/containers/docker.h
@@ -21,13 +21,3 @@ 
 /* Docker base URL */
 
 #define DOCKER_BASE_URL	DOCKER_SOCKET_URL DOCKER_API_VERSION "/"
-
-typedef enum {
-	DOCKER_IMAGE_LOAD,
-	DOCKER_IMAGE_DELETE,
-	DOCKER_IMAGE_PRUNE,
-	DOCKER_CONTAINER_CREATE,
-	DOCKER_CONTAINER_DELETE,
-	DOCKER_CONTAINER_START,
-	DOCKER_CONTAINER_STOP
-} docker_services_t;
diff --git a/handlers/docker_handler.c b/handlers/docker_handler.c
index 0509abd2..e9cbae0c 100644
--- a/handlers/docker_handler.c
+++ b/handlers/docker_handler.c
@@ -28,16 +28,6 @@ 
 #include "swupdate_image.h"
 #include "docker_interface.h"
 
-void docker_loadimage_handler(void);
-void docker_deleteimage_handler(void);
-void docker_pruneimage_handler(void);
-void docker_createcontainer_handler(void);
-void docker_deletecontainer_handler(void);
-void docker_container_start_handler(void);
-void docker_container_stop_handler(void);
-
-typedef server_op_res_t (*docker_fn)(const char *name);
-
 #define FIFO_THREAD_READ	0
 #define FIFO_HND_WRITE		1
 
@@ -154,10 +144,11 @@  handler_exit:
 }
 
 /*
- * Implementation POST /container/create
+ * Docker API requires one parameter and maybe a JSON file used as configuration.
+ * Pass to the docker client the properties (only name is checked) and a JSON file if
+ * present. This is the script itself and not an "artifact image".
  */
-static int docker_create_container(struct img_type *img,
-	void __attribute__ ((__unused__)) *data)
+static int docker_send_cmd_with_setup(struct img_type *img, void *data, docker_services_t service)
 {
 	struct script_handler_data *script_data = data;
 	char *script = NULL;
@@ -172,7 +163,6 @@  static int docker_create_container(struct img_type *img,
 	if (!script_data || script_data->scriptfn != POSTINSTALL)
 		return 0;
 
-
 	if (asprintf(&script, "%s%s", get_tmpdirscripts(), img->fname) == ENOMEM_ASPRINTF) {
 		ERROR("OOM when creating script path");
 		return -ENOMEM;
@@ -181,14 +171,17 @@  static int docker_create_container(struct img_type *img,
 	if (stat(script, &sb) == -1) {
 		ERROR("stat fails on %s", script);
 		result = -EFAULT;
-		goto create_container_exit;
+		goto send_to_docker_exit;
 	}
 
+	/*
+	 * Load the script / JSON config in memory
+	 */
 	fd = open(script, O_RDONLY);
 	if (fd < 0) {
 		ERROR("%s cannot be opened, exiting..", script);
 		result = -EFAULT;
-		goto create_container_exit;
+		goto send_to_docker_exit;
 	}
 
 	buf = (char *)malloc(sb.st_size);
@@ -196,7 +189,7 @@  static int docker_create_container(struct img_type *img,
 		ERROR("OOM creating buffer for reading %s of %ld bytes",
 		      script, sb.st_size);
 		result =  -ENOMEM;
-		goto create_container_exit;
+		goto send_to_docker_exit;
 	}
 
 	ssize_t n = read(fd, buf, sb.st_size);
@@ -204,16 +197,32 @@  static int docker_create_container(struct img_type *img,
 		ERROR("Script %s cannot be read, return value %ld != %ld",
 		      script, n, sb.st_size);
 		result = -EFAULT;
-		goto create_container_exit;
+		goto send_to_docker_exit;
 	}
 
+	/*
+	 * Check for a "name" properties - this is mandatory
+	 * when a resource is deleted
+	 */
 	char *name = dict_get_value(&img->properties, "name");
 
-	TRACE("DOCKER CREATE CONTAINER");
+	/*
+	 * Retrieve which function is responsible for a service
+	 */
+	docker_fn fn = docker_fn_lookup(service);
 
-	result = docker_container_create(name, buf);
+	/*
+	 * Call docker internal client
+	 */
+	if (fn) {
+		result = fn(name, buf);
+	} else {
+		result = -EINVAL;
+		ERROR("Service %d not supported", service); 
+	}
 
-create_container_exit:
+send_to_docker_exit:
+	/* cleanup and exit */
 	free(script);
 	free(buf);
 	if (fd > 0) close(fd);
@@ -223,11 +232,14 @@  create_container_exit:
 }
 
 /*
- * Implementation DELETE /container/{id}
+ * Simple service without configuration file
+ * Just lokup for the client function and call it
  */
-static int docker_query(struct img_type *img, void *data, docker_fn fn)
+static int docker_query(struct img_type *img, void *data, docker_services_t service)
 {
 	struct script_handler_data *script_data = data;
+	docker_fn fn;
+
 	/*
 	 * Call only in case of postinstall
 	 */
@@ -241,52 +253,85 @@  static int docker_query(struct img_type *img, void *data, docker_fn fn)
 		return -EINVAL;
 	}
 
-	return fn(name);
+	fn = docker_fn_lookup(service);
+
+	if (!fn) {
+		ERROR("Docker service %d nbot supported", service);
+		return -EINVAL;
+	}
+	return fn(name, NULL);
+}
+
+/* Docker service wrappers */
+static int container_create(struct img_type *img, void *data)
+{
+	return docker_send_cmd_with_setup(img, data, DOCKER_CONTAINER_CREATE);
 }
 
 static int container_delete(struct img_type *img, void *data)
 {
-	return docker_query(img, data, docker_container_remove);
+	return docker_query(img, data, DOCKER_CONTAINER_DELETE);
 }
 
 static int image_delete(struct img_type *img, void *data)
 {
-	return docker_query(img, data, docker_image_remove);
+	return docker_query(img, data, DOCKER_IMAGE_DELETE);
 }
 
 static int image_prune(struct img_type *img, void *data)
 {
-	return docker_query(img, data, docker_image_prune);
+	return docker_query(img, data, DOCKER_IMAGE_PRUNE);
 }
 
-
 static int container_start(struct img_type *img, void *data)
 {
-	return docker_query(img, data, docker_container_start);
+	return docker_query(img, data, DOCKER_CONTAINER_START);
+}
+
+static int network_create(struct img_type *img, void *data)
+{
+	return docker_send_cmd_with_setup(img, data, DOCKER_NETWORKS_CREATE);
+}
+
+static int network_delete(struct img_type *img, void *data)
+{
+	return docker_query(img, data, DOCKER_NETWORKS_DELETE);
+}
+
+static int volume_create(struct img_type *img, void *data)
+{
+	return docker_send_cmd_with_setup(img, data, DOCKER_VOLUMES_CREATE);
+}
+
+static int volume_delete(struct img_type *img, void *data)
+{
+	return docker_query(img, data, DOCKER_VOLUMES_DELETE);
 }
 
 
 static int container_stop(struct img_type *img, void *data)
 {
-	return docker_query(img, data, docker_container_stop);
+	return docker_query(img, data, DOCKER_CONTAINER_STOP);
 }
 
+/* Handlers entry points */
+
 __attribute__((constructor))
-void docker_loadimage_handler(void)
+static void docker_loadimage_handler(void)
 {
 	register_handler("docker_imageload", docker_install_image,
 				IMAGE_HANDLER, NULL);
 }
 
 __attribute__((constructor))
-void docker_deleteimage_handler(void)
+static void docker_deleteimage_handler(void)
 {
 	register_handler("docker_imagedelete", image_delete,
 				SCRIPT_HANDLER | NO_DATA_HANDLER, NULL);
 }
 
 __attribute__((constructor))
-void docker_pruneimage_handler(void)
+static void docker_pruneimage_handler(void)
 {
 	register_handler("docker_imageprune", image_prune,
 				SCRIPT_HANDLER | NO_DATA_HANDLER, NULL);
@@ -294,29 +339,57 @@  void docker_pruneimage_handler(void)
 
 
 __attribute__((constructor))
-void docker_createcontainer_handler(void)
+static void docker_createcontainer_handler(void)
 {
-	register_handler("docker_containercreate", docker_create_container,
+	register_handler("docker_containercreate", container_create,
 				SCRIPT_HANDLER, NULL);
 }
 
 __attribute__((constructor))
-void docker_deletecontainer_handler(void)
+static void docker_deletecontainer_handler(void)
 {
 	register_handler("docker_containerdelete", container_delete,
 				SCRIPT_HANDLER | NO_DATA_HANDLER, NULL);
 }
 
 __attribute__((constructor))
-void docker_container_start_handler(void)
+static void docker_container_start_handler(void)
 {
 	register_handler("docker_containerstart", container_start,
 				SCRIPT_HANDLER | NO_DATA_HANDLER, NULL);
 }
 
 __attribute__((constructor))
-void docker_container_stop_handler(void)
+static void docker_container_stop_handler(void)
 {
 	register_handler("docker_containerstart", container_stop,
 				SCRIPT_HANDLER | NO_DATA_HANDLER, NULL);
 }
+
+__attribute__((constructor))
+static void docker_createnetwork_handler(void)
+{
+	register_handler("docker_networkcreate", network_create,
+				SCRIPT_HANDLER, NULL);
+}
+
+__attribute__((constructor))
+static void docker_deletenetwork_handler(void)
+{
+	register_handler("docker_networkdelete", network_delete,
+				SCRIPT_HANDLER | NO_DATA_HANDLER, NULL);
+}
+
+__attribute__((constructor))
+static void docker_createvolume_handler(void)
+{
+	register_handler("docker_volumecreate", volume_create,
+				SCRIPT_HANDLER, NULL);
+}
+
+__attribute__((constructor))
+static void docker_deletevolume_handler(void)
+{
+	register_handler("docker_volumedelete", volume_delete,
+				SCRIPT_HANDLER | NO_DATA_HANDLER, NULL);
+}
diff --git a/include/docker_interface.h b/include/docker_interface.h
index f77453d6..789c995c 100644
--- a/include/docker_interface.h
+++ b/include/docker_interface.h
@@ -9,13 +9,21 @@ 
 
 #include "server_utils.h"
 
-/* Load an image
- * fd : file descriptor where to read the stream to be pushed
- */
+typedef enum {
+	DOCKER_IMAGE_LOAD,
+	DOCKER_IMAGE_DELETE,
+	DOCKER_IMAGE_PRUNE,
+	DOCKER_CONTAINER_CREATE,
+	DOCKER_CONTAINER_DELETE,
+	DOCKER_CONTAINER_START,
+	DOCKER_CONTAINER_STOP,
+	DOCKER_VOLUMES_CREATE,
+	DOCKER_VOLUMES_DELETE,
+	DOCKER_NETWORKS_CREATE,
+	DOCKER_NETWORKS_DELETE,
+	DOCKER_SERVICE_LAST = DOCKER_NETWORKS_DELETE,
+} docker_services_t;
+
+typedef server_op_res_t (*docker_fn)(const char *name, const char *setup);
+docker_fn docker_fn_lookup(docker_services_t service);
 server_op_res_t docker_image_load(int fd, size_t nbytes);
-server_op_res_t docker_image_remove(const char *name);
-server_op_res_t docker_image_prune(const char *name);
-server_op_res_t docker_container_create(const char *name, char *setup);
-server_op_res_t docker_container_remove(const char *name);
-server_op_res_t docker_container_start(const char *name);
-server_op_res_t docker_container_stop(const char *name);