Implement autobackup-operator controllers, tests, CI and packaging
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.
This commit is contained in:
+92
@@ -0,0 +1,92 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package jsonutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// nullJSON represents a JSON object with null type
|
||||
var nullJSON = []byte("null")
|
||||
|
||||
const comma = byte(',')
|
||||
|
||||
var closers map[byte]byte
|
||||
|
||||
func init() {
|
||||
closers = map[byte]byte{
|
||||
'{': '}',
|
||||
'[': ']',
|
||||
}
|
||||
}
|
||||
|
||||
// ConcatJSON concatenates multiple json objects or arrays efficiently.
|
||||
//
|
||||
// Note that [ConcatJSON] performs a very simple (and fast) concatenation
|
||||
// operation: it does not attempt to merge objects.
|
||||
func ConcatJSON(blobs ...[]byte) []byte {
|
||||
if len(blobs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
last := len(blobs) - 1
|
||||
for blobs[last] == nil || bytes.Equal(blobs[last], nullJSON) {
|
||||
// strips trailing null objects
|
||||
last--
|
||||
if last < 0 {
|
||||
// there was nothing but "null"s or nil...
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if last == 0 {
|
||||
return blobs[0]
|
||||
}
|
||||
|
||||
var opening, closing byte
|
||||
var idx, a int
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
for i, b := range blobs[:last+1] {
|
||||
if b == nil || bytes.Equal(b, nullJSON) {
|
||||
// a null object is in the list: skip it
|
||||
continue
|
||||
}
|
||||
if len(b) > 0 && opening == 0 { // is this an array or an object?
|
||||
opening, closing = b[0], closers[b[0]]
|
||||
}
|
||||
|
||||
if opening != '{' && opening != '[' {
|
||||
continue // don't know how to concatenate non container objects
|
||||
}
|
||||
|
||||
const minLengthIfNotEmpty = 3
|
||||
if len(b) < minLengthIfNotEmpty { // yep empty but also the last one, so closing this thing
|
||||
if i == last && a > 0 {
|
||||
_ = buf.WriteByte(closing) // never returns err != nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
idx = 0
|
||||
if a > 0 { // we need to join with a comma for everything beyond the first non-empty item
|
||||
_ = buf.WriteByte(comma) // never returns err != nil
|
||||
idx = 1 // this is not the first or the last so we want to drop the leading bracket
|
||||
}
|
||||
|
||||
if i != last { // not the last one, strip brackets
|
||||
_, _ = buf.Write(b[idx : len(b)-1]) // never returns err != nil
|
||||
} else { // last one, strip only the leading bracket
|
||||
_, _ = buf.Write(b[idx:])
|
||||
}
|
||||
a++
|
||||
}
|
||||
|
||||
// somehow it ended up being empty, so provide a default value
|
||||
if buf.Len() == 0 && (opening == '{' || opening == '[') {
|
||||
_ = buf.WriteByte(opening) // never returns err != nil
|
||||
_ = buf.WriteByte(closing)
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
Reference in New Issue
Block a user