@@ -2,24 +2,28 @@ package deploylog
2
2
3
3
import (
4
4
"context"
5
+ "errors"
5
6
"fmt"
6
7
"sort"
7
8
"time"
8
9
9
10
"github.com/golang/glog"
10
- kapiv1 "k8s.io/api/core/v1"
11
- "k8s.io/apimachinery/pkg/api/errors"
11
+
12
+ corev1 "k8s.io/api/core/v1"
13
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
12
14
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15
+ "k8s.io/apimachinery/pkg/fields"
13
16
"k8s.io/apimachinery/pkg/labels"
14
17
"k8s.io/apimachinery/pkg/runtime"
15
18
"k8s.io/apimachinery/pkg/util/wait"
19
+ "k8s.io/apimachinery/pkg/watch"
16
20
apirequest "k8s.io/apiserver/pkg/endpoints/request"
17
21
genericrest "k8s.io/apiserver/pkg/registry/generic/rest"
18
22
"k8s.io/apiserver/pkg/registry/rest"
23
+ corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
19
24
kapi "k8s.io/kubernetes/pkg/apis/core"
20
25
kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
21
26
"k8s.io/kubernetes/pkg/controller"
22
- kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
23
27
24
28
apiserverrest "github.com/openshift/origin/pkg/apiserver/rest"
25
29
appsapi "github.com/openshift/origin/pkg/apps/apis/apps"
@@ -40,12 +44,12 @@ const (
40
44
type REST struct {
41
45
dcClient appsclient.DeploymentConfigsGetter
42
46
rcClient kcoreclient.ReplicationControllersGetter
43
- podClient kcoreclient .PodsGetter
47
+ podClient corev1client .PodsGetter
44
48
timeout time.Duration
45
49
interval time.Duration
46
50
47
51
// for unit testing
48
- getLogsFn func (podNamespace , podName string , logOpts * kapi .PodLogOptions ) (runtime.Object , error )
52
+ getLogsFn func (podNamespace , podName string , logOpts * corev1 .PodLogOptions ) (runtime.Object , error )
49
53
}
50
54
51
55
// REST implements GetterWithOptions
@@ -55,7 +59,7 @@ var _ = rest.GetterWithOptions(&REST{})
55
59
// one for deployments (replication controllers) and one for pods to get the necessary
56
60
// attributes to assemble the URL to which the request shall be redirected in order to
57
61
// get the deployment logs.
58
- func NewREST (dcClient appsclient.DeploymentConfigsGetter , rcClient kcoreclient.ReplicationControllersGetter , podClient kcoreclient .PodsGetter ) * REST {
62
+ func NewREST (dcClient appsclient.DeploymentConfigsGetter , rcClient kcoreclient.ReplicationControllersGetter , podClient corev1client .PodsGetter ) * REST {
59
63
r := & REST {
60
64
dcClient : dcClient ,
61
65
rcClient : rcClient ,
@@ -83,27 +87,27 @@ func (r *REST) Get(ctx context.Context, name string, opts runtime.Object) (runti
83
87
// Ensure we have a namespace in the context
84
88
namespace , ok := apirequest .NamespaceFrom (ctx )
85
89
if ! ok {
86
- return nil , errors .NewBadRequest ("namespace parameter required." )
90
+ return nil , apierrors .NewBadRequest ("namespace parameter required." )
87
91
}
88
92
89
93
// Validate DeploymentLogOptions
90
94
deployLogOpts , ok := opts .(* appsapi.DeploymentLogOptions )
91
95
if ! ok {
92
- return nil , errors .NewBadRequest ("did not get an expected options." )
96
+ return nil , apierrors .NewBadRequest ("did not get an expected options." )
93
97
}
94
98
if errs := validation .ValidateDeploymentLogOptions (deployLogOpts ); len (errs ) > 0 {
95
- return nil , errors .NewInvalid (appsapi .Kind ("DeploymentLogOptions" ), "" , errs )
99
+ return nil , apierrors .NewInvalid (appsapi .Kind ("DeploymentLogOptions" ), "" , errs )
96
100
}
97
101
98
102
// Fetch deploymentConfig and check latest version; if 0, there are no deployments
99
103
// for this config
100
104
config , err := r .dcClient .DeploymentConfigs (namespace ).Get (name , metav1.GetOptions {})
101
105
if err != nil {
102
- return nil , errors .NewNotFound (appsapi .Resource ("deploymentconfig" ), name )
106
+ return nil , apierrors .NewNotFound (appsapi .Resource ("deploymentconfig" ), name )
103
107
}
104
108
desiredVersion := config .Status .LatestVersion
105
109
if desiredVersion == 0 {
106
- return nil , errors .NewBadRequest (fmt .Sprintf ("no deployment exists for deploymentConfig %q" , config .Name ))
110
+ return nil , apierrors .NewBadRequest (fmt .Sprintf ("no deployment exists for deploymentConfig %q" , config .Name ))
107
111
}
108
112
109
113
// Support retrieving logs for older deployments
@@ -113,12 +117,12 @@ func (r *REST) Get(ctx context.Context, name string, opts runtime.Object) (runti
113
117
if deployLogOpts .Previous {
114
118
desiredVersion --
115
119
if desiredVersion < 1 {
116
- return nil , errors .NewBadRequest (fmt .Sprintf ("no previous deployment exists for deploymentConfig %q" , config .Name ))
120
+ return nil , apierrors .NewBadRequest (fmt .Sprintf ("no previous deployment exists for deploymentConfig %q" , config .Name ))
117
121
}
118
122
}
119
123
case * deployLogOpts .Version <= 0 || * deployLogOpts .Version > config .Status .LatestVersion :
120
124
// Invalid version
121
- return nil , errors .NewBadRequest (fmt .Sprintf ("invalid version for deploymentConfig %q: %d" , config .Name , * deployLogOpts .Version ))
125
+ return nil , apierrors .NewBadRequest (fmt .Sprintf ("invalid version for deploymentConfig %q: %d" , config .Name , * deployLogOpts .Version ))
122
126
default :
123
127
desiredVersion = * deployLogOpts .Version
124
128
}
@@ -143,16 +147,16 @@ func (r *REST) Get(ctx context.Context, name string, opts runtime.Object) (runti
143
147
}
144
148
glog .V (4 ).Infof ("Deployment %s is in %s state, waiting for it to start..." , appsutil .LabelForDeployment (target ), status )
145
149
146
- if err := appsutil . WaitForRunningDeployerPod (r .podClient , target , r .timeout ); err != nil {
147
- return nil , errors .NewBadRequest (fmt .Sprintf ("failed to run deployer pod %s: %v" , podName , err ))
150
+ if err := WaitForRunningDeployerPod (r .podClient , target , r .timeout ); err != nil {
151
+ return nil , apierrors .NewBadRequest (fmt .Sprintf ("failed to run deployer pod %s: %v" , podName , err ))
148
152
}
149
153
150
154
latest , ok , err := registry .WaitForRunningDeployment (r .rcClient , target , r .timeout )
151
155
if err != nil {
152
- return nil , errors .NewBadRequest (fmt .Sprintf ("unable to wait for deployment %s to run: %v" , appsutil .LabelForDeployment (target ), err ))
156
+ return nil , apierrors .NewBadRequest (fmt .Sprintf ("unable to wait for deployment %s to run: %v" , appsutil .LabelForDeployment (target ), err ))
153
157
}
154
158
if ! ok {
155
- return nil , errors .NewServerTimeout (kapi .Resource ("ReplicationController" ), "get" , 2 )
159
+ return nil , apierrors .NewServerTimeout (kapi .Resource ("ReplicationController" ), "get" , 2 )
156
160
}
157
161
if appsutil .IsCompleteDeployment (latest ) {
158
162
podName , err = r .returnApplicationPodName (target )
@@ -167,11 +171,11 @@ func (r *REST) Get(ctx context.Context, name string, opts runtime.Object) (runti
167
171
}
168
172
}
169
173
170
- logOpts := appsapi . DeploymentToPodLogOptions (deployLogOpts )
174
+ logOpts := DeploymentToPodLogOptions (deployLogOpts )
171
175
return r .getLogsFn (namespace , podName , logOpts )
172
176
}
173
177
174
- func (r * REST ) getLogs (podNamespace , podName string , logOpts * kapi .PodLogOptions ) (runtime.Object , error ) {
178
+ func (r * REST ) getLogs (podNamespace , podName string , logOpts * corev1 .PodLogOptions ) (runtime.Object , error ) {
175
179
logRequest := r .podClient .Pods (podNamespace ).GetLogs (podName , logOpts )
176
180
177
181
readerCloser , err := logRequest .Stream ()
@@ -196,7 +200,7 @@ func (r *REST) waitForExistingDeployment(namespace, name string) (*kapi.Replicat
196
200
condition := func () (bool , error ) {
197
201
target , err = r .rcClient .ReplicationControllers (namespace ).Get (name , metav1.GetOptions {})
198
202
switch {
199
- case errors .IsNotFound (err ):
203
+ case apierrors .IsNotFound (err ):
200
204
return false , nil
201
205
case err != nil :
202
206
return false , err
@@ -206,7 +210,7 @@ func (r *REST) waitForExistingDeployment(namespace, name string) (*kapi.Replicat
206
210
207
211
err = wait .PollImmediate (r .interval , r .timeout , condition )
208
212
if err == wait .ErrWaitTimeout {
209
- err = errors .NewNotFound (kapi .Resource ("replicationcontrollers" ), name )
213
+ err = apierrors .NewNotFound (kapi .Resource ("replicationcontrollers" ), name )
210
214
}
211
215
return target , err
212
216
}
@@ -215,11 +219,97 @@ func (r *REST) waitForExistingDeployment(namespace, name string) (*kapi.Replicat
215
219
// view its logs.
216
220
func (r * REST ) returnApplicationPodName (target * kapi.ReplicationController ) (string , error ) {
217
221
selector := labels .SelectorFromValidatedSet (labels .Set (target .Spec .Selector ))
218
- sortBy := func (pods []* kapiv1 .Pod ) sort.Interface { return controller .ByLogging (pods ) }
222
+ sortBy := func (pods []* corev1 .Pod ) sort.Interface { return controller .ByLogging (pods ) }
219
223
220
- firstPod , _ , err := kcmdutil . GetFirstPod (r .podClient , target .Namespace , selector .String (), r .timeout , sortBy )
224
+ firstPod , _ , err := GetFirstPod (r .podClient , target .Namespace , selector .String (), r .timeout , sortBy )
221
225
if err != nil {
222
- return "" , errors .NewInternalError (err )
226
+ return "" , apierrors .NewInternalError (err )
223
227
}
224
228
return firstPod .Name , nil
225
229
}
230
+
231
+ // GetFirstPod returns a pod matching the namespace and label selector
232
+ // and the number of all pods that match the label selector.
233
+ func GetFirstPod (client corev1client.PodsGetter , namespace string , selector string , timeout time.Duration , sortBy func ([]* corev1.Pod ) sort.Interface ) (* corev1.Pod , int , error ) {
234
+ options := metav1.ListOptions {LabelSelector : selector }
235
+
236
+ podList , err := client .Pods (namespace ).List (options )
237
+ if err != nil {
238
+ return nil , 0 , err
239
+ }
240
+ pods := []* corev1.Pod {}
241
+ for i := range podList .Items {
242
+ pods = append (pods , & podList .Items [i ])
243
+ }
244
+ if len (pods ) > 0 {
245
+ sort .Sort (sortBy (pods ))
246
+ return pods [0 ], len (podList .Items ), nil
247
+ }
248
+
249
+ // Watch until we observe a pod
250
+ options .ResourceVersion = podList .ResourceVersion
251
+ w , err := client .Pods (namespace ).Watch (options )
252
+ if err != nil {
253
+ return nil , 0 , err
254
+ }
255
+ defer w .Stop ()
256
+
257
+ condition := func (event watch.Event ) (bool , error ) {
258
+ return event .Type == watch .Added || event .Type == watch .Modified , nil
259
+ }
260
+ event , err := watch .Until (timeout , w , condition )
261
+ if err != nil {
262
+ return nil , 0 , err
263
+ }
264
+ pod , ok := event .Object .(* corev1.Pod )
265
+ if ! ok {
266
+ return nil , 0 , fmt .Errorf ("%#v is not a pod event" , event )
267
+ }
268
+ return pod , 1 , nil
269
+ }
270
+
271
+ // WaitForRunningDeployerPod waits a given period of time until the deployer pod
272
+ // for given replication controller is not running.
273
+ func WaitForRunningDeployerPod (podClient corev1client.PodsGetter , rc * kapi.ReplicationController , timeout time.Duration ) error {
274
+ podName := appsutil .DeployerPodNameForDeployment (rc .Name )
275
+ canGetLogs := func (p * corev1.Pod ) bool {
276
+ return corev1 .PodSucceeded == p .Status .Phase || corev1 .PodFailed == p .Status .Phase || corev1 .PodRunning == p .Status .Phase
277
+ }
278
+ pod , err := podClient .Pods (rc .Namespace ).Get (podName , metav1.GetOptions {})
279
+ if err == nil && canGetLogs (pod ) {
280
+ return nil
281
+ }
282
+ watcher , err := podClient .Pods (rc .Namespace ).Watch (
283
+ metav1.ListOptions {
284
+ FieldSelector : fields .OneTermEqualSelector ("metadata.name" , podName ).String (),
285
+ },
286
+ )
287
+ if err != nil {
288
+ return err
289
+ }
290
+
291
+ defer watcher .Stop ()
292
+ _ , err = watch .Until (timeout , watcher , func (e watch.Event ) (bool , error ) {
293
+ if e .Type == watch .Error {
294
+ return false , fmt .Errorf ("encountered error while watching for pod: %v" , e .Object )
295
+ }
296
+ obj , isPod := e .Object .(* corev1.Pod )
297
+ if ! isPod {
298
+ return false , errors .New ("received unknown object while watching for pods" )
299
+ }
300
+ return canGetLogs (obj ), nil
301
+ })
302
+ return err
303
+ }
304
+
305
+ func DeploymentToPodLogOptions (opts * appsapi.DeploymentLogOptions ) * corev1.PodLogOptions {
306
+ return & corev1.PodLogOptions {
307
+ Container : opts .Container ,
308
+ Follow : opts .Follow ,
309
+ SinceSeconds : opts .SinceSeconds ,
310
+ SinceTime : opts .SinceTime ,
311
+ Timestamps : opts .Timestamps ,
312
+ TailLines : opts .TailLines ,
313
+ LimitBytes : opts .LimitBytes ,
314
+ }
315
+ }
0 commit comments