Skip to content

Commit

Permalink
Merge pull request #784 from liu-song/volume
Browse files Browse the repository at this point in the history
Find the Orphan Volume in k8s cluster
  • Loading branch information
qmhu authored May 24, 2023
2 parents 9efc321 + 7c3e187 commit 060314e
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 0 deletions.
4 changes: 4 additions & 0 deletions deploy/craned/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ data:
acceptedResources:
- kind: Node
apiVersion: v1
- name: Volumes
acceptedResources:
- kind: PersistentVolume
apiVersion: v1
---
apiVersion: v1
kind: ConfigMap
Expand Down
14 changes: 14 additions & 0 deletions pkg/recommendation/framework/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
"sync"

jsonpatch "github.com/evanphx/json-patch"
Expand Down Expand Up @@ -178,6 +179,19 @@ func RetrieveScale(ctx *RecommendationContext) error {
return nil
}

func RetrieveVolumes(ctx *RecommendationContext) error {
if ctx.Recommendation.Spec.TargetRef.Kind == "PersistentVolume" {
volumes, err := utils.GetOrphanVolumes(ctx.Client)
if len(volumes) == 0 {
return err
}
str := strings.Join(volumes, ",")
ctx.Recommendation.Status.RecommendedValue = str
return err
}
return nil
}

func RetrievePods(ctx *RecommendationContext) error {
if ctx.Recommendation.Spec.TargetRef.Kind == "Node" {
pods, err := utils.GetNodePods(ctx.Client, ctx.Recommendation.Spec.TargetRef.Name)
Expand Down
3 changes: 3 additions & 0 deletions pkg/recommendation/recommender/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ const (

// IdleNodeRecommender name
IdleNodeRecommender string = "IdleNode"

// VolumesRecommender name
VolumesRecommender string = "Volumes"
)
21 changes: 21 additions & 0 deletions pkg/recommendation/recommender/volumes/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package volumes

import (
"github.com/gocrane/crane/pkg/recommendation/framework"
)

// Filter out k8s resources that are not supported by the recommender.
func (vr *VolumesRecommender) Filter(ctx *framework.RecommendationContext) error {
var err error

// filter resource that not match objectIdentity
if err = vr.BaseRecommender.Filter(ctx); err != nil {
return err
}

if err = framework.RetrieveVolumes(ctx); err != nil {
return err
}

return nil
}
10 changes: 10 additions & 0 deletions pkg/recommendation/recommender/volumes/observe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package volumes

import (
"github.com/gocrane/crane/pkg/recommendation/framework"
)

// Observe enhance the observability.
func (s *VolumesRecommender) Observe(ctx *framework.RecommendationContext) error {
return nil
}
22 changes: 22 additions & 0 deletions pkg/recommendation/recommender/volumes/prepare.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package volumes

import (
"github.com/gocrane/crane/pkg/recommendation/framework"
)

// CheckDataProviders in PrePrepare phase, will create data source provider via your recommendation config.
func (rr *VolumesRecommender) CheckDataProviders(ctx *framework.RecommendationContext) error {
if err := rr.BaseRecommender.CheckDataProviders(ctx); err != nil {
return err
}

return nil
}

func (rr *VolumesRecommender) CollectData(ctx *framework.RecommendationContext) error {
return nil
}

func (rr *VolumesRecommender) PostProcessing(ctx *framework.RecommendationContext) error {
return nil
}
20 changes: 20 additions & 0 deletions pkg/recommendation/recommender/volumes/recommend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package volumes

import (
"github.com/gocrane/crane/pkg/recommendation/framework"
)

func (s *VolumesRecommender) PreRecommend(ctx *framework.RecommendationContext) error {
return nil
}

func (s *VolumesRecommender) Recommend(ctx *framework.RecommendationContext) error {
ctx.Recommendation.Status.Action = "Delete"
ctx.Recommendation.Status.Description = "It is an Orphan Volumes"
return nil
}

// Policy add some logic for result of recommend phase.
func (s *VolumesRecommender) Policy(ctx *framework.RecommendationContext) error {
return nil
}
31 changes: 31 additions & 0 deletions pkg/recommendation/recommender/volumes/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package volumes

import (
analysisv1alph1 "github.com/gocrane/api/analysis/v1alpha1"
"github.com/gocrane/crane/pkg/recommendation/config"
"github.com/gocrane/crane/pkg/recommendation/recommender"
"github.com/gocrane/crane/pkg/recommendation/recommender/apis"
"github.com/gocrane/crane/pkg/recommendation/recommender/base"
)

var _ recommender.Recommender = &VolumesRecommender{}

type VolumesRecommender struct {
base.BaseRecommender
}

func init() {
recommender.RegisterRecommenderProvider(recommender.VolumesRecommender, NewVolumesRecommender)
}

func (s *VolumesRecommender) Name() string {
return recommender.VolumesRecommender
}

// NewVolumesRecommender create a new Volumes recommender.
func NewVolumesRecommender(recommender apis.Recommender, recommendationRule analysisv1alph1.RecommendationRule) (recommender.Recommender, error) {
recommender = config.MergeRecommenderConfigFromRule(recommender, recommendationRule)
return &VolumesRecommender{
*base.NewBaseRecommender(recommender),
}, nil
}
37 changes: 37 additions & 0 deletions pkg/utils/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,43 @@ func GetNodePods(kubeClient client.Client, nodeName string) ([]corev1.Pod, error
return podList.Items, nil
}

// GetOrphanVolumes returns Orphan Volumes
func GetOrphanVolumes(kubeClient client.Client) ([]string, error) {
// Get a list of all volumes
volumes := &corev1.PersistentVolumeList{}
if err := kubeClient.List(context.Background(), volumes); err != nil {
return nil, err
}

// Get a list of all pods
pods := &corev1.PodList{}
if err := kubeClient.List(context.Background(), pods); err != nil {
return nil, err
}

// Check if each volume is being used by any pods
orphanVolumesName := []string{}
for _, volume := range volumes.Items {
if isOrphanVolume(&volume, pods) {
orphanVolumesName = append(orphanVolumesName, volume.Spec.ClaimRef.Name)
}
}

return orphanVolumesName, nil
}

// volume is not being used by any pod
func isOrphanVolume(volume *corev1.PersistentVolume, pods *corev1.PodList) bool {
for _, pod := range pods.Items {
for _, volumeClaim := range pod.Spec.Volumes {
if volumeClaim.PersistentVolumeClaim != nil && volumeClaim.PersistentVolumeClaim.ClaimName == volume.Spec.ClaimRef.Name {
return false
}
}
}
return true
}

func IsPodTerminated(pod *corev1.Pod) bool {
return pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed
}

0 comments on commit 060314e

Please sign in to comment.