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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ jobs:
- name: Test deployment looping through tracks
working-directory: test-ctf
run: |
IFS=" " read -r -a tracks <<< "$(python3 -c 'from ctf.utils import get_all_available_tracks,track_has_virtual_machine,validate_track_can_be_deployed;print(str([t.name for t in get_all_available_tracks() if validate_track_can_be_deployed(t) and not track_has_virtual_machine(t)]).strip("[]\x27").replace("\x27, \x27"," "))')"
IFS=" " read -r -a tracks <<< "$(python3 -c 'from ctf.common.utils import get_all_available_tracks,track_has_virtual_machine,validate_track_can_be_deployed;print(str([t.name for t in get_all_available_tracks() if validate_track_can_be_deployed(t) and not track_has_virtual_machine(t)]).strip("[]\x27").replace("\x27, \x27"," "))')"

[ "${#tracks[@]}" -eq 0 ] && exit 1

Expand Down
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Breaking changes

- **`ctf deploy` no longer passes `ansible_incus_remote` as an Ansible extra variable.**
- **`ctf deploy` no longer passes `ansible_incus_remote` as an Ansible extra variable.**
`ansible-playbook` with `-e ansible_incus_remote=<value>` overrides inventory on every host, which broke mixed containers / VMs deployments.

**Migration:** Do not depend on `ansible_incus_remote` being injected by deploy for
playbook-wide VM/cluster targeting.

Use something like:
```
vars:
Expand Down
58 changes: 21 additions & 37 deletions ctf/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
import json
import logging
import os
import pathlib
import sys
import time
import urllib.request
from pathlib import Path

import rich
import typer
Expand All @@ -14,23 +13,23 @@
from typing_extensions import Annotated

from ctf import ENV, STATE
from ctf.askgod import app as askgod_app
from ctf.check import app as check_app
from ctf.deploy import app as deploy_app
from ctf.destroy import app as destroy_app
from ctf.flags import app as flags_app
from ctf.generate import app as generate_app
from ctf.init import app as init_app
from ctf.list import app as list_app
from ctf.logger import LOG
from ctf.new import app as new_app
from ctf.post import app as post_app
from ctf.redeploy import app as redeploy_app
from ctf.services import app as services_app
from ctf.stats import app as stats_app
from ctf.utils import find_ctf_root_directory, get_version, show_version
from ctf.validate import app as validate_app
from ctf.version import app as version_app
from ctf.commands.askgod import app as askgod_app
from ctf.commands.check import app as check_app
from ctf.commands.deploy import app as deploy_app
from ctf.commands.destroy import app as destroy_app
from ctf.commands.flags import app as flags_app
from ctf.commands.generate import app as generate_app
from ctf.commands.init import app as init_app
from ctf.commands.list import app as list_app
from ctf.commands.new import app as new_app
from ctf.commands.post import app as post_app
from ctf.commands.redeploy import app as redeploy_app
from ctf.commands.services import app as services_app
from ctf.commands.stats import app as stats_app
from ctf.commands.validate import app as validate_app
from ctf.commands.version import app as version_app
from ctf.common.logger import LOG
from ctf.common.utils import get_version, show_version

app = typer.Typer(
help="CLI tool to manage CTF challenges as code. Run from the root CTF repo directory or set the CTF_ROOT_DIR environment variable to run the tool.",
Expand Down Expand Up @@ -65,13 +64,12 @@


def check_tool_version() -> None:

# Check at most once per day
stamp = (
pathlib.Path(
stamp: Path = (
Path(
os.environ.get(
"XDG_CACHE_HOME",
str(pathlib.Path(os.environ.get("HOME", "~")).expanduser() / ".cache"),
Path(os.environ.get("HOME", "~")).expanduser() / ".cache",
)
)
/ "ctf-script"
Expand Down Expand Up @@ -188,17 +186,3 @@ def main():
console = rich.get_console()
console.width = 150 if console.width < 150 else console.width
app()


if __name__ == "__main__":
import sys

if "version" not in sys.argv and "init" not in sys.argv:
if not os.path.isdir(
s=(p := os.path.join(find_ctf_root_directory(), "challenges"))
):
LOG.error(
msg=f"Directory `{p}` not found. Make sure this script is ran from the root directory OR set the CTF_ROOT_DIR environment variable to the root directory."
)
exit(code=1)
main()
File renamed without changes.
2 changes: 1 addition & 1 deletion ctf/askgod/stats.py → ctf/commands/askgod/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import typer
from typing_extensions import Annotated

from ctf.logger import LOG
from ctf.common.logger import LOG

app = typer.Typer()

Expand Down
9 changes: 4 additions & 5 deletions ctf/check.py → ctf/commands/check.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import os
import subprocess

import typer
from typing_extensions import Annotated

from ctf import ENV
from ctf.generate import generate
from ctf.logger import LOG
from ctf.utils import check_git_lfs, find_ctf_root_directory, terraform_binary
from ctf.commands.generate import generate
from ctf.common.logger import LOG
from ctf.common.utils import check_git_lfs, find_ctf_root_directory, terraform_binary

app = typer.Typer()

Expand Down Expand Up @@ -46,7 +45,7 @@ def check(
# Then run terraform plan.
subprocess.run(
args=[terraform_binary(), "plan"],
cwd=os.path.join(find_ctf_root_directory(), ".deploy"),
cwd=find_ctf_root_directory() / ".deploy",
check=True,
)

Expand Down
56 changes: 25 additions & 31 deletions ctf/deploy.py → ctf/commands/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@
import subprocess
import textwrap
import time
from pathlib import Path

import typer
from rich.prompt import IntPrompt
from typing_extensions import Annotated

from ctf import ENV, STATE
from ctf.destroy import destroy
from ctf.generate import generate
from ctf.logger import LOG
from ctf.models import Track, TrackYaml
from ctf.utils import (
from ctf.commands.destroy import destroy
from ctf.commands.generate import generate
from ctf.common.logger import LOG
from ctf.common.models import Track, TrackYaml
from ctf.common.utils import (
add_tracks_to_terraform_modules,
check_git_lfs,
find_ctf_root_directory,
Expand Down Expand Up @@ -122,7 +123,7 @@ def deploy(
LOG.critical(
msg="Git LFS is missing from your system. Install it before deploying."
)
exit(code=1)
exit(1)

# Pull LFS files
LOG.debug("Pulling Git LFS files for specific tracks.")
Expand Down Expand Up @@ -161,9 +162,7 @@ def deploy(
remote=remote,
production=production,
track=track.name,
path=os.path.join(
find_ctf_root_directory(), "challenges", track.name, "ansible"
),
path=find_ctf_root_directory() / "challenges" / track.name / "ansible",
playbook="build.yaml",
vm_remote=vm_remote,
vm_project=vm_project,
Expand All @@ -187,18 +186,14 @@ def deploy(
):
distinct_tracks = regenerated_tracks

if not os.path.exists(
path=(
path := os.path.join(
find_ctf_root_directory(), "challenges", track.name, "ansible"
)
)
):
if not (
path := find_ctf_root_directory() / "challenges" / track.name / "ansible"
).exists():
continue

if track.has_virtual_machine:
incus_list = json.loads(
s=subprocess.run(
subprocess.run(
args=["incus", "list", f"--project={track}", "--format", "json"],
check=True,
capture_output=True,
Expand Down Expand Up @@ -283,10 +278,10 @@ def deploy(
)[0]["address"]
ipv6_to_container_name[ipv6_address] = machine["name"]

LOG.debug(msg=f"Mapping: {ipv6_to_container_name}")
LOG.debug(f"Mapping: {ipv6_to_container_name}")

if remote == "local":
LOG.debug(msg=f"Parsing track.yaml for track {track}")
LOG.debug(f"Parsing track.yaml for track {track}")
track_yaml: TrackYaml = TrackYaml.model_validate(
parse_track_yaml(track_name=track.name)
)
Expand Down Expand Up @@ -342,13 +337,13 @@ def deploy(
check=True,
)

LOG.info(msg=f"Running `incus --project={track} list`")
LOG.info(f"Running `incus --project={track} list`")
subprocess.run(
args=["incus", f"--project={track}", "list"], check=True, env=ENV
)

if distinct_tracks:
LOG.info(msg="Applying post-deploy Terraform resources...")
LOG.info("Applying post-deploy Terraform resources...")
try:
terraform_apply(
tracks=tracks,
Expand All @@ -362,7 +357,7 @@ def deploy(
LOG.critical(
"Could not apply post-deploy Terraform resources. Fix the Terraform configuration and rerun `ctf deploy`."
)
exit(code=1)
exit(1)

if not production and distinct_tracks:
tracks_list = list(distinct_tracks)
Expand Down Expand Up @@ -408,7 +403,7 @@ def terraform_apply(
try:
subprocess.run(
args=args,
cwd=os.path.join(find_ctf_root_directory(), ".deploy"),
cwd=find_ctf_root_directory() / ".deploy",
check=True,
)
except subprocess.CalledProcessError:
Expand Down Expand Up @@ -441,7 +436,7 @@ def terraform_apply(

subprocess.run(
args=args,
cwd=os.path.join(find_ctf_root_directory(), ".deploy"),
cwd=find_ctf_root_directory() / ".deploy",
check=True,
)

Expand Down Expand Up @@ -469,7 +464,7 @@ def terraform_apply(
remote=remote,
force=True,
)
exit(code=0)
exit(0)

return set()

Expand All @@ -478,7 +473,7 @@ def run_ansible_playbook(
remote: str,
production: bool,
track: str,
path: str,
path: Path,
playbook: str = "deploy.yaml",
vm_remote: str | None = None,
vm_project: str | None = None,
Expand All @@ -503,7 +498,7 @@ def run_ansible_playbook(
extra_args += ["-e", "nsec_production=true"]

if not skip_pre_common and execute_common:
LOG.info(msg=f"Running pre-common.yaml with ansible for track {track}...")
LOG.info(f"Running pre-common.yaml with ansible for track {track}...")
ansible_args = [
"ansible-playbook",
os.path.join("..", "..", "..", ".deploy", "ansible", "common.yaml"),
Expand All @@ -516,7 +511,7 @@ def run_ansible_playbook(
check=True,
)

LOG.info(msg=f"Running {playbook} with ansible for track {track}...")
LOG.info(f"Running {playbook} with ansible for track {track}...")
ansible_args = [
"ansible-playbook",
playbook,
Expand All @@ -526,7 +521,7 @@ def run_ansible_playbook(
subprocess.run(args=ansible_args, cwd=path, check=True)

if not skip_post_common and execute_common:
LOG.info(msg=f"Running post-common.yaml with ansible for track {track}...")
LOG.info(f"Running post-common.yaml with ansible for track {track}...")
ansible_args = (
[
"ansible-playbook",
Expand All @@ -543,6 +538,5 @@ def run_ansible_playbook(
check=True,
)

artifacts_path = os.path.join(path, "artifacts")
if os.path.exists(path=artifacts_path):
if (artifacts_path := path / "artifacts").exists():
shutil.rmtree(artifacts_path)
Loading
Loading