Skip to content

Commit 1f9d091

Browse files
stubbiclaude
andcommitted
feat: Phase 4 - persistence, multi-namespace, inference proxy CRD support
Add production hardening fields to the CloudSandboxSpec CRD: - CloudSandboxPersistenceSpec for PVC-backed persistent workspaces - MultiNamespace flag for per-company namespace isolation - InferenceProxySpec for transparent inference metering proxy sidecar - ResourceTiers map for named resource presets Operator changes: - Inject persistence and multi-namespace env vars into the StatefulSet - Add PVC permissions to sandbox Role when persistence is enabled - Add ClusterRole/ClusterRoleBinding builders for multi-namespace mode with namespace create/get/list permissions - Extract buildCloudSandboxEnvVars to keep cyclomatic complexity in check Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7fbc5a9 commit 1f9d091

7 files changed

Lines changed: 631 additions & 36 deletions

File tree

api/v1alpha1/paperclipinstance_types.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,49 @@ type CloudSandboxSpec struct {
397397
// Resources specifies default compute resources for sandbox pods.
398398
// +optional
399399
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
400+
401+
// Persistence configures PVC-backed persistent workspaces for sandbox pods.
402+
// +optional
403+
Persistence *CloudSandboxPersistenceSpec `json:"persistence,omitempty"`
404+
405+
// MultiNamespace enables per-company namespace isolation for sandbox pods.
406+
// When enabled, each company's sandbox pods run in a dedicated namespace.
407+
// +optional
408+
MultiNamespace bool `json:"multiNamespace,omitempty"`
409+
410+
// InferenceProxy configures the transparent inference metering proxy.
411+
// +optional
412+
InferenceProxy *InferenceProxySpec `json:"inferenceProxy,omitempty"`
413+
414+
// ResourceTiers defines named resource presets for sandbox pods.
415+
// +optional
416+
ResourceTiers map[string]corev1.ResourceRequirements `json:"resourceTiers,omitempty"`
417+
}
418+
419+
// CloudSandboxPersistenceSpec configures PVC-backed persistent workspaces.
420+
type CloudSandboxPersistenceSpec struct {
421+
// Enabled enables PVC-backed workspaces instead of emptyDir.
422+
Enabled bool `json:"enabled,omitempty"`
423+
// StorageClass is the storage class for workspace PVCs.
424+
// +optional
425+
StorageClass string `json:"storageClass,omitempty"`
426+
// Size is the storage size for workspace PVCs (e.g. "10Gi").
427+
// +kubebuilder:default="10Gi"
428+
// +optional
429+
Size string `json:"size,omitempty"`
430+
}
431+
432+
// InferenceProxySpec configures the transparent inference metering proxy.
433+
type InferenceProxySpec struct {
434+
// Enabled enables the inference proxy sidecar for metered API access.
435+
Enabled bool `json:"enabled,omitempty"`
436+
// Image is the inference proxy container image.
437+
// +optional
438+
Image string `json:"image,omitempty"`
439+
// Port is the port the proxy listens on.
440+
// +kubebuilder:default=8090
441+
// +optional
442+
Port int32 `json:"port,omitempty"`
400443
}
401444

402445
// ConnectionsSpec configures third-party OAuth provider credentials.

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 47 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: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,115 @@ spec:
9696
pod can be idle before being reaped.
9797
format: int32
9898
type: integer
99+
inferenceProxy:
100+
description: InferenceProxy configures the transparent inference
101+
metering proxy.
102+
properties:
103+
enabled:
104+
description: Enabled enables the inference proxy sidecar
105+
for metered API access.
106+
type: boolean
107+
image:
108+
description: Image is the inference proxy container image.
109+
type: string
110+
port:
111+
default: 8090
112+
description: Port is the port the proxy listens on.
113+
format: int32
114+
type: integer
115+
type: object
116+
multiNamespace:
117+
description: |-
118+
MultiNamespace enables per-company namespace isolation for sandbox pods.
119+
When enabled, each company's sandbox pods run in a dedicated namespace.
120+
type: boolean
99121
namespace:
100122
description: Namespace is the namespace for sandbox pods.
101123
Defaults to the instance namespace.
102124
type: string
125+
persistence:
126+
description: Persistence configures PVC-backed persistent
127+
workspaces for sandbox pods.
128+
properties:
129+
enabled:
130+
description: Enabled enables PVC-backed workspaces instead
131+
of emptyDir.
132+
type: boolean
133+
size:
134+
default: 10Gi
135+
description: Size is the storage size for workspace PVCs
136+
(e.g. "10Gi").
137+
type: string
138+
storageClass:
139+
description: StorageClass is the storage class for workspace
140+
PVCs.
141+
type: string
142+
type: object
143+
resourceTiers:
144+
additionalProperties:
145+
description: ResourceRequirements describes the compute
146+
resource requirements.
147+
properties:
148+
claims:
149+
description: |-
150+
Claims lists the names of resources, defined in spec.resourceClaims,
151+
that are used by this container.
152+
153+
This is an alpha field and requires enabling the
154+
DynamicResourceAllocation feature gate.
155+
156+
This field is immutable. It can only be set for containers.
157+
items:
158+
description: ResourceClaim references one entry in
159+
PodSpec.ResourceClaims.
160+
properties:
161+
name:
162+
description: |-
163+
Name must match the name of one entry in pod.spec.resourceClaims of
164+
the Pod where this field is used. It makes that resource available
165+
inside a container.
166+
type: string
167+
request:
168+
description: |-
169+
Request is the name chosen for a request in the referenced claim.
170+
If empty, everything from the claim is made available, otherwise
171+
only the result of this request.
172+
type: string
173+
required:
174+
- name
175+
type: object
176+
type: array
177+
x-kubernetes-list-map-keys:
178+
- name
179+
x-kubernetes-list-type: map
180+
limits:
181+
additionalProperties:
182+
anyOf:
183+
- type: integer
184+
- type: string
185+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
186+
x-kubernetes-int-or-string: true
187+
description: |-
188+
Limits describes the maximum amount of compute resources allowed.
189+
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
190+
type: object
191+
requests:
192+
additionalProperties:
193+
anyOf:
194+
- type: integer
195+
- type: string
196+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
197+
x-kubernetes-int-or-string: true
198+
description: |-
199+
Requests describes the minimum amount of compute resources required.
200+
If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
201+
otherwise to an implementation-defined value. Requests cannot exceed Limits.
202+
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
203+
type: object
204+
type: object
205+
description: ResourceTiers defines named resource presets
206+
for sandbox pods.
207+
type: object
103208
resources:
104209
description: Resources specifies default compute resources
105210
for sandbox pods.

config/crd/bases/paperclip.inc_instances.yaml

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,115 @@ spec:
9090
pod can be idle before being reaped.
9191
format: int32
9292
type: integer
93+
inferenceProxy:
94+
description: InferenceProxy configures the transparent inference
95+
metering proxy.
96+
properties:
97+
enabled:
98+
description: Enabled enables the inference proxy sidecar
99+
for metered API access.
100+
type: boolean
101+
image:
102+
description: Image is the inference proxy container image.
103+
type: string
104+
port:
105+
default: 8090
106+
description: Port is the port the proxy listens on.
107+
format: int32
108+
type: integer
109+
type: object
110+
multiNamespace:
111+
description: |-
112+
MultiNamespace enables per-company namespace isolation for sandbox pods.
113+
When enabled, each company's sandbox pods run in a dedicated namespace.
114+
type: boolean
93115
namespace:
94116
description: Namespace is the namespace for sandbox pods.
95117
Defaults to the instance namespace.
96118
type: string
119+
persistence:
120+
description: Persistence configures PVC-backed persistent
121+
workspaces for sandbox pods.
122+
properties:
123+
enabled:
124+
description: Enabled enables PVC-backed workspaces instead
125+
of emptyDir.
126+
type: boolean
127+
size:
128+
default: 10Gi
129+
description: Size is the storage size for workspace PVCs
130+
(e.g. "10Gi").
131+
type: string
132+
storageClass:
133+
description: StorageClass is the storage class for workspace
134+
PVCs.
135+
type: string
136+
type: object
137+
resourceTiers:
138+
additionalProperties:
139+
description: ResourceRequirements describes the compute
140+
resource requirements.
141+
properties:
142+
claims:
143+
description: |-
144+
Claims lists the names of resources, defined in spec.resourceClaims,
145+
that are used by this container.
146+
147+
This is an alpha field and requires enabling the
148+
DynamicResourceAllocation feature gate.
149+
150+
This field is immutable. It can only be set for containers.
151+
items:
152+
description: ResourceClaim references one entry in
153+
PodSpec.ResourceClaims.
154+
properties:
155+
name:
156+
description: |-
157+
Name must match the name of one entry in pod.spec.resourceClaims of
158+
the Pod where this field is used. It makes that resource available
159+
inside a container.
160+
type: string
161+
request:
162+
description: |-
163+
Request is the name chosen for a request in the referenced claim.
164+
If empty, everything from the claim is made available, otherwise
165+
only the result of this request.
166+
type: string
167+
required:
168+
- name
169+
type: object
170+
type: array
171+
x-kubernetes-list-map-keys:
172+
- name
173+
x-kubernetes-list-type: map
174+
limits:
175+
additionalProperties:
176+
anyOf:
177+
- type: integer
178+
- type: string
179+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
180+
x-kubernetes-int-or-string: true
181+
description: |-
182+
Limits describes the maximum amount of compute resources allowed.
183+
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
184+
type: object
185+
requests:
186+
additionalProperties:
187+
anyOf:
188+
- type: integer
189+
- type: string
190+
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
191+
x-kubernetes-int-or-string: true
192+
description: |-
193+
Requests describes the minimum amount of compute resources required.
194+
If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
195+
otherwise to an implementation-defined value. Requests cannot exceed Limits.
196+
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
197+
type: object
198+
type: object
199+
description: ResourceTiers defines named resource presets
200+
for sandbox pods.
201+
type: object
97202
resources:
98203
description: Resources specifies default compute resources
99204
for sandbox pods.

0 commit comments

Comments
 (0)