Skip to content

Commit 1c41f53

Browse files
stubbiclaude
andcommitted
feat: add Redis support for rate limiting in multi-replica deployments
Adds a new `redis` field to the Instance CRD spec with two modes: - `managed`: Operator provisions a Redis 7 StatefulSet with PVC, Service, liveness/readiness probes, and security context (non-root, dropped caps) - `external`: User provides a Redis URL or Secret reference The operator injects PAPERCLIP_RATE_LIMIT_REDIS_URL into Paperclip server pods, enabling shared rate limit state across replicas. Resources created for managed mode: - StatefulSet (1 replica, redis:7-alpine, AOF persistence, LRU eviction) - Service (ClusterIP, port 6379) - PVC (1Gi default, configurable size and storage class) Closes #18 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ebb12cf commit 1c41f53

8 files changed

Lines changed: 694 additions & 0 deletions

File tree

api/v1alpha1/paperclipinstance_types.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ type InstanceSpec struct {
5252
// +optional
5353
ObjectStorage *ObjectStorageSpec `json:"objectStorage,omitempty"`
5454

55+
// Redis configures Redis for rate limiting and caching in multi-replica deployments.
56+
// +optional
57+
Redis *RedisSpec `json:"redis,omitempty"`
58+
5559
// Heartbeat configures the agent heartbeat scheduler.
5660
// +optional
5761
Heartbeat HeartbeatSpec `json:"heartbeat,omitempty"`
@@ -333,6 +337,48 @@ type ObjectStorageSpec struct {
333337
CredentialsSecretRef *corev1.LocalObjectReference `json:"credentialsSecretRef,omitempty"`
334338
}
335339

340+
// RedisSpec configures Redis for rate limiting and caching.
341+
type RedisSpec struct {
342+
// Mode selects the Redis mode: "managed" (operator-provisioned) or "external" (user-provided URL).
343+
// +kubebuilder:default="managed"
344+
// +kubebuilder:validation:Enum=managed;external
345+
// +optional
346+
Mode string `json:"mode,omitempty"`
347+
348+
// ExternalURL is the Redis connection string for external mode (e.g. "redis://host:6379").
349+
// +optional
350+
ExternalURL string `json:"externalURL,omitempty"`
351+
352+
// ExternalURLSecretRef references a Secret key containing the Redis URL.
353+
// +optional
354+
ExternalURLSecretRef *corev1.SecretKeySelector `json:"externalURLSecretRef,omitempty"`
355+
356+
// Managed configures the operator-managed Redis instance.
357+
// +optional
358+
Managed ManagedRedisSpec `json:"managed,omitempty"`
359+
}
360+
361+
// ManagedRedisSpec configures the operator-managed Redis instance.
362+
type ManagedRedisSpec struct {
363+
// Image is the Redis container image.
364+
// +kubebuilder:default="redis:7-alpine"
365+
// +optional
366+
Image string `json:"image,omitempty"`
367+
368+
// StorageSize is the PVC size for Redis data. Defaults to 1Gi.
369+
// +kubebuilder:default="1Gi"
370+
// +optional
371+
StorageSize resource.Quantity `json:"storageSize,omitempty"`
372+
373+
// StorageClass is the storage class for the Redis PVC.
374+
// +optional
375+
StorageClass *string `json:"storageClass,omitempty"`
376+
377+
// Resources specifies compute resources for the Redis container.
378+
// +optional
379+
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
380+
}
381+
336382
// HeartbeatSpec configures the agent heartbeat scheduler.
337383
type HeartbeatSpec struct {
338384
// Enabled controls whether the heartbeat scheduler runs. Defaults to true.
@@ -867,6 +913,12 @@ type ManagedResources struct {
867913
DatabaseService string `json:"databaseService,omitempty"`
868914
// +optional
869915
DatabasePVC string `json:"databasePVC,omitempty"`
916+
// +optional
917+
RedisStatefulSet string `json:"redisStatefulSet,omitempty"`
918+
// +optional
919+
RedisService string `json:"redisService,omitempty"`
920+
// +optional
921+
RedisPVC string `json:"redisPVC,omitempty"`
870922
}
871923

872924
// BackupStatus tracks the state of a backup operation.

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/paperclip-operator/templates/crds/paperclip.inc_instances.yaml

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5621,6 +5621,129 @@ spec:
56215621
- tcp
56225622
type: string
56235623
type: object
5624+
redis:
5625+
description: Redis configures Redis for rate limiting and caching
5626+
in multi-replica deployments.
5627+
properties:
5628+
externalURL:
5629+
description: ExternalURL is the Redis connection string for external
5630+
mode (e.g. "redis://host:6379").
5631+
type: string
5632+
externalURLSecretRef:
5633+
description: ExternalURLSecretRef references a Secret key containing
5634+
the Redis URL.
5635+
properties:
5636+
key:
5637+
description: The key of the secret to select from. Must be
5638+
a valid secret key.
5639+
type: string
5640+
name:
5641+
default: ""
5642+
description: |-
5643+
Name of the referent.
5644+
This field is effectively required, but due to backwards compatibility is
5645+
allowed to be empty. Instances of this type with an empty value here are
5646+
almost certainly wrong.
5647+
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
5648+
type: string
5649+
optional:
5650+
description: Specify whether the Secret or its key must be
5651+
defined
5652+
type: boolean
5653+
required:
5654+
- key
5655+
type: object
5656+
x-kubernetes-map-type: atomic
5657+
managed:
5658+
description: Managed configures the operator-managed Redis instance.
5659+
properties:
5660+
image:
5661+
default: redis:7-alpine
5662+
description: Image is the Redis container image.
5663+
type: string
5664+
resources:
5665+
description: Resources specifies compute resources for the
5666+
Redis container.
5667+
properties:
5668+
claims:
5669+
description: |-
5670+
Claims lists the names of resources, defined in spec.resourceClaims,
5671+
that are used by this container.
5672+
5673+
This is an alpha field and requires enabling the
5674+
DynamicResourceAllocation feature gate.
5675+
5676+
This field is immutable. It can only be set for containers.
5677+
items:
5678+
description: ResourceClaim references one entry in PodSpec.ResourceClaims.
5679+
properties:
5680+
name:
5681+
description: |-
5682+
Name must match the name of one entry in pod.spec.resourceClaims of
5683+
the Pod where this field is used. It makes that resource available
5684+
inside a container.
5685+
type: string
5686+
request:
5687+
description: |-
5688+
Request is the name chosen for a request in the referenced claim.
5689+
If empty, everything from the claim is made available, otherwise
5690+
only the result of this request.
5691+
type: string
5692+
required:
5693+
- name
5694+
type: object
5695+
type: array
5696+
x-kubernetes-list-map-keys:
5697+
- name
5698+
x-kubernetes-list-type: map
5699+
limits:
5700+
additionalProperties:
5701+
anyOf:
5702+
- type: integer
5703+
- type: string
5704+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
5705+
x-kubernetes-int-or-string: true
5706+
description: |-
5707+
Limits describes the maximum amount of compute resources allowed.
5708+
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
5709+
type: object
5710+
requests:
5711+
additionalProperties:
5712+
anyOf:
5713+
- type: integer
5714+
- type: string
5715+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
5716+
x-kubernetes-int-or-string: true
5717+
description: |-
5718+
Requests describes the minimum amount of compute resources required.
5719+
If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
5720+
otherwise to an implementation-defined value. Requests cannot exceed Limits.
5721+
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
5722+
type: object
5723+
type: object
5724+
storageClass:
5725+
description: StorageClass is the storage class for the Redis
5726+
PVC.
5727+
type: string
5728+
storageSize:
5729+
anyOf:
5730+
- type: integer
5731+
- type: string
5732+
default: 1Gi
5733+
description: StorageSize is the PVC size for Redis data. Defaults
5734+
to 1Gi.
5735+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
5736+
x-kubernetes-int-or-string: true
5737+
type: object
5738+
mode:
5739+
default: managed
5740+
description: 'Mode selects the Redis mode: "managed" (operator-provisioned)
5741+
or "external" (user-provided URL).'
5742+
enum:
5743+
- managed
5744+
- external
5745+
type: string
5746+
type: object
56245747
resources:
56255748
description: Resources specifies the compute resources for the Paperclip
56265749
container.
@@ -7762,6 +7885,12 @@ spec:
77627885
type: string
77637886
persistentVolumeClaim:
77647887
type: string
7888+
redisPVC:
7889+
type: string
7890+
redisService:
7891+
type: string
7892+
redisStatefulSet:
7893+
type: string
77657894
service:
77667895
type: string
77677896
serviceAccount:

0 commit comments

Comments
 (0)