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.
75 lines
2.5 KiB
Go
75 lines
2.5 KiB
Go
package restful
|
|
|
|
// Copyright 2013 Ernest Micklei. All rights reserved.
|
|
// Use of this source code is governed by a license
|
|
// that can be found in the LICENSE file.
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// PathExpression holds a compiled path expression (RegExp) needed to match against
|
|
// Http request paths and to extract path parameter values.
|
|
type pathExpression struct {
|
|
LiteralCount int // the number of literal characters (means those not resulting from template variable substitution)
|
|
VarNames []string // the names of parameters (enclosed by {}) in the path
|
|
VarCount int // the number of named parameters (enclosed by {}) in the path
|
|
Matcher *regexp.Regexp
|
|
Source string // Path as defined by the RouteBuilder
|
|
tokens []string
|
|
}
|
|
|
|
// NewPathExpression creates a PathExpression from the input URL path.
|
|
// Returns an error if the path is invalid.
|
|
func newPathExpression(path string) (*pathExpression, error) {
|
|
expression, literalCount, varNames, varCount, tokens := templateToRegularExpression(path)
|
|
compiled, err := regexp.Compile(expression)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &pathExpression{literalCount, varNames, varCount, compiled, expression, tokens}, nil
|
|
}
|
|
|
|
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-370003.7.3
|
|
func templateToRegularExpression(template string) (expression string, literalCount int, varNames []string, varCount int, tokens []string) {
|
|
var buffer bytes.Buffer
|
|
buffer.WriteString("^")
|
|
//tokens = strings.Split(template, "/")
|
|
tokens = tokenizePath(template)
|
|
for _, each := range tokens {
|
|
if each == "" {
|
|
continue
|
|
}
|
|
buffer.WriteString("/")
|
|
if strings.HasPrefix(each, "{") {
|
|
// check for regular expression in variable
|
|
colon := strings.Index(each, ":")
|
|
var varName string
|
|
if colon != -1 {
|
|
// extract expression
|
|
varName = strings.TrimSpace(each[1:colon])
|
|
paramExpr := strings.TrimSpace(each[colon+1 : len(each)-1])
|
|
if paramExpr == "*" { // special case
|
|
buffer.WriteString("(.*)")
|
|
} else {
|
|
buffer.WriteString(fmt.Sprintf("(%s)", paramExpr)) // between colon and closing moustache
|
|
}
|
|
} else {
|
|
// plain var
|
|
varName = strings.TrimSpace(each[1 : len(each)-1])
|
|
buffer.WriteString("([^/]+?)")
|
|
}
|
|
varNames = append(varNames, varName)
|
|
varCount += 1
|
|
} else {
|
|
literalCount += len(each)
|
|
encoded := each // TODO URI encode
|
|
buffer.WriteString(regexp.QuoteMeta(encoded))
|
|
}
|
|
}
|
|
return strings.TrimRight(buffer.String(), "/") + "(/.*)?$", literalCount, varNames, varCount, tokens
|
|
}
|