Testing Fuse Fabric with Pax Exam



Testing Fuse Fabric with Pax Exam

0 1


testing-fuse-fabric-with-pax-exam


On Github hekonsek / testing-fuse-fabric-with-pax-exam

Testing Fuse Fabric with Pax Exam

Brought to you by Henryk Konsek / @hekonsek

This talk

Our goals Pax Exam Case studies

1. Our goals

Our problems

  • OSGi ;)
  • it works on my computer aka responsibility ping-pong
  • creating repeatable middleware proof of concepts is difficult
  • customers going production without proper testing

Our goals

  • repeatable tests of middleware - infracoding
  • OSGi issues detected and fixed as soon as possible
  • educated customers sending us runnable and repeatable examples instead of help me requests

2. Pax Exam

What is Pax Exam

  • JUnit friendly framework for running tests in real Karaf containers
  • Exam deploys JUnit test case as a bundle
  • if the bundle can be successfully tested with the Exam, then it can be successfully deployed to Karaf

Exam hello world!

Project structure
my-project
    my-project-services
    my-project-routes
    ...
    my-project-bundle
    my-project-itest

Exam hello world!

JUnit class
@RunWith(PaxExam.class)
public class MyKarafTest extends Assert {

  @Inject
  MyOsgiService myOsgiService;

  @Configuration
  public Option[] configuration() {
    ...
  }

    @Test
    public void shouldReturnResponse() {
      ...
    }

}

Exam hello world!

Karaf configuration
@Configuration
public Option[] configuration() {
  return new Option[]{
    karafDistributionConfiguration()
      .frameworkUrl(
        maven().groupId("org.apache.karaf").artifactId("apache-karaf")
          .type("zip").version("2.3.3")).
          karafVersion("2.3.3").name("Apache Karaf")
        .unpackDirectory(new File("target/pax"))
        .useDeployFolder(false), keepRuntimeFolder(),
    configureConsole().ignoreLocalConsole().ignoreRemoteShell(),

    mavenBundle().groupId("com.example").artifactId("my-project-bundle").
      versionAsInProject()};
}

Exam hello world!

Testing OSGi service
@RunWith(PaxExam.class)
public class MyKarafTest extends Assert {

    @Inject
    MyOsgiService myOsgiService;

    @Configuration
    public Option[] configuration() {
    ...
    }

    @Test
    public void shouldReturnResponse() {
      assertEquals("Hello!", myOsgiService.helloWorld());
    }

}

Exam hello world!

Testing OSGi Camel service
@RunWith(PaxExam.class)
public class MyDeployedCamelTest extends Assert {

  @Inject
  CamelContext camelContext;

  @Configuration
  public Option[] configuration() {...}

  @Test
  public void shouldReturnResponse() {
    String response = camelContext.createProducerTemplate().
      requestBody("jms:queue", "msg", String.class);
    assertEquals("Hello!", response);
  }

}

3. Case studies

Case #1: Hello world!

Arm your POM
<repositories>
  <repository>
    <id>jboss-fuse-ea</id>
    <url>https://repository.jboss.org/nexus/content/groups/ea</url>
  </repository>
</repositories>

Engineering guys deploy here.

<properties>
  <fabric-version>1.0.0.redhat-340</fabric-version>
</properties>

Pick up some bleeding edge yet stable version of Fabric test API.

Case #1: Hello world!

Arm your POM
<dependency>
  <groupid>io.fabric8</groupid>
  <artifactid>fabric8-karaf</artifactid>
  <version>${fabric-version}</version>
  <type>zip</type>
</dependency>

Download Fabric distribution.

<dependency>
  <groupid>io.fabric8.itests</groupid>
  <artifactid>fabric-itests-common</artifactid>
  <version>${fabric-version}</version>
</dependency>

Include Fabric test API (and nothing more!).

Case #1: Hello world!

Tested Camel route
public class NettyHttpRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
      from("netty-http:http://localhost:18080/").
        setBody().constant("Hello world!");
    }

}

Case #1: Hello world!

Base test class
import io.fabric8.itests.paxexam.support.FabricTestSupport;
...
@RunWith(JUnit4TestRunner.class)
@ExamReactorStrategy(AllConfinedStagedReactorFactory.class)
public class SimpleFabricTest extends FabricTestSupport {

  @Configuration
  public Option[] config() {
    return new Option[]{
      new DefaultCompositeOption(fabricDistributionConfiguration()),
        mavenBundle("commons-io", "commons-io").versionAsInProject()
      };
  }
  ...

}
  • almost no config? Buy Ioannis a beer for creating the FabricTestSupport ;)
  • yeah, this is the old and ugly Pax Exam 2.x API...
  • you need to configure everything you use in the test bundle

Case #1: Hello world!

Deploying Camel route
import static java.lang.System.err;

@Test
public void shouldCreateCamelRouter() throws Exception {
  err.println(executeCommand("fabric:create -n"));
  err.println(executeCommand("fabric:profile-create " +
    "--parents feature-camel netty-http-server"));
  err.println(executeCommand("fabric:profile-edit " +
    "--features camel-netty-http netty-http-server"));
  err.println(executeCommand("fabric:profile-edit --bundles " +
    "mvn:com.example/my-project-bundle/1.0-SNAPSHOT netty-http-server"));

  ContainerBuilder.create().
    withName("router-container").withProfiles("netty-http-server").
    assertProvisioningResult().build();
  // ... assertions
}
  • String output = executeCommand("some:karafCommand");
  • assertProvisioningResult == block until container is ready

Case #1: Hello world!

Testing deployed route
@Test
public void shouldCreateCamelRouter() throws Exception {
  // ... creating container

  InputStream inputStream =
    new URL("http://localhost:18080/").openStream();
  String response = IOUtils.toString(inputStream);
  assertEquals("Hello world!", response);
}
  • a poor-man's HTTP client

Case #1: Hello world!

User friendly output
fabric:create -n
Using specified zookeeper password:admin

fabric:profile-create --parents feature-camel netty-http-server
fabric:profile-edit --features camel-netty-http netty-http-server
Adding feature:camel-netty-http to profile:netty-http-server version:1.0

fabric:profile-edit --bundles ... netty-http-server

Adding bundle:mvn:... to profile:netty-http-server version:1.0
Waiting for containers: [ router-container1 ] to successfully provision
Container:router-container1 Alive:false Status: SSH URL:null
Container:router-container1 Alive:true Status:analyzing SSH URL:...
Container:router-container1 Alive:true Status:downloading SSH URL:...
Container:router-container1 Alive:true Status:finalizing SSH URL:...
Container:router-container1 Alive:true Status:success SSH URL:...
  • output partially omitted (SSH address!)
  • very similar to the output from the real Karaf session

Case #2: Hacking child containers with SSH

Container container = (Container) create().withName("router-container").
  withProfiles("netty-http-server").
  assertProvisioningResult().build().iterator().next();

String[] containerSshUrl = container.getSshUrl().split(":");
String containerHost = containerSshUrl[0];
String containerPort = containerSshUrl[1];

String bundlesOnContainer = executeCommand(format(
  "ssh -l %s -P %s -p %s %s osgi:list",
  "admin", "admin", containerPort, containerHost
));

assertTrue(bundlesOnContainer.contains("camel-netty-http"));
  • running Karaf container can be accessed via SSH client
  • we can execute any remote Karaf command via ssh

Case #3: Fabric Master component test

  • many customers request singleton Camel route in the clustered environment
  • Fabric Master FTW!
  • it is difficult to provide proof of concept for the customer
  • Pax Exam + Fabric Master = no brainer demo

Case #3: Fabric Master component test

public class MasterRoute extends RouteBuilder {

  @Override
  public void configure() throws Exception {
    from("master:netty-master:netty-http:http://localhost:18081/").
      setBody().constant("master");
  }

}
  • the clustered route we want to test
  • only single Netty instance should handle requests at given moment

Case #3: Fabric Master component test

fabric:create -n
fabric:profile-create --parents feature-camel master-netty
fabric:profile-edit --features camel-netty-http master-netty
fabric:profile-edit
  --bundles mvn:com.example/my-project-bundle/1.0-SNAPSHOT master-netty
  • Booooring...

Case #3: Fabric Master component test

Container master = ContainerBuilder.create().
  withName("master").withProfiles("master-netty").
  assertProvisioningResult().build();

InputStream inputStream =
  new URL("http://localhost:18081/").openStream();
String response = IOUtils.toString(inputStream);
assertEquals("master", response);
  • creating first Netty router container in the cluster
  • first router must be the master

Case #3: Fabric Master component test

Container slave = ContainerBuilder.create().
    withName("slave").withProfiles("master-netty").
    assertProvisioningResult().build();
  • no port conflicts? That's good - only the single master node is running.

Case #3: Fabric Master component test

Container master = ...;
...
master.destroy();

InputStream inputStream =
  new URL("http://localhost:18081/").openStream();
String response = IOUtils.toString(inputStream);
assertEquals("master", response);
  • we are still receiving correct response
  • slave has been nominated to the master

Examples

https://github.com/hekonsek/fuse-pocs/tree/master/fuse-pocs-fabric

Many thanks!