1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "strings"
7
+
8
+ "github.com/spf13/cobra"
9
+
10
+ //appsv1 "k8s.io/api/apps/v1"
11
+ corev1 "k8s.io/api/core/v1"
12
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13
+ "k8s.io/client-go/kubernetes"
14
+ "k8s.io/client-go/tools/clientcmd"
15
+ )
16
+
17
+ // scaleCmd represents the scale command
18
+ var scaleCmd = & cobra.Command {
19
+ Use : "scale [PVC]" ,
20
+ Short : "Finds any Pods accessing the given PVC and sacles them down through their Deployments/ReplicationSets" ,
21
+ Long : `Depending on the data that is written to the Persistent Volume Claim, it can be required to stop the Pod
22
+ writing to it first. An example is a database, which keeps data in-memory. A relyable way to flush this
23
+ data to disc is to shut the application down.
24
+
25
+ For other use-cases like static asset hosting, this might not be required.` ,
26
+ Args : cobra .MinimumNArgs (1 ),
27
+ Run : func (cmd * cobra.Command , args []string ) {
28
+ fmt .Printf ("Attempting to scale down anything that uses %s\n " , args [0 ])
29
+
30
+ config , err := clientcmd .BuildConfigFromFlags ("" , "/Users/lukasknuth/k3sup/kubeconfig" )
31
+ if err != nil {
32
+ panic (err .Error ())
33
+ }
34
+
35
+ clientset , err := kubernetes .NewForConfig (config )
36
+ if err != nil {
37
+ panic (err .Error ())
38
+ }
39
+
40
+ // todo add namespace flag!
41
+ // todo use another "context"!?
42
+
43
+ // ######
44
+ pods , err := clientset .CoreV1 ().Pods ("infrastructure" ).List (context .TODO (), metav1.ListOptions {})
45
+ if err != nil {
46
+ panic (err .Error ())
47
+ }
48
+
49
+ filtered := filterMountingPods (pods .Items , args [0 ])
50
+ tree := make ([]metav1.ObjectMeta , 0 )
51
+ for _ , pod := range filtered {
52
+ fmt .Printf ("Mounted by %s: %s\n " , "Pod" , pod .Name )
53
+ options := RequestOptions {Namespace : "infrastructure" , Context : context .TODO (), Clientset : clientset }
54
+ tree , err = dependencyTree (pod .ObjectMeta , tree , 1 , & options )
55
+ if err != nil {
56
+ panic (err .Error ())
57
+ }
58
+ }
59
+ fmt .Printf ("Should scale %d resources\n " , len (tree ))
60
+ for _ , res := range tree {
61
+ fmt .Printf (" %s\n " , res .Name )
62
+ }
63
+ },
64
+ }
65
+
66
+ func init () {
67
+ rootCmd .AddCommand (scaleCmd )
68
+
69
+ // Here you will define your flags and configuration settings.
70
+
71
+ // Cobra supports Persistent Flags which will work for this command
72
+ // and all subcommands, e.g.:
73
+ // scaleCmd.PersistentFlags().String("foo", "", "A help for foo")
74
+
75
+ // Cobra supports local flags which will only run when this command
76
+ // is called directly, e.g.:
77
+ //scaleCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
78
+ }
79
+
80
+ func filterMountingPods (pods []corev1.Pod , pvcName string ) []corev1.Pod {
81
+ var filtered []corev1.Pod
82
+ for _ , pod := range pods {
83
+ for _ , vol := range pod .Spec .Volumes { // todo also filter by state + filter yourself (by hostname)
84
+ if vol .VolumeSource .PersistentVolumeClaim != nil && vol .VolumeSource .PersistentVolumeClaim .ClaimName == pvcName {
85
+ filtered = append (filtered , pod )
86
+ }
87
+ }
88
+ }
89
+ return filtered
90
+ }
91
+
92
+ type RequestOptions struct {
93
+ Clientset * kubernetes.Clientset
94
+ Context context.Context
95
+ Namespace string
96
+ }
97
+
98
+ func dependencyTree (resource metav1.ObjectMeta , toScale []metav1.ObjectMeta , level int , options * RequestOptions ) ([]metav1.ObjectMeta , error ) {
99
+ if len (resource .OwnerReferences ) > 0 {
100
+ for _ , owner := range resource .OwnerReferences {
101
+ ownerRes , err := fetchResource (& owner , options )
102
+ if err != nil {
103
+ return nil , err
104
+ } else if ownerRes != nil {
105
+ fmt .Printf ("%s-> %s (%s)\n " , strings .Repeat (" " , level ), ownerRes .Name , "todo" ) // todo use wrappers instead?
106
+ toScale , err = dependencyTree (* ownerRes , toScale , level + 1 , options )
107
+ if err != nil {
108
+ return nil , err
109
+ }
110
+ } else {
111
+ fmt .Printf ("%s-> Unsupported owner %s of type %s\n " , strings .Repeat (" " , level ), owner .Name , owner .Kind )
112
+ }
113
+ }
114
+ return toScale , nil
115
+ } else {
116
+ return append (toScale , resource ), nil
117
+ }
118
+ }
119
+
120
+ func fetchResource (ref * metav1.OwnerReference , options * RequestOptions ) (* metav1.ObjectMeta , error ) {
121
+ switch ref .Kind {
122
+ case "ReplicaSet" :
123
+ rs , err := options .Clientset .AppsV1 ().ReplicaSets (options .Namespace ).Get (options .Context , ref .Name , metav1.GetOptions {})
124
+ if err != nil {
125
+ return nil , err
126
+ } else {
127
+ return & rs .ObjectMeta , nil
128
+ }
129
+ case "Deployment" :
130
+ d , err := options .Clientset .AppsV1 ().Deployments (options .Namespace ).Get (options .Context , ref .Name , metav1.GetOptions {})
131
+ if err != nil {
132
+ return nil , err
133
+ } else {
134
+ return & d .ObjectMeta , nil
135
+ }
136
+ case "Job" :
137
+ fallthrough
138
+ case "CronJob" :
139
+ fallthrough
140
+ case "DaemonSet" :
141
+ fallthrough
142
+ case "StatefulSet" :
143
+ fallthrough
144
+ default :
145
+ return nil , nil
146
+ }
147
+ }
0 commit comments