1
1
package controller
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
5
6
"errors"
7
+ "fmt"
8
+ "io"
9
+ "io/ioutil"
6
10
7
11
"github.com/bakito/batch-job-controller/pkg/lifecycle"
12
+ "github.com/go-logr/logr"
8
13
corev1 "k8s.io/api/core/v1"
9
14
k8serrors "k8s.io/apimachinery/pkg/api/errors"
10
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15
+ "k8s.io/client-go/kubernetes"
16
+ corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
11
17
ctrl "sigs.k8s.io/controller-runtime"
12
18
"sigs.k8s.io/controller-runtime/pkg/client"
13
- "sigs.k8s.io/controller-runtime/pkg/event"
14
19
"sigs.k8s.io/controller-runtime/pkg/handler"
15
20
"sigs.k8s.io/controller-runtime/pkg/log"
16
21
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -27,11 +32,17 @@ const (
27
32
// PodReconciler reconciler
28
33
type PodReconciler struct {
29
34
client.Client
35
+ coreClient corev1client.CoreV1Interface
30
36
Controller lifecycle.Controller
31
37
}
32
38
33
39
// SetupWithManager setup
34
40
func (r * PodReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
41
+ clientset , err := kubernetes .NewForConfig (mgr .GetConfig ())
42
+ if err != nil {
43
+ return err
44
+ }
45
+ r .coreClient = clientset .CoreV1 ()
35
46
return ctrl .NewControllerManagedBy (mgr ).
36
47
For (& corev1.Pod {}).
37
48
Watches (& source.Kind {Type : & corev1.Pod {}}, & handler.EnqueueRequestForObject {}).
@@ -59,40 +70,60 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R
59
70
executionID := pod .GetLabels ()[LabelExecutionID ]
60
71
node := pod .Spec .NodeName
61
72
62
- switch pod .Status .Phase {
63
- case corev1 .PodSucceeded :
64
- err = r .Controller .PodTerminated (executionID , node , pod .Status .Phase )
65
- case corev1 .PodFailed :
66
- err = r .Controller .PodTerminated (executionID , node , pod .Status .Phase )
67
- }
68
- if err != nil {
69
- if ! errors .Is (err , & lifecycle.ExecutionIDNotFound {}) {
70
- podLog .Error (err , "unexpected error" )
71
- return reconcile.Result {}, err
73
+ if pod .Status .Phase == corev1 .PodSucceeded || pod .Status .Phase == corev1 .PodFailed {
74
+ if r .Controller .Config ().SavePodLog {
75
+ r .savePodLogs (ctx , pod , podLog , executionID )
76
+ }
77
+ if err := r .Controller .PodTerminated (executionID , node , pod .Status .Phase ); err != nil {
78
+ if ! errors .Is (err , & lifecycle.ExecutionIDNotFound {}) {
79
+ podLog .Error (err , "unexpected error" )
80
+ return reconcile.Result {}, err
81
+ }
72
82
}
73
83
}
74
84
75
85
return reconcile.Result {}, nil
76
86
}
77
87
78
- type podPredicate struct {}
79
-
80
- func (podPredicate ) Create (e event.CreateEvent ) bool {
81
- return matches (e .Object )
88
+ func (r * PodReconciler ) savePodLogs (ctx context.Context , pod * corev1.Pod , podLog logr.Logger , executionID string ) {
89
+ for _ , c := range pod .Spec .Containers {
90
+ clog := podLog .WithValues ("container" , c .Name )
91
+ if l , err := r .getPodLog (ctx , pod .Namespace , pod .Name , c .Name ); err != nil {
92
+ clog .Info ("could not get log of container" )
93
+ } else {
94
+ if fileName , err := r .savePodLog (executionID , c .Name , l ); err != nil {
95
+ clog .Error (err , "error saving container log file" )
96
+ } else {
97
+ clog .WithValues ("name" , fileName ).Info ("saved container log file" )
98
+ }
99
+ }
100
+ }
82
101
}
83
102
84
- func (podPredicate ) Update (e event.UpdateEvent ) bool {
85
- return matches (e .ObjectNew )
86
- }
103
+ func (r * PodReconciler ) getPodLog (ctx context.Context , namespace string , name string , containerName string ) (string , error ) {
104
+ podLogOpts := corev1.PodLogOptions {
105
+ Container : containerName ,
106
+ }
107
+ req := r .coreClient .Pods (namespace ).GetLogs (name , & podLogOpts )
108
+ podLogs , err := req .Stream (ctx )
109
+ if err != nil {
110
+ return "" , err
111
+ }
112
+ defer func () { _ = podLogs .Close () }()
87
113
88
- func (podPredicate ) Delete (e event.DeleteEvent ) bool {
89
- return matches (e .Object )
90
- }
114
+ buf := new (bytes.Buffer )
115
+ if _ , err = io .Copy (buf , podLogs ); err != nil {
116
+ return "" , err
117
+ }
118
+ str := buf .String ()
91
119
92
- func (podPredicate ) Generic (e event.GenericEvent ) bool {
93
- return matches (e .Object )
120
+ return str , nil
94
121
}
95
122
96
- func matches (m metav1.Object ) bool {
97
- return m .GetLabels ()[LabelExecutionID ] != "" && m .GetLabels ()[LabelOwner ] != ""
123
+ func (r * PodReconciler ) savePodLog (executionID string , name string , data string ) (string , error ) {
124
+ if err := r .Controller .Config ().MkReportDir (executionID ); err != nil {
125
+ return "" , err
126
+ }
127
+ fileName := r .Controller .Config ().ReportFileName (executionID , fmt .Sprintf ("container-%s.log" , name ))
128
+ return fileName , ioutil .WriteFile (fileName , []byte (data ), 0o600 )
98
129
}
0 commit comments