mirror of https://github.com/portainer/portainer
fix(kubernetes/pods): save note (#4675)
* feat(kubernetes/pods): introduce patch api * feat(k8s/pods): pod converter * feat(kubernetes/pods): introduce patch api * feat(k8s/pod): add annotations only if needed * fix(k8s/pod): replace class with factory functionpull/4730/head
parent
2b257d2785
commit
46ff8a01bc
|
@ -1,5 +1,16 @@
|
||||||
|
import * as JsonPatch from 'fast-json-patch';
|
||||||
import _ from 'lodash-es';
|
import _ from 'lodash-es';
|
||||||
import { KubernetesPod, KubernetesPodToleration, KubernetesPodAffinity, KubernetesPodContainer, KubernetesPodContainerTypes } from 'Kubernetes/pod/models';
|
|
||||||
|
import KubernetesCommonHelper from 'Kubernetes/helpers/commonHelper';
|
||||||
|
import {
|
||||||
|
KubernetesPortainerApplicationStackNameLabel,
|
||||||
|
KubernetesPortainerApplicationNameLabel,
|
||||||
|
KubernetesPortainerApplicationOwnerLabel,
|
||||||
|
KubernetesPortainerApplicationNote,
|
||||||
|
} from 'Kubernetes/models/application/models';
|
||||||
|
|
||||||
|
import { createPayloadFactory } from './payloads/create';
|
||||||
|
import { KubernetesPod, KubernetesPodToleration, KubernetesPodAffinity, KubernetesPodContainer, KubernetesPodContainerTypes } from './models';
|
||||||
|
|
||||||
function computeStatus(statuses) {
|
function computeStatus(statuses) {
|
||||||
const containerStatuses = _.map(statuses, 'state');
|
const containerStatuses = _.map(statuses, 'state');
|
||||||
|
@ -104,4 +115,48 @@ export default class KubernetesPodConverter {
|
||||||
res.Tolerations = computeTolerations(data.spec.tolerations);
|
res.Tolerations = computeTolerations(data.spec.tolerations);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static patchPayload(oldPod, newPod) {
|
||||||
|
const oldPayload = createPayload(oldPod);
|
||||||
|
const newPayload = createPayload(newPod);
|
||||||
|
const payload = JsonPatch.compare(oldPayload, newPayload);
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPayload(pod) {
|
||||||
|
const payload = createPayloadFactory();
|
||||||
|
payload.metadata.name = pod.Name;
|
||||||
|
payload.metadata.namespace = pod.Namespace;
|
||||||
|
payload.metadata.labels[KubernetesPortainerApplicationStackNameLabel] = pod.StackName;
|
||||||
|
payload.metadata.labels[KubernetesPortainerApplicationNameLabel] = pod.ApplicationName;
|
||||||
|
payload.metadata.labels[KubernetesPortainerApplicationOwnerLabel] = pod.ApplicationOwner;
|
||||||
|
if (pod.Note) {
|
||||||
|
payload.metadata.annotations[KubernetesPortainerApplicationNote] = pod.Note;
|
||||||
|
} else {
|
||||||
|
payload.metadata.annotations = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.spec.replicas = pod.ReplicaCount;
|
||||||
|
payload.spec.selector.matchLabels.app = pod.Name;
|
||||||
|
payload.spec.template.metadata.labels.app = pod.Name;
|
||||||
|
payload.spec.template.metadata.labels[KubernetesPortainerApplicationNameLabel] = pod.ApplicationName;
|
||||||
|
payload.spec.template.spec.containers[0].name = pod.Name;
|
||||||
|
payload.spec.template.spec.containers[0].image = pod.Image;
|
||||||
|
payload.spec.template.spec.affinity = pod.Affinity;
|
||||||
|
KubernetesCommonHelper.assignOrDeleteIfEmpty(payload, 'spec.template.spec.containers[0].env', pod.Env);
|
||||||
|
KubernetesCommonHelper.assignOrDeleteIfEmpty(payload, 'spec.template.spec.containers[0].volumeMounts', pod.VolumeMounts);
|
||||||
|
KubernetesCommonHelper.assignOrDeleteIfEmpty(payload, 'spec.template.spec.volumes', pod.Volumes);
|
||||||
|
if (pod.MemoryLimit) {
|
||||||
|
payload.spec.template.spec.containers[0].resources.limits.memory = pod.MemoryLimit;
|
||||||
|
payload.spec.template.spec.containers[0].resources.requests.memory = pod.MemoryLimit;
|
||||||
|
}
|
||||||
|
if (pod.CpuLimit) {
|
||||||
|
payload.spec.template.spec.containers[0].resources.limits.cpu = pod.CpuLimit;
|
||||||
|
payload.spec.template.spec.containers[0].resources.requests.cpu = pod.CpuLimit;
|
||||||
|
}
|
||||||
|
if (!pod.CpuLimit && !pod.MemoryLimit) {
|
||||||
|
delete payload.spec.template.spec.containers[0].resources;
|
||||||
|
}
|
||||||
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { KubernetesCommonMetadataPayload } from 'Kubernetes/models/common/payloads';
|
||||||
|
|
||||||
|
export function createPayloadFactory() {
|
||||||
|
return {
|
||||||
|
metadata: new KubernetesCommonMetadataPayload(),
|
||||||
|
spec: {
|
||||||
|
replicas: 0,
|
||||||
|
selector: {
|
||||||
|
matchLabels: {
|
||||||
|
app: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
strategy: {
|
||||||
|
type: 'RollingUpdate',
|
||||||
|
rollingUpdate: {
|
||||||
|
maxSurge: 0,
|
||||||
|
maxUnavailable: '100%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: {
|
||||||
|
metadata: {
|
||||||
|
labels: {
|
||||||
|
app: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
affinity: {},
|
||||||
|
containers: [
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
image: '',
|
||||||
|
env: [],
|
||||||
|
resources: {
|
||||||
|
limits: {},
|
||||||
|
requests: {},
|
||||||
|
},
|
||||||
|
volumeMounts: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
volumes: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import angular from 'angular';
|
||||||
import PortainerError from 'Portainer/error';
|
import PortainerError from 'Portainer/error';
|
||||||
|
|
||||||
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
|
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
|
||||||
|
import KubernetesPodConverter from './converter';
|
||||||
|
|
||||||
class KubernetesPodService {
|
class KubernetesPodService {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
|
@ -13,6 +14,7 @@ class KubernetesPodService {
|
||||||
this.getAllAsync = this.getAllAsync.bind(this);
|
this.getAllAsync = this.getAllAsync.bind(this);
|
||||||
this.logsAsync = this.logsAsync.bind(this);
|
this.logsAsync = this.logsAsync.bind(this);
|
||||||
this.deleteAsync = this.deleteAsync.bind(this);
|
this.deleteAsync = this.deleteAsync.bind(this);
|
||||||
|
this.patchAsync = this.patchAsync.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAsync(namespace, name) {
|
async getAsync(namespace, name) {
|
||||||
|
@ -74,6 +76,29 @@ class KubernetesPodService {
|
||||||
return this.$async(this.logsAsync, namespace, podName, containerName);
|
return this.$async(this.logsAsync, namespace, podName, containerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PATCH
|
||||||
|
*/
|
||||||
|
async patchAsync(oldPod, newPod) {
|
||||||
|
try {
|
||||||
|
const params = new KubernetesCommonParams();
|
||||||
|
params.id = newPod.Name;
|
||||||
|
const namespace = newPod.Namespace;
|
||||||
|
const payload = KubernetesPodConverter.patchPayload(oldPod, newPod);
|
||||||
|
if (!payload.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = await this.KubernetesPods(namespace).patch(params, payload).$promise;
|
||||||
|
return data;
|
||||||
|
} catch (err) {
|
||||||
|
throw new PortainerError('Unable to patch pod', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patch(oldPod, newPod) {
|
||||||
|
return this.$async(this.patchAsync, oldPod, newPod);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DELETE
|
* DELETE
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -31,6 +31,12 @@ angular.module('portainer.kubernetes').factory('KubernetesPods', [
|
||||||
create: { method: 'POST' },
|
create: { method: 'POST' },
|
||||||
update: { method: 'PUT' },
|
update: { method: 'PUT' },
|
||||||
delete: { method: 'DELETE' },
|
delete: { method: 'DELETE' },
|
||||||
|
patch: {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json-patch+json',
|
||||||
|
},
|
||||||
|
},
|
||||||
logs: {
|
logs: {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: { action: 'log' },
|
params: { action: 'log' },
|
||||||
|
|
Loading…
Reference in New Issue