From 4f0039537c7ecc2e3ba1f63e2a53bb699318da6f Mon Sep 17 00:00:00 2001 From: Eric Wittmann Date: Mon, 27 Jan 2025 13:32:22 -0500 Subject: [PATCH 1/5] Add support for managed NetworkPolicy in operator --- .../operator/ApicurioRegistry3Reconciler.java | 53 ++++++++++++++++-- .../resource/ActivationConditions.java | 33 +++++++++++ .../resource/LabelDiscriminators.java | 43 ++++++++++++++ .../operator/resource/ResourceFactory.java | 26 +++++++++ .../operator/resource/ResourceKey.java | 19 +++++++ .../app/AppNetworkPolicyResource.java | 37 ++++++++++++ .../StudioUINetworkPolicyResource.java | 37 ++++++++++++ .../resource/ui/UINetworkPolicyResource.java | 37 ++++++++++++ .../k8s/default/app.networkpolicy.yaml | 10 ++++ .../k8s/default/studio-ui.networkpolicy.yaml | 10 ++++ .../k8s/default/ui.networkpolicy.yaml | 10 ++++ .../apicurio/registry/operator/it/ITBase.java | 39 ++++++++++++- .../operator/it/NetworkPolicyITTest.java | 56 +++++++++++++++++++ operator/install/install.yaml | 17 +++++- .../operator/api/v1/spec/ComponentSpec.java | 13 ++++- 15 files changed, 433 insertions(+), 7 deletions(-) create mode 100644 operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppNetworkPolicyResource.java create mode 100644 operator/controller/src/main/java/io/apicurio/registry/operator/resource/studioui/StudioUINetworkPolicyResource.java create mode 100644 operator/controller/src/main/java/io/apicurio/registry/operator/resource/ui/UINetworkPolicyResource.java create mode 100644 operator/controller/src/main/resources/k8s/default/app.networkpolicy.yaml create mode 100644 operator/controller/src/main/resources/k8s/default/studio-ui.networkpolicy.yaml create mode 100644 operator/controller/src/main/resources/k8s/default/ui.networkpolicy.yaml create mode 100644 operator/controller/src/test/java/io/apicurio/registry/operator/it/NetworkPolicyITTest.java diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/ApicurioRegistry3Reconciler.java b/operator/controller/src/main/java/io/apicurio/registry/operator/ApicurioRegistry3Reconciler.java index 4b2cd4e2d0..0cbc655607 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/ApicurioRegistry3Reconciler.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/ApicurioRegistry3Reconciler.java @@ -4,24 +4,51 @@ import io.apicurio.registry.operator.resource.LabelDiscriminators.AppDeploymentDiscriminator; import io.apicurio.registry.operator.resource.app.AppDeploymentResource; import io.apicurio.registry.operator.resource.app.AppIngressResource; +import io.apicurio.registry.operator.resource.app.AppNetworkPolicyResource; import io.apicurio.registry.operator.resource.app.AppServiceResource; import io.apicurio.registry.operator.resource.studioui.StudioUIDeploymentResource; import io.apicurio.registry.operator.resource.studioui.StudioUIIngressResource; +import io.apicurio.registry.operator.resource.studioui.StudioUINetworkPolicyResource; import io.apicurio.registry.operator.resource.studioui.StudioUIServiceResource; import io.apicurio.registry.operator.resource.ui.UIDeploymentResource; import io.apicurio.registry.operator.resource.ui.UIIngressResource; +import io.apicurio.registry.operator.resource.ui.UINetworkPolicyResource; import io.apicurio.registry.operator.resource.ui.UIServiceResource; import io.apicurio.registry.operator.updater.IngressCRUpdater; import io.apicurio.registry.operator.updater.KafkaSqlCRUpdater; import io.apicurio.registry.operator.updater.SqlCRUpdater; import io.fabric8.kubernetes.api.model.apps.Deployment; -import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.api.reconciler.Cleaner; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.DeleteControl; +import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusHandler; +import io.javaoperatorsdk.operator.api.reconciler.ErrorStatusUpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.apicurio.registry.operator.resource.ActivationConditions.*; -import static io.apicurio.registry.operator.resource.ResourceKey.*; +import static io.apicurio.registry.operator.resource.ActivationConditions.AppIngressActivationCondition; +import static io.apicurio.registry.operator.resource.ActivationConditions.AppNetworkPolicyActivationCondition; +import static io.apicurio.registry.operator.resource.ActivationConditions.StudioUIDeploymentActivationCondition; +import static io.apicurio.registry.operator.resource.ActivationConditions.StudioUIIngressActivationCondition; +import static io.apicurio.registry.operator.resource.ActivationConditions.StudioUINetworkPolicyActivationCondition; +import static io.apicurio.registry.operator.resource.ActivationConditions.UIIngressActivationCondition; +import static io.apicurio.registry.operator.resource.ActivationConditions.UINetworkPolicyActivationCondition; +import static io.apicurio.registry.operator.resource.ResourceKey.APP_DEPLOYMENT_ID; +import static io.apicurio.registry.operator.resource.ResourceKey.APP_INGRESS_ID; +import static io.apicurio.registry.operator.resource.ResourceKey.APP_NETWORK_POLICY_ID; +import static io.apicurio.registry.operator.resource.ResourceKey.APP_SERVICE_ID; +import static io.apicurio.registry.operator.resource.ResourceKey.STUDIO_UI_DEPLOYMENT_ID; +import static io.apicurio.registry.operator.resource.ResourceKey.STUDIO_UI_INGRESS_ID; +import static io.apicurio.registry.operator.resource.ResourceKey.STUDIO_UI_NETWORK_POLICY_ID; +import static io.apicurio.registry.operator.resource.ResourceKey.STUDIO_UI_SERVICE_ID; +import static io.apicurio.registry.operator.resource.ResourceKey.UI_DEPLOYMENT_ID; +import static io.apicurio.registry.operator.resource.ResourceKey.UI_INGRESS_ID; +import static io.apicurio.registry.operator.resource.ResourceKey.UI_NETWORK_POLICY_ID; +import static io.apicurio.registry.operator.resource.ResourceKey.UI_SERVICE_ID; // spotless:off @ControllerConfiguration( @@ -42,6 +69,12 @@ dependsOn = {APP_SERVICE_ID}, activationCondition = AppIngressActivationCondition.class ), + @Dependent( + type = AppNetworkPolicyResource.class, + name = APP_NETWORK_POLICY_ID, + dependsOn = {APP_DEPLOYMENT_ID}, + activationCondition = AppNetworkPolicyActivationCondition.class + ), // ===== Registry UI @Dependent( type = UIDeploymentResource.class, @@ -58,6 +91,12 @@ dependsOn = {UI_SERVICE_ID}, activationCondition = UIIngressActivationCondition.class ), + @Dependent( + type = UINetworkPolicyResource.class, + name = UI_NETWORK_POLICY_ID, + dependsOn = {UI_DEPLOYMENT_ID}, + activationCondition = UINetworkPolicyActivationCondition.class + ), // ===== Studio UI @Dependent( type = StudioUIDeploymentResource.class, @@ -74,7 +113,13 @@ name = STUDIO_UI_INGRESS_ID, dependsOn = {STUDIO_UI_SERVICE_ID}, activationCondition = StudioUIIngressActivationCondition.class - ) + ), + @Dependent( + type = StudioUINetworkPolicyResource.class, + name = STUDIO_UI_NETWORK_POLICY_ID, + dependsOn = {STUDIO_UI_DEPLOYMENT_ID}, + activationCondition = StudioUINetworkPolicyActivationCondition.class + ), } ) // spotless:on diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java index 476c110bec..d81ea990f5 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java @@ -3,6 +3,7 @@ import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; import io.apicurio.registry.operator.api.v1.ApicurioRegistry3Spec; import io.apicurio.registry.operator.api.v1.spec.AppSpec; +import io.apicurio.registry.operator.api.v1.spec.ComponentSpec; import io.apicurio.registry.operator.api.v1.spec.IngressSpec; import io.apicurio.registry.operator.api.v1.spec.StudioUiSpec; import io.apicurio.registry.operator.api.v1.spec.UiSpec; @@ -12,6 +13,7 @@ import io.apicurio.registry.operator.resource.ui.UIIngressResource; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; +import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; @@ -42,6 +44,16 @@ public boolean isMet(DependentResource resource, } } + public static class AppNetworkPolicyActivationCondition + implements Condition { + @Override + public boolean isMet(DependentResource resource, + ApicurioRegistry3 primary, Context context) { + return ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getApp) + .map(ComponentSpec::getManageNetworkPolicy).orElse(Boolean.TRUE); + } + } + // ===== Registry UI public static class UIIngressActivationCondition implements Condition { @@ -60,6 +72,16 @@ public boolean isMet(DependentResource resource, } } + public static class UINetworkPolicyActivationCondition + implements Condition { + @Override + public boolean isMet(DependentResource resource, + ApicurioRegistry3 primary, Context context) { + return ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getUi) + .map(ComponentSpec::getManageNetworkPolicy).orElse(Boolean.TRUE); + } + } + // ===== Studio UI public static class StudioUIDeploymentActivationCondition @@ -93,4 +115,15 @@ public boolean isMet(DependentResource resource, return enabled; } } + + public static class StudioUINetworkPolicyActivationCondition + implements Condition { + @Override + public boolean isMet(DependentResource resource, + ApicurioRegistry3 primary, Context context) { + return ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getStudioUi) + .map(ComponentSpec::getManageNetworkPolicy).orElse(Boolean.TRUE); + } + } + } diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/LabelDiscriminators.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/LabelDiscriminators.java index c65c96a806..f76f6b14e8 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/LabelDiscriminators.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/LabelDiscriminators.java @@ -4,6 +4,7 @@ import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; +import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import java.util.Map; @@ -59,6 +60,20 @@ public AppIngressDiscriminator() { } } + public static class AppNetworkPolicyDiscriminator extends LabelDiscriminator { + + public static final ResourceDiscriminator INSTANCE = new AppNetworkPolicyDiscriminator(); + + public AppNetworkPolicyDiscriminator() { + // spotless:off + super(Map.of( + "app.kubernetes.io/name", "apicurio-registry", + "app.kubernetes.io/component", COMPONENT_APP + )); + // spotless:on + } + } + // ===== Registry UI public static class UIDeploymentDiscriminator extends LabelDiscriminator { @@ -103,6 +118,20 @@ public UIIngressDiscriminator() { } } + public static class UINetworkPolicyDiscriminator extends LabelDiscriminator { + + public static final ResourceDiscriminator INSTANCE = new AppNetworkPolicyDiscriminator(); + + public UINetworkPolicyDiscriminator() { + // spotless:off + super(Map.of( + "app.kubernetes.io/name", "apicurio-registry", + "app.kubernetes.io/component", COMPONENT_UI + )); + // spotless:on + } + } + // ===== Studio UI public static class StudioUIDeploymentDiscriminator extends LabelDiscriminator { @@ -146,4 +175,18 @@ public StudioUIIngressDiscriminator() { // spotless:on } } + + public static class StudioUINetworkPolicyDiscriminator extends LabelDiscriminator { + + public static final ResourceDiscriminator INSTANCE = new AppNetworkPolicyDiscriminator(); + + public StudioUINetworkPolicyDiscriminator() { + // spotless:off + super(Map.of( + "app.kubernetes.io/name", "apicurio-registry", + "app.kubernetes.io/component", COMPONENT_STUDIO_UI + )); + // spotless:on + } + } } diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceFactory.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceFactory.java index dd65faaab2..3cea5abf73 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceFactory.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceFactory.java @@ -12,6 +12,7 @@ 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 io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; import java.nio.charset.Charset; import java.util.ArrayList; @@ -37,6 +38,7 @@ public class ResourceFactory { public static final String RESOURCE_TYPE_DEPLOYMENT = "deployment"; public static final String RESOURCE_TYPE_SERVICE = "service"; public static final String RESOURCE_TYPE_INGRESS = "ingress"; + public static final String RESOURCE_TYPE_NETWORK_POLICY = "networkpolicy"; public Deployment getDefaultAppDeployment(ApicurioRegistry3 primary) { var r = initDefaultDeployment(primary, COMPONENT_APP, @@ -240,6 +242,30 @@ private T getDefaultResource(ApicurioRegistry3 primary, return r; } + public NetworkPolicy getDefaultAppNetworkPolicy(ApicurioRegistry3 primary) { + var networkPolicy = getDefaultResource(primary, NetworkPolicy.class, RESOURCE_TYPE_NETWORK_POLICY, + COMPONENT_APP); + networkPolicy.getSpec().getPodSelector().getMatchLabels().put("app.kubernetes.io/instance", + primary.getMetadata().getName()); + return networkPolicy; + } + + public NetworkPolicy getDefaultUINetworkPolicy(ApicurioRegistry3 primary) { + var networkPolicy = getDefaultResource(primary, NetworkPolicy.class, RESOURCE_TYPE_NETWORK_POLICY, + COMPONENT_UI); + networkPolicy.getSpec().getPodSelector().getMatchLabels().put("app.kubernetes.io/instance", + primary.getMetadata().getName()); + return networkPolicy; + } + + public NetworkPolicy getDefaultStudioUINetworkPolicy(ApicurioRegistry3 primary) { + var networkPolicy = getDefaultResource(primary, NetworkPolicy.class, RESOURCE_TYPE_NETWORK_POLICY, + COMPONENT_STUDIO_UI); + networkPolicy.getSpec().getPodSelector().getMatchLabels().put("app.kubernetes.io/instance", + primary.getMetadata().getName()); + return networkPolicy; + } + private void addDefaultLabels(Map labels, ApicurioRegistry3 primary, String component) { // spotless:off labels.putAll(Map.of( diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceKey.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceKey.java index c7183689f9..04fe31c385 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceKey.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ResourceKey.java @@ -6,6 +6,7 @@ import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; +import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; @@ -28,14 +29,17 @@ public class ResourceKey { public static final String APP_DEPLOYMENT_ID = "AppDeploymentResource"; public static final String APP_SERVICE_ID = "AppServiceResource"; public static final String APP_INGRESS_ID = "AppIngressResource"; + public static final String APP_NETWORK_POLICY_ID = "AppNetworkPolicyResource"; public static final String UI_DEPLOYMENT_ID = "UIDeploymentResource"; public static final String UI_SERVICE_ID = "UIServiceResource"; public static final String UI_INGRESS_ID = "UIIngressResource"; + public static final String UI_NETWORK_POLICY_ID = "UINetworkPolicyResource"; public static final String STUDIO_UI_DEPLOYMENT_ID = "StudioUIDeploymentResource"; public static final String STUDIO_UI_SERVICE_ID = "StudioUIServiceResource"; public static final String STUDIO_UI_INGRESS_ID = "StudioUIIngressResource"; + public static final String STUDIO_UI_NETWORK_POLICY_ID = "StudioUINetworkPolicyResource"; public static final ResourceKey REGISTRY_KEY = new ResourceKey<>( REGISTRY_ID, ApicurioRegistry3.class, @@ -59,6 +63,11 @@ public class ResourceKey { AppIngressDiscriminator.INSTANCE, ResourceFactory.INSTANCE::getDefaultAppIngress ); + public static final ResourceKey APP_NETWORK_POLICY_KEY = new ResourceKey<>( + APP_NETWORK_POLICY_ID, NetworkPolicy.class, + AppNetworkPolicyDiscriminator.INSTANCE, ResourceFactory.INSTANCE::getDefaultAppNetworkPolicy + ); + // ===== Registry UI public static final ResourceKey UI_DEPLOYMENT_KEY = new ResourceKey<>( @@ -76,6 +85,11 @@ public class ResourceKey { UIIngressDiscriminator.INSTANCE, ResourceFactory.INSTANCE::getDefaultUIIngress ); + public static final ResourceKey UI_NETWORK_POLICY_KEY = new ResourceKey<>( + UI_NETWORK_POLICY_ID, NetworkPolicy.class, + UINetworkPolicyDiscriminator.INSTANCE, ResourceFactory.INSTANCE::getDefaultUINetworkPolicy + ); + // ===== Studio UI public static final ResourceKey STUDIO_UI_DEPLOYMENT_KEY = new ResourceKey<>( @@ -93,6 +107,11 @@ public class ResourceKey { StudioUIIngressDiscriminator.INSTANCE, ResourceFactory.INSTANCE::getDefaultStudioUIIngress ); + public static final ResourceKey STUDIO_UI_NETWORK_POLICY_KEY = new ResourceKey<>( + STUDIO_UI_NETWORK_POLICY_ID, NetworkPolicy.class, + StudioUINetworkPolicyDiscriminator.INSTANCE, ResourceFactory.INSTANCE::getDefaultStudioUINetworkPolicy + ); + // spotless:on @EqualsAndHashCode.Include diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppNetworkPolicyResource.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppNetworkPolicyResource.java new file mode 100644 index 0000000000..8514070499 --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/app/AppNetworkPolicyResource.java @@ -0,0 +1,37 @@ +package io.apicurio.registry.operator.resource.app; + +import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; +import io.apicurio.registry.operator.resource.LabelDiscriminators.AppNetworkPolicyDiscriminator; +import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_APP; +import static io.apicurio.registry.operator.resource.ResourceKey.APP_NETWORK_POLICY_KEY; +import static io.apicurio.registry.operator.utils.Mapper.toYAML; + +// spotless:off +@KubernetesDependent( + labelSelector = "app.kubernetes.io/name=apicurio-registry,app.kubernetes.io/component=" + COMPONENT_APP, + resourceDiscriminator = AppNetworkPolicyDiscriminator.class +) +// spotless:on +public class AppNetworkPolicyResource + extends CRUDKubernetesDependentResource { + + private static final Logger log = LoggerFactory.getLogger(AppNetworkPolicyResource.class); + + public AppNetworkPolicyResource() { + super(NetworkPolicy.class); + } + + @Override + protected NetworkPolicy desired(ApicurioRegistry3 primary, Context context) { + var networkPolicy = APP_NETWORK_POLICY_KEY.getFactory().apply(primary); + log.debug("Desired {} is {}", APP_NETWORK_POLICY_KEY.getId(), toYAML(networkPolicy)); + return networkPolicy; + } +} diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/studioui/StudioUINetworkPolicyResource.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/studioui/StudioUINetworkPolicyResource.java new file mode 100644 index 0000000000..efc19eb123 --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/studioui/StudioUINetworkPolicyResource.java @@ -0,0 +1,37 @@ +package io.apicurio.registry.operator.resource.studioui; + +import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; +import io.apicurio.registry.operator.resource.LabelDiscriminators.StudioUINetworkPolicyDiscriminator; +import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_STUDIO_UI; +import static io.apicurio.registry.operator.resource.ResourceKey.STUDIO_UI_NETWORK_POLICY_KEY; +import static io.apicurio.registry.operator.utils.Mapper.toYAML; + +// spotless:off +@KubernetesDependent( + labelSelector = "app.kubernetes.io/name=apicurio-registry,app.kubernetes.io/component=" + COMPONENT_STUDIO_UI, + resourceDiscriminator = StudioUINetworkPolicyDiscriminator.class +) +// spotless:on +public class StudioUINetworkPolicyResource + extends CRUDKubernetesDependentResource { + + private static final Logger log = LoggerFactory.getLogger(StudioUINetworkPolicyResource.class); + + public StudioUINetworkPolicyResource() { + super(NetworkPolicy.class); + } + + @Override + protected NetworkPolicy desired(ApicurioRegistry3 primary, Context context) { + var networkPolicy = STUDIO_UI_NETWORK_POLICY_KEY.getFactory().apply(primary); + log.debug("Desired {} is {}", STUDIO_UI_NETWORK_POLICY_KEY.getId(), toYAML(networkPolicy)); + return networkPolicy; + } +} diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ui/UINetworkPolicyResource.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ui/UINetworkPolicyResource.java new file mode 100644 index 0000000000..20310dee65 --- /dev/null +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ui/UINetworkPolicyResource.java @@ -0,0 +1,37 @@ +package io.apicurio.registry.operator.resource.ui; + +import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; +import io.apicurio.registry.operator.resource.LabelDiscriminators.UINetworkPolicyDiscriminator; +import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static io.apicurio.registry.operator.resource.ResourceFactory.COMPONENT_UI; +import static io.apicurio.registry.operator.resource.ResourceKey.UI_NETWORK_POLICY_KEY; +import static io.apicurio.registry.operator.utils.Mapper.toYAML; + +// spotless:off +@KubernetesDependent( + labelSelector = "app.kubernetes.io/name=apicurio-registry,app.kubernetes.io/component=" + COMPONENT_UI, + resourceDiscriminator = UINetworkPolicyDiscriminator.class +) +// spotless:on +public class UINetworkPolicyResource + extends CRUDKubernetesDependentResource { + + private static final Logger log = LoggerFactory.getLogger(UINetworkPolicyResource.class); + + public UINetworkPolicyResource() { + super(NetworkPolicy.class); + } + + @Override + protected NetworkPolicy desired(ApicurioRegistry3 primary, Context context) { + var networkPolicy = UI_NETWORK_POLICY_KEY.getFactory().apply(primary); + log.debug("Desired {} is {}", UI_NETWORK_POLICY_KEY.getId(), toYAML(networkPolicy)); + return networkPolicy; + } +} diff --git a/operator/controller/src/main/resources/k8s/default/app.networkpolicy.yaml b/operator/controller/src/main/resources/k8s/default/app.networkpolicy.yaml new file mode 100644 index 0000000000..72c8244d79 --- /dev/null +++ b/operator/controller/src/main/resources/k8s/default/app.networkpolicy.yaml @@ -0,0 +1,10 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: { } +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: apicurio-registry + app.kubernetes.io/component: app + policyTypes: + - Ingress diff --git a/operator/controller/src/main/resources/k8s/default/studio-ui.networkpolicy.yaml b/operator/controller/src/main/resources/k8s/default/studio-ui.networkpolicy.yaml new file mode 100644 index 0000000000..00d8e51623 --- /dev/null +++ b/operator/controller/src/main/resources/k8s/default/studio-ui.networkpolicy.yaml @@ -0,0 +1,10 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: { } +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: apicurio-registry + app.kubernetes.io/component: studio-ui + policyTypes: + - Ingress diff --git a/operator/controller/src/main/resources/k8s/default/ui.networkpolicy.yaml b/operator/controller/src/main/resources/k8s/default/ui.networkpolicy.yaml new file mode 100644 index 0000000000..8653ad5b79 --- /dev/null +++ b/operator/controller/src/main/resources/k8s/default/ui.networkpolicy.yaml @@ -0,0 +1,10 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: { } +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: apicurio-registry + app.kubernetes.io/component: ui + policyTypes: + - Ingress diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/ITBase.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/ITBase.java index 46abde619e..27f8589fcd 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/it/ITBase.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/ITBase.java @@ -5,6 +5,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.KubernetesClient; @@ -18,7 +19,11 @@ import jakarta.enterprise.util.TypeLiteral; import org.awaitility.Awaitility; import org.eclipse.microprofile.config.ConfigProvider; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -151,6 +156,19 @@ protected static void checkIngressDoesNotExist(ApicurioRegistry3 primary, String }); } + protected static NetworkPolicy checkNetworkPolicyExists(ApicurioRegistry3 primary, String component) { + final ValueOrNull rval = new ValueOrNull<>(); + + await().ignoreExceptions().untilAsserted(() -> { + NetworkPolicy networkPolicy = client.network().v1().networkPolicies() + .withName(primary.getMetadata().getName() + "-" + component + "-networkpolicy").get(); + assertThat(networkPolicy).isNotNull(); + rval.setValue(networkPolicy); + }); + + return rval.getValue(); + } + static KubernetesClient createK8sClient(String namespace) { return new KubernetesClientBuilder() .withConfig(new ConfigBuilder(Config.autoConfigure(null)).withNamespace(namespace).build()) @@ -267,4 +285,23 @@ public static void after() throws Exception { } client.close(); } + + private static class ValueOrNull { + private T value; + + ValueOrNull() { + } + + ValueOrNull(T value) { + this.value = value; + } + + public void setValue(T value) { + this.value = value; + } + + public T getValue() { + return value; + } + } } diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/NetworkPolicyITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/NetworkPolicyITTest.java new file mode 100644 index 0000000000..435f856e97 --- /dev/null +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/NetworkPolicyITTest.java @@ -0,0 +1,56 @@ +package io.apicurio.registry.operator.it; + +import io.apicurio.registry.operator.api.v1.ApicurioRegistry3; +import io.apicurio.registry.operator.resource.ResourceFactory; +import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; +import io.quarkus.test.junit.QuarkusTest; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.stream.Collectors; + +@QuarkusTest +public class NetworkPolicyITTest extends ITBase { + + private static final Logger log = LoggerFactory.getLogger(NetworkPolicyITTest.class); + + @Test + void testNetworkPolicy() { + ApicurioRegistry3 registry = ResourceFactory + .deserialize("/k8s/examples/simple.apicurioregistry3.yaml", ApicurioRegistry3.class); + client.resource(registry).create(); + + // Wait for the deployment to exist + checkDeploymentExists(registry, ResourceFactory.COMPONENT_APP, 1); + + // Check that the two expected NetworkPolicy resources were created + NetworkPolicy appPolicy = checkNetworkPolicyExists(registry, ResourceFactory.COMPONENT_APP); + NetworkPolicy uiNetworkPolicy = checkNetworkPolicyExists(registry, ResourceFactory.COMPONENT_UI); + + // Verify the content of the app component's network policy + Assertions + .assertThat(appPolicy.getMetadata().getLabels().entrySet().stream() + .map(l -> l.getKey() + "=" + l.getValue()).collect(Collectors.toSet())) + .contains("app.kubernetes.io/component=app", + "app.kubernetes.io/managed-by=apicurio-registry-operator", + "app.kubernetes.io/name=apicurio-registry"); + Assertions + .assertThat(appPolicy.getSpec().getPodSelector().getMatchLabels().entrySet().stream() + .map(l -> l.getKey() + "=" + l.getValue()).collect(Collectors.toSet())) + .contains("app.kubernetes.io/component=app", "app.kubernetes.io/name=apicurio-registry"); + + // Verify the content of the ui component's network policy + Assertions + .assertThat(uiNetworkPolicy.getMetadata().getLabels().entrySet().stream() + .map(l -> l.getKey() + "=" + l.getValue()).collect(Collectors.toSet())) + .contains("app.kubernetes.io/component=ui", + "app.kubernetes.io/managed-by=apicurio-registry-operator", + "app.kubernetes.io/name=apicurio-registry"); + Assertions + .assertThat(uiNetworkPolicy.getSpec().getPodSelector().getMatchLabels().entrySet().stream() + .map(l -> l.getKey() + "=" + l.getValue()).collect(Collectors.toSet())) + .contains("app.kubernetes.io/component=ui", "app.kubernetes.io/name=apicurio-registry"); + } +} \ No newline at end of file diff --git a/operator/install/install.yaml b/operator/install/install.yaml index a1be0c3854..5aa89e45ed 100644 --- a/operator/install/install.yaml +++ b/operator/install/install.yaml @@ -117,6 +117,11 @@ spec: automatically.' type: string type: object + manageNetworkPolicy: + description: | + Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. + Set this to 'false' if you want to create your own custom NetworkPolicy. + type: boolean podTemplateSpec: description: |- `PodTemplateSpec` describes the data a pod should have when created from a template. @@ -3254,6 +3259,11 @@ spec: IMPORTANT: If the Ingress already exists and the value becomes empty, the Ingress will be deleted. type: string type: object + manageNetworkPolicy: + description: | + Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. + Set this to 'false' if you want to create your own custom NetworkPolicy. + type: boolean podTemplateSpec: description: |- `PodTemplateSpec` describes the data a pod should have when created from a template. @@ -6243,6 +6253,11 @@ spec: IMPORTANT: If the Ingress already exists and the value becomes empty, the Ingress will be deleted. type: string type: object + manageNetworkPolicy: + description: | + Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. + Set this to 'false' if you want to create your own custom NetworkPolicy. + type: boolean podTemplateSpec: description: |- `PodTemplateSpec` describes the data a pod should have when created from a template. @@ -9362,7 +9377,7 @@ spec: value: quay.io/apicurio/apicurio-registry-ui:latest-snapshot - name: STUDIO_UI_IMAGE value: quay.io/apicurio/apicurio-studio-ui:latest-snapshot - image: quay.io/apicurio/apicurio-registry-operator:latest-snapshot + image: quay.io/apicurio/apicurio-registry-operator:3.0.7-snapshot imagePullPolicy: Always livenessProbe: httpGet: diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/ComponentSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/ComponentSpec.java index c29c2107d9..4ce20c0fc4 100644 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/ComponentSpec.java +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/ComponentSpec.java @@ -19,7 +19,7 @@ @JsonDeserialize(using = None.class) @JsonInclude(NON_NULL) -@JsonPropertyOrder({ "env", "ingress", "host", "podTemplateSpec" }) +@JsonPropertyOrder({ "env", "ingress", "host", "podTemplateSpec", "manageNetworkPolicy" }) @NoArgsConstructor(access = PROTECTED) @AllArgsConstructor(access = PROTECTED) @SuperBuilder(toBuilder = true) @@ -92,6 +92,17 @@ public abstract class ComponentSpec { @JsonSetter(nulls = Nulls.SKIP) private Integer replicas; + /** + * Indicates whether to create a pod disruption budget + */ + @JsonProperty("manageNetworkPolicy") + @JsonPropertyDescription(""" + Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. + Set this to 'false' if you want to create your own custom NetworkPolicy. + """) + @JsonSetter(nulls = Nulls.SKIP) + private Boolean manageNetworkPolicy; + public IngressSpec withIngress() { if (ingress == null) { ingress = new IngressSpec(); From cd6430b4bdadb7d092810a36090f6c6a2913cb2d Mon Sep 17 00:00:00 2001 From: Eric Wittmann Date: Mon, 27 Jan 2025 14:03:37 -0500 Subject: [PATCH 2/5] Delete NetworkPolicy resource in activation condition --- .../resource/ActivationConditions.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java index d81ea990f5..bef9b18bd2 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java @@ -8,9 +8,12 @@ import io.apicurio.registry.operator.api.v1.spec.StudioUiSpec; import io.apicurio.registry.operator.api.v1.spec.UiSpec; import io.apicurio.registry.operator.resource.app.AppIngressResource; +import io.apicurio.registry.operator.resource.app.AppNetworkPolicyResource; import io.apicurio.registry.operator.resource.studioui.StudioUIDeploymentResource; import io.apicurio.registry.operator.resource.studioui.StudioUIIngressResource; +import io.apicurio.registry.operator.resource.studioui.StudioUINetworkPolicyResource; import io.apicurio.registry.operator.resource.ui.UIIngressResource; +import io.apicurio.registry.operator.resource.ui.UINetworkPolicyResource; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; @@ -49,8 +52,12 @@ public static class AppNetworkPolicyActivationCondition @Override public boolean isMet(DependentResource resource, ApicurioRegistry3 primary, Context context) { - return ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getApp) + Boolean isManaged = ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getApp) .map(ComponentSpec::getManageNetworkPolicy).orElse(Boolean.TRUE); + if (!isManaged) { + ((AppNetworkPolicyResource) resource).delete(primary, context); + } + return isManaged; } } @@ -77,8 +84,12 @@ public static class UINetworkPolicyActivationCondition @Override public boolean isMet(DependentResource resource, ApicurioRegistry3 primary, Context context) { - return ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getUi) + Boolean isManaged = ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getUi) .map(ComponentSpec::getManageNetworkPolicy).orElse(Boolean.TRUE); + if (!isManaged) { + ((UINetworkPolicyResource) resource).delete(primary, context); + } + return isManaged; } } @@ -121,8 +132,12 @@ public static class StudioUINetworkPolicyActivationCondition @Override public boolean isMet(DependentResource resource, ApicurioRegistry3 primary, Context context) { - return ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getStudioUi) + Boolean isManaged = ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getStudioUi) .map(ComponentSpec::getManageNetworkPolicy).orElse(Boolean.TRUE); + if (!isManaged) { + ((StudioUINetworkPolicyResource) resource).delete(primary, context); + } + return isManaged; } } From d52e4c20fef90f5e6f1e1b119674aa5e3e078a33 Mon Sep 17 00:00:00 2001 From: Jakub Senko Date: Wed, 29 Jan 2025 13:58:19 +0100 Subject: [PATCH 3/5] fix(operator): missing RBAC for NetworkPolicy --- .../controller/src/main/deploy/rbac/cluster/cluster-role.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/operator/controller/src/main/deploy/rbac/cluster/cluster-role.yaml b/operator/controller/src/main/deploy/rbac/cluster/cluster-role.yaml index 5db5b96b24..be03887dcc 100644 --- a/operator/controller/src/main/deploy/rbac/cluster/cluster-role.yaml +++ b/operator/controller/src/main/deploy/rbac/cluster/cluster-role.yaml @@ -47,5 +47,6 @@ rules: - networking.k8s.io resources: - ingresses + - networkpolicies verbs: - "*" From 2242c4df73e95791aaca1539fcbda76eaae86d6f Mon Sep 17 00:00:00 2001 From: Eric Wittmann Date: Wed, 29 Jan 2025 14:07:13 -0500 Subject: [PATCH 4/5] Apply PR feedback --- .../resource/ActivationConditions.java | 10 ++-- .../operator/it/NetworkPolicyITTest.java | 53 +++++++++++-------- operator/install/install.yaml | 49 +++++++++++------ .../operator/api/v1/spec/ComponentSpec.java | 25 ++++++--- .../api/v1/spec/NetworkPolicySpec.java | 46 ++++++++++++++++ 5 files changed, 135 insertions(+), 48 deletions(-) create mode 100644 operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/NetworkPolicySpec.java diff --git a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java index bef9b18bd2..81469f2851 100644 --- a/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java +++ b/operator/controller/src/main/java/io/apicurio/registry/operator/resource/ActivationConditions.java @@ -5,6 +5,7 @@ import io.apicurio.registry.operator.api.v1.spec.AppSpec; import io.apicurio.registry.operator.api.v1.spec.ComponentSpec; import io.apicurio.registry.operator.api.v1.spec.IngressSpec; +import io.apicurio.registry.operator.api.v1.spec.NetworkPolicySpec; import io.apicurio.registry.operator.api.v1.spec.StudioUiSpec; import io.apicurio.registry.operator.api.v1.spec.UiSpec; import io.apicurio.registry.operator.resource.app.AppIngressResource; @@ -53,7 +54,8 @@ public static class AppNetworkPolicyActivationCondition public boolean isMet(DependentResource resource, ApicurioRegistry3 primary, Context context) { Boolean isManaged = ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getApp) - .map(ComponentSpec::getManageNetworkPolicy).orElse(Boolean.TRUE); + .map(ComponentSpec::getNetworkPolicy).map(NetworkPolicySpec::getEnabled) + .orElse(Boolean.TRUE); if (!isManaged) { ((AppNetworkPolicyResource) resource).delete(primary, context); } @@ -85,7 +87,8 @@ public static class UINetworkPolicyActivationCondition public boolean isMet(DependentResource resource, ApicurioRegistry3 primary, Context context) { Boolean isManaged = ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getUi) - .map(ComponentSpec::getManageNetworkPolicy).orElse(Boolean.TRUE); + .map(ComponentSpec::getNetworkPolicy).map(NetworkPolicySpec::getEnabled) + .orElse(Boolean.TRUE); if (!isManaged) { ((UINetworkPolicyResource) resource).delete(primary, context); } @@ -133,7 +136,8 @@ public static class StudioUINetworkPolicyActivationCondition public boolean isMet(DependentResource resource, ApicurioRegistry3 primary, Context context) { Boolean isManaged = ofNullable(primary.getSpec()).map(ApicurioRegistry3Spec::getStudioUi) - .map(ComponentSpec::getManageNetworkPolicy).orElse(Boolean.TRUE); + .map(ComponentSpec::getNetworkPolicy).map(NetworkPolicySpec::getEnabled) + .orElse(Boolean.TRUE); if (!isManaged) { ((StudioUINetworkPolicyResource) resource).delete(primary, context); } diff --git a/operator/controller/src/test/java/io/apicurio/registry/operator/it/NetworkPolicyITTest.java b/operator/controller/src/test/java/io/apicurio/registry/operator/it/NetworkPolicyITTest.java index 435f856e97..8253b1a79b 100644 --- a/operator/controller/src/test/java/io/apicurio/registry/operator/it/NetworkPolicyITTest.java +++ b/operator/controller/src/test/java/io/apicurio/registry/operator/it/NetworkPolicyITTest.java @@ -9,6 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Map; import java.util.stream.Collectors; @QuarkusTest @@ -18,8 +19,8 @@ public class NetworkPolicyITTest extends ITBase { @Test void testNetworkPolicy() { - ApicurioRegistry3 registry = ResourceFactory - .deserialize("/k8s/examples/simple.apicurioregistry3.yaml", ApicurioRegistry3.class); + ApicurioRegistry3 registry = ResourceFactory.deserialize( + "/k8s/examples/simple-with-studio.apicurioregistry3.yaml", ApicurioRegistry3.class); client.resource(registry).create(); // Wait for the deployment to exist @@ -28,29 +29,37 @@ void testNetworkPolicy() { // Check that the two expected NetworkPolicy resources were created NetworkPolicy appPolicy = checkNetworkPolicyExists(registry, ResourceFactory.COMPONENT_APP); NetworkPolicy uiNetworkPolicy = checkNetworkPolicyExists(registry, ResourceFactory.COMPONENT_UI); + NetworkPolicy studioNetworkPolicy = checkNetworkPolicyExists(registry, + ResourceFactory.COMPONENT_STUDIO_UI); // Verify the content of the app component's network policy - Assertions - .assertThat(appPolicy.getMetadata().getLabels().entrySet().stream() - .map(l -> l.getKey() + "=" + l.getValue()).collect(Collectors.toSet())) - .contains("app.kubernetes.io/component=app", - "app.kubernetes.io/managed-by=apicurio-registry-operator", - "app.kubernetes.io/name=apicurio-registry"); - Assertions - .assertThat(appPolicy.getSpec().getPodSelector().getMatchLabels().entrySet().stream() - .map(l -> l.getKey() + "=" + l.getValue()).collect(Collectors.toSet())) - .contains("app.kubernetes.io/component=app", "app.kubernetes.io/name=apicurio-registry"); + assertLabelsContains(appPolicy.getMetadata().getLabels(), "app.kubernetes.io/component=app", + "app.kubernetes.io/managed-by=apicurio-registry-operator", + "app.kubernetes.io/name=apicurio-registry"); + assertLabelsContains(appPolicy.getSpec().getPodSelector().getMatchLabels(), + "app.kubernetes.io/component=app", "app.kubernetes.io/name=apicurio-registry", + "app.kubernetes.io/instance=" + registry.getMetadata().getName()); // Verify the content of the ui component's network policy - Assertions - .assertThat(uiNetworkPolicy.getMetadata().getLabels().entrySet().stream() - .map(l -> l.getKey() + "=" + l.getValue()).collect(Collectors.toSet())) - .contains("app.kubernetes.io/component=ui", - "app.kubernetes.io/managed-by=apicurio-registry-operator", - "app.kubernetes.io/name=apicurio-registry"); - Assertions - .assertThat(uiNetworkPolicy.getSpec().getPodSelector().getMatchLabels().entrySet().stream() - .map(l -> l.getKey() + "=" + l.getValue()).collect(Collectors.toSet())) - .contains("app.kubernetes.io/component=ui", "app.kubernetes.io/name=apicurio-registry"); + assertLabelsContains(uiNetworkPolicy.getMetadata().getLabels(), "app.kubernetes.io/component=ui", + "app.kubernetes.io/managed-by=apicurio-registry-operator", + "app.kubernetes.io/name=apicurio-registry"); + assertLabelsContains(uiNetworkPolicy.getSpec().getPodSelector().getMatchLabels(), + "app.kubernetes.io/component=ui", "app.kubernetes.io/name=apicurio-registry", + "app.kubernetes.io/instance=" + registry.getMetadata().getName()); + + // Verify the content of the studio component's network policy + assertLabelsContains(studioNetworkPolicy.getMetadata().getLabels(), + "app.kubernetes.io/component=studio-ui", + "app.kubernetes.io/managed-by=apicurio-registry-operator", + "app.kubernetes.io/name=apicurio-registry"); + assertLabelsContains(studioNetworkPolicy.getSpec().getPodSelector().getMatchLabels(), + "app.kubernetes.io/component=studio-ui", "app.kubernetes.io/name=apicurio-registry", + "app.kubernetes.io/instance=" + registry.getMetadata().getName()); + } + + private void assertLabelsContains(Map labels, String... values) { + Assertions.assertThat(labels.entrySet().stream().map(l -> l.getKey() + "=" + l.getValue()) + .collect(Collectors.toSet())).contains(values); } } \ No newline at end of file diff --git a/operator/install/install.yaml b/operator/install/install.yaml index 5aa89e45ed..020b715941 100644 --- a/operator/install/install.yaml +++ b/operator/install/install.yaml @@ -117,11 +117,17 @@ spec: automatically.' type: string type: object - manageNetworkPolicy: - description: | - Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. - Set this to 'false' if you want to create your own custom NetworkPolicy. - type: boolean + networkPolicy: + description: |2 + Configuration of a NetworkPolicy for the component. + properties: + enabled: + description: | + Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. + + Set this to 'false' if you want to create your own custom NetworkPolicy. + type: boolean + type: object podTemplateSpec: description: |- `PodTemplateSpec` describes the data a pod should have when created from a template. @@ -3259,11 +3265,17 @@ spec: IMPORTANT: If the Ingress already exists and the value becomes empty, the Ingress will be deleted. type: string type: object - manageNetworkPolicy: - description: | - Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. - Set this to 'false' if you want to create your own custom NetworkPolicy. - type: boolean + networkPolicy: + description: |2 + Configuration of a NetworkPolicy for the component. + properties: + enabled: + description: | + Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. + + Set this to 'false' if you want to create your own custom NetworkPolicy. + type: boolean + type: object podTemplateSpec: description: |- `PodTemplateSpec` describes the data a pod should have when created from a template. @@ -6253,11 +6265,17 @@ spec: IMPORTANT: If the Ingress already exists and the value becomes empty, the Ingress will be deleted. type: string type: object - manageNetworkPolicy: - description: | - Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. - Set this to 'false' if you want to create your own custom NetworkPolicy. - type: boolean + networkPolicy: + description: |2 + Configuration of a NetworkPolicy for the component. + properties: + enabled: + description: | + Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. + + Set this to 'false' if you want to create your own custom NetworkPolicy. + type: boolean + type: object podTemplateSpec: description: |- `PodTemplateSpec` describes the data a pod should have when created from a template. @@ -9312,6 +9330,7 @@ rules: - networking.k8s.io resources: - ingresses + - networkpolicies verbs: - '*' --- diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/ComponentSpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/ComponentSpec.java index 4ce20c0fc4..53514581c8 100644 --- a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/ComponentSpec.java +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/ComponentSpec.java @@ -1,12 +1,22 @@ package io.apicurio.registry.operator.api.v1.spec; -import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; import com.fasterxml.jackson.databind.JsonDeserializer.None; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import io.apicurio.registry.operator.api.v1.ContainerNames; import io.fabric8.kubernetes.api.model.EnvVar; import io.fabric8.kubernetes.api.model.PodTemplateSpec; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; import lombok.experimental.SuperBuilder; import java.util.ArrayList; @@ -19,7 +29,7 @@ @JsonDeserialize(using = None.class) @JsonInclude(NON_NULL) -@JsonPropertyOrder({ "env", "ingress", "host", "podTemplateSpec", "manageNetworkPolicy" }) +@JsonPropertyOrder({ "env", "ingress", "host", "podTemplateSpec", "networkPolicy" }) @NoArgsConstructor(access = PROTECTED) @AllArgsConstructor(access = PROTECTED) @SuperBuilder(toBuilder = true) @@ -93,15 +103,14 @@ public abstract class ComponentSpec { private Integer replicas; /** - * Indicates whether to create a pod disruption budget + * Network policy config */ - @JsonProperty("manageNetworkPolicy") + @JsonProperty("networkPolicy") @JsonPropertyDescription(""" - Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. - Set this to 'false' if you want to create your own custom NetworkPolicy. + Configuration of a NetworkPolicy for the component. """) @JsonSetter(nulls = Nulls.SKIP) - private Boolean manageNetworkPolicy; + private NetworkPolicySpec networkPolicy; public IngressSpec withIngress() { if (ingress == null) { diff --git a/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/NetworkPolicySpec.java b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/NetworkPolicySpec.java new file mode 100644 index 0000000000..e2ac24f00f --- /dev/null +++ b/operator/model/src/main/java/io/apicurio/registry/operator/api/v1/spec/NetworkPolicySpec.java @@ -0,0 +1,46 @@ +package io.apicurio.registry.operator.api.v1.spec; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.JsonDeserializer.None; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; +import static lombok.AccessLevel.PRIVATE; + +@JsonDeserialize(using = None.class) +@JsonInclude(NON_NULL) +@JsonPropertyOrder({ "enabled" }) +@NoArgsConstructor +@AllArgsConstructor(access = PRIVATE) +@SuperBuilder(toBuilder = true) +@Getter +@Setter +@EqualsAndHashCode +@ToString +public class NetworkPolicySpec { + + /** + * Indicates whether to create and manage a network policy + */ + @JsonProperty("enabled") + @JsonPropertyDescription(""" + Whether a NetworkPolicy should be managed by the operator. Defaults to 'true'. + + Set this to 'false' if you want to create your own custom NetworkPolicy. + """) + @JsonSetter(nulls = Nulls.SKIP) + private Boolean enabled; + +} \ No newline at end of file From 7ca4ce30344c3a503ae96bce4beec6f7e48b6063 Mon Sep 17 00:00:00 2001 From: Jakub Senko Date: Thu, 30 Jan 2025 11:47:14 +0100 Subject: [PATCH 5/5] chore(operator): update install file --- operator/install/install.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/install/install.yaml b/operator/install/install.yaml index 020b715941..b6c97c6e02 100644 --- a/operator/install/install.yaml +++ b/operator/install/install.yaml @@ -9396,7 +9396,7 @@ spec: value: quay.io/apicurio/apicurio-registry-ui:latest-snapshot - name: STUDIO_UI_IMAGE value: quay.io/apicurio/apicurio-studio-ui:latest-snapshot - image: quay.io/apicurio/apicurio-registry-operator:3.0.7-snapshot + image: quay.io/apicurio/apicurio-registry-operator:latest-snapshot imagePullPolicy: Always livenessProbe: httpGet: