Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement structured logging #1438

Merged
merged 6 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 18 additions & 3 deletions cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import (
goflag "flag"
"fmt"
"io"
"log"
"log/slog"
"os"
"time"

flag "github.com/spf13/pflag"

"github.com/bitnami-labs/sealed-secrets/pkg/controller"
"github.com/bitnami-labs/sealed-secrets/pkg/flagenv"
"github.com/bitnami-labs/sealed-secrets/pkg/log"
"github.com/bitnami-labs/sealed-secrets/pkg/pflagenv"

ssv1alpha1 "github.com/bitnami-labs/sealed-secrets/pkg/apis/sealedsecrets/v1alpha1"
Expand Down Expand Up @@ -52,6 +53,8 @@ func bindControllerFlags(f *controller.Flags, fs *flag.FlagSet) {
fs.BoolVar(&f.SkipRecreate, "skip-recreate", false, "if true the controller will skip listening for managed secret changes to recreate them. This helps on limited permission environments.")

fs.BoolVar(&f.LogInfoToStdout, "log-info-stdout", false, "if true the controller will log info to stdout.")
fs.StringVar(&f.LogLevel, "log-level", "INFO", "Log level (INFO|ERROR).")
fs.StringVar(&f.LogFormat, "log-format", "text", "Log format (text|json).")

fs.DurationVar(&f.KeyRenewPeriod, "rotate-period", defaultKeyRenewPeriod, "")
_ = fs.MarkDeprecated("rotate-period", "please use key-renew-period instead")
Expand Down Expand Up @@ -85,14 +88,26 @@ func mainE(w io.Writer, fs *flag.FlagSet, gofs *goflag.FlagSet, args []string) e
return err
}

// Set logging
logLevel := slog.Level(0)
logLevel.UnmarshalText([]byte(flags.LogLevel))
opts := &slog.HandlerOptions{
Level: logLevel,
}
if flags.LogInfoToStdout {
slog.SetDefault(slog.New(log.New(os.Stdout, os.Stderr, flags.LogFormat, opts)))
} else {
slog.SetDefault(slog.New(log.New(os.Stderr, os.Stderr, flags.LogFormat, opts)))
}

ssv1alpha1.AcceptDeprecatedV1Data = flags.AcceptV1Data

fmt.Fprintf(w, "controller version: %s\n", VERSION)
if printVersion {
fmt.Fprintf(w, "controller version: %s\n", VERSION)
return nil
}

log.Printf("Starting sealed-secrets controller version: %s\n", VERSION)
slog.Info("Starting sealed-secrets controller", "version", VERSION)
if err := controller.Main(&flags, VERSION); err != nil {
panic(err)
}
Expand Down
2 changes: 2 additions & 0 deletions helm/sealed-secrets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ The command removes all the Kubernetes components associated with the chart and
| `privateKeyAnnotations` | Map of annotations to be set on the sealing keypairs | `{}` |
| `privateKeyLabels` | Map of labels to be set on the sealing keypairs | `{}` |
| `logInfoStdout` | Specifies whether the Sealed Secrets controller will log info to stdout | `false` |
| `logLevel` | Specifies log level of controller (INFO,ERROR) | `""` |
| `logFormat` | Specifies log format (text,json) | `""` |
| `command` | Override default container command | `[]` |
| `args` | Override default container args | `[]` |
| `livenessProbe.enabled` | Enable livenessProbe on Sealed Secret containers | `true` |
Expand Down
8 changes: 8 additions & 0 deletions helm/sealed-secrets/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ spec:
{{- if .Values.logInfoStdout }}
- --log-info-stdout
{{- end }}
{{- if .Values.logLevel }}
- --log-level
- {{ .Values.logLevel }}
{{- end }}
{{- if .Values.logFormat }}
- --log-format
- {{ .Values.logFormat }}
{{- end }}
{{- end }}
image: {{ printf "%s/%s:%s" .Values.image.registry .Values.image.repository .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
Expand Down
6 changes: 6 additions & 0 deletions helm/sealed-secrets/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ privateKeyLabels: {}
## @param logInfoStdout Specifies whether the Sealed Secrets controller will log info to stdout
##
logInfoStdout: false
## @param logLevel Specifies log level of controller (INFO,ERROR)
##
logLevel: ""
## @param logFormat Specifies log format (text,json)
##
logFormat: ""
## @param command Override default container command
##
command: []
Expand Down
33 changes: 18 additions & 15 deletions pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"reflect"
"strings"
"time"
Expand Down Expand Up @@ -33,7 +34,6 @@ import (
ssscheme "github.com/bitnami-labs/sealed-secrets/pkg/client/clientset/versioned/scheme"
ssv1alpha1client "github.com/bitnami-labs/sealed-secrets/pkg/client/clientset/versioned/typed/sealedsecrets/v1alpha1"
ssinformer "github.com/bitnami-labs/sealed-secrets/pkg/client/informers/externalversions"
"github.com/bitnami-labs/sealed-secrets/pkg/log"
"github.com/bitnami-labs/sealed-secrets/pkg/multidocyaml"
)

Expand Down Expand Up @@ -82,7 +82,10 @@ func NewController(clientset kubernetes.Interface, ssclientset ssclientset.Inter

utilruntime.Must(ssscheme.AddToScheme(scheme.Scheme))
eventBroadcaster := record.NewBroadcaster()
eventBroadcaster.StartLogging(log.Infof)
eventBroadcaster.StartLogging(func(format string, args ...interface{}) {
// Must use Sprintf to ensure slog doesn't interpret args... as key-value pairs
slog.Info(fmt.Sprintf(format, args...))
})
eventBroadcaster.StartRecordingToSink(&v1.EventSinkImpl{Interface: clientset.CoreV1().Events("")})
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "sealed-secrets"})

Expand Down Expand Up @@ -125,7 +128,7 @@ func watchSealedSecrets(ssinformer ssinformer.SharedInformerFactory, queue workq
if sealedSecretChanged(oldObj, newObj) {
queue.Add(key)
} else {
log.Infof("update suppressed, no changes in sealed secret spec of %v", key)
slog.Info("update suppressed, no changes in spec", "sealed-secret", key)
}
}
},
Expand Down Expand Up @@ -163,20 +166,20 @@ func watchSecrets(sinformer informers.SharedInformerFactory, ssclientset ssclien
DeleteFunc: func(obj interface{}) {
skey, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
if err != nil {
log.Errorf("failed to fetch Secret key: %v", err)
slog.Error("failed to fetch Secret key", "error", err)
return
}

ns, name, err := cache.SplitMetaNamespaceKey(skey)
if err != nil {
log.Errorf("failed to get namespace and name from key: %v", err)
slog.Error("failed to get namespace and name from key", "secret", skey, "error", err)
return
}

ssecret, err := ssclientset.BitnamiV1alpha1().SealedSecrets(ns).Get(context.Background(), name, metav1.GetOptions{})
if err != nil {
if !k8serrors.IsNotFound(err) {
log.Errorf("failed to get SealedSecret: %v", err)
slog.Error("failed to get SealedSecret", "secret", skey, "error", err)
}
return
}
Expand All @@ -187,7 +190,7 @@ func watchSecrets(sinformer informers.SharedInformerFactory, ssclientset ssclien

sskey, err := cache.MetaNamespaceKeyFunc(ssecret)
if err != nil {
log.Errorf("failed to fetch SealedSecret key: %v", err)
slog.Error("failed to fetch SealedSecret key", "secret", skey, "error", err)
return
}

Expand Down Expand Up @@ -242,7 +245,7 @@ func (c *Controller) Run(stopCh <-chan struct{}) {
c.runWorker(context.Background())
}, time.Second, stopCh)

log.Errorf("Shutting down controller")
slog.Error("Shutting down controller")
}

func (c *Controller) runWorker(ctx context.Context) {
Expand All @@ -264,15 +267,15 @@ func (c *Controller) processNextItem(ctx context.Context) bool {
c.queue.Forget(key)
} else if isImmutableError(err) {
// Do not retry updating immutable fields of an immutable secret
log.Errorf(formatImmutableError(key.(string)))
slog.Error(formatImmutableError(key.(string)))
c.queue.Forget(key)
utilruntime.HandleError(err)
} else if c.queue.NumRequeues(key) < maxRetries {
log.Errorf("Error updating %s, will retry: %v", key, err)
slog.Error("Error updating, will retry", "key", key, "error", err)
c.queue.AddRateLimited(key)
} else {
// err != nil and too many retries
log.Errorf("Error updating %s, giving up: %v", key, err)
slog.Error("Error updating, giving up", "key", key, "error", err)
c.queue.Forget(key)
utilruntime.HandleError(err)
}
Expand All @@ -284,7 +287,7 @@ func (c *Controller) unseal(ctx context.Context, key string) (unsealErr error) {
unsealRequestsTotal.Inc()
obj, exists, err := c.ssInformer.GetIndexer().GetByKey(key)
if err != nil {
log.Errorf("Error fetching object with key %s from store: %v", key, err)
slog.Error("Error fetching object from store", "key", key, "error", err)
unsealErrorsTotal.WithLabelValues("fetch", "").Inc()
return err
}
Expand All @@ -295,7 +298,7 @@ func (c *Controller) unseal(ctx context.Context, key string) (unsealErr error) {

// TODO: remove this feature flag in a subsequent release.
if c.oldGCBehavior {
log.Infof("SealedSecret %s has gone, deleting Secret", key)
slog.Info("SealedSecret has gone, deleting Secret", "sealed-secret", key)
ns, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
return err
Expand All @@ -312,7 +315,7 @@ func (c *Controller) unseal(ctx context.Context, key string) (unsealErr error) {
if err != nil {
return err
}
log.Infof("Updating %s", key)
slog.Info("Updating", "key", key)

// any exit of this function at this point will cause an update to the status subresource
// of the SealedSecret custom resource. The return value of the unseal function is available
Expand All @@ -321,7 +324,7 @@ func (c *Controller) unseal(ctx context.Context, key string) (unsealErr error) {
defer func() {
if err := c.updateSealedSecretStatus(ssecret, unsealErr); err != nil {
// Non-fatal. Log and continue.
log.Errorf("Error updating SealedSecret %s status: %v", key, err)
slog.Error("Error updating SealedSecret status", "sealed-secret", key, "error", err)
unsealErrorsTotal.WithLabelValues("status", ssecret.GetNamespace()).Inc()
} else {
ObserveCondition(ssecret)
Expand Down
6 changes: 3 additions & 3 deletions pkg/controller/keyregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"log/slog"
"sync"
"time"

"github.com/bitnami-labs/sealed-secrets/pkg/crypto"
"github.com/bitnami-labs/sealed-secrets/pkg/log"
"k8s.io/client-go/kubernetes"
certUtil "k8s.io/client-go/util/cert"
)
Expand Down Expand Up @@ -61,8 +61,8 @@ func (kr *KeyRegistry) generateKey(ctx context.Context, validFor time.Duration,
if err := kr.registerNewKey(generatedName, key, cert, time.Now()); err != nil {
return "", err
}
log.Infof("New key written to %s/%s\n", kr.namespace, generatedName)
log.Infof("Certificate is \n%s\n", pem.EncodeToMemory(&pem.Block{Type: certUtil.CertificateBlockType, Bytes: cert.Raw}))
slog.Info("New key written", "namespace", kr.namespace, "name", generatedName)
slog.Info("Certificate generated", "certificate", pem.EncodeToMemory(&pem.Block{Type: certUtil.CertificateBlockType, Bytes: cert.Raw}))
return generatedName, nil
}

Expand Down
20 changes: 9 additions & 11 deletions pkg/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/rand"
"crypto/x509"
"io"
"log/slog"
"os"
"os/signal"
"sort"
Expand All @@ -25,7 +26,6 @@ import (
"github.com/bitnami-labs/sealed-secrets/pkg/client/clientset/versioned"
sealedsecrets "github.com/bitnami-labs/sealed-secrets/pkg/client/clientset/versioned"
ssinformers "github.com/bitnami-labs/sealed-secrets/pkg/client/informers/externalversions"
"github.com/bitnami-labs/sealed-secrets/pkg/log"
)

var (
Expand All @@ -51,6 +51,8 @@ type Flags struct {
UpdateStatus bool
SkipRecreate bool
LogInfoToStdout bool
LogLevel string
LogFormat string
PrivateKeyAnnotations string
PrivateKeyLabels string
}
Expand All @@ -60,7 +62,7 @@ func initKeyPrefix(keyPrefix string) (string, error) {
}

func initKeyRegistry(ctx context.Context, client kubernetes.Interface, r io.Reader, namespace, prefix, label string, keysize int) (*KeyRegistry, error) {
log.Infof("Searching for existing private keys")
slog.Info("Searching for existing private keys")
secretList, err := client.CoreV1().Secrets(namespace).List(ctx, metav1.ListOptions{
LabelSelector: keySelector.String(),
})
Expand All @@ -83,12 +85,11 @@ func initKeyRegistry(ctx context.Context, client kubernetes.Interface, r io.Read
for _, secret := range items {
key, certs, err := readKey(secret)
if err != nil {
log.Errorf("Error reading key %s: %v", secret.Name, err)
slog.Error("Error reading key", "secret", secret.Name, "error", err)
}
if err := keyRegistry.registerNewKey(secret.Name, key, certs[0], certs[0].NotBefore); err != nil {
return nil, err
}
log.Infof("----- %s", secret.Name)
}
return keyRegistry, nil
}
Expand Down Expand Up @@ -123,7 +124,7 @@ func initKeyRenewal(ctx context.Context, registry *KeyRegistry, period, validFor
// wrapper function to log error thrown by generateKey function
keyGenFunc := func() {
if _, err := registry.generateKey(ctx, validFor, cn, privateKeyAnnotations, privateKeyLabels); err != nil {
log.Errorf("Failed to generate new key : %v\n", err)
slog.Error("Failed to generate new key", "error", err)
}
}
if period == 0 {
Expand All @@ -142,9 +143,6 @@ func initKeyRenewal(ctx context.Context, registry *KeyRegistry, period, validFor

func Main(f *Flags, version string) error {
registerMetrics(version)
if f.LogInfoToStdout {
log.SetInfoToStdout()
}

config, err := rest.InClusterConfig()
if err != nil {
Expand Down Expand Up @@ -193,7 +191,7 @@ func Main(f *Flags, version string) error {
namespace := v1.NamespaceAll
if !f.NamespaceAll || f.AdditionalNamespaces != "" {
namespace = myNamespace()
log.Infof("Starting informer for namespace: %s\n", namespace)
slog.Info("Starting informer", "namespace", namespace)
}

var tweakopts func(*metav1.ListOptions) = nil
Expand Down Expand Up @@ -221,7 +219,7 @@ func Main(f *Flags, version string) error {
for _, ns := range addNS {
if _, err := clientset.CoreV1().Namespaces().Get(ctx, ns, metav1.GetOptions{}); err != nil {
if errors.IsNotFound(err) {
log.Errorf("Warning: namespace '%s' doesn't exist\n", ns)
slog.Error("namespace doesn't exist", "namespace", ns)
continue
}
return err
Expand All @@ -233,7 +231,7 @@ func Main(f *Flags, version string) error {
}
ctlr.oldGCBehavior = f.OldGCBehavior
ctlr.updateStatus = f.UpdateStatus
log.Infof("Starting informer for namespace: %s\n", ns)
slog.Info("Starting informer", "namespace", ns)
go ctlr.Run(stop)
}
}
Expand Down