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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ Notable changes to Cloudfall. The format follows

### Changed

- **Breaking:** `cloudfall-engine` runs on [treaty](https://github.com/romamo/treaty).
Every command answers one JSON envelope on stdout (`ok`, `data`, `error`,
`warnings`, `meta`), errors included, which used to go to stderr in their
own shape; `inventory render` puts the inventory under `data.inventory`
(the `--output` file is unchanged). Failures exit with their own codes:
79 `PROJECT_INVALID`, 80 `CONFIG_INVALID`, 81 `ARTIFACT_BUILD_FAILED`,
82 `PLAYBOOK_FAILED`; the old snake_case code is in `error.context.code`.
A missing playbook, inventory, or role directory exits 2 before Ansible
starts. `playbook run --check` is now `--dry-run`; Ansible's play log
streams to stderr. `cloudfall-engine manifest` describes every command
- Every JSON document has the same six top-level keys: `ok` (true exactly
when the exit code is 0), `status` (the command's verdict, such as `ok`,
`plan`, `drift` or `unhealthy`), `data` (the command's payload, which
Expand Down
24 changes: 22 additions & 2 deletions engine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,32 @@ uv run cloudfall-engine playbook run engine/ansible/playbooks/proxy.yml \
--roles engine/ansible/roles --limit proxy --tags nginx
```

`--check`, `--diff`, `--syntax-check`, `--limit`, `--tags`, and `--extra-vars`
pass through to `ansible-playbook`. A project uses this instead of
`--dry-run` (Ansible's check mode), `--diff`, `--syntax-check`, `--limit`,
`--tags`, and `--extra-vars` pass through to `ansible-playbook`. Ansible's play
log streams to stderr as it runs; stdout carries one JSON document when the run
ends. A project uses this instead of
setting `ANSIBLE_CONFIG` against a checkout; the `task` targets in this
repository still call `ansible-playbook` directly because they run from the
source tree.

## Output and exit codes

`cloudfall-engine` is built on [treaty](https://github.com/romamo/treaty): every
command answers one JSON envelope on stdout (`ok`, `data`, `error`, `warnings`,
`meta`) and `cloudfall-engine manifest` describes every command, flag, and exit
code. Input errors exit 2 before anything runs. Engine failures have their own
codes, with the original snake_case code in `error.context.code`:

| Exit | Code | Meaning |
|---|---|---|
| 79 | `PROJECT_INVALID` | no project directory could be resolved |
| 80 | `CONFIG_INVALID` | the project's resources failed validation |
| 81 | `ARTIFACT_BUILD_FAILED` | the component could not be cloned or packaged |
| 82 | `PLAYBOOK_FAILED` | `ansible-playbook` exited non-zero; `error.context.returncode` has its code |

Relative paths such as `--output tmp/ansible-inventory.json` and the default
`--output-dir tmp/artifacts` land in the project, whatever the working directory.

## UTC time baseline

`ansible/playbooks/time.yml` enforces `Etc/UTC`, keeps the hardware clock in
Expand Down
10 changes: 5 additions & 5 deletions engine/src/cloudfall_engine/artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
from cloudfall.inventory import ComponentInventory, PlatformInventory

_ARTIFACT_SCHEMA = "artifact.schema.json"
_GIT_TIMEOUT_SECONDS = 600
GIT_TIMEOUT_SECONDS = 600
_SHORT_COMMIT_LENGTH = 7
_GIT_REF_PATTERN = re.compile(r"[A-Za-z0-9][A-Za-z0-9._/@^~-]{0,199}")
GIT_REF_PATTERN = re.compile(r"[A-Za-z0-9][A-Za-z0-9._/@^~-]{0,199}")
_ERROR_COMPONENT_MISSING = "artifact_component_missing"
_ERROR_SUBDIRECTORY_MISSING = "artifact_subdirectory_missing"
_ERROR_REF_INVALID = "artifact_ref_invalid"
Expand Down Expand Up @@ -192,7 +192,7 @@ def _component(


def _validate_ref(git_ref: str) -> None:
if not _GIT_REF_PATTERN.fullmatch(git_ref):
if not GIT_REF_PATTERN.fullmatch(git_ref):
detail = f"git ref contains unsupported characters: {git_ref!r}"
raise ArtifactBuildError(_ERROR_REF_INVALID, detail)

Expand Down Expand Up @@ -228,7 +228,7 @@ def _run_git(*arguments: str) -> str:
capture_output=True,
check=True,
text=True,
timeout=_GIT_TIMEOUT_SECONDS,
timeout=GIT_TIMEOUT_SECONDS,
)
except subprocess.CalledProcessError as error:
detail = (
Expand All @@ -238,7 +238,7 @@ def _run_git(*arguments: str) -> str:
except subprocess.TimeoutExpired as error:
detail = (
f"git {' '.join(arguments[:2])} exceeded "
f"{_GIT_TIMEOUT_SECONDS} seconds"
f"{GIT_TIMEOUT_SECONDS} seconds"
)
raise ArtifactBuildError(_ERROR_GIT_TIMEOUT, detail) from error
return completed.stdout
Expand Down
Loading
Loading