Skip to content

Add tomcat operator sample #659

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 55 commits into from
Nov 12, 2021
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
231608d
initial commit for moving tomcat sample back to main repo
adam-sandor Oct 21, 2021
7b5616f
improve build pipeline based on PR feedback
adam-sandor Oct 21, 2021
b64ea77
fix bug in pipeline
adam-sandor Oct 21, 2021
ab3d818
fix bug in pipeline
adam-sandor Oct 21, 2021
5be04f9
add groupid
adam-sandor Oct 21, 2021
a5e569c
set sdk version to 1.9.8
adam-sandor Oct 21, 2021
d98be67
remove unnecessary namespace creation, list namespaces during the dump
adam-sandor Oct 21, 2021
3ae2de6
more diagnostic dumping
adam-sandor Oct 21, 2021
cce5e49
reorganize dump and prevent stop on error
adam-sandor Oct 21, 2021
6f3ecb2
fix bug in printing tomcat-operator output
adam-sandor Oct 21, 2021
6b78630
use framework version 1.9.2
adam-sandor Oct 22, 2021
7fdfefa
Reformat TomcatOperator & add to pom structure
adam-sandor Oct 28, 2021
6d21f6a
Build SDK before running Tomcat JIB build
adam-sandor Oct 28, 2021
0c2613e
Rename E2E test
adam-sandor Oct 28, 2021
83e61ce
Rename E2E test
adam-sandor Oct 28, 2021
cfbb66c
Rename E2E test
adam-sandor Oct 28, 2021
4e7ade8
also print status of tomcat and webapp objects
adam-sandor Oct 28, 2021
3c96fb7
add start method
adam-sandor Oct 28, 2021
c8316d2
chore(deps): bump spring-boot.version from 2.5.5 to 2.5.6 (#617)
dependabot[bot] Oct 22, 2021
db4b4d6
chore(deps): bump awaitility from 4.1.0 to 4.1.1
dependabot[bot] Oct 26, 2021
dbc83c8
fix: prevent double registration of same CR with different controllers
metacosm Oct 26, 2021
1be67dd
Set new SNAPSHOT version into pom files.
actions-user Oct 27, 2021
101db14
fix: restart event handler (#632)
metacosm Oct 28, 2021
0bb296e
Set new SNAPSHOT version into pom files.
actions-user Oct 28, 2021
9a5aa5b
update to framework version 1.9.11-SNAPSHOT
adam-sandor Oct 28, 2021
464f892
add start call to operator
adam-sandor Oct 28, 2021
2e5e60e
fix dependency version of operator-framework
adam-sandor Oct 28, 2021
d118ab6
removing ready check from namespce as it's causing problems
adam-sandor Oct 28, 2021
579a65e
enable maven cache
adam-sandor Oct 29, 2021
ff98e0c
improve logging
adam-sandor Oct 29, 2021
f2e481e
add nicers tostring methods
adam-sandor Oct 29, 2021
e3bbc75
rename integration test to e2e test
adam-sandor Nov 4, 2021
8202787
better handling of curl pod at the end of the test
adam-sandor Nov 4, 2021
6585101
remove integration test references
adam-sandor Nov 4, 2021
c0004c5
docs: webpage docs skeleton (#631)
csviri Oct 29, 2021
13e1f91
fix: replace jandex plugin by Quarkus detection of beans.xml files
metacosm Oct 28, 2021
53a6178
fix: improve duplicated controller detection, add tests
metacosm Oct 28, 2021
6ed14d5
Set new SNAPSHOT version into pom files.
actions-user Oct 29, 2021
944e08a
chore(deps): bump formatter-maven-plugin from 2.16.0 to 2.17.0 (#644)
dependabot[bot] Nov 1, 2021
28e40b0
docs: link for basic operato articles (#643)
csviri Nov 1, 2021
c34aa65
fix: use `close` as destroyMethod of Bean (#645)
KimDoubleB Nov 2, 2021
a97c3dd
docs: finalizers and best braticies page (#647)
csviri Nov 3, 2021
9097659
fix: typo in docs (#649)
csviri Nov 3, 2021
dc2a543
fix: jekyll build warning with highliting (#650)
csviri Nov 3, 2021
7af9900
chore(deps): bump auto-service from 1.0 to 1.0.1 (#652)
dependabot[bot] Nov 4, 2021
7f97b44
remove integration test references
adam-sandor Nov 5, 2021
95b4985
Merge branch 'master' into add_tomcat_operator_sample
csviri Nov 5, 2021
715126b
fix: parent version update from master
csviri Nov 5, 2021
2115de4
fix: migrated tomcat operator to informers, some minor changes to mak…
csviri Nov 8, 2021
9e13536
refactor: rename samples to smoke test samples
csviri Nov 8, 2021
7eb3aa7
feature: log warning when passing a running event source
csviri Nov 8, 2021
289841f
docs: tomcat sample DeploymentEventSource to InformerEventSource in docs
csviri Nov 11, 2021
f23a78d
docs: explanation for cache usage
csviri Nov 11, 2021
116134f
chore(docs): update to point to `sample-operators` director
metacosm Nov 12, 2021
114062f
refactor: generate CRDs automatically and use them
metacosm Nov 12, 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
79 changes: 79 additions & 0 deletions .github/workflows/end-to-end-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# End to end integration test which deploys the Tomcat operator to a Kubernetes
# (Kind) cluster and creates custom resources to verify the operator's functionality
name: TomcatOperator End to End test
on:
push:
branches:
- "*"
jobs:
tomcat_e2e_test:
runs-on: ubuntu-latest
env:
KIND_CL_NAME: e2e-test
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/[email protected]
with:
cluster_name: e2e-test
registry: false

- name: Apply CRDs
run: kubectl apply -f k8s/crd.yaml
working-directory: sample-operators/tomcat-operator

- name: Set up Java and Maven
uses: actions/setup-java@v2
with:
java-version: 11
distribution: adopt-hotspot
cache: 'maven'

- name: Build SDK
run: mvn install -DskipTests

- name: build jib
working-directory: sample-operators/tomcat-operator
run: |
mvn --version
mvn -B package jib:dockerBuild jib:buildTar -Djib-maven-image=tomcat-operator -DskipTests
kind load image-archive target/jib-image.tar --name=${{ env.KIND_CL_NAME }}

- name: Apply CRDs
working-directory: sample-operators/tomcat-operator
run: kubectl apply -f k8s/crd.yaml

- name: Deploy Tomcat Operator
working-directory: sample-operators/tomcat-operator
run: |
kubectl apply -f k8s/operator.yaml

- name: Run E2E Tests
working-directory: sample-operators/tomcat-operator
run: mvn -B test -P end-to-end-tests

- name: Dump state
if: ${{ failure() }}
run: |
set +e
echo "All namespaces"
kubectl get ns
echo "All objects in tomcat-operator"
kubectl get all -n tomcat-operator -o yaml
echo "Output of tomcat-operator pod"
kubectl logs -l app=tomcat-operator -n tomcat-operator
echo "All objects in tomcat-test"
kubectl get deployment,pod,tomcat,webapp -n tomcat-test -o yaml
echo "Output of curl command"
kubectl logs curl -n tomcat-test
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public Optional<T> getLatestResource(String uuid) {
return Optional.ofNullable(resources.get(uuid)).map(this::clone);
}

public List<T> getLatestResources(Predicate<CustomResource> selector) {
public List<T> getLatestResources(Predicate<T> selector) {
try {
lock.lock();
return resources.values().stream()
Expand All @@ -88,7 +88,7 @@ public List<T> getLatestResources(Predicate<CustomResource> selector) {
}
}

public Set<String> getLatestResourcesUids(Predicate<CustomResource> selector) {
public Set<String> getLatestResourcesUids(Predicate<T> selector) {
try {
lock.lock();
return resources.values().stream()
Expand All @@ -113,4 +113,5 @@ private T clone(CustomResource customResource) {
public T cleanup(String customResourceUid) {
return resources.remove(customResourceUid);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
public class DefaultEventSourceManager<R extends CustomResource<?, ?>>
implements EventSourceManager {

public static final String CUSTOM_RESOURCE_EVENT_SOURCE_NAME = "custom-resource-event-source";
public static final String RETRY_TIMER_EVENT_SOURCE_NAME = "retry-timer-event-source";
private static final String CUSTOM_RESOURCE_EVENT_SOURCE_NAME = "custom-resource-event-source";
private static final Logger log = LoggerFactory.getLogger(DefaultEventSourceManager.class);

private final ReentrantLock lock = new ReentrantLock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ public void onClose(WatcherException e) {
}
}

// todo: remove
public CustomResourceCache<T> getCache() {
return customResourceCache;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import java.util.Set;
import java.util.function.Function;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
Expand All @@ -15,6 +18,8 @@

public class InformerEventSource<T extends HasMetadata> extends AbstractEventSource {

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

private final SharedInformer<T> sharedInformer;
private final Function<T, Set<String>> resourceToUIDs;
private final Function<HasMetadata, T> associatedWith;
Expand All @@ -41,6 +46,11 @@ public InformerEventSource(SharedInformer<T> sharedInformer,
Function<T, Set<String>> resourceToUIDs,
Function<HasMetadata, T> associatedWith,
boolean skipUpdateEventPropagationIfNoChange) {
if (sharedInformer.isRunning()) {
log.warn(
"Informer is already running on event source creation, this is not desirable and may " +
"lead to non deterministic behavior.");
}
this.sharedInformer = sharedInformer;
this.resourceToUIDs = resourceToUIDs;
this.skipUpdateEventPropagationIfNoChange = skipUpdateEventPropagationIfNoChange;
Expand Down Expand Up @@ -81,7 +91,10 @@ private void propagateEvent(InformerEvent.Action action, T object, T oldObject)
}
uids.forEach(uid -> {
InformerEvent event = new InformerEvent(uid, this, action, object, oldObject);
this.eventHandler.handleEvent(event);
// In case user passes an already running informer this would fail.
if (this.eventHandler != null) {
this.eventHandler.handleEvent(event);
}
});
}

Expand Down
29 changes: 27 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@
<module>operator-framework-core</module>
<module>operator-framework-junit5</module>
<module>operator-framework</module>
<module>samples</module>
<module>micrometer-support</module>
<module>smoke-test-samples</module>
<module>micrometer-support</module>
<module>sample-operators</module>
</modules>


Expand Down Expand Up @@ -272,6 +273,7 @@
</includes>
<excludes>
<exclude>**/*IT.java</exclude>
<exclude>**/*E2E.java</exclude>
</excludes>
</configuration>
</plugin>
Expand Down Expand Up @@ -316,6 +318,7 @@
<includes>
<include>**/*Test.java</include>
<include>**/*IT.java</include>
<include>**/*E2E.java</include>
</includes>
</configuration>
</plugin>
Expand All @@ -335,6 +338,27 @@
</includes>
<excludes>
<exclude>**/*Test.java</exclude>
<exclude>**/*E2E.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>end-to-end-tests</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*E2E.java</include>
</includes>
<excludes>
<exclude>**/*Test.java</exclude>
<exclude>**/*IT.java</exclude>
</excludes>
</configuration>
</plugin>
Expand All @@ -351,6 +375,7 @@
<configuration>
<excludes>
<exclude>**/*IT.java</exclude>
<exclude>**/*E2E.java</exclude>
</excludes>
</configuration>
</plugin>
Expand Down
78 changes: 78 additions & 0 deletions sample-operators/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>java-operator-sdk</artifactId>
<version>1.9.12-SNAPSHOT</version>
</parent>

<artifactId>sample-operators</artifactId>
<name>Operator SDK - Samples</name>
<packaging>pom</packaging>

<properties>
<jib-maven-plugin.version>3.1.4</jib-maven-plugin.version>
</properties>

<modules>
<module>tomcat-operator</module>
</modules>

<dependencies>
<dependency>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>operator-framework</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>org.takes</groupId>
<artifactId>takes</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.1.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>${jib-maven-plugin.version}</version>
<configuration>
<from>
<image>gcr.io/distroless/java:11</image>
</from>
<to>
<image>tomcat-operator</image>
</to>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>

</project>
67 changes: 67 additions & 0 deletions sample-operators/tomcat-operator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Tomcat Operator

Creates a Tomcat deployment from a Custom Resource, while keeping the WAR separated with another Custom Resource.

This sample demonstrates the following capabilities of the Java Operator SDK:
* Multiple Controllers in a single Operator. The Tomcat resource is managed by the TomcatController while the Webapp
resource is managed by the WebappController.
* Reacting to events about resources created by the controller. The TomcatController will receive events about the
Deployment resources it created. See EventSource section below for more detail.

## Example input for creating a Tomcat instance
```
apiVersion: "tomcatoperator.io/v1"
kind: Tomcat
metadata:
name: test-tomcat1
spec:
version: 9.0
replicas: 2
```

## Example input for the Webapp
```
apiVersion: "tomcatoperator.io/v1"
kind: Webapp
metadata:
name: sample-webapp1
spec:
tomcat: test-tomcat1
url: http://tomcat.apache.org/tomcat-7.0-doc/appdev/sample/sample.war
contextPath: mysample
```

## Getting started / Testing

The quickest way to try the operator is to run it on your local machine, while it connects to a local or remote
Kubernetes cluster. When you start it, it will use the current kubectl context on your machine to connect to the cluster.

Before you run it you have to install the CRD on your cluster by running `kubectl apply -f k8s/crd.yaml`.

When the Operator is running you can create some Tomcat Custom Resources. You can find a sample custom resources
in the k8s folder.

If you want the Operator to be running as a deployment in your cluster, follow the below steps.

## Build
You can build the sample using `mvn install jib:dockerBuild` this will produce a Docker image you can push to the
registry of your choice. The JAR file is built using your local Maven and JDK and then copied into the Docker image.

## Install Operator into cluster

Run `kubectl apply -f k8s/crd.yaml` if you haven't already, then run `kubectl apply -f k8s/operator.yaml`.
Now you can create Tomcat instances with CRs (see examples above).

## EventSources
The TomcatController is listening to events about Deployments created by the TomcatOperator by registering a
DeploymentEventSource with the EventSourceManager. The DeploymentEventSource will in turn register a watch on
all Deployments managed by the Controller (identified by the `app.kubernetes.io/managed-by` label).
When an event from a Deployment is received we have to identify which Tomcat object does the Deployment
belong to. This is done when the DeploymentEventSource creates the DeploymentEvent.

The TomcatController has to take care of setting the `app.kubernetes.io/managed-by` label on the Deployment so the
DeploymentEventSource can watch the right Deployments.
The TomcatController also has to set `ownerReference` on the Deployment so later the DeploymentEventSource can
identify which Tomcat does the Deployment belong to. This is necessary so the frameowork can call the Controller
`createOrUpdate` method correctly.

Loading