Skip to content

Commit 2d67198

Browse files
fix(cli/capture): remove global Logger, inject via dependency injection (issue #585)
Removes the global var Logger *log.ZapLogger from cli/cmd/root.go and replaces all usages across the capture package with proper dependency injection. Each sub-command gets its own named child logger: - NewCreateSubCommand -> .Named("retina-capture-create") - NewDeleteSubCommand -> .Named("retina-capture-delete") - NewDownloadSubCommand -> .Named("retina-capture-download") All code comments in create.go, delete.go, and download.go are preserved. Unit tests added to pkg/log/zap_test.go verifying the logger= field appears in structured log output (TestNamedLoggerContext, TestNamedLoggerContextDeleteCmd). Kind cluster test confirms injected logger flows through all capture call sites without nil pointer dereference. Closes #585 Signed-off-by: Sudheer Obbu <mail2sudheerobbu@gmail.com>
1 parent 1c29d9c commit 2d67198

7 files changed

Lines changed: 119 additions & 55 deletions

File tree

cli/cmd/capture/capture.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package capture
66
import (
77
"time"
88

9+
"github.com/microsoft/retina/pkg/log"
910
"github.com/spf13/cobra"
1011
"k8s.io/cli-runtime/pkg/genericclioptions"
1112
"k8s.io/client-go/kubernetes"
@@ -39,6 +40,7 @@ type Opts struct {
3940
s3Region string
4041
s3SecretAccessKey string
4142
tcpdumpFilter string
43+
logger *log.ZapLogger
4244
}
4345

4446
var opts = Opts{

cli/cmd/capture/create.go

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ import (
2323
"k8s.io/kubectl/pkg/util/i18n"
2424
"k8s.io/kubectl/pkg/util/templates"
2525

26-
retinacmd "github.com/microsoft/retina/cli/cmd"
2726
retinav1alpha1 "github.com/microsoft/retina/crd/api/v1alpha1"
2827
"github.com/microsoft/retina/internal/buildinfo"
2928
pkgcapture "github.com/microsoft/retina/pkg/capture"
3029
captureConstants "github.com/microsoft/retina/pkg/capture/constants"
3130
"github.com/microsoft/retina/pkg/capture/file"
3231
captureUtils "github.com/microsoft/retina/pkg/capture/utils"
3332
"github.com/microsoft/retina/pkg/config"
33+
"github.com/microsoft/retina/pkg/log"
3434
)
3535

3636
const (
@@ -109,47 +109,47 @@ func create(kubeClient kubernetes.Interface) error {
109109

110110
jobsCreated, err := createJobs(ctx, kubeClient, capture)
111111
if err != nil {
112-
retinacmd.Logger.Error("Failed to create job", zap.Error(err))
112+
opts.logger.Error("Failed to create job", zap.Error(err))
113113
return err
114114
}
115115
if opts.nowait {
116-
retinacmd.Logger.Info("Please manually delete all capture jobs")
116+
opts.logger.Info("Please manually delete all capture jobs")
117117
if capture.Spec.OutputConfiguration.BlobUpload != nil {
118-
retinacmd.Logger.Info("Please manually delete capture secret", zap.String("namespace", *opts.Namespace), zap.String("secret name", *capture.Spec.OutputConfiguration.BlobUpload))
118+
opts.logger.Info("Please manually delete capture secret", zap.String("namespace", *opts.Namespace), zap.String("secret name", *capture.Spec.OutputConfiguration.BlobUpload))
119119
}
120120
if capture.Spec.OutputConfiguration.S3Upload != nil && capture.Spec.OutputConfiguration.S3Upload.SecretName != "" {
121-
retinacmd.Logger.Info("Please manually delete capture secret", zap.String("namespace", *opts.Namespace), zap.String("secret name", capture.Spec.OutputConfiguration.S3Upload.SecretName))
121+
opts.logger.Info("Please manually delete capture secret", zap.String("namespace", *opts.Namespace), zap.String("secret name", capture.Spec.OutputConfiguration.S3Upload.SecretName))
122122
}
123123
printCaptureResult(jobsCreated)
124124
return nil
125125
}
126126

127127
// Wait until all jobs finish then delete the jobs before the timeout, otherwise print jobs created to
128128
// let the customer recycle them.
129-
retinacmd.Logger.Info("Waiting for capture jobs to finish")
129+
opts.logger.Info("Waiting for capture jobs to finish")
130130

131131
allJobsCompleted := waitUntilJobsComplete(ctx, kubeClient, jobsCreated)
132132

133133
// Delete all jobs created only if they all completed, otherwise keep the jobs for debugging.
134134
if allJobsCompleted {
135-
retinacmd.Logger.Info("Deleting jobs as all jobs are completed")
135+
opts.logger.Info("Deleting jobs as all jobs are completed")
136136
jobsFailedToDelete := deleteJobs(ctx, kubeClient, jobsCreated)
137137
if len(jobsFailedToDelete) != 0 {
138-
retinacmd.Logger.Info("Please manually delete capture jobs failed to delete", zap.String("namespace", *opts.Namespace), zap.String("job list", strings.Join(jobsFailedToDelete, ",")))
138+
opts.logger.Info("Please manually delete capture jobs failed to delete", zap.String("namespace", *opts.Namespace), zap.String("job list", strings.Join(jobsFailedToDelete, ",")))
139139
}
140140

141141
if capture.Spec.OutputConfiguration.BlobUpload != nil {
142142
err = deleteSecret(ctx, kubeClient, capture.Spec.OutputConfiguration.BlobUpload)
143143
if err != nil {
144-
retinacmd.Logger.Error("Failed to delete capture secret, please manually delete it",
144+
opts.logger.Error("Failed to delete capture secret, please manually delete it",
145145
zap.String("namespace", *opts.Namespace), zap.String("secret name", *capture.Spec.OutputConfiguration.BlobUpload), zap.Error(err))
146146
}
147147
}
148148

149149
if capture.Spec.OutputConfiguration.S3Upload != nil && capture.Spec.OutputConfiguration.S3Upload.SecretName != "" {
150150
err = deleteSecret(ctx, kubeClient, &capture.Spec.OutputConfiguration.S3Upload.SecretName)
151151
if err != nil {
152-
retinacmd.Logger.Error("Failed to delete capture secret, please manually delete it",
152+
opts.logger.Error("Failed to delete capture secret, please manually delete it",
153153
zap.String("namespace", *opts.Namespace),
154154
zap.String("secret name", capture.Spec.OutputConfiguration.S3Upload.SecretName),
155155
zap.Error(err),
@@ -158,13 +158,13 @@ func create(kubeClient kubernetes.Interface) error {
158158
}
159159

160160
if len(jobsFailedToDelete) == 0 && err == nil {
161-
retinacmd.Logger.Info("Done for deleting jobs")
161+
opts.logger.Info("Done for deleting jobs")
162162
}
163163
return nil
164164
}
165165

166-
retinacmd.Logger.Info("Not all job are completed in the given time")
167-
retinacmd.Logger.Info("Please manually delete the Capture")
166+
opts.logger.Info("Not all job are completed in the given time")
167+
opts.logger.Info("Please manually delete the Capture")
168168

169169
return getCaptureAndPrintCaptureResult(ctx, kubeClient, capture.Name, *opts.Namespace)
170170
}
@@ -191,6 +191,7 @@ func NewCreateSubCommand(kubeClient kubernetes.Interface) *cobra.Command {
191191
}
192192

193193
createCapture.RunE = func(*cobra.Command, []string) error {
194+
opts.logger = log.Logger().Named("retina-capture-create")
194195
return create(kubeClient)
195196
}
196197

@@ -299,10 +300,10 @@ func createCaptureF(ctx context.Context, kubeClient kubernetes.Interface) (*reti
299300
},
300301
}
301302

302-
retinacmd.Logger.Info(fmt.Sprintf("Capture timestamp: %s", timestamp))
303+
opts.logger.Info(fmt.Sprintf("Capture timestamp: %s", timestamp))
303304

304305
if opts.duration != 0 {
305-
retinacmd.Logger.Info(fmt.Sprintf("The capture duration is set to %s", opts.duration))
306+
opts.logger.Info(fmt.Sprintf("The capture duration is set to %s", opts.duration))
306307
capture.Spec.CaptureConfiguration.CaptureOption.Duration = &metav1.Duration{Duration: opts.duration}
307308
}
308309

@@ -366,17 +367,17 @@ func createCaptureF(ctx context.Context, kubeClient kubernetes.Interface) (*reti
366367
for i := range podNameSlice {
367368
podNameSlice[i] = strings.TrimSpace(podNameSlice[i])
368369
}
369-
retinacmd.Logger.Info(fmt.Sprintf("Capturing on specific pods: %v", podNameSlice))
370+
opts.logger.Info(fmt.Sprintf("Capturing on specific pods: %v", podNameSlice))
370371
capture.Spec.CaptureConfiguration.CaptureTarget.PodNames = podNameSlice
371372
}
372373

373374
if opts.maxSize != 0 {
374-
retinacmd.Logger.Info(fmt.Sprintf("The capture file max size is set to %dMB", opts.maxSize))
375+
opts.logger.Info(fmt.Sprintf("The capture file max size is set to %dMB", opts.maxSize))
375376
capture.Spec.CaptureConfiguration.CaptureOption.MaxCaptureSize = &opts.maxSize
376377
}
377378

378379
if opts.packetSize != 0 {
379-
retinacmd.Logger.Info(fmt.Sprintf("The capture packet size is set to %d bytes", opts.packetSize))
380+
opts.logger.Info(fmt.Sprintf("The capture packet size is set to %d bytes", opts.packetSize))
380381
capture.Spec.CaptureConfiguration.CaptureOption.PacketSize = &opts.packetSize
381382
}
382383

@@ -385,7 +386,7 @@ func createCaptureF(ctx context.Context, kubeClient kubernetes.Interface) (*reti
385386
for i := range interfaceSlice {
386387
interfaceSlice[i] = strings.TrimSpace(interfaceSlice[i])
387388
}
388-
retinacmd.Logger.Info(fmt.Sprintf("Capturing on specific interfaces: %v", interfaceSlice))
389+
opts.logger.Info(fmt.Sprintf("Capturing on specific interfaces: %v", interfaceSlice))
389390
capture.Spec.CaptureConfiguration.CaptureOption.Interfaces = interfaceSlice
390391
}
391392

@@ -447,7 +448,7 @@ func getCLICaptureConfig() config.CaptureConfig {
447448
}
448449

449450
func createJobs(ctx context.Context, kubeClient kubernetes.Interface, capture *retinav1alpha1.Capture) ([]batchv1.Job, error) {
450-
translator := pkgcapture.NewCaptureToPodTranslator(kubeClient, retinacmd.Logger, getCLICaptureConfig())
451+
translator := pkgcapture.NewCaptureToPodTranslator(kubeClient, opts.logger, getCLICaptureConfig())
451452
jobs, err := translator.TranslateCaptureToJobs(ctx, capture)
452453
if err != nil {
453454
return nil, err
@@ -460,7 +461,7 @@ func createJobs(ctx context.Context, kubeClient kubernetes.Interface, capture *r
460461
return nil, err
461462
}
462463
jobsCreated = append(jobsCreated, *jobCreated)
463-
retinacmd.Logger.Info("Packet capture job is created", zap.String("namespace", *opts.Namespace), zap.String("capture job", jobCreated.Name))
464+
opts.logger.Info("Packet capture job is created", zap.String("namespace", *opts.Namespace), zap.String("capture job", jobCreated.Name))
464465
}
465466
return jobsCreated, nil
466467
}
@@ -479,7 +480,7 @@ func waitUntilJobsComplete(ctx context.Context, kubeClient kubernetes.Interface,
479480
if period < opts.duration/10 {
480481
period = opts.duration / 10
481482
}
482-
retinacmd.Logger.Info(fmt.Sprintf("Waiting timeout is set to %s", deadline))
483+
opts.logger.Info(fmt.Sprintf("Waiting timeout is set to %s", deadline))
483484

484485
ctx, cancel := context.WithTimeout(ctx, deadline)
485486
defer cancel()
@@ -491,7 +492,7 @@ func waitUntilJobsComplete(ctx context.Context, kubeClient kubernetes.Interface,
491492
for _, job := range jobs {
492493
jobRet, err := kubeClient.BatchV1().Jobs(job.Namespace).Get(ctx, job.Name, metav1.GetOptions{})
493494
if err != nil {
494-
retinacmd.Logger.Error("Failed to get job", zap.String("namespace", job.Namespace), zap.String("job name", job.Name), zap.Error(err))
495+
opts.logger.Error("Failed to get job", zap.String("namespace", job.Namespace), zap.String("job name", job.Name), zap.Error(err))
495496
jobsIncompleted = append(jobsIncompleted, job.Name)
496497
continue
497498
}
@@ -503,7 +504,7 @@ func waitUntilJobsComplete(ctx context.Context, kubeClient kubernetes.Interface,
503504
}
504505

505506
if len(jobsIncompleted) != 0 {
506-
retinacmd.Logger.Info("Not all jobs are completed",
507+
opts.logger.Info("Not all jobs are completed",
507508
zap.String("namespace", *opts.Namespace),
508509
zap.String("Completed jobs", strings.Join(jobsCompleted, ",")),
509510
zap.String("Uncompleted packet capture jobs", strings.Join(jobsIncompleted, ",")),
@@ -529,7 +530,7 @@ func deleteJobs(ctx context.Context, kubeClient kubernetes.Interface, jobs []bat
529530
})
530531
if err != nil {
531532
jobsFailedtoDelete = append(jobsFailedtoDelete, job.Name)
532-
retinacmd.Logger.Error("Failed to delete job", zap.String("namespace", job.Namespace), zap.String("job name", job.Name), zap.Error(err))
533+
opts.logger.Error("Failed to delete job", zap.String("namespace", job.Namespace), zap.String("job name", job.Name), zap.Error(err))
533534
}
534535
}
535536
return jobsFailedtoDelete

cli/cmd/capture/delete.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import (
99
"os/signal"
1010
"syscall"
1111

12-
retinacmd "github.com/microsoft/retina/cli/cmd"
1312
captureConstants "github.com/microsoft/retina/pkg/capture/constants"
1413
"github.com/microsoft/retina/pkg/label"
14+
"github.com/microsoft/retina/pkg/log"
1515
"github.com/pkg/errors"
1616
"github.com/spf13/cobra"
1717
"go.uber.org/zap"
@@ -33,6 +33,7 @@ func NewDeleteSubCommand(kubeClient kubernetes.Interface) *cobra.Command {
3333
Short: "Delete a Retina capture",
3434
Example: deleteExample,
3535
RunE: func(*cobra.Command, []string) error {
36+
opts.logger = log.Logger().Named("retina-capture-delete")
3637
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM)
3738
defer cancel()
3839

@@ -70,7 +71,7 @@ func NewDeleteSubCommand(kubeClient kubernetes.Interface) *cobra.Command {
7071
if err := kubeClient.BatchV1().Jobs(jobList.Items[idx].Namespace).Delete(ctx, jobList.Items[idx].Name, metav1.DeleteOptions{
7172
PropagationPolicy: &deletePropagationBackground,
7273
}); err != nil {
73-
retinacmd.Logger.Info("Failed to delete job", zap.String("job name", jobList.Items[idx].Name), zap.Error(err))
74+
opts.logger.Info("Failed to delete job", zap.String("job name", jobList.Items[idx].Name), zap.Error(err))
7475
}
7576
}
7677

@@ -82,7 +83,7 @@ func NewDeleteSubCommand(kubeClient kubernetes.Interface) *cobra.Command {
8283
break
8384
}
8485
}
85-
retinacmd.Logger.Info(fmt.Sprintf("Retina Capture %q delete", *opts.Name))
86+
opts.logger.Info(fmt.Sprintf("Retina Capture %q delete", *opts.Name))
8687

8788
return nil
8889
},

0 commit comments

Comments
 (0)