Skip to content

Commit

Permalink
fix: bump kubernetes-client to 7.0.0 (redhat-developer#794)
Browse files Browse the repository at this point in the history
Signed-off-by: Andre Dietisheim <[email protected]>
  • Loading branch information
adietish committed Dec 20, 2024
1 parent 719d72c commit 1e8ea78
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 130 deletions.
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ org.gradle.configuration-cache=true
org.gradle.caching=true

kotlin.daemon.jvm.options=-Xmx2g
org.gradle.jvmargs=-Xmx2g
org.gradle.jvmargs=-Xmx2g

4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]
# libraries
kubernetes-client = "6.12.0"
kubernetes-client = "7.0.0"
devtools-common = "1.9.7-SNAPSHOT"
jackson-core = "2.17.0"
commons-lang3 = "3.12.0"
Expand All @@ -19,7 +19,7 @@ kotlinJvm = "2.0.20"
[libraries]
openshift-client = { group = "io.fabric8", name = "openshift-client", version.ref = "kubernetes-client" }
kubernetes-client = { group = "io.fabric8", name = "kubernetes-client", version.ref = "kubernetes-client" }
kubernetes-model = { group = "io.fabric8", name = "kubernetes-model", version.ref = "kubernetes-client" }
kubernetes-model = { group = "io.fabric8", name = "kubernetes-model-core", version.ref = "kubernetes-client" }
kubernetes-model-common = { group = "io.fabric8", name = "kubernetes-model-common", version.ref = "kubernetes-client" }
kubernetes-httpclient-okhttp = { group = "io.fabric8", name = "kubernetes-httpclient-okhttp", version.ref = "kubernetes-client" }
devtools-common = { group = "com.redhat.devtools.intellij", name = "intellij-common", version.ref = "devtools-common" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ package com.redhat.devtools.intellij.kubernetes.model

import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.logger
import com.redhat.devtools.intellij.common.utils.ConfigHelper
import com.redhat.devtools.intellij.common.utils.ConfigWatcher
import com.redhat.devtools.intellij.common.utils.ExecHelper
import com.redhat.devtools.intellij.kubernetes.model.client.ClientAdapter
Expand All @@ -30,7 +29,6 @@ import com.redhat.devtools.intellij.kubernetes.telemetry.TelemetryService.PROP_O
import io.fabric8.kubernetes.api.model.HasMetadata
import io.fabric8.kubernetes.client.Config
import io.fabric8.kubernetes.client.KubernetesClient
import java.nio.file.Paths
import java.util.concurrent.CompletionException
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
Expand Down Expand Up @@ -83,11 +81,13 @@ open class AllContexts(
namespace: String?,
context: String?
) -> ClientAdapter<out KubernetesClient>
= { namespace, context -> ClientAdapter.Factory.create(namespace, context) }
= { namespace, config ->
ClientAdapter.Factory.create(namespace, config)
}
) : IAllContexts {

init {
watchKubeConfig()
watchKubeConfigs()
}

private val lock = ReentrantReadWriteLock()
Expand Down Expand Up @@ -148,7 +148,8 @@ open class AllContexts(
) : IActiveContext<out HasMetadata, out KubernetesClient>? {
lock.write {
try {
replaceClient(newClient, this.client.get())
client.get()?.close()
client.set(newClient)
newClient.config.save().join()
current?.close()
clearAllContexts() // causes reload of all contexts when accessed afterwards
Expand Down Expand Up @@ -204,13 +205,6 @@ open class AllContexts(
}
}

private fun replaceClient(new: ClientAdapter<out KubernetesClient>, old: ClientAdapter<out KubernetesClient>?)
: ClientAdapter<out KubernetesClient> {
old?.close()
this.client.set(new)
return new
}

private fun createActiveContext(client: ClientAdapter<out KubernetesClient>?)
: IActiveContext<out HasMetadata, out KubernetesClient>? {
if (client == null) {
Expand Down Expand Up @@ -241,8 +235,7 @@ open class AllContexts(
}
}

protected open fun watchKubeConfig() {
val path = Paths.get(Config.getKubeconfigFilename())
protected open fun watchKubeConfigs() {
/**
* [ConfigWatcher] cannot add/remove listeners nor can it get closed (and stop the [java.nio.file.WatchService]).
* We therefore have to create a single instance in here rather than using it in a shielded/private way within
Expand All @@ -251,16 +244,16 @@ open class AllContexts(
* The latter gets closed/recreated whenever the context changes in
* [com.redhat.devtools.intellij.kubernetes.model.client.KubeConfigAdapter].
*/
val watcher = ConfigWatcher(path) { _, config: io.fabric8.kubernetes.api.model.Config? -> onKubeConfigChanged(config) }
val watcher = ConfigWatcher { config: Config? -> onKubeConfigChanged(config) }
runAsync(watcher::run)
}

protected open fun onKubeConfigChanged(fileConfig: io.fabric8.kubernetes.api.model.Config?) {
protected open fun onKubeConfigChanged(updated: Config?) {
lock.read {
fileConfig ?: return
updated ?: return
val client = client.get() ?: return
val clientConfig = client.config.configuration
if (ConfigHelper.areEqual(fileConfig, clientConfig)) {
val existing = client.config
if (existing.isEqualConfig(updated)) {
return
}
this.client.reset() // create new client when accessed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ open class OSClientAdapter(client: OpenShiftClient, private val kubeClient: Kube

override val config by lazy {
// openshift client configuration does not have kube config entries
ClientConfig(kubeClient)
ClientConfig(kubeClient.configuration)
}

override fun isOpenShift(): Boolean {
Expand All @@ -56,47 +56,31 @@ open class KubeClientAdapter(client: KubernetesClient) :
abstract class ClientAdapter<C : KubernetesClient>(private val fabric8Client: C) {

companion object Factory {

fun create(
namespace: String? = null,
context: String? = null,
clientBuilder: KubernetesClientBuilder = KubernetesClientBuilder(),
trustManagerProvider: ((toIntegrate: List<X509ExtendedTrustManager>) -> X509TrustManager)
= IDEATrustManager()::configure
clientBuilder: KubernetesClientBuilder? = null,
externalTrustManagerProvider: ((toIntegrate: List<X509ExtendedTrustManager>) -> X509TrustManager)? = null
): ClientAdapter<out KubernetesClient> {
val config = Config.autoConfigure(context)
return create(namespace, config, clientBuilder, trustManagerProvider)
}

fun create(
namespace: String? = null,
config: Config,
clientBuilder: KubernetesClientBuilder,
externalTrustManagerProvider: (toIntegrate: List<X509ExtendedTrustManager>) -> X509TrustManager
= IDEATrustManager()::configure
): ClientAdapter<out KubernetesClient> {
setNamespace(namespace, config)
val kubeClient = clientBuilder
val builder = clientBuilder ?: KubernetesClientBuilder()
val trustManager = externalTrustManagerProvider ?: IDEATrustManager()::configure
val kubeClient = builder
.withConfig(config)
.withHttpClientBuilderConsumer { builder ->
setSslContext(builder, config, externalTrustManagerProvider)
.withHttpClientBuilderConsumer { httpClientBuilder ->
setSslContext(httpClientBuilder, config, trustManager)
}
.build()
return if (isOpenShift(kubeClient)) {
return if (ClusterHelper.isOpenShift(kubeClient)) {
val osClient = kubeClient.adapt(NamespacedOpenShiftClient::class.java)
OSClientAdapter(osClient, kubeClient)
} else {
KubeClientAdapter(kubeClient)
}
}

private fun isOpenShift(client: KubernetesClient): Boolean {
return try {
ClusterHelper.isOpenShift(client)
} catch (e: Exception) {
false;
}
}

private fun setSslContext(
builder: HttpClient.Builder,
config: Config,
Expand Down Expand Up @@ -124,7 +108,7 @@ abstract class ClientAdapter<C : KubernetesClient>(private val fabric8Client: C)
private val clients = ConcurrentHashMap<Class<out Client>, Client>()

open val config by lazy {
ClientConfig(fabric8Client)
ClientConfig(fabric8Client.configuration)
}

abstract fun isOpenShift(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ package com.redhat.devtools.intellij.kubernetes.model.client

import com.redhat.devtools.intellij.common.utils.ConfigHelper
import com.redhat.devtools.intellij.kubernetes.CompletableFutureUtils.PLATFORM_EXECUTOR
import io.fabric8.kubernetes.api.model.Context
import com.redhat.devtools.intellij.kubernetes.model.util.ConfigUtils
import io.fabric8.kubernetes.api.model.NamedContext
import io.fabric8.kubernetes.client.Client
import io.fabric8.kubernetes.client.Config
import io.fabric8.kubernetes.client.internal.KubeConfigUtils
import java.util.concurrent.CompletableFuture
Expand All @@ -24,98 +23,53 @@ import java.util.concurrent.Executor
* An adapter to access [io.fabric8.kubernetes.client.Config].
* It also saves the kube config [KubeConfigUtils] when it changes the client config.
*/
open class ClientConfig(private val client: Client, private val executor: Executor = PLATFORM_EXECUTOR) {
open class ClientConfig(private val config: Config, private val executor: Executor = PLATFORM_EXECUTOR) {

open var currentContext: NamedContext?
get() {
return configuration.currentContext
return config.currentContext
}
set(context) {
configuration.currentContext = context
config.currentContext = context
}

open val allContexts: List<NamedContext>
get() {
return configuration.contexts ?: emptyList()
return config.contexts ?: emptyList()
}

open val configuration: Config by lazy {
client.configuration
}

protected open val kubeConfig: KubeConfigAdapter by lazy {
KubeConfigAdapter()
fun isEqualConfig(config: Config): Boolean {
return ConfigHelper.areEqualCurrentContext(config, this.config)
&& ConfigHelper.areEqualContexts(config, this.config)
&& ConfigHelper.areEqualCluster(config, this.config)
&& ConfigHelper.areEqualAuthInfo(config, this.config)
}

fun save(): CompletableFuture<Boolean> {
return CompletableFuture.supplyAsync(
{
if (!kubeConfig.exists()) {
return@supplyAsync false
val modified = HashSet<KubeConfigAdapter>()

val currentContextPair = ConfigUtils.getFileWithCurrentContext()
if (currentContextPair != null) {
val kubeConfig = KubeConfigAdapter(currentContextPair.first, currentContextPair.second)
if (kubeConfig.setCurrentContext(config.currentContext?.name)) {
modified.add(kubeConfig)
}
}
val fromFile = kubeConfig.load() ?: return@supplyAsync false
return@supplyAsync if (setCurrentContext(
currentContext,
KubeConfigUtils.getCurrentContext(fromFile),
fromFile
).or( // no short-circuit
setCurrentNamespace(
currentContext?.context,
KubeConfigUtils.getCurrentContext(fromFile)?.context
)
)
) {
kubeConfig.save(fromFile)
true
} else {
false
val kubeConfig = KubeConfigAdapter(config.file)
if (kubeConfig.setCurrentNamespace(config.currentContext?.name, config.namespace)) {
modified.add(kubeConfig)
}
modified.forEach { kubeConfig ->
kubeConfig.save()
}
modified.isNotEmpty()
},
executor
)
}

private fun setCurrentContext(
currentContext: NamedContext?,
kubeConfigCurrentContext: NamedContext?,
kubeConfig: io.fabric8.kubernetes.api.model.Config
): Boolean {
return if (currentContext != null
&& !ConfigHelper.areEqual(currentContext, kubeConfigCurrentContext)
) {
kubeConfig.currentContext = currentContext.name
true
} else {
false
}
}

/**
* Sets the namespace in the given source [Context] to the given target [Context].
* Does nothing if the target config has no current context
* or if the source config has no current context
* or if setting it would not change it.
*
* @param source Context whose namespace should be copied
* @param target Context whose namespace should be overriden
* @return
*/
private fun setCurrentNamespace(
source: Context?,
target: Context?
): Boolean {
val sourceNamespace = source?.namespace ?: return false
val targetNamespace = target?.namespace
return if (target != null
&& sourceNamespace != targetNamespace
) {
target.namespace = source.namespace
true
} else {
false
}
}

fun isCurrent(context: NamedContext): Boolean {
return context == currentContext
}
Expand Down
Loading

0 comments on commit 1e8ea78

Please sign in to comment.