Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade kubernetes-client library to 6.0.0 #51

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@
</scm>

<properties>
<version.commons>3.12.0</version.commons>
<version.micronaut>1.3.7</version.micronaut>
<version.keycloak>11.0.2</version.keycloak>
<version.resteasy>3.6.3.Final</version.resteasy>
<version.logback>1.2.3</version.logback>
<version.logback-contrib>0.1.5</version.logback-contrib>
<version.k8s-client>4.9.2</version.k8s-client>
<version.lombok>1.18.16</version.lombok>
<version.k8s-client>6.0.0</version.k8s-client>
<version.lombok>1.18.24</version.lombok>
<version.com.spotify.ile>1.4.10</version.com.spotify.ile>
<version.janino>3.1.2</version.janino>
<version.mockito>3.7.7</version.mockito>
Expand Down Expand Up @@ -82,6 +83,12 @@
</dependencyManagement>
<dependencies>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${version.commons}</version>
</dependency>

<!-- Micronaut -->
<dependency>
<groupId>io.micronaut</groupId>
Expand Down Expand Up @@ -150,6 +157,13 @@
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<version>${version.k8s-client}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client-api</artifactId>
<version>${version.k8s-client}</version>
<scope>compile</scope>
</dependency>

<!-- Monitoring -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.kiwigrid.keycloak.controller;

import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
import io.micronaut.runtime.Micronaut;
Expand All @@ -17,6 +17,6 @@ public static void main(String[] args) {
@Singleton
@Bean(preDestroy = "close")
KubernetesClient kubernetesClient() {
return new DefaultKubernetesClient();
return new KubernetesClientBuilder().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,39 @@
import java.util.HashMap;
import java.util.Map;

import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinition;
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
import io.fabric8.kubernetes.client.*;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext;
import io.fabric8.kubernetes.internal.KubernetesDeserializer;
import io.micronaut.context.annotation.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class KubernetesController<T extends CustomResource> implements Watcher<T> {
public abstract class KubernetesController<T extends CustomResource<?, ?>> implements Watcher<T> {

protected final Logger log = LoggerFactory.getLogger(getClass());
protected final KubernetesClient kubernetes;
protected final Map<String, T> resources = new HashMap<>();
protected final MixedOperation<T, ? extends CustomResourceList<T>, ?, ?> customResources;
protected final MixedOperation<T, KubernetesResourceList<T>, Resource<T>> customResources;

@Value("${controller.namespaced:true}")
protected boolean namespaced;

protected KubernetesController(KubernetesClient kubernetes,
CustomResourceDefinition crd,
Class<T> type,
Class<? extends CustomResourceList<T>> listType,
Class<? extends CustomResourceDoneable<T>> doneableType) {
protected KubernetesController(
KubernetesClient kubernetes,
Class<T> type
) {
this.kubernetes = kubernetes;
this.customResources = kubernetes.customResources(crd, type, listType, doneableType);
this.customResources = kubernetes.resources(type);

CustomResourceDefinitionContext context = CustomResourceDefinitionContext.fromCustomResourceType(type);

KubernetesDeserializer.registerCustomKind(
crd.getSpec().getGroup() + "/" + crd.getSpec().getVersion(),
crd.getSpec().getNames().getKind(), type);
context.getGroup() + "/" + context.getVersion(),
context.getKind(),
type);
}

public abstract void apply(T resource);
Expand All @@ -43,15 +48,14 @@ protected KubernetesController(KubernetesClient kubernetes,

public Watch watch() {
log.trace("Start watcher.");
if(namespaced) {
if (namespaced) {
return customResources.watch(this);
}
return customResources.inAnyNamespace().watch(this);
}

@Override
public void eventReceived(Action action, T resource) {

var id = resource.getMetadata().getNamespace() + "/" + resource.getMetadata().getName();
log.trace("Received event {} for {}.", action, id);

Expand All @@ -73,7 +77,7 @@ public void eventReceived(Action action, T resource) {
}

@Override
public void onClose(KubernetesClientException cause) {
public void onClose(WatcherException cause) {
if (cause != null) {
log.error("Unexpectedly closed watcher.", cause);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

import com.kiwigrid.keycloak.controller.KubernetesController;
import com.kiwigrid.keycloak.controller.keycloak.KeycloakController;

import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import javax.inject.Singleton;
import javax.ws.rs.NotFoundException;
Expand All @@ -34,8 +37,7 @@ public ClientController(KeycloakController keycloak,
AssignedClientScopesSyncer assignedClientScopesSyncer,
ServiceAccountRoleAssignmentSynchronizer serviceAccountRoleAssignmentSynchronizer) {

super(kubernetes, ClientResource.DEFINITION, ClientResource.class, ClientResource.ClientResourceList.class,
ClientResource.ClientResourceDoneable.class);
super(kubernetes, ClientResource.class);
this.keycloak = keycloak;
this.assignedClientScopesSyncer = assignedClientScopesSyncer;
this.serviceAccountRoleAssignmentSynchronizer = serviceAccountRoleAssignmentSynchronizer;
Expand Down Expand Up @@ -138,14 +140,17 @@ public void delete(ClientResource clientResource) {
@Override
public void retry() {
customResources.list().getItems().stream()
.filter(r -> r.getStatus().getError() != null)
.filter(r -> r.getStatus() != null && r.getStatus().getError() != null)
.forEach(this::apply);
}

// internal

void updateStatus(ClientResource clientResource, String error) {

if (clientResource.getStatus() == null) {
clientResource.setStatus(new ClientResourceStatus());
}
// skip if nothing changed

if (clientResource.getStatus().getTimestamp() != null && Objects.equals(clientResource.getStatus().getError(), error)) {
Expand All @@ -156,7 +161,8 @@ void updateStatus(ClientResource clientResource, String error) {

clientResource.getStatus().setError(error);
clientResource.getStatus().setTimestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS).toString());
customResources.withName(clientResource.getMetadata().getName()).replace(clientResource);

kubernetes.resource(clientResource).replace();
}

Optional<RealmResource> realm(String keycloakName, String realmName) {
Expand All @@ -173,7 +179,7 @@ Optional<RealmResource> realm(String keycloakName, String realmName) {
});
}

boolean map(boolean create, ClientResource.ClientResourceSpec spec, ClientRepresentation client) {
boolean map(boolean create, ClientSpec spec, ClientRepresentation client) {
var changed = false;

if (changed |= changed(create, spec, "name", spec.getName(), client.getName())) {
Expand Down Expand Up @@ -240,7 +246,7 @@ boolean map(boolean create, ClientResource.ClientResourceSpec spec, ClientRepres
return changed;
}

boolean changed(boolean create, ClientResource.ClientResourceSpec spec, String name, Object specValue, Object clientValue) {
boolean changed(boolean create, ClientSpec spec, String name, Object specValue, Object clientValue) {
boolean changed = specValue != null && !specValue.equals(clientValue);
if (changed) {
if (create) {
Expand Down Expand Up @@ -279,12 +285,15 @@ void manageSecret(RealmResource realmResource, String clientUuid, ClientResource

if (kubernetesSecret == null) {

kubernetesSecretsInNamespace.createOrReplaceWithNew()
Secret newSecret = new SecretBuilder()
.withNewMetadata()
.withName(secretName)
.endMetadata()
.addToData(secretKey, Base64.getEncoder().encodeToString(keycloakSecretValue.getBytes()))
.done();
.build();

var secretResource = kubernetes.resource(newSecret).inNamespace(secretNamespace);
secretResource.createOrReplace();

log.info("{}/{}/{}: kubernetes secret {}/{} created",
keycloak, realm, clientId, secretNamespace, secretName);
Expand Down Expand Up @@ -355,7 +364,7 @@ void manageMapper(RealmResource realmResource, String clientUuid, ClientResource

// remove obsolete mappers

var names = specMappers.stream().map(ClientResource.ClientMapper::getName).collect(Collectors.toSet());
var names = specMappers.stream().map(ClientMapper::getName).collect(Collectors.toSet());
for (var mapper : keycloakMappers) {
if (!names.contains(mapper.getName())) {
keycloakResource.delete(mapper.getId());
Expand Down Expand Up @@ -383,7 +392,7 @@ private void manageRoles(RealmResource realmResource, String clientUuid, ClientR

// remove obsolete roles

var specRoleNames = specRoles.stream().map(ClientResource.ClientRole::getName).collect(Collectors.toSet());
var specRoleNames = specRoles.stream().map(ClientRole::getName).collect(Collectors.toSet());
for (var clientRoleRepresentation : clientRoleRepresentations) {
if (!specRoleNames.contains(clientRoleRepresentation.getName())) {
clientRolesResource.deleteRole(clientRoleRepresentation.getName());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.kiwigrid.keycloak.controller.client;

import java.util.Map;

@lombok.Getter
@lombok.Setter
@lombok.EqualsAndHashCode
public class ClientMapper {

private String name;
private String protocolMapper;
private Map<String, String> config;
}
Original file line number Diff line number Diff line change
@@ -1,100 +1,29 @@
package com.kiwigrid.keycloak.controller.client;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import io.fabric8.kubernetes.api.builder.Function;
import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinition;
import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinitionBuilder;
import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinitionSpec;
import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinitionStatus;
import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.client.CustomResourceDoneable;
import io.fabric8.kubernetes.client.CustomResourceList;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Kind;
import io.fabric8.kubernetes.model.annotation.Plural;
import io.fabric8.kubernetes.model.annotation.ShortNames;
import io.fabric8.kubernetes.model.annotation.Singular;
import io.fabric8.kubernetes.model.annotation.Version;

@SuppressWarnings("serial")
@lombok.Getter
@lombok.Setter
@lombok.EqualsAndHashCode(of = "spec", callSuper = false)
public class ClientResource extends CustomResource {

public static final CustomResourceDefinition DEFINITION = new CustomResourceDefinitionBuilder()
.withNewSpec()
.withScope("Namespaced")
.withGroup("k8s.kiwigrid.com")
.withVersion("v1beta1")
.withNewNames()
.withKind("KeycloakClient")
.withSingular("keycloakclient")
.withPlural("keycloakclients")
.withShortNames("kcc")
.endNames()
.endSpec().build();

private ClientResourceSpec spec = new ClientResourceSpec();
private ClientResourceStatus status = new ClientResourceStatus();

@lombok.Getter
@lombok.Setter
@lombok.EqualsAndHashCode(callSuper = false)
public static class ClientResourceSpec extends CustomResourceDefinitionSpec {

private String keycloak = "default";
private String realm;
private String clientId;
private ClientType clientType;
private String name;
private Boolean enabled;
private Boolean directAccessGrantsEnabled;
private Boolean standardFlowEnabled;
private Boolean implicitFlowEnabled;
private Boolean serviceAccountsEnabled;
private List<String> defaultRoles = new ArrayList<>();
private List<String> defaultClientScopes = new ArrayList<>();
private List<String> optionalClientScopes = new ArrayList<>();
private List<String> webOrigins = new ArrayList<>();
private List<String> redirectUris = new ArrayList<>();
private String secretNamespace;
private String secretName;
private String secretKey = "secret";
private List<ClientMapper> mapper = new ArrayList<>();
private List<ClientRole> roles = new ArrayList<>();
private List<String> serviceAccountRealmRoles = new ArrayList<>();
}

@lombok.Getter
@lombok.Setter
@lombok.EqualsAndHashCode
public static class ClientMapper {

private String name;
private String protocolMapper;
private Map<String, String> config;
}

@lombok.Getter
@lombok.Setter
@lombok.EqualsAndHashCode
public static class ClientRole {

private String name;
private List<String> realmRoles;
}

@lombok.Getter
@lombok.Setter
public static class ClientResourceStatus extends CustomResourceDefinitionStatus {

private String timestamp;
private String error;
}

public static class ClientResourceList extends CustomResourceList<ClientResource> {}

public static class ClientResourceDoneable extends CustomResourceDoneable<ClientResource> {
public ClientResourceDoneable(ClientResource resource, Function<ClientResource, ClientResource> function) {
super(resource, function);
}
@lombok.EqualsAndHashCode(callSuper = false)
@Group("k8s.kiwigrid.com")
@Version("v1beta1")
@Kind("KeycloakClient")
@Singular("keycloakclient")
@Plural("keycloakclients")
@ShortNames("kcc")
public class ClientResource extends CustomResource<ClientSpec, ClientResourceStatus> implements Namespaced {

public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.kiwigrid.keycloak.controller.client;

@lombok.Getter
@lombok.Setter
public class ClientResourceStatus {

private String timestamp;
private String error;
}
Loading