Skip to content

Commit 0526f10

Browse files
committed
save kube conf files when current ctx or ns changes
Signed-off-by: Andre Dietisheim <[email protected]>
1 parent 4e06c8c commit 0526f10

File tree

9 files changed

+502
-421
lines changed

9 files changed

+502
-421
lines changed

build.gradle

+11
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ sourceSets {
1717
compileClasspath += sourceSets.main.output + configurations.runtimeClasspath
1818
runtimeClasspath += output + compileClasspath
1919
}
20+
main {
21+
java.srcDirs("src/main/java")
22+
kotlin.srcDirs("src/main/kotlin")
23+
}
24+
test {
25+
java.srcDirs("src/test/java")
26+
kotlin.srcDirs("src/test/kotlin")
27+
// #779: unit tests need to see kubernetes-client classes in src/main/java
28+
compileClasspath += sourceSets.main.output + configurations.runtimeClasspath
29+
runtimeClasspath += output + compileClasspath
30+
}
2031
}
2132

2233
task integrationTest(type: Test) {

src/main/java/io/fabric8/kubernetes/client/Config.java

+20-3
Original file line numberDiff line numberDiff line change
@@ -1789,13 +1789,30 @@ public File getFile() {
17891789
}
17901790

17911791
public KubeConfigFile getFileWithAuthInfo(String name) {
1792-
if (Utils.isNullOrEmpty(name)
1793-
|| Utils.isNullOrEmpty(getFiles())) {
1792+
if (Utils.isNullOrEmpty(name)) {
1793+
return null;
1794+
}
1795+
return getFirstKubeConfigFileMatching(config -> KubeConfigUtils.hasAuthInfoNamed(config, name));
1796+
}
1797+
1798+
public KubeConfigFile getFileWithContext(String name) {
1799+
if (Utils.isNullOrEmpty(name)) {
1800+
return null;
1801+
}
1802+
return getFirstKubeConfigFileMatching(config -> KubeConfigUtils.getContext(config, name) != null);
1803+
}
1804+
1805+
public KubeConfigFile getFileWithCurrentContext() {
1806+
return getFirstKubeConfigFileMatching(config -> Utils.isNotNullOrEmpty(config.getCurrentContext()));
1807+
}
1808+
1809+
private KubeConfigFile getFirstKubeConfigFileMatching(Predicate<io.fabric8.kubernetes.api.model.Config> predicate) {
1810+
if (Utils.isNullOrEmpty(kubeConfigFiles)) {
17941811
return null;
17951812
}
17961813
return kubeConfigFiles.stream()
17971814
.filter(KubeConfigFile::isReadable)
1798-
.filter(entry -> KubeConfigUtils.hasAuthInfoNamed(entry.getConfig(), name))
1815+
.filter(entry -> predicate.test(entry.getConfig()))
17991816
.findFirst()
18001817
.orElse(null);
18011818
}

src/main/java/io/fabric8/kubernetes/client/internal/KubeConfigUtils.java

+21-6
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,31 @@ public static Config parseConfigFromString(String contents) {
6060
public static NamedContext getCurrentContext(Config config) {
6161
String contextName = config.getCurrentContext();
6262
if (contextName != null) {
63+
return getContext(config, contextName);
64+
}
65+
return null;
66+
}
67+
68+
/**
69+
* Returns the {@link NamedContext} with the given name.
70+
* Returns {@code null} otherwise
71+
*
72+
* @param config the config to search
73+
* @param name the context name to match
74+
* @return the context with the the given name
75+
*/
76+
public static NamedContext getContext(Config config, String name) {
77+
NamedContext context = null;
78+
if (config != null && name != null) {
6379
List<NamedContext> contexts = config.getContexts();
6480
if (contexts != null) {
65-
for (NamedContext context : contexts) {
66-
if (contextName.equals(context.getName())) {
67-
return context;
68-
}
69-
}
81+
context = contexts.stream()
82+
.filter(toInspect -> name.equals(toInspect.getName()))
83+
.findAny()
84+
.orElse(null);
7085
}
7186
}
72-
return null;
87+
return context;
7388
}
7489

7590
/**

src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/AllContexts.kt

+2-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ package com.redhat.devtools.intellij.kubernetes.model
1212

1313
import com.intellij.openapi.application.ApplicationManager
1414
import com.intellij.openapi.diagnostic.logger
15-
import com.redhat.devtools.intellij.common.utils.ConfigHelper
1615
import com.redhat.devtools.intellij.common.utils.ConfigWatcher
1716
import com.redhat.devtools.intellij.common.utils.ExecHelper
1817
import com.redhat.devtools.intellij.kubernetes.model.client.ClientAdapter
@@ -249,7 +248,7 @@ open class AllContexts(
249248
* [com.redhat.devtools.intellij.kubernetes.model.client.ClientConfig].
250249
* Closing/Recreating [ConfigWatcher] is needed when used within [com.redhat.devtools.intellij.kubernetes.model.client.ClientConfig].
251250
* The latter gets closed/recreated whenever the context changes in
252-
* [com.redhat.devtools.intellij.kubernetes.model.client.KubeConfigAdapter].
251+
* [com.redhat.devtools.intellij.kubernetes.model.client.KubeConfigPersistence].
253252
*/
254253
val watcher = ConfigWatcher(Paths.get(filename)) { _, config: io.fabric8.kubernetes.api.model.Config? -> onKubeConfigChanged(config) }
255254
runAsync(watcher::run)
@@ -259,8 +258,7 @@ open class AllContexts(
259258
lock.read {
260259
fileConfig ?: return
261260
val client = client.get() ?: return
262-
val clientConfig = client.config.configuration
263-
if (ConfigHelper.areEqual(fileConfig, clientConfig)) {
261+
if (client.config.isEqual(fileConfig)) {
264262
return
265263
}
266264
this.client.reset() // create new client when accessed

src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/ClientConfig.kt

+46-56
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,31 @@
1010
******************************************************************************/
1111
package com.redhat.devtools.intellij.kubernetes.model.client
1212

13+
import com.intellij.openapi.diagnostic.logger
1314
import com.redhat.devtools.intellij.common.utils.ConfigHelper
1415
import com.redhat.devtools.intellij.kubernetes.CompletableFutureUtils.PLATFORM_EXECUTOR
15-
import io.fabric8.kubernetes.api.model.Context
1616
import io.fabric8.kubernetes.api.model.NamedContext
1717
import io.fabric8.kubernetes.client.Client
1818
import io.fabric8.kubernetes.client.Config
1919
import io.fabric8.kubernetes.client.internal.KubeConfigUtils
20+
import java.io.File
2021
import java.util.concurrent.CompletableFuture
2122
import java.util.concurrent.Executor
2223

2324
/**
2425
* An adapter to access [io.fabric8.kubernetes.client.Config].
2526
* It also saves the kube config [KubeConfigUtils] when it changes the client config.
2627
*/
27-
open class ClientConfig(private val client: Client, private val executor: Executor = PLATFORM_EXECUTOR) {
28+
open class ClientConfig(
29+
private val client: Client,
30+
private val executor: Executor = PLATFORM_EXECUTOR,
31+
private val persistence: (io.fabric8.kubernetes.api.model.Config?, String?) -> Unit = KubeConfigUtils::persistKubeConfigIntoFile
32+
) {
2833

29-
open var currentContext: NamedContext?
34+
open val currentContext: NamedContext?
3035
get() {
3136
return configuration.currentContext
3237
}
33-
set(context) {
34-
configuration.currentContext = context
35-
}
3638

3739
open val allContexts: List<NamedContext>
3840
get() {
@@ -43,73 +45,57 @@ open class ClientConfig(private val client: Client, private val executor: Execut
4345
client.configuration
4446
}
4547

46-
protected open val kubeConfig: KubeConfigAdapter by lazy {
47-
KubeConfigAdapter()
48-
}
49-
5048
fun save(): CompletableFuture<Boolean> {
5149
return CompletableFuture.supplyAsync(
5250
{
53-
if (!kubeConfig.exists()) {
54-
return@supplyAsync false
51+
val toSave = mutableMapOf<File, io.fabric8.kubernetes.api.model.Config>()
52+
val withCurrentContext = configuration.fileWithCurrentContext
53+
if (withCurrentContext != null
54+
&& setCurrentContext(withCurrentContext.config)
55+
) {
56+
toSave[withCurrentContext.file] = withCurrentContext.config
5557
}
56-
val fromFile = kubeConfig.load() ?: return@supplyAsync false
57-
if (setCurrentContext(
58-
currentContext,
59-
KubeConfigUtils.getCurrentContext(fromFile),
60-
fromFile
61-
).or( // no short-circuit
62-
setCurrentNamespace(
63-
currentContext?.context,
64-
KubeConfigUtils.getCurrentContext(fromFile)?.context
65-
)
66-
)
58+
val withCurrentNamespace = configuration.getFileWithContext(currentContext?.name)
59+
if (withCurrentNamespace != null
60+
&& setCurrentNamespace(withCurrentNamespace.config)
6761
) {
68-
kubeConfig.save(fromFile)
69-
return@supplyAsync true
70-
} else {
71-
return@supplyAsync false
62+
toSave[withCurrentNamespace.file] = withCurrentNamespace.config
63+
}
64+
toSave.forEach {
65+
save(it.value, it.key)
7266
}
67+
toSave.isNotEmpty()
7368
},
7469
executor
7570
)
7671
}
7772

78-
private fun setCurrentContext(
79-
currentContext: NamedContext?,
80-
kubeConfigCurrentContext: NamedContext?,
81-
kubeConfig: io.fabric8.kubernetes.api.model.Config
82-
): Boolean {
83-
return if (currentContext != null
84-
&& !ConfigHelper.areEqual(currentContext, kubeConfigCurrentContext)
85-
) {
86-
kubeConfig.currentContext = currentContext.name
73+
private fun save(kubeConfig: io.fabric8.kubernetes.api.model.Config?, file: File?) {
74+
if (kubeConfig != null
75+
&& file?.absolutePath != null) {
76+
logger<ClientConfig>().debug("Saving ${file.absolutePath}.")
77+
persistence.invoke(kubeConfig, file.absolutePath)
78+
}
79+
}
80+
81+
private fun setCurrentNamespace(kubeConfig: io.fabric8.kubernetes.api.model.Config?): Boolean {
82+
val currentNamespace = currentContext?.context?.namespace ?: return false
83+
val context = KubeConfigUtils.getContext(kubeConfig, currentContext?.name)
84+
return if (context?.context != null
85+
&& context.context.namespace != currentNamespace) {
86+
context.context.namespace = currentNamespace
8787
true
8888
} else {
8989
false
9090
}
9191
}
9292

93-
/**
94-
* Sets the namespace in the given source [Context] to the given target [Context].
95-
* Does nothing if the target config has no current context
96-
* or if the source config has no current context
97-
* or if setting it would not change it.
98-
*
99-
* @param source Context whose namespace should be copied
100-
* @param target Context whose namespace should be overriden
101-
* @return
102-
*/
103-
private fun setCurrentNamespace(
104-
source: Context?,
105-
target: Context?
106-
): Boolean {
107-
val sourceNamespace = source?.namespace ?: return false
108-
val targetNamespace = target?.namespace
109-
return if (target != null
110-
&& sourceNamespace != targetNamespace
111-
) {
112-
target.namespace = source.namespace
93+
private fun setCurrentContext(kubeConfig: io.fabric8.kubernetes.api.model.Config?): Boolean {
94+
val currentContext = currentContext?.name ?: return false
95+
return if (
96+
kubeConfig != null
97+
&& currentContext != kubeConfig.currentContext) {
98+
kubeConfig.currentContext = currentContext
11399
true
114100
} else {
115101
false
@@ -119,4 +105,8 @@ open class ClientConfig(private val client: Client, private val executor: Execut
119105
fun isCurrent(context: NamedContext): Boolean {
120106
return context == currentContext
121107
}
108+
109+
fun isEqual(config: io.fabric8.kubernetes.api.model.Config): Boolean {
110+
return ConfigHelper.areEqual(config, configuration)
111+
}
122112
}

src/main/kotlin/com/redhat/devtools/intellij/kubernetes/model/client/KubeConfigAdapter.kt

-42
This file was deleted.

0 commit comments

Comments
 (0)