mirror of
https://github.com/portainer/portainer.git
synced 2025-08-05 05:45:22 +02:00
feat(k8s/application): expose tolerations and affinities (#4063)
* feat(k8s/application): expose placement conditions * feat(k8s/applications): minor UI update * feat(k8s/application): update message for admin and non admin users * feat(kubernetes/applications): minor UI update Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
This commit is contained in:
parent
63bf654d8d
commit
4431d748c2
22 changed files with 635 additions and 112 deletions
54
app/kubernetes/pod/converter.js
Normal file
54
app/kubernetes/pod/converter.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
import _ from 'lodash-es';
|
||||
import { KubernetesPod, KubernetesPodToleration, KubernetesPodAffinity } from 'Kubernetes/pod/models';
|
||||
|
||||
function computeStatus(statuses) {
|
||||
const containerStatuses = _.map(statuses, 'state');
|
||||
const running = _.filter(containerStatuses, (s) => s.running).length;
|
||||
const waiting = _.filter(containerStatuses, (s) => s.waiting).length;
|
||||
if (waiting) {
|
||||
return 'Waiting';
|
||||
} else if (!running) {
|
||||
return 'Terminated';
|
||||
}
|
||||
return 'Running';
|
||||
}
|
||||
|
||||
function computeAffinity(affinity) {
|
||||
const res = new KubernetesPodAffinity();
|
||||
if (affinity) {
|
||||
res.NodeAffinity = affinity.nodeAffinity || {};
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function computeTolerations(tolerations) {
|
||||
return _.map(tolerations, (item) => {
|
||||
const res = new KubernetesPodToleration();
|
||||
res.Key = item.key;
|
||||
res.Operator = item.operator;
|
||||
res.Value = item.value;
|
||||
res.TolerationSeconds = item.tolerationSeconds;
|
||||
res.Effect = item.effect;
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
export default class KubernetesPodConverter {
|
||||
static apiToModel(data) {
|
||||
const res = new KubernetesPod();
|
||||
res.Id = data.metadata.uid;
|
||||
res.Name = data.metadata.name;
|
||||
res.Namespace = data.metadata.namespace;
|
||||
res.Images = _.map(data.spec.containers, 'image');
|
||||
res.Status = computeStatus(data.status.containerStatuses);
|
||||
res.Restarts = _.sumBy(data.status.containerStatuses, 'restartCount');
|
||||
res.Node = data.spec.nodeName;
|
||||
res.CreationDate = data.status.startTime;
|
||||
res.Containers = data.spec.containers;
|
||||
res.Labels = data.metadata.labels;
|
||||
res.Affinity = computeAffinity(data.spec.affinity);
|
||||
res.NodeSelector = data.spec.nodeSelector;
|
||||
res.Tolerations = computeTolerations(data.spec.tolerations);
|
||||
return res;
|
||||
}
|
||||
}
|
84
app/kubernetes/pod/filters.js
Normal file
84
app/kubernetes/pod/filters.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
import _ from 'lodash-es';
|
||||
|
||||
angular
|
||||
.module('portainer.kubernetes')
|
||||
.filter('kubernetesPodStatusColor', function () {
|
||||
'use strict';
|
||||
return function (text) {
|
||||
var status = _.toLower(text);
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return 'success';
|
||||
case 'waiting':
|
||||
return 'warning';
|
||||
case 'terminated':
|
||||
return 'info';
|
||||
default:
|
||||
return 'danger';
|
||||
}
|
||||
};
|
||||
})
|
||||
.filter('kubernetesPodConditionStatusBadge', function () {
|
||||
'use strict';
|
||||
return function (status, type) {
|
||||
switch (type) {
|
||||
case 'Unschedulable':
|
||||
switch (status) {
|
||||
case 'True':
|
||||
return 'fa-exclamation-triangle red-icon';
|
||||
case 'False':
|
||||
return 'fa-check green-icon';
|
||||
case 'Unknown':
|
||||
return 'fa-exclamation-circle orange-icon';
|
||||
}
|
||||
break;
|
||||
case 'PodScheduled':
|
||||
case 'Ready':
|
||||
case 'Initialized':
|
||||
case 'ContainersReady':
|
||||
switch (status) {
|
||||
case 'True':
|
||||
return 'fa-check green-icon';
|
||||
case 'False':
|
||||
return 'fa-exclamation-triangle red-icon';
|
||||
case 'Unknown':
|
||||
return 'fa-exclamation-circle orange-icon';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 'fa-question-circle red-icon';
|
||||
}
|
||||
};
|
||||
})
|
||||
.filter('kubernetesPodConditionStatusText', function () {
|
||||
'use strict';
|
||||
return function (status, type) {
|
||||
switch (type) {
|
||||
case 'Unschedulable':
|
||||
switch (status) {
|
||||
case 'True':
|
||||
return 'Alert';
|
||||
case 'False':
|
||||
return 'OK';
|
||||
case 'Unknown':
|
||||
return 'Warning';
|
||||
}
|
||||
break;
|
||||
case 'PodScheduled':
|
||||
case 'Ready':
|
||||
case 'Initialized':
|
||||
case 'ContainersReady':
|
||||
switch (status) {
|
||||
case 'True':
|
||||
return 'Ok';
|
||||
case 'False':
|
||||
return 'Alert';
|
||||
case 'Unknown':
|
||||
return 'Warning';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
};
|
||||
});
|
65
app/kubernetes/pod/models/affinities.js
Normal file
65
app/kubernetes/pod/models/affinities.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
export const KubernetesPodNodeAffinityNodeSelectorRequirementOperators = Object.freeze({
|
||||
IN: 'In',
|
||||
NOT_IN: 'NotIn',
|
||||
EXISTS: 'Exists',
|
||||
DOES_NOT_EXIST: 'DoesNotExist',
|
||||
GREATER_THAN: 'Gt',
|
||||
LOWER_THAN: 'Lt',
|
||||
});
|
||||
|
||||
/**
|
||||
* KubernetesPodAffinity Model
|
||||
*/
|
||||
const _KubernetesPodAffinity = Object.freeze({
|
||||
NodeAffinity: {},
|
||||
// PodAffinity: {},
|
||||
// PodAntiAffinity: {},
|
||||
});
|
||||
|
||||
export class KubernetesPodAffinity {
|
||||
constructor() {
|
||||
Object.assign(this, JSON.parse(JSON.stringify(_KubernetesPodAffinity)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* KubernetesPodNodeAffinity Model
|
||||
*/
|
||||
const _KubernetesPodNodeAffinity = Object.freeze({
|
||||
PreferredDuringSchedulingIgnoredDuringExecution: [],
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: {},
|
||||
});
|
||||
|
||||
export class KubernetesPodNodeAffinity {
|
||||
constructor() {
|
||||
Object.assign(this, JSON.parse(JSON.stringify(_KubernetesPodNodeAffinity)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* KubernetesPodPodAffinity Model
|
||||
*/
|
||||
const _KubernetesPodPodAffinity = Object.freeze({
|
||||
PreferredDuringSchedulingIgnoredDuringExecution: [],
|
||||
equiredDuringSchedulingIgnoredDuringExecution: [],
|
||||
});
|
||||
|
||||
export class KubernetesPodPodAffinity {
|
||||
constructor() {
|
||||
Object.assign(this, JSON.parse(JSON.stringify(_KubernetesPodPodAffinity)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* KubernetesPodPodAntiAffinity Model
|
||||
*/
|
||||
const _KubernetesPodPodAntiAffinity = Object.freeze({
|
||||
preferredDuringSchedulingIgnoredDuringExecution: [],
|
||||
requiredDuringSchedulingIgnoredDuringExecution: [],
|
||||
});
|
||||
|
||||
export class KubernetesPodPodAntiAffinity {
|
||||
constructor() {
|
||||
Object.assign(this, JSON.parse(JSON.stringify(_KubernetesPodPodAntiAffinity)));
|
||||
}
|
||||
}
|
42
app/kubernetes/pod/models/index.js
Normal file
42
app/kubernetes/pod/models/index.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
export * from './affinities';
|
||||
|
||||
/**
|
||||
* KubernetesPod Model
|
||||
*/
|
||||
const _KubernetesPod = Object.freeze({
|
||||
Id: '',
|
||||
Name: '',
|
||||
Namespace: '',
|
||||
Images: [],
|
||||
Status: '',
|
||||
Restarts: 0,
|
||||
Node: '',
|
||||
CreationDate: '',
|
||||
Containers: [],
|
||||
Labels: [],
|
||||
Affinity: {}, // KubernetesPodAffinity
|
||||
Tolerations: [], // KubernetesPodToleration[]
|
||||
});
|
||||
|
||||
export class KubernetesPod {
|
||||
constructor() {
|
||||
Object.assign(this, JSON.parse(JSON.stringify(_KubernetesPod)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* KubernetesPodToleration Model
|
||||
*/
|
||||
const _KubernetesPodToleration = Object.freeze({
|
||||
Key: '',
|
||||
Operator: '',
|
||||
Value: '',
|
||||
TolerationSeconds: 0,
|
||||
Effect: '',
|
||||
});
|
||||
|
||||
export class KubernetesPodToleration {
|
||||
constructor() {
|
||||
Object.assign(this, JSON.parse(JSON.stringify(_KubernetesPodToleration)));
|
||||
}
|
||||
}
|
75
app/kubernetes/pod/service.js
Normal file
75
app/kubernetes/pod/service.js
Normal file
|
@ -0,0 +1,75 @@
|
|||
import _ from 'lodash-es';
|
||||
import angular from 'angular';
|
||||
import PortainerError from 'Portainer/error';
|
||||
|
||||
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
|
||||
import KubernetesPodConverter from 'Kubernetes/pod/converter';
|
||||
|
||||
class KubernetesPodService {
|
||||
/* @ngInject */
|
||||
constructor($async, KubernetesPods) {
|
||||
this.$async = $async;
|
||||
this.KubernetesPods = KubernetesPods;
|
||||
|
||||
this.getAllAsync = this.getAllAsync.bind(this);
|
||||
this.logsAsync = this.logsAsync.bind(this);
|
||||
this.deleteAsync = this.deleteAsync.bind(this);
|
||||
}
|
||||
/**
|
||||
* GET ALL
|
||||
*/
|
||||
async getAllAsync(namespace) {
|
||||
try {
|
||||
const data = await this.KubernetesPods(namespace).get().$promise;
|
||||
return _.map(data.items, (item) => KubernetesPodConverter.apiToModel(item));
|
||||
} catch (err) {
|
||||
throw new PortainerError('Unable to retrieve pods', err);
|
||||
}
|
||||
}
|
||||
|
||||
get(namespace) {
|
||||
return this.$async(this.getAllAsync, namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs
|
||||
*
|
||||
* @param {string} namespace
|
||||
* @param {string} podName
|
||||
*/
|
||||
async logsAsync(namespace, podName) {
|
||||
try {
|
||||
const params = new KubernetesCommonParams();
|
||||
params.id = podName;
|
||||
const data = await this.KubernetesPods(namespace).logs(params).$promise;
|
||||
return data.logs.length === 0 ? [] : data.logs.split('\n');
|
||||
} catch (err) {
|
||||
throw new PortainerError('Unable to retrieve pod logs', err);
|
||||
}
|
||||
}
|
||||
|
||||
logs(namespace, podName) {
|
||||
return this.$async(this.logsAsync, namespace, podName);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE
|
||||
*/
|
||||
async deleteAsync(pod) {
|
||||
try {
|
||||
const params = new KubernetesCommonParams();
|
||||
params.id = pod.Name;
|
||||
const namespace = pod.Namespace;
|
||||
await this.KubernetesPods(namespace).delete(params).$promise;
|
||||
} catch (err) {
|
||||
throw new PortainerError('Unable to remove pod', err);
|
||||
}
|
||||
}
|
||||
|
||||
delete(pod) {
|
||||
return this.$async(this.deleteAsync, pod);
|
||||
}
|
||||
}
|
||||
|
||||
export default KubernetesPodService;
|
||||
angular.module('portainer.kubernetes').service('KubernetesPodService', KubernetesPodService);
|
Loading…
Add table
Add a link
Reference in a new issue