Skip to content

Commit d151c95

Browse files
authored
Making track.yaml the center of all the configurations so that OpenTO… (#61)
* Making track.yaml the center of all the configurations so that OpenTOFU and Ansible are based on the same data instead of hardcoding here and there the values. * Added services validation
1 parent 5b3014b commit d151c95

File tree

7 files changed

+384
-121
lines changed

7 files changed

+384
-121
lines changed

ctf/models.py

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
from __future__ import annotations
22

3-
from typing import Annotated, Any
3+
from typing import Annotated, Any, Literal
44

55
from pydantic import (
66
BaseModel,
7+
ConfigDict,
8+
Field,
9+
RootModel,
710
StringConstraints,
811
)
912

1013
IncusStr = Annotated[str, StringConstraints(pattern=r"^[a-z][a-z0-9\-]{0,61}[a-z0-9]$")]
14+
PortNumber = Annotated[int, Field(ge=1, le=65535)]
15+
CheckType = Literal["http", "https", "ssh", "tcp"]
1116

1217

1318
class Track(BaseModel):
@@ -49,3 +54,92 @@ def __str__(self) -> str:
4954

5055
def __repr__(self) -> str:
5156
return f'{self.__class__.__name__}(error_name="{self.error_name}", error_description="{self.error_description}", track_name="{self.track_name}", details= {self.details})'
57+
58+
59+
class TrackContacts(BaseModel):
60+
dev: list[str]
61+
qa: list[str]
62+
support: list[str]
63+
64+
65+
class InstanceConfig(BaseModel):
66+
model_config = ConfigDict(extra="allow")
67+
68+
69+
class InstanceDeviceProperties(BaseModel):
70+
model_config = ConfigDict(extra="allow")
71+
72+
73+
class InstanceDevice(BaseModel):
74+
name: str
75+
type: str
76+
properties: InstanceDeviceProperties
77+
78+
79+
class InstanceWaitFor(BaseModel):
80+
model_config = ConfigDict(extra="allow")
81+
type: str | None = None
82+
83+
84+
class InstanceService(BaseModel):
85+
name: str
86+
port: PortNumber
87+
check: CheckType
88+
dev_port_mapping: PortNumber | None = None
89+
90+
91+
class TrackInstance(BaseModel):
92+
is_build_container: bool = False
93+
image: str
94+
profiles: list[str]
95+
type: Literal["container", "virtual-machine"]
96+
description: str
97+
hwaddr: str | None = None
98+
record: str | None = None
99+
ipv6: str | None = None
100+
config: InstanceConfig
101+
devices: list[InstanceDevice]
102+
wait_for: InstanceWaitFor | None = None
103+
services: list[InstanceService]
104+
105+
106+
class TrackInstances(RootModel[dict[str, TrackInstance]]): ...
107+
108+
109+
class FlagTags(BaseModel):
110+
model_config = ConfigDict(extra="allow")
111+
discourse: str | None = None
112+
ui_sound: str | None = None
113+
ui_gif: str | None = None
114+
115+
116+
class TrackFlag(BaseModel):
117+
flag: str
118+
value: int
119+
description: str | None = None
120+
return_string: str
121+
cfss: str | None = None
122+
tags: FlagTags | None = None
123+
124+
125+
class DeprecatedTrackService(BaseModel):
126+
name: str
127+
instance: str
128+
address: str
129+
port: PortNumber
130+
check: CheckType
131+
dev_port_mapping: PortNumber | None = None
132+
133+
134+
class TrackYaml(BaseModel):
135+
name: str
136+
description: str
137+
integrated_with_scenario: bool
138+
contacts: TrackContacts
139+
instances: TrackInstances | None = None
140+
flags: list[TrackFlag]
141+
services: list[DeprecatedTrackService] | None = None
142+
143+
144+
# TrackInstances.model_rebuild()
145+
# TrackYaml.model_rebuild()

ctf/new.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,10 @@ def new(
126126
data={
127127
"name": name,
128128
"full_ipv6_address": full_ipv6_address,
129-
"ipv6_subnet": ipv6_subnet,
129+
"hardware_address": hardware_address,
130+
"is_windows": template == Template.WINDOWS_VM,
130131
"template": template.value,
132+
"with_build": with_build_container,
131133
}
132134
)
133135
with open(
@@ -201,8 +203,6 @@ def new(
201203
render = track_template.render(
202204
data={
203205
"name": name,
204-
"hardware_address": hardware_address,
205-
"ipv6": ipv6_address,
206206
"ipv6_subnet": ipv6_subnet,
207207
"full_ipv6_address": full_ipv6_address,
208208
"with_build": with_build_container,

ctf/templates/init/schemas/track.yaml.json

Lines changed: 135 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,139 @@
5454
"support"
5555
]
5656
},
57+
"instances": {
58+
"description": "Map of instance definitions for this track.",
59+
"type": "object",
60+
"additionalProperties": {
61+
"type": "object",
62+
"additionalProperties": false,
63+
"required": [
64+
"image",
65+
"profiles",
66+
"type",
67+
"description",
68+
"hwaddr",
69+
"record",
70+
"ipv6",
71+
"config",
72+
"devices",
73+
"services"
74+
],
75+
"properties": {
76+
"is_build_container": {
77+
"type": "boolean",
78+
"description": "Set to true for build-only containers."
79+
},
80+
"image": {
81+
"type": "string",
82+
"minLength": 1,
83+
"description": "Incus image reference used by this instance."
84+
},
85+
"profiles": {
86+
"type": "array",
87+
"minItems": 1,
88+
"items": {
89+
"type": "string",
90+
"minLength": 1
91+
},
92+
"description": "Incus profiles applied to this instance."
93+
},
94+
"type": {
95+
"type": "string",
96+
"enum": ["container", "virtual-machine"],
97+
"description": "Type of instance."
98+
},
99+
"description": {
100+
"type": "string",
101+
"minLength": 1,
102+
"description": "Instance description."
103+
},
104+
"hwaddr": {
105+
"type": ["string", "null"],
106+
"description": "MAC address of the instance network interface."
107+
},
108+
"record": {
109+
"type": ["string", "null"],
110+
"description": "DNS record prefix (without .ctf)."
111+
},
112+
"ipv6": {
113+
"type": ["string", "null"],
114+
"description": "IPv6 address of the instance."
115+
},
116+
"config": {
117+
"type": "object",
118+
"additionalProperties": true,
119+
"description": "Incus instance config values."
120+
},
121+
"devices": {
122+
"type": "array",
123+
"description": "Extra Incus devices for this instance.",
124+
"items": {
125+
"type": "object",
126+
"additionalProperties": false,
127+
"required": ["name", "type", "properties"],
128+
"properties": {
129+
"name": {
130+
"type": "string",
131+
"minLength": 1
132+
},
133+
"type": {
134+
"type": "string",
135+
"minLength": 1
136+
},
137+
"properties": {
138+
"type": "object",
139+
"additionalProperties": true
140+
}
141+
}
142+
}
143+
},
144+
"wait_for": {
145+
"type": "object",
146+
"description": "Optional wait condition for instance startup.",
147+
"additionalProperties": true
148+
},
149+
"services": {
150+
"description": "List of network services running on this instance.",
151+
"type": "array",
152+
"uniqueItems": true,
153+
"items": {
154+
"type": "object",
155+
"additionalProperties": false,
156+
"required": ["name", "port", "check"],
157+
"properties": {
158+
"name": {
159+
"description": "Name of the network service. Must be unique for a given instance.",
160+
"type": "string",
161+
"minLength": 1,
162+
"pattern": "^[a-zA-Z0-9-]+$"
163+
},
164+
"port": {
165+
"description": "Port number where the network service listens on.",
166+
"type": "integer",
167+
"minimum": 1,
168+
"maximum": 65535
169+
},
170+
"check": {
171+
"description": "Type of check used to validate service availability.",
172+
"type": "string",
173+
"enum": ["http", "https", "ssh", "tcp"]
174+
},
175+
"dev_port_mapping": {
176+
"type": "integer",
177+
"minimum": 1,
178+
"maximum": 65535,
179+
"description": "Optional localhost port mapping used during development."
180+
}
181+
}
182+
}
183+
}
184+
}
185+
}
186+
},
57187
"services": {
58-
"description": "List of network services used by the track.",
188+
"description": "DEPRECATED: List of network services used by the track.",
189+
"deprecated": true,
59190
"type": "array",
60191
"uniqueItems": true,
61192
"items": {
@@ -81,7 +212,9 @@
81212
},
82213
"port": {
83214
"description": "Port number where the network service listens on.",
84-
"type": "number"
215+
"type": "integer",
216+
"minimum": 1,
217+
"maximum": 65535
85218
},
86219
"check": {
87220
"description": "Type of check to do to make sure this service is up and alert us during the CTF if it's down.",
@@ -167,7 +300,6 @@
167300
"description",
168301
"integrated_with_scenario",
169302
"contacts",
170-
"services",
171303
"flags"
172304
]
173305
}

0 commit comments

Comments
 (0)