The Provider interface (Create/Get/Delete/ListByTag), Instance, and CreateRequest that every cloud backend implements — kept independent of api/v1alpha1 so this package has no CRD-type coupling. Error taxonomy (ErrNotFound/ErrQuotaExceeded/ErrTransient/ErrPermanent) wrapped via a multi-error Unwrap() []error, so errors.Is and errors.As both work off the same value: the reconciler branches on classification, logs keep the underlying SDK error. Unclassified errors default to ErrTransient — retrying is always safer than latching Failed. Deterministic instance naming (SHA-256 -> base32 -> 16 chars, 22 total with the "proxy-" prefix) satisfying GCP's RFC1035 name rules with headroom, and idempotency-tested across 10k UIDs with zero collisions. --providers-config YAML parsing (config.go) with fail-fast validation: unknown type, duplicate name, missing gcp.project, mismatched type/config-block, and strict-mode rejection of unknown keys. internal/provider/registry/registry.go takes its type->constructor map as a parameter rather than hardcoding it, so the package has zero import on internal/provider/mock or internal/provider/gcp (neither exists yet — mock is Step 3, gcp is Step 8) and compiles today. Explicit wiring moves to the composition root in cmd/main.go (Step 10). Deferred internal/provider/metrics.go (the WithMetrics decorator) to Step 9, where the Prometheus vectors it needs actually get built — nothing in this step depends on it. internal/provider at 96.2% coverage, internal/provider/registry at 100%. make test green. Co-Authored-By: Claude <noreply@anthropic.com>
34 lines
1.3 KiB
Go
34 lines
1.3 KiB
Go
package provider
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/base32"
|
|
"strings"
|
|
|
|
"k8s.io/apimachinery/pkg/types"
|
|
)
|
|
|
|
const namePrefix = "proxy-"
|
|
|
|
// NameFromUID derives a deterministic instance name from a Proxy CR's UID.
|
|
// Provider.Create implementations key idempotency on this name: a repeat
|
|
// call after a crash must find the existing instance by this name rather
|
|
// than create a duplicate, and the reconciler relies on that to recover
|
|
// providerID after a crash even if status was wiped.
|
|
//
|
|
// SHA-256 of the UID, truncated to the first 10 bytes, RFC 4648 base32
|
|
// (no padding), lowercased: exactly 16 characters, for 22 total with the
|
|
// "proxy-" prefix. That gives 80 bits of collision resistance — a birthday
|
|
// collision only becomes likely around 2^40 objects, against a fleet of
|
|
// tens — while satisfying GCP's instance name rules
|
|
// (^[a-z]([-a-z0-9]{0,61}[a-z0-9])?$, <=63 chars) with headroom, since GCP
|
|
// auto-names some resources (e.g. the boot disk) after the instance name.
|
|
// base32's alphabet (A-Z2-7) is entirely legal once lowercased; base64 is
|
|
// not (has '+', '/', and uppercase), and hex would need 20 characters for
|
|
// the same 80 bits.
|
|
func NameFromUID(uid types.UID) string {
|
|
sum := sha256.Sum256([]byte(uid))
|
|
enc := base32.StdEncoding.WithPadding(base32.NoPadding)
|
|
return namePrefix + strings.ToLower(enc.EncodeToString(sum[:10]))
|
|
}
|