11package controller
22
33import (
4+ "bytes"
45 "context"
56 "errors"
7+ "fmt"
8+ "io"
9+ "io/ioutil"
610
711 "github.com/bakito/batch-job-controller/pkg/lifecycle"
12+ "github.com/go-logr/logr"
813 corev1 "k8s.io/api/core/v1"
914 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"
1117 ctrl "sigs.k8s.io/controller-runtime"
1218 "sigs.k8s.io/controller-runtime/pkg/client"
13- "sigs.k8s.io/controller-runtime/pkg/event"
1419 "sigs.k8s.io/controller-runtime/pkg/handler"
1520 "sigs.k8s.io/controller-runtime/pkg/log"
1621 "sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -27,11 +32,17 @@ const (
2732// PodReconciler reconciler
2833type PodReconciler struct {
2934 client.Client
35+ coreClient corev1client.CoreV1Interface
3036 Controller lifecycle.Controller
3137}
3238
3339// SetupWithManager setup
3440func (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 ()
3546 return ctrl .NewControllerManagedBy (mgr ).
3647 For (& corev1.Pod {}).
3748 Watches (& source.Kind {Type : & corev1.Pod {}}, & handler.EnqueueRequestForObject {}).
@@ -59,40 +70,60 @@ func (r *PodReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.R
5970 executionID := pod .GetLabels ()[LabelExecutionID ]
6071 node := pod .Spec .NodeName
6172
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+ }
7282 }
7383 }
7484
7585 return reconcile.Result {}, nil
7686}
7787
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+ }
82101}
83102
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 () }()
87113
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 ()
91119
92- func (podPredicate ) Generic (e event.GenericEvent ) bool {
93- return matches (e .Object )
120+ return str , nil
94121}
95122
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 )
98129}
0 commit comments