Adding the ability to set ingressClassName (#20796)

Closes #20723
This commit is contained in:
Steven Hawkins 2023-06-05 17:10:39 -04:00 committed by GitHub
parent d5ef798cac
commit d045156ba4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 104 additions and 19 deletions

View file

@ -176,7 +176,23 @@ CONDITION: RollingUpdate
=== Accessing the Keycloak deployment
The Keycloak deployment is exposed through a basic Ingress and is accessible through the provided hostname.
The Keycloak deployment is exposed through a basic Ingress and is accessible through the provided hostname. On installations with multiple default IngressClass instances
or when running on OpenShift 4.12+ you should provide an ingressClassName by setting `ingress` spec with `className` property to the desired class name:
Edit YAML file `example-kc.yaml`:
[source,yaml]
----
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
name: example-kc
spec:
...
ingress:
className: openshift-default
----
If the default ingress does not fit your use case, disable it by setting `ingress` spec with `enabled` property to `false` value:
Edit YAML file `example-kc.yaml`:

View file

@ -72,11 +72,8 @@ public class KeycloakIngress extends OperatorManagedResource implements StatusUp
annotations.put("route.openshift.io/termination", "edge");
}
if (keycloak.getSpec().getIngressSpec() != null &&
keycloak.getSpec().getIngressSpec().getAnnotations() != null) {
annotations.putAll(keycloak.getSpec().getIngressSpec().getAnnotations());
}
var optionalSpec = Optional.ofNullable(keycloak.getSpec().getIngressSpec());
optionalSpec.map(IngressSpec::getAnnotations).ifPresent(annotations::putAll);
Ingress ingress = new IngressBuilder()
.withNewMetadata()
@ -85,6 +82,7 @@ public class KeycloakIngress extends OperatorManagedResource implements StatusUp
.addToAnnotations(annotations)
.endMetadata()
.withNewSpec()
.withIngressClassName(optionalSpec.map(IngressSpec::getIngressClassName).orElse(null))
.withNewDefaultBackend()
.withNewService()
.withName(keycloak.getMetadata().getName() + Constants.KEYCLOAK_SERVICE_SUFFIX)

View file

@ -27,18 +27,29 @@ import java.util.Map;
public class IngressSpec {
@JsonProperty("enabled")
private boolean enabled = true;
private boolean ingressEnabled = true;
@JsonProperty("className")
private String ingressClassName;
@JsonProperty("annotations")
@JsonPropertyDescription("Additional annotations to be appended to the Ingress object")
Map<String, String> annotations;
public boolean isIngressEnabled() {
return enabled;
return ingressEnabled;
}
public void setIngressEnabled(boolean enabled) {
this.enabled = enabled;
this.ingressEnabled = enabled;
}
public String getIngressClassName() {
return ingressClassName;
}
public void setIngressClassName(String className) {
this.ingressClassName = className;
}
public Map<String, String> getAnnotations() {

View file

@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test;
import org.keycloak.operator.Constants;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpec;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpecBuilder;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.HostnameSpecBuilder;
import org.keycloak.operator.testsuite.utils.K8sUtils;
import org.keycloak.operator.controllers.KeycloakIngress;
@ -270,6 +271,39 @@ public class KeycloakIngressTest extends BaseOperatorTest {
}
}
@Test
public void testCustomIngressClassName() {
var kc = K8sUtils.getDefaultKeycloakDeployment();
kc.getSpec().setIngressSpec(new IngressSpecBuilder().withIngressClassName("nginx").build());
K8sUtils.deployKeycloak(k8sclient, kc, true);
var ingress = new KeycloakIngress(k8sclient, kc);
var ingressSelector = k8sclient
.network()
.v1()
.ingresses()
.inNamespace(namespace)
.withName(ingress.getName());
Awaitility.await()
.ignoreExceptions()
.untilAsserted(() -> {
var i = ingressSelector.get();
assertEquals("nginx", i.getSpec().getIngressClassName());
});
// update to a different classname
kc.getSpec().setIngressSpec(new IngressSpecBuilder().withIngressClassName("nginx-latest").build());
K8sUtils.deployKeycloak(k8sclient, kc, true);
Awaitility.await()
.ignoreExceptions()
.untilAsserted(() -> {
var i = ingressSelector.get();
assertEquals("nginx-latest", i.getSpec().getIngressClassName());
});
}
@Test
public void testCustomIngressAnnotations() {
var kc = K8sUtils.getDefaultKeycloakDeployment();

View file

@ -55,6 +55,7 @@ public class CRSerializationTest {
assertEquals("my-image", keycloak.getSpec().getImage());
assertEquals("my-tls-secret", keycloak.getSpec().getHttpSpec().getTlsSecret());
assertFalse(keycloak.getSpec().getIngressSpec().isIngressEnabled());
assertEquals("nginx", keycloak.getSpec().getIngressSpec().getIngressClassName());
assertEquals(CUSTOM_INGRESS_ANNOTATION, keycloak.getSpec().getIngressSpec().getAnnotations());
final TransactionsSpec transactionsSpec = keycloak.getSpec().getTransactionsSpec();

View file

@ -27,6 +27,7 @@ import org.junit.jupiter.api.Test;
import org.keycloak.operator.controllers.KeycloakIngress;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpec;
import org.keycloak.operator.crds.v2alpha1.deployment.spec.IngressSpecBuilder;
import org.keycloak.operator.testsuite.utils.K8sUtils;
import io.fabric8.kubernetes.api.model.HasMetadata;
@ -35,6 +36,7 @@ import io.fabric8.kubernetes.api.model.networking.v1.IngressBuilder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class IngressLogicTest {
@ -43,15 +45,11 @@ public class IngressLogicTest {
static class MockKeycloakIngress extends KeycloakIngress {
private static Keycloak getKeycloak(Boolean defaultIngressEnabled, boolean ingressSpecDefined, boolean tlsConfigured, Map<String, String> annotations) {
private static Keycloak getKeycloak(boolean tlsConfigured, IngressSpec ingressSpec) {
var kc = K8sUtils.getDefaultKeycloakDeployment();
kc.getMetadata().setUid("this-is-a-fake-uid");
if (ingressSpecDefined) {
kc.getSpec().setIngressSpec(new IngressSpec());
if (defaultIngressEnabled != null) kc.getSpec().getIngressSpec().setIngressEnabled(defaultIngressEnabled);
if (annotations != null) {
kc.getSpec().getIngressSpec().setAnnotations(annotations);
}
if (ingressSpec != null) {
kc.getSpec().setIngressSpec(ingressSpec);
}
if (!tlsConfigured) {
kc.getSpec().getHttpSpec().setTlsSecret(null);
@ -69,13 +67,23 @@ public class IngressLogicTest {
public static MockKeycloakIngress build(Boolean defaultIngressEnabled, boolean ingressExists, boolean ingressSpecDefined, boolean tlsConfigured, Map<String, String> annotations) {
MockKeycloakIngress.ingressExists = ingressExists;
return new MockKeycloakIngress(defaultIngressEnabled, ingressSpecDefined, tlsConfigured, annotations);
IngressSpec ingressSpec = null;
if (ingressSpecDefined) {
ingressSpec = new IngressSpec();
if (defaultIngressEnabled != null) {
ingressSpec.setIngressEnabled(defaultIngressEnabled);
}
if (annotations != null) {
ingressSpec.setAnnotations(annotations);
}
}
return new MockKeycloakIngress(tlsConfigured, ingressSpec);
}
public static boolean ingressExists = false;
private boolean deleted = false;
public MockKeycloakIngress(Boolean defaultIngressEnabled, boolean ingressSpecDefined, boolean tlsConfigured, Map<String, String> annotations) {
super(null, getKeycloak(defaultIngressEnabled, ingressSpecDefined, tlsConfigured, annotations));
public MockKeycloakIngress(boolean tlsConfigured, IngressSpec ingressSpec) {
super(null, getKeycloak(tlsConfigured, ingressSpec));
}
@Override
@ -218,4 +226,20 @@ public class IngressLogicTest {
assertEquals("passthrough", reconciled.get().getMetadata().getAnnotations().get("route.openshift.io/termination"));
assertEquals("another-value", reconciled.get().getMetadata().getAnnotations().get(EXISTING_ANNOTATION_KEY));
}
@Test
public void testIngressSpecDefinedWithoutClassName() {
var kc = new MockKeycloakIngress(true, new IngressSpec());
Optional<HasMetadata> reconciled = kc.getReconciledResource();
Ingress ingress = reconciled.map(Ingress.class::cast).orElseThrow();
assertNull(ingress.getSpec().getIngressClassName());
}
@Test
public void testIngressSpecDefinedWithClassName() {
var kc = new MockKeycloakIngress(true, new IngressSpecBuilder().withIngressClassName("my-class").build());
Optional<HasMetadata> reconciled = kc.getReconciledResource();
Ingress ingress = reconciled.map(Ingress.class::cast).orElseThrow();
assertEquals("my-class", ingress.getSpec().getIngressClassName());
}
}

View file

@ -28,6 +28,7 @@ spec:
poolMaxSize: 3
ingress:
enabled: false
className: nginx
annotations:
myAnnotation: myValue
anotherAnnotation: anotherValue