Skip to content

Commit 9aca265

Browse files
universe-opsUniverse Ops
andauthored
Pass through ephemeral-storage size for k8s deployments (#183)
Add necessary k8s layer conversion for ephemeral-storage pass through to k8s Co-authored-by: Universe Ops <universe-ops@github.com>
1 parent 580f6ef commit 9aca265

2 files changed

Lines changed: 76 additions & 9 deletions

File tree

.github/workflows/simple-forge.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ on:
3232
description: 'JWT token for both AI gateway and forge service API access'
3333
required: false
3434
type: string
35+
anthropic_base_url:
36+
description: 'Anthropic API base URL (overrides secrets if provided)'
37+
required: false
38+
type: string
3539

3640
jobs:
3741
generate-code:
@@ -62,7 +66,7 @@ jobs:
6266
model_name: ${{ inputs.model_name }}
6367
branch: ${{ inputs.branch }}
6468
anthropic_api_key: ${{ inputs.api_token || secrets.ANTHROPIC_API_KEY }}
65-
anthropic_base_url: ${{ secrets.ANTHROPIC_BASE_URL || format('{0}/ai', inputs.service_url || 'https://forge.simple-container.com') || 'https://api.anthropic.com' }}
69+
anthropic_base_url: ${{ inputs.anthropic_base_url || secrets.ANTHROPIC_BASE_URL || format('{0}/ai', inputs.service_url || 'https://forge.simple-container.com') || 'https://api.anthropic.com' }}
6670
simple_forge_api_key: ${{ inputs.api_token || secrets.SIMPLE_FORGE_API_KEY }}
6771
github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
6872
script_version: ${{ inputs.script_version }}

pkg/clouds/k8s/types.go

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ func ToResources(cfg *api.StackConfigCompose, svc types.ServiceConfig) (*Resourc
174174
if err != nil {
175175
return nil, errors.Wrapf(err, "failed to convert memory limits")
176176
}
177+
ephemeralLimitInBytesInt, err := toEphemeralLimit(cfg, svc)
178+
if err != nil {
179+
return nil, errors.Wrapf(err, "failed to convert ephemeral storage limits")
180+
}
177181

178182
// Get requests (with fallback to limits if not specified)
179183
cpuRequestInt, err := toCpuRequest(cfg, svc, cpuLimitInt)
@@ -184,16 +188,31 @@ func ToResources(cfg *api.StackConfigCompose, svc types.ServiceConfig) (*Resourc
184188
if err != nil {
185189
return nil, errors.Wrapf(err, "failed to convert memory requests")
186190
}
191+
ephemeralRequestInBytesInt, err := toEphemeralRequest(cfg, svc, ephemeralLimitInBytesInt)
192+
if err != nil {
193+
return nil, errors.Wrapf(err, "failed to convert ephemeral storage requests")
194+
}
195+
196+
limits := map[string]string{
197+
"memory": bytesSizeToHuman(memLimitInBytesInt), // must be in MB
198+
"cpu": fmt.Sprintf("%dm", cpuLimitInt),
199+
}
200+
requests := map[string]string{
201+
"memory": bytesSizeToHuman(memRequestInBytesInt), // must be in MB
202+
"cpu": fmt.Sprintf("%dm", cpuRequestInt),
203+
}
204+
205+
// Add ephemeral storage if configured
206+
if ephemeralLimitInBytesInt > 0 {
207+
limits["ephemeral-storage"] = bytesSizeToHuman(ephemeralLimitInBytesInt)
208+
}
209+
if ephemeralRequestInBytesInt > 0 {
210+
requests["ephemeral-storage"] = bytesSizeToHuman(ephemeralRequestInBytesInt)
211+
}
187212

188213
return &Resources{
189-
Limits: map[string]string{
190-
"memory": bytesSizeToHuman(memLimitInBytesInt), // must be in MB
191-
"cpu": fmt.Sprintf("%dm", cpuLimitInt),
192-
},
193-
Requests: map[string]string{
194-
"memory": bytesSizeToHuman(memRequestInBytesInt), // must be in MB
195-
"cpu": fmt.Sprintf("%dm", cpuRequestInt),
196-
},
214+
Limits: limits,
215+
Requests: requests,
197216
}, nil
198217
}
199218

@@ -303,6 +322,50 @@ func toMemoryRequest(cfg *api.StackConfigCompose, svc types.ServiceConfig, memor
303322
return memoryLimit / 2, nil
304323
}
305324

325+
// toEphemeralLimit extracts ephemeral storage limits from configuration
326+
func toEphemeralLimit(cfg *api.StackConfigCompose, svc types.ServiceConfig) (int64, error) {
327+
// Priority 1: Explicit limits in size configuration
328+
if len(cfg.Runs) == 1 && cfg.Size != nil && cfg.Size.Limits != nil && cfg.Size.Limits.Ephemeral != "" {
329+
if v, err := strconv.Atoi(cfg.Size.Limits.Ephemeral); err != nil {
330+
return 0, errors.Wrapf(err, "failed to parse ephemeral storage limit specified for stack: %q", cfg.Size.Limits.Ephemeral)
331+
} else {
332+
return int64(v) * 1024 * 1024, nil // Convert MB to bytes
333+
}
334+
}
335+
336+
// Priority 2: Size configuration ephemeral field
337+
if len(cfg.Runs) == 1 && cfg.Size != nil && cfg.Size.Ephemeral != "" {
338+
if v, err := strconv.Atoi(cfg.Size.Ephemeral); err != nil {
339+
return 0, errors.Wrapf(err, "failed to parse ephemeral storage specified for stack: %q", cfg.Size.Ephemeral)
340+
} else {
341+
return int64(v) * 1024 * 1024, nil // Convert MB to bytes
342+
}
343+
}
344+
345+
// No ephemeral storage configured - return 0 (will be omitted from resources)
346+
return 0, nil
347+
}
348+
349+
// toEphemeralRequest extracts ephemeral storage requests from configuration, with fallback to limits
350+
func toEphemeralRequest(cfg *api.StackConfigCompose, svc types.ServiceConfig, ephemeralLimit int64) (int64, error) {
351+
// Priority 1: Explicit requests in size configuration
352+
if len(cfg.Runs) == 1 && cfg.Size != nil && cfg.Size.Requests != nil && cfg.Size.Requests.Ephemeral != "" {
353+
if v, err := strconv.Atoi(cfg.Size.Requests.Ephemeral); err != nil {
354+
return 0, errors.Wrapf(err, "failed to parse ephemeral storage request specified for stack: %q", cfg.Size.Requests.Ephemeral)
355+
} else {
356+
return int64(v) * 1024 * 1024, nil // Convert MB to bytes
357+
}
358+
}
359+
360+
// If no limit is set, don't set a request either
361+
if ephemeralLimit == 0 {
362+
return 0, nil
363+
}
364+
365+
// Fallback: Use 50% of limit as request (Kubernetes best practice)
366+
return ephemeralLimit / 2, nil
367+
}
368+
306369
func ToHeaders(headers *api.Headers) Headers {
307370
if headers == nil {
308371
return nil

0 commit comments

Comments
 (0)