Refine Ingress settings in Keycloak CR

Closes Keycloak#14407

Signed-off-by: Peter Zaoral <pzaoral@redhat.com>
This commit is contained in:
Peter Zaoral 2022-10-15 21:14:58 +02:00 committed by Václav Muzikář
parent 76d9125c3f
commit 4dfbb42680
8 changed files with 102 additions and 34 deletions

View file

@ -164,7 +164,7 @@ CONDITION: RollingUpdate
=== Accessing the Keycloak Deployment === Accessing the Keycloak Deployment
The Keycloak deployment is, by default, exposed through a basic nginx ingress and it will be accessible through the provided hostname. The Keycloak deployment is, by default, exposed through a basic nginx ingress and it will be accessible through the provided hostname.
If the default ingress doesn't fit your use-case you can disable it by setting `disableDefaultIngress: true`: If the default ingress doesn't fit your use-case, disable it by setting `ingress` spec with `enabled` property to `false` value:
[source,bash] [source,bash]
---- ----
@ -175,7 +175,8 @@ metadata:
name: example-kc name: example-kc
spec: spec:
... ...
disableDefaultIngress: true ingress:
enabled: false
EOF EOF
kubectl apply -f example-kc.yaml kubectl apply -f example-kc.yaml
---- ----

View file

@ -21,6 +21,7 @@ import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder;
import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.api.model.networking.v1.Ingress; import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import org.keycloak.operator.Constants; import org.keycloak.operator.Constants;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpec;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak; import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusBuilder; import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusBuilder;
@ -42,7 +43,8 @@ public class KeycloakIngress extends OperatorManagedResource implements StatusUp
@Override @Override
protected Optional<HasMetadata> getReconciledResource() { protected Optional<HasMetadata> getReconciledResource() {
if (keycloak.getSpec().isDisableDefaultIngress()) { IngressSpec ingressSpec = keycloak.getSpec().getIngressSpec();
if (ingressSpec != null && !ingressSpec.isIngressEnabled()) {
if (existingIngress != null) { if (existingIngress != null) {
deleteExistingIngress(); deleteExistingIngress();
} }
@ -121,9 +123,13 @@ public class KeycloakIngress extends OperatorManagedResource implements StatusUp
} }
public void updateStatus(KeycloakStatusBuilder status) { public void updateStatus(KeycloakStatusBuilder status) {
if (!keycloak.getSpec().isDisableDefaultIngress() && existingIngress == null) { IngressSpec ingressSpec = keycloak.getSpec().getIngressSpec();
if (ingressSpec == null) {
ingressSpec = new IngressSpec();
ingressSpec.setIngressEnabled(true);
}
if (ingressSpec.isIngressEnabled() && existingIngress == null) {
status.addNotReadyMessage("No existing Keycloak Ingress found, waiting for creating a new one"); status.addNotReadyMessage("No existing Keycloak Ingress found, waiting for creating a new one");
return;
} }
} }

View file

@ -25,6 +25,7 @@ import org.keycloak.operator.Constants;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.FeatureSpec; import org.keycloak.operator.crds.v2alpha1.deployment.spec.FeatureSpec;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.HttpSpec; import org.keycloak.operator.crds.v2alpha1.deployment.spec.HttpSpec;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.UnsupportedSpec; import org.keycloak.operator.crds.v2alpha1.deployment.spec.UnsupportedSpec;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpec;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.TransactionsSpec; import org.keycloak.operator.crds.v2alpha1.deployment.spec.TransactionsSpec;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@ -54,14 +55,16 @@ public class KeycloakSpec {
@JsonPropertyDescription("In this section you can configure Keycloak features related to HTTP and HTTPS") @JsonPropertyDescription("In this section you can configure Keycloak features related to HTTP and HTTPS")
private HttpSpec httpSpec; private HttpSpec httpSpec;
@JsonPropertyDescription("Disable the default ingress.")
private boolean disableDefaultIngress;
@JsonPropertyDescription( @JsonPropertyDescription(
"In this section you can configure podTemplate advanced features, not production-ready, and not supported settings.\n" + "In this section you can configure podTemplate advanced features, not production-ready, and not supported settings.\n" +
"Use at your own risk and open an issue with your use-case if you don't find an alternative way.") "Use at your own risk and open an issue with your use-case if you don't find an alternative way.")
private UnsupportedSpec unsupported; private UnsupportedSpec unsupported;
@JsonProperty("ingress")
@JsonPropertyDescription("The deployment is, by default, exposed through a basic ingress.\n" +
"You can change this behaviour by setting the enabled property to false.")
private IngressSpec ingressSpec;
@JsonProperty("features") @JsonProperty("features")
@JsonPropertyDescription("In this section you can configure Keycloak features, which should be enabled/disabled.") @JsonPropertyDescription("In this section you can configure Keycloak features, which should be enabled/disabled.")
private FeatureSpec featureSpec; private FeatureSpec featureSpec;
@ -91,14 +94,6 @@ public class KeycloakSpec {
this.httpSpec = httpSpec; this.httpSpec = httpSpec;
} }
public void setDisableDefaultIngress(boolean value) {
this.disableDefaultIngress = value;
}
public boolean isDisableDefaultIngress() {
return this.disableDefaultIngress;
}
public UnsupportedSpec getUnsupported() { public UnsupportedSpec getUnsupported() {
return unsupported; return unsupported;
} }
@ -123,6 +118,14 @@ public class KeycloakSpec {
this.transactionsSpec = transactionsSpec; this.transactionsSpec = transactionsSpec;
} }
public IngressSpec getIngressSpec() {
return ingressSpec;
}
public void setIngressSpec(IngressSpec ingressSpec) {
this.ingressSpec = ingressSpec;
}
public int getInstances() { public int getInstances() {
return instances; return instances;
} }

View file

@ -0,0 +1,36 @@
/*
* Copyright 2022 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.operator.crds.v2alpha1.deployment.spec;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.sundr.builder.annotations.Buildable;
@Buildable(editableEnabled = false, builderPackage = "io.fabric8.kubernetes.api.builder")
public class IngressSpec {
@JsonProperty("enabled")
private boolean enabled = true;
public boolean isIngressEnabled() {
return enabled;
}
public void setIngressEnabled(boolean enabled) {
this.enabled = enabled;
}
}

View file

@ -24,6 +24,7 @@ import io.restassured.RestAssured;
import org.awaitility.Awaitility; import org.awaitility.Awaitility;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.keycloak.operator.Constants; import org.keycloak.operator.Constants;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpec;
import org.keycloak.operator.testsuite.utils.K8sUtils; import org.keycloak.operator.testsuite.utils.K8sUtils;
import org.keycloak.operator.controllers.KeycloakIngress; import org.keycloak.operator.controllers.KeycloakIngress;
@ -126,6 +127,8 @@ public class KeycloakIngressTest extends BaseOperatorTest {
@Test @Test
public void testMainIngressDurability() { public void testMainIngressDurability() {
var kc = K8sUtils.getDefaultKeycloakDeployment(); var kc = K8sUtils.getDefaultKeycloakDeployment();
kc.getSpec().setIngressSpec(new IngressSpec());
kc.getSpec().getIngressSpec().setIngressEnabled(true);
K8sUtils.deployKeycloak(k8sclient, kc, true); K8sUtils.deployKeycloak(k8sclient, kc, true);
var ingress = new KeycloakIngress(k8sclient, kc); var ingress = new KeycloakIngress(k8sclient, kc);
@ -165,7 +168,7 @@ public class KeycloakIngressTest extends BaseOperatorTest {
}); });
// Delete the ingress // Delete the ingress
kc.getSpec().setDisableDefaultIngress(true); kc.getSpec().getIngressSpec().setIngressEnabled(false);
K8sUtils.deployKeycloak(k8sclient, kc, true); K8sUtils.deployKeycloak(k8sclient, kc, true);
Awaitility.await() Awaitility.await()

View file

@ -29,7 +29,7 @@ import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse;
public class CRSerializationTest { public class CRSerializationTest {
@ -40,7 +40,7 @@ public class CRSerializationTest {
assertEquals("my-hostname", keycloak.getSpec().getHostname()); assertEquals("my-hostname", keycloak.getSpec().getHostname());
assertEquals("my-image", keycloak.getSpec().getImage()); assertEquals("my-image", keycloak.getSpec().getImage());
assertEquals("my-tls-secret", keycloak.getSpec().getHttpSpec().getTlsSecret()); assertEquals("my-tls-secret", keycloak.getSpec().getHttpSpec().getTlsSecret());
assertTrue(keycloak.getSpec().isDisableDefaultIngress()); assertFalse(keycloak.getSpec().getIngressSpec().isIngressEnabled());
final TransactionsSpec transactionsSpec = keycloak.getSpec().getTransactionsSpec(); final TransactionsSpec transactionsSpec = keycloak.getSpec().getTransactionsSpec();
assertThat(transactionsSpec, notNullValue()); assertThat(transactionsSpec, notNullValue());

View file

@ -21,6 +21,7 @@ import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder; import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.keycloak.operator.controllers.KeycloakIngress; import org.keycloak.operator.controllers.KeycloakIngress;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpec;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak; import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import org.keycloak.operator.testsuite.utils.K8sUtils; import org.keycloak.operator.testsuite.utils.K8sUtils;
@ -31,21 +32,24 @@ public class IngressLogicTest {
static class MockKeycloakIngress extends KeycloakIngress { static class MockKeycloakIngress extends KeycloakIngress {
private static Keycloak getKeycloak(boolean defaultIngressDisabled) { private static Keycloak getKeycloak(Boolean defaultIngressEnabled, boolean ingressSpecDefined) {
var kc = K8sUtils.getDefaultKeycloakDeployment(); var kc = K8sUtils.getDefaultKeycloakDeployment();
kc.getSpec().setDisableDefaultIngress(defaultIngressDisabled); if (ingressSpecDefined) {
kc.getSpec().setIngressSpec(new IngressSpec());
if (defaultIngressEnabled != null) kc.getSpec().getIngressSpec().setIngressEnabled(defaultIngressEnabled);
}
return kc; return kc;
} }
public static MockKeycloakIngress build(boolean defaultIngressDisabled, boolean ingressExists) { public static MockKeycloakIngress build(Boolean defaultIngressEnabled, boolean ingressExists, boolean ingressSpecDefined) {
MockKeycloakIngress.ingressExists = ingressExists; MockKeycloakIngress.ingressExists = ingressExists;
return new MockKeycloakIngress(defaultIngressDisabled); return new MockKeycloakIngress(defaultIngressEnabled, ingressSpecDefined);
} }
public static boolean ingressExists = false; public static boolean ingressExists = false;
private boolean deleted = false; private boolean deleted = false;
public MockKeycloakIngress(boolean defaultIngressDisabled) { public MockKeycloakIngress(Boolean defaultIngressEnabled, boolean ingressSpecDefined) {
super(null, getKeycloak(defaultIngressDisabled)); super(null, getKeycloak(defaultIngressEnabled, ingressSpecDefined));
} }
public boolean reconciled() { public boolean reconciled() {
@ -72,29 +76,43 @@ public class IngressLogicTest {
} }
@Test @Test
public void testIngressEnabledExisting() { public void testIngressDisabledExisting() {
var kc = MockKeycloakIngress.build(true, true); var kc = MockKeycloakIngress.build(false, true, true);
assertFalse(kc.reconciled()); assertFalse(kc.reconciled());
assertTrue(kc.deleted()); assertTrue(kc.deleted());
} }
@Test @Test
public void testIngressEnabledNotExisting() { public void testIngressDisabledNotExisting() {
var kc = MockKeycloakIngress.build(true, false); var kc = MockKeycloakIngress.build(false, false, true);
assertFalse(kc.reconciled()); assertFalse(kc.reconciled());
assertFalse(kc.deleted()); assertFalse(kc.deleted());
} }
@Test @Test
public void testIngressDisabledExisting() { public void testIngressEnabledExisting() {
var kc = MockKeycloakIngress.build(false, true); var kc = MockKeycloakIngress.build(true, true, true);
assertTrue(kc.reconciled()); assertTrue(kc.reconciled());
assertFalse(kc.deleted()); assertFalse(kc.deleted());
} }
@Test @Test
public void testIngressDisabledNotExisting() { public void testIngressEnabledNotExisting() {
var kc = MockKeycloakIngress.build(false, false); var kc = MockKeycloakIngress.build(true, false, true);
assertTrue(kc.reconciled());
assertFalse(kc.deleted());
}
@Test
public void testIngressEnabledNotSpecified() {
var kc = MockKeycloakIngress.build(true, false, false);
assertTrue(kc.reconciled());
assertFalse(kc.deleted());
}
@Test
public void testIngressSpecDefinedWithoutProperty() {
var kc = MockKeycloakIngress.build(null, false, true);
assertTrue(kc.reconciled()); assertTrue(kc.reconciled());
assertFalse(kc.deleted()); assertFalse(kc.deleted());
} }

View file

@ -11,6 +11,8 @@ spec:
- name: features - name: features
value: docker value: docker
hostname: my-hostname hostname: my-hostname
ingress:
enabled: false
http: http:
httpEnabled: true httpEnabled: true
httpPort: 123 httpPort: 123
@ -23,7 +25,6 @@ spec:
disabled: disabled:
- admin - admin
- step-up-authentication - step-up-authentication
disableDefaultIngress: true
transaction: transaction:
xaEnabled: false xaEnabled: false
unsupported: unsupported: