Skip to content

New Sample structure to v2 #682

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 1 commit into from
Nov 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
77 changes: 77 additions & 0 deletions .github/workflows/end-to-end-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# 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: 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 target/classes/META-INF/fabric8/tomcats.tomcatoperator.io-v1.yml
kubectl apply -f target/classes/META-INF/fabric8/webapps.tomcatoperator.io-v1.yml

- 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 @@ -161,8 +161,11 @@ public void onDelete(T resource, boolean b) {

@Override
public Optional<T> getCustomResource(CustomResourceID resourceID) {
var sharedIndexInformer =
sharedIndexInformers.get(resourceID.getNamespace().orElse(ANY_NAMESPACE_MAP_KEY));
var sharedIndexInformer = sharedIndexInformers.get(ANY_NAMESPACE_MAP_KEY);
if (sharedIndexInformer == null) {
sharedIndexInformer =
sharedIndexInformers.get(resourceID.getNamespace().orElse(ANY_NAMESPACE_MAP_KEY));
}
var resource = sharedIndexInformer.getStore()
.getByKey(Cache.namespaceKeyFunc(resourceID.getNamespace().orElse(null),
resourceID.getName()));
Expand All @@ -173,6 +176,8 @@ public Optional<T> getCustomResource(CustomResourceID resourceID) {
}
}



/**
* @return shared informers by namespace. If custom resource is not namespace scoped use
* CustomResourceEventSource.ANY_NAMESPACE_MAP_KEY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ public void onDelete(T t, boolean b) {
}

private void propagateEvent(T object) {
var uids = resourceToCustomResourceIDSet.apply(object);
if (uids.isEmpty()) {
var customResourceIDSet = resourceToCustomResourceIDSet.apply(object);
if (customResourceIDSet.isEmpty()) {
return;
}
uids.forEach(uid -> {
Event event = new Event(CustomResourceID.fromResource(object));
customResourceIDSet.forEach(customResourceId -> {
Event event = new Event(customResourceId);
/*
* In fabric8 client for certain cases informers can be created on in a way that they are
* automatically started, what would cause a NullPointerException here, since an event might
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import io.javaoperatorsdk.operator.sample.informereventsource.InformerEventSourceTestCustomReconciler;
import io.javaoperatorsdk.operator.sample.informereventsource.InformerEventSourceTestCustomResource;

import static io.javaoperatorsdk.operator.sample.informereventsource.InformerEventSourceTestCustomReconciler.RELATED_RESOURCE_UID;
import static io.javaoperatorsdk.operator.sample.informereventsource.InformerEventSourceTestCustomReconciler.RELATED_RESOURCE_NAME;
import static io.javaoperatorsdk.operator.sample.informereventsource.InformerEventSourceTestCustomReconciler.TARGET_CONFIG_MAP_KEY;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
Expand All @@ -36,11 +36,11 @@ public void testUsingInformerToWatchChangesOfConfigMap() {
var customResource = initialCustomResource();
customResource = operator.create(InformerEventSourceTestCustomResource.class, customResource);
ConfigMap configMap =
operator.create(ConfigMap.class, relatedConfigMap(customResource.getMetadata().getUid()));
operator.create(ConfigMap.class, relatedConfigMap(customResource.getMetadata().getName()));
waitForCRStatusValue(INITIAL_STATUS_MESSAGE);

configMap.getData().put(TARGET_CONFIG_MAP_KEY, UPDATE_STATUS_MESSAGE);
operator.replace(ConfigMap.class, configMap);
configMap = operator.replace(ConfigMap.class, configMap);

waitForCRStatusValue(UPDATE_STATUS_MESSAGE);
}
Expand All @@ -51,7 +51,7 @@ private ConfigMap relatedConfigMap(String relatedResourceAnnotation) {
ObjectMeta objectMeta = new ObjectMeta();
objectMeta.setName(RESOURCE_NAME);
objectMeta.setAnnotations(new HashMap<>());
objectMeta.getAnnotations().put(RELATED_RESOURCE_UID, relatedResourceAnnotation);
objectMeta.getAnnotations().put(RELATED_RESOURCE_NAME, relatedResourceAnnotation);
configMap.setMetadata(objectMeta);

configMap.setData(new HashMap<>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class InformerEventSourceTestCustomReconciler implements
private static final Logger LOGGER =
LoggerFactory.getLogger(InformerEventSourceTestCustomReconciler.class);

public static final String RELATED_RESOURCE_UID = "relatedResourceName";
public static final String RELATED_RESOURCE_NAME = "relatedResourceName";
public static final String TARGET_CONFIG_MAP_KEY = "targetStatus";

private KubernetesClient kubernetesClient;
Expand All @@ -38,7 +38,7 @@ public class InformerEventSourceTestCustomReconciler implements
@Override
public void prepareEventSources(EventSourceRegistry eventSourceRegistry) {
eventSource = new InformerEventSource<>(kubernetesClient, ConfigMap.class,
Mappers.fromAnnotation(RELATED_RESOURCE_UID));
Mappers.fromAnnotation(RELATED_RESOURCE_NAME));
eventSourceRegistry.registerEventSource(eventSource);
}

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 @@ -271,6 +272,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 @@ -352,6 +376,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>2.0.0-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>
76 changes: 76 additions & 0 deletions sample-operators/tomcat-operator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# 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 CRDs on your cluster by running:
- `kubectl apply -f target/classes/META-INF/fabric8/tomcats.tomcatoperator.io-v1.yml`
- `kubectl apply -f target/classes/META-INF/fabric8/webapps.tomcatoperator.io-v1.yml`

The CRDs are generated automatically from your code by simply adding the `crd-generator-apt`
dependency to your `pom.xml` file.

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

Install the CRDs as shown above 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
InformerEventSource with the EventSourceManager. The InformerEventSource 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 InformerEventSource creates the event.

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

Loading