Browse Source

Merge pull request #15007 from thingsboard/lts-4.3

LTS to RC
pull/15008/head
Viacheslav Klimov 8 months ago
committed by GitHub
parent
commit
1ce1914edf
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      .gitignore
  2. 39
      TEST_FAST.md
  3. 10
      application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java
  4. 2
      application/src/main/resources/thingsboard.yml
  5. 52
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java
  6. 60
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SwLwM2MDevice.java
  7. 2
      edqs/src/main/resources/edqs.yml
  8. 2
      msa/vc-executor/src/main/resources/tb-vc-executor.yml
  9. 31
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java
  10. 2
      transport/coap/src/main/resources/tb-coap-transport.yml
  11. 2
      transport/http/src/main/resources/tb-http-transport.yml
  12. 2
      transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml
  13. 2
      transport/mqtt/src/main/resources/tb-mqtt-transport.yml
  14. 2
      transport/snmp/src/main/resources/tb-snmp-transport.yml

1
.gitignore

@ -37,3 +37,4 @@ rebuild-docker.sh
*/.run/** */.run/**
.run/** .run/**
.run .run
.claude/

39
TEST_FAST.md

@ -10,22 +10,43 @@ mvn clean install -T6 -DskipTests
mvn test -pl='!application,!dao,!ui-ngx,!msa/js-executor,!msa/web-ui' -T4 mvn test -pl='!application,!dao,!ui-ngx,!msa/js-executor,!msa/web-ui' -T4
mvn test -pl dao -Dparallel=packages -DforkCount=4 mvn test -pl dao -Dparallel=packages -DforkCount=4
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.controller.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.controller.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.edge.**' -DforkCount=4 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.edge.**' -DforkCount=4 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.service.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.service.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.mqtt.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.transport.mqtt.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.coap.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.transport.coap.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.lwm2m.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='!**/nosql/**,org.thingsboard.server.transport.lwm2m.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='**/*TestSuite.java' -DforkCount=4 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 mvn test -pl application -Dtest='**/*TestSuite.java' -DforkCount=4 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
#the rest of application tests #the rest of application tests
mvn test -pl application -Dtest=' mvn test -pl application -Dtest='
!**/nosql/*Test.java, !**/nosql/**,
!org.thingsboard.server.controller.**, !org.thingsboard.server.controller.**,
!org.thingsboard.server.edge.**, !org.thingsboard.server.edge.**,
!org.thingsboard.server.service.**, !org.thingsboard.server.service.**,
!org.thingsboard.server.transport.mqtt.**, !org.thingsboard.server.transport.mqtt.**,
!org.thingsboard.server.transport.coap.**, !org.thingsboard.server.transport.coap.**,
!org.thingsboard.server.transport.lwm2m.** !org.thingsboard.server.transport.lwm2m.**,
!**/*TestSuite.java
' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5 ' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
``` ```
## Testcontainers compatibility with the Docker API workaround
In case your tests failed to run testcontainers due to unsupported Docker API version
:coffee: testcontainers (Docker API 1.32) + :whale: docker 29 (min API 1.44) workaround
Add to /etc/docker/daemon.json and restart docker
```json
{
"min-api-version": "1.32"
}
```
Same works on Mac, except `daemon.json` are located in another folder and required to be edited from Docker Desktop UI.
Tip: If your testcontainer are struggling to find any Docker. You can try to remove the testcontainers property file. It will be recreated on the next testcontainers run.
```bash
rm ~/.testcontainers.properties
```

10
application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java

@ -29,18 +29,10 @@ public class RestAuthenticationDetails implements Serializable {
private final Client userAgent; private final Client userAgent;
public RestAuthenticationDetails(HttpServletRequest request) { public RestAuthenticationDetails(HttpServletRequest request) {
this.clientAddress = getClientIP(request); this.clientAddress = request.getRemoteAddr();
this.userAgent = getUserAgent(request); this.userAgent = getUserAgent(request);
} }
private static String getClientIP(HttpServletRequest request) {
String xfHeader = request.getHeader("X-Forwarded-For");
if (xfHeader == null) {
return request.getRemoteAddr();
}
return xfHeader.split(",")[0];
}
private static Client getUserAgent(HttpServletRequest request) { private static Client getUserAgent(HttpServletRequest request) {
Parser uaParser = new Parser(); Parser uaParser = new Parser();
return uaParser.parse(request.getHeader("User-Agent")); return uaParser.parse(request.getHeader("User-Agent"));

2
application/src/main/resources/thingsboard.yml

@ -2036,7 +2036,7 @@ management:
web: web:
exposure: exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
include: '${METRICS_ENDPOINTS_EXPOSE:info}' include: "${METRICS_ENDPOINTS_EXPOSE:info}"
health: health:
elasticsearch: elasticsearch:
# Enable the org.springframework.boot.actuate.elasticsearch.ElasticsearchRestClientHealthIndicator.doHealthCheck # Enable the org.springframework.boot.actuate.elasticsearch.ElasticsearchRestClientHealthIndicator.doHealthCheck

52
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java

@ -140,45 +140,61 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
} }
private void startDownloading() { private void startDownloading() {
long delay = 0;
// Step 1: state = 1
scheduler.schedule(() -> { scheduler.schedule(() -> {
try { state.set(1);
state.set(1); fireResourceChange(3);
fireResourceChange(3); log.info("Downloading started: state=[{}]", state.get());
Thread.sleep(100); }, delay, TimeUnit.MILLISECONDS);
state.set(2);
fireResourceChange(3); delay += 100; // next step after 100 ms
} catch (Exception e) {
} // Step 2: state = 2
}, 100, TimeUnit.MILLISECONDS); scheduler.schedule(() -> {
state.set(2);
fireResourceChange(3);
log.info("Downloading in progress: state=[{}]", state.get());
}, delay, TimeUnit.MILLISECONDS);
} }
private void startUpdating(LwM2mServer identity) { private void startUpdating(LwM2mServer identity) {
scheduler.schedule(() -> { scheduler.schedule(() -> {
try { try {
// Update state + result
state.set(3); state.set(3);
fireResourceChange(3); fireResourceChange(3);
Thread.sleep(100);
updateResult.set(1); updateResult.set(1);
fireResourceChange(5); fireResourceChange(5);
this.pkgName = TITLE;
fireResourceChange(6);
this.pkgVersion = TARGET_FW_VERSION;
fireResourceChange(7);
if (this.leshanClient != null) { if (this.leshanClient != null) {
log.info("Stop/reboot LwM2M client {}", this.leshanClient.getEndpoint(identity)); log.info("Stop/reboot LwM2M client {}", this.leshanClient.getEndpoint(identity));
this.leshanClient.stop(false); this.leshanClient.stop(false);
log.info("Start after update fw LwM2M client {}", this.leshanClient.getEndpoint(identity)); log.info("Start after update fw LwM2M client {}", this.leshanClient.getEndpoint(identity));
this.leshanClient.start(); this.leshanClient.start();
this.pkgName = this.pkgNameDef;
this.pkgVersion = this.pkgVersionDef; // Delayed reset pkgName/pkgVersion, after reboot + registration
scheduler.schedule(() -> {
this.pkgName = this.pkgNameDef;
fireResourceChange(6);
this.pkgVersion = this.pkgVersionDef;
fireResourceChange(7);
log.info("FW resources updating to new values: pkgName=[{}], pkgVersion=[{}]",
this.pkgName, this.pkgVersion);
}, 15, TimeUnit.SECONDS); // 15 sec — safe timing
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Error during firmware update", e);
} }
}, 100, TimeUnit.MILLISECONDS); }, 0, TimeUnit.SECONDS); // start immediately, without further delay
} }
protected void setLeshanClient(LeshanClient leshanClient) { protected void setLeshanClient(LeshanClient leshanClient) {
this.leshanClient = leshanClient; this.leshanClient = leshanClient;
} }
} }

60
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SwLwM2MDevice.java

@ -33,6 +33,8 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static org.thingsboard.server.controller.AbstractWebTest.TIMEOUT;
@Slf4j @Slf4j
public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable { public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
@ -85,10 +87,7 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId); log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceId);
switch (resourceId) { switch (resourceId) {
case 2: case 2, 3:
startDownloading();
return WriteResponse.success();
case 3:
startDownloading(); startDownloading();
return WriteResponse.success(); return WriteResponse.success();
default: default:
@ -123,25 +122,34 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
} }
private void startDownloading() { private void startDownloading() {
long delay = 0;
// Step 1: start downloading
scheduler.schedule(() -> { scheduler.schedule(() -> {
try { state.set(1);
state.set(1); updateResult.set(1);
updateResult.set(1); fireResourceChange(7);
fireResourceChange(7); fireResourceChange(9);
fireResourceChange(9); }, delay, TimeUnit.MILLISECONDS);
Thread.sleep(100);
state.set(2); delay += 100;
fireResourceChange(7);
Thread.sleep(100); // Step 2: downloading in progress
state.set(3); scheduler.schedule(() -> {
fireResourceChange(7); state.set(2);
Thread.sleep(100); fireResourceChange(7);
updateResult.set(3); }, delay, TimeUnit.MILLISECONDS);
fireResourceChange(9);
} catch (Exception e) { delay += 100;
} // Step 3: downloading finished
}, 100, TimeUnit.MILLISECONDS); scheduler.schedule(() -> {
state.set(3);
fireResourceChange(7);
updateResult.set(3);
fireResourceChange(9);
}, delay, TimeUnit.MILLISECONDS);
} }
private void startUpdating() { private void startUpdating() {
@ -150,7 +158,13 @@ public class SwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
updateResult.set(2); updateResult.set(2);
fireResourceChange(7); fireResourceChange(7);
fireResourceChange(9); fireResourceChange(9);
// Optional: delayed log about FW update
scheduler.schedule(() -> {
log.info("FW resources updating to new values: state=[{}], updateResult=[{}]",
state.get(), updateResult.get());
}, 500, TimeUnit.MILLISECONDS);
}, 100, TimeUnit.MILLISECONDS); }, 100, TimeUnit.MILLISECONDS);
} }
} }

2
edqs/src/main/resources/edqs.yml

@ -210,7 +210,7 @@ management:
web: web:
exposure: exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
include: '${METRICS_ENDPOINTS_EXPOSE:info}' include: "${METRICS_ENDPOINTS_EXPOSE:info}"
health: health:
elasticsearch: elasticsearch:
# Enable the org.springframework.boot.actuate.elasticsearch.ElasticsearchRestClientHealthIndicator.doHealthCheck # Enable the org.springframework.boot.actuate.elasticsearch.ElasticsearchRestClientHealthIndicator.doHealthCheck

2
msa/vc-executor/src/main/resources/tb-vc-executor.yml

@ -232,7 +232,7 @@ management:
web: web:
exposure: exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
include: '${METRICS_ENDPOINTS_EXPOSE:info}' include: "${METRICS_ENDPOINTS_EXPOSE:info}"
# Service common properties # Service common properties
service: service:

31
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java

@ -56,6 +56,7 @@ import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@ -115,18 +116,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
assertTrue(request.containsHeader("Foo"), "Custom header included"); assertTrue(request.containsHeader("Foo"), "Custom header included");
assertEquals("Bar", request.getFirstHeader("Foo").getValue(), "Custom header value"); assertEquals("Bar", request.getFirstHeader("Foo").getValue(), "Custom header value");
response.setStatusCode(200); response.setStatusCode(200);
new Thread(new Runnable() { latch.countDown();
@Override
public void run() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
// ignore
} finally {
latch.countDown();
}
}
}).start();
} catch (Exception e) { } catch (Exception e) {
System.out.println("Exception handling request: " + e.toString()); System.out.println("Exception handling request: " + e.toString());
e.printStackTrace(); e.printStackTrace();
@ -158,7 +148,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class); ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
verify(ctx).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); verify(ctx, timeout(10_000)).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
assertNotSame(metaData, metadataCaptor.getValue()); assertNotSame(metaData, metadataCaptor.getValue());
assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue()); assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue());
@ -184,18 +174,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
assertTrue(request.containsHeader("Foo"), "Custom header included"); assertTrue(request.containsHeader("Foo"), "Custom header included");
assertEquals("Bar", request.getFirstHeader("Foo").getValue(), "Custom header value"); assertEquals("Bar", request.getFirstHeader("Foo").getValue(), "Custom header value");
response.setStatusCode(200); response.setStatusCode(200);
new Thread(new Runnable() { latch.countDown();
@Override
public void run() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
// ignore
} finally {
latch.countDown();
}
}
}).start();
} catch (Exception e) { } catch (Exception e) {
System.out.println("Exception handling request: " + e.toString()); System.out.println("Exception handling request: " + e.toString());
e.printStackTrace(); e.printStackTrace();
@ -227,7 +206,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class); ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
verify(ctx).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); verify(ctx, timeout(10_000)).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
assertNotSame(metaData, metadataCaptor.getValue()); assertNotSame(metaData, metadataCaptor.getValue());
assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue()); assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue());

2
transport/coap/src/main/resources/tb-coap-transport.yml

@ -435,7 +435,7 @@ management:
web: web:
exposure: exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
include: '${METRICS_ENDPOINTS_EXPOSE:info}' include: "${METRICS_ENDPOINTS_EXPOSE:info}"
# Notification system parameters # Notification system parameters
notification_system: notification_system:

2
transport/http/src/main/resources/tb-http-transport.yml

@ -384,7 +384,7 @@ management:
web: web:
exposure: exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
include: '${METRICS_ENDPOINTS_EXPOSE:info}' include: "${METRICS_ENDPOINTS_EXPOSE:info}"
# Notification system parameters # Notification system parameters
notification_system: notification_system:

2
transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml

@ -485,7 +485,7 @@ management:
web: web:
exposure: exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
include: '${METRICS_ENDPOINTS_EXPOSE:info}' include: "${METRICS_ENDPOINTS_EXPOSE:info}"
# Notification system parameters # Notification system parameters
notification_system: notification_system:

2
transport/mqtt/src/main/resources/tb-mqtt-transport.yml

@ -418,7 +418,7 @@ management:
web: web:
exposure: exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
include: '${METRICS_ENDPOINTS_EXPOSE:info}' include: "${METRICS_ENDPOINTS_EXPOSE:info}"
# Notification system parameters # Notification system parameters
notification_system: notification_system:

2
transport/snmp/src/main/resources/tb-snmp-transport.yml

@ -373,7 +373,7 @@ management:
web: web:
exposure: exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
include: '${METRICS_ENDPOINTS_EXPOSE:info}' include: "${METRICS_ENDPOINTS_EXPOSE:info}"
# Notification system parameters # Notification system parameters
notification_system: notification_system:

Loading…
Cancel
Save