1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
lens/dashboard/client/api/endpoints/pods.api.ts
Jari Kolehmainen 1d0815abd2
Lens app source code (#119)
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
2020-03-15 09:52:02 +02:00

412 lines
11 KiB
TypeScript

import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
import { autobind } from "../../utils";
import { IMetrics, metricsApi } from "./metrics.api";
import { KubeApi } from "../kube-api";
export class PodsApi extends KubeApi<Pod> {
async getLogs(params: { namespace: string; name: string }, query?: IPodLogsQuery): Promise<string> {
const path = this.getUrl(params) + "/log";
return this.request.get(path, { query });
}
getMetrics(pods: Pod[], namespace: string, selector = "pod, namespace"): Promise<IPodMetrics> {
const podSelector = pods.map(pod => pod.getName()).join("|");
const cpuUsage = `sum(rate(container_cpu_usage_seconds_total{container_name!="POD",container_name!="",pod_name=~"${podSelector}",namespace="${namespace}"}[1m])) by (${selector})`;
const cpuRequests = `sum(kube_pod_container_resource_requests{pod=~"${podSelector}",resource="cpu",namespace="${namespace}"}) by (${selector})`;
const cpuLimits = `sum(kube_pod_container_resource_limits{pod=~"${podSelector}",resource="cpu",namespace="${namespace}"}) by (${selector})`;
const memoryUsage = `sum(container_memory_working_set_bytes{container_name!="POD",container_name!="",pod_name=~"${podSelector}",namespace="${namespace}"}) by (${selector})`;
const memoryRequests = `sum(kube_pod_container_resource_requests{pod=~"${podSelector}",resource="memory",namespace="${namespace}"}) by (${selector})`;
const memoryLimits = `sum(kube_pod_container_resource_limits{pod=~"${podSelector}",resource="memory",namespace="${namespace}"}) by (${selector})`;
const fsUsage = `sum(container_fs_usage_bytes{container_name!="POD",container_name!="",pod_name=~"${podSelector}",namespace="${namespace}"}) by (${selector})`;
const networkReceive = `sum(rate(container_network_receive_bytes_total{pod_name=~"${podSelector}",namespace="${namespace}"}[1m])) by (${selector})`;
const networkTransit = `sum(rate(container_network_transmit_bytes_total{pod_name=~"${podSelector}",namespace="${namespace}"}[1m])) by (${selector})`;
return metricsApi.getMetrics({
cpuUsage,
cpuRequests,
cpuLimits,
memoryUsage,
memoryRequests,
memoryLimits,
fsUsage,
networkReceive,
networkTransit,
}, {
namespace,
});
}
}
export interface IPodMetrics<T = IMetrics> {
[metric: string]: T;
cpuUsage: T;
cpuRequests: T;
cpuLimits: T;
memoryUsage: T;
memoryRequests: T;
memoryLimits: T;
fsUsage: T;
networkReceive: T;
networkTransit: T;
}
export interface IPodLogsQuery {
container?: string;
tailLines?: number;
timestamps?: boolean;
sinceTime?: string; // Date.toISOString()-format
}
export enum PodStatus {
TERMINATED = "Terminated",
FAILED = "Failed",
PENDING = "Pending",
RUNNING = "Running",
SUCCEEDED = "Succeeded",
EVICTED = "Evicted"
}
export interface IPodContainer {
name: string;
image: string;
command?: string[];
args?: string[];
ports: {
name?: string;
containerPort: number;
protocol: string;
}[];
resources?: {
limits: {
cpu: string;
memory: string;
};
requests: {
cpu: string;
memory: string;
};
};
env?: {
name: string;
value?: string;
valueFrom?: {
fieldRef?: {
apiVersion: string;
fieldPath: string;
};
secretKeyRef?: {
key: string;
name: string;
};
configMapKeyRef?: {
key: string;
name: string;
};
};
}[];
envFrom?: {
configMapRef?: {
name: string;
};
}[];
volumeMounts?: {
name: string;
readOnly: boolean;
mountPath: string;
}[];
livenessProbe?: IContainerProbe;
readinessProbe?: IContainerProbe;
imagePullPolicy: string;
}
interface IContainerProbe {
httpGet?: {
path?: string;
port: number;
scheme: string;
host?: string;
};
exec?: {
command: string[];
};
tcpSocket?: {
port: number;
};
initialDelaySeconds?: number;
timeoutSeconds?: number;
periodSeconds?: number;
successThreshold?: number;
failureThreshold?: number;
}
export interface IPodContainerStatus {
name: string;
state: {
[index: string]: object;
running?: {
startedAt: string;
};
waiting?: {
reason: string;
message: string;
};
terminated?: {
startedAt: string;
finishedAt: string;
exitCode: number;
reason: string;
};
};
lastState: {};
ready: boolean;
restartCount: number;
image: string;
imageID: string;
containerID: string;
}
@autobind()
export class Pod extends WorkloadKubeObject {
static kind = "Pod"
spec: {
volumes?: {
name: string;
persistentVolumeClaim: {
claimName: string;
};
secret: {
secretName: string;
defaultMode: number;
};
}[];
initContainers: IPodContainer[];
containers: IPodContainer[];
restartPolicy: string;
terminationGracePeriodSeconds: number;
dnsPolicy: string;
serviceAccountName: string;
serviceAccount: string;
priority: number;
priorityClassName: string;
nodeName: string;
nodeSelector?: {
[selector: string]: string;
};
securityContext: {};
schedulerName: string;
tolerations: {
key: string;
operator: string;
effect: string;
tolerationSeconds: number;
}[];
affinity: IAffinity;
}
status: {
phase: string;
conditions: {
type: string;
status: string;
lastProbeTime: number;
lastTransitionTime: string;
}[];
hostIP: string;
podIP: string;
startTime: string;
initContainerStatuses?: IPodContainerStatus[];
containerStatuses?: IPodContainerStatus[];
qosClass: string;
reason?: string;
}
getInitContainers() {
return this.spec.initContainers || [];
}
getContainers() {
return this.spec.containers || [];
}
getAllContainers() {
return this.getContainers().concat(this.getInitContainers());
}
getRunningContainers() {
const statuses = this.getContainerStatuses()
return this.getAllContainers().filter(container => {
return statuses.find(status => status.name === container.name && !!status.state["running"])
}
)
}
getContainerStatuses(includeInitContainers = true) {
const statuses: IPodContainerStatus[] = [];
const { containerStatuses, initContainerStatuses } = this.status;
if (containerStatuses) {
statuses.push(...containerStatuses);
}
if (includeInitContainers && initContainerStatuses) {
statuses.push(...initContainerStatuses);
}
return statuses;
}
getRestartsCount(): number {
const { containerStatuses } = this.status;
if (!containerStatuses) return 0;
return containerStatuses.reduce((count, item) => count + item.restartCount, 0);
}
getQosClass() {
return this.status.qosClass || "";
}
getReason() {
return this.status.reason || "";
}
getPriorityClassName() {
return this.spec.priorityClassName || "";
}
// Returns one of 5 statuses: Running, Succeeded, Pending, Failed, Evicted
getStatus() {
const phase = this.getStatusPhase();
const reason = this.getReason();
const goodConditions = ["Initialized", "Ready"].every(condition =>
!!this.getConditions().find(item => item.type === condition && item.status === "True")
);
if (reason === PodStatus.EVICTED) {
return PodStatus.EVICTED;
}
if (phase === PodStatus.FAILED) {
return PodStatus.FAILED;
}
if (phase === PodStatus.SUCCEEDED) {
return PodStatus.SUCCEEDED;
}
if (phase === PodStatus.RUNNING && goodConditions) {
return PodStatus.RUNNING;
}
return PodStatus.PENDING;
}
// Returns pod phase or container error if occured
getStatusMessage() {
let message = "";
const statuses = this.getContainerStatuses(false); // not including initContainers
if (statuses.length) {
statuses.forEach(status => {
const { state } = status;
if (state.waiting) {
const { reason } = state.waiting;
message = reason ? reason : "Waiting";
}
if (state.terminated) {
const { reason } = state.terminated;
message = reason ? reason : "Terminated";
}
})
}
if (this.getReason() === PodStatus.EVICTED) return "Evicted";
if (message) return message;
return this.getStatusPhase();
}
getStatusPhase() {
return this.status.phase;
}
getConditions() {
return this.status.conditions || [];
}
getVolumes() {
return this.spec.volumes || [];
}
getSecrets(): string[] {
return this.getVolumes()
.filter(vol => vol.secret)
.map(vol => vol.secret.secretName);
}
getNodeSelectors(): string[] {
const { nodeSelector } = this.spec
if (!nodeSelector) return []
return Object.entries(nodeSelector).map(values => values.join(": "))
}
getTolerations() {
return this.spec.tolerations || []
}
getAffinity(): IAffinity {
return this.spec.affinity
}
hasIssues() {
const notReady = !!this.getConditions().find(condition => {
return condition.type == "Ready" && condition.status !== "True"
});
const crashLoop = !!this.getContainerStatuses().find(condition => {
const waiting = condition.state.waiting
return (waiting && waiting.reason == "CrashLoopBackOff")
})
return (
notReady ||
crashLoop ||
this.getStatusPhase() !== "Running"
)
}
getLivenessProbe(container: IPodContainer) {
return this.getProbe(container.livenessProbe);
}
getReadinessProbe(container: IPodContainer) {
return this.getProbe(container.readinessProbe);
}
getProbe(probeData: IContainerProbe) {
if (!probeData) return [];
const {
httpGet, exec, tcpSocket, initialDelaySeconds, timeoutSeconds,
periodSeconds, successThreshold, failureThreshold
} = probeData;
const probe = [];
// HTTP Request
if (httpGet) {
const { path, port, host, scheme } = httpGet;
probe.push(
"http-get",
`${scheme.toLowerCase()}://${host || ""}:${port || ""}${path || ""}`,
);
}
// Command
if (exec && exec.command) {
probe.push(`exec [${exec.command.join(" ")}]`);
}
// TCP Probe
if (tcpSocket && tcpSocket.port) {
probe.push(`tcp-socket :${tcpSocket.port}`);
}
probe.push(
`delay=${initialDelaySeconds || "0"}s`,
`timeout=${timeoutSeconds || "0"}s`,
`period=${periodSeconds || "0"}s`,
`#success=${successThreshold || "0"}`,
`#failure=${failureThreshold || "0"}`,
);
return probe;
}
}
export const podsApi = new PodsApi({
kind: Pod.kind,
apiBase: "/api/v1/pods",
isNamespaced: true,
objectConstructor: Pod,
});