feat: 앱 배포 요청
- 앱 배포 요청 API 작성 - 앱 배포 요청 MQTT 발행 - 배포 완료된 앱 정보 저장 Closed: #SCDD-194
This commit is contained in:
parent
eb2aa72e40
commit
02bb5611f4
|
@ -11,6 +11,10 @@ java {
|
||||||
sourceCompatibility = '17'
|
sourceCompatibility = '17'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
@ -20,7 +24,10 @@ dependencies {
|
||||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-validation")
|
implementation("org.springframework.boot:spring-boot-starter-validation")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
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")
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
|
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
|
|
|
@ -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<Void> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Void> apply(InboundProcessMessagePayload inboundProcessMessagePayload) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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<InboundDeployMessagePayload, Mono<Void>> {
|
||||||
|
void publish(DeployMessage assetApp, String assetCode);
|
||||||
|
}
|
|
@ -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<InboundProcessMessagePayload, Mono<Void>> {
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Port> ports;
|
||||||
|
private HashMap<String, String> env;
|
||||||
|
private String command;
|
||||||
|
private OperationType operationType;
|
||||||
|
private String requestId;
|
||||||
|
|
||||||
|
protected DeployMessage() {}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Port> getPorts() {
|
||||||
|
return ports;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, String> 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<Port> ports;
|
||||||
|
private HashMap<String, String> 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<Port> ports) {
|
||||||
|
this.ports = ports;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder env(HashMap<String, String> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package inc.sdt.blokworks.devicedeployer.domain;
|
||||||
|
|
||||||
|
public enum OperationType {
|
||||||
|
DEPLOY,
|
||||||
|
START,
|
||||||
|
STOP,
|
||||||
|
DELETE
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package inc.sdt.blokworks.devicedeployer.domain;
|
||||||
|
|
||||||
|
public enum Status {
|
||||||
|
success,
|
||||||
|
fail
|
||||||
|
}
|
|
@ -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
|
||||||
|
) {
|
||||||
|
}
|
|
@ -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
|
||||||
|
) {
|
||||||
|
}
|
|
@ -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<Port> ports,
|
||||||
|
HashMap<String, String> env,
|
||||||
|
String command,
|
||||||
|
OperationType operationType,
|
||||||
|
String requestId
|
||||||
|
) {
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package inc.sdt.blokworks.devicedeployer.infrastructure.relational;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface AssetAppJpaRepository extends JpaRepository<AssetAppEntity, String> {
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<PortResource> ports,
|
||||||
|
HashMap<String, String> env,
|
||||||
|
String command
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<T> {
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
public MqttMessageConverter(ObjectMapper objectMapper) {
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<T> convertFromByte(String payload, Class<T> type) {
|
||||||
|
return Mono.fromFuture(CompletableFuture.supplyAsync(() -> {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(payload, type);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<InboundDeployMessagePayload> deployMessagePayloadConverter;
|
||||||
|
private final MqttMessageConverter<InboundProcessMessagePayload> processMessagePayloadConverter;
|
||||||
|
|
||||||
|
public MqttMessageHandler(DeployerService deployerService,
|
||||||
|
ProcessService processService,
|
||||||
|
MqttMessageConverter<InboundDeployMessagePayload> deployMessagePayloadConverter,
|
||||||
|
MqttMessageConverter<InboundProcessMessagePayload> 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<String> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package inc.sdt.blokworks.devicedeployer.presentation;
|
||||||
|
|
||||||
|
public record PortResource(
|
||||||
|
String protocol,
|
||||||
|
Integer hostPort,
|
||||||
|
Integer containerPort
|
||||||
|
) {
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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) +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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/+
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -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/+
|
Loading…
Reference in New Issue