Dark Launcher and Automatic Tests
Before StartYou should have NO virtualservice nor destinationrule (in tutorial namespace) kubectl get virtualservice kubectl get destinationrule if so run:
|
Preparation
We are assuming that you have recommendation v1 and v2 deployed on tutorial namespace. |
The first thing you need to do is to apply Istio resources to redirect all traffic to recommendation v1.
From the main istio-tutorial directory,
istioctl create -f istiofiles/destination-rule-recommendation-v1-v2.yml -n tutorial
istioctl create -f istiofiles/virtual-service-recommendation-v1.yml -n tutorial
curl customer-tutorial.$(minishift ip).nip.io
customer => preference => recommendation v1 from '2819441432-qsp25': 3
Now you see that your production traffic always goes to recommendation v1.
Explanation
Dark Launching is a process where software is selectively or stealthily released to your users to validate that this new version behaves correctly and into expected parameters.
There are many techniques under Dark Launch technique, you’ve seen one in this tutorial ROOT:5advanced-routerules.adoc#mirroringtraffic, in this case, we are going to see how you can make that your tests (even BDD tests) can be used to test your new version without affecting your current users.
You might need to use this technique if, for example, you don’t want to start stressing your new service with public traffic at first (like happens in Mirroring Traffic technique at first.
To write these tests you can use Arquillian Cube.
Arquillian Cube allows you to control Docker, Kubernetes and OpenShift containers in your tests with ease.As a side effect, it also supports Istio, so you can write tests that apply some Istio rules to configured cluster, runs the test, and finally restores the state of Istio.
For this use case what you want is to execute an automatic test against recommendation v2 meanwhile public traffic still goes to recommendation v1.For this concrete example and for sake of simplicity, we set that if user-agent
header contains DarkLaunch string then traffic is redirected to v2.
Recommendation project comes with a test that it is ready to be executed, but in the next section, you’ll read how we have arrived at the given solution.
Code
The first things you need to do is adding Arquillian Cube
dependency in recommendation project.
recommendation/java/vertx/pom.xml
<dependency>
<groupId>org.arquillian.cube</groupId>
<artifactId>arquillian-cube-openshift-starter</artifactId> (1)
<version>${cube.version}</version> (2)
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.arquillian.cube</groupId>
<artifactId>arquillian-cube-istio-kubernetes</artifactId> (3)
<version>${cube.version}</version>
<scope>test</scope>
</dependency>
1 | Dependency for OpenShift. There is also the same for Kubernetes. |
2 | Version 1.18.2 or beyond. |
3 | Dependency for Istio integration. |
Then you need to configure the namespace of the project.For this purpose, you need to create an arquillian.xml
file.
recommendation/java/vertx/src/test/resources/arquillian.xml
<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://jboss.org/schema/arquillian"
xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<extension qualifier="openshift"> (1)
<property name="namespace.use.existing">tutorial</property> (2)
<property name="env.init.enabled">false</property> (3)
</extension>
</arquillian>
1 | OpenShift cluster. |
2 | Use existing namespace named tutorial . |
3 | Do not initialize anything, application is already running. |
INFO: Arquillian Cube can also create a namespace and deploy any Kubernetes/OpenShift resource automatically. But since the application has been already deployed in Deploy Microservices, this step is skipped.
And finally, you need to create an automatic test that runs against v2.
In this kind of tests you only want to validate happy path, just a quality gate that allows you to validate that everything is in place.
recommendation/java/vertx/src/test/java/com/…/recommendation/DarkLaunchIT.java
@RunWith(Arquillian.class) (1)
@IstioResource("classpath:dark-launch-redirect-traffic-to-new-version.yml") (2)
@RestoreIstioResource("classpath:virtual-service-recommendation-v1.yml") (3)
public class DarkLaunchIT {
@RouteURL("customer")
@AwaitRoute
private URL url; (4)
@ArquillianResource
IstioAssistant istioAssistant; (5)
@Before
public void waitUntilIstioResourcesArePopulated() { (6)
istioAssistant.await(createRequestForRecommendationV2(), response -> {
try {
return response.body().string().contains("v2");
} catch (IOException e) {
return false;
}
});
}
@Test
public void should_return_accessing_v2_message() throws IOException { (7)
// Given
final Request request = createRequestForRecommendationV2(); (8)
// When
final String content = makeRequest(request);
// Then
assertThat(content)
.startsWith("customer => preference => recommendation v2 from"); (9)
}
private String makeRequest(Request request) throws IOException {
final HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(interceptor)
.build();
try(Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
private Request createRequestForRecommendationV2() {
return new Request.Builder()
.url(url.toString())
.addHeader("User-Agent", "Recommendation-v2-DarkLaunch-Test")
.build();
}
}
1 | Arquillian JUnit runner. |
2 | Resource to apply before executing the test. |
3 | Resource to apply after executing the test. |
4 | Public URL of application. |
5 | IstioAssistant instance to interact with Istio ecosystem. |
6 | Waits until Istio resources are populated acorss the cluster. |
7 | Test to validate that recommendation v2 works as expected. |
8 | Creates a request setting user-agent as DarkLaunch. |
9 | Validates the response. |
You’ve seen a simple example here, but you can use other strategies to get the same effect:-Instead of using user-agent you can create a custom header, or just use the request IP range.-@IstioResource and @RestoreIstioResource supports expressions like ${prop} resolving the property from environment variables or system properties.-Use failsafe plugin to run this kind of tests so they are not run all the time.-This example works with OpenShift route, but you can use @PortForwarding annotation in the test to do a port forwarding to given service.-It is so important to add a guard to wait until Istio resources are populated across the cluster. In case of minishift/kube it is almost instantly but in case of real clusters, it might take some tenths of seconds. |
Test project can be found here.
Clean Up
istioctl delete -f istiofiles/virtual-service-recommendation-v1.yml -n tutorial
istioctl delete -f istiofiles/destination-rule-recommendation-v1-v2.yml -n tutorial
or you can run:
./scripts/clean.sh