Skip to content

Commit

Permalink
fix: display kubeconfig parse error on startup (#809)
Browse files Browse the repository at this point in the history
Signed-off-by: Andre Dietisheim <[email protected]>
  • Loading branch information
adietish committed Jan 21, 2025
1 parent 52ad428 commit 4727cb9
Show file tree
Hide file tree
Showing 14 changed files with 564 additions and 508 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ class SetAsCurrentClusterAction: StructureTreeAction(IContext::class.java) {
val context: IContext = selectedNode?.getElement() ?: return
val telemetry = TelemetryService.instance
.action(NAME_PREFIX_CONTEXT + "switch")
run("Setting ${context.context.name} as current cluster...", true,
run("Setting ${context.name} as current cluster...", true,
Progressive {
try {
getResourceModel()?.setCurrentContext(context)
telemetry.success().send()
} catch (e: Exception) {
logger<SetAsCurrentClusterAction>().warn(
"Could not set current context to ${context.context.name}.", e
"Could not set current context to ${context.name}.", e
)
telemetry.error(e).send()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import com.redhat.devtools.intellij.kubernetes.model.context.IActiveContext
import com.redhat.devtools.intellij.kubernetes.model.context.IContext
import com.redhat.devtools.intellij.kubernetes.model.resource.ResourceKind
import com.redhat.devtools.intellij.kubernetes.model.util.ResettableLazyProperty
import com.redhat.devtools.intellij.kubernetes.model.util.ResourceException
import com.redhat.devtools.intellij.kubernetes.model.util.toMessage
import com.redhat.devtools.intellij.kubernetes.telemetry.TelemetryService
import com.redhat.devtools.intellij.kubernetes.telemetry.TelemetryService.NAME_PREFIX_CONTEXT
import com.redhat.devtools.intellij.kubernetes.telemetry.TelemetryService.PROP_IS_OPENSHIFT
Expand Down Expand Up @@ -110,10 +112,13 @@ open class AllContexts(
lock.write {
if (_all.isEmpty()) {
try {
val all = createContexts(client.get(), client.get()?.config)
_all.addAll(all)
val client = client.get()
if (client != null) {
val all = createContexts(client, client.config)
_all.addAll(all)
}
} catch (e: Exception) {
//
throw ResourceException("Config error: ${toMessage(e)}", e)
}
}
return _all
Expand All @@ -124,7 +129,7 @@ open class AllContexts(
if (current == context) {
return current
}
val newClient = clientFactory.invoke(context.context.context.namespace, context.context.name)
val newClient = clientFactory.invoke(context.namespace, context.name)
val new = setCurrentContext(newClient, emptyList())
if (new != null) {
modelChange.fireAllContextsChanged()
Expand All @@ -134,7 +139,7 @@ open class AllContexts(

override fun setCurrentNamespace(namespace: String): IActiveContext<out HasMetadata, out KubernetesClient>? {
val old = this.current ?: return null
val newClient = clientFactory.invoke(namespace, old.context.name)
val newClient = clientFactory.invoke(namespace, old.name)
val new = setCurrentContext(newClient, old.getWatched())
if (new != null) {
modelChange.fireCurrentNamespaceChanged(new, old)
Expand Down Expand Up @@ -173,6 +178,7 @@ open class AllContexts(
lock.write {
this.current?.close()
clearAllContexts() // latter access will cause reload
client.reset()
}
modelChange.fireAllContextsChanged()
}
Expand Down Expand Up @@ -244,20 +250,21 @@ open class AllContexts(
* The latter gets closed/recreated whenever the context changes in
* [com.redhat.devtools.intellij.kubernetes.model.client.KubeConfigAdapter].
*/
val watcher = ConfigWatcher { config: Config? -> onKubeConfigChanged(config) }
val watcher = ConfigWatcher { config: Config?, error: Exception? -> onKubeConfigChanged(config, error) }
runAsync(watcher::run)
}

protected open fun onKubeConfigChanged(updated: Config?) {
protected open fun onKubeConfigChanged(updated: Config?, error: Exception?) {
lock.read {
updated ?: return
val client = client.get() ?: return
val existing = client.config
if (existing.isEqualConfig(updated)) {
return
if (error == null) {
val client = client.get() ?: return
val existing = client.config
if (existing.isEqualConfig(updated)) {
return
}
this.client.reset() // create new client when accessed
client.close()
}
this.client.reset() // create new client when accessed
client.close()
}
refresh()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ open class ClientConfig(
* @param config the [Config] to compare the adapted config in this class to.
* @return true if the given config is equal to the one that this class adapts
*/
fun isEqualConfig(config: Config): Boolean {
fun isEqualConfig(config: Config?): Boolean {
if (config == null) {
return false
}
return ConfigHelper.areEqualCurrentContext(config, this.config)
&& ConfigHelper.areEqualContexts(config, this.config)
&& ConfigHelper.areEqualCluster(config, this.config)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ abstract class ActiveContext<N : HasMetadata, C : KubernetesClient>(
}

override fun close() {
logger<ActiveContext<*, *>>().debug("Closing context ${context.name}.")
logger<ActiveContext<*, *>>().debug("Closing context $name.")
watch.close()
dashboard.close()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,25 @@
******************************************************************************/
package com.redhat.devtools.intellij.kubernetes.model.context

import com.redhat.devtools.intellij.kubernetes.model.util.toMessage
import io.fabric8.kubernetes.api.model.NamedContext

interface IContext {
val context: NamedContext
val active: Boolean
val name: String?
val namespace: String?
}

open class Context(override val context: NamedContext): IContext {
open class Context(private val context: NamedContext): IContext {
override val active: Boolean = false
}
override val name: String?
get() = context.name
override val namespace: String?
get() = context.context?.namespace
}

class KubeConfigError(error: Exception? = null): IContext {
override val active: Boolean = false
override val name: String = "Configuration error: ${toMessage(error)}"
override val namespace: String? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,6 @@ interface IActiveContext<N: HasMetadata, C: KubernetesClient>: IContext {
}
}

val name: String?
get() {
return context.name
}
/**
* The master url for this context. This is the url of the cluster for this context.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ open class ResettableLazyProperty<T>(private val initializer: () -> T?) {
fun reset() {
this.initialized = false
}

fun isReset(): Boolean {
return !initialized
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ abstract class AbstractTreeStructureContribution(override val model: IResourceMo
abstract fun descriptorFactory(): (Any, ResourceKind<out HasMetadata>?, NodeDescriptor<*>?, IResourceModel, Project) -> NodeDescriptor<*>?

protected fun getRootElement(): Any? {
return model.getCurrentContext()
return try {
model.getCurrentContext()
} catch (e: Exception) {
null
}
}

fun <T> element(initializer: ElementNode<T>.() -> Unit): ElementNode<T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,13 @@ import javax.swing.Icon

object KubernetesDescriptors {

fun createDescriptor(element: Any, childrenKind: ResourceKind<out HasMetadata>?, parent: NodeDescriptor<*>?, model: IResourceModel, project: Project): NodeDescriptor<*>? {
fun createDescriptor(
element: Any,
childrenKind: ResourceKind<out HasMetadata>?,
parent: NodeDescriptor<*>?,
model: IResourceModel,
project: Project
): NodeDescriptor<*>? {
return when {
element is DescriptorFactory<*> ->
element.create(parent, model, project)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,25 @@ open class TreeStructure(

override fun getChildElements(element: Any): Array<Any> {
return when (element) {
rootElement -> model.getAllContexts().toTypedArray()
rootElement -> getAllContexts()
else -> getValidContributions()
.flatMap { getChildElements(element, it) }
.toTypedArray()
.flatMap { getChildElements(element, it) }
.toTypedArray()
}
}

private fun getAllContexts(): Array<Any> {
return try {
model.getAllContexts().toTypedArray()
} catch (e: Exception) {
arrayOf(e)
}
}

private fun getChildElements(element: Any, contribution: ITreeStructureContribution): Collection<Any> {
return try {
contribution.getChildElements(element)
} catch (e: java.lang.Exception) {
} catch (e: Exception) {
logger<TreeStructure>().warn(e)
listOf(e)
}
Expand Down Expand Up @@ -101,24 +109,30 @@ open class TreeStructure(
}

override fun createDescriptor(element: Any, parent: NodeDescriptor<*>?): NodeDescriptor<*> {
val descriptor: NodeDescriptor<*>? =
getValidContributions()
.map { it.createDescriptor(element, parent, project) }
.find { it != null }
if (descriptor != null) {
return descriptor
}
return when (element) {
is IContext -> ContextDescriptor(element, parent, model, project)
is Exception -> ErrorDescriptor(element, parent, model, project)
is Folder -> FolderDescriptor(element, parent, model, project)
else -> Descriptor(element, null, parent, model, project)
return try {
val descriptor: NodeDescriptor<*>? = getValidContributions()
.map { it.createDescriptor(element, parent, project) }
.find { it != null }
descriptor ?: when (element) {
is IContext -> ContextDescriptor(element, parent, model, project)
is Exception -> ErrorDescriptor(element, parent, model, project)
is Folder -> FolderDescriptor(element, parent, model, project)
else -> Descriptor(element, null, parent, model, project)
}
} catch (e: Exception) {
ErrorDescriptor(e, parent, model, project)
}
}

private fun getValidContributions(): Collection<ITreeStructureContribution> {
return contributions
.filter { it.canContribute() }
.filter {
try {
it.canContribute()
} catch (e: Exception) {
false
}
}
}

private fun getTreeStructureExtensions(model: IResourceModel): List<ITreeStructureContribution> {
Expand Down Expand Up @@ -167,11 +181,7 @@ open class TreeStructure(
project
) {
override fun getLabel(element: C?): String {
return if (element?.context?.name == null) {
"<unknown context>"
} else {
element.context.name
}
return element?.name ?: "<unknown context>"
}

override fun getIcon(element: C): Icon? {
Expand Down Expand Up @@ -237,26 +247,22 @@ open class TreeStructure(
}

private class ErrorDescriptor(
exception: java.lang.Exception,
exception: Exception,
parent: NodeDescriptor<*>?,
model: IResourceModel,
project: Project
) : Descriptor<java.lang.Exception>(
) : Descriptor<Exception>(
exception,
null,
parent,
model,
project
) {
override fun getLabel(element: java.lang.Exception?): String {
return getMessage(element)
}

private fun getMessage(e: Exception?): String {
return toMessage(e)
override fun getLabel(element: Exception?): String {
return toMessage(element)
}

override fun getIcon(element: java.lang.Exception): Icon {
override fun getIcon(element: Exception): Icon {
return AllIcons.General.BalloonError
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class TreeUpdater(
}
}

private fun isRootNode(element: Any?): Boolean {
private fun isRootElement(element: Any?): Boolean {
val descriptor = (treeModel.root as? DefaultMutableTreeNode)?.userObject as? NodeDescriptor<*>
return element == descriptor?.element
}
Expand All @@ -150,7 +150,7 @@ class TreeUpdater(
private fun findNodes(element: Any?): Collection<TreeNode> {
return if (element == null) {
emptyList()
} else if (isRootNode(element)) {
} else if (isRootElement(element)) {
listOf(treeModel.root)
} else {
findNodes({ node: TreeNode -> hasElement(element, node) }, treeModel.root)
Expand Down
Loading

0 comments on commit 4727cb9

Please sign in to comment.