@@ -11,9 +11,13 @@ import (
11
11
"time"
12
12
13
13
"github.com/mcuadros/go-defaults"
14
+ "github.com/mitchellh/mapstructure"
14
15
batchv1 "k8s.io/api/batch/v1"
15
16
corev1 "k8s.io/api/core/v1"
16
17
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18
+ "k8s.io/apimachinery/pkg/fields"
19
+ "k8s.io/apimachinery/pkg/labels"
20
+ "k8s.io/apimachinery/pkg/selection"
17
21
"k8s.io/client-go/kubernetes"
18
22
typedbatchv1 "k8s.io/client-go/kubernetes/typed/batch/v1"
19
23
"k8s.io/client-go/rest"
@@ -25,6 +29,7 @@ const (
25
29
bufferSize = 4096
26
30
sleepTime = 500
27
31
defaultTTLSecondsAfterFinished = 60
32
+ trueString = "true"
28
33
)
29
34
30
35
var (
@@ -48,6 +53,72 @@ type Pod struct {
48
53
Containers []string `json:"containers"`
49
54
}
50
55
56
+ type LogOptions struct {
57
+ App string `mapstructure:"app"`
58
+ Pod string `mapstructure:"pod"`
59
+ Container string `mapstructure:"container"`
60
+ Follow string `mapstructure:"follow"`
61
+ Previous string `mapstructure:"previous"`
62
+ SinceSeconds string `mapstructure:"since_seconds"`
63
+ Timestamps string `mapstructure:"timestamps"`
64
+ TailLines string `mapstructure:"tail_lines"`
65
+ }
66
+
67
+ func (l LogOptions ) getPodListOptions () (metav1.ListOptions , error ) {
68
+ labelSelector := labels .NewSelector ()
69
+ fieldSelector := fields .Everything ()
70
+ r , err := labels .NewRequirement ("app" , selection .Equals , []string {l .App })
71
+ if err != nil {
72
+ return metav1.ListOptions {}, err
73
+ }
74
+ labelSelector = labelSelector .Add (* r )
75
+
76
+ if l .Pod != "" {
77
+ fieldSelector = fields .AndSelectors (fieldSelector , fields .OneTermEqualSelector ("metadata.name" , l .Pod ))
78
+ }
79
+
80
+ return metav1.ListOptions {
81
+ LabelSelector : labelSelector .String (),
82
+ FieldSelector : fieldSelector .String (),
83
+ }, nil
84
+ }
85
+
86
+ func (l LogOptions ) getPodLogOptions () (* corev1.PodLogOptions , error ) {
87
+ podLogOpts := & corev1.PodLogOptions {
88
+ Container : l .Container ,
89
+ }
90
+
91
+ if l .Follow == trueString {
92
+ podLogOpts .Follow = true
93
+ }
94
+
95
+ if l .Previous == trueString {
96
+ podLogOpts .Previous = true
97
+ }
98
+
99
+ if l .Timestamps == trueString {
100
+ podLogOpts .Timestamps = true
101
+ }
102
+
103
+ if l .SinceSeconds != "" {
104
+ ss , err := strconv .ParseInt (l .SinceSeconds , 10 , 64 )
105
+ if err != nil {
106
+ return nil , err
107
+ }
108
+ podLogOpts .SinceSeconds = & ss
109
+ }
110
+
111
+ if l .TailLines != "" {
112
+ tl , err := strconv .ParseInt (l .TailLines , 10 , 64 )
113
+ if err != nil {
114
+ return nil , err
115
+ }
116
+ podLogOpts .TailLines = & tl
117
+ }
118
+
119
+ return podLogOpts , nil
120
+ }
121
+
51
122
func DefaultClientConfig () Config {
52
123
var defaultProviderConfig Config
53
124
defaults .SetDefaults (& defaultProviderConfig )
@@ -62,44 +133,14 @@ func NewClient(config Config) *Client {
62
133
}
63
134
64
135
func (c Client ) StreamLogs (ctx context.Context , namespace string , filter map [string ]string ) (<- chan LogChunk , error ) {
65
- var selectors []string
66
- var podName , containerName , labelSelector , filedSelector string
67
- var sinceSeconds , tailLines int64
68
- var opts metav1.ListOptions
136
+ var logOptions LogOptions
69
137
70
- for k , v := range filter {
71
- switch k {
72
- case "pod" :
73
- podName = v
74
- case "container" :
75
- containerName = v
76
- case "sinceSeconds" :
77
- i , err := strconv .ParseInt (v , 10 , 64 )
78
- if err != nil {
79
- return nil , errors .ErrInvalid .WithMsgf ("invalid sinceSeconds filter value: %v" , err )
80
- }
81
- sinceSeconds = i
82
- case "tailLine" :
83
- i , err := strconv .ParseInt (v , 10 , 64 )
84
- if err != nil {
85
- return nil , errors .ErrInvalid .WithMsgf ("invalid tailLine filter value: %v" , err )
86
- }
87
- tailLines = i
88
- default :
89
- s := fmt .Sprintf ("%s=%s" , k , v )
90
- selectors = append (selectors , s )
91
- }
92
- }
93
-
94
- if podName == "" {
95
- labelSelector = strings .Join (selectors , "," )
96
- opts = metav1.ListOptions {LabelSelector : labelSelector }
97
- } else {
98
- filedSelector = fmt .Sprintf ("metadata.name=%s" , podName )
99
- opts = metav1.ListOptions {FieldSelector : filedSelector }
138
+ err := mapstructure .Decode (filter , & logOptions )
139
+ if err != nil {
140
+ return nil , errors .ErrInvalid .WithMsgf (err .Error ())
100
141
}
101
142
102
- return c .streamFromPods (ctx , namespace , containerName , opts , tailLines , sinceSeconds , filter )
143
+ return c .streamFromPods (ctx , namespace , logOptions )
103
144
}
104
145
105
146
func (c Client ) RunJob (ctx context.Context , namespace , name string , image string , cmd []string , retries int32 ) error {
@@ -170,13 +211,18 @@ func waitForJob(ctx context.Context, jobName string, jobs typedbatchv1.JobInterf
170
211
}
171
212
}
172
213
173
- func (c Client ) streamFromPods (ctx context.Context , namespace , containerName string , opts metav1. ListOptions , tailLines , sinceSeconds int64 , filter map [ string ] string ) (<- chan LogChunk , error ) {
214
+ func (c Client ) streamFromPods (ctx context.Context , namespace string , logOptions LogOptions ) (<- chan LogChunk , error ) {
174
215
clientSet , err := kubernetes .NewForConfig (& c .restConfig )
175
216
if err != nil {
176
217
return nil , err
177
218
}
178
219
179
- pods , err := clientSet .CoreV1 ().Pods (namespace ).List (ctx , opts )
220
+ listOpts , err := logOptions .getPodListOptions ()
221
+ if err != nil {
222
+ return nil , err
223
+ }
224
+
225
+ pods , err := clientSet .CoreV1 ().Pods (namespace ).List (ctx , listOpts )
180
226
if err != nil {
181
227
return nil , err
182
228
}
@@ -191,16 +237,21 @@ func (c Client) streamFromPods(ctx context.Context, namespace, containerName str
191
237
wg := & sync.WaitGroup {}
192
238
for _ , pod := range pods .Items {
193
239
for _ , container := range append (pod .Spec .InitContainers , pod .Spec .Containers ... ) {
194
- if containerName != "" && container .Name != containerName {
240
+ if logOptions . Container != "" && container .Name != logOptions . Container {
195
241
continue
196
242
}
243
+ plo , err := logOptions .getPodLogOptions ()
244
+ if err != nil {
245
+ return nil , err
246
+ }
247
+ plo .Container = container .Name
197
248
wg .Add (1 )
198
- go func (podName string , c corev1.Container ) {
249
+ go func (podName string , plo corev1.PodLogOptions ) {
199
250
defer wg .Done ()
200
- if err := streamContainerLogs (ctx , namespace , podName , logCh , streamingClientSet , c , tailLines , sinceSeconds , filter ); err != nil {
201
- log .Printf ("[WARN] failed to stream from container '%s':%s" , c . Name , err )
251
+ if err := streamContainerLogs (ctx , namespace , podName , logCh , streamingClientSet , plo ); err != nil {
252
+ log .Printf ("[WARN] failed to stream from container '%s':%s" , plo . Container , err )
202
253
}
203
- }(pod .Name , container )
254
+ }(pod .Name , * plo )
204
255
}
205
256
}
206
257
@@ -249,27 +300,14 @@ func (c Client) GetPodDetails(ctx context.Context, namespace string, labelSelect
249
300
return podDetails , nil
250
301
}
251
302
252
- func streamContainerLogs (ctx context.Context , ns , podName string , logCh chan <- LogChunk , clientSet * kubernetes.Clientset , container corev1.Container , tailLines , sinceSeconds int64 , filter map [string ]string ) error {
253
- podLogOpts := corev1.PodLogOptions {}
254
- podLogOpts .Follow = true
255
- podLogOpts .Container = container .Name
256
-
257
- if sinceSeconds > 0 {
258
- podLogOpts .SinceSeconds = & sinceSeconds
259
- }
260
-
261
- if tailLines > 0 {
262
- podLogOpts .TailLines = & tailLines
263
- }
264
-
303
+ func streamContainerLogs (ctx context.Context , ns , podName string , logCh chan <- LogChunk , clientSet * kubernetes.Clientset ,
304
+ podLogOpts corev1.PodLogOptions ,
305
+ ) error {
265
306
podLogs , err := clientSet .CoreV1 ().Pods (ns ).GetLogs (podName , & podLogOpts ).Stream (ctx )
266
307
if err != nil {
267
308
return err
268
309
}
269
310
270
- filter ["pod" ] = podName
271
- filter ["container" ] = container .Name
272
-
273
311
buf := make ([]byte , bufferSize )
274
312
for {
275
313
numBytes , err := podLogs .Read (buf )
@@ -285,7 +323,7 @@ func streamContainerLogs(ctx context.Context, ns, podName string, logCh chan<- L
285
323
286
324
logChunk := LogChunk {
287
325
Data : []byte (string (buf [:numBytes ])),
288
- Labels : filter ,
326
+ Labels : map [ string ] string { "pod" : podName , "container" : podLogOpts . Container } ,
289
327
}
290
328
291
329
select {
0 commit comments