Add 'imagePullSecret' field to the Keycloak CR

This commit is contained in:
Andre Nascimento RH 2022-09-07 14:03:28 +02:00 committed by GitHub
parent aec8e6af50
commit 4594243a33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 0 deletions

View file

@ -30,6 +30,7 @@ import io.fabric8.kubernetes.api.model.apps.StatefulSet;
import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.quarkus.logging.Log;
import org.keycloak.common.util.CollectionUtil;
import org.keycloak.operator.Config;
import org.keycloak.operator.Constants;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
@ -152,6 +153,11 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu
overlayTemplate.getSpec().getContainers().get(0).getImage() != null) {
status.addWarningMessage("The image of the keycloak container cannot be modified using podTemplate");
}
if (overlayTemplate.getSpec() != null &&
CollectionUtil.isNotEmpty(overlayTemplate.getSpec().getImagePullSecrets())) {
status.addWarningMessage("The imagePullSecrets of the keycloak container cannot be modified using podTemplate");
}
}
private <T, V> void mergeMaps(Map<T, V> map1, Map<T, V> map2, Consumer<Map<T, V>> consumer) {
@ -523,6 +529,10 @@ public class KeycloakDeployment extends OperatorManagedResource implements Statu
container.getArgs().add("--optimized");
}
if (CollectionUtil.isNotEmpty(keycloakCR.getSpec().getImagePullSecrets())) {
baseDeployment.getSpec().getTemplate().getSpec().setImagePullSecrets(keycloakCR.getSpec().getImagePullSecrets());
}
container.setImagePullPolicy(config.keycloak().imagePullPolicy());
container.setEnv(getEnvVars());

View file

@ -19,6 +19,7 @@ package org.keycloak.operator.crds.v2alpha1.deployment;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import io.fabric8.kubernetes.api.model.LocalObjectReference;
import org.keycloak.operator.Constants;
import javax.validation.constraints.NotNull;
@ -30,6 +31,8 @@ public class KeycloakSpec {
private int instances = 1;
@JsonPropertyDescription("Custom Keycloak image to be used.")
private String image;
@JsonPropertyDescription("Secret(s) that might be used when pulling an image from a private container image registry or repository.")
private List<LocalObjectReference> imagePullSecrets;
@JsonPropertyDescription("Configuration of the Keycloak server.\n" +
"expressed as a keys (reference: https://www.keycloak.org/server/all-config) and values that can be either direct values or references to secrets.")
private List<ValueOrSecret> serverConfiguration; // can't use Set due to a bug in Sundrio https://github.com/sundrio/sundrio/issues/316
@ -110,6 +113,14 @@ public class KeycloakSpec {
this.image = image;
}
public List<LocalObjectReference> getImagePullSecrets() {
return this.imagePullSecrets;
}
public void setImagePullSecrets(List<LocalObjectReference> imagePullSecrets) {
this.imagePullSecrets = imagePullSecrets;
}
public List<ValueOrSecret> getServerConfiguration() {
return serverConfiguration;
}

View file

@ -18,6 +18,9 @@
package org.keycloak.operator.testsuite.integration;
import io.fabric8.kubernetes.api.model.EnvVarBuilder;
import io.fabric8.kubernetes.api.model.LocalObjectReference;
import io.fabric8.kubernetes.api.model.LocalObjectReferenceBuilder;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.api.model.SecretKeySelectorBuilder;
import io.fabric8.kubernetes.api.model.apps.StatefulSetSpecBuilder;
@ -38,6 +41,7 @@ import org.keycloak.operator.crds.v2alpha1.deployment.ValueOrSecret;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
@ -52,6 +56,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.keycloak.operator.testsuite.utils.CRAssert.assertKeycloakStatusCondition;
import static org.keycloak.operator.testsuite.utils.K8sUtils.deployKeycloak;
import static org.keycloak.operator.testsuite.utils.K8sUtils.getDefaultKeycloakDeployment;
import static org.keycloak.operator.testsuite.utils.K8sUtils.getResourceFromFile;
import static org.keycloak.operator.testsuite.utils.K8sUtils.waitForKeycloakToBeReady;
@QuarkusTest
@ -397,6 +402,37 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
}
}
@Test
@EnabledIfSystemProperty(named = OPERATOR_CUSTOM_IMAGE, matches = ".+")
public void testCustomImageWithImagePullSecrets() {
String imagePullSecretName = "docker-regcred-custom-kc-imagepullsecret-01";
String secretDescriptorFilename = "test-docker-registry-secret.yaml";
try {
var kc = getDefaultKeycloakDeployment();
kc.getSpec().setImage(customImage);
handleFakeImagePullSecretCreation(kc, secretDescriptorFilename);
deployKeycloak(k8sclient, kc, true);
var pods = k8sclient
.pods()
.inNamespace(namespace)
.withLabels(Constants.DEFAULT_LABELS)
.list()
.getItems();
assertThat(pods.get(0).getSpec().getContainers().get(0).getArgs()).containsExactly("start", "--optimized");
assertThat(pods.get(0).getSpec().getImagePullSecrets().size()).isEqualTo(1);
assertThat(pods.get(0).getSpec().getImagePullSecrets().get(0).getName()).isEqualTo(imagePullSecretName);
} catch (Exception e) {
savePodLogs();
throw e;
}
}
@Test
public void testHttpRelativePathWithPlainValue() {
try {
@ -498,4 +534,12 @@ public class KeycloakDeploymentTest extends BaseOperatorTest {
}
}
private void handleFakeImagePullSecretCreation(Keycloak keycloakCR,
String secretDescriptorFilename) {
Secret imagePullSecret = getResourceFromFile(secretDescriptorFilename, Secret.class);
k8sclient.secrets().inNamespace(namespace).createOrReplace(imagePullSecret);
LocalObjectReference localObjRefAsSecretTmp = new LocalObjectReferenceBuilder().withName(imagePullSecret.getMetadata().getName()).build();
keycloakCR.getSpec().setImagePullSecrets(Collections.singletonList(localObjRefAsSecretTmp));
}
}

View file

@ -17,7 +17,10 @@
package org.keycloak.operator.testsuite.integration;
import io.fabric8.kubernetes.api.model.LocalObjectReference;
import io.fabric8.kubernetes.api.model.LocalObjectReferenceBuilder;
import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.kubernetes.client.utils.Serialization;
import io.quarkus.logging.Log;
@ -27,9 +30,12 @@ import org.junit.jupiter.api.Test;
import org.keycloak.operator.testsuite.utils.CRAssert;
import org.keycloak.operator.crds.v2alpha1.deployment.Keycloak;
import java.util.Collections;
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.assertj.core.api.Assertions.assertThat;
import static org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition.HAS_ERRORS;
import static org.keycloak.operator.testsuite.utils.K8sUtils.getResourceFromFile;
@QuarkusTest
public class PodTemplateTest extends BaseOperatorTest {
@ -181,4 +187,39 @@ public class PodTemplateTest extends BaseOperatorTest {
});
}
@Test
public void testPodTemplateIncorrectImagePullSecretsConfig() {
String imagePullSecretName = "docker-regcred-custom-kc-imagepullsecret-01";
String secretDescriptorFilename = "test-docker-registry-secret.yaml";
Secret imagePullSecret = getResourceFromFile(secretDescriptorFilename, Secret.class);
k8sclient.secrets().inNamespace(namespace).createOrReplace(imagePullSecret);
LocalObjectReference localObjRefAsSecretTmp = new LocalObjectReferenceBuilder().withName(imagePullSecret.getMetadata().getName()).build();
assertThat(localObjRefAsSecretTmp.getName()).isNotNull();
assertThat(localObjRefAsSecretTmp.getName()).isEqualTo(imagePullSecretName);
var podTemplate = new PodTemplateSpecBuilder()
.withNewSpec()
.addAllToImagePullSecrets(Collections.singletonList(localObjRefAsSecretTmp))
.endSpec()
.build();
var plainKc = getEmptyPodTemplateKeycloak();
plainKc.getSpec().getUnsupported().setPodTeplate(podTemplate);
// Act
k8sclient.resource(plainKc).createOrReplace();
// Assert
Log.info("Getting status of Keycloak");
Awaitility
.await()
.ignoreExceptions()
.atMost(3, MINUTES).untilAsserted(() -> {
CRAssert.assertKeycloakStatusCondition(getCrSelector().get(), HAS_ERRORS, false, "imagePullSecrets");
CRAssert.assertKeycloakStatusCondition(getCrSelector().get(), HAS_ERRORS, false, "cannot be modified");
});
}
}

View file

@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
data:
.dockerconfigjson: eyJhdXRocyI6eyJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOnsidXNlcm5hbWUiOiJrZXljbG9hazR0ZXN0IiwicGFzc3dvcmQiOiJ2S2xRJWMyNDY5RUBMIiwiZW1haWwiOiJhbmFzY2ltZUByZWRoYXQuY29tIiwiYXV0aCI6ImEyVjVZMnh2WVdzMGRHVnpkRHAyUzJ4UkpXTXlORFk1UlVCTSJ9fX0=
metadata:
name: docker-regcred-custom-kc-imagepullsecret-01
type: kubernetes.io/dockerconfigjson