@@ -35,39 +35,38 @@ const HookContainerName = "lifecycle"
35
35
36
36
// HookExecutor executes a deployment lifecycle hook.
37
37
type HookExecutor struct {
38
- // podClient provides access to pods.
39
- podClient HookExecutorPodClient
38
+ // pods provides client to pods
39
+ pods kclient. PodsNamespacer
40
40
// tags allows setting image stream tags
41
41
tags client.ImageStreamTagsNamespacer
42
42
// out is where hook pod logs should be written to.
43
43
out io.Writer
44
- // podLogStream provides a reader for a pod's logs.
45
- podLogStream func (namespace , name string , opts * kapi.PodLogOptions ) (io.ReadCloser , error )
46
44
// decoder is used for encoding/decoding.
47
45
decoder runtime.Decoder
48
46
// recorder is used to emit events from hooks
49
47
events kclient.EventNamespacer
48
+ // getPodLogs knows how to get logs from a pod and is used for testing
49
+ getPodLogs func (* kapi.Pod ) (io.ReadCloser , error )
50
50
}
51
51
52
52
// NewHookExecutor makes a HookExecutor from a client.
53
- func NewHookExecutor (client kclient.PodsNamespacer , tags client.ImageStreamTagsNamespacer , events kclient.EventNamespacer , out io.Writer , decoder runtime.Decoder ) * HookExecutor {
54
- return & HookExecutor {
55
- tags : tags ,
56
- events : events ,
57
- podClient : & HookExecutorPodClientImpl {
58
- CreatePodFunc : func (namespace string , pod * kapi.Pod ) (* kapi.Pod , error ) {
59
- return client .Pods (namespace ).Create (pod )
60
- },
61
- PodWatchFunc : func (namespace , name , resourceVersion string , stopChannel chan struct {}) func () * kapi.Pod {
62
- return NewPodWatch (client , namespace , name , resourceVersion , stopChannel )
63
- },
64
- },
65
- podLogStream : func (namespace , name string , opts * kapi.PodLogOptions ) (io.ReadCloser , error ) {
66
- return client .Pods (namespace ).GetLogs (name , opts ).Stream ()
67
- },
53
+ func NewHookExecutor (pods kclient.PodsNamespacer , tags client.ImageStreamTagsNamespacer , events kclient.EventNamespacer , out io.Writer , decoder runtime.Decoder ) * HookExecutor {
54
+ executor := & HookExecutor {
55
+ tags : tags ,
56
+ pods : pods ,
57
+ events : events ,
68
58
out : out ,
69
59
decoder : decoder ,
70
60
}
61
+ executor .getPodLogs = func (pod * kapi.Pod ) (io.ReadCloser , error ) {
62
+ opts := & kapi.PodLogOptions {
63
+ Container : HookContainerName ,
64
+ Follow : true ,
65
+ Timestamps : false ,
66
+ }
67
+ return executor .pods .Pods (pod .Namespace ).GetLogs (pod .Name , opts ).Stream ()
68
+ }
69
+ return executor
71
70
}
72
71
73
72
// Execute executes hook in the context of deployment. The suffix is used to
@@ -83,26 +82,30 @@ func (e *HookExecutor) Execute(hook *deployapi.LifecycleHook, deployment *kapi.R
83
82
tagEventMessages = append (tagEventMessages , fmt .Sprintf ("image %q as %q" , image , t .To .Name ))
84
83
}
85
84
}
86
- strategyutil .RecordConfigEvent (e .events , deployment , e .decoder , kapi .EventTypeNormal , "Started" , fmt .Sprintf ("Running %s-hook (TagImages) %s for deployment %s/%s" , label , strings .Join (tagEventMessages , "," ), deployment .Namespace , deployment .Name ))
85
+ strategyutil .RecordConfigEvent (e .events , deployment , e .decoder , kapi .EventTypeNormal , "Started" ,
86
+ fmt .Sprintf ("Running %s-hook (TagImages) %s for deployment %s/%s" , label , strings .Join (tagEventMessages , "," ), deployment .Namespace , deployment .Name ))
87
87
err = e .tagImages (hook , deployment , suffix , label )
88
88
case hook .ExecNewPod != nil :
89
- strategyutil .RecordConfigEvent (e .events , deployment , e .decoder , kapi .EventTypeNormal , "Started" , fmt .Sprintf ("Running %s-hook (%q) for deployment %s/%s" , label , strings .Join (hook .ExecNewPod .Command , " " ), deployment .Namespace , deployment .Name ))
89
+ strategyutil .RecordConfigEvent (e .events , deployment , e .decoder , kapi .EventTypeNormal , "Started" ,
90
+ fmt .Sprintf ("Running %s-hook (%q) for deployment %s/%s" , label , strings .Join (hook .ExecNewPod .Command , " " ), deployment .Namespace , deployment .Name ))
90
91
err = e .executeExecNewPod (hook , deployment , suffix , label )
91
92
}
92
93
93
94
if err == nil {
94
- strategyutil .RecordConfigEvent (e .events , deployment , e .decoder , kapi .EventTypeNormal , "Completed" , fmt .Sprintf ("The %s-hook for deployment %s/%s completed successfully" , label , deployment .Namespace , deployment .Name ))
95
+ strategyutil .RecordConfigEvent (e .events , deployment , e .decoder , kapi .EventTypeNormal , "Completed" ,
96
+ fmt .Sprintf ("The %s-hook for deployment %s/%s completed successfully" , label , deployment .Namespace , deployment .Name ))
95
97
return nil
96
98
}
97
99
98
100
// Retry failures are treated the same as Abort.
99
101
switch hook .FailurePolicy {
100
102
case deployapi .LifecycleHookFailurePolicyAbort , deployapi .LifecycleHookFailurePolicyRetry :
101
- strategyutil .RecordConfigEvent (e .events , deployment , e .decoder , kapi .EventTypeWarning , "Failed" , fmt .Sprintf ("The %s-hook failed: %v, aborting deployment %s/%s" , label , err , deployment .Namespace , deployment .Name ))
103
+ strategyutil .RecordConfigEvent (e .events , deployment , e .decoder , kapi .EventTypeWarning , "Failed" ,
104
+ fmt .Sprintf ("The %s-hook failed: %v, aborting deployment %s/%s" , label , err , deployment .Namespace , deployment .Name ))
102
105
return fmt .Errorf ("the %s hook failed: %v, aborting deployment: %s/%s" , label , err , deployment .Namespace , deployment .Name )
103
106
case deployapi .LifecycleHookFailurePolicyIgnore :
104
- strategyutil .RecordConfigEvent (e .events , deployment , e .decoder , kapi .EventTypeWarning , "Failed" , fmt . Sprintf ( "The %s-hook failed: %v (ignore), deployment %s/%s will continue" , label , err , deployment . Namespace , deployment . Name ))
105
- fmt .Fprintf ( e . out , "the %s hook failed: %v (ignore), deployment %s/%s will continue" , label , err , deployment .Namespace , deployment .Name )
107
+ strategyutil .RecordConfigEvent (e .events , deployment , e .decoder , kapi .EventTypeWarning , "Failed" ,
108
+ fmt .Sprintf ( "The %s- hook failed: %v (ignore), deployment %s/%s will continue" , label , err , deployment .Namespace , deployment .Name ) )
106
109
return nil
107
110
default :
108
111
return err
@@ -170,8 +173,13 @@ func (e *HookExecutor) executeExecNewPod(hook *deployapi.LifecycleHook, deployme
170
173
return err
171
174
}
172
175
176
+ deployerPod , err := e .pods .Pods (deployment .Namespace ).Get (deployutil .DeployerPodNameForDeployment (deployment .Name ))
177
+ if err != nil {
178
+ return err
179
+ }
180
+
173
181
// Build a pod spec from the hook config and deployment
174
- podSpec , err := makeHookPod (hook , deployment , & config .Spec .Strategy , suffix )
182
+ podSpec , err := makeHookPod (hook , deployment , deployerPod , & config .Spec .Strategy , suffix )
175
183
if err != nil {
176
184
return err
177
185
}
@@ -181,7 +189,7 @@ func (e *HookExecutor) executeExecNewPod(hook *deployapi.LifecycleHook, deployme
181
189
completed , created := false , false
182
190
183
191
// Try to create the pod.
184
- pod , err := e .podClient . CreatePod (deployment .Namespace , podSpec )
192
+ pod , err := e .pods . Pods (deployment .Namespace ). Create ( podSpec )
185
193
if err != nil {
186
194
if ! kerrors .IsAlreadyExists (err ) {
187
195
return fmt .Errorf ("couldn't create lifecycle pod for %s: %v" , deployment .Name , err )
@@ -196,7 +204,7 @@ func (e *HookExecutor) executeExecNewPod(hook *deployapi.LifecycleHook, deployme
196
204
197
205
stopChannel := make (chan struct {})
198
206
defer close (stopChannel )
199
- nextPod := e . podClient . PodWatch ( pod .Namespace , pod .Name , pod .ResourceVersion , stopChannel )
207
+ nextPod := NewPodWatch ( e . pods . Pods ( pod . Namespace ), pod .Namespace , pod .Name , pod .ResourceVersion , stopChannel )
200
208
201
209
// Wait for the hook pod to reach a terminal phase. Start reading logs as
202
210
// soon as the pod enters a usable phase.
@@ -264,12 +272,7 @@ waitLoop:
264
272
// done.
265
273
func (e * HookExecutor ) readPodLogs (pod * kapi.Pod , wg * sync.WaitGroup ) {
266
274
defer wg .Done ()
267
- opts := & kapi.PodLogOptions {
268
- Container : HookContainerName ,
269
- Follow : true ,
270
- Timestamps : false ,
271
- }
272
- logStream , err := e .podLogStream (pod .Namespace , pod .Name , opts )
275
+ logStream , err := e .getPodLogs (pod )
273
276
if err != nil || logStream == nil {
274
277
fmt .Fprintf (e .out , "warning: Unable to retrieve hook logs from %s: %v\n " , pod .Name , err )
275
278
return
@@ -282,7 +285,7 @@ func (e *HookExecutor) readPodLogs(pod *kapi.Pod, wg *sync.WaitGroup) {
282
285
}
283
286
284
287
// makeHookPod makes a pod spec from a hook and deployment.
285
- func makeHookPod (hook * deployapi.LifecycleHook , deployment * kapi.ReplicationController , strategy * deployapi.DeploymentStrategy , suffix string ) (* kapi.Pod , error ) {
288
+ func makeHookPod (hook * deployapi.LifecycleHook , deployment * kapi.ReplicationController , deployerPod * kapi. Pod , strategy * deployapi.DeploymentStrategy , suffix string ) (* kapi.Pod , error ) {
286
289
exec := hook .ExecNewPod
287
290
var baseContainer * kapi.Container
288
291
for _ , container := range deployment .Spec .Template .Spec .Containers {
@@ -318,7 +321,7 @@ func makeHookPod(hook *deployapi.LifecycleHook, deployment *kapi.ReplicationCont
318
321
}
319
322
320
323
// Assigning to a variable since its address is required
321
- maxDeploymentDurationSeconds := deployapi .MaxDeploymentDurationSeconds
324
+ maxDeploymentDurationSeconds := deployapi .MaxDeploymentDurationSeconds - int64 ( time . Since ( deployerPod . Status . StartTime . Time ). Seconds ())
322
325
323
326
// Let the kubelet manage retries if requested
324
327
restartPolicy := kapi .RestartPolicyNever
@@ -405,40 +408,20 @@ func canRetryReading(pod *kapi.Pod, restarts int32) (bool, int32) {
405
408
return pod .Spec .RestartPolicy == kapi .RestartPolicyOnFailure && restartCount > restarts , restartCount
406
409
}
407
410
408
- // HookExecutorPodClient abstracts access to pods.
409
- type HookExecutorPodClient interface {
410
- CreatePod (namespace string , pod * kapi.Pod ) (* kapi.Pod , error )
411
- PodWatch (namespace , name , resourceVersion string , stopChannel chan struct {}) func () * kapi.Pod
412
- }
413
-
414
- // HookExecutorPodClientImpl is a pluggable HookExecutorPodClient.
415
- type HookExecutorPodClientImpl struct {
416
- CreatePodFunc func (namespace string , pod * kapi.Pod ) (* kapi.Pod , error )
417
- PodWatchFunc func (namespace , name , resourceVersion string , stopChannel chan struct {}) func () * kapi.Pod
418
- }
419
-
420
- func (i * HookExecutorPodClientImpl ) CreatePod (namespace string , pod * kapi.Pod ) (* kapi.Pod , error ) {
421
- return i .CreatePodFunc (namespace , pod )
422
- }
423
-
424
- func (i * HookExecutorPodClientImpl ) PodWatch (namespace , name , resourceVersion string , stopChannel chan struct {}) func () * kapi.Pod {
425
- return i .PodWatchFunc (namespace , name , resourceVersion , stopChannel )
426
- }
427
-
428
411
// NewPodWatch creates a pod watching function which is backed by a
429
412
// FIFO/reflector pair. This avoids managing watches directly.
430
413
// A stop channel to close the watch's reflector is also returned.
431
414
// It is the caller's responsibility to defer closing the stop channel to prevent leaking resources.
432
- func NewPodWatch (client kclient.PodsNamespacer , namespace , name , resourceVersion string , stopChannel chan struct {}) func () * kapi.Pod {
415
+ func NewPodWatch (client kclient.PodInterface , namespace , name , resourceVersion string , stopChannel chan struct {}) func () * kapi.Pod {
433
416
fieldSelector := fields .OneTermEqualSelector ("metadata.name" , name )
434
417
podLW := & cache.ListWatch {
435
418
ListFunc : func (options kapi.ListOptions ) (runtime.Object , error ) {
436
- opts := kapi. ListOptions { FieldSelector : fieldSelector }
437
- return client .Pods ( namespace ). List (opts )
419
+ options . FieldSelector = fieldSelector
420
+ return client .List (options )
438
421
},
439
422
WatchFunc : func (options kapi.ListOptions ) (watch.Interface , error ) {
440
- opts := kapi. ListOptions { FieldSelector : fieldSelector , ResourceVersion : options . ResourceVersion }
441
- return client .Pods ( namespace ). Watch (opts )
423
+ options . FieldSelector = fieldSelector
424
+ return client .Watch (options )
442
425
},
443
426
}
444
427
@@ -472,12 +455,12 @@ func NewAcceptNewlyObservedReadyPods(
472
455
store := cache .NewStore (cache .MetaNamespaceKeyFunc )
473
456
lw := & cache.ListWatch {
474
457
ListFunc : func (options kapi.ListOptions ) (runtime.Object , error ) {
475
- opts := kapi. ListOptions { LabelSelector : selector }
476
- return kclient .Pods (deployment .Namespace ).List (opts )
458
+ options . LabelSelector = selector
459
+ return kclient .Pods (deployment .Namespace ).List (options )
477
460
},
478
461
WatchFunc : func (options kapi.ListOptions ) (watch.Interface , error ) {
479
- opts := kapi. ListOptions { LabelSelector : selector , ResourceVersion : options . ResourceVersion }
480
- return kclient .Pods (deployment .Namespace ).Watch (opts )
462
+ options . LabelSelector = selector
463
+ return kclient .Pods (deployment .Namespace ).Watch (options )
481
464
},
482
465
}
483
466
stop := make (chan struct {})
0 commit comments