AI rule node: fix flaky Azure IoT hub node test
This commit is contained in:
		
							parent
							
								
									9bcd3d8849
								
							
						
					
					
						commit
						fd7faa5a9e
					
				@ -26,11 +26,13 @@ import java.nio.file.DirectoryStream;
 | 
				
			|||||||
import java.nio.file.Files;
 | 
					import java.nio.file.Files;
 | 
				
			||||||
import java.nio.file.Path;
 | 
					import java.nio.file.Path;
 | 
				
			||||||
import java.nio.file.Paths;
 | 
					import java.nio.file.Paths;
 | 
				
			||||||
 | 
					import java.time.Clock;
 | 
				
			||||||
import java.util.Base64;
 | 
					import java.util.Base64;
 | 
				
			||||||
import java.util.Iterator;
 | 
					import java.util.Iterator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Slf4j
 | 
					@Slf4j
 | 
				
			||||||
public final class AzureIotHubUtil {
 | 
					public final class AzureIotHubUtil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final String BASE_DIR_PATH = System.getProperty("user.dir");
 | 
					    private static final String BASE_DIR_PATH = System.getProperty("user.dir");
 | 
				
			||||||
    private static final String APP_DIR = "application";
 | 
					    private static final String APP_DIR = "application";
 | 
				
			||||||
    private static final String SRC_DIR = "src";
 | 
					    private static final String SRC_DIR = "src";
 | 
				
			||||||
@ -52,41 +54,37 @@ public final class AzureIotHubUtil {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final long SAS_TOKEN_VALID_SECS = 365 * 24 * 60 * 60;
 | 
					    private static final long SAS_TOKEN_VALID_SECS = 365 * 24 * 60 * 60; // one year
 | 
				
			||||||
    private static final long ONE_SECOND_IN_MILLISECONDS = 1000;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final String SAS_TOKEN_FORMAT = "SharedAccessSignature sr=%s&sig=%s&se=%s";
 | 
					    private static final String SAS_TOKEN_FORMAT = "SharedAccessSignature sr=%s&sig=%s&se=%s";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final String USERNAME_FORMAT = "%s/%s/?api-version=2018-06-30";
 | 
					    private static final String USERNAME_FORMAT = "%s/%s/?api-version=2018-06-30";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private AzureIotHubUtil() {
 | 
					    private AzureIotHubUtil() {}
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static String buildUsername(String host, String deviceId) {
 | 
					    public static String buildUsername(String host, String deviceId) {
 | 
				
			||||||
        return String.format(USERNAME_FORMAT, host, deviceId);
 | 
					        return String.format(USERNAME_FORMAT, host, deviceId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static String buildSasToken(String host, String sasKey) {
 | 
					    public static String buildSasToken(String host, String sasKey, Clock clock) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            final String targetUri = URLEncoder.encode(host.toLowerCase(), "UTF-8");
 | 
					            final String targetUri = URLEncoder.encode(host.toLowerCase(), StandardCharsets.UTF_8);
 | 
				
			||||||
            final long expiryTime = buildExpiresOn();
 | 
					            final long expiryTime = buildExpiresOn(clock);
 | 
				
			||||||
            String toSign = targetUri + "\n" + expiryTime;
 | 
					            String toSign = targetUri + "\n" + expiryTime;
 | 
				
			||||||
            byte[] keyBytes = Base64.getDecoder().decode(sasKey.getBytes(StandardCharsets.UTF_8));
 | 
					            byte[] keyBytes = Base64.getDecoder().decode(sasKey.getBytes(StandardCharsets.UTF_8));
 | 
				
			||||||
            SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA256");
 | 
					            SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA256");
 | 
				
			||||||
            Mac mac = Mac.getInstance("HmacSHA256");
 | 
					            Mac mac = Mac.getInstance("HmacSHA256");
 | 
				
			||||||
            mac.init(signingKey);
 | 
					            mac.init(signingKey);
 | 
				
			||||||
            byte[] rawHmac = mac.doFinal(toSign.getBytes(StandardCharsets.UTF_8));
 | 
					            byte[] rawHmac = mac.doFinal(toSign.getBytes(StandardCharsets.UTF_8));
 | 
				
			||||||
            String signature = URLEncoder.encode(Base64.getEncoder().encodeToString(rawHmac), "UTF-8");
 | 
					            String signature = URLEncoder.encode(Base64.getEncoder().encodeToString(rawHmac), StandardCharsets.UTF_8);
 | 
				
			||||||
            return String.format(SAS_TOKEN_FORMAT, targetUri, signature, expiryTime);
 | 
					            return String.format(SAS_TOKEN_FORMAT, targetUri, signature, expiryTime);
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (Exception e) {
 | 
				
			||||||
            throw new RuntimeException("Failed to build SAS token!!!", e);
 | 
					            throw new RuntimeException("Failed to build SAS token!", e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static long buildExpiresOn() {
 | 
					    private static long buildExpiresOn(Clock clock) {
 | 
				
			||||||
        long expiresOnDate = System.currentTimeMillis();
 | 
					        return clock.instant().plusSeconds(SAS_TOKEN_VALID_SECS).getEpochSecond();
 | 
				
			||||||
        expiresOnDate += SAS_TOKEN_VALID_SECS * ONE_SECOND_IN_MILLISECONDS;
 | 
					 | 
				
			||||||
        return expiresOnDate / ONE_SECOND_IN_MILLISECONDS;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static String getDefaultCaCert() {
 | 
					    public static String getDefaultCaCert() {
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,7 @@ package org.thingsboard.rule.engine.mqtt.azure;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.fasterxml.jackson.databind.JsonNode;
 | 
					import com.fasterxml.jackson.databind.JsonNode;
 | 
				
			||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
 | 
					import com.fasterxml.jackson.databind.node.ObjectNode;
 | 
				
			||||||
 | 
					import com.google.common.annotations.VisibleForTesting;
 | 
				
			||||||
import io.netty.handler.codec.mqtt.MqttVersion;
 | 
					import io.netty.handler.codec.mqtt.MqttVersion;
 | 
				
			||||||
import lombok.extern.slf4j.Slf4j;
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
import org.thingsboard.common.util.AzureIotHubUtil;
 | 
					import org.thingsboard.common.util.AzureIotHubUtil;
 | 
				
			||||||
@ -36,6 +37,8 @@ import org.thingsboard.server.common.data.plugin.ComponentClusteringMode;
 | 
				
			|||||||
import org.thingsboard.server.common.data.plugin.ComponentType;
 | 
					import org.thingsboard.server.common.data.plugin.ComponentType;
 | 
				
			||||||
import org.thingsboard.server.common.data.util.TbPair;
 | 
					import org.thingsboard.server.common.data.util.TbPair;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.time.Clock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Slf4j
 | 
					@Slf4j
 | 
				
			||||||
@RuleNode(
 | 
					@RuleNode(
 | 
				
			||||||
        type = ComponentType.EXTERNAL,
 | 
					        type = ComponentType.EXTERNAL,
 | 
				
			||||||
@ -49,6 +52,8 @@ import org.thingsboard.server.common.data.util.TbPair;
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
public class TbAzureIotHubNode extends TbMqttNode {
 | 
					public class TbAzureIotHubNode extends TbMqttNode {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Clock clock = Clock.systemUTC();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
 | 
					    public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
 | 
				
			||||||
        super.init(ctx);
 | 
					        super.init(ctx);
 | 
				
			||||||
@ -73,7 +78,7 @@ public class TbAzureIotHubNode extends TbMqttNode {
 | 
				
			|||||||
        config.setUsername(AzureIotHubUtil.buildUsername(mqttNodeConfiguration.getHost(), config.getClientId()));
 | 
					        config.setUsername(AzureIotHubUtil.buildUsername(mqttNodeConfiguration.getHost(), config.getClientId()));
 | 
				
			||||||
        ClientCredentials credentials = mqttNodeConfiguration.getCredentials();
 | 
					        ClientCredentials credentials = mqttNodeConfiguration.getCredentials();
 | 
				
			||||||
        if (CredentialsType.SAS == credentials.getType()) {
 | 
					        if (CredentialsType.SAS == credentials.getType()) {
 | 
				
			||||||
            config.setPassword(AzureIotHubUtil.buildSasToken(mqttNodeConfiguration.getHost(), ((AzureIotHubSasCredentials) credentials).getSasKey()));
 | 
					            config.setPassword(AzureIotHubUtil.buildSasToken(mqttNodeConfiguration.getHost(), ((AzureIotHubSasCredentials) credentials).getSasKey(), clock));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -81,6 +86,11 @@ public class TbAzureIotHubNode extends TbMqttNode {
 | 
				
			|||||||
        return initClient(ctx);
 | 
					        return initClient(ctx);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @VisibleForTesting
 | 
				
			||||||
 | 
					    void setClock(Clock clock) {
 | 
				
			||||||
 | 
					        this.clock = clock;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
 | 
					    public TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException {
 | 
				
			||||||
        boolean hasChanges = false;
 | 
					        boolean hasChanges = false;
 | 
				
			||||||
 | 
				
			|||||||
@ -34,6 +34,9 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
				
			|||||||
import org.thingsboard.rule.engine.credentials.CertPemCredentials;
 | 
					import org.thingsboard.rule.engine.credentials.CertPemCredentials;
 | 
				
			||||||
import org.thingsboard.rule.engine.mqtt.TbMqttNodeConfiguration;
 | 
					import org.thingsboard.rule.engine.mqtt.TbMqttNodeConfiguration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.time.Clock;
 | 
				
			||||||
 | 
					import java.time.Instant;
 | 
				
			||||||
 | 
					import java.time.ZoneOffset;
 | 
				
			||||||
import java.util.stream.Stream;
 | 
					import java.util.stream.Stream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
					import static org.assertj.core.api.Assertions.assertThat;
 | 
				
			||||||
@ -77,7 +80,10 @@ public class TbAzureIotHubNodeTest extends AbstractRuleNodeUpgradeTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    public void verifyPrepareMqttClientConfigMethodWithAzureIotHubSasCredentials() throws Exception {
 | 
					    public void verifyPrepareMqttClientConfigMethodWithAzureIotHubSasCredentials() throws Exception {
 | 
				
			||||||
        AzureIotHubSasCredentials credentials = new AzureIotHubSasCredentials();
 | 
					        var fixedClock = Clock.fixed(Instant.parse("2030-01-01T00:00:00Z"), ZoneOffset.UTC);
 | 
				
			||||||
 | 
					        azureIotHubNode.setClock(fixedClock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var credentials = new AzureIotHubSasCredentials();
 | 
				
			||||||
        credentials.setSasKey("testSasKey");
 | 
					        credentials.setSasKey("testSasKey");
 | 
				
			||||||
        credentials.setCaCert("test-ca-cert.pem");
 | 
					        credentials.setCaCert("test-ca-cert.pem");
 | 
				
			||||||
        azureIotHubNodeConfig.setCredentials(credentials);
 | 
					        azureIotHubNodeConfig.setCredentials(credentials);
 | 
				
			||||||
@ -89,7 +95,7 @@ public class TbAzureIotHubNodeTest extends AbstractRuleNodeUpgradeTest {
 | 
				
			|||||||
        azureIotHubNode.prepareMqttClientConfig(mqttClientConfig);
 | 
					        azureIotHubNode.prepareMqttClientConfig(mqttClientConfig);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assertThat(mqttClientConfig.getUsername()).isEqualTo(AzureIotHubUtil.buildUsername(azureIotHubNodeConfig.getHost(), mqttClientConfig.getClientId()));
 | 
					        assertThat(mqttClientConfig.getUsername()).isEqualTo(AzureIotHubUtil.buildUsername(azureIotHubNodeConfig.getHost(), mqttClientConfig.getClientId()));
 | 
				
			||||||
        assertThat(mqttClientConfig.getPassword()).isEqualTo(AzureIotHubUtil.buildSasToken(azureIotHubNodeConfig.getHost(), credentials.getSasKey()));
 | 
					        assertThat(mqttClientConfig.getPassword()).isEqualTo(AzureIotHubUtil.buildSasToken(azureIotHubNodeConfig.getHost(), credentials.getSasKey(), fixedClock));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user