Skip to content

Commit aee780f

Browse files
committed
Added persistence flags
1 parent d75377b commit aee780f

File tree

2 files changed

+86
-18
lines changed

2 files changed

+86
-18
lines changed

cmd/deploy.go

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,26 @@ Examples:
2323
coderun deploy my-app:latest --name web-app --http-port 8080 --env-file .env
2424
coderun deploy redis:latest --name my-redis --tcp-port 6379
2525
coderun deploy postgres:latest --name my-db --tcp-port 5432 --env-file database.env
26-
coderun deploy my-app:latest --name prod-app --replicas 2 --cpu 200m --memory 512Mi --http-port 3000 --env-file production.env`,
26+
coderun deploy my-app:latest --name prod-app --replicas 2 --cpu 200m --memory 512Mi --http-port 3000 --env-file production.env
27+
28+
# With persistent storage (automatically forces replicas to 1):
29+
coderun deploy postgres:15 --name my-postgres --tcp-port 5432 --storage-size 5Gi --storage-path /var/lib/postgresql/data
30+
coderun deploy mysql:8 --name my-mysql --tcp-port 3306 --storage-size 10Gi --storage-path /var/lib/mysql
31+
coderun deploy nginx:latest --name web-server --http-port 80 --storage-size 1Gi --storage-path /usr/share/nginx/html`,
2732
Args: cobra.ExactArgs(1),
2833
Run: runDeploy,
2934
}
3035

3136
var (
32-
replicas int
33-
cpu string
34-
memory string
35-
httpPort int
36-
tcpPort int
37-
envFile string
38-
appName string
37+
replicas int
38+
cpu string
39+
memory string
40+
httpPort int
41+
tcpPort int
42+
envFile string
43+
appName string
44+
persistentVolumeSize string
45+
persistentVolumeMountPath string
3946
)
4047

4148
func init() {
@@ -49,6 +56,10 @@ func init() {
4956
deployCmd.Flags().IntVar(&tcpPort, "tcp-port", 0, "TCP port to expose")
5057
deployCmd.Flags().StringVar(&envFile, "env-file", "", "Path to environment file")
5158
deployCmd.Flags().StringVar(&appName, "name", "", "Application name (required, 3-30 chars, lowercase letters/numbers/hyphens only)")
59+
60+
// Persistent storage flags
61+
deployCmd.Flags().StringVar(&persistentVolumeSize, "storage-size", "", "Size of persistent volume (e.g., '1Gi', '500Mi', '10Gi')")
62+
deployCmd.Flags().StringVar(&persistentVolumeMountPath, "storage-path", "", "Path where to mount the volume (e.g., '/data', '/var/lib/mysql')")
5263
}
5364

5465
// parseValidationError tries to parse backend validation errors and return user-friendly messages
@@ -91,6 +102,23 @@ func parseValidationError(errorMsg string) string {
91102
return "Image name cannot be empty"
92103
}
93104

105+
// Persistent storage validation errors
106+
if strings.Contains(lowerError, "persistent_volume_size") || strings.Contains(lowerError, "storage") {
107+
if strings.Contains(lowerError, "together") || strings.Contains(lowerError, "provided together") {
108+
return "When using persistent storage, both --storage-size and --storage-path are required"
109+
}
110+
if strings.Contains(lowerError, "format") || strings.Contains(lowerError, "10gi") || strings.Contains(lowerError, "500mi") {
111+
return "Storage size must be in format like '1Gi', '500Mi', '10Gi'"
112+
}
113+
return "Invalid storage configuration. Use --storage-size and --storage-path together"
114+
}
115+
if strings.Contains(lowerError, "persistent_volume_mount_path") || strings.Contains(lowerError, "mount") {
116+
if strings.Contains(lowerError, "absolute") || strings.Contains(lowerError, "starting with") {
117+
return "Storage path must be an absolute path starting with '/' (e.g., '/data', '/var/lib/mysql')"
118+
}
119+
return "Invalid storage path. Must be absolute path like '/data' or '/var/lib/mysql'"
120+
}
121+
94122
// Generic validation error
95123
if strings.Contains(lowerError, "422") || strings.Contains(lowerError, "validation") {
96124
return "Validation error: Please check your input parameters"
@@ -173,6 +201,38 @@ func runDeploy(cmd *cobra.Command, args []string) {
173201
os.Exit(1)
174202
}
175203

204+
// Validate persistent storage flags
205+
if persistentVolumeSize != "" || persistentVolumeMountPath != "" {
206+
// Both flags must be provided together
207+
if persistentVolumeSize == "" {
208+
fmt.Println("When using persistent storage, both --storage-size and --storage-path are required")
209+
os.Exit(1)
210+
}
211+
if persistentVolumeMountPath == "" {
212+
fmt.Println("When using persistent storage, both --storage-size and --storage-path are required")
213+
os.Exit(1)
214+
}
215+
216+
// Validate storage size format
217+
matched, _ := regexp.MatchString(`^\d+[MGT]i$`, persistentVolumeSize)
218+
if !matched {
219+
fmt.Println("Storage size must be in format like '1Gi', '500Mi', '10Gi'")
220+
os.Exit(1)
221+
}
222+
223+
// Validate mount path format (must be absolute path)
224+
if !strings.HasPrefix(persistentVolumeMountPath, "/") {
225+
fmt.Println("Storage path must be an absolute path starting with '/' (e.g., '/data', '/var/lib/mysql')")
226+
os.Exit(1)
227+
}
228+
229+
// Force replicas to 1 when using persistent storage
230+
if replicas > 1 {
231+
fmt.Printf("Warning: Persistent storage requested, forcing replicas to 1 (was %d)\n", replicas)
232+
replicas = 1
233+
}
234+
}
235+
176236
// Parse environment file if provided
177237
var envVars map[string]string
178238
if envFile != "" {
@@ -194,6 +254,12 @@ func runDeploy(cmd *cobra.Command, args []string) {
194254
EnvironmentVars: envVars,
195255
}
196256

257+
// Add persistent storage if specified
258+
if persistentVolumeSize != "" && persistentVolumeMountPath != "" {
259+
deployReq.PersistentVolumeSize = persistentVolumeSize
260+
deployReq.PersistentVolumeMountPath = persistentVolumeMountPath
261+
}
262+
197263
// Add HTTP port if specified
198264
if httpPort > 0 {
199265
deployReq.HTTPPort = &httpPort

internal/client/types.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,18 @@ type LoginResponse struct {
1818

1919
// DeploymentCreate represents a deployment creation request
2020
type DeploymentCreate struct {
21-
AppName string `json:"app_name"`
22-
Image string `json:"image"`
23-
Replicas int `json:"replicas"`
24-
CPULimit string `json:"cpu_limit,omitempty"`
25-
MemoryLimit string `json:"memory_limit,omitempty"`
26-
CPURequest string `json:"cpu_request,omitempty"`
27-
MemoryRequest string `json:"memory_request,omitempty"`
28-
HTTPPort *int `json:"http_port,omitempty"`
29-
TCPPort *int `json:"tcp_port,omitempty"`
30-
EnvironmentVars map[string]string `json:"environment_vars,omitempty"`
21+
AppName string `json:"app_name"`
22+
Image string `json:"image"`
23+
Replicas int `json:"replicas"`
24+
CPULimit string `json:"cpu_limit,omitempty"`
25+
MemoryLimit string `json:"memory_limit,omitempty"`
26+
CPURequest string `json:"cpu_request,omitempty"`
27+
MemoryRequest string `json:"memory_request,omitempty"`
28+
HTTPPort *int `json:"http_port,omitempty"`
29+
TCPPort *int `json:"tcp_port,omitempty"`
30+
EnvironmentVars map[string]string `json:"environment_vars,omitempty"`
31+
PersistentVolumeSize string `json:"persistent_volume_size,omitempty"`
32+
PersistentVolumeMountPath string `json:"persistent_volume_mount_path,omitempty"`
3133
}
3234

3335
// DeploymentResponse represents a deployment response

0 commit comments

Comments
 (0)