Mazurenko Nikita 4 days ago
committed by GitHub
parent
commit
b418da3b38
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 30
      application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java
  2. 3
      application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeUpgradeInstructionsService.java
  3. 56
      application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeVersionGraphResolver.java
  4. 8
      application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java
  5. 19
      application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
  6. 33
      common/data/src/main/java/org/thingsboard/server/common/data/EdgeUpgradeMessageV2.java
  7. 55
      common/util/src/main/java/org/thingsboard/common/util/TbVersionUtils.java

30
application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java

@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.instructions;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.TbVersionUtils;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EdgeUpgradeInfo;
@ -30,6 +31,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.install.InstallScripts;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -62,7 +64,11 @@ public class DefaultEdgeUpgradeInstructionsService extends BaseEdgeInstallUpgrad
}
@Override
public void updateInstructionMap(Map<String, EdgeUpgradeInfo> map) {
public void updateVersionGraph(Map<String, List<EdgeUpgradeInfo>> versionGraph) {
updateInstructionMap(EdgeVersionGraphResolver.resolve(versionGraph, platformEdgeVersion));
}
private void updateInstructionMap(Map<String, EdgeUpgradeInfo> map) {
for (String key : map.keySet()) {
upgradeVersionHashMap.put(key, map.get(key));
}
@ -73,29 +79,13 @@ public class DefaultEdgeUpgradeInstructionsService extends BaseEdgeInstallUpgrad
Optional<AttributeKvEntry> attributeKvEntryOpt = attributesService.find(tenantId, edgeId, AttributeScope.SERVER_SCOPE, DataConstants.EDGE_VERSION_ATTR_KEY).get();
if (attributeKvEntryOpt.isPresent()) {
String edgeVersionFormatted = convertEdgeVersionToDocsFormat(attributeKvEntryOpt.get().getValueAsString());
return isVersionGreaterOrEqualsThan(edgeVersionFormatted, "3.6.0") && !isVersionGreaterOrEqualsThan(edgeVersionFormatted, platformEdgeVersion);
String platformEdgeVersionFormatted = TbVersionUtils.extractStartingDigits(platformEdgeVersion);
return TbVersionUtils.compare(edgeVersionFormatted, "3.6.0") >= 0
&& TbVersionUtils.compare(edgeVersionFormatted, platformEdgeVersionFormatted) < 0;
}
return false;
}
private boolean isVersionGreaterOrEqualsThan(String version1, String version2) {
String[] v1 = version1.split("\\.");
String[] v2 = version2.split("\\.");
int length = Math.max(v1.length, v2.length);
for (int i = 0; i < length; i++) {
int num1 = i < v1.length ? Integer.parseInt(v1[i]) : 0;
int num2 = i < v2.length ? Integer.parseInt(v2[i]) : 0;
if (num1 < num2) {
return false;
} else if (num1 > num2) {
return true;
}
}
return true;
}
private EdgeInstructions getDockerUpgradeInstructions(String platformEdgeVersion, String currentEdgeVersion) {
EdgeUpgradeInfo edgeUpgradeInfo = upgradeVersionHashMap.get(currentEdgeVersion);
if (edgeUpgradeInfo == null || edgeUpgradeInfo.getNextEdgeVersion() == null || platformEdgeVersion.equals(currentEdgeVersion)) {

3
application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeUpgradeInstructionsService.java

@ -20,13 +20,14 @@ import org.thingsboard.server.common.data.edge.EdgeInstructions;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.List;
import java.util.Map;
public interface EdgeUpgradeInstructionsService {
EdgeInstructions getUpgradeInstructions(String edgeVersion, String upgradeMethod);
void updateInstructionMap(Map<String, EdgeUpgradeInfo> upgradeVersions);
void updateVersionGraph(Map<String, List<EdgeUpgradeInfo>> versionGraph);
void setPlatformEdgeVersion(String version);

56
application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeVersionGraphResolver.java

@ -0,0 +1,56 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.instructions;
import org.thingsboard.common.util.TbVersionUtils;
import org.thingsboard.server.common.data.EdgeUpgradeInfo;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class EdgeVersionGraphResolver {
private EdgeVersionGraphResolver() {
}
/**
* Collapses a branching upgrade version graph into a single upgrade option per source version.
* For each version the option with the highest {@code nextEdgeVersion} that is lower than or equal to the
* given platform version is kept; if every option points to a newer version the source version becomes
* terminal ({@code nextEdgeVersion == null}).
*/
public static Map<String, EdgeUpgradeInfo> resolve(Map<String, List<EdgeUpgradeInfo>> versionGraph, String platformVersion) {
String platform = TbVersionUtils.extractStartingDigits(platformVersion);
Map<String, EdgeUpgradeInfo> resolved = new HashMap<>();
for (var entry : versionGraph.entrySet()) {
EdgeUpgradeInfo best = null;
for (EdgeUpgradeInfo option : entry.getValue()) {
String next = option.getNextEdgeVersion();
// eligible only if next is present and lower than or equal to the platform version
if (next == null || TbVersionUtils.compare(next, platform) > 0) {
continue;
}
// keep the option with the highest eligible nextEdgeVersion
if (best == null || TbVersionUtils.compare(next, best.getNextEdgeVersion()) >= 0) {
best = option;
}
}
resolved.put(entry.getKey(), best != null ? best : new EdgeUpgradeInfo(false, null));
}
return resolved;
}
}

8
application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java

@ -28,7 +28,7 @@ import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.EdgeUpgradeMessage;
import org.thingsboard.server.common.data.EdgeUpgradeMessageV2;
import org.thingsboard.server.common.data.UpdateMessage;
import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionTrigger;
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
@ -150,14 +150,14 @@ public class DefaultUpdateService implements UpdateService {
.build());
}
ObjectNode edgeRequest = JacksonUtil.newObjectNode().put(VERSION_PARAM, version);
String edgePlatformVersion = restClient.postForObject(UPDATE_SERVER_BASE_URL + "/api/v1/edge/installMapping", new HttpEntity<>(edgeRequest.toString(), headers), String.class);
String edgePlatformVersion = restClient.postForObject(UPDATE_SERVER_BASE_URL + "/api/v2/edge/installMapping", new HttpEntity<>(edgeRequest.toString(), headers), String.class);
if (edgePlatformVersion != null) {
edgeInstallInstructionsService.setPlatformEdgeVersion(edgePlatformVersion);
edgeUpgradeInstructionsService.setPlatformEdgeVersion(edgePlatformVersion);
}
EdgeUpgradeMessage edgeUpgradeMessage = restClient.postForObject(UPDATE_SERVER_BASE_URL + "/api/v1/edge/upgradeMapping", new HttpEntity<>(edgeRequest.toString(), headers), EdgeUpgradeMessage.class);
EdgeUpgradeMessageV2 edgeUpgradeMessage = restClient.getForObject(UPDATE_SERVER_BASE_URL + "/api/v2/edge/upgradeMapping", EdgeUpgradeMessageV2.class);
if (edgeUpgradeMessage != null) {
edgeUpgradeInstructionsService.updateInstructionMap(edgeUpgradeMessage.getEdgeVersions());
edgeUpgradeInstructionsService.updateVersionGraph(edgeUpgradeMessage.getEdgeVersions());
}
} catch (Exception e) {
log.trace(e.getMessage());

19
application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java

@ -1287,12 +1287,16 @@ public class EdgeControllerTest extends AbstractControllerTest {
@Test
public void testGetEdgeUpgradeInstructions() throws Exception {
// UpdateInfo config is updating from the Thingsboard Update server
HashMap<String, EdgeUpgradeInfo> upgradeInfoHashMap = new HashMap<>();
upgradeInfoHashMap.put("3.6.0", new EdgeUpgradeInfo(true, "3.6.1"));
upgradeInfoHashMap.put("3.6.1", new EdgeUpgradeInfo(true, "3.6.2"));
upgradeInfoHashMap.put("3.6.2", new EdgeUpgradeInfo(true, null));
edgeUpgradeInstructionsService.updateInstructionMap(upgradeInfoHashMap);
// Version graph is fetched from the Thingsboard Update server and resolved against the platform edge version
HashMap<String, List<EdgeUpgradeInfo>> versionGraph = new HashMap<>();
versionGraph.put("3.6.0", List.of(new EdgeUpgradeInfo(true, "3.6.1")));
versionGraph.put("3.6.1", List.of(new EdgeUpgradeInfo(true, "3.6.2")));
// branch: resolver must keep the highest nextEdgeVersion <= platform version (3.6.4), not the newer 3.7.0 line
versionGraph.put("3.6.2", List.of(new EdgeUpgradeInfo(false, "3.6.4"), new EdgeUpgradeInfo(true, "3.7.0")));
versionGraph.put("3.6.4", List.of(new EdgeUpgradeInfo(true, null)));
versionGraph.put("3.7.0", List.of(new EdgeUpgradeInfo(true, null)));
edgeUpgradeInstructionsService.setPlatformEdgeVersion("3.6.4");
edgeUpgradeInstructionsService.updateVersionGraph(versionGraph);
Edge edge = constructEdge("Edge for Test Docker Upgrade Instructions", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
String body = "{\"edgeVersion\": \"V_3_6_0\"}";
@ -1300,6 +1304,9 @@ public class EdgeControllerTest extends AbstractControllerTest {
String upgradeInstructions = doGet("/api/edge/instructions/upgrade/" + EdgeVersion.V_3_6_0.name() + "/docker", String.class);
Assert.assertTrue(upgradeInstructions.contains("Upgrading to 3.6.1EDGE"));
Assert.assertTrue(upgradeInstructions.contains("Upgrading to 3.6.2EDGE"));
Assert.assertTrue(upgradeInstructions.contains("Upgrading to 3.6.4EDGE"));
// 3.7.0 is newer than the platform edge version, so the resolver must not include it in the path
Assert.assertFalse(upgradeInstructions.contains("3.7.0EDGE"));
}
@Test

33
common/data/src/main/java/org/thingsboard/server/common/data/EdgeUpgradeMessageV2.java

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@Data
@Schema
public class EdgeUpgradeMessageV2 implements Serializable {
private static final long serialVersionUID = -6647214126993298281L;
@Schema(description = "Mapping of edge version to the list of available upgrade options (next ver + strategy).")
private final Map<String, List<EdgeUpgradeInfo>> edgeVersions;
}

55
common/util/src/main/java/org/thingsboard/common/util/TbVersionUtils.java

@ -0,0 +1,55 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.common.util;
public class TbVersionUtils {
private TbVersionUtils() {
}
/**
* Compares two dot-separated numeric version strings component-wise, treating missing trailing components as
* zero (so {@code "3.6"} equals {@code "3.6.0"}). A {@code null} or empty string is treated as the lowest version.
*
* @return a negative int, zero, or a positive int as {@code v1} is less than, equal to, or greater than {@code v2}
*/
public static int compare(String v1, String v2) {
String[] parts1 = split(v1);
String[] parts2 = split(v2);
int length = Math.max(parts1.length, parts2.length);
for (int i = 0; i < length; i++) {
int num1 = i < parts1.length ? Integer.parseInt(parts1[i]) : 0;
int num2 = i < parts2.length ? Integer.parseInt(parts2[i]) : 0;
if (num1 != num2) {
return Integer.compare(num1, num2);
}
}
return 0;
}
/**
* Strips any suffix that follows the leading dotted-numeric part of a version string
* (e.g. {@code "4.3.1.1PE-SNAPSHOT"} -> {@code "4.3.1.1"}). Returns an empty string for a {@code null} input.
*/
public static String extractStartingDigits(String version) {
return version == null ? "" : version.replaceAll("[^0-9.].*$", "");
}
private static String[] split(String version) {
return version == null || version.isEmpty() ? new String[0] : version.split("\\.");
}
}
Loading…
Cancel
Save