mirror of https://github.com/portainer/portainer
fix(app): allow editing pod services [EE-6480] (#10875)
* fix(app): allow editing pod services [EE-6480] * address review comment --------- Co-authored-by: testa113 <testa113> Co-authored-by: prabhat khera <prabhat.khera@portainer.io>pull/10731/merge
parent
7cba02226e
commit
4c0049edbe
|
@ -22,6 +22,7 @@ import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
|
|||
import KubernetesDeploymentConverter from 'Kubernetes/converters/deployment';
|
||||
import KubernetesDaemonSetConverter from 'Kubernetes/converters/daemonSet';
|
||||
import KubernetesStatefulSetConverter from 'Kubernetes/converters/statefulSet';
|
||||
import KubernetesPodConverter from 'Kubernetes/pod/converter';
|
||||
import KubernetesServiceConverter from 'Kubernetes/converters/service';
|
||||
import KubernetesPersistentVolumeClaimConverter from 'Kubernetes/converters/persistentVolumeClaim';
|
||||
import PortainerError from 'Portainer/error';
|
||||
|
@ -55,6 +56,8 @@ class KubernetesApplicationConverter {
|
|||
res.Id = data.metadata.uid;
|
||||
res.Name = data.metadata.name;
|
||||
res.Metadata = data.metadata;
|
||||
res.ApplicationType = data.kind;
|
||||
res.Labels = data.metadata.labels || {};
|
||||
|
||||
if (data.metadata.labels) {
|
||||
const { labels } = data.metadata;
|
||||
|
@ -281,6 +284,7 @@ class KubernetesApplicationConverter {
|
|||
res.ApplicationType = app.ApplicationType;
|
||||
res.ResourcePool = _.find(resourcePools, ['Namespace.Name', app.ResourcePool]);
|
||||
res.Name = app.Name;
|
||||
res.Labels = app.Labels;
|
||||
res.Services = KubernetesApplicationHelper.generateServicesFormValuesFromServices(app, ingresses);
|
||||
res.Selector = KubernetesApplicationHelper.generateSelectorFromService(app);
|
||||
res.StackName = app.StackName;
|
||||
|
@ -341,6 +345,8 @@ class KubernetesApplicationConverter {
|
|||
(claims.length === 0 || (claims.length > 0 && formValues.DataAccessPolicy === KubernetesApplicationDataAccessPolicies.Shared && rwx))) ||
|
||||
formValues.ApplicationType === KubernetesApplicationTypes.DaemonSet;
|
||||
|
||||
const pod = formValues.ApplicationType === KubernetesApplicationTypes.POD;
|
||||
|
||||
let app;
|
||||
if (deployment) {
|
||||
app = KubernetesDeploymentConverter.applicationFormValuesToDeployment(formValues, claims);
|
||||
|
@ -348,6 +354,8 @@ class KubernetesApplicationConverter {
|
|||
app = KubernetesStatefulSetConverter.applicationFormValuesToStatefulSet(formValues, claims);
|
||||
} else if (daemonSet) {
|
||||
app = KubernetesDaemonSetConverter.applicationFormValuesToDaemonSet(formValues, claims);
|
||||
} else if (pod) {
|
||||
app = KubernetesPodConverter.applicationFormValuesToPod(formValues, claims);
|
||||
} else {
|
||||
throw new PortainerError('Unable to determine which association to use to convert form');
|
||||
}
|
||||
|
|
|
@ -315,7 +315,12 @@ class KubernetesApplicationHelper {
|
|||
ports.push(svcport);
|
||||
});
|
||||
svc.Ports = ports;
|
||||
svc.Selector = app.Raw.spec.selector.matchLabels;
|
||||
// if the app is a pod (doesn't have a selector), then get the pod labels
|
||||
if (app.Raw.spec.selector) {
|
||||
svc.Selector = app.Raw.spec.selector.matchLabels;
|
||||
} else {
|
||||
svc.Selector = app.Raw.metadata.labels || { app: app.Name };
|
||||
}
|
||||
services.push(svc);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -14,10 +14,12 @@ class KubernetesServiceHelper {
|
|||
}
|
||||
|
||||
static findApplicationBoundServices(services, rawApp) {
|
||||
if (!rawApp.spec.template) {
|
||||
// if the app is a naked pod (doesn't have a template), then get the pod labels
|
||||
const appLabels = rawApp.spec.template ? rawApp.spec.template.metadata.labels : rawApp.metadata.labels;
|
||||
if (!appLabels) {
|
||||
return undefined;
|
||||
}
|
||||
return _.filter(services, (item) => item.spec.selector && _.isMatch(rawApp.spec.template.metadata.labels, item.spec.selector));
|
||||
return _.filter(services, (item) => item.spec.selector && _.isMatch(appLabels, item.spec.selector));
|
||||
}
|
||||
}
|
||||
export default KubernetesServiceHelper;
|
||||
|
|
|
@ -9,7 +9,9 @@ import {
|
|||
KubernetesPortainerApplicationNote,
|
||||
} from 'Kubernetes/models/application/models';
|
||||
|
||||
import KubernetesResourceReservationHelper from 'Kubernetes/helpers/resourceReservationHelper';
|
||||
import { KubernetesPod, KubernetesPodToleration, KubernetesPodAffinity, KubernetesPodContainer, KubernetesPodContainerTypes, KubernetesPodEviction } from 'Kubernetes/pod/models';
|
||||
import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
|
||||
import { createPayloadFactory } from './payloads/create';
|
||||
|
||||
function computeStatus(statuses) {
|
||||
|
@ -100,6 +102,32 @@ function computeContainers(data) {
|
|||
}
|
||||
|
||||
export default class KubernetesPodConverter {
|
||||
static applicationFormValuesToPod(formValues, volumeClaims) {
|
||||
let serviceSelector = {};
|
||||
if (formValues.Services.length) {
|
||||
serviceSelector = formValues.Services[0].Selector || { app: formValues.Name };
|
||||
}
|
||||
|
||||
const res = new KubernetesPod();
|
||||
res.Namespace = formValues.ResourcePool.Namespace.Name;
|
||||
res.Name = formValues.Name;
|
||||
res.StackName = formValues.StackName ? formValues.StackName : formValues.Name;
|
||||
res.ApplicationOwner = formValues.ApplicationOwner;
|
||||
res.ApplicationName = formValues.Name;
|
||||
res.ImageModel = formValues.ImageModel;
|
||||
res.CpuLimit = formValues.CpuLimit;
|
||||
res.MemoryLimit = KubernetesResourceReservationHelper.bytesValue(formValues.MemoryLimit);
|
||||
res.Env = KubernetesApplicationHelper.generateEnvFromEnvVariables(formValues.EnvironmentVariables);
|
||||
res.Containers = formValues.Containers;
|
||||
res.ApplicationType = formValues.ApplicationType;
|
||||
res.ServiceSelector = serviceSelector;
|
||||
res.Labels = formValues.Labels;
|
||||
KubernetesApplicationHelper.generateVolumesFromPersistentVolumClaims(res, volumeClaims);
|
||||
KubernetesApplicationHelper.generateEnvOrVolumesFromConfigurations(res, formValues.ConfigMaps, formValues.Secrets);
|
||||
KubernetesApplicationHelper.generateAffinityFromPlacements(res, formValues);
|
||||
return res;
|
||||
}
|
||||
|
||||
static apiToModel(data) {
|
||||
const res = new KubernetesPod();
|
||||
res.Id = data.metadata.uid;
|
||||
|
@ -115,6 +143,7 @@ export default class KubernetesPodConverter {
|
|||
res.Affinity = computeAffinity(data.spec.affinity);
|
||||
res.NodeSelector = data.spec.nodeSelector;
|
||||
res.Tolerations = computeTolerations(data.spec.tolerations);
|
||||
res.Labels = data.metadata.labels;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -140,6 +169,7 @@ function createPayload(pod) {
|
|||
payload.metadata.labels[KubernetesPortainerApplicationStackNameLabel] = pod.StackName;
|
||||
payload.metadata.labels[KubernetesPortainerApplicationNameLabel] = pod.ApplicationName;
|
||||
payload.metadata.labels[KubernetesPortainerApplicationOwnerLabel] = pod.ApplicationOwner;
|
||||
payload.metadata.labels = { ...(pod.Labels || {}), ...(pod.ServiceSelector || {}), ...payload.metadata.labels };
|
||||
if (pod.Note) {
|
||||
payload.metadata.annotations[KubernetesPortainerApplicationNote] = pod.Note;
|
||||
} else {
|
||||
|
|
|
@ -199,13 +199,6 @@ class KubernetesCreateApplicationController {
|
|||
this.updateApplicationType();
|
||||
}
|
||||
|
||||
updateApplicationType() {
|
||||
return this.$scope.$evalAsync(() => {
|
||||
this.formValues.ApplicationType = this.getAppType();
|
||||
this.validatePersistedFolders();
|
||||
});
|
||||
}
|
||||
|
||||
getAppType() {
|
||||
if (this.formValues.DeploymentType === this.ApplicationDeploymentTypes.Global) {
|
||||
return this.ApplicationTypes.DaemonSet;
|
||||
|
@ -216,6 +209,14 @@ class KubernetesCreateApplicationController {
|
|||
return this.ApplicationTypes.Deployment;
|
||||
}
|
||||
|
||||
// keep the application type up to date
|
||||
updateApplicationType() {
|
||||
return this.$scope.$evalAsync(() => {
|
||||
this.formValues.ApplicationType = this.getAppType();
|
||||
this.validatePersistedFolders();
|
||||
});
|
||||
}
|
||||
|
||||
onChangeFileContent(value) {
|
||||
this.$scope.$evalAsync(() => {
|
||||
if (this.oldStackFileContent.replace(/(\r\n|\n|\r)/gm, '') !== value.replace(/(\r\n|\n|\r)/gm, '')) {
|
||||
|
@ -273,7 +274,9 @@ class KubernetesCreateApplicationController {
|
|||
}
|
||||
|
||||
imageValidityIsValid() {
|
||||
return this.state.pullImageValidity || (this.formValues.registryDetails && this.formValues.registryDetails.Registry.Type !== RegistryTypes.DOCKERHUB);
|
||||
return (
|
||||
this.isExternalApplication() || this.state.pullImageValidity || (this.formValues.registryDetails && this.formValues.registryDetails.Registry.Type !== RegistryTypes.DOCKERHUB)
|
||||
);
|
||||
}
|
||||
|
||||
onChangeAppName(appName) {
|
||||
|
@ -349,6 +352,7 @@ class KubernetesCreateApplicationController {
|
|||
persistedFolder.useNewVolume = true;
|
||||
});
|
||||
this.validatePersistedFolders();
|
||||
this.updateApplicationType();
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
|
|
Loading…
Reference in New Issue