add labels and annotations to service (httpSpec) (#39925)

closes #23283

Signed-off-by: Gilvan Filho <gfilho@redhat.com>
This commit is contained in:
Gilvan Filho 2025-06-17 06:27:16 -03:00 committed by GitHub
parent d4392779f6
commit e5bb7f5249
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 117 additions and 2 deletions

View File

@ -462,4 +462,25 @@ They need to access {project_name} to scrape the available metrics.
Check the https://kubernetes.io/docs/concepts/services-networking/network-policies/[Kubernetes Network Policies documentation] for more information about NetworkPolicies.
=== Parameterizing service labels and annotations
If you need to set custom labels or annotations to keycloak service you can do that through `spec.http.labels` and `spec.http.annotations`
.Custom service labels and annotations
[source,yaml]
----
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
name: example-kc
spec:
http:
labels:
label1: label-value1
label2: label-value2
annotations:
annotation1: annotation-value1
annotation2: annotation-value2
----
</@tmpl.guide>

View File

@ -17,6 +17,9 @@
package org.keycloak.operator.controllers;
import java.util.Optional;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.Service;
@ -76,11 +79,19 @@ public class KeycloakServiceDependentResource extends CRUDKubernetesDependentRes
@Override
protected Service desired(Keycloak primary, Context<Keycloak> context) {
Map<String,String> labels = Utils.allInstanceLabels(primary);
var optionalSpec = Optional.ofNullable(primary.getSpec().getHttpSpec());
optionalSpec.map(HttpSpec::getLabels).ifPresent(labels::putAll);
Map<String,String> annotations = optionalSpec.map(HttpSpec::getAnnotations).orElse(new HashMap<>());
Service service = new ServiceBuilder()
.withNewMetadata()
.withName(getServiceName(primary))
.withNamespace(primary.getMetadata().getNamespace())
.addToLabels(Utils.allInstanceLabels(primary))
.addToLabels(labels)
.addToAnnotations(annotations)
.endMetadata()
.withSpec(getServiceSpec(primary))
.build();

View File

@ -18,11 +18,14 @@
package org.keycloak.operator.crds.v2alpha1.deployment.spec;
import java.util.Optional;
import java.util.Map;
import org.keycloak.operator.Constants;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import io.sundr.builder.annotations.Buildable;
import org.keycloak.operator.Constants;
import org.keycloak.operator.crds.v2alpha1.CRDUtils;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakSpec;
@ -45,6 +48,12 @@ public class HttpSpec {
@JsonPropertyDescription("The used HTTPS port.")
private Integer httpsPort = Constants.KEYCLOAK_HTTPS_PORT;
@JsonPropertyDescription("Annotations to be appended to the Service object")
Map<String, String> annotations;
@JsonPropertyDescription("Labels to be appended to the Service object")
Map<String, String> labels;
public String getTlsSecret() {
return tlsSecret;
}
@ -95,4 +104,20 @@ public class HttpSpec {
.map(KeycloakSpec::getHttpSpec);
}
public Map<String, String> getAnnotations() {
return annotations;
}
public void setAnnotations(Map<String, String> annotations) {
this.annotations = annotations;
}
public Map<String, String> getLabels() {
return labels;
}
public void setLabels(Map<String, String> labels) {
this.labels = labels;
}
}

View File

@ -31,12 +31,15 @@ import java.time.Duration;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@QuarkusTest
public class KeycloakServicesTest extends BaseOperatorTest {
@Test
public void testMainServiceDurability() {
var kc = getTestKeycloakDeployment(true);
kc.getSpec().getHttpSpec().setLabels(Map.of("foo","bar"));
K8sUtils.deployKeycloak(k8sclient, kc, true);
String serviceName = KeycloakServiceDependentResource.getServiceName(kc);
var serviceSelector = k8sclient.services().inNamespace(namespace).withName(serviceName);
@ -71,6 +74,7 @@ public class KeycloakServicesTest extends BaseOperatorTest {
.untilAsserted(() -> {
var s = serviceSelector.get();
assertThat(s.getMetadata().getLabels().entrySet().containsAll(labels.entrySet())).isTrue(); // additional labels should not be overwritten
assertEquals("bar", s.getMetadata().getLabels().get("foo"));
// ignoring assigned IP/s and generated config
s.getSpec().setClusterIP(null);
s.getSpec().setClusterIPs(null);
@ -79,6 +83,60 @@ public class KeycloakServicesTest extends BaseOperatorTest {
});
}
@Test
public void testCustomServiceAnnotations() {
var kc = getTestKeycloakDeployment(true);
// set 'a'
kc.getSpec().getHttpSpec().setAnnotations(Map.of("a", "b"));
K8sUtils.deployKeycloak(k8sclient, kc, true);
String serviceName = KeycloakServiceDependentResource.getServiceName(kc);
var serviceSelector = k8sclient.services().inNamespace(namespace).withName(serviceName);
Awaitility.await()
.ignoreExceptions()
.untilAsserted(() -> {
var s = serviceSelector.get();
assertEquals("b", s.getMetadata().getAnnotations().get("a"));
});
// update 'a'
kc.getSpec().getHttpSpec().setAnnotations(Map.of("a", "bb"));
K8sUtils.deployKeycloak(k8sclient, kc, true);
Awaitility.await()
.ignoreExceptions()
.untilAsserted(() -> {
var s = serviceSelector.get();
assertEquals("bb", s.getMetadata().getAnnotations().get("a"));
});
// remove 'a' and add 'c'
kc.getSpec().getHttpSpec().setAnnotations(Map.of("c", "d"));
K8sUtils.deployKeycloak(k8sclient, kc, true);
Awaitility.await()
.ignoreExceptions()
.untilAsserted(() -> {
var s = serviceSelector.get();
assertFalse(s.getMetadata().getAnnotations().containsKey("a"));
assertEquals("d", s.getMetadata().getAnnotations().get("c"));
});
// remove all
kc.getSpec().getHttpSpec().setAnnotations(null);
K8sUtils.deployKeycloak(k8sclient, kc, true);
Awaitility.await()
.ignoreExceptions()
.untilAsserted(() -> {
var s = serviceSelector.get();
assertFalse(s.getMetadata().getAnnotations().containsKey("a"));
assertFalse(s.getMetadata().getAnnotations().containsKey("c"));
});
}
@Test
public void testDiscoveryServiceDurability() {
var kc = getTestKeycloakDeployment(true);