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

first draft for a more persistent webapps war #11

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions tomcat/k8s/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ spec:
properties:
deployedArtifact:
type: string
contextPath:
type: string
ready:
type: string
message:
type: string
updateTimestamp:
type: string
required: [spec]
# either Namespaced or Cluster
scope: Namespaced
Expand Down
4 changes: 2 additions & 2 deletions tomcat/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
<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.</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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getUID;
import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getVersion;

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.Watcher;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package io.javaoperatorsdk.operator.sample;

import io.fabric8.kubernetes.api.model.OwnerReference;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.apps.DeploymentStatus;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.dsl.RollableScalableResource;
import io.fabric8.kubernetes.client.dsl.ServiceResource;
import io.fabric8.kubernetes.client.utils.Serialization;
import io.javaoperatorsdk.operator.api.*;
import io.javaoperatorsdk.operator.api.Context;
import io.javaoperatorsdk.operator.processing.event.EventSourceManager;
import io.javaoperatorsdk.operator.processing.event.internal.CustomResourceEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.ListIterator;
import java.util.Objects;
import java.util.Optional;

Expand Down Expand Up @@ -73,7 +74,7 @@ public DeleteControl deleteResource(Tomcat tomcat, Context<Tomcat> context) {
private Tomcat updateTomcatStatus(Tomcat tomcat, Deployment deployment) {
DeploymentStatus deploymentStatus =
Objects.requireNonNullElse(deployment.getStatus(), new DeploymentStatus());
int readyReplicas = Objects.requireNonNullElse(deploymentStatus.getReadyReplicas(), 0);
int readyReplicas = Objects.requireNonNullElse(deploymentStatus.getUpdatedReplicas(), 0);
TomcatStatus status = new TomcatStatus();
status.setReadyReplicas(readyReplicas);
tomcat.setStatus(status);
Expand All @@ -82,6 +83,27 @@ private Tomcat updateTomcatStatus(Tomcat tomcat, Deployment deployment) {

private void createOrUpdateDeployment(Tomcat tomcat) {
String ns = tomcat.getMetadata().getNamespace();

ConfigMap configMap = kubernetesClient
.configMaps().inNamespace(ns).withName(tomcat.getMetadata().getName()).get();

if(configMap == null){
// create it
configMap = loadYaml(ConfigMap.class, "configmap.yaml");
configMap.getMetadata().setName(tomcat.getMetadata().getName());
configMap.getMetadata().setNamespace(ns);
configMap.getMetadata().getLabels().put("app.kubernetes.io/part-of", tomcat.getMetadata().getName());
configMap.getMetadata().getLabels().put("app.kubernetes.io/managed-by", "tomcat-operator");
//
// I wonder if that's ok that this is resource is created by TomcatController and updated by webapps
OwnerReference ownerReference = configMap.getMetadata().getOwnerReferences().get(0);
ownerReference.setName(tomcat.getMetadata().getName());
ownerReference.setUid(tomcat.getMetadata().getUid());

log.info("Creating Init configmap {} in {}", configMap.getMetadata().getName(), ns);
kubernetesClient.configMaps().inNamespace(ns).create(configMap);
}

Deployment existingDeployment =
kubernetesClient
.apps()
Expand All @@ -96,14 +118,6 @@ private void createOrUpdateDeployment(Tomcat tomcat) {
deployment.getMetadata().getLabels().put("app.kubernetes.io/part-of", tomcat.getMetadata().getName());
deployment.getMetadata().getLabels().put("app.kubernetes.io/managed-by", "tomcat-operator");
// set tomcat version
deployment
.getSpec()
.getTemplate()
.getSpec()
.getContainers()
.get(0)
.setImage("tomcat:" + tomcat.getSpec().getVersion());
deployment.getSpec().setReplicas(tomcat.getSpec().getReplicas());

// make sure label selector matches label (which has to be matched by service selector too)
deployment
Expand All @@ -121,22 +135,38 @@ private void createOrUpdateDeployment(Tomcat tomcat) {
OwnerReference ownerReference = deployment.getMetadata().getOwnerReferences().get(0);
ownerReference.setName(tomcat.getMetadata().getName());
ownerReference.setUid(tomcat.getMetadata().getUid());

setTomcatDeploymentReplicats(tomcat, ns, deployment, configMap);
log.info("Creating or updating Deployment {} in {}", deployment.getMetadata().getName(), ns);
kubernetesClient.apps().deployments().inNamespace(ns).create(deployment);
} else {
existingDeployment
.getSpec()
.getTemplate()
.getSpec()
.getContainers()
.get(0)
.setImage("tomcat:" + tomcat.getSpec().getVersion());
existingDeployment.getSpec().setReplicas(tomcat.getSpec().getReplicas());
kubernetesClient.apps().deployments().inNamespace(ns).createOrReplace(existingDeployment);
setTomcatDeploymentReplicats(tomcat, ns, existingDeployment, configMap);
}
}

private void setTomcatDeploymentReplicats(Tomcat tomcat, String ns, Deployment existingDeployment, ConfigMap conf) {
ListIterator<Volume> volumeListIterator = existingDeployment
.getSpec()
.getTemplate()
.getSpec().getVolumes().listIterator();

while (volumeListIterator.hasNext()){
Volume volume = volumeListIterator.next();
if (volume.getName().equals("webapps-war-list")) {
volume.getConfigMap().setName(conf.getMetadata().getName());
}
}

existingDeployment
.getSpec()
.getTemplate()
.getSpec()
.getContainers()
.get(0)
.setImage("tomcat:" + tomcat.getSpec().getVersion());
existingDeployment.getSpec().setReplicas(tomcat.getSpec().getReplicas());
kubernetesClient.apps().deployments().inNamespace(ns).createOrReplace(existingDeployment);
}

private void deleteDeployment(Tomcat tomcat) {
log.info("Deleting Deployment {}", tomcat.getMetadata().getName());
RollableScalableResource<Deployment> deployment =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package io.javaoperatorsdk.operator.sample;

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.api.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.Objects;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.*;

@Controller
public class WebappController implements ResourceController<Webapp> {
Expand All @@ -24,15 +27,86 @@ public WebappController(KubernetesClient kubernetesClient) {

@Override
public UpdateControl<Webapp> createOrUpdateResource(Webapp webapp, Context<Webapp> context) {
log.info("UpdateControl");
if (webapp.getStatus() != null && Objects.equals(webapp.getSpec().getUrl(), webapp.getStatus().getDeployedArtifact())) {
return UpdateControl.noUpdate();
}
if (webapp.getStatus() == null) {
webapp.setStatus(new WebappStatus());
}
log.info(MessageFormat.format("looking for configMap {0}", webapp.getSpec().getTomcat()));
ConfigMap configMap = kubernetesClient
.configMaps()
.inNamespace(webapp.getMetadata().getNamespace())
.withName(webapp.getSpec().getTomcat()).get();

String[] command = new String[] {"wget", "-O", "/data/" + webapp.getSpec().getContextPath() + ".war", webapp.getSpec().getUrl()};
if (configMap ==null){
webapp.getStatus().setReady("False");
webapp.getStatus().setMessage(MessageFormat.format("configMap {0} does not exist", webapp.getSpec().getTomcat()));
webapp.getStatus().setUpdateTimestamp(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new Date()));

executeCommandInAllPods(kubernetesClient, webapp, command);
log.info(MessageFormat.format("configMap {0} is not ready yet", webapp.getSpec().getTomcat()));
return UpdateControl.noUpdate();//.updateStatusSubResource(webapp);
}

String warList = configMap.getData().get("war-list.txt");
String newWebapp = MessageFormat.format("{0}={1}", webapp.getSpec().getContextPath(), webapp.getSpec().getUrl());
String newWarList = "";

if(StringUtils.isNotBlank(webapp.getStatus().getDeployedArtifact()) &&
StringUtils.isNotBlank(webapp.getStatus().getDeployedContextPath()) ) {

String previousWebapp = MessageFormat.format("{0}={1}", webapp.getStatus().getDeployedContextPath(), webapp.getStatus().getDeployedArtifact());

log.info(MessageFormat.format("replace {0} with {1}", previousWebapp, newWebapp));
newWarList = warList.replace(previousWebapp, newWebapp);
}else{
StringBuilder sb = new StringBuilder();
newWarList = sb.append(newWebapp).append("\n").append(warList).toString();
}
configMap.getData().put("war-list.txt", newWarList);
kubernetesClient.configMaps()
.inNamespace(webapp.getMetadata().getNamespace())
.createOrReplace(configMap);

Deployment existingDeployment =
kubernetesClient
.apps()
.deployments()
.inNamespace(webapp.getMetadata().getNamespace())
.withName(webapp.getSpec().getTomcat())
.get();
if (existingDeployment == null) {
webapp.getStatus().setReady("False");
webapp.getStatus().setMessage(MessageFormat.format("Deployment {0} does not exist", webapp.getSpec().getTomcat()));
webapp.getStatus().setUpdateTimestamp(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new Date()));
log.info(MessageFormat.format("Deployment {0} Deployment", webapp.getSpec().getTomcat()));
return UpdateControl.updateStatusSubResource(webapp);
}
if (existingDeployment.getSpec()
.getTemplate()
.getMetadata()
.getAnnotations() !=null) {
existingDeployment.getSpec()
.getTemplate()
.getMetadata()
.getAnnotations()
.put("tomcat-operator-updated", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new Date()));
}else{
Map<String,String> annotations = new HashMap<>();
annotations.put("tomcat-operator-updated", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new Date()));
existingDeployment.getSpec()
.getTemplate()
.getMetadata()
.setAnnotations(annotations);
}

//webapp.getStatus().setDeployedArtifact(webapp.getSpec().getUrl());
webapp.getStatus().setDeployedArtifact(webapp.getSpec().getUrl());
webapp.getStatus().setDeployedContextPath(webapp.getSpec().getContextPath());
webapp.getStatus().setReady("True");
webapp.getStatus().setMessage(MessageFormat.format("Deployment {0} Updated", webapp.getSpec().getTomcat()));
webapp.getStatus().setUpdateTimestamp(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new Date()));
kubernetesClient.apps().deployments().inNamespace(webapp.getMetadata().getNamespace()).createOrReplace(existingDeployment);
return UpdateControl.updateStatusSubResource(webapp);
}

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

private String ready;

public String getReady() { return ready; }

public void setReady(String ready) { this.ready = ready; }

private String message;

public String getMessage() { return message; }

public void setMessage(String message) { this.message = message; }

private String updateTimestamp;

public String getUpdateTimestamp() { return updateTimestamp; }

public void setUpdateTimestamp(String updateTimestamp) { this.updateTimestamp = updateTimestamp; }

private String contextPath;

public void setDeployedContextPath(String contextPath) { this.contextPath = contextPath; }

public String getDeployedContextPath(){ return contextPath; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: ""
labels:
app.kubernetes.io/part-of: ""
app.kubernetes.io/managed-by: "" # used for filtering of Deployments created by the controller
ownerReferences: # used for finding which Tomcat does this Deployment belong to
- apiVersion: apps/v1
kind: Tomcat
name: ""
uid: ""
data:
CHECKSHA256: "false"
wgetscript.sh: |
#!/bin/sh
#
#
#
if [[ ! -f /etc/war-list/war-list.txt ]]; then
echo "war-list.txt is empty";
exit;
fi

for WAR in $(cat /etc/war-list/war-list.txt); do
CONTEXT=$(echo $WAR | cut -d '=' -f 1)
URL=$(echo ${WAR#"$CONTEXT="})
wget -O /data/$CONTEXT.war $URL
# todo add a sha256 validation
# if [[ CHECKSHA256 -eq "true" ]]
# wget -O /data/$CONTEXT.war.sha256 $URL.sha256
#
done

# the format is contextPath=url
war-list.txt: ""

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ spec:
labels:
app: ""
spec:
initContainers:
- name: war-downloader
image: busybox:1.28
command: ['/bin/sh', '/etc/war-list/wgetscript.sh']
volumeMounts:
- name: webapps-volume
mountPath: /data
- name: webapps-war-list
mountPath: /etc/war-list
containers:
- name: tomcat
image: tomcat:8.0
Expand All @@ -28,12 +37,9 @@ spec:
volumeMounts:
- mountPath: /usr/local/tomcat/webapps
name: webapps-volume
- name: war-downloader
image: busybox:1.28
command: ['tail', '-f', '/dev/null']
volumeMounts:
- name: webapps-volume
mountPath: /data
volumes:
- name: webapps-volume
emptydir: {}
- name: webapps-war-list
configMap:
name: "blank"
Loading