From 4b983ae5bdca1cb7fead62386e28072e36b504b2 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Mon, 17 May 2021 17:06:49 +0300 Subject: [PATCH] dedicated action button Signed-off-by: Jari Kolehmainen --- .../{01-namespace.yml => 01-namespace.yml.hb} | 2 + .../src/metrics-feature.ts | 11 +- .../src/metrics-settings.tsx | 113 ++++++++++-------- 3 files changed, 72 insertions(+), 54 deletions(-) rename extensions/metrics-cluster-feature/resources/{01-namespace.yml => 01-namespace.yml.hb} (53%) diff --git a/extensions/metrics-cluster-feature/resources/01-namespace.yml b/extensions/metrics-cluster-feature/resources/01-namespace.yml.hb similarity index 53% rename from extensions/metrics-cluster-feature/resources/01-namespace.yml rename to extensions/metrics-cluster-feature/resources/01-namespace.yml.hb index 85d13c1046..dd3816fdff 100644 --- a/extensions/metrics-cluster-feature/resources/01-namespace.yml +++ b/extensions/metrics-cluster-feature/resources/01-namespace.yml.hb @@ -2,3 +2,5 @@ apiVersion: v1 kind: Namespace metadata: name: lens-metrics + annotations: + extensionVersion: "{{ version }}" diff --git a/extensions/metrics-cluster-feature/src/metrics-feature.ts b/extensions/metrics-cluster-feature/src/metrics-feature.ts index c5688f9be5..655c8e60ed 100644 --- a/extensions/metrics-cluster-feature/src/metrics-feature.ts +++ b/extensions/metrics-cluster-feature/src/metrics-feature.ts @@ -46,6 +46,7 @@ export interface MetricsConfiguration { alertManagers: string[]; replicas: number; storageClass: string; + version?: string; } export interface MetricsStatus { @@ -77,6 +78,8 @@ export class MetricsFeature { sc.metadata?.annotations?.["storageclass.beta.kubernetes.io/is-default-class"] === "true" )); + config.version = this.latestVersion; + return this.stack.kubectlApplyFolder(this.resourceFolder, config, ["--prune"]); } @@ -88,11 +91,11 @@ export class MetricsFeature { const status: MetricsStatus = { installed: false, canUpgrade: false}; try { - const statefulSet = K8sApi.forCluster(this.cluster, K8sApi.StatefulSet); - const prometheus = await statefulSet.get({name: "prometheus", namespace: "lens-metrics"}); + const namespaceApi = K8sApi.forCluster(this.cluster, K8sApi.Namespace); + const namespace = await namespaceApi.get({name: "lens-metrics"}); - if (prometheus?.kind) { - const currentVersion = prometheus.spec.template.spec.containers[0].image.split(":")[1]; + if (namespace?.kind) { + const currentVersion = namespace.metadata.annotations?.extensionVersion || "0.0.0"; status.installed = true; status.canUpgrade = semver.lt(currentVersion, this.latestVersion, true); diff --git a/extensions/metrics-cluster-feature/src/metrics-settings.tsx b/extensions/metrics-cluster-feature/src/metrics-settings.tsx index 8d9c62d44a..bc1784ca3c 100644 --- a/extensions/metrics-cluster-feature/src/metrics-settings.tsx +++ b/extensions/metrics-cluster-feature/src/metrics-settings.tsx @@ -17,6 +17,8 @@ export class MetricsSettings extends React.Component { }; @observable canUpgrade = false; @observable upgrading = false; + @observable changed = false; + @observable inProgress = false; config: MetricsConfiguration = { prometheus: { @@ -44,6 +46,7 @@ export class MetricsSettings extends React.Component { feature: MetricsFeature; @computed get isTogglable() { + if (this.inProgress) return false; if (!this.props.cluster.status.active) return false; if (this.canUpgrade) return false; if (!this.isActiveMetricsProvider) return false; @@ -69,7 +72,23 @@ export class MetricsSettings extends React.Component { const status = await this.feature.getStatus(); this.canUpgrade = status.canUpgrade; - this.featureStates.prometheus = status.installed; + + if (this.canUpgrade) { + this.changed = true; + } + + const statefulSet = K8sApi.forCluster(this.props.cluster, K8sApi.StatefulSet); + + try { + await statefulSet.get({name: "prometheus", namespace: "lens-metrics"}); + this.featureStates.prometheus = true; + } catch(e) { + if (e?.error?.code === 404) { + this.featureStates.prometheus = false; + } else { + this.featureStates.prometheus = undefined; + } + } const deployment = K8sApi.forCluster(this.props.cluster, K8sApi.Deployment); @@ -103,55 +122,47 @@ export class MetricsSettings extends React.Component { this.config.kubeStateMetrics.enabled = !!this.featureStates.kubeStateMetrics; this.config.nodeExporter.enabled = !!this.featureStates.nodeExporter; - if (!this.config.prometheus.enabled && !this.config.kubeStateMetrics.enabled && !this.config.nodeExporter.enabled) { - await this.feature.uninstall(this.config); - } else { - await this.feature.install(this.config); + this.inProgress = true; + + try { + if (!this.config.prometheus.enabled && !this.config.kubeStateMetrics.enabled && !this.config.nodeExporter.enabled) { + await this.feature.uninstall(this.config); + } else { + await this.feature.install(this.config); + } + } finally { + this.inProgress = false; + this.changed = false; + + await this.updateFeatureStates(); } } async togglePrometheus(enabled: boolean) { this.featureStates.prometheus = enabled; - - try { - await this.save(); - } catch(error) { - this.featureStates.prometheus = !enabled; - Component.Notifications.error(`Failed to ${enabled ? "enable" : "disable"} Prometheus: ${error}`); - } + this.changed = true; } async toggleKubeStateMetrics(enabled: boolean) { this.featureStates.kubeStateMetrics = enabled; - - try { - await this.save(); - } catch(error) { - this.featureStates.kubeStateMetrics = !enabled; - Component.Notifications.error(`Failed to ${enabled ? "enable" : "disable"} kube-state-metrics: ${error}`); - } + this.changed = true; } async toggleNodeExporter(enabled: boolean) { this.featureStates.nodeExporter = enabled; - - try { - await this.save(); - } catch(error) { - this.featureStates.nodeExporter = !enabled; - Component.Notifications.error(`Failed to ${enabled ? "enable" : "disable"} node-exporter: ${error}`); - } + this.changed = true; } - async updateStack() { - this.upgrading = true; + @computed get buttonLabel() { + if (this.inProgress && this.canUpgrade) return "Upgrading ..."; + if (this.inProgress) return "Applying ..."; + if (this.canUpgrade) return "Upgrade"; - await this.save(); - setTimeout(() => { - Component.Notifications.info("Lens Metrics stack updated!", {timeout: 5_000}); - this.upgrading = false; - this.canUpgrade = false; - }, 1000); + if (this.changed && !this.featureStates.kubeStateMetrics && !this.featureStates.nodeExporter && !this.featureStates.prometheus) { + return "Uninstall"; + } + + return "Apply"; } render() { @@ -171,17 +182,6 @@ export class MetricsSettings extends React.Component {

)} - { this.canUpgrade && ( -
- - - this.updateStack() } waiting={this.upgrading} /> - - - An update is available for enabled metrics components. - -
- )}
{ name="prometheus" /> } - label="Install bundled Prometheus metrics stack" + label="Enable bundled Prometheus metrics stack" /> Enable timeseries data visualization (Prometheus stack) for your cluster. @@ -211,11 +211,11 @@ export class MetricsSettings extends React.Component { name="node-exporter" /> } - label="Install bundled kube-state-metrics stack" + label="Enable bundled kube-state-metrics stack" /> Enable Kubernetes API object metrics for your cluster. - Install this only if you don't have existing kube-state-metrics stack installed. + Enable this only if you don't have existing kube-state-metrics stack installed.
@@ -230,13 +230,26 @@ export class MetricsSettings extends React.Component { name="node-exporter" /> } - label="Install bundled node-exporter stack" + label="Enable bundled node-exporter stack" /> Enable node level metrics for your cluster. - Install this only if you don't have existing node-exporter stack installed. + Enable this only if you don't have existing node-exporter stack installed. + +
+ this.save()} + primary + disabled={!this.changed} /> + + {this.canUpgrade && ( + An update is available for enabled metrics components. + )} +
); }