mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
clarify ui
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
parent
c4e501f329
commit
331a617c5b
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Component, Catalog, K8sApi } from "@k8slens/extensions";
|
import { Component, Catalog, K8sApi } from "@k8slens/extensions";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { observable } from "mobx";
|
import { computed, observable } from "mobx";
|
||||||
import { MetricsFeature, MetricsConfiguration } from "./metrics-feature";
|
import { MetricsFeature, MetricsConfiguration } from "./metrics-feature";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -43,6 +43,22 @@ export class MetricsSettings extends React.Component<Props> {
|
|||||||
};
|
};
|
||||||
feature: MetricsFeature;
|
feature: MetricsFeature;
|
||||||
|
|
||||||
|
@computed get isTogglable() {
|
||||||
|
if (!this.props.cluster.status.active) return false;
|
||||||
|
if (this.canUpgrade) return false;
|
||||||
|
if (!this.isActiveMetricsProvider) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get metricsProvider() {
|
||||||
|
return this.props.cluster.spec?.metrics?.prometheus?.type || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
get isActiveMetricsProvider() {
|
||||||
|
return (!this.metricsProvider || this.metricsProvider === "lens");
|
||||||
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
this.feature = new MetricsFeature(this.props.cluster);
|
this.feature = new MetricsFeature(this.props.cluster);
|
||||||
|
|
||||||
@ -148,6 +164,13 @@ export class MetricsSettings extends React.Component<Props> {
|
|||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
{ !this.isActiveMetricsProvider && (
|
||||||
|
<section>
|
||||||
|
<p style={ {color: "var(--colorError)"} }>
|
||||||
|
Other metrics provider is currently active. See "Metrics" tab for details.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
{ this.canUpgrade && (
|
{ this.canUpgrade && (
|
||||||
<section>
|
<section>
|
||||||
<Component.SubTitle title="Software Update" />
|
<Component.SubTitle title="Software Update" />
|
||||||
@ -155,7 +178,7 @@ export class MetricsSettings extends React.Component<Props> {
|
|||||||
<Component.Button label={this.upgrading ? "Updating ..." : "Update Now"} primary onClick={() => this.updateStack() } waiting={this.upgrading} />
|
<Component.Button label={this.upgrading ? "Updating ..." : "Update Now"} primary onClick={() => this.updateStack() } waiting={this.upgrading} />
|
||||||
|
|
||||||
<small className="hint">
|
<small className="hint">
|
||||||
An update is available for installed metrics components.
|
An update is available for enabled metrics components.
|
||||||
</small>
|
</small>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
@ -164,7 +187,7 @@ export class MetricsSettings extends React.Component<Props> {
|
|||||||
<Component.FormSwitch
|
<Component.FormSwitch
|
||||||
control={
|
control={
|
||||||
<Component.Switcher
|
<Component.Switcher
|
||||||
disabled={this.featureStates.kubeStateMetrics === undefined || !this.props.cluster.status.active}
|
disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable}
|
||||||
checked={!!this.featureStates.prometheus && this.props.cluster.status.active}
|
checked={!!this.featureStates.prometheus && this.props.cluster.status.active}
|
||||||
onChange={v => this.togglePrometheus(v.target.checked)}
|
onChange={v => this.togglePrometheus(v.target.checked)}
|
||||||
name="prometheus"
|
name="prometheus"
|
||||||
@ -182,7 +205,7 @@ export class MetricsSettings extends React.Component<Props> {
|
|||||||
<Component.FormSwitch
|
<Component.FormSwitch
|
||||||
control={
|
control={
|
||||||
<Component.Switcher
|
<Component.Switcher
|
||||||
disabled={this.featureStates.kubeStateMetrics === undefined || !this.props.cluster.status.active}
|
disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable}
|
||||||
checked={!!this.featureStates.kubeStateMetrics && this.props.cluster.status.active}
|
checked={!!this.featureStates.kubeStateMetrics && this.props.cluster.status.active}
|
||||||
onChange={v => this.toggleKubeStateMetrics(v.target.checked)}
|
onChange={v => this.toggleKubeStateMetrics(v.target.checked)}
|
||||||
name="node-exporter"
|
name="node-exporter"
|
||||||
@ -201,7 +224,7 @@ export class MetricsSettings extends React.Component<Props> {
|
|||||||
<Component.FormSwitch
|
<Component.FormSwitch
|
||||||
control={
|
control={
|
||||||
<Component.Switcher
|
<Component.Switcher
|
||||||
disabled={this.featureStates.nodeExporter === undefined || !this.props.cluster.status.active}
|
disabled={this.featureStates.nodeExporter === undefined || !this.isTogglable}
|
||||||
checked={!!this.featureStates.nodeExporter && this.props.cluster.status.active}
|
checked={!!this.featureStates.nodeExporter && this.props.cluster.status.active}
|
||||||
onChange={v => this.toggleNodeExporter(v.target.checked)}
|
onChange={v => this.toggleNodeExporter(v.target.checked)}
|
||||||
name="node-exporter"
|
name="node-exporter"
|
||||||
|
|||||||
@ -6,9 +6,24 @@ import { requestMain } from "../ipc";
|
|||||||
import { productName } from "../vars";
|
import { productName } from "../vars";
|
||||||
import { CatalogCategory, CatalogCategorySpec } from "../catalog";
|
import { CatalogCategory, CatalogCategorySpec } from "../catalog";
|
||||||
|
|
||||||
|
|
||||||
|
export type KubernetesClusterPrometheusMetrics = {
|
||||||
|
address?: {
|
||||||
|
namespace: string;
|
||||||
|
service: string;
|
||||||
|
port: number;
|
||||||
|
prefix: string;
|
||||||
|
};
|
||||||
|
type?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type KubernetesClusterSpec = {
|
export type KubernetesClusterSpec = {
|
||||||
kubeconfigPath: string;
|
kubeconfigPath: string;
|
||||||
kubeconfigContext: string;
|
kubeconfigContext: string;
|
||||||
|
metrics?: {
|
||||||
|
source: string;
|
||||||
|
prometheus?: KubernetesClusterPrometheusMetrics;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface KubernetesClusterStatus extends CatalogEntityStatus {
|
export interface KubernetesClusterStatus extends CatalogEntityStatus {
|
||||||
|
|||||||
@ -11,13 +11,14 @@ export interface EntitySettingComponents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface EntitySettingRegistration {
|
export interface EntitySettingRegistration {
|
||||||
title: string;
|
|
||||||
kind: string;
|
|
||||||
apiVersions: string[];
|
apiVersions: string[];
|
||||||
source?: string;
|
kind: string;
|
||||||
|
title: string;
|
||||||
components: EntitySettingComponents;
|
components: EntitySettingComponents;
|
||||||
|
source?: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
priority?: number;
|
priority?: number;
|
||||||
|
group?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RegisteredEntitySetting extends EntitySettingRegistration {
|
export interface RegisteredEntitySetting extends EntitySettingRegistration {
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import logger from "./logger";
|
|||||||
import { apiKubePrefix } from "../common/vars";
|
import { apiKubePrefix } from "../common/vars";
|
||||||
import { Singleton } from "../common/utils";
|
import { Singleton } from "../common/utils";
|
||||||
import { catalogEntityRegistry } from "../common/catalog";
|
import { catalogEntityRegistry } from "../common/catalog";
|
||||||
import { KubernetesCluster } from "../common/catalog-entities/kubernetes-cluster";
|
import { KubernetesCluster, KubernetesClusterPrometheusMetrics } from "../common/catalog-entities/kubernetes-cluster";
|
||||||
|
|
||||||
export class ClusterManager extends Singleton {
|
export class ClusterManager extends Singleton {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -47,7 +47,7 @@ export class ClusterManager extends Singleton {
|
|||||||
const index = catalogEntityRegistry.items.findIndex((entity) => entity.metadata.uid === cluster.id);
|
const index = catalogEntityRegistry.items.findIndex((entity) => entity.metadata.uid === cluster.id);
|
||||||
|
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
const entity = catalogEntityRegistry.items[index];
|
const entity = catalogEntityRegistry.items[index] as KubernetesCluster;
|
||||||
|
|
||||||
entity.status.phase = cluster.disconnected ? "disconnected" : "connected";
|
entity.status.phase = cluster.disconnected ? "disconnected" : "connected";
|
||||||
entity.status.active = !cluster.disconnected;
|
entity.status.active = !cluster.disconnected;
|
||||||
@ -55,6 +55,17 @@ export class ClusterManager extends Singleton {
|
|||||||
if (cluster.preferences?.clusterName) {
|
if (cluster.preferences?.clusterName) {
|
||||||
entity.metadata.name = cluster.preferences.clusterName;
|
entity.metadata.name = cluster.preferences.clusterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entity.spec.metrics ||= { source: "local" };
|
||||||
|
|
||||||
|
if (entity.spec.metrics.source === "local") {
|
||||||
|
const prometheus: KubernetesClusterPrometheusMetrics = entity.spec?.metrics?.prometheus || {};
|
||||||
|
|
||||||
|
prometheus.type = cluster.preferences.prometheusProvider?.type;
|
||||||
|
prometheus.address = cluster.preferences.prometheus;
|
||||||
|
entity.spec.metrics.prometheus = prometheus;
|
||||||
|
}
|
||||||
|
|
||||||
catalogEntityRegistry.items.splice(index, 1, entity);
|
catalogEntityRegistry.items.splice(index, 1, entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { CatalogEntity } from "../../api/catalog-entity";
|
|||||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||||
import { entitySettingRegistry } from "../../../extensions/registries";
|
import { entitySettingRegistry } from "../../../extensions/registries";
|
||||||
import { EntitySettingsRouteParams } from "./entity-settings.route";
|
import { EntitySettingsRouteParams } from "./entity-settings.route";
|
||||||
|
import { groupBy } from "lodash";
|
||||||
|
|
||||||
interface Props extends RouteComponentProps<EntitySettingsRouteParams> {
|
interface Props extends RouteComponentProps<EntitySettingsRouteParams> {
|
||||||
}
|
}
|
||||||
@ -52,18 +53,24 @@ export class EntitySettings extends React.Component<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderNavigation() {
|
renderNavigation() {
|
||||||
|
const groups = Object.entries(groupBy(this.menuItems, (item) => item.group || "Extensions"));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h2>{this.entity.metadata.name}</h2>
|
<h2>{this.entity.metadata.name}</h2>
|
||||||
<Tabs className="flex column" scrollable={false} onChange={this.onTabChange} value={this.activeTab}>
|
<Tabs className="flex column" scrollable={false} onChange={this.onTabChange} value={this.activeTab}>
|
||||||
<div className="header">Settings</div>
|
{ groups.map((group) => (
|
||||||
{ this.menuItems.map((setting) => (
|
<>
|
||||||
<Tab
|
<div className="header">{group[0]}</div>
|
||||||
key={setting.id}
|
{ group[1].map((setting) => (
|
||||||
value={setting.id}
|
<Tab
|
||||||
label={setting.title}
|
key={setting.id}
|
||||||
data-testid={`${setting.id}-tab`}
|
value={setting.id}
|
||||||
/>
|
label={setting.title}
|
||||||
|
data-testid={`${setting.id}-tab`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
))}
|
))}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -22,6 +22,7 @@ entitySettingRegistry.add([
|
|||||||
kind: "KubernetesCluster",
|
kind: "KubernetesCluster",
|
||||||
source: "local",
|
source: "local",
|
||||||
title: "General",
|
title: "General",
|
||||||
|
group: "Settings",
|
||||||
components: {
|
components: {
|
||||||
View: (props: { entity: CatalogEntity }) => {
|
View: (props: { entity: CatalogEntity }) => {
|
||||||
const cluster = getClusterForEntity(props.entity);
|
const cluster = getClusterForEntity(props.entity);
|
||||||
@ -47,6 +48,7 @@ entitySettingRegistry.add([
|
|||||||
apiVersions: ["entity.k8slens.dev/v1alpha1"],
|
apiVersions: ["entity.k8slens.dev/v1alpha1"],
|
||||||
kind: "KubernetesCluster",
|
kind: "KubernetesCluster",
|
||||||
title: "Proxy",
|
title: "Proxy",
|
||||||
|
group: "Settings",
|
||||||
components: {
|
components: {
|
||||||
View: (props: { entity: CatalogEntity }) => {
|
View: (props: { entity: CatalogEntity }) => {
|
||||||
const cluster = getClusterForEntity(props.entity);
|
const cluster = getClusterForEntity(props.entity);
|
||||||
@ -67,6 +69,7 @@ entitySettingRegistry.add([
|
|||||||
apiVersions: ["entity.k8slens.dev/v1alpha1"],
|
apiVersions: ["entity.k8slens.dev/v1alpha1"],
|
||||||
kind: "KubernetesCluster",
|
kind: "KubernetesCluster",
|
||||||
title: "Terminal",
|
title: "Terminal",
|
||||||
|
group: "Settings",
|
||||||
components: {
|
components: {
|
||||||
View: (props: { entity: CatalogEntity }) => {
|
View: (props: { entity: CatalogEntity }) => {
|
||||||
const cluster = getClusterForEntity(props.entity);
|
const cluster = getClusterForEntity(props.entity);
|
||||||
@ -87,6 +90,7 @@ entitySettingRegistry.add([
|
|||||||
apiVersions: ["entity.k8slens.dev/v1alpha1"],
|
apiVersions: ["entity.k8slens.dev/v1alpha1"],
|
||||||
kind: "KubernetesCluster",
|
kind: "KubernetesCluster",
|
||||||
title: "Namespaces",
|
title: "Namespaces",
|
||||||
|
group: "Settings",
|
||||||
components: {
|
components: {
|
||||||
View: (props: { entity: CatalogEntity }) => {
|
View: (props: { entity: CatalogEntity }) => {
|
||||||
const cluster = getClusterForEntity(props.entity);
|
const cluster = getClusterForEntity(props.entity);
|
||||||
@ -107,6 +111,7 @@ entitySettingRegistry.add([
|
|||||||
apiVersions: ["entity.k8slens.dev/v1alpha1"],
|
apiVersions: ["entity.k8slens.dev/v1alpha1"],
|
||||||
kind: "KubernetesCluster",
|
kind: "KubernetesCluster",
|
||||||
title: "Metrics",
|
title: "Metrics",
|
||||||
|
group: "Settings",
|
||||||
components: {
|
components: {
|
||||||
View: (props: { entity: CatalogEntity }) => {
|
View: (props: { entity: CatalogEntity }) => {
|
||||||
const cluster = getClusterForEntity(props.entity);
|
const cluster = getClusterForEntity(props.entity);
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
.MetricsSelect {
|
|
||||||
$spacing: $padding;
|
|
||||||
--flex-gap: #{$spacing};
|
|
||||||
|
|
||||||
.Badge {
|
|
||||||
margin-top: $spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Button {
|
|
||||||
margin-top: $spacing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,3 @@
|
|||||||
import "./cluster-metrics-setting.scss";
|
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { disposeOnUnmount, observer } from "mobx-react";
|
import { disposeOnUnmount, observer } from "mobx-react";
|
||||||
import { Select, SelectOption } from "../../select/select";
|
import { Select, SelectOption } from "../../select/select";
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { SubTitle } from "../../layout/sub-title";
|
|||||||
import { Select, SelectOption } from "../../select";
|
import { Select, SelectOption } from "../../select";
|
||||||
import { Input } from "../../input";
|
import { Input } from "../../input";
|
||||||
import { observable, computed, autorun } from "mobx";
|
import { observable, computed, autorun } from "mobx";
|
||||||
|
import { productName } from "../../../../common/vars";
|
||||||
|
|
||||||
const options: SelectOption<string>[] = [
|
const options: SelectOption<string>[] = [
|
||||||
{ value: "", label: "Auto detect" },
|
{ value: "", label: "Auto detect" },
|
||||||
@ -81,23 +82,20 @@ export class ClusterPrometheusSetting extends React.Component<Props> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SubTitle title="Prometheus installation method"/>
|
<section>
|
||||||
<p>
|
<SubTitle title="Prometheus"/>
|
||||||
Use pre-installed Prometheus service for metrics. Please refer to the{" "}
|
<Select
|
||||||
<a href="https://github.com/lensapp/lens/blob/master/troubleshooting/custom-prometheus.md" target="_blank" rel="noreferrer">guide</a>{" "}
|
value={this.provider}
|
||||||
for possible configuration changes.
|
onChange={({value}) => {
|
||||||
</p>
|
this.provider = value;
|
||||||
<Select
|
this.onSaveProvider();
|
||||||
value={this.provider}
|
}}
|
||||||
onChange={({value}) => {
|
options={options}
|
||||||
this.provider = value;
|
/>
|
||||||
this.onSaveProvider();
|
<small className="hint">What query format is used to fetch metrics from Prometheus</small>
|
||||||
}}
|
</section>
|
||||||
options={options}
|
|
||||||
/>
|
|
||||||
<small className="hint">What query format is used to fetch metrics from Prometheus</small>
|
|
||||||
{this.canEditPrometheusPath && (
|
{this.canEditPrometheusPath && (
|
||||||
<>
|
<section>
|
||||||
<p>Prometheus service address.</p>
|
<p>Prometheus service address.</p>
|
||||||
<Input
|
<Input
|
||||||
theme="round-black"
|
theme="round-black"
|
||||||
@ -108,9 +106,9 @@ export class ClusterPrometheusSetting extends React.Component<Props> {
|
|||||||
/>
|
/>
|
||||||
<small className="hint">
|
<small className="hint">
|
||||||
An address to an existing Prometheus installation{" "}
|
An address to an existing Prometheus installation{" "}
|
||||||
({"<namespace>/<service>:<port>"}). Lens tries to auto-detect address if left empty.
|
({"<namespace>/<service>:<port>"}). {productName} tries to auto-detect address if left empty.
|
||||||
</small>
|
</small>
|
||||||
</>
|
</section>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import "./cluster-metrics-setting.scss";
|
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { disposeOnUnmount, observer } from "mobx-react";
|
import { disposeOnUnmount, observer } from "mobx-react";
|
||||||
import { Cluster } from "../../../../main/cluster";
|
import { Cluster } from "../../../../main/cluster";
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user