6
6
"reflect"
7
7
"regexp"
8
8
"strconv"
9
- "sync"
10
9
"time"
11
10
12
11
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -18,11 +17,19 @@ import (
18
17
"k8s.io/client-go/tools/cache"
19
18
)
20
19
20
+ const resyncPeriod = time .Second * 30
21
+
21
22
// PodController watches the kubernetes api for changes to Pods and
22
23
// delete completed Pods without specific annotation
23
24
type PodController struct {
24
25
podInformer cache.SharedIndexInformer
25
26
kclient * kubernetes.Clientset
27
+
28
+ keepSuccessHours float64
29
+ keepFailedHours float64
30
+ keepPendingHours float64
31
+ dryRun bool
32
+ isLegacySystem bool
26
33
}
27
34
28
35
// CreatedByAnnotation type used to match pods created by job
@@ -39,41 +46,63 @@ type CreatedByAnnotation struct {
39
46
}
40
47
}
41
48
42
- // NewPodController creates a new NewPodController
43
- func NewPodController (kclient * kubernetes.Clientset , opts map [string ]string ) * PodController {
44
- podWatcher := & PodController {}
49
+ func isLegacySystem (v version.Info ) bool {
50
+ oldVersion := false
51
+
52
+ major , _ := strconv .Atoi (v .Major )
45
53
46
- keepSuccessHours , _ := strconv .Atoi (opts ["keepSuccessHours" ])
47
- keepFailedHours , _ := strconv .Atoi (opts ["keepFailedHours" ])
48
- keepPendingHours , _ := strconv .Atoi (opts ["keepPendingHours" ])
49
- dryRun , _ := strconv .ParseBool (opts ["dryRun" ])
50
- version , err := kclient .ServerVersion ()
54
+ var minor int
55
+ re := regexp .MustCompile ("[0-9]+" )
56
+ m := re .FindAllString (v .Minor , 1 )
57
+ if len (m ) != 0 {
58
+ minor , _ = strconv .Atoi (m [0 ])
59
+ } else {
60
+ log .Printf ("failed to parse minor version %s" , v .Minor )
61
+ minor = 0
62
+ }
51
63
64
+ if major < 2 && minor < 8 {
65
+ oldVersion = true
66
+ }
67
+
68
+ return oldVersion
69
+ }
70
+
71
+ // NewPodController creates a new NewPodController
72
+ func NewPodController (kclient * kubernetes.Clientset , namespace string , dryRun bool , opts map [string ]float64 ) * PodController {
73
+ serverVersion , err := kclient .ServerVersion ()
52
74
if err != nil {
53
- log .Fatalf ("Failed to retrieve server version %v" , err )
75
+ log .Fatalf ("Failed to retrieve server serverVersion %v" , err )
54
76
}
55
77
78
+ podWatcher := & PodController {
79
+ keepSuccessHours : opts ["keepSuccessHours" ],
80
+ keepFailedHours : opts ["keepFailedHours" ],
81
+ keepPendingHours : opts ["keepPendingHours" ],
82
+ dryRun : dryRun ,
83
+ isLegacySystem : isLegacySystem (* serverVersion ),
84
+ }
56
85
// Create informer for watching Namespaces
57
86
podInformer := cache .NewSharedIndexInformer (
58
87
& cache.ListWatch {
59
88
ListFunc : func (options metav1.ListOptions ) (runtime.Object , error ) {
60
- return kclient .CoreV1 ().Pods (opts [ " namespace" ] ).List (options )
89
+ return kclient .CoreV1 ().Pods (namespace ).List (options )
61
90
},
62
91
WatchFunc : func (options metav1.ListOptions ) (watch.Interface , error ) {
63
- return kclient .CoreV1 ().Pods (opts [ " namespace" ] ).Watch (options )
92
+ return kclient .CoreV1 ().Pods (namespace ).Watch (options )
64
93
},
65
94
},
66
95
& v1.Pod {},
67
- time . Second * 30 ,
96
+ resyncPeriod ,
68
97
cache.Indexers {},
69
98
)
70
99
podInformer .AddEventHandler (cache.ResourceEventHandlerFuncs {
71
- AddFunc : func (cur interface {}) {
72
- podWatcher .doTheMagic ( cur , keepSuccessHours , keepFailedHours , keepPendingHours , dryRun , * version )
100
+ AddFunc : func (obj interface {}) {
101
+ podWatcher .Process ( obj )
73
102
},
74
- UpdateFunc : func (old , cur interface {}) {
75
- if ! reflect .DeepEqual (old , cur ) {
76
- podWatcher .doTheMagic ( cur , keepSuccessHours , keepFailedHours , keepPendingHours , dryRun , * version )
103
+ UpdateFunc : func (old , new interface {}) {
104
+ if ! reflect .DeepEqual (old , new ) {
105
+ podWatcher .Process ( new )
77
106
}
78
107
},
79
108
})
@@ -84,112 +113,100 @@ func NewPodController(kclient *kubernetes.Clientset, opts map[string]string) *Po
84
113
return podWatcher
85
114
}
86
115
116
+ func (c * PodController ) periodicCacheCheck () {
117
+ for {
118
+ for _ , obj := range c .podInformer .GetStore ().List () {
119
+ c .Process (obj )
120
+ }
121
+ time .Sleep (2 * resyncPeriod )
122
+ }
123
+ }
124
+
87
125
// Run starts the process for listening for pod changes and acting upon those changes.
88
- func (c * PodController ) Run (stopCh <- chan struct {}, wg * sync. WaitGroup ) {
126
+ func (c * PodController ) Run (stopCh <- chan struct {}) {
89
127
log .Printf ("Listening for changes..." )
90
- // When this function completes, mark the go function as done
91
- defer wg .Done ()
92
-
93
- // Increment wait group as we're about to execute a go function
94
- wg .Add (1 )
95
128
96
- // Execute go function
97
129
go c .podInformer .Run (stopCh )
130
+ go c .periodicCacheCheck ()
98
131
99
- // Wait till we receive a stop signal
100
132
<- stopCh
101
133
}
102
134
103
- func (c * PodController ) doTheMagic (cur interface {}, keepSuccessHours int , keepFailedHours int , keepPendingHours int , dryRun bool , version version.Info ) {
104
-
105
- podObj := cur .(* v1.Pod )
106
- parentJobName := c .getParentJobName (podObj , version )
135
+ func (c * PodController ) Process (obj interface {}) {
136
+ podObj := obj .(* v1.Pod )
137
+ parentJobName := c .getParentJobName (podObj )
107
138
// if we couldn't find a prent job name, ignore this pod
108
139
if parentJobName == "" {
109
- log .Printf ("Pod %s was not created by a job... ignoring" , podObj .Name )
110
140
return
111
141
}
112
142
113
143
executionTimeHours := c .getExecutionTimeHours (podObj )
114
- log .Printf ("Checking pod %s with %s status that was executed %f hours ago" , podObj .Name , podObj .Status .Phase , executionTimeHours )
115
144
switch podObj .Status .Phase {
116
145
case v1 .PodSucceeded :
117
- if keepSuccessHours == 0 || (keepSuccessHours > 0 && executionTimeHours > float32 ( keepSuccessHours ) ) {
118
- c .deleteObjects (podObj , parentJobName , dryRun )
146
+ if c . keepSuccessHours == 0 || (c . keepSuccessHours > 0 && executionTimeHours > c . keepSuccessHours ) {
147
+ c .deleteObjects (podObj , parentJobName )
119
148
}
120
149
case v1 .PodFailed :
121
- if keepFailedHours == 0 || (keepFailedHours > 0 && executionTimeHours > float32 ( keepFailedHours ) ) {
122
- c .deleteObjects (podObj , parentJobName , dryRun )
150
+ if c . keepFailedHours == 0 || (c . keepFailedHours > 0 && executionTimeHours > c . keepFailedHours ) {
151
+ c .deleteObjects (podObj , parentJobName )
123
152
}
124
153
case v1 .PodPending :
125
- if keepPendingHours > 0 && executionTimeHours > float32 ( keepPendingHours ) {
126
- c .deleteObjects (podObj , parentJobName , dryRun )
154
+ if c . keepPendingHours > 0 && executionTimeHours > c . keepPendingHours {
155
+ c .deleteObjects (podObj , parentJobName )
127
156
}
128
157
default :
129
158
return
130
159
}
131
160
}
132
161
133
162
// method to calculate the hours that passed since the pod's execution end time
134
- func (c * PodController ) getExecutionTimeHours (podObj * v1.Pod ) (executionTimeHours float32 ) {
135
- executionTimeHours = 0.0
136
- currentUnixTime := time .Now ().Unix ()
137
- podConditions := podObj .Status .Conditions
138
- var pc v1.PodCondition
139
- for _ , pc = range podConditions {
163
+ func (c * PodController ) getExecutionTimeHours (podObj * v1.Pod ) float64 {
164
+ currentUnixTime := time .Now ()
165
+ for _ , pc := range podObj .Status .Conditions {
140
166
// Looking for the time when pod's condition "Ready" became "false" (equals end of execution)
141
167
if pc .Type == v1 .PodReady && pc .Status == v1 .ConditionFalse {
142
- executionTimeUnix := pc .LastTransitionTime .Unix ()
143
- executionTimeHours = (float32 (currentUnixTime ) - float32 (executionTimeUnix )) / float32 (3600 )
168
+ return currentUnixTime .Sub (pc .LastTransitionTime .Time ).Hours ()
144
169
}
145
170
}
146
171
147
- return
172
+ return 0.0
148
173
}
149
174
150
- func (c * PodController ) deleteObjects (podObj * v1.Pod , parentJobName string , dryRun bool ) {
175
+ func (c * PodController ) deleteObjects (podObj * v1.Pod , parentJobName string ) {
151
176
// Delete Pod
152
- if ! dryRun {
177
+ if ! c . dryRun {
153
178
log .Printf ("Deleting pod '%s'" , podObj .Name )
154
179
var po metav1.DeleteOptions
155
- c .kclient .CoreV1 ().Pods (podObj .Namespace ).Delete (podObj .Name , & po )
180
+ err := c .kclient .CoreV1 ().Pods (podObj .Namespace ).Delete (podObj .Name , & po )
181
+ if err != nil {
182
+ log .Printf ("failed to delete job %s: %v" , parentJobName , err )
183
+ }
156
184
} else {
157
185
log .Printf ("dry-run: Pod '%s' would have been deleted" , podObj .Name )
158
186
}
159
187
// Delete Job itself
160
- if ! dryRun {
188
+ if ! c . dryRun {
161
189
log .Printf ("Deleting job '%s'" , parentJobName )
162
190
var jo metav1.DeleteOptions
163
- c .kclient .BatchV1Client .Jobs (podObj .Namespace ).Delete (parentJobName , & jo )
191
+ err := c .kclient .BatchV1Client .Jobs (podObj .Namespace ).Delete (parentJobName , & jo )
192
+ if err != nil {
193
+ log .Printf ("failed to delete job %s: %v" , parentJobName , err )
194
+ }
164
195
} else {
165
196
log .Printf ("dry-run: Job '%s' would have been deleted" , parentJobName )
166
197
}
167
198
return
168
199
}
169
200
170
- func (c * PodController ) getParentJobName (podObj * v1.Pod , version version.Info ) (parentJobName string ) {
171
-
172
- oldVersion := false
173
-
174
- major , _ := strconv .Atoi (version .Major )
175
-
176
- var minor int
177
- re := regexp .MustCompile ("[0-9]+" )
178
- m := re .FindAllString (version .Minor , 1 )
179
- if len (m ) != 0 {
180
- minor , _ = strconv .Atoi (m [0 ])
181
- } else {
182
- log .Printf ("failed to parse minor version %s" , version .Minor )
183
- minor = 0
184
- }
185
-
186
- if major < 2 && minor < 8 {
187
- oldVersion = true
188
- }
201
+ func (c * PodController ) getParentJobName (podObj * v1.Pod ) (parentJobName string ) {
189
202
190
- if oldVersion {
203
+ if c . isLegacySystem {
191
204
var createdMeta CreatedByAnnotation
192
- json .Unmarshal ([]byte (podObj .ObjectMeta .Annotations ["kubernetes.io/created-by" ]), & createdMeta )
205
+ err := json .Unmarshal ([]byte (podObj .ObjectMeta .Annotations ["kubernetes.io/created-by" ]), & createdMeta )
206
+ if err != nil {
207
+ log .Printf ("failed to unmarshal annotations for pod %s. %v" , podObj .Name , err )
208
+ return
209
+ }
193
210
if createdMeta .Reference .Kind == "Job" {
194
211
parentJobName = createdMeta .Reference .Name
195
212
}
0 commit comments