Skip to content

Commit

Permalink
refactor(operator): change approach to PTS support
Browse files Browse the repository at this point in the history
  • Loading branch information
jsenko committed Oct 23, 2024
1 parent 60fded7 commit b65a6b5
Show file tree
Hide file tree
Showing 16 changed files with 1,452 additions and 825 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import io.apicurio.registry.operator.OperatorException;
import io.apicurio.registry.operator.api.v1.ApicurioRegistry3;
import io.fabric8.kubernetes.api.model.HasMetadata;
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.DeploymentSpec;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.getContainer;
import static io.apicurio.registry.operator.utils.Mapper.YAML_MAPPER;
import static io.apicurio.registry.operator.utils.Utils.isBlank;
import static io.apicurio.registry.operator.utils.Utils.mergeNotOverride;

public class ResourceFactory {

Expand All @@ -32,22 +38,157 @@ public class ResourceFactory {
public static final String UI_CONTAINER_NAME = "apicurio-registry-ui";

public Deployment getDefaultAppDeployment(ApicurioRegistry3 primary) {
// WARNING: Make sure to update the io.apicurio.registry.operator.utils.PodTemplateSpecUtils.merge
// method
// when making significant changes here:
var r = getDefaultResource(primary, Deployment.class, RESOURCE_TYPE_DEPLOYMENT, COMPONENT_APP);
addDefaultLabels(r.getSpec().getTemplate().getMetadata().getLabels(), primary, COMPONENT_APP);
var r = new Deployment();

// Metadata
if (r.getMetadata() == null) {
r.setMetadata(new ObjectMeta());
}
if (r.getMetadata().getLabels() == null) {
r.getMetadata().setLabels(new HashMap<>());
}
r.getMetadata().setNamespace(primary.getMetadata().getNamespace());
r.getMetadata().setName(
primary.getMetadata().getName() + "-" + COMPONENT_APP + "-" + RESOURCE_TYPE_DEPLOYMENT);
addDefaultLabels(r.getMetadata().getLabels(), primary, COMPONENT_APP);

// Spec
r.setSpec(new DeploymentSpec());
r.getSpec().setReplicas(1);
r.getSpec().setSelector(new LabelSelector());
addSelectorLabels(r.getSpec().getSelector().getMatchLabels(), primary, COMPONENT_APP);
if (primary.getSpec().getApp().getPodTemplateSpec() != null) {
r.getSpec().setTemplate(primary.getSpec().getApp().getPodTemplateSpec());
} else {
r.getSpec().setTemplate(new PodTemplateSpec());
}
if (r.getSpec().getTemplate().getMetadata() == null) {
r.getSpec().getTemplate().setMetadata(new ObjectMeta());
}
addDefaultLabels(r.getSpec().getTemplate().getMetadata().getLabels(), primary, COMPONENT_APP);
var c = getContainer(r.getSpec().getTemplate(), APP_CONTAINER_NAME);
if (c == null) {
if (r.getSpec().getTemplate().getSpec() == null) {
r.getSpec().getTemplate().setSpec(new PodSpec());
}
c = new Container();
c.setName(APP_CONTAINER_NAME);
if (r.getSpec().getTemplate().getSpec().getContainers() == null) {
r.getSpec().getTemplate().getSpec().setContainers(new ArrayList<>());
}
r.getSpec().getTemplate().getSpec().getContainers().add(c);
}
if (isBlank(c.getImage())) {
c.setImage("quay.io/apicurio/apicurio-registry:latest-snapshot");
}
if (c.getEnv() != null && !c.getEnv().isEmpty()) {
throw new OperatorException("""
Field spec.(app/ui).podTemplateSpec.spec.containers[name = %s].env must be empty. \
Use spec.(app/ui).env to configure environment variables."""
.formatted(APP_CONTAINER_NAME));
}
if (c.getPorts() == null) {
c.setPorts(new ArrayList<>());
}
mergeNotOverride(c.getPorts(), List.of(new ContainerPortBuilder().withName("http").withProtocol("TCP")
.withContainerPort(8080).build()), ContainerPort::getName);
if (c.getReadinessProbe() == null) {
c.setReadinessProbe(
new ProbeBuilder().withHttpGet(new HTTPGetActionBuilder().withPath("/health/ready")
.withPort(new IntOrString(8080)).withScheme("HTTP").build()).build());
}
if (c.getLivenessProbe() == null) {
c.setLivenessProbe(
new ProbeBuilder().withHttpGet(new HTTPGetActionBuilder().withPath("/health/live")
.withPort(new IntOrString(8080)).withScheme("HTTP").build()).build());
}
if (c.getResources() == null) {
c.setResources(new ResourceRequirements());
}
if (c.getResources().getRequests() == null || c.getResources().getRequests().isEmpty()) {
c.getResources()
.setRequests(Map.of("cpu", new Quantity("500m"), "memory", new Quantity("512Mi")));
}
if (c.getResources().getLimits() == null || c.getResources().getLimits().isEmpty()) {
c.getResources().setLimits(Map.of("cpu", new Quantity("1"), "memory", new Quantity("1Gi")));
}

return r;
}

public Deployment getDefaultUIDeployment(ApicurioRegistry3 primary) {
// WARNING: Make sure to update the io.apicurio.registry.operator.utils.PodTemplateSpecUtils.merge
// method
// when making significant changes here:
var r = getDefaultResource(primary, Deployment.class, RESOURCE_TYPE_DEPLOYMENT, COMPONENT_UI);
addDefaultLabels(r.getSpec().getTemplate().getMetadata().getLabels(), primary, COMPONENT_UI);
var r = new Deployment();

// Metadata
if (r.getMetadata() == null) {
r.setMetadata(new ObjectMeta());
}
r.getMetadata().setNamespace(primary.getMetadata().getNamespace());
r.getMetadata().setName(
primary.getMetadata().getName() + "-" + COMPONENT_UI + "-" + RESOURCE_TYPE_DEPLOYMENT);
addDefaultLabels(r.getMetadata().getLabels(), primary, COMPONENT_UI);

// Spec
r.setSpec(new DeploymentSpec());
r.getSpec().setReplicas(1);
r.getSpec().setSelector(new LabelSelector());
addSelectorLabels(r.getSpec().getSelector().getMatchLabels(), primary, COMPONENT_UI);
if (primary.getSpec().getUi().getPodTemplateSpec() != null) {
r.getSpec().setTemplate(primary.getSpec().getUi().getPodTemplateSpec());
} else {
r.getSpec().setTemplate(new PodTemplateSpec());
}
if (r.getSpec().getTemplate().getMetadata() == null) {
r.getSpec().getTemplate().setMetadata(new ObjectMeta());
}
addDefaultLabels(r.getSpec().getTemplate().getMetadata().getLabels(), primary, COMPONENT_UI);
var c = getContainer(r.getSpec().getTemplate(), UI_CONTAINER_NAME);
if (c == null) {
if (r.getSpec().getTemplate().getSpec() == null) {
r.getSpec().getTemplate().setSpec(new PodSpec());
}
c = new Container();
c.setName(UI_CONTAINER_NAME);
if (r.getSpec().getTemplate().getSpec().getContainers() == null) {
r.getSpec().getTemplate().getSpec().setContainers(new ArrayList<>());
}
r.getSpec().getTemplate().getSpec().getContainers().add(c);
}
if (isBlank(c.getImage())) {
c.setImage("quay.io/apicurio/apicurio-registry-ui:latest-snapshot");
}
if (c.getEnv() != null && !c.getEnv().isEmpty()) {
throw new OperatorException("""
Field spec.(app/ui).podTemplateSpec.spec.containers[name = %s].env must be empty. \
Use spec.(app/ui).env to configure environment variables."""
.formatted(UI_CONTAINER_NAME));
}
if (c.getPorts() == null) {
c.setPorts(new ArrayList<>());
}
mergeNotOverride(c.getPorts(), List.of(new ContainerPortBuilder().withName("http").withProtocol("TCP")
.withContainerPort(8080).build()), ContainerPort::getName);
if (c.getReadinessProbe() == null) {
c.setReadinessProbe(
new ProbeBuilder().withHttpGet(new HTTPGetActionBuilder().withPath("/config.js")
.withPort(new IntOrString(8080)).withScheme("HTTP").build()).build());
}
if (c.getLivenessProbe() == null) {
c.setLivenessProbe(
new ProbeBuilder().withHttpGet(new HTTPGetActionBuilder().withPath("/config.js")
.withPort(new IntOrString(8080)).withScheme("HTTP").build()).build());
}
if (c.getResources() == null) {
c.setResources(new ResourceRequirements());
}
if (c.getResources().getRequests() == null || c.getResources().getRequests().isEmpty()) {
c.getResources()
.setRequests(Map.of("cpu", new Quantity("100m"), "memory", new Quantity("256Mi")));
}
if (c.getResources().getLimits() == null || c.getResources().getLimits().isEmpty()) {
c.getResources().setLimits(Map.of("cpu", new Quantity("200m"), "memory", new Quantity("512Mi")));
}

return r;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import io.apicurio.registry.operator.OperatorException;
import io.apicurio.registry.operator.api.v1.ApicurioRegistry3;
import io.apicurio.registry.operator.utils.PodTemplateSpecFeature;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.EnvVar;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.PodTemplateSpec;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
Expand All @@ -22,7 +22,6 @@
import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_APP;
import static io.apicurio.registry.operator.resource.ResourceKey.APP_DEPLOYMENT_KEY;
import static io.apicurio.registry.operator.utils.Mapper.toYAML;
import static io.apicurio.registry.operator.utils.PodTemplateSpecFeature.merge;
import static java.util.Objects.requireNonNull;

// spotless:off
Expand All @@ -44,9 +43,6 @@ protected Deployment desired(ApicurioRegistry3 primary, Context<ApicurioRegistry

var d = APP_DEPLOYMENT_KEY.getFactory().apply(primary);

d.getSpec().setTemplate(merge(primary.getSpec().getApp().getPodTemplateSpec(),
d.getSpec().getTemplate(), APP_CONTAINER_NAME));

var envVars = new LinkedHashMap<String, EnvVar>();
primary.getSpec().getApp().getEnv().forEach(e -> {
envVars.put(e.getName(), e);
Expand Down Expand Up @@ -86,12 +82,30 @@ public static Container getContainer(Deployment d, String name) {
requireNonNull(name);
log.debug("Getting container {} in Deployment {}", name, ResourceID.fromResource(d));
if (d.getSpec() != null & d.getSpec().getTemplate() != null) {
var c = PodTemplateSpecFeature.getContainer(d.getSpec().getTemplate(), name);
var c = getContainer(d.getSpec().getTemplate(), name);
if (c != null) {
return c;
}
}
throw new OperatorException(
"Container %s not found in Deployment %s".formatted(name, ResourceID.fromResource(d)));
}

/**
* Get container with a given name from the given PTS.
*
* @return null when container was not found
*/
public static Container getContainer(PodTemplateSpec pts, String name) {
requireNonNull(pts);
requireNonNull(name);
if (pts.getSpec() != null && pts.getSpec().getContainers() != null) {
for (var c : pts.getSpec().getContainers()) {
if (name.equals(c.getName())) {
return c;
}
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import static io.apicurio.registry.operator.resource.app.AppDeploymentResource.getContainer;
import static io.apicurio.registry.operator.utils.IngressUtils.withIngressRule;
import static io.apicurio.registry.operator.utils.Mapper.toYAML;
import static io.apicurio.registry.operator.utils.PodTemplateSpecFeature.merge;

// spotless:off
@KubernetesDependent(
Expand All @@ -41,9 +40,6 @@ protected Deployment desired(ApicurioRegistry3 primary, Context<ApicurioRegistry

var d = UI_DEPLOYMENT_KEY.getFactory().apply(primary);

d.getSpec().setTemplate(merge(primary.getSpec().getUi().getPodTemplateSpec(),
d.getSpec().getTemplate(), UI_CONTAINER_NAME));

var envVars = new LinkedHashMap<String, EnvVar>();
primary.getSpec().getUi().getEnv().forEach(e -> {
envVars.put(e.getName(), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.apicurio.registry.operator.OperatorException;

import java.io.IOException;

import static java.util.Objects.requireNonNull;

public class Mapper {

private static final ObjectMapper MAPPER;
Expand All @@ -24,17 +20,6 @@ public class Mapper {
YAML_MAPPER.registerModule(new JavaTimeModule());
}

public static <T> T duplicate(T source, Class<T> type) {
requireNonNull(source);
requireNonNull(type);
try {
var json = MAPPER.writeValueAsString(source);
return MAPPER.readValue(json, type);
} catch (IOException ex) {
throw new OperatorException("Could not duplicate value of type " + type.getCanonicalName(), ex);
}
}

public static String toYAML(Object value) {
try {
return YAML_MAPPER.writeValueAsString(value);
Expand Down
Loading

0 comments on commit b65a6b5

Please sign in to comment.