k8s add hpa and ingresses UI (#2640)

* k8s add hpa and ingresses UI

* add todo
This commit is contained in:
Matthew Clarke
2020-09-28 19:32:35 +01:00
committed by GitHub
parent 964dbd0dad
commit d66c6a7dcb
19 changed files with 632 additions and 36 deletions
@@ -7,7 +7,9 @@ An app to roll dice (it doesn't actually do that).
## Prerequisites
- kubectl installed
- Minikube installed
- Minikube installed, with the following addons
- metrics-server
- ingress
- jq installed
- Backstage locally built and ready to run
@@ -53,6 +53,41 @@ spec:
ports:
- containerPort: 82
---
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: dice-roller
labels:
'backstage.io/kubernetes-id': dice-roller
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: dice-roller
minReplicas: 10
maxReplicas: 15
targetCPUUtilizationPercentage: 50
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: dice-roller
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
labels:
'backstage.io/kubernetes-id': dice-roller
spec:
rules:
- host: nginx
http:
paths:
- path: /
backend:
serviceName: dice-roller
servicePort: 80
---
apiVersion: v1
kind: ConfigMap
@@ -15,7 +15,13 @@
*/
import { ClusterDetails } from '..';
import { AppsV1Api, CoreV1Api, KubeConfig } from '@kubernetes/client-node';
import {
AppsV1Api,
AutoscalingV1Api,
CoreV1Api,
KubeConfig,
NetworkingV1beta1Api,
} from '@kubernetes/client-node';
export class KubernetesClientProvider {
private readonly coreClientMap: {
@@ -26,9 +32,19 @@ export class KubernetesClientProvider {
[key: string]: AppsV1Api;
};
private readonly autoscalingClientMap: {
[key: string]: AutoscalingV1Api;
};
private readonly networkingBeta1ClientMap: {
[key: string]: NetworkingV1beta1Api;
};
constructor() {
this.coreClientMap = {};
this.appsClientMap = {};
this.autoscalingClientMap = {};
this.networkingBeta1ClientMap = {};
}
// visible for testing
@@ -93,4 +109,36 @@ export class KubernetesClientProvider {
return client;
}
getAutoscalingClientByClusterDetails(clusterDetails: ClusterDetails) {
const clientMapKey = clusterDetails.name;
if (this.autoscalingClientMap.hasOwnProperty(clientMapKey)) {
return this.autoscalingClientMap[clientMapKey];
}
const kc = this.getKubeConfig(clusterDetails);
const client = kc.makeApiClient(AutoscalingV1Api);
this.autoscalingClientMap[clientMapKey] = client;
return client;
}
getNetworkingBeta1Client(clusterDetails: ClusterDetails) {
const clientMapKey = clusterDetails.name;
if (this.networkingBeta1ClientMap.hasOwnProperty(clientMapKey)) {
return this.networkingBeta1ClientMap[clientMapKey];
}
const kc = this.getKubeConfig(clusterDetails);
const client = kc.makeApiClient(NetworkingV1beta1Api);
this.networkingBeta1ClientMap[clientMapKey] = client;
return client;
}
}
@@ -31,6 +31,8 @@ describe('KubernetesClientProvider', () => {
const kubernetesClientProvider: any = {
getCoreClientByClusterDetails: jest.fn(() => clientMock),
getAppsClientByClusterDetails: jest.fn(() => clientMock),
getAutoscalingClientByClusterDetails: jest.fn(() => clientMock),
getNetworkingBeta1Client: jest.fn(() => clientMock),
};
const sut = new KubernetesClientBasedFetcher({
@@ -16,12 +16,15 @@
import {
AppsV1Api,
AutoscalingV1Api,
CoreV1Api,
ExtensionsV1beta1Ingress,
NetworkingV1beta1Api,
V1ConfigMap,
V1Deployment,
V1HorizontalPodAutoscaler,
V1Pod,
V1ReplicaSet,
V1Secret,
} from '@kubernetes/client-node';
import { KubernetesClientProvider } from './KubernetesClientProvider';
import { V1Service } from '@kubernetes/client-node/dist/gen/model/v1Service';
@@ -36,6 +39,8 @@ import {
export interface Clients {
core: CoreV1Api;
apps: AppsV1Api;
autoscaling: AutoscalingV1Api;
networkingBeta1: NetworkingV1beta1Api;
}
export interface KubernetesClientBasedFetcherOptions {
@@ -67,6 +72,7 @@ export class KubernetesClientBasedFetcher implements KubernetesFetcher {
);
}
// TODO could probably do with a tidy up
private fetchByObjectType(
serviceId: string,
clusterDetails: ClusterDetails,
@@ -93,13 +99,18 @@ export class KubernetesClientBasedFetcher implements KubernetesFetcher {
serviceId,
clusterDetails,
).then(r => ({ type: type, resources: r }));
case 'secrets':
return this.fetchSecretsByServiceId(
case 'services':
return this.fetchServicesByServiceId(
serviceId,
clusterDetails,
).then(r => ({ type: type, resources: r }));
case 'services':
return this.fetchServicesByServiceId(
case 'horizontalpodautoscalers':
return this.fetchHorizontalPodAutoscalersByServiceId(
serviceId,
clusterDetails,
).then(r => ({ type: type, resources: r }));
case 'ingresses':
return this.fetchIngressesByServiceId(
serviceId,
clusterDetails,
).then(r => ({ type: type, resources: r }));
@@ -119,9 +130,15 @@ export class KubernetesClientBasedFetcher implements KubernetesFetcher {
const apps = this.kubernetesClientProvider.getAppsClientByClusterDetails(
clusterDetails,
);
const autoscaling = this.kubernetesClientProvider.getAutoscalingClientByClusterDetails(
clusterDetails,
);
const networkingBeta1 = this.kubernetesClientProvider.getNetworkingBeta1Client(
clusterDetails,
);
this.logger.debug(`calling cluster=${clusterDetails.name}`);
return fn({ core, apps }).then(result => {
return fn({ core, apps, autoscaling, networkingBeta1 }).then(result => {
return result.body.items;
});
}
@@ -168,20 +185,6 @@ export class KubernetesClientBasedFetcher implements KubernetesFetcher {
);
}
private fetchSecretsByServiceId(
serviceId: string,
clusterDetails: ClusterDetails,
): Promise<Array<V1Secret>> {
return this.singleClusterFetch<V1Secret>(clusterDetails, ({ core }) =>
core.listSecretForAllNamespaces(
false,
'',
'',
`backstage.io/kubernetes-id=${serviceId}`,
),
);
}
private fetchDeploymentsByServiceId(
serviceId: string,
clusterDetails: ClusterDetails,
@@ -209,4 +212,36 @@ export class KubernetesClientBasedFetcher implements KubernetesFetcher {
),
);
}
private fetchHorizontalPodAutoscalersByServiceId(
serviceId: string,
clusterDetails: ClusterDetails,
): Promise<Array<V1HorizontalPodAutoscaler>> {
return this.singleClusterFetch<V1HorizontalPodAutoscaler>(
clusterDetails,
({ autoscaling }) =>
autoscaling.listHorizontalPodAutoscalerForAllNamespaces(
false,
'',
'',
`backstage.io/kubernetes-id=${serviceId}`,
),
);
}
private fetchIngressesByServiceId(
serviceId: string,
clusterDetails: ClusterDetails,
): Promise<Array<ExtensionsV1beta1Ingress>> {
return this.singleClusterFetch<ExtensionsV1beta1Ingress>(
clusterDetails,
({ networkingBeta1 }) =>
networkingBeta1.listIngressForAllNamespaces(
false,
'',
'',
`backstage.io/kubernetes-id=${serviceId}`,
),
);
}
}
@@ -36,6 +36,8 @@ const DEFAULT_OBJECTS = new Set<KubernetesObjectTypes>([
'configmaps',
'deployments',
'replicasets',
'horizontalpodautoscalers',
'ingresses',
]);
export const handleGetKubernetesObjectsByServiceId: GetKubernetesObjectsByServiceIdHandler = async (
+18 -10
View File
@@ -15,11 +15,12 @@
*/
import {
ExtensionsV1beta1Ingress,
V1ConfigMap,
V1Deployment,
V1HorizontalPodAutoscaler,
V1Pod,
V1ReplicaSet,
V1Secret,
V1Service,
} from '@kubernetes/client-node';
@@ -43,9 +44,10 @@ export type FetchResponse =
| PodFetchResponse
| ServiceFetchResponse
| ConfigMapFetchResponse
| SecretFetchResponse
| DeploymentFetchResponse
| ReplicaSetsFetchResponse;
| ReplicaSetsFetchResponse
| HorizontalPodAutoscalersFetchResponse
| IngressesFetchResponse;
// TODO fairly sure there's a easier way to do this
@@ -53,9 +55,10 @@ export type KubernetesObjectTypes =
| 'pods'
| 'services'
| 'configmaps'
| 'secrets'
| 'deployments'
| 'replicasets';
| 'replicasets'
| 'horizontalpodautoscalers'
| 'ingresses';
export interface PodFetchResponse {
type: 'pods';
@@ -72,11 +75,6 @@ export interface ConfigMapFetchResponse {
resources: Array<V1ConfigMap>;
}
export interface SecretFetchResponse {
type: 'secrets';
resources: Array<V1Secret>;
}
export interface DeploymentFetchResponse {
type: 'deployments';
resources: Array<V1Deployment>;
@@ -87,6 +85,16 @@ export interface ReplicaSetsFetchResponse {
resources: Array<V1ReplicaSet>;
}
export interface HorizontalPodAutoscalersFetchResponse {
type: 'horizontalpodautoscalers';
resources: Array<V1HorizontalPodAutoscaler>;
}
export interface IngressesFetchResponse {
type: 'ingresses';
resources: Array<ExtensionsV1beta1Ingress>;
}
// Fetches information from a kubernetes cluster using the cluster details object
// to target a specific cluster
export interface KubernetesFetcher {