Refine Ingress settings in Keycloak CR
Closes Keycloak#14407 Signed-off-by: Peter Zaoral <pzaoral@redhat.com>
This commit is contained in:
parent
76d9125c3f
commit
4dfbb42680
8 changed files with 102 additions and 34 deletions
|
@ -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
|
||||||
----
|
----
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue