From 942d224f2d003ff2c56e5b56c9db6c8f762d172c Mon Sep 17 00:00:00 2001 From: Evgeny Date: Tue, 21 Jun 2022 18:40:29 +0300 Subject: [PATCH 1/4] Add support named volumes --- internal/clients/kuber/deployment_utils.go | 113 +++++++++++++----- .../clients/kuber/persistent_volume_claim.go | 71 +++++++++++ internal/domain_logic/containers.go | 21 ++++ internal/domain_logic/deployment.go | 5 + .../resource_name_utils.go | 15 ++- internal/types/domain/container_list.go | 29 +++++ internal/types/domain/container_volume.go | 28 +++++ 7 files changed, 254 insertions(+), 28 deletions(-) create mode 100644 internal/clients/kuber/persistent_volume_claim.go create mode 100644 internal/types/domain/container_volume.go diff --git a/internal/clients/kuber/deployment_utils.go b/internal/clients/kuber/deployment_utils.go index d0dc53f8..cbf8e48c 100644 --- a/internal/clients/kuber/deployment_utils.go +++ b/internal/clients/kuber/deployment_utils.go @@ -53,6 +53,59 @@ func prepareContainerSecrets(container domainTypes.Container, secret *corev1.Sec func prepareDeploymentVolumes(containerList domainTypes.ContainerList) []corev1.Volume { volumes := []corev1.Volume{} + configFileVolumes := prepareDeploymentConfigFileVolumes(containerList) + volumes = append(volumes, configFileVolumes...) + pvcVolumes := prepareDeploymentPvcVolumes(containerList) + volumes = append(volumes, pvcVolumes...) + + return volumes +} + +func prepareContainerVolumeMounts(container domainTypes.Container) []corev1.VolumeMount { + volumeMounts := []corev1.VolumeMount{} + configVolumeMounts := prepareContainerConfigFileVolumeMounts(container) + volumeMounts = append(volumeMounts, configVolumeMounts...) + namedVolumeMounts := prepareContainerNamedVolumeMounts(container) + volumeMounts = append(volumeMounts, namedVolumeMounts...) + + return volumeMounts +} + +func prepareConfigFileMountPath(containerConfigFile *domainTypes.ContainerConfigFile) (string, string) { + mountPath := containerConfigFile.MountPath + + if len(mountPath) == 0 { + mountPath = "/" + } + + if mountPath[0] != '/' { + mountPath = fmt.Sprintf("/%v", containerConfigFile.MountPath) + } + + if mountPath == "/" { + mountPath = fmt.Sprintf("/%v", containerConfigFile.ConfigFile.Filename) + } + + mountPathParts := strings.Split(mountPath, "/") + mountFileName := mountPathParts[len(mountPathParts)-1] + + return mountPath, mountFileName +} + +func prepareCredentialsDeployment(credentials []domainTypes.Credential) []corev1.LocalObjectReference { + references := []corev1.LocalObjectReference{} + + for _, credential := range credentials { + credentialName := global.Settings.ResourceName.Credential(credential.ID) + + references = append(references, corev1.LocalObjectReference{Name: credentialName}) + } + + return references +} + +func prepareDeploymentConfigFileVolumes(containerList domainTypes.ContainerList) []corev1.Volume { + volumes := []corev1.Volume{} for _, container := range containerList.Items { for _, containerConfigFile := range container.ContainerConfigFiles { @@ -93,7 +146,26 @@ func prepareDeploymentVolumes(containerList domainTypes.ContainerList) []corev1. return volumes } -func prepareContainerVolumeMounts(container domainTypes.Container) []corev1.VolumeMount { +func prepareDeploymentPvcVolumes(containerList domainTypes.ContainerList) []corev1.Volume { + volumes := []corev1.Volume{} + + for _, namedVolume := range containerList.GetUniqNamedVolumes() { + volume := corev1.Volume{ + Name: global.Settings.ResourceName.VolumeName(namedVolume.Source), + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: global.Settings.ResourceName.PvcName(namedVolume.Source), + }, + }, + } + + volumes = append(volumes, volume) + } + + return volumes +} + +func prepareContainerConfigFileVolumeMounts(container domainTypes.Container) []corev1.VolumeMount { volumeMounts := []corev1.VolumeMount{} for _, containerConfigFile := range container.ContainerConfigFiles { @@ -116,37 +188,24 @@ func prepareContainerVolumeMounts(container domainTypes.Container) []corev1.Volu return volumeMounts } -func prepareConfigFileMountPath(containerConfigFile *domainTypes.ContainerConfigFile) (string, string) { - mountPath := containerConfigFile.MountPath - - if len(mountPath) == 0 { - mountPath = "/" - } - - if mountPath[0] != '/' { - mountPath = fmt.Sprintf("/%v", containerConfigFile.MountPath) - } - - if mountPath == "/" { - mountPath = fmt.Sprintf("/%v", containerConfigFile.ConfigFile.Filename) - } - - mountPathParts := strings.Split(mountPath, "/") - mountFileName := mountPathParts[len(mountPathParts)-1] - - return mountPath, mountFileName -} +func prepareContainerNamedVolumeMounts(container domainTypes.Container) []corev1.VolumeMount { + volumeMounts := []corev1.VolumeMount{} -func prepareCredentialsDeployment(credentials []domainTypes.Credential) []corev1.LocalObjectReference { - references := []corev1.LocalObjectReference{} + for _, containerVolume := range container.ContainerVolumes { + if containerVolume.Type != domainTypes.ContainerVolumeTypeNamed { + continue + } - for _, credential := range credentials { - credentialName := global.Settings.ResourceName.Credential(credential.ID) + volumeMount := corev1.VolumeMount{ + Name: global.Settings.ResourceName.VolumeName(containerVolume.Source), + MountPath: containerVolume.Target, + ReadOnly: containerVolume.ReadOnly, + } - references = append(references, corev1.LocalObjectReference{Name: credentialName}) + volumeMounts = append(volumeMounts, volumeMount) } - return references + return volumeMounts } func prepareContainerHealthcheck(container domainTypes.Container) *corev1.Probe { diff --git a/internal/clients/kuber/persistent_volume_claim.go b/internal/clients/kuber/persistent_volume_claim.go new file mode 100644 index 00000000..2084180d --- /dev/null +++ b/internal/clients/kuber/persistent_volume_claim.go @@ -0,0 +1,71 @@ +package kuber + +import ( + "gitlab.com/dualbootpartners/idyl/uffizzi_controller/internal/global" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (client Client) FindOrInitializePersistentVolumeClaim( + namespace, name string) (*corev1.PersistentVolumeClaim, error) { + persistentVolumeClaim, err := client.GetPersistentVolumeClaim(namespace, name) + if err != nil && !errors.IsNotFound(err) { + return persistentVolumeClaim, err + } + + if persistentVolumeClaim != nil && len(persistentVolumeClaim.UID) > 0 { + return persistentVolumeClaim, nil + } + + var storageClassName string = "standard" + + persistentVolumeClaimDraft := &corev1.PersistentVolumeClaim{ + Spec: corev1.PersistentVolumeClaimSpec{ + StorageClassName: &storageClassName, + AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("1Gi"), + }, + }, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: map[string]string{ + "app.kubernetes.io/managed-by": global.Settings.ManagedApplication, + }, + }, + } + + return persistentVolumeClaimDraft, nil +} + +func (client *Client) GetPersistentVolumeClaim(namespace, name string) (*corev1.PersistentVolumeClaim, error) { + persistentVolumeClaimClient := client.clientset.CoreV1().PersistentVolumeClaims(namespace) + + persistentVolumeClaim, err := persistentVolumeClaimClient.Get(client.context, name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + return persistentVolumeClaim, nil +} + +func (client *Client) CreatePersistentVolumeClaim( + namespace string, + persistentVolumeClaimDraft *corev1.PersistentVolumeClaim, +) (*corev1.PersistentVolumeClaim, error) { + persistentVolumeClaimClient := client.clientset.CoreV1().PersistentVolumeClaims(namespace) + + persistentVolumeClaim, err := persistentVolumeClaimClient.Create( + client.context, persistentVolumeClaimDraft, metav1.CreateOptions{}) + + if err != nil { + return nil, err + } + + return persistentVolumeClaim, nil +} diff --git a/internal/domain_logic/containers.go b/internal/domain_logic/containers.go index 577103e3..840e9bc5 100644 --- a/internal/domain_logic/containers.go +++ b/internal/domain_logic/containers.go @@ -58,3 +58,24 @@ func (l *Logic) ApplyContainerSecrets(namespace string, containerList domainType return nil } + +func (l *Logic) ApplyContainersNamedVolumes(namespace string, containerList domainTypes.ContainerList) error { + for _, volume := range containerList.GetUniqNamedVolumes() { + pvcName := global.Settings.ResourceName.PvcName(volume.Source) + pvc, err := l.KuberClient.FindOrInitializePersistentVolumeClaim(namespace, pvcName) + + if err != nil { + return err + } + + if len(pvc.UID) == 0 { + _, err = l.KuberClient.CreatePersistentVolumeClaim(namespace, pvc) + } + + if err != nil { + return err + } + } + + return nil +} diff --git a/internal/domain_logic/deployment.go b/internal/domain_logic/deployment.go index 9d459ffd..ccdd5115 100644 --- a/internal/domain_logic/deployment.go +++ b/internal/domain_logic/deployment.go @@ -122,6 +122,11 @@ func (l *Logic) ApplyContainers( return err } + err = l.ApplyContainersNamedVolumes(namespaceName, containerList) + if err != nil { + return err + } + if containerList.IsEmpty() { return l.CleaningNamespaceForEmptyContainers(namespace) } diff --git a/internal/pkg/resource_name_utils/resource_name_utils.go b/internal/pkg/resource_name_utils/resource_name_utils.go index d82cb681..b5878e3b 100644 --- a/internal/pkg/resource_name_utils/resource_name_utils.go +++ b/internal/pkg/resource_name_utils/resource_name_utils.go @@ -1,6 +1,9 @@ package resource_name_utils -import "fmt" +import ( + "fmt" + "strings" +) type ResouceNameUtils struct{} @@ -27,3 +30,13 @@ func (resouceNameUtils *ResouceNameUtils) Deployment(namespace string) string { func (resouceNameUtils *ResouceNameUtils) Policy(namespace string) string { return fmt.Sprintf("app-%v", namespace) } + +func (resouceNameUtils *ResouceNameUtils) PvcName(name string) string { + rfcName := strings.ReplaceAll(name, "_", "-") + return fmt.Sprintf("pvc-%v", rfcName) +} + +func (resouceNameUtils *ResouceNameUtils) VolumeName(name string) string { + rfcName := strings.ReplaceAll(name, "_", "-") + return fmt.Sprintf("volume-%v", rfcName) +} diff --git a/internal/types/domain/container_list.go b/internal/types/domain/container_list.go index 52fec1bb..8b4e28bf 100644 --- a/internal/types/domain/container_list.go +++ b/internal/types/domain/container_list.go @@ -61,3 +61,32 @@ func (list ContainerList) GetUserContainerList() ContainerList { func (list *ContainerList) AddContainer(container Container) { list.Items = append(list.Items, container) } + +func (list ContainerList) GetUniqNamedVolumes() []*ContainerVolume { + volumes := []*ContainerVolume{} + + for _, container := range list.Items { + for _, containerVolume := range container.ContainerVolumes { + if containerVolume.Type != ContainerVolumeTypeNamed { + continue + } + + isVolumeExists := false + + for _, existsVolume := range volumes { + if existsVolume.Source == containerVolume.Source { + isVolumeExists = true + break + } + } + + if isVolumeExists { + continue + } + + volumes = append(volumes, containerVolume) + } + } + + return volumes +} diff --git a/internal/types/domain/container_volume.go b/internal/types/domain/container_volume.go new file mode 100644 index 00000000..f33598c3 --- /dev/null +++ b/internal/types/domain/container_volume.go @@ -0,0 +1,28 @@ +package types + +type ContainerVolume struct { + Source string `json:"source"` + Target string `json:"target"` + Type ContainerVolumeType `json:"type"` + ReadOnly bool `json:"read_only"` +} + +type ContainerVolumeType string + +const ( + ContainerVolumeTypeNamed ContainerVolumeType = "named" + ContainerVolumeTypeAnonymous ContainerVolumeType = "anonymous" + ContainerVolumeTypeHost ContainerVolumeType = "host" +) + +// func (volume ContainerVolume) IsHostTypeContainerVolume() bool { +// return volume.Type == "host" +// } + +// func (volume ContainerVolume) IsNamedTypeContainerVolume() bool { +// return volume.Type == "named" +// } + +// func (volume ContainerVolume) IsAnonymousTypeContainerVolume() bool { +// return volume.Type == "anonymous" +// } From 44a69b706cfe74f25b39e99b14aaff3bd65f5a4a Mon Sep 17 00:00:00 2001 From: Adam Vollrath Date: Wed, 29 Jun 2022 15:36:46 -0500 Subject: [PATCH 2/4] Add file missed from previous commit de4560b8faa3c37ae22c1d8a6394a09b4198dd1e --- internal/types/domain/container.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/types/domain/container.go b/internal/types/domain/container.go index 75aafd95..ec3c53d4 100644 --- a/internal/types/domain/container.go +++ b/internal/types/domain/container.go @@ -26,6 +26,7 @@ type Container struct { MemoryRequest uint `json:"memory_request"` ContainerConfigFiles []*ContainerConfigFile `json:"container_config_files"` Healthcheck *Healthcheck `json:"healthcheck"` + ContainerVolumes []*ContainerVolume `json:"volumes"` } func (c Container) IsPublic() bool { From c0e2ba3b249f173f73068fe093d5701744c014e0 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Fri, 24 Jun 2022 00:53:58 +0300 Subject: [PATCH 3/4] add anonymous volumes --- configs/settings.yml | 1 + internal/clients/kuber/deployment_utils.go | 38 ++++++++++-- .../clients/kuber/persistent_volume_claim.go | 2 +- internal/config/settings/settings.go | 1 + internal/domain_logic/containers.go | 23 +++++++- internal/domain_logic/deployment.go | 5 ++ .../resource_name_utils.go | 14 ++++- internal/types/domain/container.go | 1 + internal/types/domain/container_list.go | 59 +++++++++++++++++-- internal/types/domain/container_volume.go | 31 +++++++--- 10 files changed, 151 insertions(+), 24 deletions(-) diff --git a/configs/settings.yml b/configs/settings.yml index f63074e8..767ce967 100644 --- a/configs/settings.yml +++ b/configs/settings.yml @@ -26,6 +26,7 @@ base: &base cert_manager_cluster_issuer: ${CERT_MANAGER_CLUSTER_ISSUER:-} sandbox_enabled: ${SANDBOX_ENABLED} ingress_default_port: 443 + pvc_storage_class_name: uffizzi-standard service_checks: ip_ping_timeout: 1800s availability_timeout: 1800s diff --git a/internal/clients/kuber/deployment_utils.go b/internal/clients/kuber/deployment_utils.go index cbf8e48c..8ddebd21 100644 --- a/internal/clients/kuber/deployment_utils.go +++ b/internal/clients/kuber/deployment_utils.go @@ -65,9 +65,13 @@ func prepareContainerVolumeMounts(container domainTypes.Container) []corev1.Volu volumeMounts := []corev1.VolumeMount{} configVolumeMounts := prepareContainerConfigFileVolumeMounts(container) volumeMounts = append(volumeMounts, configVolumeMounts...) + namedVolumeMounts := prepareContainerNamedVolumeMounts(container) volumeMounts = append(volumeMounts, namedVolumeMounts...) + anonymousVolumeMounts := prepareContainerAnonymousVolumeMounts(container) + volumeMounts = append(volumeMounts, anonymousVolumeMounts...) + return volumeMounts } @@ -148,13 +152,15 @@ func prepareDeploymentConfigFileVolumes(containerList domainTypes.ContainerList) func prepareDeploymentPvcVolumes(containerList domainTypes.ContainerList) []corev1.Volume { volumes := []corev1.Volume{} + pvcVolumes := containerList.GetUniqNamedVolumes() + pvcVolumes = append(pvcVolumes, containerList.GetUniqAnonymousVolumes()...) - for _, namedVolume := range containerList.GetUniqNamedVolumes() { + for _, pvcVolume := range pvcVolumes { volume := corev1.Volume{ - Name: global.Settings.ResourceName.VolumeName(namedVolume.Source), + Name: global.Settings.ResourceName.VolumeName(pvcVolume.UniqName), VolumeSource: corev1.VolumeSource{ PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: global.Settings.ResourceName.PvcName(namedVolume.Source), + ClaimName: global.Settings.ResourceName.PvcName(pvcVolume.UniqName), }, }, } @@ -192,12 +198,13 @@ func prepareContainerNamedVolumeMounts(container domainTypes.Container) []corev1 volumeMounts := []corev1.VolumeMount{} for _, containerVolume := range container.ContainerVolumes { - if containerVolume.Type != domainTypes.ContainerVolumeTypeNamed { + if !containerVolume.IsNamedType() { continue } + name := containerVolume.BuildUniqName(&container) volumeMount := corev1.VolumeMount{ - Name: global.Settings.ResourceName.VolumeName(containerVolume.Source), + Name: global.Settings.ResourceName.VolumeName(name), MountPath: containerVolume.Target, ReadOnly: containerVolume.ReadOnly, } @@ -208,6 +215,27 @@ func prepareContainerNamedVolumeMounts(container domainTypes.Container) []corev1 return volumeMounts } +func prepareContainerAnonymousVolumeMounts(container domainTypes.Container) []corev1.VolumeMount { + volumeMounts := []corev1.VolumeMount{} + + for _, containerVolume := range container.ContainerVolumes { + if !containerVolume.IsAnonymousType() { + continue + } + + name := containerVolume.BuildUniqName(&container) + volumeMount := corev1.VolumeMount{ + Name: global.Settings.ResourceName.VolumeName(name), + MountPath: containerVolume.Source, + ReadOnly: containerVolume.ReadOnly, + } + + volumeMounts = append(volumeMounts, volumeMount) + } + + return volumeMounts +} + func prepareContainerHealthcheck(container domainTypes.Container) *corev1.Probe { if container.Healthcheck == nil { return nil diff --git a/internal/clients/kuber/persistent_volume_claim.go b/internal/clients/kuber/persistent_volume_claim.go index 2084180d..6fa98b31 100644 --- a/internal/clients/kuber/persistent_volume_claim.go +++ b/internal/clients/kuber/persistent_volume_claim.go @@ -19,7 +19,7 @@ func (client Client) FindOrInitializePersistentVolumeClaim( return persistentVolumeClaim, nil } - var storageClassName string = "standard" + var storageClassName string = global.Settings.PvcStorageClassName persistentVolumeClaimDraft := &corev1.PersistentVolumeClaim{ Spec: corev1.PersistentVolumeClaimSpec{ diff --git a/internal/config/settings/settings.go b/internal/config/settings/settings.go index f38264c0..ce1961de 100644 --- a/internal/config/settings/settings.go +++ b/internal/config/settings/settings.go @@ -36,6 +36,7 @@ type Settings struct { CertManagerClusterIssuer string `yaml:"cert_manager_cluster_issuer"` EphemeralStorageCoefficient float64 `yaml:"ephemeral_storage_coefficient"` IngressDefaultPort int `yaml:"ingress_default_port"` + PvcStorageClassName string `yaml:"pvc_storage_class_name"` } type ServiceChecksSettings struct { diff --git a/internal/domain_logic/containers.go b/internal/domain_logic/containers.go index 840e9bc5..e5c346f3 100644 --- a/internal/domain_logic/containers.go +++ b/internal/domain_logic/containers.go @@ -61,7 +61,28 @@ func (l *Logic) ApplyContainerSecrets(namespace string, containerList domainType func (l *Logic) ApplyContainersNamedVolumes(namespace string, containerList domainTypes.ContainerList) error { for _, volume := range containerList.GetUniqNamedVolumes() { - pvcName := global.Settings.ResourceName.PvcName(volume.Source) + pvcName := global.Settings.ResourceName.PvcName(volume.UniqName) + pvc, err := l.KuberClient.FindOrInitializePersistentVolumeClaim(namespace, pvcName) + + if err != nil { + return err + } + + if len(pvc.UID) == 0 { + _, err = l.KuberClient.CreatePersistentVolumeClaim(namespace, pvc) + } + + if err != nil { + return err + } + } + + return nil +} + +func (l *Logic) ApplyContainersAnonymousVolumes(namespace string, containerList domainTypes.ContainerList) error { + for _, volume := range containerList.GetUniqAnonymousVolumes() { + pvcName := global.Settings.ResourceName.PvcName(volume.UniqName) pvc, err := l.KuberClient.FindOrInitializePersistentVolumeClaim(namespace, pvcName) if err != nil { diff --git a/internal/domain_logic/deployment.go b/internal/domain_logic/deployment.go index ccdd5115..ed1edc36 100644 --- a/internal/domain_logic/deployment.go +++ b/internal/domain_logic/deployment.go @@ -127,6 +127,11 @@ func (l *Logic) ApplyContainers( return err } + err = l.ApplyContainersAnonymousVolumes(namespaceName, containerList) + if err != nil { + return err + } + if containerList.IsEmpty() { return l.CleaningNamespaceForEmptyContainers(namespace) } diff --git a/internal/pkg/resource_name_utils/resource_name_utils.go b/internal/pkg/resource_name_utils/resource_name_utils.go index b5878e3b..cd263428 100644 --- a/internal/pkg/resource_name_utils/resource_name_utils.go +++ b/internal/pkg/resource_name_utils/resource_name_utils.go @@ -2,7 +2,7 @@ package resource_name_utils import ( "fmt" - "strings" + "regexp" ) type ResouceNameUtils struct{} @@ -32,11 +32,19 @@ func (resouceNameUtils *ResouceNameUtils) Policy(namespace string) string { } func (resouceNameUtils *ResouceNameUtils) PvcName(name string) string { - rfcName := strings.ReplaceAll(name, "_", "-") + rfcName := toRfc(name) + return fmt.Sprintf("pvc-%v", rfcName) } func (resouceNameUtils *ResouceNameUtils) VolumeName(name string) string { - rfcName := strings.ReplaceAll(name, "_", "-") + rfcName := toRfc(name) + return fmt.Sprintf("volume-%v", rfcName) } + +func toRfc(str string) string { + regexp := regexp.MustCompile(`(\/|~|\.|_)`) + + return regexp.ReplaceAllString(str, "-") +} diff --git a/internal/types/domain/container.go b/internal/types/domain/container.go index ec3c53d4..fc6ecd8b 100644 --- a/internal/types/domain/container.go +++ b/internal/types/domain/container.go @@ -27,6 +27,7 @@ type Container struct { ContainerConfigFiles []*ContainerConfigFile `json:"container_config_files"` Healthcheck *Healthcheck `json:"healthcheck"` ContainerVolumes []*ContainerVolume `json:"volumes"` + ServiceName string `json:"service_name"` } func (c Container) IsPublic() bool { diff --git a/internal/types/domain/container_list.go b/internal/types/domain/container_list.go index 8b4e28bf..3a734794 100644 --- a/internal/types/domain/container_list.go +++ b/internal/types/domain/container_list.go @@ -6,6 +6,12 @@ type ContainerList struct { Items []Container `json:"items"` } +type DeploymentVolume struct { + Volume *ContainerVolume + Container *Container + UniqName string +} + func (list ContainerList) IsEmpty() bool { return list.Count() == 0 } @@ -62,19 +68,56 @@ func (list *ContainerList) AddContainer(container Container) { list.Items = append(list.Items, container) } -func (list ContainerList) GetUniqNamedVolumes() []*ContainerVolume { - volumes := []*ContainerVolume{} +func (list ContainerList) GetUniqNamedVolumes() []DeploymentVolume { + volumes := []DeploymentVolume{} - for _, container := range list.Items { + for i, container := range list.Items { for _, containerVolume := range container.ContainerVolumes { if containerVolume.Type != ContainerVolumeTypeNamed { continue } isVolumeExists := false + uniqName := containerVolume.BuildUniqName(&list.Items[i]) + + for _, existsVolume := range volumes { + if existsVolume.UniqName == uniqName { + isVolumeExists = true + break + } + } + + if isVolumeExists { + continue + } + + volume := DeploymentVolume{ + Volume: containerVolume, + Container: &list.Items[i], + UniqName: uniqName, + } + + volumes = append(volumes, volume) + } + } + + return volumes +} + +func (list ContainerList) GetUniqAnonymousVolumes() []DeploymentVolume { + volumes := []DeploymentVolume{} + + for i, container := range list.Items { + for _, containerVolume := range container.ContainerVolumes { + if containerVolume.Type != ContainerVolumeTypeAnonymous { + continue + } + + isVolumeExists := false + uniqName := containerVolume.BuildUniqName(&list.Items[i]) for _, existsVolume := range volumes { - if existsVolume.Source == containerVolume.Source { + if existsVolume.UniqName == uniqName { isVolumeExists = true break } @@ -84,7 +127,13 @@ func (list ContainerList) GetUniqNamedVolumes() []*ContainerVolume { continue } - volumes = append(volumes, containerVolume) + volume := DeploymentVolume{ + Volume: containerVolume, + Container: &list.Items[i], + UniqName: uniqName, + } + + volumes = append(volumes, volume) } } diff --git a/internal/types/domain/container_volume.go b/internal/types/domain/container_volume.go index f33598c3..1fedce41 100644 --- a/internal/types/domain/container_volume.go +++ b/internal/types/domain/container_volume.go @@ -1,5 +1,7 @@ package types +import "fmt" + type ContainerVolume struct { Source string `json:"source"` Target string `json:"target"` @@ -15,14 +17,25 @@ const ( ContainerVolumeTypeHost ContainerVolumeType = "host" ) -// func (volume ContainerVolume) IsHostTypeContainerVolume() bool { -// return volume.Type == "host" -// } +func (volume ContainerVolume) BuildUniqName(container *Container) string { + switch volume.Type { + case ContainerVolumeTypeAnonymous: + return fmt.Sprintf("%s-%s", container.ServiceName, volume.Source) + case ContainerVolumeTypeNamed: + return volume.Source + default: + return "" + } +} + +func (volume ContainerVolume) IsHostType() bool { + return volume.Type == ContainerVolumeTypeHost +} -// func (volume ContainerVolume) IsNamedTypeContainerVolume() bool { -// return volume.Type == "named" -// } +func (volume ContainerVolume) IsNamedType() bool { + return volume.Type == ContainerVolumeTypeNamed +} -// func (volume ContainerVolume) IsAnonymousTypeContainerVolume() bool { -// return volume.Type == "anonymous" -// } +func (volume ContainerVolume) IsAnonymousType() bool { + return volume.Type == ContainerVolumeTypeAnonymous +} From 2ec5a56ac63fa86ff1df952e3469fbb88f0e27c8 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Fri, 24 Jun 2022 16:42:02 +0300 Subject: [PATCH 4/4] add remove unused pvc --- .../clients/kuber/persistent_volume_claim.go | 26 +++++++++++++++ internal/domain_logic/containers.go | 32 +++++++++++++++++++ internal/domain_logic/deployment.go | 5 +++ 3 files changed, 63 insertions(+) diff --git a/internal/clients/kuber/persistent_volume_claim.go b/internal/clients/kuber/persistent_volume_claim.go index 6fa98b31..bd313e1c 100644 --- a/internal/clients/kuber/persistent_volume_claim.go +++ b/internal/clients/kuber/persistent_volume_claim.go @@ -1,6 +1,8 @@ package kuber import ( + "fmt" + "gitlab.com/dualbootpartners/idyl/uffizzi_controller/internal/global" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -54,6 +56,19 @@ func (client *Client) GetPersistentVolumeClaim(namespace, name string) (*corev1. return persistentVolumeClaim, nil } +func (client *Client) GetPersistentVolumeClaims(namespace string) ([]corev1.PersistentVolumeClaim, error) { + persistentVolumeClaimClient := client.clientset.CoreV1().PersistentVolumeClaims(namespace) + + persistentVolumeClaimList, err := persistentVolumeClaimClient.List(client.context, metav1.ListOptions{ + LabelSelector: fmt.Sprintf("app.kubernetes.io/managed-by=%v", global.Settings.ManagedApplication), + }) + if err != nil { + return nil, err + } + + return persistentVolumeClaimList.Items, nil +} + func (client *Client) CreatePersistentVolumeClaim( namespace string, persistentVolumeClaimDraft *corev1.PersistentVolumeClaim, @@ -69,3 +84,14 @@ func (client *Client) CreatePersistentVolumeClaim( return persistentVolumeClaim, nil } + +func (client *Client) DeletePersistentVolumeClaim(namespace, name string) error { + persistentVolumeClaimClient := client.clientset.CoreV1().PersistentVolumeClaims(namespace) + + err := persistentVolumeClaimClient.Delete(client.context, name, metav1.DeleteOptions{}) + if err != nil { + return nil + } + + return nil +} diff --git a/internal/domain_logic/containers.go b/internal/domain_logic/containers.go index e5c346f3..e174904f 100644 --- a/internal/domain_logic/containers.go +++ b/internal/domain_logic/containers.go @@ -1,9 +1,11 @@ package domain import ( + "log" "time" "gitlab.com/dualbootpartners/idyl/uffizzi_controller/internal/global" + "gitlab.com/dualbootpartners/idyl/uffizzi_controller/internal/pkg/string_utils" domainTypes "gitlab.com/dualbootpartners/idyl/uffizzi_controller/internal/types/domain" ) @@ -100,3 +102,33 @@ func (l *Logic) ApplyContainersAnonymousVolumes(namespace string, containerList return nil } + +func (l *Logic) RemoveUnusedContainersVolumes(namespace string, containerList domainTypes.ContainerList) error { + uniqVolumes := containerList.GetUniqNamedVolumes() + uniqVolumes = append(uniqVolumes, containerList.GetUniqAnonymousVolumes()...) + newPersistentVolumeClaimNames := []string{} + + for _, volume := range uniqVolumes { + newPersistentVolumeClaimNames = append(newPersistentVolumeClaimNames, global.Settings.ResourceName.PvcName(volume.UniqName)) + } + + existsPersistentVolumeClaims, err := l.KuberClient.GetPersistentVolumeClaims(namespace) + if err != nil { + return err + } + + for _, existsPersistentVolumeClaim := range existsPersistentVolumeClaims { + if string_utils.Contains(newPersistentVolumeClaimNames, existsPersistentVolumeClaim.Name) { + continue + } + + err := l.KuberClient.DeletePersistentVolumeClaim(namespace, existsPersistentVolumeClaim.Name) + if err != nil { + return nil + } + + log.Printf("%v/pvc %v was deleted\n", namespace, existsPersistentVolumeClaim.Name) + } + + return nil +} diff --git a/internal/domain_logic/deployment.go b/internal/domain_logic/deployment.go index ed1edc36..466f80f5 100644 --- a/internal/domain_logic/deployment.go +++ b/internal/domain_logic/deployment.go @@ -117,6 +117,11 @@ func (l *Logic) ApplyContainers( return err } + err = l.RemoveUnusedContainersVolumes(namespaceName, containerList) + if err != nil { + return err + } + err = l.ApplyContainerSecrets(namespaceName, containerList) if err != nil { return err