changes condition status to be a string, rather than a boolean

Closes #13074
This commit is contained in:
Steve Hawkins 2023-06-18 09:29:13 -04:00 committed by Bruno Oliveira da Silva
parent 4540ca365c
commit 5701f70157
9 changed files with 169 additions and 132 deletions

View file

@ -86,6 +86,10 @@ The previous and now removed WildFly distribution provided a built-in vault prov
In relation to the KeyStore Vault news, we also integrated Quarkus's recently released feature called KeyStore Config Source. This means that among the already existing configuration sources (CLI parameters, environment variables and files), you can now configure your Keycloak server via configuration properties stored in a Java keystore file. You can learn more about this feature in the https://www.keycloak.org/server/configuration[Configuration guide].
= k8s.keycloak.org/v2alpha1 changes
The are additional fields available in the keycloak.status to facilitate keycloak being a scalable resource. There are also additional fields that make the status easier to interpret such as observedGeneration and condition observedGeneration and lastTransitionTime fields. However the condition status field was also changed from a boolean to a string for conformance with standard Kubernetes conditions. Please make sure any of your usage of this field is updated to expect the values "True", "False", or "Unknown", rather than true or false.
= Account Console v3 promoted to preview
In version 21.1.0 of Keycloak the new Account Console (version 3) was introduced as an experimental feature. Starting this version it has been promoted to a preview feature.

View file

@ -3,7 +3,7 @@ set -euxo pipefail
max_retries=500
c=0
while [[ $(kubectl get keycloaks/example-kc -o jsonpath="{.status.conditions[?(@.type == 'Ready')].status}") != "true" ]]
while [[ $(kubectl get keycloaks/example-kc -o jsonpath="{.status.conditions[?(@.type == 'Ready')].status}") != "True" ]]
do
echo "waiting for Keycloak example-kc status"
((c++)) && ((c==max_retries)) && exit -1
@ -11,7 +11,7 @@ do
done
c=0
while [[ $(kubectl get keycloakrealmimports/example-count0-kc -o jsonpath="{.status.conditions[?(@.type == 'Done')].status}") != "true" ]]
while [[ $(kubectl get keycloakrealmimports/example-count0-kc -o jsonpath="{.status.conditions[?(@.type == 'Done')].status}") != "True" ]]
do
echo "waiting for Keycloak Realm Import example-count0-kc status"
((c++)) && ((c==max_retries)) && exit -1

View file

@ -96,7 +96,7 @@ public class KeycloakRealmImportController implements Reconciler<KeycloakRealmIm
if (status
.getConditions()
.stream()
.anyMatch(c -> c.getType().equals(KeycloakRealmImportStatusCondition.DONE) && !c.getStatus())) {
.anyMatch(c -> c.getType().equals(KeycloakRealmImportStatusCondition.DONE) && !Boolean.TRUE.equals(c.getStatus()))) {
updateControl.rescheduleAfter(10, TimeUnit.SECONDS);
}

View file

@ -0,0 +1,138 @@
/*
* 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;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class StatusCondition {
public enum Status {
True,
False,
Unknown
}
private String type;
private String status = Status.Unknown.name();
private String message;
private String lastTransitionTime;
private Long observedGeneration;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@JsonIgnore
public Boolean getStatus() {
if (status == null) {
return null;
}
// account for the legacy boolean string as well
switch (status) {
case "false":
case "False":
return false;
case "true":
case "True":
return true;
default:
return null;
}
}
@JsonProperty("status")
public String getStatusString() {
return status;
}
@JsonProperty("status")
public void setStatusString(String status) {
this.status = status;
}
@JsonIgnore
public void setStatus(Boolean status) {
if (status == null) {
this.status = Status.Unknown.name();
} else if (status) {
this.status = Status.True.name();
} else {
this.status = Status.False.name();
}
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getLastTransitionTime() {
return lastTransitionTime;
}
public void setLastTransitionTime(String lastTransitionTime) {
this.lastTransitionTime = lastTransitionTime;
}
public Long getObservedGeneration() {
return observedGeneration;
}
public void setObservedGeneration(Long observedGeneration) {
this.observedGeneration = observedGeneration;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StatusCondition that = (StatusCondition) o;
return Objects.equals(getType(), that.getType()) && Objects.equals(getStatus(), that.getStatus()) && Objects.equals(getMessage(), that.getMessage())
&& Objects.equals(getLastTransitionTime(), that.getLastTransitionTime())
&& Objects.equals(getObservedGeneration(), that.getObservedGeneration());
}
@Override
public int hashCode() {
return Objects.hash(getType(), getStatus(), getMessage(), getObservedGeneration(), getLastTransitionTime());
}
@Override
public String toString() {
return getClass().getSimpleName() + "{" +
"type='" + type + '\'' +
", status=" + status +
", message='" + message + '\'' +
'}';
}
}

View file

@ -17,84 +17,13 @@
package org.keycloak.operator.crds.v2alpha1.deployment;
import java.util.Objects;
import org.keycloak.operator.crds.v2alpha1.StatusCondition;
/**
* @author Vaclav Muzikar <vmuzikar@redhat.com>
*/
public class KeycloakStatusCondition {
public class KeycloakStatusCondition extends StatusCondition {
public static final String READY = "Ready";
public static final String HAS_ERRORS = "HasErrors";
public static final String ROLLING_UPDATE = "RollingUpdate";
// string to avoid enums in CRDs
private String type;
private Boolean status;
private String message;
private String lastTransitionTime;
private Long observedGeneration;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Boolean getStatus() {
return status;
}
public void setStatus(Boolean status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getLastTransitionTime() {
return lastTransitionTime;
}
public void setLastTransitionTime(String lastTransitionTime) {
this.lastTransitionTime = lastTransitionTime;
}
public Long getObservedGeneration() {
return observedGeneration;
}
public void setObservedGeneration(Long observedGeneration) {
this.observedGeneration = observedGeneration;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeycloakStatusCondition that = (KeycloakStatusCondition) o;
return Objects.equals(getType(), that.getType()) && Objects.equals(getStatus(), that.getStatus()) && Objects.equals(getMessage(), that.getMessage())
&& Objects.equals(getLastTransitionTime(), that.getLastTransitionTime())
&& Objects.equals(getObservedGeneration(), that.getObservedGeneration());
}
@Override
public int hashCode() {
return Objects.hash(getType(), getStatus(), getMessage(), getObservedGeneration(), getLastTransitionTime());
}
@Override
public String toString() {
return "KeycloakStatusCondition{" +
"type='" + type + '\'' +
", status=" + status +
", message='" + message + '\'' +
'}';
}
}

View file

@ -38,7 +38,7 @@ public class KeycloakRealmImportStatus {
public boolean isDone() {
return conditions
.stream()
.anyMatch(c -> c.getStatus() && c.getType().equals(DONE));
.anyMatch(c -> Boolean.TRUE.equals(c.getStatus()) && c.getType().equals(DONE));
}
@Override

View file

@ -17,61 +17,10 @@
package org.keycloak.operator.crds.v2alpha1.realmimport;
import java.util.Objects;
import org.keycloak.operator.crds.v2alpha1.StatusCondition;
public class KeycloakRealmImportStatusCondition {
public class KeycloakRealmImportStatusCondition extends StatusCondition {
public static final String DONE = "Done";
public static final String STARTED = "Started";
public static final String HAS_ERRORS = "HasErrors";
// string to avoid enums in CRDs
private String type;
private Boolean status;
private String message;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Boolean getStatus() {
return status;
}
public void setStatus(Boolean status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeycloakRealmImportStatusCondition that = (KeycloakRealmImportStatusCondition) o;
return getType() == that.getType() && Objects.equals(getStatus(), that.getStatus()) && Objects.equals(getMessage(), that.getMessage());
}
@Override
public int hashCode() {
return Objects.hash(getType(), getStatus(), getMessage());
}
@Override
public String toString() {
return "KeycloakRealmImportStatusCondition{" +
"type='" + type + '\'' +
", status=" + status +
", message='" + message + '\'' +
'}';
}
}

View file

@ -17,6 +17,8 @@
package org.keycloak.operator.testsuite.unit;
import io.fabric8.kubernetes.client.utils.Serialization;
import org.assertj.core.api.Condition;
import org.junit.jupiter.api.Test;
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatus;
@ -25,6 +27,7 @@ import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition;
import org.keycloak.operator.testsuite.utils.CRAssert;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
public class KeycloakStatusTest {
@ -137,4 +140,16 @@ public class KeycloakStatusTest {
assertEquals(3, status.getInstances());
}
@Test
public void testStatusSerializtion() {
KeycloakStatusCondition condition = new KeycloakStatusCondition();
condition.setStatus(false);
String yaml = Serialization.asYaml(condition);
assertEquals("---\nstatus: \"False\"\n", yaml);
var deserialized = Serialization.unmarshal(yaml, KeycloakStatusCondition.class);
assertFalse(deserialized.getStatus());
}
}

View file

@ -26,6 +26,8 @@ import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatus;
import org.keycloak.operator.crds.v2alpha1.deployment.KeycloakStatusCondition;
import org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImport;
import java.util.Objects;
import static org.assertj.core.api.Assertions.assertThat;
/**
@ -70,8 +72,8 @@ public final class CRAssert {
.noneMatch(c -> c.getMessage().contains(message));
}
public static void assertKeycloakRealmImportStatusCondition(KeycloakRealmImport kri, String condition, boolean status) {
public static void assertKeycloakRealmImportStatusCondition(KeycloakRealmImport kri, String condition, Boolean status) {
assertThat(kri.getStatus().getConditions())
.anyMatch(c -> c.getType().equals(condition) && c.getStatus() == status);
.anyMatch(c -> c.getType().equals(condition) && Objects.equals(c.getStatus(), status));
}
}