diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java index dca113621c..d93dc0cc6d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java @@ -51,6 +51,7 @@ import static org.thingsboard.server.controller.ControllerConstants.PROTOCOL; import static org.thingsboard.server.controller.ControllerConstants.PROTOCOL_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.CA_ROOT_CERT_PEM; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.DOCKER_COMPOSE_YML; @RestController @TbCoreComponent @@ -89,36 +90,6 @@ public class DeviceConnectivityController extends BaseController { return deviceConnectivityService.findDevicePublishTelemetryCommands(baseUrl, device); } - @ApiOperation(value = "Get commands to launch gateway (getGatewayLaunchCommands)", - notes = "Fetch the list of commands for different operation systems to launch a gateway using docker." + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) - @ApiResponses(value = { - @ApiResponse(code = 200, message = "OK", - examples = @io.swagger.annotations.Example( - value = { - @io.swagger.annotations.ExampleProperty( - mediaType = "application/json", - value = "{\"mqtt\": {\n" + - " \"linux\": \"docker run --rm -it -v ~/.tb-gateway/logs:/thingsboard_gateway/logs -v ~/.tb-gateway/extensions:/thingsboard_gateway/extensions -v ~/.tb-gateway/config:/thingsboard_gateway/config --name tbGateway127001 -e host=localhost -e port=1883 -e accessToken=qTe5oDBHPJf0KCSKO8J3 --restart always thingsboard/tb-gateway\",\n" + - " \"windows\": \"docker run --rm -it -v %HOMEPATH%/tb-gateway/logs:/thingsboard_gateway/logs -v %HOMEPATH%/tb-gateway/extensions:/thingsboard_gateway/extensions -v %HOMEPATH%/tb-gateway/config:/thingsboard_gateway/config --name tbGateway127001 -e host=localhost -e port=1883 -e accessToken=qTe5oDBHPJf0KCSKO8J3 --restart always thingsboard/tb-gateway\"}\n" + - "}")}))}) - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/device-connectivity/gateway-launch/{deviceId}", method = RequestMethod.GET) - @ResponseBody - public JsonNode getGatewayLaunchCommands(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION) - @PathVariable(DEVICE_ID) String strDeviceId, HttpServletRequest request) throws ThingsboardException, URISyntaxException { - checkParameter(DEVICE_ID, strDeviceId); - DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); - Device device = checkDeviceId(deviceId, Operation.READ_CREDENTIALS); - - if (!checkIsGateway(device)) { - throw new ThingsboardException("The device must be a gateway!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - - String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); - return deviceConnectivityService.findGatewayLaunchCommands(baseUrl, device); - } - @ApiOperation(value = "Download server certificate using file path defined in device.connectivity properties (downloadServerCertificate)", notes = "Download server certificate.") @RequestMapping(value = "/device-connectivity/{protocol}/certificate/download", method = RequestMethod.GET) @ResponseBody @@ -136,6 +107,30 @@ public class DeviceConnectivityController extends BaseController { .body(pemCert); } + @ApiOperation(value = "Download generated docker-compose.yml file for gateway (downloadGatewayDockerCompose)", notes = "Download generated docker-compose.yml for gateway.") + @RequestMapping(value = "/device-connectivity/gateway-launch/{deviceId}/docker-compose/download", method = RequestMethod.GET) + @ResponseBody + public ResponseEntity downloadGatewayDockerCompose(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION) + @PathVariable(DEVICE_ID) String strDeviceId, HttpServletRequest request) throws ThingsboardException, URISyntaxException, IOException { + checkParameter(DEVICE_ID, strDeviceId); + DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); + Device device = checkDeviceId(deviceId, Operation.READ_CREDENTIALS); + + if (!checkIsGateway(device)) { + throw new ThingsboardException("The device must be a gateway!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); + var dockerCompose = checkNotNull(deviceConnectivityService.createGatewayDockerComposeFile(baseUrl, device), "Failed to create docker-compose.yml file!"); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + DOCKER_COMPOSE_YML) + .header("x-filename", DOCKER_COMPOSE_YML) + .contentLength(dockerCompose.contentLength()) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(dockerCompose); + } + private static boolean checkIsGateway(Device device) { return device.getAdditionalInfo().has(DataConstants.GATEWAY_PARAMETER) && device.getAdditionalInfo().get(DataConstants.GATEWAY_PARAMETER).asBoolean(); diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java index 55943c819e..a2ab83635d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java @@ -60,11 +60,8 @@ import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAPS; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.DOCKER; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTP; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTPS; -import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.LINUX; -import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MACOS; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTT; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTTS; -import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.WINDOWS; @TestPropertySource(properties = { "device.connectivity.mqtts.pem_cert_file=/tmp/" + CA_ROOT_CERT_PEM @@ -294,7 +291,7 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { } @Test - public void testFetchGatewayLaunchCommands() throws Exception { + public void testFetchGatewayDockerComposeFile() throws Exception { String deviceName = "My device"; Device device = new Device(); device.setName(deviceName); @@ -306,24 +303,50 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest { DeviceCredentials credentials = doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); - JsonNode commands = - doGetTyped("/api/device-connectivity/gateway-launch/" + savedDevice.getId().getId(), new TypeReference<>() { - }); + String commands = + doGet("/api/device-connectivity/gateway-launch/" + savedDevice.getId().getId() + "/docker-compose/download", String.class); - String expectedContainerName = deviceName.replace(" ", "_").replaceAll("[^A-Za-z0-9_.-]", ""); - - JsonNode dockerMqttCommands = commands.get(MQTT); - assertThat(dockerMqttCommands.get(LINUX).asText()).isEqualTo(String.format("docker run -it -v ~/.tb-gateway/logs:/thingsboard_gateway/logs -v ~/.tb-gateway/extensions:/thingsboard_gateway/extensions -v ~/.tb-gateway/config:/thingsboard_gateway/config --name " + expectedContainerName + " --network=host --add-host=host.docker.internal:host-gateway -e host=host.docker.internal -e accessToken=%s --restart always thingsboard/tb-gateway", credentials.getCredentialsId())); - assertThat(dockerMqttCommands.get(WINDOWS).asText()).isEqualTo("docker run -it -v %HOMEDRIVE%%HOMEPATH%\\tb-gateway\\logs:/thingsboard_gateway/logs -v %HOMEDRIVE%%HOMEPATH%\\tb-gateway\\extensions:/thingsboard_gateway/extensions -v %HOMEDRIVE%%HOMEPATH%\\tb-gateway\\config:/thingsboard_gateway/config --name " + expectedContainerName + " -p 6400-6420:6400-6420 -e host=host.docker.internal -e accessToken=" + credentials.getCredentialsId() + " --restart always thingsboard/tb-gateway"); - assertThat(dockerMqttCommands.get(MACOS).asText()).isEqualTo("docker run -it -v ~/.tb-gateway/logs:/thingsboard_gateway/logs -v ~/.tb-gateway/extensions:/thingsboard_gateway/extensions -v ~/.tb-gateway/config:/thingsboard_gateway/config --name " + expectedContainerName + " -p 6400-6420:6400-6420 -e host=host.docker.internal -e accessToken=" + credentials.getCredentialsId() + " --restart always thingsboard/tb-gateway"); - - JsonNode dockerMqttsCommands = commands.get(MQTTS); - assertThat(dockerMqttsCommands.get(LINUX).asText()).isEqualTo(String.format("curl -f -S -o ~/.tb-gateway/config/ca-root.pem http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + - "docker run -it -v ~/.tb-gateway/logs:/thingsboard_gateway/logs -v ~/.tb-gateway/extensions:/thingsboard_gateway/extensions -v ~/.tb-gateway/config:/thingsboard_gateway/config --name " + expectedContainerName + " --network=host --add-host=host.docker.internal:host-gateway -e host=host.docker.internal -e port=8883 -e accessToken=%s -e caCert=/thingsboard_gateway/config/ca-root.pem --restart always thingsboard/tb-gateway", credentials.getCredentialsId())); - assertThat(dockerMqttsCommands.get(WINDOWS).asText()).isEqualTo("curl -f -S -o %HOMEDRIVE%%HOMEPATH%\\tb-gateway\\config\\ca-root.pem http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + - "docker run -it -v %HOMEDRIVE%%HOMEPATH%\\tb-gateway\\logs:/thingsboard_gateway/logs -v %HOMEDRIVE%%HOMEPATH%\\tb-gateway\\extensions:/thingsboard_gateway/extensions -v %HOMEDRIVE%%HOMEPATH%\\tb-gateway\\config:/thingsboard_gateway/config --name " + expectedContainerName + " -p 6400-6420:6400-6420 -e host=host.docker.internal -e port=8883 -e accessToken=" + credentials.getCredentialsId() + " -e caCert=/thingsboard_gateway/config/ca-root.pem --restart always thingsboard/tb-gateway"); - assertThat(dockerMqttsCommands.get(MACOS).asText()).isEqualTo("curl -f -S -o ~/.tb-gateway/config/ca-root.pem http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + - "docker run -it -v ~/.tb-gateway/logs:/thingsboard_gateway/logs -v ~/.tb-gateway/extensions:/thingsboard_gateway/extensions -v ~/.tb-gateway/config:/thingsboard_gateway/config --name " + expectedContainerName + " -p 6400-6420:6400-6420 -e host=host.docker.internal -e port=8883 -e accessToken=" + credentials.getCredentialsId() + " -e caCert=/thingsboard_gateway/config/ca-root.pem --restart always thingsboard/tb-gateway"); + assertThat(commands).isEqualTo(String.format("version: '3.4'\n" + + "services:\n" + + " # ThingsBoard IoT Gateway Service Configuration\n" + + " tb-gateway:\n" + + " image: thingsboard/tb-gateway\n" + + " container_name: tb-gateway\n" + + " restart: always\n" + + "\n" + + " # Ports bindings - required by some connectors\n" + + " ports:\n" + + " - \"5000:5000\" # Comment if you don't use REST connector and change if you use another port\n" + + " # Uncomment and modify the following ports based on connector usage:\n" + + "# - \"1052:1052\" # BACnet connector\n" + + "# - \"5026:5026\" # Modbus TCP connector (Modbus Slave)\n" + + "# - \"50000:50000/tcp\" # Socket connector with type TCP\n" + + "# - \"50000:50000/udp\" # Socket connector with type UDP\n" + + "\n" + + " # Necessary mapping for Linux\n" + + " extra_hosts:\n" + + " - \"host.docker.internal:host-gateway\"\n" + + "\n" + + " # Environment variables\n" + + " environment:\n" + + " - host=host.docker.internal\n" + + " - port=1883\n" + + " - accessToken=" + credentials.getCredentialsId() + "\n" + + "\n" + + " # Volumes bind\n" + + " volumes:\n" + + " - tb-gw-config:/thingsboard_gateway/config\n" + + " - tb-gw-logs:/thingsboard_gateway/logs\n" + + " - tb-gw-extensions:/thingsboard_gateway/extensions\n" + + "\n" + + "# Volumes declaration for configurations, extensions and configuration\n" + + "volumes:\n" + + " tb-gw-config:\n" + + " name: tb-gw-config\n" + + " tb-gw-logs:\n" + + " name: tb-gw-logs\n" + + " tb-gw-extensions:\n" + + " name: tb-gw-extensions\n")); } @Test diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityService.java index 4f2d9a1aee..97e69c5ec7 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityService.java @@ -25,7 +25,7 @@ public interface DeviceConnectivityService { JsonNode findDevicePublishTelemetryCommands(String baseUrl, Device device) throws URISyntaxException; - JsonNode findGatewayLaunchCommands(String baseUrl, Device device) throws URISyntaxException; - Resource getPemCertFile(String protocol); + + Resource createGatewayDockerComposeFile(String baseUrl, Device device) throws URISyntaxException; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java index bc1eeae8e7..cd872dce22 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java @@ -59,11 +59,8 @@ import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAPS; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.DOCKER; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTP; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTPS; -import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.LINUX; -import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MACOS; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTT; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTTS; -import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.WINDOWS; import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.getHost; @Service("DeviceConnectivityDaoService") @@ -131,27 +128,6 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService return commands; } - @Override - public JsonNode findGatewayLaunchCommands(String baseUrl, Device device) throws URISyntaxException { - DeviceId deviceId = device.getId(); - log.trace("Executing findDevicePublishTelemetryCommands [{}]", deviceId); - validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); - - DeviceCredentials creds = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), deviceId); - String deviceName = device.getName(); - - ObjectNode commands = JacksonUtil.newObjectNode(); - if (isEnabled(MQTT)) { - Optional.ofNullable(getGatewayDockerCommands(baseUrl, deviceName, creds, MQTT)) - .ifPresent(v -> commands.set(MQTT, v)); - } - if (isEnabled(MQTTS)) { - Optional.ofNullable(getGatewayDockerCommands(baseUrl, deviceName, creds, MQTTS)) - .ifPresent(v -> commands.set(MQTTS, v)); - } - return commands; - } - @Override public Resource getPemCertFile(String protocol) { return certs.computeIfAbsent(protocol, key -> { @@ -175,6 +151,14 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService }); } + @Override + public Resource createGatewayDockerComposeFile(String baseUrl, Device device) throws URISyntaxException { + String mqttType = isEnabled(MQTTS) ? MQTTS : MQTT; + DeviceConnectivityInfo properties = getConnectivity(mqttType); + DeviceCredentials creds = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); + return DeviceConnectivityUtil.getGatewayDockerComposeFile(baseUrl, properties, creds, mqttType); + } + private DeviceConnectivityInfo getConnectivity(String protocol) { AdminSettings connectivitySettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "connectivity"); JsonNode connectivity; @@ -300,18 +284,6 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService return null; } - private JsonNode getGatewayDockerCommands(String baseUrl, String deviceName, DeviceCredentials deviceCredentials, String mqttType) throws URISyntaxException { - ObjectNode dockerLaunchCommands = JacksonUtil.newObjectNode(); - DeviceConnectivityInfo properties = getConnectivity(mqttType); - Optional.ofNullable(DeviceConnectivityUtil.getGatewayLaunchCommand(LINUX, deviceName, properties, deviceCredentials, mqttType, baseUrl)) - .ifPresent(v -> dockerLaunchCommands.put(LINUX, v)); - Optional.ofNullable(DeviceConnectivityUtil.getGatewayLaunchCommand(WINDOWS, deviceName, properties, deviceCredentials, mqttType, baseUrl)) - .ifPresent(v -> dockerLaunchCommands.put(WINDOWS, v)); - Optional.ofNullable(DeviceConnectivityUtil.getGatewayLaunchCommand(MACOS, deviceName, properties, deviceCredentials, mqttType, baseUrl)) - .ifPresent(v -> dockerLaunchCommands.put(MACOS, v)); - return dockerLaunchCommands.isEmpty() ? null : dockerLaunchCommands; - } - private String getDockerMqttPublishCommand(String protocol, String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException { DeviceConnectivityInfo properties = getConnectivity(protocol); String mqttHost = getHost(baseUrl, properties, protocol); diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index 4cdc891eac..5b90f6927b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.dao.util; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials; @@ -25,6 +27,7 @@ import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; import java.util.regex.Pattern; public class DeviceConnectivityUtil { @@ -40,13 +43,10 @@ public class DeviceConnectivityUtil { public static final String COAP = "coap"; public static final String COAPS = "coaps"; public static final String CA_ROOT_CERT_PEM = "ca-root.pem"; - public static final String CA_ROOT_GATEWAY_CERT_PEM_PATH = "{gatewayVolumePathPrefix}config{pathSep}" + CA_ROOT_CERT_PEM; + public static final String DOCKER_COMPOSE_YML = "docker-compose.yml"; public static final String CHECK_DOCUMENTATION = "Check documentation"; public static final String JSON_EXAMPLE_PAYLOAD = "\"{temperature:25}\""; public static final String DOCKER_RUN = "docker run --rm -it "; - public static final String GATEWAY_DOCKER_RUN = "docker run -it "; - - public static final String NETWORK_HOST_PARAM = "--network=host "; public static final String HOST_DOCKER_INTERNAL = "host.docker.internal"; public static final String ADD_DOCKER_INTERNAL_HOST = "--add-host=" + HOST_DOCKER_INTERNAL + ":host-gateway "; public static final String MQTT_IMAGE = "thingsboard/mosquitto-clients "; @@ -94,75 +94,72 @@ public class DeviceConnectivityUtil { return command.toString(); } - public static String getGatewayLaunchCommand(String os, String deviceName, DeviceConnectivityInfo properties, DeviceCredentials deviceCredentials, String mqttType, String baseUrl) throws URISyntaxException { + public static Resource getGatewayDockerComposeFile(String baseUrl, DeviceConnectivityInfo properties, DeviceCredentials deviceCredentials, String mqttType) throws URISyntaxException { String host = getHost(baseUrl, properties, mqttType); - String port = properties.getPort().isEmpty() ? null : properties.getPort(); - String gatewayVolumePathPrefix = "~/.tb-gateway/"; - String pathSep = "/"; - if (WINDOWS.equals(os)) { - gatewayVolumePathPrefix = "%HOMEDRIVE%%HOMEPATH%\\tb-gateway\\"; - pathSep = "\\"; - } - - String gatewayContainerName = deviceName.replace(" ", "_").replaceAll("[^A-Za-z0-9_.-]", ""); - - StringBuilder command = new StringBuilder(); - String caRootGatewayCertPemPath = CA_ROOT_GATEWAY_CERT_PEM_PATH - .replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix) - .replace("{pathSep}", pathSep); - if (MQTTS.equals(mqttType)) { - command.append(getCurlPemCertCommand(baseUrl, MQTTS, caRootGatewayCertPemPath)); - command.append(" && "); - } - command.append(GATEWAY_DOCKER_RUN); - command.append("-v {gatewayVolumePathPrefix}logs:/thingsboard_gateway/logs ".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); - command.append("-v {gatewayVolumePathPrefix}extensions:/thingsboard_gateway/extensions ".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); - command.append("-v {gatewayVolumePathPrefix}config:/thingsboard_gateway/config ".replace("{gatewayVolumePathPrefix}", gatewayVolumePathPrefix)); - command.append("--name ").append(gatewayContainerName).append(" "); - if (LINUX.equals(os)) { - command.append(NETWORK_HOST_PARAM); - command.append(isLocalhost(host) ? ADD_DOCKER_INTERNAL_HOST : ""); - } else { - command.append("-p 6400-6420:6400-6420 "); - } - command.append("-e host=").append(isLocalhost(host) ? HOST_DOCKER_INTERNAL : host); - if (port != null && !"1883".equals(port)) { - command.append(" -e port=").append(port); - } + StringBuilder dockerComposeBuilder = new StringBuilder(); + dockerComposeBuilder.append("version: '3.4'\n"); + dockerComposeBuilder.append("services:\n"); + dockerComposeBuilder.append(" # ThingsBoard IoT Gateway Service Configuration\n"); + dockerComposeBuilder.append(" tb-gateway:\n"); + dockerComposeBuilder.append(" image: thingsboard/tb-gateway\n"); + dockerComposeBuilder.append(" container_name: tb-gateway\n"); + dockerComposeBuilder.append(" restart: always\n"); + dockerComposeBuilder.append("\n"); + dockerComposeBuilder.append(" # Ports bindings - required by some connectors\n"); + dockerComposeBuilder.append(" ports:\n"); + dockerComposeBuilder.append(" - \"5000:5000\" # Comment if you don't use REST connector and change if you use another port\n"); + dockerComposeBuilder.append(" # Uncomment and modify the following ports based on connector usage:\n"); + dockerComposeBuilder.append("# - \"1052:1052\" # BACnet connector\n"); + dockerComposeBuilder.append("# - \"5026:5026\" # Modbus TCP connector (Modbus Slave)\n"); + dockerComposeBuilder.append("# - \"50000:50000/tcp\" # Socket connector with type TCP\n"); + dockerComposeBuilder.append("# - \"50000:50000/udp\" # Socket connector with type UDP\n"); + dockerComposeBuilder.append("\n"); + dockerComposeBuilder.append(" # Necessary mapping for Linux\n"); + dockerComposeBuilder.append(" extra_hosts:\n"); + dockerComposeBuilder.append(" - \"host.docker.internal:host-gateway\"\n"); + dockerComposeBuilder.append("\n"); + dockerComposeBuilder.append(" # Environment variables\n"); + dockerComposeBuilder.append(" environment:\n"); + dockerComposeBuilder.append(" - host=").append(isLocalhost(host) ? HOST_DOCKER_INTERNAL : host).append("\n"); + dockerComposeBuilder.append(" - port=1883\n"); switch (deviceCredentials.getCredentialsType()) { case ACCESS_TOKEN: - command.append(" -e accessToken=").append(deviceCredentials.getCredentialsId()); + dockerComposeBuilder.append(" - accessToken=").append(deviceCredentials.getCredentialsId()).append("\n"); break; case MQTT_BASIC: BasicMqttCredentials credentials = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), BasicMqttCredentials.class); if (credentials != null) { if (credentials.getClientId() != null) { - command.append(" -e clientId=").append(credentials.getClientId()); + dockerComposeBuilder.append(" - clientId=").append(credentials.getClientId()).append("\n"); } if (credentials.getUserName() != null) { - command.append(" -e username=").append(credentials.getUserName()); + dockerComposeBuilder.append(" - username=").append(credentials.getUserName()).append("\n"); } if (credentials.getPassword() != null) { - command.append(" -e password=").append(credentials.getPassword()); + dockerComposeBuilder.append(" - password=").append(credentials.getPassword()).append("\n"); } - } else { - return null; } break; - default: - return null; } + dockerComposeBuilder.append("\n"); + dockerComposeBuilder.append(" # Volumes bind\n"); + dockerComposeBuilder.append(" volumes:\n"); + dockerComposeBuilder.append(" - tb-gw-config:/thingsboard_gateway/config\n"); + dockerComposeBuilder.append(" - tb-gw-logs:/thingsboard_gateway/logs\n"); + dockerComposeBuilder.append(" - tb-gw-extensions:/thingsboard_gateway/extensions\n"); + dockerComposeBuilder.append("\n"); + dockerComposeBuilder.append("# Volumes declaration for configurations, extensions and configuration\n"); + dockerComposeBuilder.append("volumes:\n"); + dockerComposeBuilder.append(" tb-gw-config:\n"); + dockerComposeBuilder.append(" name: tb-gw-config\n"); + dockerComposeBuilder.append(" tb-gw-logs:\n"); + dockerComposeBuilder.append(" name: tb-gw-logs\n"); + dockerComposeBuilder.append(" tb-gw-extensions:\n"); + dockerComposeBuilder.append(" name: tb-gw-extensions\n"); - if (MQTTS.equals(mqttType)) { - command.append(" -e caCert=/thingsboard_gateway/config/").append(CA_ROOT_CERT_PEM); - } - - command.append(" --restart always"); - command.append(" thingsboard/tb-gateway"); - - return command.toString(); + return new ByteArrayResource(dockerComposeBuilder.toString().getBytes(StandardCharsets.UTF_8)); } public static String getDockerMqttPublishCommand(String protocol, String baseUrl, String host, String port, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) {