Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,19 @@ jobs:
$BINARY_PATH --help | grep -q "insecure" && echo "✅ --insecure flag documented in help" || echo "❌ --insecure flag missing from help"

# Create test YAML files using echo
echo 'name: "Test Breeder"' > test_breeder.yml
echo 'config: >' >> test_breeder.yml
echo ' {"setting1": "value1", "setting2": 42}' >> test_breeder.yml
echo 'meta:' > test_breeder.yml
echo ' configVersion: "0.2"' >> test_breeder.yml
echo 'breeder:' >> test_breeder.yml
echo ' type: "linux_performance"' >> test_breeder.yml
echo 'settings:' >> test_breeder.yml
echo ' sysctl:' >> test_breeder.yml
echo ' vm.swappiness:' >> test_breeder.yml
echo ' constraints:' >> test_breeder.yml
echo ' lower: 0' >> test_breeder.yml
echo ' upper: 100' >> test_breeder.yml

echo "Testing: breeder create"
$BINARY_PATH --hostname=localhost --port=4010 breeder create --file=test_breeder.yml
$BINARY_PATH --hostname=localhost --port=4010 breeder create --name="test-breeder" --file=test_breeder.yml

# Test breeder show with a mock UUID
echo "Testing: breeder show"
Expand All @@ -243,8 +250,10 @@ jobs:
echo 'uuid: "550e8400-e29b-41d4-a716-446655440000"' > test_breeder_update.yml
echo 'name: "Updated Test Breeder"' >> test_breeder_update.yml
echo 'description: "Updated integration test breeder"' >> test_breeder_update.yml
echo 'config: >' >> test_breeder_update.yml
echo ' {"setting1": "updated_value1", "setting2": 100, "new_setting": "new_value"}' >> test_breeder_update.yml
echo 'config:' >> test_breeder_update.yml
echo ' setting1: "updated_value1"' >> test_breeder_update.yml
echo ' setting2: 100' >> test_breeder_update.yml
echo ' new_setting: "new_value"' >> test_breeder_update.yml

echo "Testing: breeder update"
$BINARY_PATH --hostname=localhost --port=4010 breeder update --file=test_breeder_update.yml
Expand Down
3 changes: 2 additions & 1 deletion godon_cli.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ license = "AGPL-3.0"

# Dependencies

requires "nim >= 2.0.0", "yaml"
requires "nim >= 2.0.0"
requires "yaml == 2.1.1"

# Task definitions

Expand Down
64 changes: 43 additions & 21 deletions src/godon/breeder.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
## Implementation of breeder-related API endpoints

import std/[httpclient, json, strutils, uri]
import yaml
import yaml, yaml/tojson
import client, types

proc listBreeders*(client: GodonClient): ApiResponse[seq[BreederSummary]] =
Expand All @@ -18,10 +18,10 @@ proc createBreeder*(client: GodonClient, request: BreederCreateRequest): ApiResp
## Create a new breeder
try:
let url = client.baseUrl() & "/breeders"
# Convert config string to JsonNode
# Config is already a JsonNode, no parsing needed
var jsonData = %*{
"name": request.name,
"config": parseJson(request.config)
"config": request.config
}
echo "Sending JSON: ", $jsonData
client.httpClient.headers = newHttpHeaders({"Content-Type": "application/json"})
Expand All @@ -30,17 +30,24 @@ proc createBreeder*(client: GodonClient, request: BreederCreateRequest): ApiResp
except CatchableError as e:
result = ApiResponse[BreederSummary](success: false, data: default(BreederSummary), error: e.msg)

proc parseBreederFromYaml*(yamlContent: string): BreederCreateRequest =
## Parse breeder configuration from YAML content using yaml library
proc createBreederFromYamlWithName*(client: GodonClient, yamlContent: string, name: string): ApiResponse[BreederSummary] =
## Create a breeder from YAML content with explicit name parameter
## YAML contains only the config (no name field), name comes from parameter
try:
result = yaml.loadAs[BreederCreateRequest](yamlContent)
except CatchableError as e:
raise newException(ValueError, "Failed to parse YAML: " & e.msg)
# Parse YAML and convert to JsonNode
let jsonNodes = loadToJson(yamlContent)

proc createBreederFromYaml*(client: GodonClient, yamlContent: string): ApiResponse[BreederSummary] =
## Create a breeder from YAML content
try:
let request = parseBreederFromYaml(yamlContent)
# Take the first document (should be only one)
if jsonNodes.len == 0:
return ApiResponse[BreederSummary](success: false, data: default(BreederSummary), error: "No YAML documents found")

let configNode = jsonNodes[0]

# Create BreederCreateRequest with name from parameter and config from YAML
let request = BreederCreateRequest(
name: name,
config: configNode
)
result = client.createBreeder(request)
except CatchableError as e:
result = ApiResponse[BreederSummary](success: false, data: default(BreederSummary), error: e.msg)
Expand All @@ -58,7 +65,7 @@ proc updateBreeder*(client: GodonClient, request: BreederUpdateRequest): ApiResp
## Update an existing breeder
try:
let url = client.baseUrl() & "/breeders/" & encodeUrl(request.uuid)
# Convert config string to JsonNode
# Config is a string (JSON), parse it first
var jsonData = %*{
"name": request.name,
"description": request.description,
Expand All @@ -70,17 +77,32 @@ proc updateBreeder*(client: GodonClient, request: BreederUpdateRequest): ApiResp
except CatchableError as e:
result = ApiResponse[Breeder](success: false, data: default(Breeder), error: e.msg)

proc parseBreederUpdateFromYaml*(yamlContent: string): BreederUpdateRequest =
## Parse breeder update configuration from YAML content
try:
result = yaml.loadAs[BreederUpdateRequest](yamlContent)
except CatchableError as e:
raise newException(ValueError, "Failed to parse YAML: " & e.msg)

proc updateBreederFromYaml*(client: GodonClient, yamlContent: string): ApiResponse[Breeder] =
## Update a breeder from YAML content
try:
let request = parseBreederUpdateFromYaml(yamlContent)
# Parse YAML and convert to JsonNode
let jsonNodes = loadToJson(yamlContent)

# Take the first document (should be only one)
if jsonNodes.len == 0:
return ApiResponse[Breeder](success: false, data: default(Breeder), error: "No YAML documents found")

let yamlData = jsonNodes[0]

# Extract fields from YAML
let uuid = if yamlData.hasKey("uuid"): yamlData["uuid"].getStr() else: ""
let name = if yamlData.hasKey("name"): yamlData["name"].getStr() else: ""
let description = if yamlData.hasKey("description"): yamlData["description"].getStr() else: ""

# Config should be a nested object - convert to JSON string
let config = if yamlData.hasKey("config"): $yamlData["config"] else: "{}"

let request = BreederUpdateRequest(
uuid: uuid,
name: name,
description: description,
config: config
)
result = client.updateBreeder(request)
except CatchableError as e:
result = ApiResponse[Breeder](success: false, data: default(Breeder), error: e.msg)
Expand Down
2 changes: 1 addition & 1 deletion src/godon/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type

BreederCreateRequest* = object
name*: string
config*: string
config*: JsonNode

BreederUpdateRequest* = object
uuid*: string
Expand Down
26 changes: 17 additions & 9 deletions src/godon_cli.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Usage:

Commands:
breeder list List all configured breeders
breeder create --file <path> Create a breeder from file
breeder create --name <name> --file <path> Create a breeder from file
breeder show --id <id> Show breeder details
breeder update --file <path> Update a breeder from file
breeder purge --id <id> Delete a breeder
Expand All @@ -29,7 +29,7 @@ Global Options:
Examples:
godon_cli breeder list
godon_cli --hostname api.example.com --port 9090 breeder list
godon_cli breeder create --file breeder.yaml
godon_cli breeder create --name my-breeder --file breeder-config.yaml
godon_cli breeder show --id 550e8400-e29b-41d4-a716-446655440000
godon_cli credential list
godon_cli credential create --file credential.yaml
Expand Down Expand Up @@ -74,8 +74,11 @@ proc parseArgs(): (string, string, int, string, bool, seq[string]) =
of "file":
# Reconstruct as argument for subcommand parsing
args.add("--file=" & val)
of "name":
# Reconstruct as argument for subcommand parsing
args.add("--name=" & val)
of "id":
# Reconstruct as argument for subcommand parsing
# Reconstruct as argument for subcommand parsing
args.add("--id=" & val)
of "insecure":
insecure = true
Expand Down Expand Up @@ -114,20 +117,25 @@ proc handleBreederCommand(client: GodonClient, command: string, args: seq[string

of "create":
var file = ""
var name = ""
for arg in args:
if arg.startsWith("--file="):
file = arg.split("=")[1]
break

elif arg.startsWith("--name="):
name = arg.split("=")[1]

if file.len == 0:
writeError("breeder create requires --file <path>")


if name.len == 0:
writeError("breeder create requires --name <name>")

if not fileExists(file):
writeError("File not found: " & file)
echo "Creating breeder from file: ", file

echo "Creating breeder '", name, "' from file: ", file
let content = readFile(file)
let response = client.createBreederFromYaml(content)
let response = client.createBreederFromYamlWithName(content, name)
if response.success:
echo "Breeder created successfully:"
echo " ID: ", response.data.id
Expand Down