From 02bb5611f4ff84c518378c15c8f2cfa964f85c2c Mon Sep 17 00:00:00 2001 From: hyunjujeong Date: Thu, 10 Aug 2023 15:00:49 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=95=B1=20=EB=B0=B0=ED=8F=AC=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20-=20=EC=95=B1=20=EB=B0=B0=ED=8F=AC=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20API=20=EC=9E=91=EC=84=B1=20-=20=EC=95=B1?= =?UTF-8?q?=20=EB=B0=B0=ED=8F=AC=20=EC=9A=94=EC=B2=AD=20MQTT=20=EB=B0=9C?= =?UTF-8?q?=ED=96=89=20-=20=EB=B0=B0=ED=8F=AC=20=EC=99=84=EB=A3=8C?= =?UTF-8?q?=EB=90=9C=20=EC=95=B1=20=EC=A0=95=EB=B3=B4=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20Closed:=20#SCDD-194?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 7 + .../application/DefaultDeployerService.java | 76 +++++++++++ .../application/DefaultProcessService.java | 14 ++ .../DeployerRepositoryDelegate.java | 8 ++ .../application/DeployerService.java | 11 ++ .../application/ProcessService.java | 10 ++ .../devicedeployer/domain/AssetApp.java | 88 +++++++++++++ .../devicedeployer/domain/DeployMessage.java | 122 ++++++++++++++++++ .../devicedeployer/domain/OperationType.java | 8 ++ .../blokworks/devicedeployer/domain/Port.java | 63 +++++++++ .../devicedeployer/domain/Status.java | 6 + .../mqtt/InboundDeployMessagePayload.java | 13 ++ .../mqtt/InboundProcessMessagePayload.java | 15 +++ .../mqtt/OutboundMessagePayload.java | 20 +++ .../relational/AppProcessEntity.java | 63 +++++++++ .../relational/AssetAppEntity.java | 60 +++++++++ .../relational/AssetAppJpaRepository.java | 7 + .../AssetAppRelationalRepository.java | 26 ++++ .../presentation/AssetAppResource.java | 18 +++ .../AssetAppResourceConverter.java | 41 ++++++ .../presentation/DeployerController.java | 48 +++++++ .../presentation/MqttMessageConverter.java | 27 ++++ .../presentation/MqttMessageHandler.java | 48 +++++++ .../presentation/PortResource.java | 8 ++ .../presentation/PortResourceConverter.java | 23 ++++ .../configuration/MqttConfiguration.java | 58 +++++++++ .../MqttConfigurationProperties.java | 55 ++++++++ src/main/resources/application-dev.yml | 0 src/main/resources/application-local.yml | 24 ++++ src/main/resources/application.properties | 1 - src/main/resources/application.yml | 20 +++ 31 files changed, 987 insertions(+), 1 deletion(-) create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/application/DefaultDeployerService.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/application/DefaultProcessService.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/application/DeployerRepositoryDelegate.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/application/DeployerService.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/application/ProcessService.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/domain/AssetApp.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/domain/DeployMessage.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/domain/OperationType.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/domain/Port.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/domain/Status.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/InboundDeployMessagePayload.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/InboundProcessMessagePayload.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/OutboundMessagePayload.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AppProcessEntity.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppEntity.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppJpaRepository.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppRelationalRepository.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/presentation/AssetAppResource.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/presentation/AssetAppResourceConverter.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/presentation/DeployerController.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/presentation/MqttMessageConverter.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/presentation/MqttMessageHandler.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/presentation/PortResource.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/presentation/PortResourceConverter.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/presentation/configuration/MqttConfiguration.java create mode 100644 src/main/java/inc/sdt/blokworks/devicedeployer/presentation/configuration/MqttConfigurationProperties.java create mode 100644 src/main/resources/application-dev.yml create mode 100644 src/main/resources/application-local.yml delete mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/application.yml diff --git a/build.gradle b/build.gradle index c746978..55b9fec 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,10 @@ java { sourceCompatibility = '17' } +jar { + enabled = false +} + repositories { mavenCentral() } @@ -20,7 +24,10 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.integration:spring-integration-mqtt") + runtimeOnly("org.postgresql:postgresql") testImplementation("org.springframework.boot:spring-boot-starter-test") + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" } tasks.named('test') { diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/application/DefaultDeployerService.java b/src/main/java/inc/sdt/blokworks/devicedeployer/application/DefaultDeployerService.java new file mode 100644 index 0000000..c0b73e1 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/application/DefaultDeployerService.java @@ -0,0 +1,76 @@ +package inc.sdt.blokworks.devicedeployer.application; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import inc.sdt.blokworks.devicedeployer.domain.AssetApp; +import inc.sdt.blokworks.devicedeployer.domain.DeployMessage; +import inc.sdt.blokworks.devicedeployer.domain.OperationType; +import inc.sdt.blokworks.devicedeployer.infrastructure.mqtt.InboundDeployMessagePayload; +import inc.sdt.blokworks.devicedeployer.infrastructure.mqtt.OutboundMessagePayload; +import org.eclipse.paho.client.mqttv3.IMqttClient; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@Service +public class DefaultDeployerService implements DeployerService{ + private final Logger log; + private final IMqttClient mqttClient; + private final ObjectMapper objectMapper; + private final DeployerRepositoryDelegate deployerRepositoryDelegate; + + public DefaultDeployerService(IMqttClient mqttClient, + ObjectMapper objectMapper, + DeployerRepositoryDelegate deployerRepositoryDelegate) { + this.log = LoggerFactory.getLogger(this.getClass()); + this.mqttClient = mqttClient; + this.objectMapper = objectMapper; + this.deployerRepositoryDelegate = deployerRepositoryDelegate; + } + + @Override + public void publish(DeployMessage deployMessage, String assetCode) { + log.info("[publish]"); + try { + OutboundMessagePayload payload = new OutboundMessagePayload( + deployMessage.getUrl(), + deployMessage.getName(), + deployMessage.getPorts(), + deployMessage.getEnv(), + deployMessage.getCommand(), + OperationType.DEPLOY, + deployMessage.getRequestId() + ); + + byte[] bytes = objectMapper.writeValueAsBytes(payload); + MqttMessage message = new MqttMessage(); + message.setPayload(bytes); + + mqttClient.publish("/assets/"+assetCode+"/apps/deploy", message); + }catch (JsonProcessingException | MqttException e) { + + } + } + + @Override + public Mono apply(InboundDeployMessagePayload inboundDeployMessagePayload) { + log.info("[apply] inboundDeployMessagePayload = {}", inboundDeployMessagePayload); + // 배포된 앱 정보 저장 + deployerRepositoryDelegate.save(fromMessage(inboundDeployMessagePayload)); + return null; + } + + private AssetApp fromMessage(InboundDeployMessagePayload payload) { + return AssetApp.builder() + .assetCode(payload.assetCode()) + .name(payload.name()) + .size(payload.size()) + .releasedAt(payload.releasedAt()) + .modifiedAt(payload.modifiedAt()) + .build(); + } + +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/application/DefaultProcessService.java b/src/main/java/inc/sdt/blokworks/devicedeployer/application/DefaultProcessService.java new file mode 100644 index 0000000..1996922 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/application/DefaultProcessService.java @@ -0,0 +1,14 @@ +package inc.sdt.blokworks.devicedeployer.application; + +import inc.sdt.blokworks.devicedeployer.infrastructure.mqtt.InboundProcessMessagePayload; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@Service +public class DefaultProcessService implements ProcessService{ + + @Override + public Mono apply(InboundProcessMessagePayload inboundProcessMessagePayload) { + return null; + } +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/application/DeployerRepositoryDelegate.java b/src/main/java/inc/sdt/blokworks/devicedeployer/application/DeployerRepositoryDelegate.java new file mode 100644 index 0000000..79eeaff --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/application/DeployerRepositoryDelegate.java @@ -0,0 +1,8 @@ +package inc.sdt.blokworks.devicedeployer.application; + +import inc.sdt.blokworks.devicedeployer.domain.AssetApp; +import inc.sdt.blokworks.devicedeployer.domain.DeployMessage; + +public interface DeployerRepositoryDelegate { + AssetApp save(AssetApp assetApp); +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/application/DeployerService.java b/src/main/java/inc/sdt/blokworks/devicedeployer/application/DeployerService.java new file mode 100644 index 0000000..e635b19 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/application/DeployerService.java @@ -0,0 +1,11 @@ +package inc.sdt.blokworks.devicedeployer.application; + +import inc.sdt.blokworks.devicedeployer.domain.DeployMessage; +import inc.sdt.blokworks.devicedeployer.infrastructure.mqtt.InboundDeployMessagePayload; +import reactor.core.publisher.Mono; + +import java.util.function.Function; + +public interface DeployerService extends Function> { + void publish(DeployMessage assetApp, String assetCode); +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/application/ProcessService.java b/src/main/java/inc/sdt/blokworks/devicedeployer/application/ProcessService.java new file mode 100644 index 0000000..76aa801 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/application/ProcessService.java @@ -0,0 +1,10 @@ +package inc.sdt.blokworks.devicedeployer.application; + +import inc.sdt.blokworks.devicedeployer.infrastructure.mqtt.InboundProcessMessagePayload; +import reactor.core.publisher.Mono; + +import java.util.function.Function; + +public interface ProcessService extends Function> { + +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/domain/AssetApp.java b/src/main/java/inc/sdt/blokworks/devicedeployer/domain/AssetApp.java new file mode 100644 index 0000000..a7a8ac8 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/domain/AssetApp.java @@ -0,0 +1,88 @@ +package inc.sdt.blokworks.devicedeployer.domain; + +import java.sql.Timestamp; + +public class AssetApp { + private String assetCode; + private String name; + private Long size; + private Long releaseAt; + private Long modifiedAt; + + protected AssetApp() {} + + public AssetApp(String assetCode, String name, Long size, Long releaseAt, Long modifiedAt) { + this.assetCode = assetCode; + this.name = name; + this.size = size; + this.releaseAt = releaseAt; + this.modifiedAt = modifiedAt; + } + + public String getAssetCode() { + return assetCode; + } + + public String getName() { + return name; + } + + public Long getSize() { + return size; + } + + public Long getReleaseAt() { + return releaseAt; + } + + public Long getModifiedAt() { + return modifiedAt; + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private String assetCode; + private String name; + private Long size; + private Long releasedAt; + private Long modifiedAt; + + public Builder assetCode(String assetCode) { + this.assetCode = assetCode; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder size(Long size) { + this.size = size; + return this; + } + + public Builder releasedAt(Long releasedAt) { + this.releasedAt = releasedAt; + return this; + } + + public Builder modifiedAt(Long modifiedAt) { + this.modifiedAt = modifiedAt; + return this; + } + + public AssetApp build() { + AssetApp assetApp = new AssetApp(); + assetApp.assetCode = this.assetCode; + assetApp.name = this.name; + assetApp.size = this.size; + assetApp.releaseAt = this.releasedAt; + assetApp.modifiedAt = this.modifiedAt; + return assetApp; + } + } +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/domain/DeployMessage.java b/src/main/java/inc/sdt/blokworks/devicedeployer/domain/DeployMessage.java new file mode 100644 index 0000000..9b72d08 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/domain/DeployMessage.java @@ -0,0 +1,122 @@ +package inc.sdt.blokworks.devicedeployer.domain; + +import java.util.HashMap; +import java.util.Set; + +public class DeployMessage { + private String url; + private String name; + private Set ports; + private HashMap env; + private String command; + private OperationType operationType; + private String requestId; + + protected DeployMessage() {} + + public String getUrl() { + return url; + } + + public String getName() { + return name; + } + + public Set getPorts() { + return ports; + } + + public HashMap getEnv() { + return env; + } + + public String getCommand() { + return command; + } + + public OperationType getOperationType() { + return operationType; + } + + public void setRequestId(String requestId) { + this.requestId = requestId; + } + + public String getRequestId() { + return requestId; + } + + @Override + public String toString() { + return "AssetApp{" + + "url='" + url + '\'' + + ", name='" + name + '\'' + + ", ports=" + ports + + ", env=" + env + + ", command='" + command + '\'' + + ", operationType=" + operationType + + ", requestId='" + requestId + '\'' + + '}'; + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private String url; + private String name; + private Set ports; + private HashMap env; + private String command; + private OperationType operationType; + private String requestId; + + public Builder url(String url) { + this.url = url; + return this; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder ports(Set ports) { + this.ports = ports; + return this; + } + + public Builder env(HashMap env) { + this.env = env; + return this; + } + + public Builder command(String command) { + this.command = command; + return this; + } + + public Builder operationType(OperationType operationType) { + this.operationType = operationType; + return this; + } + + public Builder requestId(String requestId) { + this.requestId = requestId; + return this; + } + + public DeployMessage build() { + DeployMessage deployMessage = new DeployMessage(); + deployMessage.url = this.url; + deployMessage.name = this.name; + deployMessage.ports = this.ports; + deployMessage.env = this.env; + deployMessage.command = this.command; + deployMessage.operationType = this.operationType; + deployMessage.requestId = this.requestId; + return deployMessage; + } + } +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/domain/OperationType.java b/src/main/java/inc/sdt/blokworks/devicedeployer/domain/OperationType.java new file mode 100644 index 0000000..8f7ae09 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/domain/OperationType.java @@ -0,0 +1,8 @@ +package inc.sdt.blokworks.devicedeployer.domain; + +public enum OperationType { + DEPLOY, + START, + STOP, + DELETE +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/domain/Port.java b/src/main/java/inc/sdt/blokworks/devicedeployer/domain/Port.java new file mode 100644 index 0000000..ab14d23 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/domain/Port.java @@ -0,0 +1,63 @@ +package inc.sdt.blokworks.devicedeployer.domain; + +public class Port { + private String protocol; + private Integer hostPort; + private Integer containerPort; + + protected Port() {} + + public String getProtocol() { + return protocol; + } + + public Integer getHostPort() { + return hostPort; + } + + public Integer getContainerPort() { + return containerPort; + } + + @Override + public String toString() { + return "Port{" + + "protocol='" + protocol + '\'' + + ", hostPort=" + hostPort + + ", containerPort=" + containerPort + + '}'; + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private String protocol; + private Integer hostPort; + private Integer containerPort; + + public Builder protocol(String protocol) { + this.protocol = protocol; + return this; + } + + public Builder hostPort(Integer hostPort) { + this.hostPort = hostPort; + return this; + } + + public Builder containerPort(Integer containerPort) { + this.containerPort = containerPort; + return this; + } + + public Port build() { + Port port = new Port(); + port.protocol = this.protocol; + port.hostPort = this.hostPort; + port.containerPort = this.containerPort; + return port; + } + } +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/domain/Status.java b/src/main/java/inc/sdt/blokworks/devicedeployer/domain/Status.java new file mode 100644 index 0000000..3a4b522 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/domain/Status.java @@ -0,0 +1,6 @@ +package inc.sdt.blokworks.devicedeployer.domain; + +public enum Status { + success, + fail +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/InboundDeployMessagePayload.java b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/InboundDeployMessagePayload.java new file mode 100644 index 0000000..6fdf3b8 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/InboundDeployMessagePayload.java @@ -0,0 +1,13 @@ +package inc.sdt.blokworks.devicedeployer.infrastructure.mqtt; + +import inc.sdt.blokworks.devicedeployer.domain.Status; + +public record InboundDeployMessagePayload( + Status status, + String assetCode, + String name, + Long size, + Long releasedAt, + Long modifiedAt +) { +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/InboundProcessMessagePayload.java b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/InboundProcessMessagePayload.java new file mode 100644 index 0000000..3044c14 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/InboundProcessMessagePayload.java @@ -0,0 +1,15 @@ +package inc.sdt.blokworks.devicedeployer.infrastructure.mqtt; + +import inc.sdt.blokworks.devicedeployer.domain.Status; + +public record InboundProcessMessagePayload( + Status status, + String assetCode, + Integer pid, + String name, + Integer cpu, + Integer memory, + Integer network, + Long processedAt +) { +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/OutboundMessagePayload.java b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/OutboundMessagePayload.java new file mode 100644 index 0000000..0e971a6 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/mqtt/OutboundMessagePayload.java @@ -0,0 +1,20 @@ +package inc.sdt.blokworks.devicedeployer.infrastructure.mqtt; + +import inc.sdt.blokworks.devicedeployer.domain.OperationType; +import inc.sdt.blokworks.devicedeployer.domain.Port; +import inc.sdt.blokworks.devicedeployer.presentation.PortResource; +import inc.sdt.blokworks.devicedeployer.presentation.PortResourceConverter; + +import java.util.HashMap; +import java.util.Set; + +public record OutboundMessagePayload( + String url, + String name, + Set ports, + HashMap env, + String command, + OperationType operationType, + String requestId +) { +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AppProcessEntity.java b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AppProcessEntity.java new file mode 100644 index 0000000..9af187f --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AppProcessEntity.java @@ -0,0 +1,63 @@ +package inc.sdt.blokworks.devicedeployer.infrastructure.relational; + +import jakarta.persistence.*; + +@Entity(name = "app_process") +class AppProcessEntity { + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column(name = "id") + private String id; + @Column(name = "app_id", length = 36) + private String appId; + @Column(name = "asset_code", length = 255) + private String assetCode; + @Column(name = "pid") + private Integer pid; + @Column(name = "cpu") + private Integer cpu; + @Column(name = "memory") + private Integer memory; + @Column(name = "network") + private Integer network; + + protected AppProcessEntity() {} + + public AppProcessEntity(String id, String appId, String assetCode, Integer pid, Integer cpu, Integer memory, Integer network) { + this.id = id; + this.appId = appId; + this.assetCode = assetCode; + this.pid = pid; + this.cpu = cpu; + this.memory = memory; + this.network = network; + } + + public String getId() { + return id; + } + + public String getAppId() { + return appId; + } + + public String getAssetCode() { + return assetCode; + } + + public Integer getPid() { + return pid; + } + + public Integer getCpu() { + return cpu; + } + + public Integer getMemory() { + return memory; + } + + public Integer getNetwork() { + return network; + } +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppEntity.java b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppEntity.java new file mode 100644 index 0000000..60f6fd4 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppEntity.java @@ -0,0 +1,60 @@ +package inc.sdt.blokworks.devicedeployer.infrastructure.relational; + +import jakarta.persistence.*; + +import java.sql.Timestamp; + +@Entity(name = "asset_app") +class AssetAppEntity { + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column(name = "app_id", length = 36) + private String id; + @Column(name = "asset_code", length = 255) + private String assetCode; + @Column(name = "app_name", length = 255) + private String name; + @Column(name = "size") + private Long size; + @Column(name = "released_at") + private Long releasedAt; + @Column(name = "modfied_at") + private Long modifiedAt; + + protected AssetAppEntity() {} + + public AssetAppEntity(String id, String assetCode, String name, long size, Long releasedAt, Long modifiedAt) { + this.id = id; + this.assetCode = assetCode; + this.name = name; + this.size = size; + this.releasedAt = releasedAt; + this.modifiedAt = modifiedAt; + } + + public String getId() { + return id; + } + + public String getAssetCode() { + return assetCode; + } + + public String getName() { + return name; + } + + public long getSize() { + return size; + } + + public Long getReleasedAt() { + return releasedAt; + } + + public Long getModifiedAt() { + return modifiedAt; + } + + +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppJpaRepository.java b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppJpaRepository.java new file mode 100644 index 0000000..6dbecc7 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppJpaRepository.java @@ -0,0 +1,7 @@ +package inc.sdt.blokworks.devicedeployer.infrastructure.relational; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AssetAppJpaRepository extends JpaRepository { + +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppRelationalRepository.java b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppRelationalRepository.java new file mode 100644 index 0000000..e3afce1 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/infrastructure/relational/AssetAppRelationalRepository.java @@ -0,0 +1,26 @@ +package inc.sdt.blokworks.devicedeployer.infrastructure.relational; + +import inc.sdt.blokworks.devicedeployer.application.DeployerRepositoryDelegate; +import inc.sdt.blokworks.devicedeployer.domain.AssetApp; +import inc.sdt.blokworks.devicedeployer.domain.DeployMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class AssetAppRelationalRepository implements DeployerRepositoryDelegate { + private final Logger log; + private final AssetAppJpaRepository assetAppJpaRepository; + + public AssetAppRelationalRepository(AssetAppJpaRepository assetAppJpaRepository) { + this.log = LoggerFactory.getLogger(this.getClass()); + this.assetAppJpaRepository = assetAppJpaRepository; + } + + @Override + public AssetApp save(AssetApp deployMessage) { + + return null; + } + +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/AssetAppResource.java b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/AssetAppResource.java new file mode 100644 index 0000000..3176add --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/AssetAppResource.java @@ -0,0 +1,18 @@ +package inc.sdt.blokworks.devicedeployer.presentation; + +import org.wildfly.common.annotation.NotNull; + +import java.util.HashMap; +import java.util.Set; + +record AssetAppResource( + @NotNull + String url, + @NotNull + String name, + Set ports, + HashMap env, + String command +) { + +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/AssetAppResourceConverter.java b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/AssetAppResourceConverter.java new file mode 100644 index 0000000..47f271a --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/AssetAppResourceConverter.java @@ -0,0 +1,41 @@ +package inc.sdt.blokworks.devicedeployer.presentation; + +import inc.sdt.blokworks.devicedeployer.domain.DeployMessage; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.stream.Collectors; + +@Component +public class AssetAppResourceConverter { + private final PortResourceConverter portResourceConverter; + + public AssetAppResourceConverter(PortResourceConverter portResourceConverter) { + this.portResourceConverter = portResourceConverter; + } + + public AssetAppResource toResource(DeployMessage deployMessage) { + return new AssetAppResource( + deployMessage.getUrl(), + deployMessage.getName(), + deployMessage.getPorts() != null + ? deployMessage.getPorts().stream().map(portResourceConverter::toResource).collect(Collectors.toSet()) + : null, + deployMessage.getEnv(), + deployMessage.getCommand() + ); + } + + public DeployMessage fromResource(AssetAppResource resource) { + return DeployMessage.builder() + .url(resource.url()) + .name(resource.name()) + .ports(resource.ports() != null + ? resource.ports().stream().map(portResourceConverter::fromResource).collect(Collectors.toSet()) + : new HashSet<>()) + .env(resource.env()) + .command(resource.command()) + .build(); + + } +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/DeployerController.java b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/DeployerController.java new file mode 100644 index 0000000..e6b83d1 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/DeployerController.java @@ -0,0 +1,48 @@ +package inc.sdt.blokworks.devicedeployer.presentation; + +import inc.sdt.blokworks.devicedeployer.application.DeployerService; +import inc.sdt.blokworks.devicedeployer.domain.DeployMessage; +import jakarta.validation.Valid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +@RestController +public class DeployerController { + private final Logger log; + private final DeployerService deployerService; + private final AssetAppResourceConverter appResourceConverter; + + public DeployerController(DeployerService deployerService, + AssetAppResourceConverter appResourceConverter) { + this.log = LoggerFactory.getLogger(this.getClass()); + this.deployerService = deployerService; + this.appResourceConverter = appResourceConverter; + } + + /** + * 앱 배포 명령 + * @param assetCode 자산 코드 + * @param assetAppResource 배포하려는 앱의 정보 + */ + @ResponseStatus(HttpStatus.CREATED) + @PostMapping("/assets/{assetCode}/apps") + public void deploy(@PathVariable String assetCode, + @Valid @RequestBody AssetAppResource assetAppResource) { + log.info("[deploy] assetCode = {}, assetAppResource = {}", assetCode, assetAppResource); + DeployMessage deployMessage = appResourceConverter.fromResource(assetAppResource); + deployMessage.setRequestId("requestId"); + deployerService.publish(appResourceConverter.fromResource(assetAppResource), assetCode); + } + + /** + * 배포된 앱 정보 조회 + * @param assetCode 자산 코드 + */ + @ResponseStatus(HttpStatus.OK) + @GetMapping("/assets/{assetCode}/apps") + public void get(@PathVariable String assetCode) { + log.info("[get] assetCode = {}", assetCode); + } +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/MqttMessageConverter.java b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/MqttMessageConverter.java new file mode 100644 index 0000000..f959de5 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/MqttMessageConverter.java @@ -0,0 +1,27 @@ +package inc.sdt.blokworks.devicedeployer.presentation; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +@Component +public class MqttMessageConverter { + private final ObjectMapper objectMapper; + + public MqttMessageConverter(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + public Mono convertFromByte(String payload, Class type) { + return Mono.fromFuture(CompletableFuture.supplyAsync(() -> { + try { + return objectMapper.readValue(payload, type); + } catch (IOException e) { + throw new RuntimeException(e); + } + })); + } +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/MqttMessageHandler.java b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/MqttMessageHandler.java new file mode 100644 index 0000000..8206914 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/MqttMessageHandler.java @@ -0,0 +1,48 @@ +package inc.sdt.blokworks.devicedeployer.presentation; + +import inc.sdt.blokworks.devicedeployer.application.DeployerService; +import inc.sdt.blokworks.devicedeployer.application.ProcessService; +import inc.sdt.blokworks.devicedeployer.infrastructure.mqtt.InboundDeployMessagePayload; +import inc.sdt.blokworks.devicedeployer.infrastructure.mqtt.InboundProcessMessagePayload; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.integration.annotation.MessageEndpoint; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.messaging.Message; + +@MessageEndpoint +public class MqttMessageHandler { + private final Logger log; + private final DeployerService deployerService; + private final ProcessService processService; + private final MqttMessageConverter deployMessagePayloadConverter; + private final MqttMessageConverter processMessagePayloadConverter; + + public MqttMessageHandler(DeployerService deployerService, + ProcessService processService, + MqttMessageConverter deployMessagePayloadConverter, + MqttMessageConverter processMessagePayloadConverter) { + this.log = LoggerFactory.getLogger(this.getClass()); + this.deployerService = deployerService; + this.processService = processService; + this.deployMessagePayloadConverter = deployMessagePayloadConverter; + this.processMessagePayloadConverter = processMessagePayloadConverter; + } + + @ServiceActivator(inputChannel = "mqttInboundChannel") + void handleMessage(Message message) { + log.debug("[handleMessage] message={}", message); + + if(!message.getPayload().contains("pid")) { + deployMessagePayloadConverter.convertFromByte(message.getPayload(), InboundDeployMessagePayload.class) + .flatMap(deployerService) + .subscribe(); + }else { + processMessagePayloadConverter.convertFromByte(message.getPayload(), InboundProcessMessagePayload.class) + .flatMap(processService) + .subscribe(); + } + + } + +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/PortResource.java b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/PortResource.java new file mode 100644 index 0000000..d0ff53c --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/PortResource.java @@ -0,0 +1,8 @@ +package inc.sdt.blokworks.devicedeployer.presentation; + +public record PortResource( + String protocol, + Integer hostPort, + Integer containerPort +) { +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/PortResourceConverter.java b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/PortResourceConverter.java new file mode 100644 index 0000000..391aaee --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/PortResourceConverter.java @@ -0,0 +1,23 @@ +package inc.sdt.blokworks.devicedeployer.presentation; + +import inc.sdt.blokworks.devicedeployer.domain.Port; +import org.springframework.stereotype.Component; + +@Component +public class PortResourceConverter { + public PortResource toResource(Port port) { + return new PortResource( + port.getProtocol(), + port.getHostPort(), + port.getContainerPort() + ); + } + + public Port fromResource(PortResource resource) { + return Port.builder() + .protocol(resource.protocol()) + .hostPort(resource.hostPort()) + .containerPort(resource.containerPort()) + .build(); + } +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/configuration/MqttConfiguration.java b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/configuration/MqttConfiguration.java new file mode 100644 index 0000000..7a7eb01 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/configuration/MqttConfiguration.java @@ -0,0 +1,58 @@ +package inc.sdt.blokworks.devicedeployer.presentation.configuration; + +import org.eclipse.paho.client.mqttv3.*; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.core.MessageProducer; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; +import org.springframework.messaging.MessageChannel; + +@Configuration +@EnableConfigurationProperties(MqttConfigurationProperties.class) +public class MqttConfiguration { + @Bean(name = "mqttInboundChannel") + MessageChannel mqttMessageChannel() { + return new DirectChannel(); + } + + @Bean + MessageProducer messageChannel(MessageChannel mqttMessageChannel, + MqttConfigurationProperties mqttConfigurationProperties) { + MqttConnectOptions options = new MqttConnectOptions(); + options.setUserName(mqttConfigurationProperties.getUsername()); + options.setPassword(mqttConfigurationProperties.getPassword().toCharArray()); + options.setServerURIs(new String[]{mqttConfigurationProperties.getUrl()}); + + DefaultMqttPahoClientFactory clientFactory = new DefaultMqttPahoClientFactory(); + clientFactory.setConnectionOptions(options); + + MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter( + mqttConfigurationProperties.getUrl(), + MqttAsyncClient.generateClientId(), + clientFactory + ); + adapter.setCompletionTimeout(5000); + adapter.setConverter(new DefaultPahoMessageConverter()); + adapter.setQos(1); + adapter.addTopic(mqttConfigurationProperties.getTopics()); + adapter.setOutputChannel(mqttMessageChannel); + return adapter; + } + + @Bean + public IMqttClient mqttClient(MqttConfigurationProperties mqttConfigurationProperties) throws MqttException { + MqttConnectOptions options = new MqttConnectOptions(); + options.setUserName(mqttConfigurationProperties.getUsername()); + options.setPassword(mqttConfigurationProperties.getPassword().toCharArray()); + options.setServerURIs(new String[]{mqttConfigurationProperties.getUrl()}); + + IMqttClient mqttClient = new MqttClient(mqttConfigurationProperties.getUrl(), MqttClient.generateClientId()); + mqttClient.connect(options); + return mqttClient; + } + +} diff --git a/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/configuration/MqttConfigurationProperties.java b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/configuration/MqttConfigurationProperties.java new file mode 100644 index 0000000..38da336 --- /dev/null +++ b/src/main/java/inc/sdt/blokworks/devicedeployer/presentation/configuration/MqttConfigurationProperties.java @@ -0,0 +1,55 @@ +package inc.sdt.blokworks.devicedeployer.presentation.configuration; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.Arrays; + +@ConfigurationProperties(prefix = "inbound.mqtt") +public class MqttConfigurationProperties { + private String url; + private String username; + private String password; + private String[] topics; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String[] getTopics() { + return topics; + } + + public void setTopics(String[] topics) { + this.topics = topics; + } + + @Override + public String toString() { + return "MqttConfigurationProperties{" + + "url='" + url + '\'' + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + ", topics=" + Arrays.toString(topics) + + '}'; + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml new file mode 100644 index 0000000..a33f789 --- /dev/null +++ b/src/main/resources/application-local.yml @@ -0,0 +1,24 @@ +spring: + datasource: + url: jdbc:postgresql://localhost:5432/blokworks + username: sdt + password: 251327 + hikari: + maximum-pool-size: 3 + jpa: + hibernate: + ddl-auto: update + show-sql: true + +inbound: + mqtt: + url: tcp://localhost:1883 + username: sdt + password: 251327 + topics: + - /assets/+/apps/process + - /assets/+/apps/deploy +outbound: + mqtt: + topic: + - /assets/+/command-req/+ \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 8b13789..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..56cacee --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,20 @@ +server: + port: 8085 +spring: + application: + name: device-deployer + datasource: + driver-class-name: org.postgresql.Driver + +inbound: + mqtt: + url: tcp://192.168.1.162:32102 + username: sdt + password: 251327 + topics: + - /assets/+/apps/process + - /assets/+/apps/deploy +outbound: + mqtt: + topic: + - /assets/+/command-req/+ \ No newline at end of file