From 4b0df84dbe0fe4bd018d993760f606d61a4289c8 Mon Sep 17 00:00:00 2001 From: 0utplay Date: Fri, 9 Jun 2023 15:17:23 +0200 Subject: [PATCH 1/2] feat: add custom labels to docker services --- .../modules/docker/DockerCommand.java | 32 +++++++++++++++++++ .../DockerizedLocalCloudServiceFactory.java | 5 +++ .../modules/docker/DockerizedService.java | 27 ++++++++++++---- .../docker/config/DockerConfiguration.java | 6 ++-- .../docker/config/TaskDockerConfig.java | 26 +++++++++++++-- 5 files changed, 84 insertions(+), 12 deletions(-) diff --git a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerCommand.java b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerCommand.java index 1dc92bce9a..2ac000bc0b 100644 --- a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerCommand.java +++ b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerCommand.java @@ -33,6 +33,7 @@ import eu.cloudnetservice.node.command.annotation.Description; import eu.cloudnetservice.node.command.source.CommandSource; import jakarta.inject.Singleton; +import java.util.Map; import java.util.Set; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -165,6 +166,37 @@ public void removeExposedPort( source.sendMessage(I18n.trans("command-tasks-remove-collection-property", "exposedPort", task.name(), port)); } + @CommandMethod("docker task add label ") + public void addLabel( + @NonNull CommandSource source, + @Argument("task") @NonNull ServiceTask task, + @Argument("key") @NonNull String key, + @Argument("value") @NonNull String value + ) { + var entry = Map.entry(key, value); + this.updateTaskDockerConfig(task, ($, builder) -> builder.addLabel(entry)); + source.sendMessage(I18n.trans("command-tasks-add-collection-property", "labels", task.name(), entry)); + } + + @CommandMethod("docker task clear labels") + public void clearLabels( + @NonNull CommandSource source, + @Argument("task") @NonNull ServiceTask task + ) { + this.updateTaskDockerConfig(task, ($, builder) -> builder.labels(Map.of())); + source.sendMessage(I18n.trans("command-tasks-clear-property", "labels", task.name())); + } + + @CommandMethod("docker task remove label ") + public void removeLabel( + @NonNull CommandSource source, + @Argument("task") @NonNull ServiceTask task, + @Argument("key") @NonNull String key + ) { + this.updateTaskDockerConfig(task, (config, builder) -> builder.removeLabel(key)); + source.sendMessage(I18n.trans("command-tasks-remove-collection-property", "labels", task.name(), key)); + } + @CommandMethod("docker config network ") public void setNetwork(@NonNull CommandSource source, @Argument("network") @NonNull String network) { this.updateDockerConfig(($, builder) -> builder.network(network)); diff --git a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedLocalCloudServiceFactory.java b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedLocalCloudServiceFactory.java index 5696f93188..fc075dd3a8 100644 --- a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedLocalCloudServiceFactory.java +++ b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedLocalCloudServiceFactory.java @@ -18,6 +18,7 @@ import com.github.dockerjava.api.DockerClient; import eu.cloudnetservice.driver.event.EventManager; +import eu.cloudnetservice.driver.provider.ServiceTaskProvider; import eu.cloudnetservice.driver.service.ServiceConfiguration; import eu.cloudnetservice.modules.docker.config.DockerConfiguration; import eu.cloudnetservice.node.TickLoop; @@ -36,6 +37,7 @@ public class DockerizedLocalCloudServiceFactory extends BaseLocalCloudServiceFac protected final TickLoop mainThread; protected final EventManager eventManager; protected final DockerClient dockerClient; + protected final ServiceTaskProvider taskProvider; protected final DockerConfiguration dockerConfiguration; protected final CloudServiceManager cloudServiceManager; @@ -47,6 +49,7 @@ public DockerizedLocalCloudServiceFactory( @NonNull EventManager eventManager, @NonNull ServiceVersionProvider versionProvider, @NonNull DockerClient dockerClient, + @NonNull ServiceTaskProvider taskProvider, @NonNull DockerConfiguration configuration ) { super(nodeConfig, versionProvider); @@ -54,6 +57,7 @@ public DockerizedLocalCloudServiceFactory( this.eventManager = eventManager; this.cloudServiceManager = cloudServiceManager; this.dockerClient = dockerClient; + this.taskProvider = taskProvider; this.dockerConfiguration = configuration; } @@ -70,6 +74,7 @@ public DockerizedLocalCloudServiceFactory( return new DockerizedService( this.mainThread, this.configuration, + this.taskProvider, config, manager, this.eventManager, diff --git a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedService.java b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedService.java index e53db41090..0e0316b267 100644 --- a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedService.java +++ b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedService.java @@ -34,7 +34,9 @@ import com.google.common.collect.Lists; import eu.cloudnetservice.common.util.StringUtil; import eu.cloudnetservice.driver.event.EventManager; +import eu.cloudnetservice.driver.provider.ServiceTaskProvider; import eu.cloudnetservice.driver.service.ServiceConfiguration; +import eu.cloudnetservice.driver.service.ServiceTask; import eu.cloudnetservice.modules.docker.config.DockerConfiguration; import eu.cloudnetservice.modules.docker.config.DockerImage; import eu.cloudnetservice.modules.docker.config.TaskDockerConfig; @@ -51,6 +53,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -83,6 +86,7 @@ public class DockerizedService extends JVMService { Capability.NET_BIND_SERVICE ).toArray(Capability[]::new); + protected final ServiceTask selfTask; protected final DockerClient dockerClient; protected final DockerConfiguration configuration; protected final DockerizedServiceLogCache logCache; @@ -96,6 +100,7 @@ public class DockerizedService extends JVMService { protected DockerizedService( @NonNull TickLoop tickLoop, @NonNull Configuration nodeConfig, + @NonNull ServiceTaskProvider taskProvider, @NonNull ServiceConfiguration configuration, @NonNull CloudServiceManager manager, @NonNull EventManager eventManager, @@ -106,6 +111,7 @@ protected DockerizedService( ) { super(tickLoop, nodeConfig, configuration, manager, eventManager, versionProvider, serviceConfigurationPreparer); + this.selfTask = taskProvider.serviceTask(configuration.serviceId().taskName()); this.dockerClient = dockerClient; this.configuration = dockerConfiguration; @@ -197,6 +203,19 @@ protected void doStartProcess( // an isolated, single java installation available which is always accessible via 'java' arguments.set(0, "java"); + // build the default docker labels and add the configured labels later on to make sure + // that the user can override any default labels + var labels = new HashMap<>(Map.of( + "Service", "CloudNet", + "Name", this.serviceId().name(), + "Uid", this.serviceId().uniqueId().toString(), + "Id", Integer.toString(this.serviceId().taskServiceId()))); + + var taskLabels = this.readFromTaskConfig(TaskDockerConfig::labels); + if (taskLabels != null) { + labels.putAll(taskLabels); + } + // create the container and store the container id this.containerId = this.dockerClient.createContainerCmd(image.imageName()) .withEnv(env) @@ -216,11 +235,7 @@ protected void doStartProcess( .withRestartPolicy(RestartPolicy.noRestart()) .withNetworkMode(this.configuration.network()) .withLogConfig(new LogConfig(LogConfig.LoggingType.LOCAL, LOGGING_OPTIONS))) - .withLabels(Map.of( - "Service", "CloudNet", - "Name", this.serviceId().name(), - "Uid", this.serviceId().uniqueId().toString(), - "Id", Integer.toString(this.serviceId().taskServiceId()))) + .withLabels(labels) .exec() .getId(); } @@ -317,7 +332,7 @@ public void doDelete() { } protected @Nullable T readFromTaskConfig(@NonNull Function reader) { - var config = this.serviceConfiguration.propertyHolder().readObject("dockerConfig", TaskDockerConfig.class); + var config = this.selfTask.propertyHolder().readObject("dockerConfig", TaskDockerConfig.class); return config == null ? null : reader.apply(config); } diff --git a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/config/DockerConfiguration.java b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/config/DockerConfiguration.java index 5e0448668c..633164fd36 100644 --- a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/config/DockerConfiguration.java +++ b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/config/DockerConfiguration.java @@ -166,9 +166,9 @@ public static final class Builder { this.factoryName, this.network, this.javaImage, - this.volumes, - this.binds, - this.exposedPorts, + Set.copyOf(this.volumes), + Set.copyOf(this.binds), + Set.copyOf(this.exposedPorts), this.dockerHost, this.dockerCertPath, this.registryUsername, diff --git a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/config/TaskDockerConfig.java b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/config/TaskDockerConfig.java index 52f96c4a52..9e736e78d0 100644 --- a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/config/TaskDockerConfig.java +++ b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/config/TaskDockerConfig.java @@ -17,7 +17,9 @@ package eu.cloudnetservice.modules.docker.config; import com.github.dockerjava.api.model.ExposedPort; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import lombok.NonNull; import org.jetbrains.annotations.Nullable; @@ -26,7 +28,8 @@ public record TaskDockerConfig( @Nullable DockerImage javaImage, @NonNull Set volumes, @NonNull Set binds, - @NonNull Set exposedPorts + @NonNull Set exposedPorts, + @NonNull Map labels ) { public static @NonNull Builder builder() { @@ -38,7 +41,8 @@ public record TaskDockerConfig( .javaImage(config.javaImage()) .volumes(config.volumes()) .binds(config.binds()) - .exposedPorts(config.exposedPorts()); + .exposedPorts(config.exposedPorts()) + .labels(config.labels()); } public static class Builder { @@ -47,6 +51,7 @@ public static class Builder { private Set volumes = new HashSet<>(); private Set binds = new HashSet<>(); private Set exposedPorts = new HashSet<>(); + private Map labels = new HashMap<>(); public @NonNull Builder javaImage(@Nullable DockerImage javaImage) { this.javaImage = javaImage; @@ -83,8 +88,23 @@ public static class Builder { return this; } + public @NonNull Builder labels(@NonNull Map labels) { + this.labels = new HashMap<>(labels); + return this; + } + + public @NonNull Builder addLabel(@NonNull Map.Entry label) { + this.labels.put(label.getKey(), label.getValue()); + return this; + } + + public @NonNull Builder removeLabel(@NonNull String label) { + this.labels.remove(label); + return this; + } + public @NonNull TaskDockerConfig build() { - return new TaskDockerConfig(this.javaImage, this.volumes, this.binds, this.exposedPorts); + return new TaskDockerConfig(this.javaImage, this.volumes, this.binds, this.exposedPorts, this.labels); } } } From bb58e759cda44f4810c12936c15f23027cdb78fe Mon Sep 17 00:00:00 2001 From: 0utplay Date: Fri, 9 Jun 2023 15:23:30 +0200 Subject: [PATCH 2/2] fix: allow services without existing task --- .../cloudnetservice/modules/docker/DockerizedService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedService.java b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedService.java index 0e0316b267..7f0eb3ddc3 100644 --- a/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedService.java +++ b/modules/dockerized-services/src/main/java/eu/cloudnetservice/modules/docker/DockerizedService.java @@ -332,6 +332,12 @@ public void doDelete() { } protected @Nullable T readFromTaskConfig(@NonNull Function reader) { + // it is possible to start a service with a task name that is not linked to a real task therefore we need to + // make sure that the task exists before proceeding + if (this.selfTask == null) { + return null; + } + var config = this.selfTask.propertyHolder().readObject("dockerConfig", TaskDockerConfig.class); return config == null ? null : reader.apply(config); }