mirror of https://github.com/portainer/portainer
fix(volumes): update the external, unused badges and used by col [r8s-105] (#12302)
parent
44d6c0885e
commit
469a4e94c2
|
@ -214,6 +214,7 @@ func (kcl *KubeClient) CombineVolumesWithApplications(volumes *[]models.K8sVolum
|
||||||
|
|
||||||
hasReplicaSetOwnerReference := containsReplicaSetOwnerReference(pods)
|
hasReplicaSetOwnerReference := containsReplicaSetOwnerReference(pods)
|
||||||
replicaSetItems := make([]appsv1.ReplicaSet, 0)
|
replicaSetItems := make([]appsv1.ReplicaSet, 0)
|
||||||
|
deploymentItems := make([]appsv1.Deployment, 0)
|
||||||
if hasReplicaSetOwnerReference {
|
if hasReplicaSetOwnerReference {
|
||||||
replicaSets, err := kcl.cli.AppsV1().ReplicaSets("").List(context.Background(), metav1.ListOptions{})
|
replicaSets, err := kcl.cli.AppsV1().ReplicaSets("").List(context.Background(), metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -221,19 +222,48 @@ func (kcl *KubeClient) CombineVolumesWithApplications(volumes *[]models.K8sVolum
|
||||||
return nil, fmt.Errorf("an error occurred during the CombineVolumesWithApplications operation, unable to list replica sets across the cluster. Error: %w", err)
|
return nil, fmt.Errorf("an error occurred during the CombineVolumesWithApplications operation, unable to list replica sets across the cluster. Error: %w", err)
|
||||||
}
|
}
|
||||||
replicaSetItems = replicaSets.Items
|
replicaSetItems = replicaSets.Items
|
||||||
|
|
||||||
|
deployments, err := kcl.cli.AppsV1().Deployments("").List(context.Background(), metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to list deployments across the cluster")
|
||||||
|
return nil, fmt.Errorf("an error occurred during the CombineVolumesWithApplications operation, unable to list deployments across the cluster. Error: %w", err)
|
||||||
|
}
|
||||||
|
deploymentItems = deployments.Items
|
||||||
}
|
}
|
||||||
|
|
||||||
return kcl.updateVolumesWithOwningApplications(volumes, pods, replicaSetItems)
|
hasStatefulSetOwnerReference := containsStatefulSetOwnerReference(pods)
|
||||||
|
statefulSetItems := make([]appsv1.StatefulSet, 0)
|
||||||
|
if hasStatefulSetOwnerReference {
|
||||||
|
statefulSets, err := kcl.cli.AppsV1().StatefulSets("").List(context.Background(), metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to list stateful sets across the cluster")
|
||||||
|
return nil, fmt.Errorf("an error occurred during the CombineVolumesWithApplications operation, unable to list stateful sets across the cluster. Error: %w", err)
|
||||||
|
}
|
||||||
|
statefulSetItems = statefulSets.Items
|
||||||
|
}
|
||||||
|
|
||||||
|
hasDaemonSetOwnerReference := containsDaemonSetOwnerReference(pods)
|
||||||
|
daemonSetItems := make([]appsv1.DaemonSet, 0)
|
||||||
|
if hasDaemonSetOwnerReference {
|
||||||
|
daemonSets, err := kcl.cli.AppsV1().DaemonSets("").List(context.Background(), metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to list daemon sets across the cluster")
|
||||||
|
return nil, fmt.Errorf("an error occurred during the CombineVolumesWithApplications operation, unable to list daemon sets across the cluster. Error: %w", err)
|
||||||
|
}
|
||||||
|
daemonSetItems = daemonSets.Items
|
||||||
|
}
|
||||||
|
|
||||||
|
return kcl.updateVolumesWithOwningApplications(volumes, pods, deploymentItems, replicaSetItems, statefulSetItems, daemonSetItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateVolumesWithOwningApplications updates the volumes with the applications that use them.
|
// updateVolumesWithOwningApplications updates the volumes with the applications that use them.
|
||||||
func (kcl *KubeClient) updateVolumesWithOwningApplications(volumes *[]models.K8sVolumeInfo, pods *corev1.PodList, replicaSetItems []appsv1.ReplicaSet) (*[]models.K8sVolumeInfo, error) {
|
func (kcl *KubeClient) updateVolumesWithOwningApplications(volumes *[]models.K8sVolumeInfo, pods *corev1.PodList, deploymentItems []appsv1.Deployment, replicaSetItems []appsv1.ReplicaSet, statefulSetItems []appsv1.StatefulSet, daemonSetItems []appsv1.DaemonSet) (*[]models.K8sVolumeInfo, error) {
|
||||||
for i, volume := range *volumes {
|
for i, volume := range *volumes {
|
||||||
for _, pod := range pods.Items {
|
for _, pod := range pods.Items {
|
||||||
if pod.Spec.Volumes != nil {
|
if pod.Spec.Volumes != nil {
|
||||||
for _, podVolume := range pod.Spec.Volumes {
|
for _, podVolume := range pod.Spec.Volumes {
|
||||||
if podVolume.PersistentVolumeClaim != nil && podVolume.PersistentVolumeClaim.ClaimName == volume.PersistentVolumeClaim.Name && pod.Namespace == volume.PersistentVolumeClaim.Namespace {
|
if podVolume.VolumeSource.PersistentVolumeClaim != nil && podVolume.VolumeSource.PersistentVolumeClaim.ClaimName == volume.PersistentVolumeClaim.Name && pod.Namespace == volume.PersistentVolumeClaim.Namespace {
|
||||||
application, err := kcl.ConvertPodToApplication(pod, replicaSetItems, []appsv1.Deployment{}, []appsv1.StatefulSet{}, []appsv1.DaemonSet{}, []corev1.Service{}, false)
|
application, err := kcl.ConvertPodToApplication(pod, replicaSetItems, deploymentItems, statefulSetItems, daemonSetItems, []corev1.Service{}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to convert pod to application")
|
log.Error().Err(err).Msg("Failed to convert pod to application")
|
||||||
return nil, fmt.Errorf("an error occurred during the CombineServicesWithApplications operation, unable to convert pod to application. Error: %w", err)
|
return nil, fmt.Errorf("an error occurred during the CombineServicesWithApplications operation, unable to convert pod to application. Error: %w", err)
|
||||||
|
|
|
@ -21,7 +21,7 @@ import {
|
||||||
KubernetesApplicationVolumePersistentPayload,
|
KubernetesApplicationVolumePersistentPayload,
|
||||||
KubernetesApplicationVolumeSecretPayload,
|
KubernetesApplicationVolumeSecretPayload,
|
||||||
} from 'Kubernetes/models/application/payloads';
|
} from 'Kubernetes/models/application/payloads';
|
||||||
import KubernetesVolumeHelper from 'Kubernetes/helpers/volumeHelper';
|
import { generatedApplicationConfigVolumeName } from '@/react/kubernetes/volumes/utils';
|
||||||
import { HelmApplication } from 'Kubernetes/models/application/models';
|
import { HelmApplication } from 'Kubernetes/models/application/models';
|
||||||
import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants';
|
import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants';
|
||||||
import { KubernetesPodAffinity, KubernetesPodNodeAffinityNodeSelectorRequirementOperators } from 'Kubernetes/pod/models';
|
import { KubernetesPodAffinity, KubernetesPodNodeAffinityNodeSelectorRequirementOperators } from 'Kubernetes/pod/models';
|
||||||
|
@ -239,7 +239,7 @@ class KubernetesApplicationHelper {
|
||||||
const volKeys = _.filter(config.overridenKeys, (item) => item.type === 'FILESYSTEM');
|
const volKeys = _.filter(config.overridenKeys, (item) => item.type === 'FILESYSTEM');
|
||||||
const groupedVolKeys = _.groupBy(volKeys, 'path');
|
const groupedVolKeys = _.groupBy(volKeys, 'path');
|
||||||
_.forEach(groupedVolKeys, (items, path) => {
|
_.forEach(groupedVolKeys, (items, path) => {
|
||||||
const volumeName = KubernetesVolumeHelper.generatedApplicationConfigVolumeName(app.Name);
|
const volumeName = generatedApplicationConfigVolumeName(app.Name);
|
||||||
const configurationName = config.selectedConfiguration.metadata.name;
|
const configurationName = config.selectedConfiguration.metadata.name;
|
||||||
const itemsMap = _.map(items, (item) => {
|
const itemsMap = _.map(items, (item) => {
|
||||||
const entry = new KubernetesApplicationVolumeEntryPayload();
|
const entry = new KubernetesApplicationVolumeEntryPayload();
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import _ from 'lodash-es';
|
import _ from 'lodash-es';
|
||||||
import uuidv4 from 'uuid/v4';
|
|
||||||
import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants';
|
import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants';
|
||||||
|
|
||||||
class KubernetesVolumeHelper {
|
class KubernetesVolumeHelper {
|
||||||
|
@ -20,18 +19,6 @@ class KubernetesVolumeHelper {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static isUsed(item) {
|
|
||||||
return item.Applications.length !== 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static generatedApplicationConfigVolumeName(name) {
|
|
||||||
return 'config-' + name + '-' + uuidv4();
|
|
||||||
}
|
|
||||||
|
|
||||||
static isExternalVolume(volume) {
|
|
||||||
return !volume.PersistentVolumeClaim.ApplicationOwner;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default KubernetesVolumeHelper;
|
export default KubernetesVolumeHelper;
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { confirm, confirmUpdate, confirmWebEditorDiscard } from '@@/modals/confi
|
||||||
import { buildConfirmButton } from '@@/modals/utils';
|
import { buildConfirmButton } from '@@/modals/utils';
|
||||||
import { ModalType } from '@@/modals';
|
import { ModalType } from '@@/modals';
|
||||||
import { KUBE_STACK_NAME_VALIDATION_REGEX } from '@/react/kubernetes/DeployView/StackName/constants';
|
import { KUBE_STACK_NAME_VALIDATION_REGEX } from '@/react/kubernetes/DeployView/StackName/constants';
|
||||||
|
import { isVolumeUsed } from '@/react/kubernetes/volumes/utils';
|
||||||
|
|
||||||
class KubernetesCreateApplicationController {
|
class KubernetesCreateApplicationController {
|
||||||
/* #region CONSTRUCTOR */
|
/* #region CONSTRUCTOR */
|
||||||
|
@ -785,7 +786,7 @@ class KubernetesCreateApplicationController {
|
||||||
});
|
});
|
||||||
this.volumes = volumes;
|
this.volumes = volumes;
|
||||||
const filteredVolumes = _.filter(this.volumes, (volume) => {
|
const filteredVolumes = _.filter(this.volumes, (volume) => {
|
||||||
const isUnused = !KubernetesVolumeHelper.isUsed(volume);
|
const isUnused = !isVolumeUsed(volume);
|
||||||
const isRWX = volume.PersistentVolumeClaim.storageClass && _.includes(volume.PersistentVolumeClaim.storageClass.AccessModes, 'RWX');
|
const isRWX = volume.PersistentVolumeClaim.storageClass && _.includes(volume.PersistentVolumeClaim.storageClass.AccessModes, 'RWX');
|
||||||
return isUnused || isRWX;
|
return isUnused || isRWX;
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,6 +6,7 @@ import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
|
||||||
import { KubernetesStorageClassAccessPolicies } from 'Kubernetes/models/storage-class/models';
|
import { KubernetesStorageClassAccessPolicies } from 'Kubernetes/models/storage-class/models';
|
||||||
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
||||||
import { confirmRedeploy } from '@/react/kubernetes/volumes/ItemView/ConfirmRedeployModal';
|
import { confirmRedeploy } from '@/react/kubernetes/volumes/ItemView/ConfirmRedeployModal';
|
||||||
|
import { isVolumeUsed, isVolumeExternal } from '@/react/kubernetes/volumes/utils';
|
||||||
|
|
||||||
class KubernetesVolumeController {
|
class KubernetesVolumeController {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
|
@ -49,7 +50,7 @@ class KubernetesVolumeController {
|
||||||
}
|
}
|
||||||
|
|
||||||
isExternalVolume() {
|
isExternalVolume() {
|
||||||
return KubernetesVolumeHelper.isExternalVolume(this.volume);
|
return isVolumeExternal(this.volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
isSystemNamespace() {
|
isSystemNamespace() {
|
||||||
|
@ -57,7 +58,7 @@ class KubernetesVolumeController {
|
||||||
}
|
}
|
||||||
|
|
||||||
isUsed() {
|
isUsed() {
|
||||||
return KubernetesVolumeHelper.isUsed(this.volume);
|
return isVolumeUsed(this.volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeSize() {
|
onChangeSize() {
|
||||||
|
@ -102,7 +103,7 @@ class KubernetesVolumeController {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateVolume() {
|
updateVolume() {
|
||||||
if (KubernetesVolumeHelper.isUsed(this.volume)) {
|
if (isVolumeUsed(this.volume)) {
|
||||||
confirmRedeploy().then((redeploy) => {
|
confirmRedeploy().then((redeploy) => {
|
||||||
return this.$async(this.updateVolumeAsync, redeploy);
|
return this.$async(this.updateVolumeAsync, redeploy);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Database } from 'lucide-react';
|
import { Database } from 'lucide-react';
|
||||||
|
|
||||||
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
|
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
|
||||||
import KubernetesVolumeHelper from '@/kubernetes/helpers/volumeHelper';
|
|
||||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||||
|
|
||||||
import { refreshableSettings } from '@@/datatables/types';
|
import { refreshableSettings } from '@@/datatables/types';
|
||||||
|
@ -20,6 +19,7 @@ import { useNamespacesQuery } from '../../namespaces/queries/useNamespacesQuery'
|
||||||
import { useAllVolumesQuery } from '../queries/useVolumesQuery';
|
import { useAllVolumesQuery } from '../queries/useVolumesQuery';
|
||||||
import { isSystemNamespace } from '../../namespaces/queries/useIsSystemNamespace';
|
import { isSystemNamespace } from '../../namespaces/queries/useIsSystemNamespace';
|
||||||
import { useDeleteVolumes } from '../queries/useDeleteVolumes';
|
import { useDeleteVolumes } from '../queries/useDeleteVolumes';
|
||||||
|
import { isVolumeUsed } from '../utils';
|
||||||
|
|
||||||
import { columns } from './columns';
|
import { columns } from './columns';
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ export function VolumesDatatable() {
|
||||||
disableSelect={!hasWriteAuth}
|
disableSelect={!hasWriteAuth}
|
||||||
isRowSelectable={({ original: volume }) =>
|
isRowSelectable={({ original: volume }) =>
|
||||||
!isSystemNamespace(volume.ResourcePool.Namespace.Name, namespaces) &&
|
!isSystemNamespace(volume.ResourcePool.Namespace.Name, namespaces) &&
|
||||||
!KubernetesVolumeHelper.isUsed(volume)
|
!isVolumeUsed(volume)
|
||||||
}
|
}
|
||||||
renderTableActions={(selectedItems) => (
|
renderTableActions={(selectedItems) => (
|
||||||
<Authorized authorizations="K8sVolumesW">
|
<Authorized authorizations="K8sVolumesW">
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { CellContext } from '@tanstack/react-table';
|
import { CellContext } from '@tanstack/react-table';
|
||||||
|
|
||||||
import KubernetesVolumeHelper from '@/kubernetes/helpers/volumeHelper';
|
|
||||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||||
|
|
||||||
import { Link } from '@@/Link';
|
import { Link } from '@@/Link';
|
||||||
|
@ -9,6 +8,7 @@ import { ExternalBadge } from '@@/Badge/ExternalBadge';
|
||||||
import { UnusedBadge } from '@@/Badge/UnusedBadge';
|
import { UnusedBadge } from '@@/Badge/UnusedBadge';
|
||||||
|
|
||||||
import { useNamespacesQuery } from '../../namespaces/queries/useNamespacesQuery';
|
import { useNamespacesQuery } from '../../namespaces/queries/useNamespacesQuery';
|
||||||
|
import { isVolumeExternal, isVolumeUsed } from '../utils';
|
||||||
|
|
||||||
import { VolumeViewModel } from './types';
|
import { VolumeViewModel } from './types';
|
||||||
import { helper } from './columns.helper';
|
import { helper } from './columns.helper';
|
||||||
|
@ -44,8 +44,8 @@ export function NameCell({
|
||||||
<SystemBadge />
|
<SystemBadge />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{KubernetesVolumeHelper.isExternalVolume(item) && <ExternalBadge />}
|
{isVolumeExternal(item) && <ExternalBadge />}
|
||||||
{!KubernetesVolumeHelper.isUsed(item) && <UnusedBadge />}
|
{!isVolumeUsed(item) && <UnusedBadge />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import uuidv4 from 'uuid/v4';
|
||||||
|
|
||||||
|
import { VolumeViewModel } from './ListView/types';
|
||||||
|
|
||||||
|
export function isVolumeUsed(volume: VolumeViewModel) {
|
||||||
|
return volume.Applications.length !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isVolumeExternal(volume: VolumeViewModel) {
|
||||||
|
return !volume.PersistentVolumeClaim.ApplicationOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generatedApplicationConfigVolumeName(applicationName: string) {
|
||||||
|
return `config-${applicationName}-${uuidv4()}`;
|
||||||
|
}
|
Loading…
Reference in New Issue