diff --git a/pom.xml b/pom.xml index c2a2829..ca3ff05 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ 1.9.1.Final 1.0.8.Final - 1.65.1 + 1.66.0 1.3.2 2.0.0 4.12.0 diff --git a/subsystem/src/main/resources/schema/grpc-subsystem_1_0.xsd b/subsystem/src/main/resources/schema/grpc-subsystem_1_0.xsd index 97e51a1..e9e2820 100644 --- a/subsystem/src/main/resources/schema/grpc-subsystem_1_0.xsd +++ b/subsystem/src/main/resources/schema/grpc-subsystem_1_0.xsd @@ -19,12 +19,11 @@ targetNamespace="urn:wildfly:grpc:1.0" xmlns="urn:wildfly:grpc:1.0" elementFormDefault="qualified" - attributeFormDefault="unqualified" version="1.0"> - + @@ -33,7 +32,40 @@ ]]> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/subsystem/src/test/java/org/wildfly/extension/grpc/SubsystemTestCase.java b/subsystem/src/test/java/org/wildfly/extension/grpc/SubsystemTestCase.java index ea61444..1267363 100644 --- a/subsystem/src/test/java/org/wildfly/extension/grpc/SubsystemTestCase.java +++ b/subsystem/src/test/java/org/wildfly/extension/grpc/SubsystemTestCase.java @@ -19,6 +19,7 @@ import org.jboss.as.subsystem.test.AbstractSubsystemBaseTest; import org.jboss.as.subsystem.test.AdditionalInitialization; +import org.junit.Test; /** * @author Kabir Khan @@ -43,4 +44,9 @@ protected String getSubsystemXsdPath() { protected AdditionalInitialization createAdditionalInitialization() { return AdditionalInitialization.withCapabilities("org.wildfly.weld"); } + + @Test + public void testExpressions() throws Exception { + standardSubsystemTest("grpc-subsystem-expressions.xml", false); + } } diff --git a/subsystem/src/test/resources/org/wildfly/extension/grpc/grpc-subsystem-expressions.xml b/subsystem/src/test/resources/org/wildfly/extension/grpc/grpc-subsystem-expressions.xml new file mode 100644 index 0000000..0b4b126 --- /dev/null +++ b/subsystem/src/test/resources/org/wildfly/extension/grpc/grpc-subsystem-expressions.xml @@ -0,0 +1,42 @@ + + \ No newline at end of file diff --git a/subsystem/src/test/resources/org/wildfly/extension/grpc/grpc-subsystem-test.xml b/subsystem/src/test/resources/org/wildfly/extension/grpc/grpc-subsystem-test.xml index 315a493..9517c81 100644 --- a/subsystem/src/test/resources/org/wildfly/extension/grpc/grpc-subsystem-test.xml +++ b/subsystem/src/test/resources/org/wildfly/extension/grpc/grpc-subsystem-test.xml @@ -1,6 +1,6 @@ - \ No newline at end of file + diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index 416b3bf..49a3821 100644 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -41,6 +41,11 @@ junit test + + org.jboss + jboss-dmr + 1.7.0.Final + org.jboss.arquillian.junit arquillian-junit-container @@ -61,10 +66,21 @@ shrinkwrap-impl-base test + + org.wildfly.arquillian + wildfly-arquillian-common + 5.1.0.Beta4 + test + org.wildfly.arquillian wildfly-arquillian-container-managed test + + org.wildfly.core + wildfly-controller-client + 25.0.0.Final + \ No newline at end of file diff --git a/testsuite/integration/subsystem/pom.xml b/testsuite/integration/subsystem/pom.xml index 14512f1..924af8b 100644 --- a/testsuite/integration/subsystem/pom.xml +++ b/testsuite/integration/subsystem/pom.xml @@ -16,80 +16,142 @@ limitations under the License. --> - - 4.0.0 + + 4.0.0 - - wildfly-grpc-testsuite-integration - org.wildfly.extras.grpc - 0.1.4-SNAPSHOT - ../pom.xml - + + wildfly-grpc-testsuite-integration + org.wildfly.extras.grpc + 0.1.4-SNAPSHOT + ../pom.xml + - wildfly-grpc-testsuite-integration-subsystem - WildFly gRPC :: Test Suite :: Integration :: Subsystem + wildfly-grpc-testsuite-integration-subsystem + WildFly gRPC :: Test Suite :: Integration :: Subsystem - - - ${project.groupId} - wildfly-grpc-feature-pack - pom - provided - - + + + ${project.groupId} + wildfly-grpc-feature-pack + pom + provided + - - - - org.wildfly.plugins - wildfly-maven-plugin - - - - org.wildfly - wildfly-galleon-pack - ${version.wildfly} - - - ${project.groupId} - wildfly-grpc-feature-pack - ${project.version} - - - - web-server - jmx-remoting - - grpc - - - ${galleon.fork.embedded} - - wildfly - ${galleon.log.time} - true - - - - server-provisioning - - provision - - compile - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - ${jboss.dist} - ${server.jvm.args} - - - - - + + io.grpc + grpc-netty-shaded + runtime + + + io.grpc + grpc-protobuf + + + io.grpc + grpc-stub + + + javax.annotation + javax.annotation-api + + + com.google.guava + failureaccess + + + org.wildfly.extras.grpc + wildfly-grpc-subsystem + + + + + + + kr.motd.maven + os-maven-plugin + 1.7.1 + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + + compile-proto + + compile + compile-custom + + + grpc-java + + com.google.protobuf:protoc:${version.protobuf}:exe:${os.detected.classifier} + + io.grpc:protoc-gen-grpc-java:${version.grpc}:exe:${os.detected.classifier} + + /home/rsigal/tmp/wildfly.grpc.v014/wildfly-grpc-feature-pack/testsuite/integration/subsystem/src/main/proto/ + true + + helloworld.proto + chat.proto + + + + + + + org.wildfly.plugins + wildfly-maven-plugin + + + + org.wildfly + wildfly-galleon-pack + ${version.wildfly} + + + ${project.groupId} + wildfly-grpc-feature-pack + ${project.version} + + + + web-server + jmx-remoting + + grpc + + + ${galleon.fork.embedded} + + wildfly + ${galleon.log.time} + true + + + + server-provisioning + + provision + + compile + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${jboss.dist} + ${server.jvm.args} + + + + + \ No newline at end of file diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/GreeterServiceImpl.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/GreeterServiceImpl.java new file mode 100644 index 0000000..c15f573 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/GreeterServiceImpl.java @@ -0,0 +1,29 @@ +/* + * Copyright 2022 Red Hat + * + * 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 + * + * https://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.wildfly.feature.pack.grpc.test.helloworld; + +import io.grpc.stub.StreamObserver; + +public class GreeterServiceImpl extends GreeterGrpc.GreeterImplBase { + + @Override + public void sayHello(HelloRequest request, StreamObserver responseObserver) { + String name = request.getName(); + String message = "Hello " + name; + responseObserver.onNext(HelloReply.newBuilder().setMessage(message).build()); + responseObserver.onCompleted(); + } +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/HelloWorldParent.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/HelloWorldParent.java new file mode 100644 index 0000000..2ec7d99 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/HelloWorldParent.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022 Red Hat + * + * 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 + * + * https://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.wildfly.feature.pack.grpc.test.helloworld; + +import java.util.concurrent.TimeUnit; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Test; + +import io.grpc.ManagedChannel; + +public class HelloWorldParent { + + protected static final String TARGET = "localhost:9555"; + protected static final String TARGET_HOST = "localhost"; + protected static final int TARGET_PORT = 9555; + + protected static ManagedChannel channel = null; + protected static GreeterGrpc.GreeterBlockingStub blockingStub; + + @AfterClass + public static void afterClass() throws Exception { + channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + + @Test + public void hello() { + HelloRequest request = HelloRequest.newBuilder().setName("Bob").build(); + HelloReply reply = blockingStub.sayHello(request); + Assert.assertEquals("Hello Bob", reply.getMessage()); + } +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/OnewaySecureHelloWorldTest.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/OnewaySecureHelloWorldTest.java new file mode 100644 index 0000000..633fbdd --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/OnewaySecureHelloWorldTest.java @@ -0,0 +1,131 @@ +/* + * Copyright 2022 Red Hat + * + * 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 + * + * https://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.wildfly.feature.pack.grpc.test.helloworld; + +import java.io.InputStream; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.as.arquillian.api.ServerSetup; +import org.jboss.as.arquillian.container.ManagementClient; +import org.jboss.as.arquillian.setup.SnapshotServerSetupTask; +import org.jboss.as.controller.client.helpers.Operations; +import org.jboss.as.controller.client.helpers.Operations.CompositeOperationBuilder; +import org.jboss.dmr.ModelNode; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.wildfly.feature.pack.grpc.test.utility.ServerReload; + +import io.grpc.ChannelCredentials; +import io.grpc.Grpc; +import io.grpc.TlsChannelCredentials; + +@RunWith(Arquillian.class) +@ServerSetup(OnewaySecureHelloWorldTest.SslServerSetupTask.class) +@RunAsClient +public class OnewaySecureHelloWorldTest extends HelloWorldParent { + + public static class SslServerSetupTask extends SnapshotServerSetupTask { + + @Override + protected void doSetup(final ManagementClient client, final String containerId) throws Exception { + secureServer(client); + } + } + + protected static void secureServer(final ManagementClient client) throws Exception { + final CompositeOperationBuilder builder = CompositeOperationBuilder.create(); + + // /subsystem=elytron/key-store=grpc-key-store:add(credential-reference={clear-text="secret"}, type=JKS, + // path="server.keystore.jks", relative-to="jboss.server.config.dir", required=false) + ModelNode address = Operations.createAddress("subsystem", "elytron", "key-store", "grpc-key-store"); + ModelNode op = Operations.createAddOperation(address); + final ModelNode credentialRef = new ModelNode(); + credentialRef.get("clear-text").set("secret"); + op.get("credential-reference").set(credentialRef); + op.get("type").set("JKS"); + op.get("path").set("../../../ssl/server.keystore.jks"); + // op.get("relative-to").set("jboss.server.config.dir"); + op.get("required").set(false); + builder.addStep(op); + + // /subsystem=elytron/key-manager=grpc-key-manager:add(key-store=grpc-key-store, + // credential-reference={clear-text="secret"}) + address = Operations.createAddress("subsystem", "elytron", "key-manager", "grpc-key-manager"); + op = Operations.createAddOperation(address); + op.get("key-store").set("grpc-key-store"); + op.get("credential-reference").set(credentialRef); + builder.addStep(op); + + // /subsystem=elytron/server-ssl-context=grpc-ssl-context:add(cipher-suite-filter=DEFAULT, protocols=["TLSv1.2"], + // want-client-auth="false", need-client-auth="true", authentication-optional="false", + // use-cipher-suites-order="false", key-manager="grpc-key-manager", + // trust-manager="grpc-key-store-trust-manager") + address = Operations.createAddress("subsystem", "elytron", "server-ssl-context", "grpc-ssl-context"); + op = Operations.createAddOperation(address); + op.get("cipher-suite-filter").set("DEFAULT"); + final ModelNode protocols = new ModelNode().setEmptyList(); + protocols.add("TLSv1.2"); + op.get("protocols").set(protocols); + op.get("want-client-auth").set(false); + op.get("need-client-auth").set(true); + op.get("authentication-optional").set(false); + op.get("use-cipher-suites-order").set(false); + op.get("key-manager").set("grpc-key-manager"); + builder.addStep(op); + + // /subsystem=undertow/server=default-server/https-listener=https:add(socket-binding=https, + // ssl-context="grpc-ssl-context") + address = Operations.createAddress("subsystem", "undertow", "server", "default-server", "https-listener", "https"); + op = Operations.createAddOperation(address); + op.get("socket-binding").set("https"); + op.get("ssl-context").set("grpc-ssl-context"); + builder.addStep(op); + + // /subsystem=grpc:write-attribute(name=key-manager-name, value="grpc-key-manager") + address = Operations.createAddress("subsystem", "grpc"); + builder.addStep(Operations.createWriteAttributeOperation(address, "key-manager-name", "grpc-key-manager")); + + final var result = client.getControllerClient().execute(builder.build()); + if (!Operations.isSuccessfulOutcome(result)) { + throw new RuntimeException("Failed to configure SSL context: " + Operations.getFailureDescription(result)); + } + ServerReload.reloadIfRequired(client.getControllerClient()); + } + + @Deployment + public static Archive createTestArchive() { + WebArchive war = ShrinkWrap.create(WebArchive.class, "OnewaySecureHelloWorldTest.war"); + war.addClasses(OnewaySecureHelloWorldTest.class, GreeterServiceImpl.class); + war.addPackage(HelloRequest.class.getPackage()); + war.addClass(GreeterGrpc.class); + // war.as(ZipExporter.class).exportTo( + // new File("/tmp/hello.war"), true); + return war; + } + + @BeforeClass + public static void beforeClass() throws Exception { + InputStream trustStore = OnewaySecureHelloWorldTest.class.getClassLoader().getResourceAsStream("client.truststore.pem"); + ChannelCredentials creds = TlsChannelCredentials.newBuilder().trustManager(trustStore).build(); + channel = Grpc.newChannelBuilderForAddress(TARGET_HOST, TARGET_PORT, creds).build(); + blockingStub = GreeterGrpc.newBlockingStub(channel); + } +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/PlaintextHelloWorldTest.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/PlaintextHelloWorldTest.java new file mode 100644 index 0000000..489ebd2 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/PlaintextHelloWorldTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2022 Red Hat + * + * 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 + * + * https://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.wildfly.feature.pack.grpc.test.helloworld; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; + +import io.grpc.ManagedChannelBuilder; + +@RunWith(Arquillian.class) +@RunAsClient +public class PlaintextHelloWorldTest extends HelloWorldParent { + + @Deployment + public static Archive createTestArchive() { + WebArchive war = ShrinkWrap.create(WebArchive.class, "PlaintextHelloWorldTest.war"); + war.addClasses(PlaintextHelloWorldTest.class, GreeterServiceImpl.class); + war.addPackage(HelloRequest.class.getPackage()); + war.addClass(GreeterGrpc.class); + // war.as(ZipExporter.class).exportTo( + // new File("/tmp/hello.war"), true); + return war; + } + + @BeforeClass + public static void beforeClass() { + channel = ManagedChannelBuilder.forTarget(TARGET).usePlaintext().build(); + blockingStub = GreeterGrpc.newBlockingStub(channel); + } +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/TwowaySecureHelloWorldTest.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/TwowaySecureHelloWorldTest.java new file mode 100644 index 0000000..5176941 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/helloworld/TwowaySecureHelloWorldTest.java @@ -0,0 +1,155 @@ +/* + * Copyright 2022 Red Hat + * + * 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 + * + * https://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.wildfly.feature.pack.grpc.test.helloworld; + +import java.io.InputStream; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.as.arquillian.api.ServerSetup; +import org.jboss.as.arquillian.container.ManagementClient; +import org.jboss.as.arquillian.setup.SnapshotServerSetupTask; +import org.jboss.as.controller.client.helpers.Operations; +import org.jboss.as.controller.client.helpers.Operations.CompositeOperationBuilder; +import org.jboss.dmr.ModelNode; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.wildfly.feature.pack.grpc.test.utility.ServerReload; + +import io.grpc.ChannelCredentials; +import io.grpc.Grpc; +import io.grpc.TlsChannelCredentials; + +@RunWith(Arquillian.class) +@ServerSetup(TwowaySecureHelloWorldTest.SslServerSetupTask.class) +@RunAsClient +public class TwowaySecureHelloWorldTest extends HelloWorldParent { + + public static class SslServerSetupTask extends SnapshotServerSetupTask { + + @Override + protected void doSetup(final ManagementClient client, final String containerId) throws Exception { + secureServer(client); + } + } + + protected static void secureServer(final ManagementClient client) throws Exception { + final CompositeOperationBuilder builder = CompositeOperationBuilder.create(); + + // /subsystem=elytron/key-store=grpc-key-store:add(credential-reference={clear-text="secret"}, type=JKS, + // path="server.keystore.jks", relative-to="jboss.server.config.dir", required=false) + ModelNode address = Operations.createAddress("subsystem", "elytron", "key-store", "grpc-key-store"); + ModelNode op = Operations.createAddOperation(address); + final ModelNode credentialRef = new ModelNode(); + credentialRef.get("clear-text").set("secret"); + op.get("credential-reference").set(credentialRef); + op.get("type").set("JKS"); + op.get("path").set("../../../ssl/server.keystore.jks"); + // op.get("relative-to").set("jboss.server.config.dir"); + op.get("required").set(false); + builder.addStep(op); + + // /subsystem=elytron/key-store=grpc-trust-store:add(credential-reference={clear-text="secret"}, type=JKS, + // required=false, path="server.truststore.jks", relative-to="jboss.server.config.dir") + address = Operations.createAddress("subsystem", "elytron", "key-store", "grpc-trust-store"); + op = Operations.createAddOperation(address); + op.get("credential-reference").set(credentialRef); + op.get("type").set("JKS"); + op.get("path").set("./../../ssl/server.truststore.jks"); + // op.get("relative-to").set("jboss.server.config.dir"); + builder.addStep(op); + + // /subsystem=elytron/key-manager=grpc-key-manager:add(key-store=grpc-key-store, + // credential-reference={clear-text="secret"}) + address = Operations.createAddress("subsystem", "elytron", "key-manager", "grpc-key-manager"); + op = Operations.createAddOperation(address); + op.get("key-store").set("grpc-key-store"); + op.get("credential-reference").set(credentialRef); + builder.addStep(op); + + // /subsystem=elytron/trust-manager=grpc-key-store-trust-manager:add(key-store="grpc-trust-store") + address = Operations.createAddress("subsystem", "elytron", "trust-manager", "grpc-key-store-trust-manager"); + op = Operations.createAddOperation(address); + op.get("key-store").set("grpc-trust-store"); + builder.addStep(op); + + // /subsystem=elytron/server-ssl-context=grpc-ssl-context:add(cipher-suite-filter=DEFAULT, protocols=["TLSv1.2"], + // want-client-auth="false", need-client-auth="true", authentication-optional="false", + // use-cipher-suites-order="false", key-manager="grpc-key-manager", + // trust-manager="grpc-key-store-trust-manager") + address = Operations.createAddress("subsystem", "elytron", "server-ssl-context", "grpc-ssl-context"); + op = Operations.createAddOperation(address); + op.get("cipher-suite-filter").set("DEFAULT"); + final ModelNode protocols = new ModelNode().setEmptyList(); + protocols.add("TLSv1.2"); + op.get("protocols").set(protocols); + op.get("want-client-auth").set(false); + op.get("need-client-auth").set(true); + op.get("authentication-optional").set(false); + op.get("use-cipher-suites-order").set(false); + op.get("key-manager").set("grpc-key-manager"); + op.get("trust-manager").set("grpc-key-store-trust-manager"); + builder.addStep(op); + + // /subsystem=undertow/server=default-server/https-listener=https:add(socket-binding=https, + // ssl-context="grpc-ssl-context") + address = Operations.createAddress("subsystem", "undertow", "server", "default-server", "https-listener", "https"); + op = Operations.createAddOperation(address); + op.get("socket-binding").set("https"); + op.get("ssl-context").set("grpc-ssl-context"); + builder.addStep(op); + + // /subsystem=grpc:write-attribute(name=key-manager-name, value="grpc-key-manager") + address = Operations.createAddress("subsystem", "grpc"); + builder.addStep(Operations.createWriteAttributeOperation(address, "key-manager-name", "grpc-key-manager")); + + final var result = client.getControllerClient().execute(builder.build()); + if (!Operations.isSuccessfulOutcome(result)) { + throw new RuntimeException("Failed to configure SSL context: " + Operations.getFailureDescription(result)); + } + ServerReload.reloadIfRequired(client.getControllerClient()); + } + + @Deployment + public static Archive createTestArchive() { + WebArchive war = ShrinkWrap.create(WebArchive.class, "TwowaySecureHelloWorldTest.war"); + war.addClasses(TwowaySecureHelloWorldTest.class, GreeterServiceImpl.class); + war.addPackage(HelloRequest.class.getPackage()); + war.addClass(GreeterGrpc.class); + // war.as(ZipExporter.class).exportTo( + // new File("/tmp/hello.war"), true); + return war; + } + + @BeforeClass + public static void beforeClass() throws Exception { + + ClassLoader classLoader = TwowaySecureHelloWorldTest.class.getClassLoader(); + InputStream trustStore = classLoader.getResourceAsStream("client.truststore.pem"); + InputStream keyStore = classLoader.getResourceAsStream("client.keystore.pem"); + InputStream key = classLoader.getResourceAsStream("client.key.pem"); + ChannelCredentials creds = TlsChannelCredentials.newBuilder() + .trustManager(trustStore) + .keyManager(keyStore, key) + .build(); + channel = Grpc.newChannelBuilderForAddress(TARGET_HOST, TARGET_PORT, creds).build(); + blockingStub = GreeterGrpc.newBlockingStub(channel); + } +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/ChatServiceImpl.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/ChatServiceImpl.java new file mode 100644 index 0000000..abc5bff --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/ChatServiceImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022 Red Hat + * + * 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 + * + * https://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.wildfly.feature.pack.grpc.test.stream; + +import org.wildfly.extension.grpc.example.chat.ChatMessage; +import org.wildfly.extension.grpc.example.chat.ChatServiceGrpc; + +import io.grpc.stub.StreamObserver; + +public class ChatServiceImpl extends ChatServiceGrpc.ChatServiceImplBase { + + @Override + public StreamObserver chat(final StreamObserver responseObserver) { + return new StreamObserver() { + @Override + public void onNext(ChatMessage request) { + String s = Integer.toString(Integer.valueOf(request.getMessage()) * 2); + ChatMessage cm = ChatMessage.newBuilder().setMessage(s).build(); + responseObserver.onNext(cm); + } + + @Override + public void onCompleted() { + responseObserver.onCompleted(); + } + + @Override + public void onError(Throwable t) { + throw new RuntimeException(t); + } + }; + } +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/OnewaySecureStreamingTest.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/OnewaySecureStreamingTest.java new file mode 100644 index 0000000..ee122c6 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/OnewaySecureStreamingTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2022 Red Hat + * + * 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 + * + * https://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.wildfly.feature.pack.grpc.test.stream; + +import java.io.InputStream; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.as.arquillian.api.ServerSetup; +import org.jboss.as.arquillian.container.ManagementClient; +import org.jboss.as.arquillian.setup.SnapshotServerSetupTask; +import org.jboss.as.controller.client.helpers.Operations; +import org.jboss.as.controller.client.helpers.Operations.CompositeOperationBuilder; +import org.jboss.dmr.ModelNode; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.wildfly.extension.grpc.example.chat.ChatMessage; +import org.wildfly.extension.grpc.example.chat.ChatServiceGrpc; +import org.wildfly.feature.pack.grpc.test.utility.ServerReload; + +import io.grpc.ChannelCredentials; +import io.grpc.Grpc; +import io.grpc.TlsChannelCredentials; + +@RunWith(Arquillian.class) +@ServerSetup(OnewaySecureStreamingTest.SslServerSetupTask.class) +@RunAsClient +public class OnewaySecureStreamingTest extends StreamingTestParent { + + public static class SslServerSetupTask extends SnapshotServerSetupTask { + + @Override + protected void doSetup(final ManagementClient client, final String containerId) throws Exception { + secureServer(client); + } + } + + protected static void secureServer(final ManagementClient client) throws Exception { + final CompositeOperationBuilder builder = CompositeOperationBuilder.create(); + + // /subsystem=elytron/key-store=grpc-key-store:add(credential-reference={clear-text="secret"}, type=JKS, + // path="server.keystore.jks", relative-to="jboss.server.config.dir", required=false) + ModelNode address = Operations.createAddress("subsystem", "elytron", "key-store", "grpc-key-store"); + ModelNode op = Operations.createAddOperation(address); + final ModelNode credentialRef = new ModelNode(); + credentialRef.get("clear-text").set("secret"); + op.get("credential-reference").set(credentialRef); + op.get("type").set("JKS"); + op.get("path").set("../../../ssl/server.keystore.jks"); + // op.get("relative-to").set("jboss.server.config.dir"); + op.get("required").set(false); + builder.addStep(op); + + // /subsystem=elytron/key-manager=grpc-key-manager:add(key-store=grpc-key-store, + // credential-reference={clear-text="secret"}) + address = Operations.createAddress("subsystem", "elytron", "key-manager", "grpc-key-manager"); + op = Operations.createAddOperation(address); + op.get("key-store").set("grpc-key-store"); + op.get("credential-reference").set(credentialRef); + builder.addStep(op); + + // /subsystem=elytron/server-ssl-context=grpc-ssl-context:add(cipher-suite-filter=DEFAULT, protocols=["TLSv1.2"], + // want-client-auth="false", need-client-auth="true", authentication-optional="false", + // use-cipher-suites-order="false", key-manager="grpc-key-manager", + // trust-manager="grpc-key-store-trust-manager") + address = Operations.createAddress("subsystem", "elytron", "server-ssl-context", "grpc-ssl-context"); + op = Operations.createAddOperation(address); + op.get("cipher-suite-filter").set("DEFAULT"); + final ModelNode protocols = new ModelNode().setEmptyList(); + protocols.add("TLSv1.2"); + op.get("protocols").set(protocols); + op.get("want-client-auth").set(false); + op.get("need-client-auth").set(true); + op.get("authentication-optional").set(false); + op.get("use-cipher-suites-order").set(false); + op.get("key-manager").set("grpc-key-manager"); + builder.addStep(op); + + // /subsystem=undertow/server=default-server/https-listener=https:add(socket-binding=https, + // ssl-context="grpc-ssl-context") + address = Operations.createAddress("subsystem", "undertow", "server", "default-server", "https-listener", "https"); + op = Operations.createAddOperation(address); + op.get("socket-binding").set("https"); + op.get("ssl-context").set("grpc-ssl-context"); + builder.addStep(op); + + // /subsystem=grpc:write-attribute(name=key-manager-name, value="grpc-key-manager") + address = Operations.createAddress("subsystem", "grpc"); + builder.addStep(Operations.createWriteAttributeOperation(address, "key-manager-name", "grpc-key-manager")); + + final var result = client.getControllerClient().execute(builder.build()); + if (!Operations.isSuccessfulOutcome(result)) { + throw new RuntimeException("Failed to configure SSL context: " + Operations.getFailureDescription(result)); + } + ServerReload.reloadIfRequired(client.getControllerClient()); + } + + @Deployment + public static Archive createTestArchive() { + WebArchive war = ShrinkWrap.create(WebArchive.class, "OnewaySecureStreamingTest.war"); + war.addClasses(OnewaySecureStreamingTest.class, ChatServiceImpl.class); + war.addPackage(ChatMessage.class.getPackage()); + // war.as(ZipExporter.class).exportTo( + // new File("/tmp/hello.war"), true); + return war; + } + + @BeforeClass + public static void beforeClass() throws Exception { + InputStream trustStore = OnewaySecureStreamingTest.class.getClassLoader().getResourceAsStream("client.truststore.pem"); + ChannelCredentials creds = TlsChannelCredentials.newBuilder().trustManager(trustStore).build(); + channel = Grpc.newChannelBuilderForAddress(TARGET_HOST, TARGET_PORT, creds).build(); + stub = ChatServiceGrpc.newStub(channel); + } +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/PlaintextStreamingTest.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/PlaintextStreamingTest.java new file mode 100644 index 0000000..2d5a976 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/PlaintextStreamingTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2022 Red Hat + * + * 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 + * + * https://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.wildfly.feature.pack.grpc.test.stream; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.wildfly.extension.grpc.example.chat.ChatMessage; +import org.wildfly.extension.grpc.example.chat.ChatServiceGrpc; + +import io.grpc.ManagedChannelBuilder; + +@RunWith(Arquillian.class) +@RunAsClient +public class PlaintextStreamingTest extends StreamingTestParent { + + @Deployment + public static Archive createTestArchive() { + WebArchive war = ShrinkWrap.create(WebArchive.class, "PlaintextStreamingTest.war"); + war.addClasses(PlaintextStreamingTest.class, ChatServiceImpl.class); + war.addPackage(ChatMessage.class.getPackage()); + // war.as(ZipExporter.class).exportTo( + // new File("/tmp/hello.war"), true); + return war; + } + + @BeforeClass + public static void beforeClass() { + channel = ManagedChannelBuilder.forTarget(TARGET).usePlaintext().build(); + stub = ChatServiceGrpc.newStub(channel); + } +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/StreamingTestParent.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/StreamingTestParent.java new file mode 100644 index 0000000..dea1bba --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/StreamingTestParent.java @@ -0,0 +1,86 @@ +/* + * Copyright 2022 Red Hat + * + * 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 + * + * https://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.wildfly.feature.pack.grpc.test.stream; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Test; +import org.wildfly.extension.grpc.example.chat.ChatMessage; +import org.wildfly.extension.grpc.example.chat.ChatServiceGrpc; + +import io.grpc.ManagedChannel; +import io.grpc.stub.StreamObserver; + +public class StreamingTestParent { + + protected static final String TARGET = "localhost:9555"; + protected static final String TARGET_HOST = "localhost"; + protected static final int TARGET_PORT = 9555; + + protected static ManagedChannel channel = null; + protected static ChatServiceGrpc.ChatServiceStub stub; + protected int sum; + + @AfterClass + public static void afterClass() throws Exception { + channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + + @Test + public void streamingTest() throws InterruptedException { + + final CountDownLatch finishLatch = new CountDownLatch(1); + StreamObserver responseObserver = new StreamObserver() { + + @Override + public void onNext(ChatMessage message) { + sum += Integer.valueOf(message.getMessage()); + } + + @Override + public void onCompleted() { + finishLatch.countDown(); + } + + @Override + public void onError(Throwable t) { + finishLatch.countDown(); + } + }; + StreamObserver requestObserver = stub.chat(responseObserver); + try { + for (int i = 0; i < 5; i++) { + requestObserver.onNext(ChatMessage.newBuilder().setMessage(Integer.toString(i)).build()); + if (finishLatch.getCount() == 0) { + return; + } + } + } catch (RuntimeException e) { + requestObserver.onError(e); + throw e; + } + requestObserver.onCompleted(); + + if (!finishLatch.await(1, TimeUnit.MINUTES)) { + Assert.fail("test can not finish within 1 minute"); + } + Assert.assertEquals(20, sum); + } + +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/TwowaySecureStreamingTest.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/TwowaySecureStreamingTest.java new file mode 100644 index 0000000..ce20fc6 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/stream/TwowaySecureStreamingTest.java @@ -0,0 +1,155 @@ +/* + * Copyright 2022 Red Hat + * + * 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 + * + * https://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.wildfly.feature.pack.grpc.test.stream; + +import java.io.InputStream; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.as.arquillian.api.ServerSetup; +import org.jboss.as.arquillian.container.ManagementClient; +import org.jboss.as.arquillian.setup.SnapshotServerSetupTask; +import org.jboss.as.controller.client.helpers.Operations; +import org.jboss.as.controller.client.helpers.Operations.CompositeOperationBuilder; +import org.jboss.dmr.ModelNode; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.wildfly.extension.grpc.example.chat.ChatMessage; +import org.wildfly.extension.grpc.example.chat.ChatServiceGrpc; +import org.wildfly.feature.pack.grpc.test.utility.ServerReload; + +import io.grpc.ChannelCredentials; +import io.grpc.Grpc; +import io.grpc.TlsChannelCredentials; + +@RunWith(Arquillian.class) +@ServerSetup(TwowaySecureStreamingTest.SslServerSetupTask.class) +@RunAsClient +public class TwowaySecureStreamingTest extends StreamingTestParent { + + public static class SslServerSetupTask extends SnapshotServerSetupTask { + + @Override + protected void doSetup(final ManagementClient client, final String containerId) throws Exception { + secureServer(client); + } + } + + protected static void secureServer(final ManagementClient client) throws Exception { + final CompositeOperationBuilder builder = CompositeOperationBuilder.create(); + + // /subsystem=elytron/key-store=grpc-key-store:add(credential-reference={clear-text="secret"}, type=JKS, + // path="server.keystore.jks", relative-to="jboss.server.config.dir", required=false) + ModelNode address = Operations.createAddress("subsystem", "elytron", "key-store", "grpc-key-store"); + ModelNode op = Operations.createAddOperation(address); + final ModelNode credentialRef = new ModelNode(); + credentialRef.get("clear-text").set("secret"); + op.get("credential-reference").set(credentialRef); + op.get("type").set("JKS"); + op.get("path").set("../../../ssl/server.keystore.jks"); + // op.get("relative-to").set("jboss.server.config.dir"); + op.get("required").set(false); + builder.addStep(op); + + // /subsystem=elytron/key-store=grpc-trust-store:add(credential-reference={clear-text="secret"}, type=JKS, + // required=false, path="server.truststore.jks", relative-to="jboss.server.config.dir") + address = Operations.createAddress("subsystem", "elytron", "key-store", "grpc-trust-store"); + op = Operations.createAddOperation(address); + op.get("credential-reference").set(credentialRef); + op.get("type").set("JKS"); + op.get("path").set("./../../ssl/server.truststore.jks"); + // op.get("relative-to").set("jboss.server.config.dir"); + builder.addStep(op); + + // /subsystem=elytron/key-manager=grpc-key-manager:add(key-store=grpc-key-store, + // credential-reference={clear-text="secret"}) + address = Operations.createAddress("subsystem", "elytron", "key-manager", "grpc-key-manager"); + op = Operations.createAddOperation(address); + op.get("key-store").set("grpc-key-store"); + op.get("credential-reference").set(credentialRef); + builder.addStep(op); + + // /subsystem=elytron/trust-manager=grpc-key-store-trust-manager:add(key-store="grpc-trust-store") + address = Operations.createAddress("subsystem", "elytron", "trust-manager", "grpc-key-store-trust-manager"); + op = Operations.createAddOperation(address); + op.get("key-store").set("grpc-trust-store"); + builder.addStep(op); + + // /subsystem=elytron/server-ssl-context=grpc-ssl-context:add(cipher-suite-filter=DEFAULT, protocols=["TLSv1.2"], + // want-client-auth="false", need-client-auth="true", authentication-optional="false", + // use-cipher-suites-order="false", key-manager="grpc-key-manager", + // trust-manager="grpc-key-store-trust-manager") + address = Operations.createAddress("subsystem", "elytron", "server-ssl-context", "grpc-ssl-context"); + op = Operations.createAddOperation(address); + op.get("cipher-suite-filter").set("DEFAULT"); + final ModelNode protocols = new ModelNode().setEmptyList(); + protocols.add("TLSv1.2"); + op.get("protocols").set(protocols); + op.get("want-client-auth").set(false); + op.get("need-client-auth").set(true); + op.get("authentication-optional").set(false); + op.get("use-cipher-suites-order").set(false); + op.get("key-manager").set("grpc-key-manager"); + op.get("trust-manager").set("grpc-key-store-trust-manager"); + builder.addStep(op); + + // /subsystem=undertow/server=default-server/https-listener=https:add(socket-binding=https, + // ssl-context="grpc-ssl-context") + address = Operations.createAddress("subsystem", "undertow", "server", "default-server", "https-listener", "https"); + op = Operations.createAddOperation(address); + op.get("socket-binding").set("https"); + op.get("ssl-context").set("grpc-ssl-context"); + builder.addStep(op); + + // /subsystem=grpc:write-attribute(name=key-manager-name, value="grpc-key-manager") + address = Operations.createAddress("subsystem", "grpc"); + builder.addStep(Operations.createWriteAttributeOperation(address, "key-manager-name", "grpc-key-manager")); + + final var result = client.getControllerClient().execute(builder.build()); + if (!Operations.isSuccessfulOutcome(result)) { + throw new RuntimeException("Failed to configure SSL context: " + Operations.getFailureDescription(result)); + } + ServerReload.reloadIfRequired(client.getControllerClient()); + } + + @Deployment + public static Archive createTestArchive() { + WebArchive war = ShrinkWrap.create(WebArchive.class, "TwowaySecureStreamingTest.war"); + war.addClasses(OnewaySecureStreamingTest.class, ChatServiceImpl.class); + war.addPackage(ChatMessage.class.getPackage()); + // war.as(ZipExporter.class).exportTo( + // new File("/tmp/hello.war"), true); + return war; + } + + @BeforeClass + public static void beforeClass() throws Exception { + ClassLoader classLoader = TwowaySecureStreamingTest.class.getClassLoader(); + InputStream trustStore = classLoader.getResourceAsStream("client.truststore.pem"); + InputStream keyStore = classLoader.getResourceAsStream("client.keystore.pem"); + InputStream key = classLoader.getResourceAsStream("client.key.pem"); + ChannelCredentials creds = TlsChannelCredentials.newBuilder() + .trustManager(trustStore) + .keyManager(keyStore, key) + .build(); + channel = Grpc.newChannelBuilderForAddress(TARGET_HOST, TARGET_PORT, creds).build(); + stub = ChatServiceGrpc.newStub(channel); + } +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/utility/ServerReload.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/utility/ServerReload.java new file mode 100644 index 0000000..4cc0ffb --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/feature/pack/grpc/test/utility/ServerReload.java @@ -0,0 +1,162 @@ +/* + * Copyright 2022 Red Hat + * + * 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 + * + * https://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.wildfly.feature.pack.grpc.test.utility; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; + +import org.jboss.as.controller.client.ModelControllerClient; +import org.jboss.as.controller.client.ModelControllerClientConfiguration; +import org.jboss.as.controller.client.helpers.Operations; +import org.jboss.dmr.ModelNode; +import org.junit.Assert; + +/** + * Utilities for handling server reloads. + * + * @author Stuart Douglas + * @author James R. Perkins + */ +@SuppressWarnings("unused") +public class ServerReload { + /** + * Default time, in ms, to wait for reload to complete. + */ + public static final int TIMEOUT = 100000; + private static final ModelNode EMPTY_ADDRESS = new ModelNode().setEmptyList(); + + /** + * Reloads the server and returns immediately. + * + * @param client the client used to execute the reload operation + */ + public static void executeReload(final ModelControllerClient client) { + executeReload(client, Operations.createOperation("reload")); + } + + /** + * Reloads the server and returns immediately. + * + * @param client the client used to execute the reload operation + * @param reloadOp the reload operation to execute + */ + public static void executeReload(final ModelControllerClient client, final ModelNode reloadOp) { + try { + final ModelNode result = client.execute(reloadOp); + if (!Operations.isSuccessfulOutcome(result)) { + Assert.fail(Operations.getFailureDescription(result) + .asString()); + } + } catch (IOException e) { + final Throwable cause = e.getCause(); + if (!(cause instanceof ExecutionException) && !(cause instanceof CancellationException)) { + throw new RuntimeException(e); + } // else ignore, this might happen if the channel gets closed before we got the response + } + } + + /** + * Executes a {@code reload} operation and waits the {@link #TIMEOUT default timeout} + * for the reload to complete. + * + * @param client the client to use for the request. Cannot be {@code null} + * + * @throws AssertionError if the reload does not complete within the timeout + */ + public static void executeReloadAndWaitForCompletion(final ModelControllerClient client) { + executeReloadAndWaitForCompletion(client, TIMEOUT); + } + + /** + * Executes a {@code reload} operation and waits a configurable maximum time for the reload to complete. + * + * @param client the client to use for the request. Cannot be {@code null} + * @param timeout maximum time to wait for the reload to complete, in milliseconds + * + * @throws AssertionError if the reload does not complete within the specified timeout + */ + public static void executeReloadAndWaitForCompletion(final ModelControllerClient client, final int timeout) { + executeReload(client); + waitForLiveServerToReload(timeout, createDefaultConfig()); + } + + static ModelControllerClientConfiguration createDefaultConfig() { + return new ModelControllerClientConfiguration.Builder() + .setHostName("localhost") + .setPort(9990) + .build(); + } + + /** + * Returns the current running state, {@code server-state}, of the server. + * + * @param client the client used to execute the operation + * + * @return the running state or "failed" if the operation was unsuccessful + * + * @throws IOException if a communication error occurs + */ + public static String getContainerRunningState(final ModelControllerClient client) throws IOException { + final ModelNode rsp = client.execute(Operations.createReadAttributeOperation(EMPTY_ADDRESS, "server-state")); + return Operations.isSuccessfulOutcome(rsp) ? Operations.readResult(rsp) + .asString() : "failed"; + } + + /** + * Checks if the container status is "reload-required" and if it's the case executes reload and waits for completion. + * + * @param client the client used to execute the operation + * + * @throws IOException if a communication error occurs + */ + public static void reloadIfRequired(final ModelControllerClient client) throws Exception { + final String runningState = getContainerRunningState(client); + if ("reload-required".equalsIgnoreCase(runningState)) { + executeReloadAndWaitForCompletion(client); + } else { + Assert.assertEquals("running", runningState, "Server state 'running' is expected"); + } + } + + private static void waitForLiveServerToReload(final int timeout, final ModelControllerClientConfiguration config) { + final long start = System.currentTimeMillis(); + final ModelNode operation = Operations.createReadAttributeOperation(EMPTY_ADDRESS, "server-state"); + while (System.currentTimeMillis() - start < timeout) { + // do the sleep before we check, as the attribute state may not change instantly + // also reload generally takes longer than 100ms anyway + try { + Thread.sleep(100); + } catch (InterruptedException ignore) { + } + try ( + ModelControllerClient liveClient = ModelControllerClient.Factory.create(config)) { + try { + final ModelNode result = liveClient.execute(operation); + if (Operations.isSuccessfulOutcome(result) && "running".equals(Operations.readResult(result) + .asString())) { + return; + } + } catch (IOException ignore) { + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + Assert.fail("Live Server did not reload in the imparted time."); + } +} diff --git a/testsuite/integration/subsystem/src/test/resources/arquillian2.xml b/testsuite/integration/subsystem/src/test/resources/arquillian2.xml new file mode 100644 index 0000000..247376f --- /dev/null +++ b/testsuite/integration/subsystem/src/test/resources/arquillian2.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + target/wildfly + -Xmx512m -XX:MaxPermSize=128m -Xverify:none -XX:+UseFastAccessorMethods + -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y + + + + + \ No newline at end of file diff --git a/testsuite/integration/subsystem/src/test/resources/arquillian3.xml b/testsuite/integration/subsystem/src/test/resources/arquillian3.xml new file mode 100644 index 0000000..947c102 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/resources/arquillian3.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + target/wildfly + 9990 + + + target/wildfly + true + 9990 + -Xmx512m -XX:MaxPermSize=128m + -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y + + + + + \ No newline at end of file diff --git a/testsuite/integration/subsystem/src/test/resources/arquillian_save.xml b/testsuite/integration/subsystem/src/test/resources/arquillian_save.xml new file mode 100644 index 0000000..ae2ae9c --- /dev/null +++ b/testsuite/integration/subsystem/src/test/resources/arquillian_save.xml @@ -0,0 +1,27 @@ + + + + + target/wildfly + + + \ No newline at end of file diff --git a/testsuite/integration/subsystem/src/test/resources/client.cer b/testsuite/integration/subsystem/src/test/resources/client.cer new file mode 100644 index 0000000..9ae2fa9 Binary files /dev/null and b/testsuite/integration/subsystem/src/test/resources/client.cer differ diff --git a/testsuite/integration/subsystem/src/test/resources/client.key.pem b/testsuite/integration/subsystem/src/test/resources/client.key.pem new file mode 100644 index 0000000..fa2f900 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/resources/client.key.pem @@ -0,0 +1,20 @@ +Bag Attributes + friendlyName: client + localKeyID: 54 69 6D 65 20 31 36 37 33 36 35 37 30 32 32 31 31 34 +Key Attributes: +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMqFZbw9peJumKJM +VkleSABBWMS2R2zmRhIGJTT4xbeMAVysdndArJ2m5/GDEI19HMc02TrNyXq8khkK +toocPy2jRLwO70tI8zAt3YTaQXgbNPyESpLDVfCz1c6/06RujMeg1/n5p7YhtlHv +Nr8KuZlsBCy/4FbYe0WrCy2BkSwdAgMBAAECgYBu8/CN1fSI/nCPEmV/orCtux9n +/jlZdztSap19zQF9kq24WSA6K2umn6eZUGYELlRS6yhMKCxHGwKrx4vEVL9jRWAe +SY6xf3pr4tFFs1XpuHIuVc+H9YMQR9MedXC3JLeC7EvPfQg0DWAAJtKl9hCEFohi +sR2qkoZ16pbvL/1rwQJBAP8lQJzhHLJOTYjFSoNOz/oK/xCPknFuE7ecgAbOaaEJ +vk66eB9NCoUn8ujvOUAgIOsvTx7QmOu9oyhziJLgAlECQQDLMwcaCV5vqIE2dzAL +/JEj3fGdz++09//uCE8C1dcNUe2UYCUi8jdh61PRSowYp47w1tp3jj5Gvdh2QGPL +Pq4NAkEAgEuBE+F2BoqtHgrmjuRAW+DPpMosvvC05WzCS6nbH2jA0uGcqVCZ657M +3Cf+R6pgIyJkzH/jhRaURjDiCciuIQJAOFyFAcHLgekZPgQ9PXXmxC4RkJZWhLmt +MVb4o26w4a7x2Q/5/QF2PyDI9OpahZQkX8UYf8TinTiXS+V8SKmwGQJBAJxg+hyK +fZPzC/WzlkJqHGXUvcPpOcBc6qs5boq78UG/u5G4AJvUrFSSZ9IL0bsY8C1o1TS4 +ecvkJ50LfLdYYXM= +-----END PRIVATE KEY----- diff --git a/testsuite/integration/subsystem/src/test/resources/client.keystore.jks b/testsuite/integration/subsystem/src/test/resources/client.keystore.jks new file mode 100644 index 0000000..f5e1a0a Binary files /dev/null and b/testsuite/integration/subsystem/src/test/resources/client.keystore.jks differ diff --git a/testsuite/integration/subsystem/src/test/resources/client.keystore.p12 b/testsuite/integration/subsystem/src/test/resources/client.keystore.p12 new file mode 100644 index 0000000..6ab0c23 Binary files /dev/null and b/testsuite/integration/subsystem/src/test/resources/client.keystore.p12 differ diff --git a/testsuite/integration/subsystem/src/test/resources/client.keystore.pem b/testsuite/integration/subsystem/src/test/resources/client.keystore.pem new file mode 100644 index 0000000..4e32712 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/resources/client.keystore.pem @@ -0,0 +1,39 @@ +Bag Attributes + friendlyName: client + localKeyID: 54 69 6D 65 20 31 36 37 33 36 35 37 30 32 32 31 31 34 +Key Attributes: +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIC3TBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIY/0fxTlvaBsCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBAvft7Ihc6jzZ+wh6zw7l9IBIIC +gAfZNHJCJjcYa6TAUPfDeJI52y2EiNAP2sXv1eVgjjYhjiBMGXouucUyGlhcHv93 +v6onflQcR104JH7gDxRH5h+irv5OUWxC6BnREBeYn5oCAAFLHFiw1wfo8V24eILV +8GOFg/Bre4rjkH4EztCgvy967IXqLvkUWcB1RvMRb/cpiT3L1y6V85WE5nLdk84g +PIwl2lVBeWlyPviTo1DHdB+PCgGBaWHDHar7CXhUmZGSPACeHXi8162De6z2fk+p +IwvcK3REIPat0uOVVseKGSMosncG1h8EDOsFF0zvjfFQ70Yc/cFzeGlgkoYX/u8m +JV7sW1X2e1cn9HsjF1mYmiwnNQVLRNeJv8NshzQAO3ub45lNEa2v69SaGLIbJezo +DEa5IS+84PI9UBF97/jj2XO0ab8cfwTecgtOT0q0WyQWFoX/h2+7ZOF89mrerVcr +KLvO8C0gcwN1LcglWhiNHFc0ouhWhBHRyUK0h3jFCThju2b6nIKETPIjrixjJqei +EnID1GQdZwPPvi2sdgZam1owjWH/d+U7TU32Ur5NJ6+9HZRFbzGUBvi3bcbXkF49 +DOryeaMnmaIbEkqEeL/OqOII8eC5wUyKudbIO9QJRgZD4mO8lKg2ZW/fTz3KVnUf +CtVWZnwQg3tqtnliHfrwcWuBfg6Jwf49a19R1k7sQl2q9y/nkmbLGDjd2evgYr4i +kLNYzKhWIv3clA0wZZJj1Syz1ykKVbwTyb37Cj9hUGoGeWGqO3XijI12TgrjzKUj +ksE6tPgfq4wjTYSUMlwm4V+1GYQ4CI541IQ00zfwSxycbYsAEPSuKcJeJ9Nk8oFj +8r4CPLGgwDJPKxHXCutiP8w= +-----END ENCRYPTED PRIVATE KEY----- +Bag Attributes + friendlyName: client + localKeyID: 54 69 6D 65 20 31 36 37 33 36 35 37 30 32 32 31 31 34 +subject=CN = client +issuer=CN = client +-----BEGIN CERTIFICATE----- +MIIBvDCCASWgAwIBAgIEJpQg0zANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZj +bGllbnQwHhcNMjMwMTE0MDA0MTQ5WhcNMjQwMTE0MDA0MTQ5WjARMQ8wDQYDVQQD +EwZjbGllbnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMqFZbw9peJumKJM +VkleSABBWMS2R2zmRhIGJTT4xbeMAVysdndArJ2m5/GDEI19HMc02TrNyXq8khkK +toocPy2jRLwO70tI8zAt3YTaQXgbNPyESpLDVfCz1c6/06RujMeg1/n5p7YhtlHv +Nr8KuZlsBCy/4FbYe0WrCy2BkSwdAgMBAAGjITAfMB0GA1UdDgQWBBROY4mupjhr +cqPboXYZC2FI+L9/jzANBgkqhkiG9w0BAQsFAAOBgQC9xX7ODNZq17GMl2TRJ+fv +QcQ4Wh+5CtzmSsCPgMClVZsMgWrp8Ksc3arvx0ePu9cTwTq7HlC1c6O13ix0+Oxr +Ahv53obJHvhq1Nk+2PP36xp78MmpXnLZayoeL+aFxy43IppUWU9Vfzy/FsSrhLi5 +hLtBnft9OjYclipah6FR3w== +-----END CERTIFICATE----- diff --git a/testsuite/integration/subsystem/src/test/resources/client.truststore.jks b/testsuite/integration/subsystem/src/test/resources/client.truststore.jks new file mode 100644 index 0000000..96e20a6 Binary files /dev/null and b/testsuite/integration/subsystem/src/test/resources/client.truststore.jks differ diff --git a/testsuite/integration/subsystem/src/test/resources/client.truststore.p12 b/testsuite/integration/subsystem/src/test/resources/client.truststore.p12 new file mode 100644 index 0000000..36656c7 Binary files /dev/null and b/testsuite/integration/subsystem/src/test/resources/client.truststore.p12 differ diff --git a/testsuite/integration/subsystem/src/test/resources/client.truststore.pem b/testsuite/integration/subsystem/src/test/resources/client.truststore.pem new file mode 100644 index 0000000..1f154da --- /dev/null +++ b/testsuite/integration/subsystem/src/test/resources/client.truststore.pem @@ -0,0 +1,26 @@ +Bag Attributes + friendlyName: localhost + 2.16.840.1.113894.746875.1.1: +subject=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = localhost +issuer=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = localhost +-----BEGIN CERTIFICATE----- +MIIDgDCCAmqgAwIBAgIJAM9zpAP+LEDUMAsGCSqGSIb3DQEBCzBuMRAwDgYDVQQG +EwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAw +DgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2Nh +bGhvc3QwIhgPMjAyMzAxMTQwMDQ2NDZaGA8yMDIzMDQxMzIzNDY0NlowbjEQMA4G +A1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93 +bjEQMA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjESMBAGA1UEAxMJ +bG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqv62Z24N +VKAsPAOJEN+5rnVgIR97FGvJI+QBW8/+N6jcx1K1PS0XI2wMimQUtjFb3J7zoMYw +krdMhfqtU/cKm+ZTzJdIrxnIW71HrcMv22Xdwl5O/j5RvoiEq3cwvlGyEHYFqfu/ +mLeZm1kHXp5agJaAzlPgGaRQf79XD3sPw9XgU1XdrfNOVQjjVAEsOcBisHg+IHgl +NPaVt7bZc/3obpLtoXfCae5WTF7Cf4asQXatgCgmGee98IjYhFX1YcHIFEB1J090 +Qa933jBhxDaL80ry1T0lqkF8m1imgluXvCwTasD6rM16GHSQfyYyaDlwZvm4z8cP +L9BP6T/YDsZuFQIDAQABoyEwHzAdBgNVHQ4EFgQUwR9o9yHBtox/AdxGjxyoKwXw +8TowCwYJKoZIhvcNAQELA4IBAQBYlRNkZfTJJpXGlCVbh3comEEinb6PNXupe3AB +ra3rsUGkWRR5NXp3Lu7M+0GQCz38etvV0sIEnH246M0igyYgbL1tS5O9pWfPeS77 +YDzeX4A9Vn1nYrKn70QgwazNDSp6BU7jckHZ5UiqkUmFHCnmB1vcvVy8ugFbHJdX +cNZ+3xyqKV5j+HZO8r/pFYPkRLwroZWf62wA9VY0Pf3ldnYtXzQ2iXqd0N/M+fMz +COC894eZ/pjKoyIoLGpmonw+B5jBAWAEFrDXkaYKGyqRqbPK374RiM8a2KvVzjlV +1aj1WptImi60JW0Pp79fk/Z6vrcSeTtWWHGEPqP+CzxR2AkS +-----END CERTIFICATE-----