Skip to content
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

configmap based mechanism to override the admin/canary versions #643

Merged
merged 7 commits into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
122 changes: 122 additions & 0 deletions operator/src/main/java/org/bf2/operator/managers/ImageManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package org.bf2.operator.managers;

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
import io.fabric8.kubernetes.client.utils.Serialization;
import io.quarkus.runtime.Startup;
import org.bf2.common.ResourceInformerFactory;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

@Startup
@ApplicationScoped
public class ImageManager {

public static final String CANARY = "canary";
public static final String CANARY_INIT = "canary_init";
public static final String ADMIN_API = "admin_api";

public static final String IMAGES_YAML = "images.yaml";
shawkins marked this conversation as resolved.
Show resolved Hide resolved

private static Properties EMPTY = new Properties();

private Map<String, Properties> otherImages = new ConcurrentHashMap<>();

@ConfigProperty(name = "image.admin-api")
String adminApiImage;

@ConfigProperty(name = "image.canary")
String canaryImage;

@ConfigProperty(name = "image.canary-init")
String canaryInitImage;

@Inject
KubernetesClient kubernetesClient;

@Inject
ResourceInformerFactory resourceInformerFactory;

@Inject
InformerManager informerManager;

@PostConstruct
protected void onStart() {
this.resourceInformerFactory.create(ConfigMap.class,
this.kubernetesClient.configMaps().inAnyNamespace().withLabel("app", "strimzi"),
new ResourceEventHandler<ConfigMap>() {
@Override
public void onAdd(ConfigMap obj) {
updateImages(obj);
}

@Override
public void onDelete(ConfigMap obj, boolean deletedFinalStateUnknown) {
removeImages(obj);
}

@Override
public void onUpdate(ConfigMap oldObj, ConfigMap newObj) {
updateImages(newObj);
}
});

}

private Properties getImagesForVersion(String strimzi) {
return otherImages.getOrDefault(strimzi == null ? "" : strimzi, EMPTY);
}

public String getCanaryImage(String strimzi) {
return getImagesForVersion(strimzi).getProperty(CANARY, canaryImage);
}

public String getCanaryInitImage(String strimzi) {
return getImagesForVersion(strimzi).getProperty(CANARY_INIT, canaryInitImage);
}

public String getAdminApiImage(String strimzi) {
return getImagesForVersion(strimzi).getProperty(ADMIN_API, adminApiImage);
}

void updateImages(ConfigMap obj) {
String name = obj.getMetadata().getName();
if (name.startsWith(StrimziManager.STRIMZI_CLUSTER_OPERATOR)) {
String data = obj.getData().get(IMAGES_YAML);
boolean resync = false;
if (data == null) {
otherImages.remove(name);
resync = true;
} else {
Properties p = Serialization.unmarshal(data, Properties.class);
Properties old = otherImages.put(name, p);
resync = !Objects.equals(p, old);
}
if (resync) {
informerManager.resyncManagedKafka();
}
}
}

void removeImages(ConfigMap obj) {
String name = obj.getMetadata().getName();
if (name.startsWith(StrimziManager.STRIMZI_CLUSTER_OPERATOR)) {
otherImages.remove(name);
informerManager.resyncManagedKafka();
}
}

void resetImages() {
this.otherImages.clear();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
@ApplicationScoped
public class StrimziManager {

public static final String STRIMZI_CLUSTER_OPERATOR = "strimzi-cluster-operator";
public static final String STRIMZI_PAUSE_RECONCILE_ANNOTATION = "strimzi.io/pause-reconciliation";
public static final String STRIMZI_PAUSE_REASON_ANNOTATION = "managedkafka.bf2.org/pause-reason";

Expand Down Expand Up @@ -110,7 +111,7 @@ private void updateStatus() {
}

private boolean isStrimziDeployment(Deployment deployment) {
return deployment.getMetadata().getName().startsWith("strimzi-cluster-operator");
return deployment.getMetadata().getName().startsWith(STRIMZI_CLUSTER_OPERATOR);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import io.quarkus.runtime.Startup;
import io.quarkus.runtime.StartupEvent;
import org.bf2.common.OperandUtils;
import org.bf2.operator.managers.ImageManager;
import org.bf2.operator.managers.ImagePullSecretManager;
import org.bf2.operator.managers.IngressControllerManager;
import org.bf2.operator.managers.SecuritySecretManager;
Expand Down Expand Up @@ -81,9 +82,6 @@ public class AdminServer extends AbstractAdminServer {
@Inject
Logger log;

@ConfigProperty(name = "image.admin-api")
String adminApiImage;

@ConfigProperty(name = "adminserver.cors.allowlist")
Optional<String> corsAllowList;

Expand All @@ -101,6 +99,9 @@ public class AdminServer extends AbstractAdminServer {
@Inject
protected Instance<IngressControllerManager> ingressControllerManagerInstance;

@Inject
protected ImageManager imageManager;

void onStart(@Observes StartupEvent ev) {
if (kubernetesClient.isAdaptable(OpenShiftClient.class)) {
openShiftClient = kubernetesClient.adapt(OpenShiftClient.class);
Expand Down Expand Up @@ -257,7 +258,7 @@ protected Route routeFrom(ManagedKafka managedKafka, Route current) {
protected List<Container> buildContainers(ManagedKafka managedKafka) {
Container container = new ContainerBuilder()
.withName("admin-server")
.withImage(adminApiImage)
.withImage(imageManager.getAdminApiImage(managedKafka.getSpec().getVersions().getStrimzi()))
.withEnv(buildEnvVar(managedKafka))
.withPorts(buildContainerPorts(managedKafka))
.withResources(buildResources())
Expand Down
14 changes: 6 additions & 8 deletions operator/src/main/java/org/bf2/operator/operands/Canary.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import io.quarkus.arc.DefaultBean;
import io.quarkus.runtime.Startup;
import org.bf2.common.OperandUtils;
import org.bf2.operator.managers.ImageManager;
import org.bf2.operator.managers.ImagePullSecretManager;
import org.bf2.operator.managers.IngressControllerManager;
import org.bf2.operator.managers.SecuritySecretManager;
Expand Down Expand Up @@ -58,12 +59,6 @@ public class Canary extends AbstractCanary {
private static final String METRICS_PORT_NAME = "metrics";
private static final IntOrString METRICS_PORT_TARGET = new IntOrString(METRICS_PORT_NAME);

@ConfigProperty(name = "image.canary")
String canaryImage;

@ConfigProperty(name = "image.canary-init")
String canaryInitImage;

@ConfigProperty(name = "managedkafka.canary.producer-latency-buckets")
String producerLatencyBuckets;

Expand Down Expand Up @@ -91,6 +86,9 @@ public class Canary extends AbstractCanary {
@Inject
protected KafkaInstanceConfiguration config;

@Inject
protected ImageManager imageManager;

@Override
public Deployment deploymentFrom(ManagedKafka managedKafka, Deployment current) {
String canaryName = canaryName(managedKafka);
Expand Down Expand Up @@ -192,7 +190,7 @@ private List<Volume> buildVolumes(ManagedKafka managedKafka) {
protected Container buildInitContainer(ManagedKafka managedKafka, Deployment current) {
return new ContainerBuilder()
.withName("init")
.withImage(canaryInitImage)
.withImage(imageManager.getCanaryInitImage(managedKafka.getSpec().getVersions().getStrimzi()))
.withEnv(buildInitEnvVar(managedKafka))
.withResources(buildResources())
.withCommand("/opt/strimzi-canary-tool/canary-dns-init.sh")
Expand All @@ -208,7 +206,7 @@ protected boolean hasClusterSpecificBootstrapDomain(ManagedKafka managedKafka) {
protected List<Container> buildContainers(ManagedKafka managedKafka, Deployment current) {
Container container = new ContainerBuilder()
.withName("canary")
.withImage(canaryImage)
.withImage(imageManager.getCanaryImage(managedKafka.getSpec().getVersions().getStrimzi()))
.withEnv(buildEnvVar(managedKafka, current))
.withPorts(buildContainerPorts())
.withResources(buildResources())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.bf2.operator.managers;

import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.TestProfile;
import io.quarkus.test.kubernetes.client.KubernetesServerTestResource;
import org.bf2.operator.MockProfile;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import javax.inject.Inject;

import java.util.Collections;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;

@QuarkusTestResource(KubernetesServerTestResource.class)
@TestProfile(MockProfile.class)
@QuarkusTest
public class ImageManagerTest {

@Inject
KubernetesClient client;

@Inject
ImageManager imageManager;

@AfterEach
public void cleanup() {
imageManager.resetImages();
}

@Test
void testImageOverride() {
String versionString = "strimzi-cluster-operator-0.26-1";
String defaultVersion = imageManager.getAdminApiImage(versionString);

imageManager.updateImages(new ConfigMapBuilder().withNewMetadata()
.withName(versionString)
.endMetadata()
.withData(Collections.singletonMap(ImageManager.IMAGES_YAML, "canary: something"))
.build());

String override = imageManager.getCanaryImage(versionString);

assertEquals("something", override);
assertNotEquals(defaultVersion, override);
}

}