Skip to content
This repository was archived by the owner on Jan 4, 2022. It is now read-only.

Feature/switch to skip i ts #12

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
302b789
update maven cache step
adam-sandor Aug 12, 2021
e4fee9f
try to remove unnecessary-looking steps
adam-sandor Aug 12, 2021
fc9ec88
small improvements
adam-sandor Aug 12, 2021
b0025fc
add documentation and move into right package
adam-sandor Aug 12, 2021
192757f
remove unused imports
adam-sandor Aug 12, 2021
304ac2a
improving the code (untilAsserted)
adam-sandor Aug 12, 2021
4c203c9
small code improvements
adam-sandor Aug 18, 2021
bd4622b
testing KIND_REGISTRY on github action
Aug 20, 2021
9868d4b
correct cluster name
Aug 20, 2021
7917f89
correct cluster name
Aug 20, 2021
f0a7036
set
Aug 20, 2021
e0a71ea
set $KIND_REGISTRY
kanedafromparis Aug 20, 2021
886096d
set -Djib.allowInsecureRegistries=true
kanedafromparis Aug 20, 2021
cc4042b
renamed and clean actions
kanedafromparis Aug 20, 2021
7c39b22
add test for webapp deployment
adam-sandor Aug 20, 2021
706b60a
improve logging configuration for tests
adam-sandor Aug 20, 2021
c38821f
WebappController now waits until Tomcat is fully up and running befor…
adam-sandor Aug 21, 2021
b5ca270
IntegrationTest uses a curl Pod to verify if Webapp is correctly depl…
adam-sandor Aug 21, 2021
7793ee9
logging configuration
adam-sandor Aug 21, 2021
f671d7f
add documentation
adam-sandor Aug 21, 2021
a860b2c
upgrade to jdk v 1.9.2
adam-sandor Aug 21, 2021
6315926
put a cleanner break
kanedafromparis Aug 23, 2021
6f2e0fa
merge
kanedafromparis Aug 23, 2021
06a873a
clean ressource after test
kanedafromparis Aug 23, 2021
cceb2fa
increase time out
kanedafromparis Aug 23, 2021
c164a91
change webapps path
kanedafromparis Aug 24, 2021
5ee5527
reverte to default image (deploy change in the github action)
kanedafromparis Aug 24, 2021
716b962
improve feedback on webapps download
kanedafromparis Aug 24, 2021
d5b9048
add Waiting 5 seconds for Tomcat to unpack the downloaded war
kanedafromparis Aug 24, 2021
efedf39
testing full
kanedafromparis Aug 24, 2021
a020001
correct callimg the wrong namespace
kanedafromparis Aug 24, 2021
d49b13e
improving sleeping time for webapp deployment
kanedafromparis Aug 24, 2021
f3c8e77
set use of jib+kind for local run
kanedafromparis Aug 30, 2021
11d7e0e
put test that need a kubernetes cluster (kind) into failsafe:integrat…
kanedafromparis Aug 31, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 152 additions & 13 deletions .github/workflows/TomcatIntegrationTest.yml
Original file line number Diff line number Diff line change
@@ -6,35 +6,174 @@ on:
jobs:
tomcat_integration_test:
runs-on: ubuntu-latest
env:
KIND_CL_NAME: tomcat-integration
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Helm
uses: azure/setup-helm@v1
with:
version: v3.4.0

- uses: actions/setup-python@v2
with:
python-version: 3.7

- name: clean resident local docker
if: ${{ env.ACT }}
continue-on-error: true
run: |
for DIMG in "$KIND_CL_NAME-control-plane "; do
docker stop $DIMG ; docker rm $DIMG ;
done ;
sleep 1

- name: Create kind cluster
uses: helm/kind-action@v1.2.0
with:
cluster_name: ${{ env.KIND_CL_NAME }}

- name: Apply CRDs
run: kubectl apply -f tomcat/k8s/crd.yaml

- name: Set up Java and Maven
uses: actions/setup-java@v1
uses: actions/setup-java@v2
with:
java-version: 15
distribution: adopt-hotspot

- name: cache
uses: actions/cache@v2
if: ${{ !env.ACT }}
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-m2

- name: Set up Maven
uses: stCarolas/setup-maven@v4
if: ${{ env.ACT }}
with:
maven-version: 3.8.1

- name: Run unit tests
if: ${{ env.ACT }}
run: mvn --version

- name: Run unit tests
run: mvn -B failsafe:integration-test --file tomcat/pom.xml

tomcat_local_apply_setup_test:
runs-on: ubuntu-latest
env:
KIND_CL_NAME: tomcat-local-apply
steps:
- name: Checkout
uses: actions/checkout@v2

- name: clean resident local docker
if: ${{ env.ACT }}
continue-on-error: true
run: |
for DIMG in "$KIND_CL_NAME-control-plane "; do
docker stop $DIMG ; docker rm $DIMG ;
done ;
sleep 1

- name: Create Kubernetes KinD Cluster
uses: container-tools/kind-action@v1.5.0
with:
cluster_name: ${{ env.KIND_CL_NAME }}
registry: false

# for DIMG in "tomcat-local-apply-control-plane kind-registry tomcat_local_apply_setup_test "; do docker stop $DIMG ; docker rm $DIMG ; done ; sleep 1

- name: Set up Java and Maven
uses: actions/setup-java@v2
with:
# java-version: ${{ matrix.java }}
java-version: 15
- uses: actions/cache@v2
distribution: adopt-hotspot

- name: cache
uses: actions/cache@v2
if: ${{ !env.ACT }}
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Run unit tests
run: mvn -B test --file tomcat/pom.xml

- name: Set up Maven for local ACT
uses: stCarolas/setup-maven@v4
if: ${{ env.ACT }}
with:
maven-version: 3.8.1

- name: build jib
run: |
mvn -B package jib:dockerBuild jib:buildTar -Djib-maven-image=tomcat-operator --file tomcat/pom.xml -DskipTests
kind load image-archive tomcat/target/jib-image.tar --name=${{ env.KIND_CL_NAME }}

- name: Apply CRDs
run: kubectl apply -f tomcat/k8s/crd.yaml

- name: install tomcat operator
run: |
kubectl apply -f tomcat/k8s/operator.yaml

- name: create ns tomcatoperator-sample
run: kubectl create ns tomcatoperator-sample

- name: debug local kind
if: ${{ env.ACT }}
run: |
kubectl get pods -n tomcat-operator -l app=tomcat-operator -o yaml | tee -a debug.log

- name: wait for operators ready
run: |
LOOP=0 &&\
while [[ $(kubectl get pods -n tomcat-operator -l app=tomcat-operator -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') != "True" ]]; do \
echo "waiting for pod" &&\
kubectl logs -n tomcat-operator -l app=tomcat-operator &&\
(( LOOP++ )) &&\
if [[ $LOOP -gt 10 ]]; then exit 1; fi &&\
echo "loop number $LOOP" &&\
sleep 5; \
done

- name: install sample operators
run: |
for sample in $(ls tomcat/k8s/*sample*); do
kubectl -n tomcatoperator-sample apply -f $sample;
done

- name: check pod correctly started
run: |
LOOP=0 &&\
while [[ $(kubectl get pods -n tomcatoperator-sample -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') != "True True True True True True" ]]; do \
echo "waiting for pod" &&\
kubectl logs -n tomcat-operator -l app=tomcat-operator &&\
kubectl get pods -n tomcatoperator-sample &&\
(( LOOP++ )) &&\
if [[ $LOOP -gt 10 ]]; then exit 1; fi &&\
echo "loop number $LOOP" &&\
sleep 10; \
done
#Waiting 5 seconds for Tomcat to unpack the downloaded war
sleep 5;

- name: Get webapps
run: |
kubectl get tomcats,webapps -A -o yaml | tee -a debug
kubectl -n tomcatoperator-sample -c tomcat logs -l app=test-tomcat1 | grep startup

- name: check code
run: |
kubectl -n tomcatoperator-sample run sample1 --labels=app=curl --image=curlimages/curl:7.78.0 --restart=Never --timeout=30s --command -- curl -s -v http://test-tomcat1/mysample/;
kubectl -n tomcatoperator-sample run sample2 --labels=app=curl --image=curlimages/curl:7.78.0 --restart=Never --timeout=30s --command -- curl -s -v http://test-tomcat2/othercontext/;
LOOP=0 &&\
while [[ $(kubectl get pods -n tomcatoperator-sample -l app=curl -o 'jsonpath={..status.phase}') != "Succeeded Succeeded" ]]; do \
echo "waiting for pod" &&\
kubectl logs -n tomcatoperator-sample -l app=curl &&\
(( LOOP++ )) &&\
if [[ $LOOP -gt 5 ]]; then exit 1; fi &&\
echo "loop number $LOOP" &&\
sleep 5; \
done
if [[ $(kubectl logs -n tomcatoperator-sample sample1 --tail=500 | grep tomcat.gif | wc -l) -ne 1 ]]; then exit 1; fi
if [[ $(kubectl logs -n tomcatoperator-sample sample2 --tail=500 | grep dog.jpeg | wc -l) -ne 1 ]]; then exit 1; fi
4 changes: 4 additions & 0 deletions tomcat/k8s/crd.yaml
Original file line number Diff line number Diff line change
@@ -80,6 +80,10 @@ spec:
properties:
deployedArtifact:
type: string
deploymentStatus:
type: array
items:
type: string
required: [spec]
# either Namespaced or Cluster
scope: Namespaced
19 changes: 19 additions & 0 deletions tomcat/k8s/operator.yaml
Original file line number Diff line number Diff line change
@@ -65,8 +65,27 @@ metadata:
rules:
- apiGroups:
- ""
- "extensions"
- "apps"
resources:
- deployments
- services
- pods
- pods/exec
verbs:
- '*'
- apiGroups:
- "apiextensions.k8s.io"
resources:
- customresourcedefinitions
verbs:
- '*'
- apiGroups:
- "tomcatoperator.io"
resources:
- tomcats
- tomcats/status
- webapps
- webapps/status
verbs:
- '*'
2 changes: 1 addition & 1 deletion tomcat/k8s/webapp-sample2.yaml
Original file line number Diff line number Diff line change
@@ -5,4 +5,4 @@ metadata:
spec:
tomcat: test-tomcat2
url: charlottemach.com/assets/jax.war
contextPath: mysample
contextPath: othercontext
15 changes: 13 additions & 2 deletions tomcat/pom.xml
Original file line number Diff line number Diff line change
@@ -19,14 +19,15 @@
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<jib-maven-plugin.version>2.7.1</jib-maven-plugin.version>
<jib-maven-plugin.version>3.1.4</jib-maven-plugin.version>

</properties>

<dependencies>
<dependency>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>operator-framework</artifactId>
<version>1.8.4</version>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
@@ -72,6 +73,16 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
</plugins>
</build>

Original file line number Diff line number Diff line change
@@ -2,9 +2,9 @@

import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.client.Watcher;
import io.javaoperatorsdk.operator.processing.event.AbstractEvent;
import io.javaoperatorsdk.operator.processing.event.DefaultEvent;

public class DeploymentEvent extends AbstractEvent {
public class DeploymentEvent extends DefaultEvent {

private final Watcher.Action action;
private final Deployment deployment;
Original file line number Diff line number Diff line change
@@ -11,6 +11,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Used by the TomcatController to watch changes on Deployment objects. As the Pods of the Deployment start up
* the TomcatController updates the status.readyReplicas field.
*/
public class DeploymentEventSource extends AbstractEventSource implements Watcher<Deployment> {
private static final Logger log = LoggerFactory.getLogger(DeploymentEventSource.class);

Original file line number Diff line number Diff line change
@@ -19,6 +19,10 @@
import java.util.Objects;
import java.util.Optional;

/**
* Runs a specified number of Tomcat app server Pods. It uses a Deployment to create the Pods. Also creates a
* Service over which the Pods can be accessed.
*/
@Controller
public class TomcatController implements ResourceController<Tomcat> {

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.javaoperatorsdk.operator.sample;

import io.fabric8.kubernetes.client.Watcher;
import io.javaoperatorsdk.operator.processing.event.DefaultEvent;

public class TomcatEvent extends DefaultEvent {

private final Watcher.Action action;
private final Tomcat tomcat;

public TomcatEvent(
Watcher.Action action, Tomcat resource, TomcatEventSource tomcatEventSource, String webappUid) {
super(webappUid, tomcatEventSource);
this.action = action;
this.tomcat = resource;
}

public Watcher.Action getAction() {
return action;
}

public String resourceUid() {
return getTomcat().getMetadata().getUid();
}

@Override
public String toString() {
return "CustomResourceEvent{"
+ "action="
+ action
+ ", resource=[ name="
+ getTomcat().getMetadata().getName()
+ ", kind="
+ getTomcat().getKind()
+ ", apiVersion="
+ getTomcat().getApiVersion()
+ " ,resourceVersion="
+ getTomcat().getMetadata().getResourceVersion()
+ ", markedForDeletion: "
+ (getTomcat().getMetadata().getDeletionTimestamp() != null
&& !getTomcat().getMetadata().getDeletionTimestamp().isEmpty())
+ " ]"
+ '}';
}

public Tomcat getTomcat() {
return tomcat;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package io.javaoperatorsdk.operator.sample;

import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.Watcher;
import io.fabric8.kubernetes.client.WatcherException;
import io.javaoperatorsdk.operator.processing.event.AbstractEventSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Optional;

import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getUID;
import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getVersion;

/**
* Used by the WebappController to watch changes on Tomcat objects
*/
public class TomcatEventSource extends AbstractEventSource implements Watcher<Tomcat> {
private static final Logger log = LoggerFactory.getLogger(TomcatEventSource.class);

private final KubernetesClient client;

public static TomcatEventSource createAndRegisterWatch(KubernetesClient client) {
TomcatEventSource tomcatEventSource = new TomcatEventSource(client);
tomcatEventSource.registerWatch();
return tomcatEventSource;
}

private TomcatEventSource(KubernetesClient client) {
this.client = client;
}

private void registerWatch() {
var tomcatClient = client.customResources(Tomcat.class);
tomcatClient.inAnyNamespace().watch(this);
}

@Override
public void eventReceived(Action action, Tomcat tomcat) {
log.info("Event received for action: {}, Tomcat: {}", action.name(), tomcat.getMetadata().getName());

if (action == Action.ERROR) {
log.warn(
"Skipping {} event for custom resource uid: {}, version: {}",
action,
getUID(tomcat),
getVersion(tomcat));
return;
}

var webappClient = client.customResources(Webapp.class);
Optional<Webapp> webapp = webappClient.inNamespace(tomcat.getMetadata().getNamespace())
.list().getItems().stream()
.filter(wapp -> wapp.getSpec().getTomcat().equals(tomcat.getMetadata().getName()))
.findFirst();

if (webapp.isPresent()) {
eventHandler.handleEvent(new TomcatEvent(action, tomcat, this,
webapp.get().getMetadata().getUid()));
} else {
log.debug("Webapp not found for Tomcat {}", tomcat.getMetadata().getName());
}
}

@Override
public void onClose(WatcherException e) {
if (e == null) {
return;
}
if (e.isHttpGone()) {
log.warn("Received error for watch, will try to reconnect.", e);
registerWatch();
} else {
// Note that this should not happen normally, since fabric8 client handles reconnect.
// In case it tries to reconnect this method is not called.
log.error("Unexpected error happened with watch. Will exit.", e);
System.exit(1);
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -5,6 +5,9 @@
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Version;

/**
* Represents a web application deployed in a Tomcat deployment
*/
@Group("tomcatoperator.io")
@Version("v1")
public class Webapp extends CustomResource<WebappSpec, WebappStatus> implements Namespaced {}
Original file line number Diff line number Diff line change
@@ -3,13 +3,23 @@
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.ExecListener;
import io.fabric8.kubernetes.client.dsl.ExecWatch;
import io.javaoperatorsdk.operator.api.*;
import io.javaoperatorsdk.operator.processing.event.EventSourceManager;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Controller
public class WebappController implements ResourceController<Webapp> {
@@ -22,30 +32,65 @@ public WebappController(KubernetesClient kubernetesClient) {
this.kubernetesClient = kubernetesClient;
}

@Override
public void init(EventSourceManager eventSourceManager) {
TomcatEventSource tomcatEventSource = TomcatEventSource.createAndRegisterWatch(kubernetesClient);
eventSourceManager.registerEventSource("tomcat-event-source", tomcatEventSource);
}

/**
* This method will be called not only on changes to Webapp objects but also when Tomcat objects change.
*/
@Override
public UpdateControl<Webapp> createOrUpdateResource(Webapp webapp, Context<Webapp> context) {
if (webapp.getStatus() != null && Objects.equals(webapp.getSpec().getUrl(), webapp.getStatus().getDeployedArtifact())) {
return UpdateControl.noUpdate();
}

String[] command = new String[] {"wget", "-O", "/data/" + webapp.getSpec().getContextPath() + ".war", webapp.getSpec().getUrl()};
var tomcatClient = kubernetesClient.customResources(Tomcat.class);
Tomcat tomcat = tomcatClient.inNamespace(webapp.getMetadata().getNamespace()).withName(webapp.getSpec().getTomcat()).get();
if (tomcat == null) {
throw new IllegalStateException("Cannot find Tomcat " + webapp.getSpec().getTomcat() + " for Webapp " + webapp.getMetadata().getName() + " in namespace " + webapp.getMetadata().getNamespace());
}

executeCommandInAllPods(kubernetesClient, webapp, command);
if (tomcat.getStatus() != null && Objects.equals(tomcat.getSpec().getReplicas(), tomcat.getStatus().getReadyReplicas())) {
log.info("Tomcat is ready and webapps not yet deployed. Commencing deployment of {} in Tomcat {}", webapp.getMetadata().getName(), tomcat.getMetadata().getName());
String[] command = new String[]{"wget", "-O", "/data/" + webapp.getSpec().getContextPath() + ".war", webapp.getSpec().getUrl()};
if(log.isInfoEnabled()){
command = new String[]{"time", "wget", "-O", "/data/" + webapp.getSpec().getContextPath() + ".war", webapp.getSpec().getUrl()};
}

String[] commandStatusInAllPods = executeCommandInAllPods(kubernetesClient, webapp, command);

//webapp.getStatus().setDeployedArtifact(webapp.getSpec().getUrl());
return UpdateControl.updateStatusSubResource(webapp);
if (webapp.getStatus() == null) {
webapp.setStatus(new WebappStatus());
}
webapp.getStatus().setDeployedArtifact(webapp.getSpec().getUrl());
webapp.getStatus().setDeploymentStatus(commandStatusInAllPods);
return UpdateControl.updateStatusSubResource(webapp);
} else {
log.info("WebappController invoked but Tomcat not ready yet ({}/{})",
tomcat.getStatus() != null ? tomcat.getStatus().getReadyReplicas() : 0, tomcat.getSpec().getReplicas());
return UpdateControl.noUpdate();
}
}

@Override
public DeleteControl deleteResource(Webapp webapp, Context<Webapp> context) {

String[] command = new String[] {"rm", "/data/" + webapp.getSpec().getContextPath() + ".war"};
executeCommandInAllPods(kubernetesClient, webapp, command);
String[] commandStatusInAllPods = executeCommandInAllPods(kubernetesClient, webapp, command);
if (webapp.getStatus() != null) {
webapp.getStatus().setDeployedArtifact(null);
webapp.getStatus().setDeploymentStatus(commandStatusInAllPods);
}
return DeleteControl.DEFAULT_DELETE;
}

private void executeCommandInAllPods(
private String[] executeCommandInAllPods(
KubernetesClient kubernetesClient, Webapp webapp, String[] command) {
String[] status = new String[0];

Deployment deployment =
kubernetesClient
.apps()
@@ -62,20 +107,67 @@ private void executeCommandInAllPods(
.withLabels(deployment.getSpec().getSelector().getMatchLabels())
.list()
.getItems();
for (Pod pod : pods) {
status = new String[pods.size()];
for (int i=0; i<pods.size();i++) {
Pod pod = pods.get(i);
log.info(
"Executing command {} in Pod {}",
String.join(" ", command),
pod.getMetadata().getName());
kubernetesClient
.pods()
.inNamespace(deployment.getMetadata().getNamespace())

CompletableFuture<String> data = new CompletableFuture<>();
try (ExecWatch execWatch = execCmd(pod, data, command)) {
status[i] = ""+pod.getMetadata().getName()+":"+data.get(30, TimeUnit.SECONDS);;
} catch (ExecutionException e) {
status[i] = ""+pod.getMetadata().getName()+": ExecutionException - "+e.getMessage();
} catch (InterruptedException e) {
status[i] = ""+pod.getMetadata().getName()+": InterruptedException - "+e.getMessage();
} catch (TimeoutException e) {
status[i] = ""+pod.getMetadata().getName()+": TimeoutException - "+e.getMessage();
}
}
}
return status;
}

private ExecWatch execCmd(Pod pod, CompletableFuture<String> data, String... command) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
return kubernetesClient.pods()
.inNamespace(pod.getMetadata().getNamespace())
.withName(pod.getMetadata().getName())
.inContainer("war-downloader")
.writingOutput(new ByteArrayOutputStream())
.writingError(new ByteArrayOutputStream())
.writingOutput(baos)
.writingError(baos)
.usingListener(new SimpleListener(data, baos))
.exec(command);
}
}

static class SimpleListener implements ExecListener {

private CompletableFuture<String> data;
private ByteArrayOutputStream baos;
private final Logger log = LoggerFactory.getLogger(getClass());
public SimpleListener(CompletableFuture<String> data, ByteArrayOutputStream baos) {
this.data = data;
this.baos = baos;
}

@Override
public void onOpen(Response response) {
log.debug("Reading data... " + response.message());
}

@Override
public void onFailure(Throwable t, Response response) {
log.debug(t.getMessage() + " " + response.message());
data.completeExceptionally(t);
}

@Override
public void onClose(int code, String reason) {
log.debug("Exit with: " + code + " and with reason: " + reason);
data.complete(baos.toString());
}
}

}
Original file line number Diff line number Diff line change
@@ -11,4 +11,14 @@ public String getDeployedArtifact() {
public void setDeployedArtifact(String deployedArtifact) {
this.deployedArtifact = deployedArtifact;
}

private String[] deploymentStatus;

public String[] getDeploymentStatus() {
return deploymentStatus;
}

public void setDeploymentStatus(String[] deploymentStatus) {
this.deploymentStatus = deploymentStatus;
}
}
2 changes: 1 addition & 1 deletion tomcat/src/main/resources/log4j2.xml
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<!-- <PatternLayout pattern="%style{%d}{yellow} %style{%-30c{1.}}{cyan} %highlight{[%-5level] %msg%n%throwable}{INFO=black}"/>-->
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %l{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.javaoperatorsdk.operator.sample;

import org.junit.Assert;
import org.junit.Test;
// this is for reference
// on target regular test should be set with io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
public class SimpleTest {
@Test
public void test() {
Assert.assertSame("foo","foo");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.javaoperatorsdk.operator.sample;

import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.*;
import io.fabric8.kubernetes.client.extended.run.RunConfigBuilder;
import io.javaoperatorsdk.operator.Operator;
import io.javaoperatorsdk.operator.config.runtime.DefaultConfigurationService;
import io.javaoperatorsdk.operator.test.IntegrationTest;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.util.concurrent.TimeUnit.MINUTES;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.notNullValue;

@Category(IntegrationTest.class)
public class SimpleWarIT {

final static String TEST_NS = "tomcat-test";

final static Logger log = LoggerFactory.getLogger(SimpleWarIT.class);

@Test
public void test() {
Config config = new ConfigBuilder().withNamespace(null).build();
KubernetesClient client = new DefaultKubernetesClient(config);

Operator operator = new Operator(client, DefaultConfigurationService.instance());
operator.register(new TomcatController(client));
operator.register(new WebappController(client));

Tomcat tomcat = new Tomcat();
tomcat.setMetadata(new ObjectMetaBuilder()
.withName("test-tomcat1")
.withNamespace(TEST_NS)
.build());
tomcat.setSpec(new TomcatSpec());
tomcat.getSpec().setReplicas(3);
tomcat.getSpec().setVersion(9);

Webapp webapp1 = new Webapp();
webapp1.setMetadata(new ObjectMetaBuilder()
.withName("test-webapp1")
.withNamespace(TEST_NS)
.build());
webapp1.setSpec(new WebappSpec());
webapp1.getSpec().setContextPath("webapp1");
webapp1.getSpec().setTomcat(tomcat.getMetadata().getName());
webapp1.getSpec().setUrl("http://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war");

var tomcatClient = client.customResources(Tomcat.class);
var webappClient = client.customResources(Webapp.class);

Namespace testNs = new NamespaceBuilder().withMetadata(
new ObjectMetaBuilder().withName(TEST_NS).build()).build();

if (testNs != null && client.namespaces().withName(TEST_NS).isReady() == true ) {
// We perform a pre-run cleanup instead of a post-run cleanup. This is to help with debugging test results
// when running against a persistent cluster. The test namespace would stay after the test run so we can
// check what's there, but it would be cleaned up during the next test run.
log.info("Cleanup: deleting test namespace {}", TEST_NS);
client.namespaces().delete(testNs);
await().atMost(5, MINUTES).until(() -> client.namespaces().withName("tomcat-test").get() == null);
}

log.info("Creating test namespace {}", TEST_NS);
client.namespaces().create(testNs);

log.info("Creating test resources");
tomcatClient.inNamespace(TEST_NS).create(tomcat);
webappClient.inNamespace(TEST_NS).create(webapp1);

log.info("Waiting 2 minutes for Tomcat and Webapp CR statuses to be updated");
await().atMost(2, MINUTES).untilAsserted(() -> {
Tomcat updatedTomcat = tomcatClient.inNamespace(TEST_NS).withName(tomcat.getMetadata().getName()).get();
Webapp updatedWebapp = webappClient.inNamespace(TEST_NS).withName(webapp1.getMetadata().getName()).get();
assertThat(updatedTomcat.getStatus(), is(notNullValue()));
assertThat(updatedTomcat.getStatus().getReadyReplicas(), equalTo(3));
assertThat(updatedWebapp.getStatus(), is(notNullValue()));
assertThat(updatedWebapp.getStatus().getDeployedArtifact(), is(notNullValue()));
});

log.info("Waiting 60 seconds for Tomcat to unpack the downloaded war");
// this delays is du to allows the tomcat to unpack
// kubectl -n tomcat-test -c war-downloader logs -l app=test-tomcat1
// Deployment of web application archive [/usr/local/tomcat/webapps/webapp1.war] has finished in [xxx] ms
try {
Thread.sleep(60*1000);
} catch (InterruptedException e) {
log.warn(e.getMessage(),e);
}

String url = "http://" + tomcat.getMetadata().getName() + "/" + webapp1.getSpec().getContextPath() + "/";
log.info("Starting curl Pod and waiting 2 minutes for GET of {} to return 200", url);
Pod curlPod = client.run().inNamespace(TEST_NS)
.withRunConfig(new RunConfigBuilder()
.withArgs("-s", "-o", "/dev/null", "-w", "%{http_code}", url)
.withName("curl")
.withImage("curlimages/curl:7.78.0")
.withRestartPolicy("Never")
.build()).done();
await().atMost(2, MINUTES).untilAsserted(() -> {
try {
//let's do som tries
String curlOutput = client.pods().inNamespace(TEST_NS).withName(curlPod.getMetadata().getName()).getLog();
assertThat(curlOutput, equalTo("200"));
} catch (KubernetesClientException ex) {
throw new AssertionError(ex);
}
});
}

}
71 changes: 0 additions & 71 deletions tomcat/src/test/java/sample/IntegrationTest.java

This file was deleted.

13 changes: 13 additions & 0 deletions tomcat/src/test/resources/log4j2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %l{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
22 changes: 22 additions & 0 deletions webserver/k8s/operator.yaml
Original file line number Diff line number Diff line change
@@ -72,5 +72,27 @@ rules:
- deployments
- services
- configmaps
- pods
verbs:
- '*'
- apiGroups:
- "apps"
resources:
- deployments
- services
- configmaps
verbs:
- '*'
- apiGroups:
- "apiextensions.k8s.io"
resources:
- customresourcedefinitions
verbs:
- '*'
- apiGroups:
- "sample.javaoperatorsdk"
resources:
- webservers
- webservers/status
verbs:
- '*'