Skip to content

Commit 1dfcc8c

Browse files
authored
Merge pull request #21 from godon-dev/api_start_stop_delete_progress
Feature: Add breeder lifecycle commands
2 parents b1186d0 + b15a569 commit 1dfcc8c

3 files changed

Lines changed: 108 additions & 19 deletions

File tree

.github/workflows/ci.yml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,10 +258,28 @@ jobs:
258258
echo "Testing: breeder update"
259259
$BINARY_PATH --hostname=localhost --port=4010 breeder update --file=test_breeder_update.yml
260260
261-
# Test breeder purge
262-
echo "Testing: breeder purge"
261+
# Test breeder purge (safe mode)
262+
echo "Testing: breeder purge (safe mode)"
263263
$BINARY_PATH --hostname=localhost --port=4010 breeder purge --id=550e8400-e29b-41d4-a716-446655440000
264264
265+
# Test breeder stop
266+
echo "Testing: breeder stop"
267+
$BINARY_PATH --hostname=localhost --port=4010 breeder stop --id=550e8400-e29b-41d4-a716-446655440000
268+
269+
# Test breeder start
270+
echo "Testing: breeder start"
271+
$BINARY_PATH --hostname=localhost --port=4010 breeder start --id=550e8400-e29b-41d4-a716-446655440000
272+
273+
# Test breeder purge with force flag
274+
echo "Testing: breeder purge --force"
275+
$BINARY_PATH --hostname=localhost --port=4010 --force breeder purge --id=550e8400-e29b-41d4-a716-446655440000
276+
277+
# Test help shows new commands
278+
echo "Testing: help shows stop/start/force commands"
279+
$BINARY_PATH --help | grep -q "stop" && echo "✅ stop command documented" || echo "❌ stop command missing"
280+
$BINARY_PATH --help | grep -q "start" && echo "✅ start command documented" || echo "❌ start command missing"
281+
$BINARY_PATH --help | grep -q "force" && echo "✅ force flag documented" || echo "❌ force flag missing"
282+
265283
# Test help commands
266284
echo "Testing: help commands"
267285
$BINARY_PATH --help

src/godon/breeder.nim

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,32 @@ proc updateBreederFromYaml*(client: GodonClient, yamlContent: string): ApiRespon
108108
except CatchableError as e:
109109
result = ApiResponse[Breeder](success: false, data: default(Breeder), error: e.msg)
110110

111-
proc deleteBreeder*(client: GodonClient, uuid: string): ApiResponse[JsonNode] =
111+
proc deleteBreeder*(client: GodonClient, uuid: string, force: bool = false): ApiResponse[JsonNode] =
112112
## Delete/purge a breeder by UUID
113+
## Set force=true to cancel workers immediately (default: false for safe deletion)
113114
try:
114-
let url = client.baseUrl() & "/breeders/" & encodeUrl(uuid)
115+
var url = client.baseUrl() & "/breeders/" & encodeUrl(uuid)
116+
if force:
117+
url = url & "?force=true"
115118
let response = client.httpClient.delete(url)
116119
result = handleResponse[JsonNode](client, response)
120+
except CatchableError as e:
121+
result = ApiResponse[JsonNode](success: false, data: nil, error: e.msg)
122+
123+
proc stopBreeder*(client: GodonClient, uuid: string): ApiResponse[JsonNode] =
124+
## Stop a breeder (graceful shutdown)
125+
try:
126+
let url = client.baseUrl() & "/breeders/" & encodeUrl(uuid) & "/stop"
127+
let response = client.httpClient.post(url)
128+
result = handleResponse[JsonNode](client, response)
129+
except CatchableError as e:
130+
result = ApiResponse[JsonNode](success: false, data: nil, error: e.msg)
131+
132+
proc startBreeder*(client: GodonClient, uuid: string): ApiResponse[JsonNode] =
133+
## Start/resume a stopped breeder
134+
try:
135+
let url = client.baseUrl() & "/breeders/" & encodeUrl(uuid) & "/start"
136+
let response = client.httpClient.post(url)
137+
result = handleResponse[JsonNode](client, response)
117138
except CatchableError as e:
118139
result = ApiResponse[JsonNode](success: false, data: nil, error: e.msg)

src/godon_cli.nim

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ Commands:
1919
breeder create --name <name> --file <path> Create a breeder from file
2020
breeder show --id <id> Show breeder details
2121
breeder update --file <path> Update a breeder from file
22-
breeder purge --id <id> Delete a breeder
23-
22+
breeder stop --id <id> Stop breeder workers (graceful shutdown)
23+
breeder start --id <id> Start/resume a stopped breeder
24+
breeder purge [--force] --id <id> Delete a breeder (use --force to cancel workers immediately)
25+
2426
credential list List all credentials
2527
credential create --file <path> Create a credential from file
2628
credential show --id <id> Show credential details (including content)
@@ -31,6 +33,7 @@ Global Options:
3133
--port, -p <port> Godon port (default: 8080)
3234
--api-version, -v <ver> API version (default: v0)
3335
--output, -o <format> Output format: text, json, or yaml (default: text)
36+
--force Force immediate deletion (skip graceful shutdown)
3437
--insecure Skip SSL certificate verification (HTTPS only)
3538
--help Show this help message
3639
@@ -39,6 +42,8 @@ Examples:
3942
godon_cli --hostname api.example.com --port 9090 breeder list
4043
godon_cli breeder create --name my-breeder --file breeder-config.yaml
4144
godon_cli breeder show --id 550e8400-e29b-41d4-a716-446655440000
45+
godon_cli breeder stop --id 550e8400-e29b-41d4-a716-446655440000
46+
godon_cli breeder purge --force --id 550e8400-e29b-41d4-a716-446655440000
4247
godon_cli credential list
4348
godon_cli credential create --file credential.yaml
4449
godon_cli credential show --id 550e8400-e29b-41d4-a716-446655440001
@@ -48,26 +53,27 @@ proc writeError(message: string) =
4853
stderr.writeLine("Error: " & message)
4954
quit(1)
5055

51-
proc parseArgs(): (string, string, int, string, bool, bool, OutputFormat, seq[string]) =
56+
proc parseArgs(): (string, string, int, string, bool, bool, bool, OutputFormat, seq[string]) =
5257
var command = ""
5358
var hostname = "localhost"
5459
var port = 8080
5560
var apiVersion = "v0"
5661
var insecure = false
5762
var debug = false
63+
var force = false
5864
var outputFormat = OutputFormat.Text
5965
var args: seq[string] = @[]
6066

6167
var p = initOptParser(commandLineParams())
62-
68+
6369
for kind, key, val in p.getopt():
6470
case kind
6571
of cmdArgument:
6672
if command.len == 0:
6773
command = key
6874
else:
6975
args.add(key)
70-
76+
7177
of cmdLongOption, cmdShortOption:
7278
case key.normalize()
7379
of "hostname":
@@ -90,6 +96,8 @@ proc parseArgs(): (string, string, int, string, bool, bool, OutputFormat, seq[st
9096
of "id":
9197
# Reconstruct as argument for subcommand parsing
9298
args.add("--id=" & val)
99+
of "force":
100+
force = true
93101
of "insecure":
94102
insecure = true
95103
of "debug":
@@ -111,7 +119,7 @@ proc parseArgs(): (string, string, int, string, bool, bool, OutputFormat, seq[st
111119
quit(0)
112120
else:
113121
writeError("Unknown option: " & key)
114-
122+
115123
of cmdEnd:
116124
discard
117125

@@ -123,7 +131,7 @@ proc parseArgs(): (string, string, int, string, bool, bool, OutputFormat, seq[st
123131
if existsEnv("DEBUG"):
124132
debug = true
125133

126-
(command, hostname, port, apiVersion, insecure, debug, outputFormat, args)
134+
(command, hostname, port, apiVersion, insecure, debug, force, outputFormat, args)
127135

128136
proc formatOutput*[T](data: T, outputFormat: OutputFormat) =
129137
## Format data according to output format preference
@@ -139,7 +147,7 @@ proc formatOutput*[T](data: T, outputFormat: OutputFormat) =
139147
var dumper = blockOnlyDumper()
140148
echo dumper.transform(jsonString)
141149

142-
proc handleBreederCommand(client: GodonClient, command: string, args: seq[string], outputFormat: OutputFormat) =
150+
proc handleBreederCommand(client: GodonClient, command: string, args: seq[string], force: bool, outputFormat: OutputFormat) =
143151
let subCommand = if args.len > 0: args[0] else: ""
144152

145153
case subCommand:
@@ -246,19 +254,61 @@ proc handleBreederCommand(client: GodonClient, command: string, args: seq[string
246254
if arg.startsWith("--id="):
247255
id = arg.split("=")[1]
248256
break
249-
257+
250258
if id.len == 0:
251259
writeError("breeder purge requires --id <id>")
252260

253-
let response = client.deleteBreeder(id)
261+
let response = client.deleteBreeder(id, force)
254262
if response.success:
255263
if outputFormat == OutputFormat.Text:
256-
echo "Breeder deleted successfully: ", id
264+
if force:
265+
echo "Breeder force deleted (workers cancelled): ", id
266+
else:
267+
echo "Breeder deleted: ", id
257268
else:
258269
formatOutput(response.data, outputFormat)
259270
else:
260271
writeError(response.error)
261-
272+
273+
of "stop":
274+
var id = ""
275+
for arg in args:
276+
if arg.startsWith("--id="):
277+
id = arg.split("=")[1]
278+
break
279+
280+
if id.len == 0:
281+
writeError("breeder stop requires --id <id>")
282+
283+
let response = client.stopBreeder(id)
284+
if response.success:
285+
if outputFormat == OutputFormat.Text:
286+
echo "Breeder stop requested (graceful shutdown): ", id
287+
echo "Workers will finish current trial before stopping."
288+
else:
289+
formatOutput(response.data, outputFormat)
290+
else:
291+
writeError(response.error)
292+
293+
of "start":
294+
var id = ""
295+
for arg in args:
296+
if arg.startsWith("--id="):
297+
id = arg.split("=")[1]
298+
break
299+
300+
if id.len == 0:
301+
writeError("breeder start requires --id <id>")
302+
303+
let response = client.startBreeder(id)
304+
if response.success:
305+
if outputFormat == OutputFormat.Text:
306+
echo "Breeder started/resumed: ", id
307+
else:
308+
formatOutput(response.data, outputFormat)
309+
else:
310+
writeError(response.error)
311+
262312
else:
263313
writeError("Unknown breeder command: " & subCommand)
264314

@@ -361,15 +411,15 @@ proc handleCredentialCommand(client: GodonClient, command: string, args: seq[str
361411
else:
362412
writeError("Unknown credential command: " & subCommand)
363413

364-
let (command, hostname, port, apiVersion, insecure, debug, outputFormat, args) = parseArgs()
414+
let (command, hostname, port, apiVersion, insecure, debug, force, outputFormat, args) = parseArgs()
365415

366416
let godonClient = newGodonClient(hostname, port, apiVersion, insecure, debug)
367417

368418
case command:
369419
of "breeder":
370420
if args.len == 0:
371-
writeError("breeder command requires a subcommand (list, create, show, update, purge)")
372-
handleBreederCommand(godonClient, command, args, outputFormat)
421+
writeError("breeder command requires a subcommand (list, create, show, update, stop, start, purge)")
422+
handleBreederCommand(godonClient, command, args, force, outputFormat)
373423

374424
of "credential":
375425
if args.len == 0:

0 commit comments

Comments
 (0)