Skip to content

Commit

Permalink
Switch OPC-UA tests to rely on dynamic port.
Browse files Browse the repository at this point in the history
This implies also slight change to endpoint selection method and possibility to override desired endpoint port and host which might differ from host/port reported by OPC-UA server.
Because server is not aware of actual tcp port and host used by client, it is client duty to make proper decission when looking for endpoint.

Signed-off-by: Łukasz Dywicki <[email protected]>
  • Loading branch information
splatch committed Oct 16, 2024
1 parent 9b64721 commit 1ff40e0
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 152 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ public class OpcuaConfiguration implements PlcConnectionConfiguration {
@Description("TCP encoding options")
private Limits limits;

@ConfigurationParameter("endpoint-host")
@Description("Endpoint host used to establish secure channel.")
private String endpointHost;

@ConfigurationParameter("endpoint-port")
@Description("Endpoint port used to establish secure channel")
private Integer endpointPort;

public String getProtocolCode() {
return protocolCode;
}
Expand Down Expand Up @@ -228,6 +236,14 @@ public long getNegotiationTimeout() {
return negotiationTimeout;
}

public String getEndpointHost() {
return endpointHost;
}

public Integer getEndpointPort() {
return endpointPort;
}

@Override
public String toString() {
return "OpcuaConfiguration{" +
Expand All @@ -240,5 +256,6 @@ public String toString() {
", limits=" + limits +
'}';
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,6 @@ public String getHost() {
return host;
}

public void setHost(String host) {
this.host = host;
}

public String getPort() {
return port;
}
Expand All @@ -126,10 +122,6 @@ public String getEndpoint() {
public String getTransportEndpoint() {
return transportEndpoint;
}

public void setTransportEndpoint(String transportEndpoint) {
this.transportEndpoint = transportEndpoint;
}

public X509Certificate getServerCertificate() {
return serverCertificate;
Expand All @@ -147,6 +139,13 @@ public void setConfiguration(OpcuaConfiguration configuration) {
port = matcher.group("transportPort");
transportEndpoint = matcher.group("transportEndpoint");

if (configuration.getEndpointHost() != null) {
host = configuration.getEndpointHost();
}
if (configuration.getEndpointPort() != null) {
port = String.valueOf(configuration.getEndpointPort());
}

String portAddition = port != null ? ":" + port : "";
endpoint = "opc." + code + "://" + host + portAddition + transportEndpoint;

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ public class MiloTestContainer extends GenericContainer<MiloTestContainer> {

private final static Logger logger = LoggerFactory.getLogger(MiloTestContainer.class);

private final static ImageFromDockerfile IMAGE = inlineImage();

public MiloTestContainer() {
super(inlineImage());
super(IMAGE);

waitingFor(Wait.forLogMessage("Server started\\s*", 1));
addFixedExposedPort(12686, 12686);
addExposedPort(12686);
}

private static ImageFromDockerfile inlineImage() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,17 @@
import org.slf4j.LoggerFactory;

import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutionException;
import java.util.stream.Stream;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.images.builder.ImageFromDockerfile;
import org.testcontainers.jib.JibImage;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

Expand All @@ -72,15 +76,13 @@
import static org.assertj.core.api.Assertions.fail;

@Testcontainers(disabledWithoutDocker = true)
@DisableOnJenkinsFlag
public class OpcuaPlcDriverTest {

private static final Logger LOGGER = LoggerFactory.getLogger(OpcuaPlcDriverTest.class);

@Container
public static final GenericContainer<MiloTestContainer> milo = new MiloTestContainer()
public final GenericContainer milo = new MiloTestContainer()
//.withCreateContainerCmdModifier(cmd -> cmd.withHostName("test-opcua-server"))
.withReuse(true)
.withLogConsumer(new Slf4jLogConsumer(LOGGER))
.withFileSystemBind("target/tmp/server/security", "/tmp/server/security", BindMode.READ_WRITE);

Expand Down Expand Up @@ -135,30 +137,28 @@ public class OpcuaPlcDriverTest {
public static final String STRING_IDENTIFIER_ONLY_ADMIN_READ_WRITE = "ns=2;s=HelloWorld/OnlyAdminCanRead/String";

// Address of local milo server, since it comes from test container its hostname and port is not static
private static final String miloLocalAddress = "%s:%d/milo";
private final String miloLocalAddress = "%s:%d/milo";
//Tcp pattern of OPC UA
private static final String opcPattern = "opcua:tcp://";
private final String opcPattern = "opcua:tcp://";

private static final String paramSectionDivider = "?";
private static final String paramDivider = "&";
private final String paramSectionDivider = "?";
private final String paramDivider = "&";

private static final String discoveryValidParamTrue = "discovery=true";
private static final String discoveryValidParamFalse = "discovery=false";
private static final String discoveryCorruptedParamWrongValueNum = "discovery=1";
private static final String discoveryCorruptedParamWrongName = "diskovery=false";
private final String discoveryValidParamTrue = "discovery=true";
private final String discoveryValidParamFalse = "discovery=false";
private final String discoveryCorruptedParamWrongValueNum = "discovery=1";
private final String discoveryCorruptedParamWrongName = "diskovery=false";

private String tcpConnectionAddress;
private List<String> connectionStringValidSet;

final List<String> discoveryParamValidSet = List.of(discoveryValidParamTrue, discoveryValidParamFalse);
List<String> discoveryParamCorruptedSet = List.of(discoveryCorruptedParamWrongValueNum, discoveryCorruptedParamWrongName);

private static TestMiloServer exampleServer;

@BeforeEach
public void startUp() {
//System.out.println(milo.getMappedPort(12686));
tcpConnectionAddress = String.format(opcPattern + miloLocalAddress, milo.getHost(), milo.getMappedPort(12686));
tcpConnectionAddress = String.format(opcPattern + miloLocalAddress, milo.getHost(), milo.getMappedPort(12686)) + "?endpoint-port=12686";
connectionStringValidSet = List.of(tcpConnectionAddress);
}

Expand Down Expand Up @@ -275,7 +275,7 @@ Stream<DynamicNode> connectionWithDiscoveryParam() throws Exception {
return connectionStringValidSet.stream()
.map(connectionAddress -> DynamicContainer.dynamicContainer(connectionAddress, () ->
discoveryParamValidSet.stream().map(discoveryParam -> DynamicTest.dynamicTest(discoveryParam, () -> {
String connectionString = connectionAddress + paramSectionDivider + discoveryParam;
String connectionString = connectionAddress + paramDivider + discoveryParam;
PlcConnection opcuaConnection = new DefaultPlcDriverManager().getConnection(connectionString);
Condition<PlcConnection> is_connected = new Condition<>(PlcConnection::isConnected, "is connected");
assertThat(opcuaConnection).is(is_connected);
Expand All @@ -290,7 +290,7 @@ Stream<DynamicNode> connectionWithDiscoveryParam() throws Exception {
@Test
void connectionWithUrlAuthentication() throws Exception {
DefaultPlcDriverManager driverManager = new DefaultPlcDriverManager();
try (PlcConnection opcuaConnection = driverManager.getConnection(tcpConnectionAddress + "?username=admin&password=password2")) {
try (PlcConnection opcuaConnection = driverManager.getConnection(tcpConnectionAddress + "&username=admin&password=password2")) {
Condition<PlcConnection> is_connected = new Condition<>(PlcConnection::isConnected, "is connected");
assertThat(opcuaConnection).is(is_connected);

Expand Down Expand Up @@ -325,7 +325,7 @@ void connectionWithPlcAuthentication() throws Exception {
@Test
void connectionWithPlcAuthenticationOverridesUrlParam() throws Exception {
DefaultPlcDriverManager driverManager = new DefaultPlcDriverManager();
try (PlcConnection opcuaConnection = driverManager.getConnection(tcpConnectionAddress + "?username=user&password=password1",
try (PlcConnection opcuaConnection = driverManager.getConnection(tcpConnectionAddress + "&username=user&password=password1",
new PlcUsernamePasswordAuthentication("admin", "password2"))) {
Condition<PlcConnection> is_connected = new Condition<>(PlcConnection::isConnected, "is connected");
assertThat(opcuaConnection).is(is_connected);
Expand Down Expand Up @@ -459,7 +459,6 @@ public void writeVariables(SecurityPolicy policy, MessageSecurity messageSecurit
Test added to test the synchronized TransactionHandler. (This was disabled before being enabled again so it might be a candidate for those tests not running properly on different platforms)
*/
@Test
@Disabled("Disabled flaky test. Tracking issue at https://github.com/apache/plc4x/issues/1764")
public void multipleThreads() throws Exception {
class ReadWorker extends Thread {
private final PlcConnection connection;
Expand Down Expand Up @@ -554,7 +553,7 @@ private String getConnectionString(SecurityPolicy policy, MessageSecurity messag
.map(tuple -> tuple.getKey() + "=" + URLEncoder.encode(tuple.getValue(), Charset.defaultCharset()))
.collect(Collectors.joining(paramDivider));

return tcpConnectionAddress + paramSectionDivider + connectionParams;
return tcpConnectionAddress + paramDivider + connectionParams;
default:
throw new IllegalStateException();
}
Expand All @@ -565,19 +564,19 @@ private static Stream<Arguments> getConnectionSecurityPolicies() {
Arguments.of(SecurityPolicy.NONE, MessageSecurity.NONE),
Arguments.of(SecurityPolicy.NONE, MessageSecurity.SIGN),
Arguments.of(SecurityPolicy.NONE, MessageSecurity.SIGN_ENCRYPT),
Arguments.of(SecurityPolicy.Basic256Sha256, MessageSecurity.NONE),
//Arguments.of(SecurityPolicy.Basic256Sha256, MessageSecurity.NONE),
Arguments.of(SecurityPolicy.Basic256Sha256, MessageSecurity.SIGN),
Arguments.of(SecurityPolicy.Basic256Sha256, MessageSecurity.SIGN_ENCRYPT),
Arguments.of(SecurityPolicy.Basic256, MessageSecurity.NONE),
//Arguments.of(SecurityPolicy.Basic256, MessageSecurity.NONE),
Arguments.of(SecurityPolicy.Basic256, MessageSecurity.SIGN),
Arguments.of(SecurityPolicy.Basic256, MessageSecurity.SIGN_ENCRYPT),
Arguments.of(SecurityPolicy.Basic128Rsa15, MessageSecurity.NONE),
//Arguments.of(SecurityPolicy.Basic128Rsa15, MessageSecurity.NONE),
Arguments.of(SecurityPolicy.Basic128Rsa15, MessageSecurity.SIGN),
Arguments.of(SecurityPolicy.Basic128Rsa15, MessageSecurity.SIGN_ENCRYPT),
Arguments.of(SecurityPolicy.Aes128_Sha256_RsaOaep, MessageSecurity.NONE),
//Arguments.of(SecurityPolicy.Aes128_Sha256_RsaOaep, MessageSecurity.NONE),
Arguments.of(SecurityPolicy.Aes128_Sha256_RsaOaep, MessageSecurity.SIGN),
Arguments.of(SecurityPolicy.Aes128_Sha256_RsaOaep, MessageSecurity.SIGN_ENCRYPT),
Arguments.of(SecurityPolicy.Aes256_Sha256_RsaPss, MessageSecurity.NONE),
//Arguments.of(SecurityPolicy.Aes256_Sha256_RsaPss, MessageSecurity.NONE),
Arguments.of(SecurityPolicy.Aes256_Sha256_RsaPss, MessageSecurity.SIGN),
Arguments.of(SecurityPolicy.Aes256_Sha256_RsaPss, MessageSecurity.SIGN_ENCRYPT)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@
import org.apache.plc4x.java.api.messages.PlcSubscriptionRequest;
import org.apache.plc4x.java.api.messages.PlcSubscriptionResponse;
import org.apache.plc4x.java.api.types.PlcResponseCode;
import org.apache.plc4x.java.opcua.MiloTestContainer;
import org.apache.plc4x.java.opcua.OpcuaPlcDriverTest;
import org.apache.plc4x.test.DisableInDockerFlag;
import org.apache.plc4x.test.DisableOnJenkinsFlag;
import org.apache.plc4x.test.DisableOnParallelsVmFlag;
import org.eclipse.milo.examples.server.ExampleServer;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
Expand All @@ -41,6 +40,11 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand All @@ -52,26 +56,25 @@
// cdutz: I have done way more than my fair share on tracking down this issue and am simply giving up on it.
// I tracked it down into the core of Milo several times now, but got lost in there.
// It's not a big issue as the GitHub runners and the Apache Jenkins still run the test.
@DisableOnParallelsVmFlag
@DisableInDockerFlag
@DisableOnJenkinsFlag
@Disabled("This test seems to randomly fail on ANY CI platform")
@Testcontainers(disabledWithoutDocker = true)
public class OpcuaSubscriptionHandleTest {

private static final Logger LOGGER = LoggerFactory.getLogger(OpcuaPlcDriverTest.class);

private static ExampleServer exampleServer;
@Container
public final GenericContainer milo = new MiloTestContainer()
//.withCreateContainerCmdModifier(cmd -> cmd.withHostName("test-opcua-server"))
.withLogConsumer(new Slf4jLogConsumer(LOGGER))
.withFileSystemBind("target/tmp/server/security", "/tmp/server/security", BindMode.READ_WRITE);

// Address of local milo server
private static final String miloLocalAddress = "127.0.0.1:12686/milo";
private static final String miloLocalAddress = "%s:%d/milo";
//Tcp pattern of OPC UA
private static final String opcPattern = "opcua:tcp://";

private final String paramSectionDivider = "?";
private final String paramDivider = "&";

private static final String tcpConnectionAddress = opcPattern + miloLocalAddress;

// Read only variables of milo example server of version 3.6
private static final String BOOL_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Boolean";
private static final String BYTE_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/Byte";
Expand All @@ -89,35 +92,34 @@ public class OpcuaSubscriptionHandleTest {
private static final String UINTEGER_IDENTIFIER_READ_WRITE = "ns=2;s=HelloWorld/ScalarTypes/UInteger";
private static final String DOES_NOT_EXIST_IDENTIFIER_READ_WRITE = "ns=2;i=12512623";

private static PlcConnection opcuaConnection;
private PlcConnection opcuaConnection;

// ! If this test fails, see comment at the top of the class before investigating.
@BeforeAll
public static void setup() throws Exception {
@BeforeEach
public void setup() throws Exception {
// When switching JDK versions from a newer to an older version,
// this can cause the server to not start correctly.
// Deleting the directory makes sure the key-store is initialized correctly.

String tcpConnectionAddress = String.format(opcPattern + miloLocalAddress, milo.getHost(), milo.getMappedPort(12686)) + "?endpoint-port=12686";

Path securityBaseDir = Paths.get(System.getProperty("java.io.tmpdir"), "server", "security");
try {
Files.delete(securityBaseDir);
} catch (Exception e) {
// Ignore this ...
}

exampleServer = new ExampleServer();
exampleServer.startup().get();
//Connect
opcuaConnection = new DefaultPlcDriverManager().getConnection(tcpConnectionAddress);
assertThat(opcuaConnection).extracting(PlcConnection::isConnected).isEqualTo(true);
}

@AfterAll
public static void tearDown() throws Exception {
@AfterEach
public void tearDown() throws Exception {
// Close Connection
opcuaConnection.close();
assertThat(opcuaConnection).extracting(PlcConnection::isConnected).isEqualTo(false);

exampleServer.shutdown().get();
}

// ! If this test fails, see comment at the top of the class before investigating.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;

@DisableOnJenkinsFlag
@Disabled("Disabled flaky test. Tracking issue at https://github.com/apache/plc4x/issues/1764")
class ChunkFactoryTest {

public static final Map<Integer, Entry<PrivateKey, X509Certificate>> CERTIFICATES = new ConcurrentHashMap<>();
Expand Down

0 comments on commit 1ff40e0

Please sign in to comment.