diff --git a/src/main/java/org/trustify/operator/Config.java b/src/main/java/org/trustify/operator/Config.java index 850b53f..57d8355 100644 --- a/src/main/java/org/trustify/operator/Config.java +++ b/src/main/java/org/trustify/operator/Config.java @@ -6,6 +6,9 @@ @ConfigMapping(prefix = "related.image") public interface Config { + @WithName("ui") + String uiImage(); + @WithName("server") String serverImage(); diff --git a/src/main/java/org/trustify/operator/Constants.java b/src/main/java/org/trustify/operator/Constants.java index 90cef2b..b273a64 100644 --- a/src/main/java/org/trustify/operator/Constants.java +++ b/src/main/java/org/trustify/operator/Constants.java @@ -10,6 +10,7 @@ public class Constants { // public static final String TRUSTI_NAME = "trustify"; + public static final String TRUSTI_UI_NAME = "trustify-ui"; public static final String TRUSTI_SERVER_NAME = "trustify-server"; public static final String TRUSTI_DB_NAME = "trustify-db"; @@ -20,6 +21,9 @@ public class Constants { public static final Map SERVER_SELECTOR_LABELS = Map.of( "trustify-operator/group", "server" ); + public static final Map UI_SELECTOR_LABELS = Map.of( + "trustify-operator/group", "ui" + ); // public static final Integer HTTP_PORT = 8080; @@ -33,6 +37,9 @@ public class Constants { public static final String DB_DEPLOYMENT_SUFFIX = "-" + TRUSTI_DB_NAME + "-deployment"; public static final String DB_SERVICE_SUFFIX = "-" + TRUSTI_DB_NAME + "-service"; + public static final String UI_DEPLOYMENT_SUFFIX = "-" + TRUSTI_UI_NAME + "-deployment"; + public static final String UI_SERVICE_SUFFIX = "-" + TRUSTI_UI_NAME + "-service"; + public static final String SERVER_DEPLOYMENT_SUFFIX = "-" + TRUSTI_SERVER_NAME + "-deployment"; public static final String SERVER_SERVICE_SUFFIX = "-" + TRUSTI_SERVER_NAME + "-service"; diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/TrustifySpec.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/TrustifySpec.java index 4b2673a..677bc16 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/TrustifySpec.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/TrustifySpec.java @@ -8,6 +8,9 @@ import java.util.List; public record TrustifySpec( + @JsonPropertyDescription("Custom Trustify UI image to be used. For internal use only") + String uiImage, + @JsonPropertyDescription("Custom Trustify Server image to be used. For internal use only") String serverImage, @@ -32,6 +35,10 @@ public record TrustifySpec( @JsonPropertyDescription("In this section you can configure features related to HTTP and HTTPS") HttpSpec httpSpec, + @JsonProperty("uiResourceLimits") + @JsonPropertyDescription("In this section you can configure resource limits settings for the UI.") + ResourcesLimitSpec uiResourceLimitSpec, + @JsonProperty("serverResourceLimits") @JsonPropertyDescription("In this section you can configure resource limits settings for the Server.") ResourcesLimitSpec serverResourceLimitSpec @@ -46,6 +53,8 @@ public TrustifySpec() { null, null, null, + null, + null, null ); } diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerDeploymentDiscriminator.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerDeploymentDiscriminator.java index 6f5fce6..8c8ab6c 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerDeploymentDiscriminator.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerDeploymentDiscriminator.java @@ -15,7 +15,7 @@ public class ServerDeploymentDiscriminator implements ResourceDiscriminator distinguish(Class resource, Trustify cr, Context context) { String deploymentName = ServerDeployment.getDeploymentName(cr); ResourceID resourceID = new ResourceID(deploymentName, cr.getMetadata().getNamespace()); - var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(Deployment.class, TrustifyReconciler.SERVER_DEPLOYMENT_EVENT_SOURCE); + var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(Deployment.class, TrustifyReconciler.DEPLOYMENT_EVENT_SOURCE); return informerEventSource.get(resourceID); } } \ No newline at end of file diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerServiceDiscriminator.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerServiceDiscriminator.java index 29ea024..15df354 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerServiceDiscriminator.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerServiceDiscriminator.java @@ -15,7 +15,7 @@ public class ServerServiceDiscriminator implements ResourceDiscriminator distinguish(Class resource, Trustify cr, Context context) { String serviceName = ServerService.getServiceName(cr); ResourceID resourceID = new ResourceID(serviceName, cr.getMetadata().getNamespace()); - var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(Service.class, TrustifyReconciler.SERVER_SERVICE_EVENT_SOURCE); + var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(Service.class, TrustifyReconciler.SERVICE_EVENT_SOURCE); return informerEventSource.get(resourceID); } } \ No newline at end of file diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIDeployment.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIDeployment.java new file mode 100644 index 0000000..5cb0df7 --- /dev/null +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIDeployment.java @@ -0,0 +1,221 @@ +package org.trustify.operator.cdrs.v2alpha1.ui; + +import io.fabric8.kubernetes.api.model.*; +import io.fabric8.kubernetes.api.model.apps.*; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.processing.dependent.Matcher; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.trustify.operator.Config; +import org.trustify.operator.Constants; +import org.trustify.operator.cdrs.v2alpha1.Trustify; +import org.trustify.operator.cdrs.v2alpha1.TrustifySpec; +import org.trustify.operator.cdrs.v2alpha1.server.ServerDeployment; +import org.trustify.operator.cdrs.v2alpha1.server.ServerService; +import org.trustify.operator.utils.CRDUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@KubernetesDependent(labelSelector = UIDeployment.LABEL_SELECTOR, resourceDiscriminator = UIDeploymentDiscriminator.class) +@ApplicationScoped +public class UIDeployment extends CRUDKubernetesDependentResource + implements Matcher, Condition { + + public static final String LABEL_SELECTOR = "app.kubernetes.io/managed-by=trustify-operator,component=ui"; + + @Inject + Config config; + + public UIDeployment() { + super(Deployment.class); + } + + @Override + protected Deployment desired(Trustify cr, Context context) { + return newDeployment(cr, context); + } + + @Override + public Result match(Deployment actual, Trustify cr, Context context) { + final var container = actual.getSpec() + .getTemplate().getSpec().getContainers() + .stream() + .findFirst(); + + return Result.nonComputed(container + .map(c -> c.getImage() != null) + .orElse(false) + ); + } + + @Override + public boolean isMet(DependentResource dependentResource, Trustify primary, Context context) { + return context.getSecondaryResource(Deployment.class, new UIDeploymentDiscriminator()) + .map(deployment -> { + final var status = deployment.getStatus(); + if (status != null) { + final var readyReplicas = status.getReadyReplicas(); + return readyReplicas != null && readyReplicas >= 1; + } + return false; + }) + .orElse(false); + } + + @SuppressWarnings("unchecked") + private Deployment newDeployment(Trustify cr, Context context) { + final var contextLabels = (Map) context.managedDependentResourceContext() + .getMandatory(Constants.CONTEXT_LABELS_KEY, Map.class); + + return new DeploymentBuilder() + .withNewMetadata() + .withName(getDeploymentName(cr)) + .withNamespace(cr.getMetadata().getNamespace()) + .withLabels(contextLabels) + .addToLabels("component", "ui") + .addToLabels(Map.of( + "app.openshift.io/runtime", "nodejs" + )) + .withAnnotations(Map.of("app.openshift.io/connects-to", """ + [{"apiVersion": "apps/v1", "kind":"Deployment", "name": "%s"}] + """.formatted(ServerDeployment.getDeploymentName(cr)) + )) + .withOwnerReferences(CRDUtils.getOwnerReference(cr)) + .endMetadata() + .withSpec(getDeploymentSpec(cr, context)) + .build(); + } + + @SuppressWarnings("unchecked") + private DeploymentSpec getDeploymentSpec(Trustify cr, Context context) { + final var contextLabels = (Map) context.managedDependentResourceContext() + .getMandatory(Constants.CONTEXT_LABELS_KEY, Map.class); + + Map selectorLabels = Constants.UI_SELECTOR_LABELS; + String image = Optional.ofNullable(cr.getSpec().uiImage()).orElse(config.uiImage()); + String imagePullPolicy = Optional.ofNullable(cr.getSpec().imagePullPolicy()).orElse(config.imagePullPolicy()); + + TrustifySpec.ResourcesLimitSpec resourcesLimitSpec = CRDUtils.getValueFromSubSpec(cr.getSpec(), TrustifySpec::uiResourceLimitSpec) + .orElse(null); + + return new DeploymentSpecBuilder() + .withStrategy(new DeploymentStrategyBuilder() + .withType("Recreate") + .build() + ) + .withReplicas(1) + .withSelector(new LabelSelectorBuilder() + .withMatchLabels(selectorLabels) + .build() + ) + .withTemplate(new PodTemplateSpecBuilder() + .withNewMetadata() + .withLabels(Stream + .concat(contextLabels.entrySet().stream(), selectorLabels.entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) + ) + .endMetadata() + .withSpec(new PodSpecBuilder() + .withRestartPolicy("Always") + .withTerminationGracePeriodSeconds(60L) + .withImagePullSecrets(cr.getSpec().imagePullSecrets()) + .withContainers(new ContainerBuilder() + .withName(Constants.TRUSTI_UI_NAME) + .withImage(image) + .withImagePullPolicy(imagePullPolicy) + .withEnv(getEnvVars(cr)) + .withPorts( + new ContainerPortBuilder() + .withName("http") + .withProtocol("TCP") + .withContainerPort(Constants.HTTP_PORT) + .build() + ) + .withLivenessProbe(new ProbeBuilder() + .withExec(new ExecActionBuilder() + .withCommand( + "/bin/sh", + "-c", + "ps -A | grep node" + ) + .build() + ) + .withInitialDelaySeconds(10) + .withTimeoutSeconds(1) + .withPeriodSeconds(5) + .withSuccessThreshold(1) + .withFailureThreshold(3) + .build() + ) + .withReadinessProbe(new ProbeBuilder() + .withHttpGet(new HTTPGetActionBuilder() + .withPath("/") + .withNewPort(Constants.HTTP_PORT) + .withScheme("HTTP") + .build() + ) + .withInitialDelaySeconds(10) + .withTimeoutSeconds(1) + .withPeriodSeconds(5) + .withSuccessThreshold(1) + .withFailureThreshold(3) + .build() + ) + .withResources(new ResourceRequirementsBuilder() + .withRequests(Map.of( + "cpu", new Quantity(CRDUtils.getValueFromSubSpec(resourcesLimitSpec, TrustifySpec.ResourcesLimitSpec::cpuRequest).orElse("100m")), + "memory", new Quantity(CRDUtils.getValueFromSubSpec(resourcesLimitSpec, TrustifySpec.ResourcesLimitSpec::memoryRequest).orElse("350Mi")) + )) + .withLimits(Map.of( + "cpu", new Quantity(CRDUtils.getValueFromSubSpec(resourcesLimitSpec, TrustifySpec.ResourcesLimitSpec::cpuLimit).orElse("500m")), + "memory", new Quantity(CRDUtils.getValueFromSubSpec(resourcesLimitSpec, TrustifySpec.ResourcesLimitSpec::memoryLimit).orElse("800Mi")) + )) + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .build(); + } + + private List getEnvVars(Trustify cr) { + return Arrays.asList( + new EnvVarBuilder() + .withName("AUTH_REQUIRED") + .withValue("false") + .build(), + new EnvVarBuilder() + .withName("ANALYTICS_ENABLED") + .withValue("false") + .build(), + new EnvVarBuilder() + .withName("TRUSTIFY_API_URL") + .withValue(ServerService.getServiceName(cr)) + .build(), + new EnvVarBuilder() + .withName("UI_INGRESS_PROXY_BODY_SIZE") + .withValue("50m") + .build(), + new EnvVarBuilder() + .withName("NODE_EXTRA_CA_CERTS") + .withValue("/opt/app-root/src/ca.crt") + .build() + ); + } + + public static String getDeploymentName(Trustify cr) { + return cr.getMetadata().getName() + Constants.UI_DEPLOYMENT_SUFFIX; + } +} diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIDeploymentDiscriminator.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIDeploymentDiscriminator.java new file mode 100644 index 0000000..4879442 --- /dev/null +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIDeploymentDiscriminator.java @@ -0,0 +1,21 @@ +package org.trustify.operator.cdrs.v2alpha1.ui; + +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; +import org.trustify.operator.cdrs.v2alpha1.Trustify; +import org.trustify.operator.controllers.TrustifyReconciler; + +import java.util.Optional; + +public class UIDeploymentDiscriminator implements ResourceDiscriminator { + @Override + public Optional distinguish(Class resource, Trustify cr, Context context) { + String deploymentName = UIDeployment.getDeploymentName(cr); + ResourceID resourceID = new ResourceID(deploymentName, cr.getMetadata().getNamespace()); + var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(Deployment.class, TrustifyReconciler.DEPLOYMENT_EVENT_SOURCE); + return informerEventSource.get(resourceID); + } +} \ No newline at end of file diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerIngress.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIIngress.java similarity index 87% rename from src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerIngress.java rename to src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIIngress.java index 30e9915..897ab63 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerIngress.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIIngress.java @@ -1,4 +1,4 @@ -package org.trustify.operator.cdrs.v2alpha1.server; +package org.trustify.operator.cdrs.v2alpha1.ui; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; import io.fabric8.kubernetes.api.model.networking.v1.IngressTLS; @@ -15,11 +15,11 @@ import java.util.Collections; import java.util.Map; -@KubernetesDependent(labelSelector = ServerIngress.LABEL_SELECTOR, resourceDiscriminator = ServerIngressDiscriminator.class) +@KubernetesDependent(labelSelector = UIIngress.LABEL_SELECTOR, resourceDiscriminator = UIIngressDiscriminator.class) @ApplicationScoped -public class ServerIngress extends ServerIngressBase { +public class UIIngress extends UIIngressBase { - public static final String LABEL_SELECTOR = "app.kubernetes.io/managed-by=trustify-operator,component=server,component-variant=https"; + public static final String LABEL_SELECTOR = "app.kubernetes.io/managed-by=trustify-operator,component=ui,component-variant=https"; @Override @SuppressWarnings("unchecked") @@ -29,7 +29,7 @@ protected Ingress desired(Trustify cr, Context context) { context, getIngressName(cr), Map.of( - "component", "server", + "component", "ui", "component-variant", "https" ), Collections.emptyMap() @@ -38,7 +38,7 @@ protected Ingress desired(Trustify cr, Context context) { @Override public boolean isMet(DependentResource dependentResource, Trustify primary, Context context) { - return context.getSecondaryResource(Ingress.class, new ServerIngressDiscriminator()) + return context.getSecondaryResource(Ingress.class, new UIIngressDiscriminator()) .map(in -> { final var status = in.getStatus(); if (status != null) { diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerIngressBase.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIIngressBase.java similarity index 92% rename from src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerIngressBase.java rename to src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIIngressBase.java index 4f67f50..a8ced85 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerIngressBase.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIIngressBase.java @@ -1,4 +1,4 @@ -package org.trustify.operator.cdrs.v2alpha1.server; +package org.trustify.operator.cdrs.v2alpha1.ui; import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; @@ -21,12 +21,12 @@ import java.util.Map; import java.util.Optional; -public abstract class ServerIngressBase extends CRUDKubernetesDependentResource implements Condition { +public abstract class UIIngressBase extends CRUDKubernetesDependentResource implements Condition { @Inject KubernetesClient k8sClient; - public ServerIngressBase() { + public UIIngressBase() { super(Ingress.class); } @@ -38,7 +38,7 @@ protected Ingress newIngress(Trustify cr, Context context, String ingr final var labels = (Map) context.managedDependentResourceContext() .getMandatory(Constants.CONTEXT_LABELS_KEY, Map.class); - var port = ServerService.getServicePort(cr); + var port = UIService.getServicePort(cr); String hostname = getHostname(cr); IngressTLS ingressTLS = getIngressTLS(cr); @@ -62,7 +62,7 @@ protected Ingress newIngress(Trustify cr, Context context, String ingr .withPathType("Prefix") .withNewBackend() .withNewService() - .withName(ServerService.getServiceName(cr)) + .withName(UIService.getServiceName(cr)) .withNewPort() .withNumber(port) .endPort() diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerIngressDiscriminator.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIIngressDiscriminator.java similarity index 80% rename from src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerIngressDiscriminator.java rename to src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIIngressDiscriminator.java index 326b8ac..71074e2 100644 --- a/src/main/java/org/trustify/operator/cdrs/v2alpha1/server/ServerIngressDiscriminator.java +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIIngressDiscriminator.java @@ -1,4 +1,4 @@ -package org.trustify.operator.cdrs.v2alpha1.server; +package org.trustify.operator.cdrs.v2alpha1.ui; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; import io.javaoperatorsdk.operator.api.reconciler.Context; @@ -9,10 +9,10 @@ import java.util.Optional; -public class ServerIngressDiscriminator implements ResourceDiscriminator { +public class UIIngressDiscriminator implements ResourceDiscriminator { @Override public Optional distinguish(Class resource, Trustify cr, Context context) { - String ingressName = ServerIngress.getIngressName(cr); + String ingressName = UIIngress.getIngressName(cr); ResourceID resourceID = new ResourceID(ingressName, cr.getMetadata().getNamespace()); var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(Ingress.class); return informerEventSource.get(resourceID); diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIService.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIService.java new file mode 100644 index 0000000..a09198b --- /dev/null +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIService.java @@ -0,0 +1,69 @@ +package org.trustify.operator.cdrs.v2alpha1.ui; + +import io.fabric8.kubernetes.api.model.*; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import jakarta.enterprise.context.ApplicationScoped; +import org.trustify.operator.Constants; +import org.trustify.operator.cdrs.v2alpha1.Trustify; +import org.trustify.operator.cdrs.v2alpha1.TrustifySpec; +import org.trustify.operator.utils.CRDUtils; + +import java.util.Map; + +@KubernetesDependent(labelSelector = UIService.LABEL_SELECTOR, resourceDiscriminator = UIServiceDiscriminator.class) +@ApplicationScoped +public class UIService extends CRUDKubernetesDependentResource { + + public static final String LABEL_SELECTOR = "app.kubernetes.io/managed-by=trustify-operator,component=ui"; + + public UIService() { + super(Service.class); + } + + @Override + public Service desired(Trustify cr, Context context) { + return newService(cr, context); + } + + @SuppressWarnings("unchecked") + private Service newService(Trustify cr, Context context) { + final var labels = (Map) context.managedDependentResourceContext() + .getMandatory(Constants.CONTEXT_LABELS_KEY, Map.class); + + return new ServiceBuilder() + .withNewMetadata() + .withName(getServiceName(cr)) + .withNamespace(cr.getMetadata().getNamespace()) + .withLabels(labels) + .addToLabels("component", "ui") + .withOwnerReferences(CRDUtils.getOwnerReference(cr)) + .endMetadata() + .withSpec(getServiceSpec(cr)) + .build(); + } + + private ServiceSpec getServiceSpec(Trustify cr) { + return new ServiceSpecBuilder() + .withPorts( + new ServicePortBuilder() + .withName("http") + .withPort(getServicePort(cr)) + .withProtocol(Constants.SERVICE_PROTOCOL) + .build() + ) + .withSelector(Constants.SERVER_SELECTOR_LABELS) + .withType("ClusterIP") + .build(); + } + + public static int getServicePort(Trustify cr) { + return Constants.HTTP_PORT; + } + + public static String getServiceName(Trustify cr) { + return cr.getMetadata().getName() + Constants.UI_SERVICE_SUFFIX; + } + +} diff --git a/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIServiceDiscriminator.java b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIServiceDiscriminator.java new file mode 100644 index 0000000..8c54c47 --- /dev/null +++ b/src/main/java/org/trustify/operator/cdrs/v2alpha1/ui/UIServiceDiscriminator.java @@ -0,0 +1,21 @@ +package org.trustify.operator.cdrs.v2alpha1.ui; + +import io.fabric8.kubernetes.api.model.Service; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; +import org.trustify.operator.cdrs.v2alpha1.Trustify; +import org.trustify.operator.controllers.TrustifyReconciler; + +import java.util.Optional; + +public class UIServiceDiscriminator implements ResourceDiscriminator { + @Override + public Optional distinguish(Class resource, Trustify cr, Context context) { + String serviceName = UIService.getServiceName(cr); + ResourceID resourceID = new ResourceID(serviceName, cr.getMetadata().getNamespace()); + var informerEventSource = (InformerEventSource) context.eventSourceRetriever().getResourceEventSourceFor(Service.class, TrustifyReconciler.SERVICE_EVENT_SOURCE); + return informerEventSource.get(resourceID); + } +} \ No newline at end of file diff --git a/src/main/java/org/trustify/operator/controllers/TrustifyReconciler.java b/src/main/java/org/trustify/operator/controllers/TrustifyReconciler.java index bcbd157..27ae1fd 100644 --- a/src/main/java/org/trustify/operator/controllers/TrustifyReconciler.java +++ b/src/main/java/org/trustify/operator/controllers/TrustifyReconciler.java @@ -12,8 +12,10 @@ import org.trustify.operator.cdrs.v2alpha1.TrustifyStatusCondition; import org.trustify.operator.cdrs.v2alpha1.db.*; import org.trustify.operator.cdrs.v2alpha1.server.ServerDeployment; -import org.trustify.operator.cdrs.v2alpha1.server.ServerIngress; +import org.trustify.operator.cdrs.v2alpha1.ui.UIDeployment; +import org.trustify.operator.cdrs.v2alpha1.ui.UIIngress; import org.trustify.operator.cdrs.v2alpha1.server.ServerService; +import org.trustify.operator.cdrs.v2alpha1.ui.UIService; import java.time.Duration; import java.util.Map; @@ -44,29 +46,36 @@ @Dependent( name = "db-service", type = DBService.class, - dependsOn = {"db-deployment"}, activationCondition = DBServiceActivationCondition.class ), @Dependent( name = "server-deployment", type = ServerDeployment.class, -// dependsOn = {"db-service"}, readyPostcondition = ServerDeployment.class, - useEventSourceWithName = "server-deployment" + useEventSourceWithName = TrustifyReconciler.DEPLOYMENT_EVENT_SOURCE ), @Dependent( name = "server-service", type = ServerService.class, - dependsOn = {"server-deployment"}, - useEventSourceWithName = "server-service" + useEventSourceWithName = TrustifyReconciler.SERVICE_EVENT_SOURCE ), @Dependent( - name = "ingress", - type = ServerIngress.class, - dependsOn = {"server-service"}, - readyPostcondition = ServerIngress.class + name = "ui-deployment", + type = UIDeployment.class, + readyPostcondition = UIDeployment.class, + useEventSourceWithName = TrustifyReconciler.DEPLOYMENT_EVENT_SOURCE + ), + @Dependent( + name = "ui-service", + type = UIService.class, + useEventSourceWithName = TrustifyReconciler.SERVICE_EVENT_SOURCE + ), + @Dependent( + name = "ui-ingress", + type = UIIngress.class, + readyPostcondition = UIIngress.class ) } ) @@ -74,8 +83,8 @@ public class TrustifyReconciler implements Reconciler, ContextInitiali private static final Logger logger = Logger.getLogger(TrustifyReconciler.class); - public static final String SERVER_DEPLOYMENT_EVENT_SOURCE = "server-deployment"; - public static final String SERVER_SERVICE_EVENT_SOURCE = "server-service"; + public static final String DEPLOYMENT_EVENT_SOURCE = "deploymentSource"; + public static final String SERVICE_EVENT_SOURCE = "serviceSource"; @Override public void initContext(Trustify cr, Context context) { @@ -128,8 +137,8 @@ public Map prepareEventSources(EventSourceContext var serverServiceInformerEventSource = new InformerEventSource<>(serverServiceInformerConfiguration, context); return Map.of( - SERVER_DEPLOYMENT_EVENT_SOURCE, serverDeploymentInformerEventSource, - SERVER_SERVICE_EVENT_SOURCE, serverServiceInformerEventSource + DEPLOYMENT_EVENT_SOURCE, serverDeploymentInformerEventSource, + SERVICE_EVENT_SOURCE, serverServiceInformerEventSource ); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index ee358db..81ea367 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -5,14 +5,17 @@ quarkus.container-image.tag=v${quarkus.application.version} quarkus.operator-sdk.crd.apply=true # Operator config +related.image.ui=${RELATED_IMAGE_UI:ghcr.io/trustification/trustify-ui:latest} related.image.server=${RELATED_IMAGE_SERVER:ghcr.io/trustification/trustd:latest} related.image.db=${RELATED_IMAGE_DB:quay.io/sclorg/postgresql-15-c9s:latest} related.image.pull-policy=Always # https://quarkus.io/guides/deploying-to-kubernetes#environment-variables-from-keyvalue-pairs +quarkus.kubernetes.env.vars.related-image-trustify-ui=${related.image.ui} quarkus.kubernetes.env.vars.related-image-trustify-server=${related.image.server} quarkus.kubernetes.env.vars.related-image-db=${related.image.db} quarkus.kubernetes.env.vars.related-image-importer=${related.image.importer} +quarkus.openshift.env.vars.related-image-trustify-ui=${related.image.ui} quarkus.openshift.env.vars.related-image-trustify-server=${related.image.server} quarkus.openshift.env.vars.related-image-db=${related.image.db} quarkus.openshift.env.vars.related-image-importer=${related.image.importer} diff --git a/src/test/java/org/trustify/operator/controllers/TrustifyReconcilerTest.java b/src/test/java/org/trustify/operator/controllers/TrustifyReconcilerTest.java index c6ba426..a6263cf 100644 --- a/src/test/java/org/trustify/operator/controllers/TrustifyReconcilerTest.java +++ b/src/test/java/org/trustify/operator/controllers/TrustifyReconcilerTest.java @@ -14,10 +14,12 @@ import org.trustify.operator.Constants; import org.trustify.operator.cdrs.v2alpha1.*; import org.trustify.operator.cdrs.v2alpha1.server.ServerDeployment; -import org.trustify.operator.cdrs.v2alpha1.server.ServerIngress; +import org.trustify.operator.cdrs.v2alpha1.ui.UIDeployment; +import org.trustify.operator.cdrs.v2alpha1.ui.UIIngress; import org.trustify.operator.cdrs.v2alpha1.server.ServerService; import org.trustify.operator.cdrs.v2alpha1.db.DBDeployment; import org.trustify.operator.cdrs.v2alpha1.db.DBService; +import org.trustify.operator.cdrs.v2alpha1.ui.UIService; import org.trustify.operator.controllers.setup.K3sResource; import java.util.List; @@ -32,6 +34,9 @@ public class TrustifyReconcilerTest { @ConfigProperty(name = "related.image.db") String dbImage; + @ConfigProperty(name = "related.image.ui") + String uiImage; + @ConfigProperty(name = "related.image.server") String serverImage; @@ -129,18 +134,18 @@ public void reconcileShouldWork() throws InterruptedException { .inNamespace(metadata.getNamespace()) .withName(ServerDeployment.getDeploymentName(app)) .get(); - final var webContainer = serverDeployment.getSpec() + final var serverContainer = serverDeployment.getSpec() .getTemplate() .getSpec() .getContainers() .stream() .findFirst(); - MatcherAssert.assertThat("Server container not found", webContainer.isPresent(), Matchers.is(true)); - MatcherAssert.assertThat("Server container image not valid", webContainer.get().getImage(), Matchers.is(serverImage)); - List webContainerPorts = webContainer.get().getPorts().stream() + MatcherAssert.assertThat("Server container not found", serverContainer.isPresent(), Matchers.is(true)); + MatcherAssert.assertThat("Server container image not valid", serverContainer.get().getImage(), Matchers.is(serverImage)); + List serverContainerPorts = serverContainer.get().getPorts().stream() .map(ContainerPort::getContainerPort) .toList(); - Assertions.assertTrue(webContainerPorts.contains(8080), "Server container port 8080 not found"); + Assertions.assertTrue(serverContainerPorts.contains(8080), "Server container port 8080 not found"); Assertions.assertEquals(1, serverDeployment.getStatus().getAvailableReplicas(), "Expected Server deployment number of replicas doesn't match"); @@ -156,10 +161,44 @@ public void reconcileShouldWork() throws InterruptedException { .toList(); Assertions.assertTrue(serverServicePorts.contains(8080), "Server service port not valid"); + // UI Deployment + final var uiDeployment = client.apps() + .deployments() + .inNamespace(metadata.getNamespace()) + .withName(UIDeployment.getDeploymentName(app)) + .get(); + final var uiContainer = uiDeployment.getSpec() + .getTemplate() + .getSpec() + .getContainers() + .stream() + .findFirst(); + MatcherAssert.assertThat("UI container not found", uiContainer.isPresent(), Matchers.is(true)); + MatcherAssert.assertThat("UI container image not valid", uiContainer.get().getImage(), Matchers.is(uiImage)); + List uiContainerPorts = uiContainer.get().getPorts().stream() + .map(ContainerPort::getContainerPort) + .toList(); + Assertions.assertTrue(uiContainerPorts.contains(8080), "UI container port 8080 not found"); + + Assertions.assertEquals(1, uiDeployment.getStatus().getAvailableReplicas(), "Expected UI deployment number of replicas doesn't match"); + + // Server service + final var uiService = client.services() + .inNamespace(metadata.getNamespace()) + .withName(UIService.getServiceName(app)) + .get(); + final var uiServicePorts = uiService.getSpec() + .getPorts() + .stream() + .map(ServicePort::getPort) + .toList(); + Assertions.assertTrue(uiServicePorts.contains(8080), "UI service port not valid"); + + // Ingress final var ingress = client.network().v1().ingresses() .inNamespace(metadata.getNamespace()) - .withName(ServerIngress.getIngressName(app)) + .withName(UIIngress.getIngressName(app)) .get(); final var rules = ingress.getSpec().getRules(); @@ -171,7 +210,7 @@ public void reconcileShouldWork() throws InterruptedException { final var path = paths.get(0); final var serviceBackend = path.getBackend().getService(); - MatcherAssert.assertThat(serviceBackend.getName(), Matchers.is(ServerService.getServiceName(app))); + MatcherAssert.assertThat(serviceBackend.getName(), Matchers.is(UIService.getServiceName(app))); MatcherAssert.assertThat(serviceBackend.getPort().getNumber(), Matchers.is(8080)); }); }