Skip to content

Commit d950cea

Browse files
committed
fix: create lazy OpenShift context to avoid check timeouts (#865)
Signed-off-by: Andre Dietisheim <[email protected]>
1 parent f8dd215 commit d950cea

18 files changed

+504
-90
lines changed

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

+2-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ package com.redhat.devtools.intellij.kubernetes.model
1313
import com.intellij.openapi.application.ApplicationManager
1414
import com.intellij.openapi.diagnostic.logger
1515
import com.redhat.devtools.intellij.common.utils.ConfigWatcher
16-
import com.redhat.devtools.intellij.common.utils.ExecHelper
1716
import com.redhat.devtools.intellij.kubernetes.model.client.ClientAdapter
1817
import com.redhat.devtools.intellij.kubernetes.model.client.ClientConfig
1918
import com.redhat.devtools.intellij.kubernetes.model.context.Context
@@ -77,7 +76,7 @@ interface IAllContexts {
7776

7877
open class AllContexts(
7978
private val contextFactory: (ClientAdapter<out KubernetesClient>, IResourceModelObservable) -> IActiveContext<out HasMetadata, out KubernetesClient>? =
80-
IActiveContext.Factory::create,
79+
IActiveContext.Factory::createLazyOpenShift,
8180
private val modelChange: IResourceModelObservable,
8281
private val clientFactory: (
8382
namespace: String?,
@@ -223,7 +222,7 @@ open class AllContexts(
223222
}
224223

225224
protected open fun reportTelemetry(context: IActiveContext<out HasMetadata, out KubernetesClient>) {
226-
ExecHelper.submit {
225+
runAsync {
227226
val telemetry = TelemetryService.instance.action(NAME_PREFIX_CONTEXT + "use")
228227
.property(PROP_IS_OPENSHIFT, context.isOpenShift().toString())
229228
try {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ open class ResourceModel : IResourceModel {
8383
}
8484

8585
protected open val allContexts: IAllContexts by lazy {
86-
AllContexts(IActiveContext.Factory::create, modelChange)
86+
AllContexts(IActiveContext.Factory::createLazyOpenShift, modelChange)
8787
}
8888

8989
override fun setCurrentContext(context: IContext) {

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

+35-7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ open class OSClientAdapter(client: OpenShiftClient, private val kubeClient: Kube
3737
ClientConfig(kubeClient.configuration)
3838
}
3939

40+
override fun toOpenShift(): OSClientAdapter {
41+
return this
42+
}
43+
4044
override fun isOpenShift(): Boolean {
4145
return true
4246
}
@@ -52,17 +56,33 @@ open class KubeClientAdapter(client: KubernetesClient) :
5256
override fun isOpenShift(): Boolean {
5357
return false
5458
}
59+
60+
override fun toOpenShift(): OSClientAdapter {
61+
val kubeClient = get()
62+
val osClient = kubeClient.adapt(NamespacedOpenShiftClient::class.java)
63+
return OSClientAdapter(osClient, kubeClient)
64+
}
5565
}
5666

5767
abstract class ClientAdapter<C : KubernetesClient>(private val fabric8Client: C) {
5868

5969
companion object Factory {
6070

71+
const val TIMEOUT_CONNECTION = 5000
72+
const val TIMEOUT_REQUEST = 5000
73+
const val LIMIT_RECONNECT = 2
74+
6175
fun create(
6276
namespace: String? = null,
6377
context: String? = null,
6478
clientBuilder: KubernetesClientBuilder? = null,
65-
createConfig: (context: String?) -> Config = { context -> Config.autoConfigure(context) },
79+
createConfig: (context: String?) -> Config = { context ->
80+
val config = Config.autoConfigure(context)
81+
config.connectionTimeout = TIMEOUT_CONNECTION
82+
config.requestTimeout = TIMEOUT_REQUEST
83+
config.watchReconnectLimit = LIMIT_RECONNECT
84+
config
85+
},
6686
externalTrustManagerProvider: ((toIntegrate: List<X509ExtendedTrustManager>) -> X509TrustManager)? = null
6787
): ClientAdapter<out KubernetesClient> {
6888
KubeConfigEnvValue.copyToSystem()
@@ -76,12 +96,14 @@ abstract class ClientAdapter<C : KubernetesClient>(private val fabric8Client: C)
7696
setSslContext(httpClientBuilder, config, trustManager)
7797
}
7898
.build()
79-
return if (ClusterHelper.isOpenShift(kubeClient)) {
80-
val osClient = kubeClient.adapt(NamespacedOpenShiftClient::class.java)
81-
OSClientAdapter(osClient, kubeClient)
82-
} else {
83-
KubeClientAdapter(kubeClient)
84-
}
99+
/**
100+
* Always create kubernetes client.
101+
* Upgrade existing client to OpenShift only async bcs checking if cluster is OpenShift is costly
102+
* and may timeout if cluster is not reachable.
103+
* @see [issue 865](https://github.com/redhat-developer/intellij-kubernetes/issues/865)
104+
* @see ClientAdapter.toOpenShift
105+
**/
106+
return KubeClientAdapter(kubeClient)
85107
}
86108

87109
private fun setSslContext(
@@ -114,6 +136,8 @@ abstract class ClientAdapter<C : KubernetesClient>(private val fabric8Client: C)
114136
ClientConfig(fabric8Client.configuration)
115137
}
116138

139+
abstract fun toOpenShift(): OSClientAdapter
140+
117141
abstract fun isOpenShift(): Boolean
118142

119143
fun get(): C {
@@ -145,6 +169,10 @@ abstract class ClientAdapter<C : KubernetesClient>(private val fabric8Client: C)
145169
}
146170
}
147171

172+
fun canAdaptToOpenShift(): Boolean {
173+
return ClusterHelper.isOpenShift(fabric8Client)
174+
}
175+
148176
open fun close() {
149177
clients.values.forEach{ it.close() }
150178
fabric8Client.close()

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

+13-10
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ import java.net.URL
5252

5353
abstract class ActiveContext<N : HasMetadata, C : KubernetesClient>(
5454
context: NamedContext,
55-
private val modelChange: IResourceModelObservable,
55+
protected val modelChange: IResourceModelObservable,
5656
val client: ClientAdapter<out C>,
57-
protected open val dashboard: IDashboard,
57+
protected open val dashboard: IDashboard? = null,
5858
private var singleResourceOperator: NonCachingSingleResourceOperator = NonCachingSingleResourceOperator(client),
5959
) : Context(context), IActiveContext<N, C> {
6060

@@ -72,8 +72,6 @@ abstract class ActiveContext<N : HasMetadata, C : KubernetesClient>(
7272
ClusterHelper.getClusterInfo(client.get())
7373
}
7474

75-
protected abstract val namespaceKind : ResourceKind<N>
76-
7775
private val extensionName: ExtensionPointName<IResourceOperatorFactory<HasMetadata, KubernetesClient, IResourceOperator<HasMetadata>>> =
7876
ExtensionPointName("com.redhat.devtools.intellij.kubernetes.resourceOperators")
7977

@@ -307,7 +305,7 @@ abstract class ActiveContext<N : HasMetadata, C : KubernetesClient>(
307305
override fun stopWatch(kind: ResourceKind<out HasMetadata>) {
308306
logger<ActiveContext<*, *>>().debug("Stop watching $kind resources.")
309307
watch.stopWatch(kind)
310-
// don't notify invalidation change because this would cause UI to reload
308+
// don't notify invalidation change because this would cause the UI to reload
311309
// and therefore to repopulate the cache immediately.
312310
// Any resource operation that eventually happens while the watch is not active would cause the cache
313311
// to become out-of-sync and it would therefore return invalid resources when asked to do so
@@ -489,27 +487,32 @@ abstract class ActiveContext<N : HasMetadata, C : KubernetesClient>(
489487
override fun close() {
490488
logger<ActiveContext<*, *>>().debug("Closing context $name.")
491489
watch.close()
492-
dashboard.close()
490+
dashboard?.close()
493491
}
494492

495493
private fun <P: IResourceOperator<out HasMetadata>> getAllResourceOperators(type: Class<P>)
496494
: MutableMap<ResourceKind<out HasMetadata>, P> {
497495
val operators = mutableMapOf<ResourceKind<out HasMetadata>, P>()
498496
operators.putAll(
499-
getInternalResourceOperators(client)
497+
getInternalResourceOperators()
500498
.filterIsInstance(type)
501499
.associateBy { it.kind })
502500
operators.putAll(
503-
getExtensionResourceOperators(client)
501+
getExtensionResourceOperators()
504502
.filterIsInstance(type)
505503
.associateBy { it.kind })
506504
return operators
507505
}
508506

509-
protected abstract fun getInternalResourceOperators(client: ClientAdapter<out C>): List<IResourceOperator<out HasMetadata>>
507+
abstract override fun getInternalResourceOperators(): List<IResourceOperator<out HasMetadata>>
510508

511-
protected open fun getExtensionResourceOperators(client: ClientAdapter<out C>): List<IResourceOperator<out HasMetadata>> {
509+
protected open fun getExtensionResourceOperators(): List<IResourceOperator<out HasMetadata>> {
512510
return extensionName.extensionList
513511
.map { it.create(client.get()) }
514512
}
513+
514+
override fun getDashboardUrl(): String? {
515+
return dashboard?.get()
516+
}
517+
515518
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface IContext {
1919
val namespace: String?
2020
}
2121

22-
open class Context(private val context: NamedContext): IContext {
22+
open class Context(protected val context: NamedContext): IContext {
2323
override val active: Boolean = false
2424
override val name: String?
2525
get() = context.name

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

+30-18
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import com.redhat.devtools.intellij.kubernetes.model.IResourceModelObservable
1515
import com.redhat.devtools.intellij.kubernetes.model.client.ClientAdapter
1616
import com.redhat.devtools.intellij.kubernetes.model.client.KubeClientAdapter
1717
import com.redhat.devtools.intellij.kubernetes.model.client.OSClientAdapter
18+
import com.redhat.devtools.intellij.kubernetes.model.resource.IResourceOperator
1819
import com.redhat.devtools.intellij.kubernetes.model.resource.ResourceKind
1920
import com.redhat.devtools.intellij.kubernetes.model.resource.kubernetes.KubernetesReplicas.Replicator
2021
import io.fabric8.kubernetes.api.model.GenericKubernetesResource
@@ -23,30 +24,29 @@ import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition
2324
import io.fabric8.kubernetes.client.KubernetesClient
2425
import io.fabric8.kubernetes.client.Watch
2526
import io.fabric8.kubernetes.client.Watcher
27+
import io.fabric8.openshift.api.model.Project
28+
import io.fabric8.openshift.client.OpenShiftClient
2629
import java.net.URL
2730

2831
interface IActiveContext<N: HasMetadata, C: KubernetesClient>: IContext {
2932

3033
companion object Factory {
31-
fun create(
34+
fun createLazyOpenShift(
3235
client: ClientAdapter<out KubernetesClient>,
33-
observable: IResourceModelObservable
36+
modelChange: IResourceModelObservable
3437
): IActiveContext<out HasMetadata, out KubernetesClient>? {
3538
val currentContext = client.config.currentContext ?: return null
36-
return if (client.isOpenShift()) {
37-
OpenShiftContext(
38-
currentContext,
39-
observable,
40-
client as OSClientAdapter
41-
)
42-
} else {
43-
KubernetesContext(
44-
currentContext,
45-
observable,
46-
client as KubeClientAdapter
47-
)
48-
}
39+
return LazyOpenShiftContext(currentContext, modelChange, client as KubeClientAdapter)
4940
}
41+
42+
fun createOpenShift(
43+
client: OSClientAdapter,
44+
modelChange: IResourceModelObservable
45+
): IActiveContext<Project, OpenShiftClient>? {
46+
val currentContext = client.config.currentContext ?: return null
47+
return OpenShiftContext(currentContext, modelChange, client)
48+
}
49+
5050
}
5151

5252
/**
@@ -65,6 +65,8 @@ interface IActiveContext<N: HasMetadata, C: KubernetesClient>: IContext {
6565
}
6666
}
6767

68+
val namespaceKind : ResourceKind<out HasMetadata>
69+
6870
/**
6971
* The master url for this context. This is the url of the cluster for this context.
7072
*/
@@ -75,6 +77,16 @@ interface IActiveContext<N: HasMetadata, C: KubernetesClient>: IContext {
7577
*/
7678
val version: ClusterInfo
7779

80+
/**
81+
* Returns the list of internal [IResourceOperator]s that are available in this context.
82+
* [IResourceOperator]s that are contributed by registrations to the extension point are not included.
83+
*
84+
* @return the list of [IResourceOperator]s that are available in this context.
85+
* @see IResourceOperator
86+
* @see com.redhat.devtools.intellij.kubernetes.model.context.ActiveContext.getExtensionResourceOperators
87+
*/
88+
fun getInternalResourceOperators(): List<IResourceOperator<out HasMetadata>>
89+
7890
/**
7991
* Returns {@code true} if this context is an OpenShift context. This is true for context with an OpenShift cluster.
8092
*/
@@ -129,7 +141,7 @@ interface IActiveContext<N: HasMetadata, C: KubernetesClient>: IContext {
129141
fun getAllResources(definition: CustomResourceDefinition): Collection<GenericKubernetesResource>
130142

131143
/**
132-
* Returns the latest version of the given resource from cluster. Returns `null` if none was found.
144+
* Returns the latest version of the given resource from the cluster. Returns `null` if none was found.
133145
*
134146
* @param resource which is to be requested from cluster
135147
*
@@ -265,7 +277,7 @@ interface IActiveContext<N: HasMetadata, C: KubernetesClient>: IContext {
265277
/**
266278
* Notifies the context that the given resource was replaced in the cluster.
267279
* Replaces the resource with the given new version if it exists.
268-
* Does nothing otherwiese.
280+
* Does nothing otherwise.
269281
*
270282
*
271283
* @param resource the new (version) of the resource
@@ -279,7 +291,7 @@ interface IActiveContext<N: HasMetadata, C: KubernetesClient>: IContext {
279291
*
280292
* @return the url of the Dashboard for this context
281293
*/
282-
fun getDashboardUrl(): String
294+
fun getDashboardUrl(): String?
283295

284296
/**
285297
* Closes and disposes this context.

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

+6-9
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ open class KubernetesContext(
3232
context: NamedContext,
3333
modelChange: IResourceModelObservable,
3434
client: KubeClientAdapter,
35-
) : ActiveContext<Namespace, KubernetesClient>(
35+
) : ActiveContext<HasMetadata, KubernetesClient>(
3636
context,
3737
modelChange,
3838
client,
@@ -43,7 +43,7 @@ open class KubernetesContext(
4343
)
4444
) {
4545

46-
override val namespaceKind : ResourceKind<Namespace> = NamespacesOperator.KIND
46+
override val namespaceKind : ResourceKind<out HasMetadata> = NamespacesOperator.KIND
4747

4848
private val replicasOperator = KubernetesReplicas(
4949
NonCachingSingleResourceOperator(client),
@@ -54,12 +54,14 @@ open class KubernetesContext(
5454
}
5555
)
5656

57-
override fun getInternalResourceOperators(client: ClientAdapter<out KubernetesClient>)
57+
override fun getInternalResourceOperators()
5858
: List<IResourceOperator<out HasMetadata>> {
5959
return OperatorFactory.createKubernetes(client)
6060
}
6161

62-
override fun isOpenShift() = false
62+
override fun isOpenShift(): Boolean {
63+
return false
64+
}
6365

6466
override fun setReplicas(replicas: Int, replicator: Replicator) {
6567
replicasOperator.set(replicas, replicator)
@@ -69,9 +71,4 @@ open class KubernetesContext(
6971
return replicasOperator.get(resource)
7072
}
7173

72-
override fun getDashboardUrl(): String {
73-
return dashboard.get()
74-
}
75-
76-
7774
}

0 commit comments

Comments
 (0)