9da206dc7c
PVCs and CloudNativePG Clusters need S3 buckets and backup schedules
provisioned consistently. This operator watches the
backups.unkin.net/{schedule,destination} annotations on those objects and
provisions everything needed to back them up, with no new CRDs.
- Add a PVC controller that provisions cephrgw ObjectStoreUser/Bucket/BucketAccess,
auto-generates a restic repo-password Secret and creates a k8up Schedule scoped
to the PVC via spec.backup.volumes[].persistentVolumeClaim.claimName.
- Add a CNPG Cluster controller that provisions the same bucket stack, idempotently
patches spec.backup.barmanObjectStore (leaving a user-set destinationPath alone
with a Warning event) and creates a ScheduledBackup.
- Resolve destinations through a ConfigMap lookup table; requeue until the
BucketAccess is Ready before creating schedule resources; own-reference created
resources and retain bucket data by default.
- Add schedule-mapping helpers (k8up 5-field/shortcut pass-through, CNPG 6-field
seconds-first) and deterministic, length-bounded name derivation.
- Add unit tests (schedule mapping, name derivation, destination resolution) and
envtest controller tests for both paths, wiring the external CRDs into envtest.
- Add kubebuilder-generated RBAC, a Dockerfile (distroless/nonroot), Woodpecker
lint/test/build pipelines and a tag-triggered image push to the artifactapi
docker-internal registry, plus a version-bump Makefile and deploy manifests.
108 lines
2.7 KiB
Markdown
108 lines
2.7 KiB
Markdown
Zapr :zap:
|
|
==========
|
|
|
|
A [logr](https://github.com/go-logr/logr) implementation using
|
|
[Zap](https://github.com/uber-go/zap). Can also be used as
|
|
[slog](https://pkg.go.dev/log/slog) handler.
|
|
|
|
Usage
|
|
-----
|
|
|
|
Via logr:
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"go.uber.org/zap"
|
|
"github.com/go-logr/logr"
|
|
"github.com/go-logr/zapr"
|
|
)
|
|
|
|
func main() {
|
|
var log logr.Logger
|
|
|
|
zapLog, err := zap.NewDevelopment()
|
|
if err != nil {
|
|
panic(fmt.Sprintf("who watches the watchmen (%v)?", err))
|
|
}
|
|
log = zapr.NewLogger(zapLog)
|
|
|
|
log.Info("Logr in action!", "the answer", 42)
|
|
}
|
|
```
|
|
|
|
Via slog:
|
|
|
|
```
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
|
|
"github.com/go-logr/logr/slogr"
|
|
"github.com/go-logr/zapr"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func main() {
|
|
var log *slog.Logger
|
|
|
|
zapLog, err := zap.NewDevelopment()
|
|
if err != nil {
|
|
panic(fmt.Sprintf("who watches the watchmen (%v)?", err))
|
|
}
|
|
log = slog.New(slogr.NewSlogHandler(zapr.NewLogger(zapLog)))
|
|
|
|
log.Info("Logr in action!", "the answer", 42)
|
|
}
|
|
```
|
|
|
|
Increasing Verbosity
|
|
--------------------
|
|
|
|
Zap uses semantically named levels for logging (`DebugLevel`, `InfoLevel`,
|
|
`WarningLevel`, ...). Logr uses arbitrary numeric levels. By default logr's
|
|
`V(0)` is zap's `InfoLevel` and `V(1)` is zap's `DebugLevel` (which is
|
|
numerically -1). Zap does not have named levels that are more verbose than
|
|
`DebugLevel`, but it's possible to fake it.
|
|
|
|
As of zap v1.19.0 you can do something like the following in your setup code:
|
|
|
|
```go
|
|
zc := zap.NewProductionConfig()
|
|
zc.Level = zap.NewAtomicLevelAt(zapcore.Level(-2))
|
|
z, err := zc.Build()
|
|
if err != nil {
|
|
// ...
|
|
}
|
|
log := zapr.NewLogger(z)
|
|
```
|
|
|
|
Zap's levels get more verbose as the number gets smaller and more important and
|
|
the number gets larger (`DebugLevel` is -1, `InfoLevel` is 0, `WarnLevel` is 1,
|
|
and so on).
|
|
|
|
The `-2` in the above snippet means that `log.V(2).Info()` calls will be active.
|
|
`-3` would enable `log.V(3).Info()`, etc. Note that zap's levels are `int8`
|
|
which means the most verbose level you can give it is -128. The zapr
|
|
implementation will cap `V()` levels greater than 127 to 127, so setting the
|
|
zap level to -128 really means "activate all logs".
|
|
|
|
Implementation Details
|
|
----------------------
|
|
|
|
For the most part, concepts in Zap correspond directly with those in logr.
|
|
|
|
Unlike Zap, all fields *must* be in the form of sugared fields --
|
|
it's illegal to pass a strongly-typed Zap field in a key position to any
|
|
of the logging methods (`Log`, `Error`).
|
|
|
|
The zapr `logr.LogSink` implementation also implements `logr.SlogHandler`. That
|
|
enables `slogr.NewSlogHandler` to provide a `slog.Handler` which just passes
|
|
parameters through to zapr. zapr handles special slog values (Group,
|
|
LogValuer), regardless of which front-end API is used.
|