Skip to content

Commit

Permalink
Switch opcua tests to test containers.
Browse files Browse the repository at this point in the history
Relates to #1410.
Checked in MiloTestContainer which creates on-the-fly container based on static configuration.

Signed-off-by: Łukasz Dywicki <[email protected]>
  • Loading branch information
splatch committed Aug 6, 2024
1 parent 9410635 commit b84406b
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 21 deletions.
6 changes: 6 additions & 0 deletions plc4j/drivers/opcua/Dockerfile.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM eclipse-temurin:17

ADD "target/milo-testcontainer/*.jar" "/opt/milo/"
ADD "target/test-classes/org/eclipse/milo" "/opt/milo/org/eclipse/milo"

CMD java -cp '/opt/milo/*:/opt/milo/' org.eclipse.milo.examples.server.TestMiloServer
35 changes: 35 additions & 0 deletions plc4j/drivers/opcua/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,23 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-milo-dependencies</id>
<goals>
<goal>copy-dependencies</goal>
</goals>
<phase>test-compile</phase>
<configuration>
<includeArtifactIds>server-examples</includeArtifactIds>
<outputDirectory>${project.build.directory}/milo-testcontainer</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
Expand Down Expand Up @@ -186,6 +203,24 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.20.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.20.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-core</artifactId>
<version>0.22.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.plc4x.java.opcua;

import java.nio.file.Path;
import java.nio.file.Paths;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.images.builder.ImageFromDockerfile;

public class MiloTestContainer extends GenericContainer<MiloTestContainer> {

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

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

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

private static ImageFromDockerfile inlineImage() {
Path absolutePath = Paths.get(".").toAbsolutePath();
logger.info("Building milo server image from {}", absolutePath);
return new ImageFromDockerfile("plc4x-milo-test", false)
.withBuildImageCmdModifier(cmd -> cmd.withNoCache(true))
.withDockerfile(absolutePath.resolve("Dockerfile.test"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,31 @@
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;

import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;

@Testcontainers(disabledWithoutDocker = true)
@DisableOnJenkinsFlag
@Disabled("This test regularly fails the build, it needs to be generally fixed.")
public class OpcuaPlcDriverTest {

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

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

// 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 Down Expand Up @@ -123,50 +137,55 @@ public class OpcuaPlcDriverTest {
//Restricted
public static final String STRING_IDENTIFIER_ONLY_ADMIN_READ_WRITE = "ns=2;s=HelloWorld/OnlyAdminCanRead/String";

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

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

private final String tcpConnectionAddress = opcPattern + miloLocalAddress;

private final List<String> connectionStringValidSet = List.of(tcpConnectionAddress);
private final List<String> connectionStringCorruptedSet = List.of();

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));
connectionStringValidSet = List.of(tcpConnectionAddress);
}

@BeforeAll
public static 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.
Path securityBaseDir = Paths.get(System.getProperty("java.io.tmpdir"), "server", "security");
try {
Files.delete(securityBaseDir);
} catch (Exception e) {
// Ignore this ...
}

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

@AfterAll
public static void tearDown() throws Exception {
if (exampleServer != null) {
exampleServer.shutdown().get();
}
// if (exampleServer != null) {
// exampleServer.shutdown().get();
// }
}

@Nested
Expand Down Expand Up @@ -526,7 +545,8 @@ private String getConnectionString(SecurityPolicy policy, MessageSecurity messag
case Basic256Sha256:
case Aes128_Sha256_RsaOaep:
case Aes256_Sha256_RsaPss:
Path keyStoreFile = Paths.get(System.getProperty("java.io.tmpdir"), "server", "security", "example-server.pfx");
// this file and its contents should be populated by milo container
Path keyStoreFile = Paths.get("target/tmp/server/security/example-server.pfx");
String connectionParams = Stream.of(
entry("key-store-file", keyStoreFile.toAbsolutePath().toString().replace("\\", "/")), // handle windows paths
entry("key-store-password", "password"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,17 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
import org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig;
import org.eclipse.milo.opcua.sdk.server.identity.CompositeValidator;
Expand All @@ -52,6 +57,7 @@
import org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode;
import org.eclipse.milo.opcua.stack.core.types.structured.BuildInfo;
import org.eclipse.milo.opcua.stack.core.util.CertificateUtil;
import org.eclipse.milo.opcua.stack.core.util.NonceUtil;
import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator;
import org.eclipse.milo.opcua.stack.core.util.SelfSignedHttpsCertificateBuilder;
import org.eclipse.milo.opcua.stack.server.EndpointConfiguration;
Expand All @@ -69,6 +75,32 @@ public class TestMiloServer {
private final OpcUaServer server;
private final ExampleNamespace exampleNamespace;

static {
// Required for SecurityPolicy.Aes256_Sha256_RsaPss
Security.addProvider(new BouncyCastleProvider());

try {
NonceUtil.blockUntilSecureRandomSeeded(10, TimeUnit.SECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException e) {
e.printStackTrace();
System.exit(-1);
}
}

public static void main(String[] args) throws Exception {
TestMiloServer server = new TestMiloServer();

server.startup().thenAccept(srv -> {
System.out.println("Server started");
}).get();

final CompletableFuture<Void> future = new CompletableFuture<>();

Runtime.getRuntime().addShutdownHook(new Thread(() -> future.complete(null)));

future.get();
}

public TestMiloServer() throws Exception {
Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "server", "security");
Files.createDirectories(securityTempDir);
Expand Down Expand Up @@ -168,6 +200,7 @@ private Set<EndpointConfiguration> createEndpointConfigurations(X509Certificate
bindAddresses.add("0.0.0.0");

Set<String> hostnames = new LinkedHashSet<>();
hostnames.add("localhost");
hostnames.add(HostnameUtil.getHostname());
hostnames.addAll(HostnameUtil.getHostnames("0.0.0.0"));

Expand Down

0 comments on commit b84406b

Please sign in to comment.