diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3bc3acc..de78516 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,6 +8,7 @@ on:
push:
branches:
- 'latest'
+ - 'experimental'
tags:
- 'v*'
diff --git a/.gitignore b/.gitignore
index 26e1da1..6e80684 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,3 +37,4 @@ build/
### Config Files ###
/src/main/resources/application.properties
/src/main/resources/application-local.properties
+/data/
diff --git a/README.md b/README.md
index dde2e99..c65205d 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,6 @@ Tool to manage your docker compose deployments via git.
## Table of Contents
-- [Dirigent](#dirigent)
-- [Table of Contents](#table-of-contents)
- [Setup](#setup)
- [docker-compose](#docker-compose)
- [docker CLI](#docker-cli)
@@ -16,11 +14,16 @@ Tool to manage your docker compose deployments via git.
- [API](#api)
- [Gitea Webhook](#gitea-webhook)
- [Deployments](#deployments)
- - [Start All Deployments](#start-all-deployments)
- - [Start Deployment by name](#start-deployment-by-name)
+ - [Start](#start)
+ - [All Deployments](#all-deployments)
+ - [Deployment by name](#deployment-by-name)
+ - [Stop](#stop)
+ - [Deployment by name](#deployment-by-name-1)
+ - [State](#state)
- [Develop](#develop)
- [Setup for local Tests](#setup-for-local-tests)
+
## Setup
### docker-compose
@@ -45,6 +48,7 @@ services:
volumes:
- /path/to/config:/app/config
- /path/to/deployments:/app/deployments
+ - /path/to/data:/app/data
- /var/run/docker.sock:/var/run/docker.sock
```
@@ -70,6 +74,7 @@ docker run -d \
-e DIRIGENT_GOTIFY_TOKEN= \
-v /path/to/config:/app/config \
-v /path/to/deployments:/app/deployments \
+ -v /path/to/data:/app/data \
-v /var/run/docker.sock:/var/run/docker.sock \
ghcr.io/derdavidbohl/dirigent-spring:latest
```
@@ -104,11 +109,12 @@ deployments:
### Volumes
-| Volume | Description |
-|----------------------|------------------------------------|
-| /app/config | Config directory for Dirigent |
-| /app/deployments | Deployments directory for Dirigent |
-| /var/run/docker.sock | Docker socket for Dirigent |
+| Volume | Description |
+|----------------------|----------------------------------------|
+| /app/config | Config directory for Dirigent |
+| /app/deployments | Deployments directory for Dirigent |
+| /app/data | Data directory containing the database |
+| /var/run/docker.sock | Docker socket for Dirigent |
### Step by Step (Gitea)
@@ -144,13 +150,33 @@ Store all your repositories for one host in one gitea organization. This way you
### Deployments
-#### Start All Deployments:
+#### Start
-`POST` to `/api/v1/deployments/all/start` optional add `force=true` if you want to force deployment and recreation of containers.
+**Parameters**
-#### Start Deployment by name:
+| Parameter | Description |
+|-----------------|------------------------------------------------------|
+| `force=true` | forces Recreation and Run of targeted deployment(s) |
+| `forceRun=true` | only forces run of targeted deployment(s) |
+| `forceRecreate` | only forces recreation of the targeted deployment(s) |
-`POST` to `/api/v1/deployments/{name}/start` optional add `force=true` if you want to force deployment and recreation of containers.
+##### All Deployments:
+
+`POST` to `/api/v1/deployments/all/start`
+
+##### Deployment by name:
+
+`POST` to `/api/v1/deployments/{name}/start`
+
+#### Stop
+
+##### Deployment by name:
+
+`POST` to `/api/v1/deployments/{name}/stop`
+
+#### State
+
+`GET` to `/api/v1/deployment-states`
## Develop
diff --git a/Test.http b/Test.http
index 4a5c1af..9b2e648 100644
--- a/Test.http
+++ b/Test.http
@@ -1,2 +1,9 @@
+POST http://localhost:8080/api/v1/deployments/test2/stop
+
+###
POST http://localhost:8080/api/v1/deployments/all/start
+
+###
+
+GET http://localhost:8080/api/v1/deployment-states
diff --git a/pom.xml b/pom.xml
index ccee58d..24334d8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
org.davidbohl
dirigent
- 0.2.0-SNAPSHOT
+ 0.3.0-SNAPSHOT
dirigent
Helper for Docker Composes
@@ -63,6 +63,11 @@
h2
runtime
+
+ org.projectlombok
+ lombok
+ provided
+
diff --git a/src/main/java/org/davidbohl/dirigent/DirigentApplication.java b/src/main/java/org/davidbohl/dirigent/DirigentApplication.java
index 370fc0d..2dcc955 100644
--- a/src/main/java/org/davidbohl/dirigent/DirigentApplication.java
+++ b/src/main/java/org/davidbohl/dirigent/DirigentApplication.java
@@ -7,8 +7,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
-import java.io.IOException;
-
@SpringBootApplication
@EnableConfigurationProperties
@@ -17,7 +15,7 @@ public class DirigentApplication {
static Logger logger = LoggerFactory.getLogger(DirigentApplication.class);
- public static void main(String[] args) throws IOException, InterruptedException {
+ public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DirigentApplication.class, args);
String composeCommand = context.getEnvironment().getProperty("dirigent.compose.command");
if(!isComposeInstalled(composeCommand)) {
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/api/DeploymentStatesController.java b/src/main/java/org/davidbohl/dirigent/deployments/api/DeploymentStatesController.java
new file mode 100644
index 0000000..eeb67ba
--- /dev/null
+++ b/src/main/java/org/davidbohl/dirigent/deployments/api/DeploymentStatesController.java
@@ -0,0 +1,26 @@
+package org.davidbohl.dirigent.deployments.api;
+
+import org.davidbohl.dirigent.deployments.state.DeploymentState;
+import org.davidbohl.dirigent.deployments.state.DeploymentStatePersistingService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController()
+@RequestMapping(path = "/api/v1/deployment-states")
+public class DeploymentStatesController {
+
+ private final DeploymentStatePersistingService deploymentStatePersistingService;
+
+ public DeploymentStatesController(DeploymentStatePersistingService deploymentStatePersistingService) {
+ this.deploymentStatePersistingService = deploymentStatePersistingService;
+ }
+
+ @GetMapping
+ public List getDeploymentStates() {
+ return deploymentStatePersistingService.getDeploymentStates();
+ }
+
+}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/api/DeploymentsController.java b/src/main/java/org/davidbohl/dirigent/deployments/api/DeploymentsController.java
index ae23a14..52c4585 100644
--- a/src/main/java/org/davidbohl/dirigent/deployments/api/DeploymentsController.java
+++ b/src/main/java/org/davidbohl/dirigent/deployments/api/DeploymentsController.java
@@ -2,6 +2,7 @@ package org.davidbohl.dirigent.deployments.api;
import org.davidbohl.dirigent.deployments.events.AllDeploymentsStartRequestedEvent;
import org.davidbohl.dirigent.deployments.events.NamedDeploymentStartRequestedEvent;
+import org.davidbohl.dirigent.deployments.events.NamedDeploymentStopRequestedEvent;
import org.davidbohl.dirigent.deployments.management.DeploymentNameNotFoundException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.ProblemDetail;
@@ -23,9 +24,18 @@ public class DeploymentsController {
applicationEventPublisher.publishEvent(new NamedDeploymentStartRequestedEvent(this, name, force));
}
+ @PostMapping("/{name}/stop")
+ public void stopDeployment(@PathVariable String name) {
+ applicationEventPublisher.publishEvent(new NamedDeploymentStopRequestedEvent(this, name));
+ }
+
@PostMapping("/all/start")
- public void startAllDeployments(@RequestParam(required = false) boolean force) {
- applicationEventPublisher.publishEvent(new AllDeploymentsStartRequestedEvent(this, force));
+ public void startAllDeployments(@RequestParam(required = false) boolean force,
+ @RequestParam(required = false) boolean forceRun,
+ @RequestParam(required = false) boolean forceRecreate) {
+ applicationEventPublisher.publishEvent(new AllDeploymentsStartRequestedEvent(this,
+ force || forceRun,
+ force || forceRecreate));
}
@ExceptionHandler(DeploymentNameNotFoundException.class)
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/api/GiteaDeploymentsController.java b/src/main/java/org/davidbohl/dirigent/deployments/api/GiteaDeploymentsController.java
index cee01d6..709709d 100644
--- a/src/main/java/org/davidbohl/dirigent/deployments/api/GiteaDeploymentsController.java
+++ b/src/main/java/org/davidbohl/dirigent/deployments/api/GiteaDeploymentsController.java
@@ -2,7 +2,6 @@ package org.davidbohl.dirigent.deployments.api;
import org.davidbohl.dirigent.deployments.events.AllDeploymentsStartRequestedEvent;
import org.davidbohl.dirigent.deployments.events.SourceDeploymentStartRequestedEvent;
-import org.davidbohl.dirigent.deployments.management.DeploymentsService;
import org.davidbohl.dirigent.deployments.models.GiteaRequestBody;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
@@ -20,7 +19,7 @@ public class GiteaDeploymentsController {
@Value("${dirigent.deployments.git.url}")
private String configUrl;
- public GiteaDeploymentsController(DeploymentsService deploymentsService, ApplicationEventPublisher applicationEventPublisher) {
+ public GiteaDeploymentsController(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
@@ -28,7 +27,7 @@ public class GiteaDeploymentsController {
public void webHook(@RequestBody GiteaRequestBody body) {
if(body.repository().cloneUrl().equals(configUrl)) {
- applicationEventPublisher.publishEvent(new AllDeploymentsStartRequestedEvent(this, false));
+ applicationEventPublisher.publishEvent(new AllDeploymentsStartRequestedEvent(this, true, true));
return;
}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/events/AllDeploymentsStartRequestedEvent.java b/src/main/java/org/davidbohl/dirigent/deployments/events/AllDeploymentsStartRequestedEvent.java
index 0a922ba..bd54c6d 100644
--- a/src/main/java/org/davidbohl/dirigent/deployments/events/AllDeploymentsStartRequestedEvent.java
+++ b/src/main/java/org/davidbohl/dirigent/deployments/events/AllDeploymentsStartRequestedEvent.java
@@ -1,17 +1,17 @@
package org.davidbohl.dirigent.deployments.events;
+import lombok.Getter;
import org.springframework.context.ApplicationEvent;
+@Getter
public class AllDeploymentsStartRequestedEvent extends ApplicationEvent {
- private final boolean forced;
+ private final boolean forceRun;
+ private final boolean forceRecreate;
- public AllDeploymentsStartRequestedEvent(Object source, boolean forced) {
+ public AllDeploymentsStartRequestedEvent(Object source, boolean forceRun, boolean forceRecreate) {
super(source);
- this.forced = forced;
- }
-
- public boolean isForced() {
- return forced;
+ this.forceRun = forceRun;
+ this.forceRecreate = forceRecreate;
}
}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/events/DeploymentStartFailedEvent.java b/src/main/java/org/davidbohl/dirigent/deployments/events/DeploymentStartFailedEvent.java
deleted file mode 100644
index 7a92f0e..0000000
--- a/src/main/java/org/davidbohl/dirigent/deployments/events/DeploymentStartFailedEvent.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.davidbohl.dirigent.deployments.events;
-
-import org.springframework.context.ApplicationEvent;
-
-public class DeploymentStartFailedEvent extends ApplicationEvent {
-
-
- private final String deploymentName;
- private final String message;
-
- public DeploymentStartFailedEvent(Object source, String deploymentName, String string) {
- super(source);
- this.deploymentName = deploymentName;
- this.message = string;
- }
-
- public String getMessage() {
- return message;
- }
-
- public String getDeploymentName() {
- return deploymentName;
- }
-}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/events/DeploymentStartSucceededEvent.java b/src/main/java/org/davidbohl/dirigent/deployments/events/DeploymentStartSucceededEvent.java
deleted file mode 100644
index 99a0852..0000000
--- a/src/main/java/org/davidbohl/dirigent/deployments/events/DeploymentStartSucceededEvent.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.davidbohl.dirigent.deployments.events;
-
-import org.springframework.context.ApplicationEvent;
-
-public class DeploymentStartSucceededEvent extends ApplicationEvent {
- private final String deploymentName;
-
- public DeploymentStartSucceededEvent(Object source, String deploymentName) {
- super(source);
- this.deploymentName = deploymentName;
- }
-
- public String getDeploymentName() {
- return deploymentName;
- }
-}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/events/DeploymentStateChangedEvent.java b/src/main/java/org/davidbohl/dirigent/deployments/events/DeploymentStateChangedEvent.java
new file mode 100644
index 0000000..87fd08c
--- /dev/null
+++ b/src/main/java/org/davidbohl/dirigent/deployments/events/DeploymentStateChangedEvent.java
@@ -0,0 +1,21 @@
+package org.davidbohl.dirigent.deployments.events;
+
+import lombok.Getter;
+import org.davidbohl.dirigent.deployments.state.DeploymentState;
+import org.springframework.context.ApplicationEvent;
+
+@Getter
+public class DeploymentStateChangedEvent extends ApplicationEvent {
+
+ final String deploymentName;
+ final DeploymentState.State state;
+ final String context;
+
+ public DeploymentStateChangedEvent(Object source, String deploymentName, DeploymentState.State state, String context) {
+ super(source);
+ this.deploymentName = deploymentName;
+ this.state = state;
+ this.context = context;
+ }
+
+}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/events/NamedDeploymentStartRequestedEvent.java b/src/main/java/org/davidbohl/dirigent/deployments/events/NamedDeploymentStartRequestedEvent.java
index d395933..8fc79fc 100644
--- a/src/main/java/org/davidbohl/dirigent/deployments/events/NamedDeploymentStartRequestedEvent.java
+++ b/src/main/java/org/davidbohl/dirigent/deployments/events/NamedDeploymentStartRequestedEvent.java
@@ -1,7 +1,9 @@
package org.davidbohl.dirigent.deployments.events;
+import lombok.Getter;
import org.springframework.context.ApplicationEvent;
+@Getter
public class NamedDeploymentStartRequestedEvent extends ApplicationEvent {
private final String name;
@@ -13,11 +15,4 @@ public class NamedDeploymentStartRequestedEvent extends ApplicationEvent {
this.forced = forced;
}
- public String getName() {
- return name;
- }
-
- public boolean isForced() {
- return forced;
- }
}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/events/NamedDeploymentStopRequestedEvent.java b/src/main/java/org/davidbohl/dirigent/deployments/events/NamedDeploymentStopRequestedEvent.java
new file mode 100644
index 0000000..77166b2
--- /dev/null
+++ b/src/main/java/org/davidbohl/dirigent/deployments/events/NamedDeploymentStopRequestedEvent.java
@@ -0,0 +1,16 @@
+package org.davidbohl.dirigent.deployments.events;
+
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+
+@Getter
+public class NamedDeploymentStopRequestedEvent extends ApplicationEvent {
+
+ private final String name;
+
+ public NamedDeploymentStopRequestedEvent(Object source, String name) {
+ super(source);
+ this.name = name;
+ }
+
+}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/events/NotConfiguredDeploymentStopped.java b/src/main/java/org/davidbohl/dirigent/deployments/events/NotConfiguredDeploymentStopped.java
deleted file mode 100644
index b11ae1f..0000000
--- a/src/main/java/org/davidbohl/dirigent/deployments/events/NotConfiguredDeploymentStopped.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.davidbohl.dirigent.deployments.events;
-
-import org.springframework.context.ApplicationEvent;
-
-public class NotConfiguredDeploymentStopped extends ApplicationEvent {
- private final String deploymentName;
-
- public NotConfiguredDeploymentStopped(Object source, String deploymentName) {
- super(source);
- this.deploymentName = deploymentName;
- }
-
- public String getDeploymentName() {
- return deploymentName;
- }
-}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/events/RecreateAllDeploymentStatesEvent.java b/src/main/java/org/davidbohl/dirigent/deployments/events/RecreateAllDeploymentStatesEvent.java
new file mode 100644
index 0000000..bad4cce
--- /dev/null
+++ b/src/main/java/org/davidbohl/dirigent/deployments/events/RecreateAllDeploymentStatesEvent.java
@@ -0,0 +1,9 @@
+package org.davidbohl.dirigent.deployments.events;
+
+import org.springframework.context.ApplicationEvent;
+
+public class RecreateAllDeploymentStatesEvent extends ApplicationEvent {
+ public RecreateAllDeploymentStatesEvent(Object source) {
+ super(source);
+ }
+}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/events/SourceDeploymentStartRequestedEvent.java b/src/main/java/org/davidbohl/dirigent/deployments/events/SourceDeploymentStartRequestedEvent.java
index c5d633a..b1ab9c0 100644
--- a/src/main/java/org/davidbohl/dirigent/deployments/events/SourceDeploymentStartRequestedEvent.java
+++ b/src/main/java/org/davidbohl/dirigent/deployments/events/SourceDeploymentStartRequestedEvent.java
@@ -1,17 +1,16 @@
package org.davidbohl.dirigent.deployments.events;
+import lombok.Getter;
import org.springframework.context.ApplicationEvent;
+@Getter
public class SourceDeploymentStartRequestedEvent extends ApplicationEvent {
- private String deploymentSource;
+ private final String deploymentSource;
public SourceDeploymentStartRequestedEvent(Object source, String deploymentSource) {
super(source);
this.deploymentSource = deploymentSource;
}
- public String getDeploymentSource() {
- return deploymentSource;
- }
}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/management/DeploymentScheduler.java b/src/main/java/org/davidbohl/dirigent/deployments/management/DeploymentScheduler.java
index cd5baec..23dd7d5 100644
--- a/src/main/java/org/davidbohl/dirigent/deployments/management/DeploymentScheduler.java
+++ b/src/main/java/org/davidbohl/dirigent/deployments/management/DeploymentScheduler.java
@@ -1,6 +1,6 @@
package org.davidbohl.dirigent.deployments.management;
-import org.davidbohl.dirigent.deployments.events.AllDeploymentsStartRequestedEvent;
+import org.davidbohl.dirigent.deployments.events.RecreateAllDeploymentStatesEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
@@ -33,14 +33,14 @@ public class DeploymentScheduler {
void runScheduledDeployments() {
if(enabled) {
logger.info("Starting all deployments scheduled");
- this.applicationEventPublisher.publishEvent(new AllDeploymentsStartRequestedEvent(this, false));
+ this.applicationEventPublisher.publishEvent(new RecreateAllDeploymentStatesEvent(this));
}
}
@EventListener(ContextRefreshedEvent.class)
public void onContextRefreshed() {
if(startAllDeploymentsOnStartup)
- applicationEventPublisher.publishEvent(new AllDeploymentsStartRequestedEvent(this, false));
+ applicationEventPublisher.publishEvent(new RecreateAllDeploymentStatesEvent(this));
}
}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/management/DeploymentsService.java b/src/main/java/org/davidbohl/dirigent/deployments/management/DeploymentsService.java
index 98e831f..d57a510 100644
--- a/src/main/java/org/davidbohl/dirigent/deployments/management/DeploymentsService.java
+++ b/src/main/java/org/davidbohl/dirigent/deployments/management/DeploymentsService.java
@@ -4,6 +4,8 @@ import org.davidbohl.dirigent.deployments.config.DeploymentsConfigurationProvide
import org.davidbohl.dirigent.deployments.events.*;
import org.davidbohl.dirigent.deployments.models.Deployment;
import org.davidbohl.dirigent.deployments.models.DeploynentConfiguration;
+import org.davidbohl.dirigent.deployments.state.DeploymentState;
+import org.davidbohl.dirigent.deployments.state.DeploymentStatePersistingService;
import org.davidbohl.dirigent.deployments.utility.GitService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,16 +31,18 @@ public class DeploymentsService {
private final DeploymentsConfigurationProvider deploymentsConfigurationProvider;
private final Logger logger = LoggerFactory.getLogger(DeploymentsService.class);
private final ApplicationEventPublisher applicationEventPublisher;
+ private final DeploymentStatePersistingService deploymentStatePersistingService;
@Value("${dirigent.compose.command}")
private String composeCommand;
public DeploymentsService(
DeploymentsConfigurationProvider deploymentsConfigurationProvider,
- GitService gitService, ApplicationEventPublisher applicationEventPublisher) {
+ GitService gitService, ApplicationEventPublisher applicationEventPublisher, DeploymentStatePersistingService deploymentStatePersistingService) {
this.deploymentsConfigurationProvider = deploymentsConfigurationProvider;
this.gitService = gitService;
this.applicationEventPublisher = applicationEventPublisher;
+ this.deploymentStatePersistingService = deploymentStatePersistingService;
}
@EventListener(AllDeploymentsStartRequestedEvent.class)
@@ -47,7 +51,7 @@ public class DeploymentsService {
makeDeploymentsDir();
DeploynentConfiguration deploymentsConfiguration = tryGetConfiguration();
- deployListOfDeployments(deploymentsConfiguration.deployments(), event.isForced());
+ deployListOfDeployments(deploymentsConfiguration.deployments(), event.isForceRun(), event.isForceRecreate());
stopNotConfiguredDeployments(deploymentsConfiguration.deployments());
}
@@ -64,10 +68,10 @@ public class DeploymentsService {
Optional first = deploynentConfiguration.deployments().stream().filter(d -> Objects.equals(d.name(), event.getName())).findFirst();
- if(first.isEmpty())
+ if (first.isEmpty())
throw new DeploymentNameNotFoundException(event.getName());
- deploy(first.get(), event.isForced());
+ deploy(first.get(), event.isForced(), event.isForced());
}
@EventListener(SourceDeploymentStartRequestedEvent.class)
@@ -80,10 +84,40 @@ public class DeploymentsService {
.filter(d -> Objects.equals(d.source(), event.getDeploymentSource()))
.collect(Collectors.toList());
- deployListOfDeployments(deployments, true);
+ deployListOfDeployments(deployments, true, true);
}
- private void deploy(Deployment deployment, boolean force) {
+ @EventListener(NamedDeploymentStopRequestedEvent.class)
+ public void onNamedDeploymentStopRequested(NamedDeploymentStopRequestedEvent event) throws IOException, InterruptedException {
+ makeDeploymentsDir();
+ stopDeployment(event.getName());
+ }
+
+ @EventListener(RecreateAllDeploymentStatesEvent.class)
+ public void onRecreateAllDeploymentStatesEvent() {
+ makeDeploymentsDir();
+ DeploynentConfiguration deploynentConfiguration = tryGetConfiguration();
+
+ List stoppedDeployments = deploymentStatePersistingService.getDeploymentStates().stream()
+ .filter(d -> d.getState() == DeploymentState.State.STOPPED)
+ .map(DeploymentState::getName)
+ .toList();
+ stoppedDeployments.forEach(d -> {
+ try {
+ stopDeployment(d);
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ List deployments = deploynentConfiguration.deployments().stream()
+ .filter(d -> !stoppedDeployments.contains(d.name()))
+ .toList();
+
+ deployListOfDeployments(deployments, true, false);
+ }
+
+ private void deploy(Deployment deployment, boolean forceRun, boolean forceRecreate) {
logger.info("Deploying {}", deployment.name());
File deploymentDir = new File("deployments/" + deployment.name());
@@ -91,7 +125,8 @@ public class DeploymentsService {
try {
boolean updated = gitService.updateRepo(deployment.source(), deploymentDir.getAbsolutePath());
- if(!updated && !force) {
+ if (!updated && !forceRun) {
+ applicationEventPublisher.publishEvent(new DeploymentStateChangedEvent(this, deployment.name(), DeploymentState.State.STARTED, "Deployment '%s' successfully started".formatted(deployment.name())));
logger.info("No changes in deployment. Skipping {}", deployment.name());
return;
}
@@ -101,7 +136,7 @@ public class DeploymentsService {
commandArgs.add("-d");
commandArgs.add("--remove-orphans");
- if(force)
+ if (forceRecreate)
commandArgs.add("--force-recreate");
Process process = new ProcessBuilder(commandArgs)
@@ -109,22 +144,23 @@ public class DeploymentsService {
.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
- StringBuilder output = new StringBuilder();
+ StringBuilder errorOutput = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
- output.append(line).append("\n");
+ errorOutput.append(line).append("\n");
}
- int exitVal = process.waitFor();
- if (exitVal != 0) {
- applicationEventPublisher.publishEvent(new DeploymentStartFailedEvent(this, deployment.name(), output.toString()));
+ int exitCode = process.waitFor();
+ if ((exitCode != 0)) {
+ applicationEventPublisher.publishEvent(new DeploymentStateChangedEvent(this, deployment.name(), DeploymentState.State.FAILED, errorOutput.toString()));
+ return;
}
} catch (IOException | InterruptedException e) {
- applicationEventPublisher.publishEvent(new DeploymentStartFailedEvent(this, deployment.name(), e.getMessage()));
+ applicationEventPublisher.publishEvent(new DeploymentStateChangedEvent(this, deployment.name(), DeploymentState.State.FAILED, e.getMessage()));
return;
}
- applicationEventPublisher.publishEvent(new DeploymentStartSucceededEvent(this, deployment.name()));
+ applicationEventPublisher.publishEvent(new DeploymentStateChangedEvent(this, deployment.name(), DeploymentState.State.STARTED, "Deployment '%s' successfully started".formatted(deployment.name())));
}
private void stopNotConfiguredDeployments(List deployments) {
@@ -132,21 +168,15 @@ public class DeploymentsService {
File deploymentsDir = new File("deployments");
File[] files = deploymentsDir.listFiles();
- if(files == null)
+ if (files == null)
return;
for (File file : files) {
if (file.isDirectory() && deployments.stream().noneMatch(d -> d.name().equals(file.getName()))) {
try {
- logger.info("Stopping deployment {}", file.getName());
- List commandArgs = new java.util.ArrayList<>(Arrays.stream(composeCommand.split(" ")).toList());
- commandArgs.add("down");
- new ProcessBuilder(commandArgs)
- .directory(file)
- .start()
- .waitFor();
+ stopDeployment(file.getName());
deleteDirectory(file);
- applicationEventPublisher.publishEvent(new NotConfiguredDeploymentStopped(this, file.getName()));
+ applicationEventPublisher.publishEvent(new DeploymentStateChangedEvent(this, file.getName(), DeploymentState.State.REMOVED, "Deployment '%s' removed (Not configured)".formatted(file.getName())));
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
@@ -155,6 +185,17 @@ public class DeploymentsService {
logger.info("Not configured deployments stopped");
}
+ private void stopDeployment(String deploymentName) throws InterruptedException, IOException {
+ logger.info("Stopping deployment {}", deploymentName);
+ List commandArgs = new ArrayList<>(Arrays.stream(composeCommand.split(" ")).toList());
+ commandArgs.add("down");
+ new ProcessBuilder(commandArgs)
+ .directory(new File(DEPLOYMENTS_DIR_NAME + "/" + deploymentName))
+ .start()
+ .waitFor();
+ applicationEventPublisher.publishEvent(new DeploymentStateChangedEvent(this, deploymentName, DeploymentState.State.STOPPED, "Deployment '%s' stopped".formatted(deploymentName)));
+ }
+
void deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
@@ -164,11 +205,11 @@ public class DeploymentsService {
}
boolean deleted = directoryToBeDeleted.delete();
- if(!deleted)
+ if (!deleted)
throw new RuntimeException("Could not delete directory " + directoryToBeDeleted);
}
- private void deployListOfDeployments(List deployments, boolean force) {
+ private void deployListOfDeployments(List deployments, boolean forceRun, boolean forceRecreate) {
makeDeploymentsDir();
@@ -185,7 +226,7 @@ public class DeploymentsService {
ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
for (Deployment deployment : deploymentsOrderUnit) {
- executorService.submit(() -> deploy(deployment, force));
+ executorService.submit(() -> deploy(deployment, forceRun, forceRecreate));
}
executorService.shutdown();
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/notification/NotificationService.java b/src/main/java/org/davidbohl/dirigent/deployments/notification/NotificationService.java
index f723c7d..0b9e60a 100644
--- a/src/main/java/org/davidbohl/dirigent/deployments/notification/NotificationService.java
+++ b/src/main/java/org/davidbohl/dirigent/deployments/notification/NotificationService.java
@@ -1,17 +1,14 @@
package org.davidbohl.dirigent.deployments.notification;
-import org.davidbohl.dirigent.deployments.events.DeploymentStartFailedEvent;
-import org.davidbohl.dirigent.deployments.events.DeploymentStartSucceededEvent;
-import org.davidbohl.dirigent.deployments.events.NotConfiguredDeploymentStopped;
-import org.davidbohl.dirigent.deployments.management.DeploymentsService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import lombok.extern.slf4j.Slf4j;
+import org.davidbohl.dirigent.deployments.events.DeploymentStateChangedEvent;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
+@Slf4j
public class NotificationService {
@Value("${dirigent.gotify.baseUrl:}")
@@ -20,28 +17,13 @@ public class NotificationService {
@Value("${dirigent.gotify.token:}")
private String gotifyToken;
- private final Logger logger = LoggerFactory.getLogger(DeploymentsService.class);
+ @EventListener(DeploymentStateChangedEvent.class)
+ public void onDeploymentStateChanged(DeploymentStateChangedEvent event) {
+ String title = "Deployment \"%s\" state changed to %s".formatted(event.getDeploymentName(), event.getState());
+ String context = event.getContext();
+ sendGotifyMessage(title, context);
- @EventListener(DeploymentStartFailedEvent.class)
- public void onDeploymentStartFailed(DeploymentStartFailedEvent event) {
- sendGotifyMessage(event.getMessage(), "Deployment \"%s\" Failed".formatted(event.getDeploymentName()));
-
- logger.warn("Deployment '{}' failed. Error: {}", event.getDeploymentName(), event.getMessage());
-
- }
-
- @EventListener(DeploymentStartSucceededEvent.class)
- public void onDeploymentStartSucceeded(DeploymentStartSucceededEvent event) {
- sendGotifyMessage("Deployment succeeded", "Deployment \"%s\" Succeeded".formatted(event.getDeploymentName()));
-
- logger.info("Deployment '{}' succeeded.", event.getDeploymentName());
- }
-
- @EventListener(NotConfiguredDeploymentStopped.class)
- public void onNotConfiguredDeploymentStopped(NotConfiguredDeploymentStopped event) {
- sendGotifyMessage("Deployment stopped", "Deployment \"%s\" stopped because it is not configured".formatted(event.getDeploymentName()));
-
- logger.info("Deployment '{}' stopped because it is not configured.", event.getDeploymentName());
+ log.info("Deployment '{}' state changed to {}. Context: {}", event.getDeploymentName(), event.getState(), context);
}
private void sendGotifyMessage(String title, String message) {
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/state/DeploymentState.java b/src/main/java/org/davidbohl/dirigent/deployments/state/DeploymentState.java
new file mode 100644
index 0000000..51f0d4b
--- /dev/null
+++ b/src/main/java/org/davidbohl/dirigent/deployments/state/DeploymentState.java
@@ -0,0 +1,27 @@
+package org.davidbohl.dirigent.deployments.state;
+
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+@Entity
+public class DeploymentState {
+ @Id
+ private String name;
+
+ @Enumerated(EnumType.STRING)
+ private State state;
+
+ @Column(length = 65535)
+ private String message;
+
+ public enum State {
+ STARTED, STOPPED, FAILED, REMOVED
+ }
+}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/state/DeploymentStatePersistingService.java b/src/main/java/org/davidbohl/dirigent/deployments/state/DeploymentStatePersistingService.java
new file mode 100644
index 0000000..3f96f55
--- /dev/null
+++ b/src/main/java/org/davidbohl/dirigent/deployments/state/DeploymentStatePersistingService.java
@@ -0,0 +1,31 @@
+package org.davidbohl.dirigent.deployments.state;
+
+import org.davidbohl.dirigent.deployments.events.DeploymentStateChangedEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.StreamSupport;
+
+@Service
+public class DeploymentStatePersistingService {
+
+ final DeploymentStateRepository deploymentStateRepository;
+
+ public DeploymentStatePersistingService(DeploymentStateRepository deploymentStateRepository) {
+ this.deploymentStateRepository = deploymentStateRepository;
+ }
+
+ @EventListener(DeploymentStateChangedEvent.class)
+ public void handleDeploymentStateChangedEvent(DeploymentStateChangedEvent event) {
+ DeploymentState deploymentState = new DeploymentState(event.getDeploymentName(), event.getState(), event.getContext());
+ deploymentStateRepository.save(deploymentState);
+
+ }
+
+ public List getDeploymentStates() {
+ return StreamSupport.stream(deploymentStateRepository.findAll().spliterator(), false)
+ .toList();
+ }
+
+}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/state/DeploymentStateRepository.java b/src/main/java/org/davidbohl/dirigent/deployments/state/DeploymentStateRepository.java
new file mode 100644
index 0000000..4acd328
--- /dev/null
+++ b/src/main/java/org/davidbohl/dirigent/deployments/state/DeploymentStateRepository.java
@@ -0,0 +1,6 @@
+package org.davidbohl.dirigent.deployments.state;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface DeploymentStateRepository extends CrudRepository {
+}
diff --git a/src/main/java/org/davidbohl/dirigent/deployments/utility/GitService.java b/src/main/java/org/davidbohl/dirigent/deployments/utility/GitService.java
index 20a500e..1189edf 100644
--- a/src/main/java/org/davidbohl/dirigent/deployments/utility/GitService.java
+++ b/src/main/java/org/davidbohl/dirigent/deployments/utility/GitService.java
@@ -27,7 +27,7 @@ public class GitService {
File destinationDir = new File(destination);
- boolean changed = false;
+ boolean changed;
if (destinationDir.exists() && Arrays.asList(Objects.requireNonNull(destinationDir.list())).contains(".git")) {
logger.debug("Local Repo exists. Pulling latest changes.");
diff --git a/src/main/resources/application-local.properties.template b/src/main/resources/application-local.properties.template
index f862051..14d6b23 100644
--- a/src/main/resources/application-local.properties.template
+++ b/src/main/resources/application-local.properties.template
@@ -1,2 +1,3 @@
dirigent.deployments.git.url=
dirigent.git.authToken=
+spring.h2.console.enabled=true
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 1bbd11b..8f168ec 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,8 +1,15 @@
spring.application.name=dirigent
-dirigent.compose.command=docker compose
-dirigent.deployments.cache.evict.interval=60000
-dirigent.start.all.on.startup=true
spring.profiles.active=local
+spring.datasource.url=jdbc:h2:file:./data/statedb
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=password
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+spring.h2.console.enabled=false
+spring.jpa.hibernate.ddl-auto=update
+
+dirigent.compose.command=docker compose
+dirigent.start.all.on.startup=true
dirigent.git.authToken=
dirigent.delpoyments.schedule.enabled=true
-dirigent.delpoyments.schedule.cron=0 */5 * * * *
\ No newline at end of file
+dirigent.delpoyments.schedule.cron=0 */5 * * * *