Baseline for the new Keycloak operator (#9381)
* Baseline for the new Keycloak operator * v2alpha1 and better kustomization setup
This commit is contained in:
parent
8649ca3d50
commit
8c5e158db4
15 changed files with 659 additions and 0 deletions
39
.github/workflows/operator-ci.yml
vendored
Normal file
39
.github/workflows/operator-ci.yml
vendored
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
name: Keycloak Operator CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
JDK_VERSION: 11
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
# Only run once for latest commit per ref and cancel other (previous) runs.
|
||||||
|
group: ci-operator-keycloak-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Update maven settings
|
||||||
|
run: mkdir -p ~/.m2 ; cp .github/settings.xml ~/.m2/
|
||||||
|
- uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: ${{ env.JDK_VERSION }}
|
||||||
|
- name: Cache Maven packages
|
||||||
|
id: cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.m2/repository
|
||||||
|
key: cache-1-${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||||
|
restore-keys: cache-1-${{ runner.os }}-m2
|
||||||
|
|
||||||
|
- name: Create the Keycloak distribution
|
||||||
|
run: |
|
||||||
|
mvn clean install -DskipTests -DskipExamples -DskipTestsuite
|
||||||
|
|
||||||
|
- name: Build the Keycloak Operator
|
||||||
|
run: |
|
||||||
|
mvn clean package -nsu -B -e -pl operator -Doperator -Dquarkus.container-image.build=true -Dquarkus.kubernetes.deployment-target=minikube
|
40
operator/README.md
Normal file
40
operator/README.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# Keycloak on Quarkus
|
||||||
|
|
||||||
|
The module holds the codebase to build the Keycloak Operator on top of [Quarkus](https://quarkus.io/).
|
||||||
|
Using the [Quarkus Operator SDK](https://github.com/quarkiverse/quarkus-operator-sdk).
|
||||||
|
|
||||||
|
## Activating the Module
|
||||||
|
|
||||||
|
When build from the project root directory, this module is only enabled if the installed JDK is 11 or newer.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Ensure you have JDK 11 (or newer) installed.
|
||||||
|
|
||||||
|
Build the Docker image with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean package -Doperator -Dquarkus.container-image.build=true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
### Quick start on Minikube
|
||||||
|
|
||||||
|
Enable the Minikube Docker daemon:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
eval $(minikube -p minikube docker-env)
|
||||||
|
```
|
||||||
|
|
||||||
|
Compile the project and generate the Docker image with JIB:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean package -Doperator -Dquarkus.container-image.build=true -Dquarkus.kubernetes.deployment-target=minikube
|
||||||
|
```
|
||||||
|
|
||||||
|
Install the CRD definition and the operator in the cluster:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -k .
|
||||||
|
```
|
31
operator/kubernetes/deployments-role.yaml
Normal file
31
operator/kubernetes/deployments-role.yaml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: keycloak-operator-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- apps
|
||||||
|
resources:
|
||||||
|
- deployments
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: keycloak-operator
|
||||||
|
name: keycloak-operator-role-binding
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
name: keycloak-operator-role
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: keycloak-operator
|
4
operator/kustomization.yaml
Normal file
4
operator/kustomization.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
resources:
|
||||||
|
- target/kubernetes/keycloaks.keycloak.org-v1.yml
|
||||||
|
- target/kubernetes/minikube.yml
|
||||||
|
- kubernetes/deployments-role.yaml
|
149
operator/pom.xml
Normal file
149
operator/pom.xml
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<artifactId>keycloak-parent</artifactId>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<version>17.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<name>Keycloak Operator</name>
|
||||||
|
<artifactId>keycloak-operator</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<!--
|
||||||
|
Override versions based on Quarkus dependencies.
|
||||||
|
Make sure to update these dependencies when Quarkus version changes.
|
||||||
|
See https://github.com/quarkusio/quarkus/blob/<versionTag>/bom/application/pom.xml
|
||||||
|
for reference
|
||||||
|
-->
|
||||||
|
<resteasy.version>4.7.4.Final</resteasy.version>
|
||||||
|
<wildfly.common.version>1.5.4.Final-format-001</wildfly.common.version>
|
||||||
|
|
||||||
|
<compiler-plugin.version>3.8.1</compiler-plugin.version>
|
||||||
|
<maven.compiler.parameters>true</maven.compiler.parameters>
|
||||||
|
<maven.compiler.release>11</maven.compiler.release>
|
||||||
|
<maven.compiler.source>11</maven.compiler.source>
|
||||||
|
<maven.compiler.target>11</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<quarkus.operator.sdk.version>3.0.0-SNAPSHOT</quarkus.operator.sdk.version>
|
||||||
|
<quarkus.version>2.6.1.Final</quarkus.version>
|
||||||
|
<quarkus.container-image.group>keycloak</quarkus.container-image.group>
|
||||||
|
<quarkus.jib.base-jvm-image>eclipse-temurin:11</quarkus.jib.base-jvm-image>
|
||||||
|
<quarkus.kubernetes.image-pull-policy>Never</quarkus.kubernetes.image-pull-policy>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>s01.oss.sonatype</id>
|
||||||
|
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkiverse.operatorsdk</groupId>
|
||||||
|
<artifactId>quarkus-operator-sdk-bom</artifactId>
|
||||||
|
<version>${quarkus.operator.sdk.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Quarkus -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkiverse.operatorsdk</groupId>
|
||||||
|
<artifactId>quarkus-operator-sdk</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkiverse.operatorsdk</groupId>
|
||||||
|
<artifactId>quarkus-operator-sdk-csv-generator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-resteasy-jackson</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-client-jackson</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-openshift</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-minikube</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-kubernetes-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- This dependency is needed only to ensure proper building order so that this module is build after the CSV extension -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkiverse.operatorsdk</groupId>
|
||||||
|
<artifactId>quarkus-operator-sdk-csv-generator-deployment</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Keycloak -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-maven-plugin</artifactId>
|
||||||
|
<version>${quarkus.version}</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>${compiler-plugin.version}</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-maven-plugin</artifactId>
|
||||||
|
<version>${quarkus.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>build</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>native</id>
|
||||||
|
<properties>
|
||||||
|
<quarkus.package.type>native</quarkus.package.type>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
</project>
|
36
operator/src/main/java/org/keycloak/operator/Constants.java
Normal file
36
operator/src/main/java/org/keycloak/operator/Constants.java
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class Constants {
|
||||||
|
public static final String CRDS_GROUP = "keycloak.org";
|
||||||
|
public static final String CRDS_VERSION = "v2alpha1";
|
||||||
|
public static final String SHORT_NAME = "kc";
|
||||||
|
public static final String NAME = "keycloak";
|
||||||
|
public static final String PLURAL_NAME = "keycloaks";
|
||||||
|
public static final String MANAGED_BY_LABEL = "app.kubernetes.io/managed-by";
|
||||||
|
public static final String MANAGED_BY_VALUE = "keycloak-operator";
|
||||||
|
|
||||||
|
public static final Map<String, String> DEFAULT_LABELS = Map.of(
|
||||||
|
"app", NAME
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final String DEFAULT_KEYCLOAK_IMAGE = "quay.io/keycloak/keycloak-x:latest";
|
||||||
|
public static final String DEFAULT_KEYCLOAK_INIT_IMAGE = "quay.io/keycloak/keycloak-init-container:latest";
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.v2alpha1;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||||
|
import io.javaoperatorsdk.operator.api.reconciler.*;
|
||||||
|
import io.javaoperatorsdk.operator.api.reconciler.Constants;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.operator.v2alpha1.crds.Keycloak;
|
||||||
|
import org.keycloak.operator.v2alpha1.crds.KeycloakStatus;
|
||||||
|
|
||||||
|
@ControllerConfiguration(namespaces = Constants.WATCH_CURRENT_NAMESPACE, finalizerName = Constants.NO_FINALIZER)
|
||||||
|
public class KeycloakController implements Reconciler<Keycloak> {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Logger logger;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
KubernetesClient client;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UpdateControl<Keycloak> reconcile(Keycloak kc, Context context) {
|
||||||
|
logger.trace("Reconcile loop started");
|
||||||
|
final var spec = kc.getSpec();
|
||||||
|
|
||||||
|
logger.info("Reconciling Keycloak: " + kc.getMetadata().getName() + " in namespace: " + kc.getMetadata().getNamespace());
|
||||||
|
|
||||||
|
KeycloakStatus status = kc.getStatus();
|
||||||
|
var deployment = new KeycloakDeployment(client);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var kcDeployment = deployment.getKeycloakDeployment(kc);
|
||||||
|
|
||||||
|
if (kcDeployment == null) {
|
||||||
|
// Need to create the deployment
|
||||||
|
deployment.createKeycloakDeployment(kc);
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextStatus = deployment.getNextStatus(spec, status, kcDeployment);
|
||||||
|
|
||||||
|
if (!nextStatus.equals(status)) {
|
||||||
|
logger.trace("Updating the status");
|
||||||
|
kc.setStatus(nextStatus);
|
||||||
|
return UpdateControl.updateStatus(kc);
|
||||||
|
} else {
|
||||||
|
logger.trace("Nothing to do");
|
||||||
|
return UpdateControl.noUpdate();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Error reconciling", e);
|
||||||
|
status = new KeycloakStatus();
|
||||||
|
status.setMessage("Error performing operations:\n" + e.getMessage());
|
||||||
|
status.setState(KeycloakStatus.State.ERROR);
|
||||||
|
status.setError(true);
|
||||||
|
|
||||||
|
kc.setStatus(status);
|
||||||
|
return UpdateControl.updateStatus(kc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.v2alpha1;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.api.model.apps.Deployment;
|
||||||
|
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
|
||||||
|
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||||
|
import org.keycloak.operator.v2alpha1.crds.Keycloak;
|
||||||
|
import org.keycloak.operator.v2alpha1.crds.KeycloakSpec;
|
||||||
|
import org.keycloak.operator.v2alpha1.crds.KeycloakStatus;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import static org.keycloak.operator.v2alpha1.crds.KeycloakStatus.State.*;
|
||||||
|
|
||||||
|
public class KeycloakDeployment {
|
||||||
|
|
||||||
|
KubernetesClient client = null;
|
||||||
|
|
||||||
|
KeycloakDeployment(KubernetesClient client) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Deployment baseDeployment;
|
||||||
|
|
||||||
|
public Deployment getKeycloakDeployment(Keycloak keycloak) {
|
||||||
|
// TODO this should be done through an informer to leverage caches
|
||||||
|
// WORKAROUND for: https://github.com/java-operator-sdk/java-operator-sdk/issues/781
|
||||||
|
return client
|
||||||
|
.apps()
|
||||||
|
.deployments()
|
||||||
|
.inNamespace(keycloak.getMetadata().getNamespace())
|
||||||
|
.list()
|
||||||
|
.getItems()
|
||||||
|
.stream()
|
||||||
|
.filter((d) -> d.getMetadata().getName().equals(org.keycloak.operator.Constants.NAME))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
// .withName(Constants.NAME)
|
||||||
|
// .get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createKeycloakDeployment(Keycloak keycloak) {
|
||||||
|
client
|
||||||
|
.apps()
|
||||||
|
.deployments()
|
||||||
|
.inNamespace(keycloak.getMetadata().getNamespace())
|
||||||
|
.create(newKeycloakDeployment(keycloak));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Deployment newKeycloakDeployment(Keycloak keycloak) {
|
||||||
|
if (baseDeployment == null) {
|
||||||
|
URL url = this.getClass().getResource("/base-deployment.yaml");
|
||||||
|
baseDeployment = client.apps().deployments().load(url).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
var deployment = baseDeployment;
|
||||||
|
|
||||||
|
deployment
|
||||||
|
.getSpec()
|
||||||
|
.setReplicas(keycloak.getSpec().getInstances());
|
||||||
|
|
||||||
|
return new DeploymentBuilder(deployment).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeycloakStatus getNextStatus(KeycloakSpec desired, KeycloakStatus prev, Deployment current) {
|
||||||
|
var isReady = (current != null &&
|
||||||
|
current.getStatus() != null &&
|
||||||
|
current.getStatus().getReadyReplicas() != null &&
|
||||||
|
current.getStatus().getReadyReplicas() == desired.getInstances());
|
||||||
|
|
||||||
|
var newStatus = new KeycloakStatus();
|
||||||
|
if (isReady) {
|
||||||
|
newStatus.setState(UNKNOWN);
|
||||||
|
newStatus.setMessage("Keycloak status is unmanaged");
|
||||||
|
} else {
|
||||||
|
newStatus.setState(READY);
|
||||||
|
newStatus.setMessage("Keycloak status is ready");
|
||||||
|
}
|
||||||
|
return newStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.v2alpha1.crds;
|
||||||
|
|
||||||
|
import io.fabric8.kubernetes.api.model.Namespaced;
|
||||||
|
import io.fabric8.kubernetes.client.CustomResource;
|
||||||
|
import io.fabric8.kubernetes.model.annotation.Group;
|
||||||
|
import io.fabric8.kubernetes.model.annotation.Plural;
|
||||||
|
import io.fabric8.kubernetes.model.annotation.ShortNames;
|
||||||
|
import io.fabric8.kubernetes.model.annotation.Version;
|
||||||
|
import org.keycloak.operator.Constants;
|
||||||
|
|
||||||
|
@Group(Constants.CRDS_GROUP)
|
||||||
|
@Version(Constants.CRDS_VERSION)
|
||||||
|
@ShortNames(Constants.SHORT_NAME)
|
||||||
|
@Plural(Constants.PLURAL_NAME)
|
||||||
|
public class Keycloak extends CustomResource<KeycloakSpec, KeycloakStatus> implements Namespaced {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.v2alpha1.crds;
|
||||||
|
|
||||||
|
public class KeycloakSpec {
|
||||||
|
|
||||||
|
private int instances = 1;
|
||||||
|
|
||||||
|
public int getInstances() {
|
||||||
|
return instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstances(int instances) {
|
||||||
|
this.instances = instances;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.v2alpha1.crds;
|
||||||
|
|
||||||
|
public class KeycloakStatus {
|
||||||
|
public enum State {
|
||||||
|
READY,
|
||||||
|
ERROR,
|
||||||
|
UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
private State state = State.UNKNOWN;
|
||||||
|
private boolean error;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public State getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(State state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError(boolean error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeycloakStatus clone() {
|
||||||
|
var status = new KeycloakStatus();
|
||||||
|
status.setMessage(this.message);
|
||||||
|
status.setState(this.state);
|
||||||
|
status.setError(this.error);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
4
operator/src/main/resources/application.properties
Normal file
4
operator/src/main/resources/application.properties
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
quarkus.operator-sdk.crd.apply=true
|
||||||
|
quarkus.operator-sdk.generate-csv=true
|
||||||
|
quarkus.container-image.builder=jib
|
||||||
|
quarkus.operator-sdk.crd.validate=false
|
40
operator/src/main/resources/base-deployment.yaml
Normal file
40
operator/src/main/resources/base-deployment.yaml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/managed-by: keycloak-operator
|
||||||
|
name: keycloak
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: keycloak
|
||||||
|
strategy:
|
||||||
|
rollingUpdate:
|
||||||
|
maxSurge: 25%
|
||||||
|
maxUnavailable: 25%
|
||||||
|
type: RollingUpdate
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: keycloak
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- start-dev
|
||||||
|
image: quay.io/keycloak/keycloak-x:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
name: keycloak
|
||||||
|
ports:
|
||||||
|
- containerPort: 8443
|
||||||
|
protocol: TCP
|
||||||
|
- containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
dnsPolicy: ClusterFirst
|
||||||
|
initContainers:
|
||||||
|
- image: quay.io/keycloak/keycloak-init-container:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
name: init-container
|
||||||
|
restartPolicy: Always
|
||||||
|
terminationGracePeriodSeconds: 30
|
6
operator/src/main/resources/example-keycloak.yml
Normal file
6
operator/src/main/resources/example-keycloak.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
apiVersion: keycloak.io/v2alpha1
|
||||||
|
kind: Keycloak
|
||||||
|
metadata:
|
||||||
|
name: example-kc
|
||||||
|
spec:
|
||||||
|
instances: 1
|
13
pom.xml
13
pom.xml
|
@ -2040,6 +2040,19 @@
|
||||||
</modules>
|
</modules>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>operator</id>
|
||||||
|
<activation>
|
||||||
|
<jdk>[11,)</jdk>
|
||||||
|
<property>
|
||||||
|
<name>operator</name>
|
||||||
|
</property>
|
||||||
|
</activation>
|
||||||
|
<modules>
|
||||||
|
<module>operator</module>
|
||||||
|
</modules>
|
||||||
|
</profile>
|
||||||
|
|
||||||
<profile>
|
<profile>
|
||||||
<id>doclint-java8-disable</id>
|
<id>doclint-java8-disable</id>
|
||||||
<activation>
|
<activation>
|
||||||
|
|
Loading…
Reference in a new issue