mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Implement cluster metadata detectors (#1106)
Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com>
This commit is contained in:
1 parent
99db7aca19
commit
ce995f3deb
14 files changed
+293
-49
No files matched your search
@@ -0,0 +1,33 @@
|
||||
import request, { RequestPromiseOptions } from "request-promise-native"
|
||||
import { Cluster } from "../cluster";
|
||||
|
||||
export type ClusterDetectionResult = {
|
||||
value: string | number | boolean
|
||||
accuracy: number
|
||||
}
|
||||
|
||||
export class BaseClusterDetector {
|
||||
cluster: Cluster
|
||||
key: string
|
||||
|
||||
constructor(cluster: Cluster) {
|
||||
this.cluster = cluster
|
||||
}
|
||||
|
||||
detect(): Promise<ClusterDetectionResult> {
|
||||
return null
|
||||
}
|
||||
|
||||
protected async k8sRequest<T = any>(path: string, options: RequestPromiseOptions = {}): Promise<T> {
|
||||
const apiUrl = this.cluster.kubeProxyUrl + path;
|
||||
return request(apiUrl, {
|
||||
json: true,
|
||||
timeout: 30000,
|
||||
...options,
|
||||
headers: {
|
||||
Host: `${this.cluster.id}.${new URL(this.cluster.kubeProxyUrl).host}`, // required in ClusterManager.getClusterForRequest()
|
||||
...(options.headers || {}),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { BaseClusterDetector } from "./base-cluster-detector";
|
||||
import { createHash } from "crypto"
|
||||
import { ClusterMetadataKey } from "../cluster";
|
||||
|
||||
export class ClusterIdDetector extends BaseClusterDetector {
|
||||
key = ClusterMetadataKey.CLUSTER_ID
|
||||
|
||||
public async detect() {
|
||||
let id: string
|
||||
try {
|
||||
id = await this.getDefaultNamespaceId()
|
||||
} catch(_) {
|
||||
id = this.cluster.apiUrl
|
||||
}
|
||||
const value = createHash("sha256").update(id).digest("hex")
|
||||
return { value: value, accuracy: 100 }
|
||||
}
|
||||
|
||||
protected async getDefaultNamespaceId() {
|
||||
const response = await this.k8sRequest("/api/v1/namespaces/default")
|
||||
return response.metadata.uid
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { observable } from "mobx";
|
||||
import { ClusterMetadata } from "../../common/cluster-store";
|
||||
import { Cluster } from "../cluster";
|
||||
import { BaseClusterDetector, ClusterDetectionResult } from "./base-cluster-detector";
|
||||
import { ClusterIdDetector } from "./cluster-id-detector";
|
||||
import { DistributionDetector } from "./distribution-detector";
|
||||
import { LastSeenDetector } from "./last-seen-detector";
|
||||
import { NodesCountDetector } from "./nodes-count-detector";
|
||||
import { VersionDetector } from "./version-detector";
|
||||
|
||||
export class DetectorRegistry {
|
||||
registry = observable.array<typeof BaseClusterDetector>([], { deep: false });
|
||||
|
||||
add(detectorClass: typeof BaseClusterDetector) {
|
||||
this.registry.push(detectorClass)
|
||||
}
|
||||
|
||||
async detectForCluster(cluster: Cluster): Promise<ClusterMetadata> {
|
||||
const results: {[key: string]: ClusterDetectionResult } = {}
|
||||
for (const detectorClass of this.registry) {
|
||||
const detector = new detectorClass(cluster)
|
||||
try {
|
||||
const data = await detector.detect()
|
||||
if (!data) continue;
|
||||
const existingValue = results[detector.key]
|
||||
if (existingValue && existingValue.accuracy > data.accuracy) continue; // previous value exists and is more accurate
|
||||
results[detector.key] = data
|
||||
} catch (e) {
|
||||
// detector raised error, do nothing
|
||||
}
|
||||
}
|
||||
const metadata: ClusterMetadata = {}
|
||||
for (const [key, result] of Object.entries(results)) {
|
||||
metadata[key] = result.value
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
}
|
||||
|
||||
export const detectorRegistry = new DetectorRegistry()
|
||||
detectorRegistry.add(ClusterIdDetector)
|
||||
detectorRegistry.add(LastSeenDetector)
|
||||
detectorRegistry.add(VersionDetector)
|
||||
detectorRegistry.add(DistributionDetector)
|
||||
detectorRegistry.add(NodesCountDetector)
|
||||
@@ -0,0 +1,80 @@
|
||||
import { BaseClusterDetector } from "./base-cluster-detector";
|
||||
import { ClusterMetadataKey } from "../cluster";
|
||||
|
||||
export class DistributionDetector extends BaseClusterDetector {
|
||||
key = ClusterMetadataKey.DISTRIBUTION
|
||||
version: string
|
||||
|
||||
public async detect() {
|
||||
this.version = await this.getKubernetesVersion()
|
||||
if (await this.isRancher()) {
|
||||
return { value: "rancher", accuracy: 80}
|
||||
}
|
||||
if (this.isGKE()) {
|
||||
return { value: "gke", accuracy: 80}
|
||||
}
|
||||
if (this.isEKS()) {
|
||||
return { value: "eks", accuracy: 80}
|
||||
}
|
||||
if (this.isIKS()) {
|
||||
return { value: "iks", accuracy: 80}
|
||||
}
|
||||
if (this.isAKS()) {
|
||||
return { value: "aks", accuracy: 80}
|
||||
}
|
||||
if (this.isDigitalOcean()) {
|
||||
return { value: "digitalocean", accuracy: 90}
|
||||
}
|
||||
if (this.isMinikube()) {
|
||||
return { value: "minikube", accuracy: 80}
|
||||
}
|
||||
if (this.isCustom()) {
|
||||
return { value: "custom", accuracy: 10}
|
||||
}
|
||||
return { value: "vanilla", accuracy: 10}
|
||||
}
|
||||
|
||||
public async getKubernetesVersion() {
|
||||
if (this.cluster.version) return this.cluster.version
|
||||
|
||||
const response = await this.k8sRequest("/version")
|
||||
return response.gitVersion
|
||||
}
|
||||
|
||||
protected isGKE() {
|
||||
return this.version.includes("gke")
|
||||
}
|
||||
|
||||
protected isEKS() {
|
||||
return this.version.includes("eks")
|
||||
}
|
||||
|
||||
protected isIKS() {
|
||||
return this.version.includes("IKS")
|
||||
}
|
||||
|
||||
protected isAKS() {
|
||||
return this.cluster.apiUrl.endsWith("azmk8s.io")
|
||||
}
|
||||
|
||||
protected isDigitalOcean() {
|
||||
return this.cluster.apiUrl.endsWith("k8s.ondigitalocean.com")
|
||||
}
|
||||
|
||||
protected isMinikube() {
|
||||
return this.cluster.contextName.startsWith("minikube")
|
||||
}
|
||||
|
||||
protected isCustom() {
|
||||
return this.version.includes("+")
|
||||
}
|
||||
|
||||
protected async isRancher() {
|
||||
try {
|
||||
const response = await this.k8sRequest("")
|
||||
return response.data.find((api: any) => api?.apiVersion?.group === "meta.cattle.io") !== undefined
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { BaseClusterDetector } from "./base-cluster-detector";
|
||||
import { ClusterMetadataKey } from "../cluster";
|
||||
|
||||
export class LastSeenDetector extends BaseClusterDetector {
|
||||
key = ClusterMetadataKey.LAST_SEEN
|
||||
|
||||
public async detect() {
|
||||
if (!this.cluster.accessible) return null;
|
||||
|
||||
await this.k8sRequest("/version")
|
||||
return { value: new Date().toJSON(), accuracy: 100 }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { BaseClusterDetector } from "./base-cluster-detector";
|
||||
import { ClusterMetadataKey } from "../cluster";
|
||||
|
||||
export class NodesCountDetector extends BaseClusterDetector {
|
||||
key = ClusterMetadataKey.NODES_COUNT
|
||||
|
||||
public async detect() {
|
||||
const nodeCount = await this.getNodeCount()
|
||||
return { value: nodeCount, accuracy: 100}
|
||||
}
|
||||
|
||||
protected async getNodeCount(): Promise<number> {
|
||||
if (!this.cluster.accessible) return null;
|
||||
|
||||
const response = await this.k8sRequest("/api/v1/nodes")
|
||||
return response.items.length
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { BaseClusterDetector } from "./base-cluster-detector";
|
||||
import { ClusterMetadataKey } from "../cluster";
|
||||
|
||||
export class VersionDetector extends BaseClusterDetector {
|
||||
key = ClusterMetadataKey.VERSION
|
||||
value: string
|
||||
|
||||
public async detect() {
|
||||
const version = await this.getKubernetesVersion()
|
||||
return { value: version, accuracy: 100}
|
||||
}
|
||||
|
||||
public async getKubernetesVersion() {
|
||||
const response = await this.k8sRequest("/version")
|
||||
return response.gitVersion
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user