Implement cluster metadata detectors (#1106)

Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com>
This commit is contained in:
Lauri Nevala authored and GitHub committed 2020-10-22 21:45:54 +03:00
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
}
}