The operator strict-decodes CR specs, so when the in-cluster CRDs lag the
running operator (e.g. CRDs pinned to v0.1.0 while the operator ran v0.3.0),
new spec fields fail to decode with no operator-side signal. This adds an
advisory startup check so the mismatch is visible in the operator logs.
- Add CheckCRDVersions: GET each owned CRD and verify it carries a
version-sentinel spec field, logging a distinct WARNING per problem
(missing CRD vs. present-but-stale schema); advisory only, never exits.
- Keep the sentinel list (buckets/managePolicy, objectstoreusers/
retainOnDelete, bucketaccesses/rawStatements) in one place.
- Wire the check into operator startup after the manager config is available.
- Add apiextensions customresourcedefinitions get;list RBAC marker and
regenerate config/rbac/role.yaml.
- Promote k8s.io/apiextensions-apiserver to a direct dependency.
Claude-Session: https://claude.ai/code/session_016CEncETbf8cvy1PhsHfFHM
The operator previously assumed it created every user and bucket it managed:
reconciling an existing resource could overwrite its user attributes or wipe its
bucket policy, and deleting a CRD always deleted the underlying RGW object (only
Bucket had retainOnDelete). That made taking over pre-existing radosgw state
unsafe. Make adoption first-class.
- add retainOnDelete to ObjectStoreUser and BucketAccess (dedicated users), so
deleting the CRD orphans the RGW user instead of deleting it (symmetric with
Bucket)
- merge bucket policy instead of replacing it: the operator marks its own
statements with a cephrgwop* Sid and preserves any statement it does not own,
so adopting a bucket with a hand-written policy keeps it; add Bucket
managePolicy (default true) to opt out of policy management entirely
- only reconcile user attributes the spec sets: DisplayName when non-empty and
Suspended is now an optional *bool, so adopting a user does not reset them
- record adoption: ObjectStoreUser/Bucket status.adopted (+ printcolumn) is true
when the RGW object already existed on first reconcile
- add GetBucketPolicy + MergeBucketPolicy; keyed adoption detection off the
status identity field so a Pending owner wait does not mislabel it
- regenerate CRDs/deepcopy; add docs/adoption.md and
config/samples/05-adoption.yaml; cover the merge in policy_test.go
Claude-Session: https://claude.ai/code/session_016CEncETbf8cvy1PhsHfFHM
The BucketAccess model only offered three coarse levels (read-only/read-write/
full) applied to the whole bucket. Real grants often need to be scoped to a key
prefix, limited to a source network or TLS, restricted to specific actions, or
expressed as an arbitrary S3 statement. RGW (Reef 18.2+/Squid) honours the S3
bucket-policy features to do all of this; expose them on BucketAccess while
keeping the level as the ergonomic default.
- add BucketAccess spec fields: paths (key-prefix scoping), actions (action
override), conditions (sourceIPs + secureTransportOnly), rawStatements
(arbitrary S3 statements with the principal injected)
- extend ceph.Grant + BuildBucketPolicy to render prefixed object resources,
custom-action statements, S3 condition blocks, and raw statements, keeping
output deterministic (sorted, stable sids)
- translate the new spec fields into grants in the Bucket controller and
fingerprint grants so distinct fine-grained BucketAccess objects no longer
collapse on UID+level alone
- regenerate deepcopy + CRDs; add config/samples/04-access-fine-grained.yaml
- cover paths, action override, conditions, raw statements and determinism in
policy_test.go; document the fields in the README
Claude-Session: https://claude.ai/code/session_016CEncETbf8cvy1PhsHfFHM
The operator drove the Ceph manager dashboard REST API to manage RGW users,
buckets and policies. That coupled it to a dashboard login, the dashboard's RGW
wiring, and the dashboard's bucket API surface. Rebuild the Ceph integration to
talk directly to radosgw the way the CLI does, using native Go libraries, while
keeping every operator capability identical.
The exported surface of internal/ceph is unchanged, so the three controllers
and cmd/operator's structure are untouched (bar the CEPH_RGW_* config plumbing).
- replace the internal/ceph client internals with github.com/ceph/go-ceph
rgw/admin (Admin Ops API) for users, keys, quotas and bucket info/removal
- add github.com/aws/aws-sdk-go-v2 S3 client for bucket create, versioning,
policy, tagging and object lock, signed as the bucket owner
- map go-ceph admin.ErrNoSuch*/ErrUserExists and smithy APIError codes into
IsNotFound/IsConflict so controller create-vs-update branching is preserved
- set S3 path-style addressing and WhenRequired checksum modes for RGW
- delete the hand-rolled dashboard client, token auth and JSON plumbing
- keep policy.go/BuildBucketPolicy/BuildTagJSON as pure builders
- replace the client tests with NewClient validation and error-classifier tests
- keep CGO_ENABLED=0 distroless: only go-ceph's pure-Go rgw/admin is imported
- switch env/config to CEPH_RGW_* (endpoint, admin endpoint, access/secret key,
region, CA, insecure) and update the deployment manifest
- rewrite README and docs/ceph-setup.md for the single RGW admin user
(caps users=*;buckets=*), keeping Vault/VSO as the primary credential source
Claude-Session: https://claude.ai/code/session_016CEncETbf8cvy1PhsHfFHM
Document sourcing cephrgw-credentials from Vault via VSO as the primary path,
using the shared default k8s auth role and the templated KV path
kubernetes/namespace/cephrgw-system/default/cephrgw-credentials (no dedicated
Vault role/policy needed). Keep the plain-Secret method as a fallback for
non-cluster/kind use.
Adds a Kubernetes operator that provisions Ceph RGW (S3) buckets and
access keys declaratively through the Ceph manager dashboard REST API.
Three CRDs in group ceph.unkin.net/v1alpha1:
- ObjectStoreUser: creates an RGW user, delivers its key pair to a Secret
- Bucket: creates an S3 bucket owned by an ObjectStoreUser; owns the
bucket's aggregate S3 policy (union of all BucketAccess grants)
- BucketAccess: grants read-only/read-write/full access, provisioning a
dedicated user (or reusing a referenced one) and delivering RW/RO keys
The internal/ceph client wraps the dashboard /api/auth, /api/rgw/user and
/api/rgw/bucket endpoints with lazy token auth and re-auth on 401. Bucket
policies are rendered deterministically and applied via the bucket
policy API (Reef 18.2+). Credentials come from the cephrgw-credentials
Secret via env. Includes generated CRDs/RBAC, samples, kind manifests,
Woodpecker CI, and docs/ceph-setup.md covering the required Ceph
dashboard account, RGW wiring and permissions.