Skip to content

Commit 22b6776

Browse files
committed
feat(docs): update architecture and state machines documentation
- Refactor architecture documentation to consolidate controller state machine information. - Add detailed state machines documentation for CRDs, including transitions and conditions. - Include state machines in the navigation of the operator manual. - Update requirements.txt for dependency management. - Modify mkdocs configuration to support Mermaid diagrams.
1 parent b9ea9b4 commit 22b6776

6 files changed

Lines changed: 176 additions & 40 deletions

File tree

.github/workflows/docs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
mkdocs-material-
3434
3535
- name: Install dependencies
36-
run: pip install mkdocs-material mike
36+
run: pip install -r requirements.txt
3737

3838
- name: Lint Markdown files
3939
uses: DavidAnson/markdownlint-cli2-action@ce4853d43830c74c1753b39f3cf40f71c2031eb9 # v23

docs/operator-manual/architecture.md

Lines changed: 5 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -57,45 +57,12 @@ The CLI used to start the different components is implemented using [`cobra`](ht
5757

5858
For more details on how the repository controller works with Git bundles and revisions, see the [Repository Controller documentation](./repository-controller.md).
5959

60-
### The TerraformLayer Controller
60+
### Controller state machines
6161

62-
The status of a `TerraformLayer` is defined using the [conditions standards defined by the community](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties).
63-
64-
3 conditions are defined for a layer:
65-
66-
- `IsPlanArtifactUpToDate`. This condition is used for drift detection. The evaluation is made by compraing the timestamp of the last `terraform plan` which ran and the current date. The timestamp of the last plan is "stored" using an annotation.
67-
- `IsApplyUpToDate`. This condition is used to check if an `apply` needs to run after the last `plan`. Comparison is made by comparing a checksum of the last planned binary and a checksum last applied binary stored in the annotations.
68-
- `IsLastRelevantCommitPlanned`. This condition is used to check if a new commit has been made to the layer and need to be applied. It is evaluated by comparing the commit used for the last `plan`, the last commit which intoduced changes to the layer and the last commit made to the same branch of the repository. Those commits are "stored" as annotations.
69-
70-
With those 3 conditions, we defined 3 states:
71-
72-
- `Idle`. This is the state of a layer if no runner needs be started
73-
- `PlanNeeded`. This is the state of a layer if burrito needs to start a `plan` runner
74-
- `ApplyNeeded`. This is the state of a layer if burrito needs to start an `apply` runner
75-
76-
!!! info
77-
If you use [`dry` remediation strategy](../user-guide/remediation-strategy.md) and an apply is needed, the layer will stay in the `ApplyNeeded` as long as it does not need to enter the `PlanNeeded`.
78-
79-
### The TerraformRun Controller
80-
81-
The status of a `TerraformRun` is also defined using the same [conditions standards defined by the community](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties).
82-
83-
5 conditions are defined for a run:
84-
85-
- `HasStatus`. This condition is used to check if a `TerraformRun` has already been reconciled by the controller.
86-
- `HasReachedRetryLimit`. Used to check if a `TerraformRun` has reached the maximum number of retries.
87-
- `HasSucceeded`. Used to check if a `TerraformRun` has already succeeded (runner pod exited successfully).
88-
- `IsRunning`. Used to check if a `TerraformRun` is currently running by checking the current phase of its associated pod.
89-
- `IsInfailureGracePeriod`. This condition is used to check if a Terraform workflow has already failed. If so, we use an exponential backoff strategy before restarting a runner on the given layer.
90-
91-
With those 5 conditions, we defined 6 states:
92-
93-
- `Initial`. This is the state of a run when it has just been created and has launched its first runner pod.
94-
- `Running`. This is the state of a run if a runner pod is currently running.
95-
- `FailureGracePeriod`. This is the state of a layer if a `plan` or `apply` runner has failed
96-
- `Retrying`. This is an intermediate state of a run if a runner pod has failed and is being restarted (not in failure grace period anymore).
97-
- `Succeeded`. This is one of the two final states a run can have. It means that the runner pod has exited successfully.
98-
- `Failed`. This is the other final state a run can have. It means that the run has failed multiple times and has reached the maximum number of retries.
62+
Each controller drives its resource through a state machine whose status is
63+
expressed using the [conditions standards defined by the community](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties).
64+
For the full state machines of each CRD, the conditions that drive them, and how
65+
the resources interact, see [CRD State Machines](./state-machines.md).
9966

10067
The `TerraformRun` controller also creates and deletes the [Kubernetes leases](https://kubernetes.io/docs/concepts/architecture/leases/) to avoid concurrent use of Terraform on the same layer.
10168

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# CRD State Machines
2+
3+
Each Burrito custom resource is driven by a state machine implemented in its
4+
reconciler (`internal/controllers/<crd>/states.go`). Burrito controllers are
5+
**level-based**: on every reconcile the controller *recomputes* the current state
6+
from a set of boolean conditions through an ordered `switch`. There are no stored
7+
transitions — a state's handler performs a side-effect (creating a `TerraformRun`,
8+
a runner pod, an annotation…), and the *next* reconcile observes the changed
9+
conditions and yields the next state.
10+
11+
A practical consequence: some states are **transient triggers**. For example a
12+
`TerraformLayer` in `PlanNeeded` creates a run and, on the following reconcile,
13+
is seen as running and reported as `Idle` again.
14+
15+
## TerraformRepository
16+
17+
The repository controller keeps Git bundles in the datastore in sync with the
18+
remote. It has two states, decided by whether the last sync is too old or failed.
19+
20+
- `Synced` — bundles are up to date; requeue at the repository sync interval.
21+
- `SyncNeeded` — fetch latest revisions per branch, store bundles, and annotate
22+
the affected layers.
23+
24+
```mermaid
25+
stateDiagram-v2
26+
[*] --> Synced
27+
Synced --> SyncNeeded: last sync too old / last sync failed
28+
SyncNeeded --> Synced: all branches synced successfully
29+
SyncNeeded --> SyncNeeded: sync error (requeue)
30+
```
31+
32+
## TerraformLayer
33+
34+
The layer controller watches declared layers and creates `TerraformRun` resources
35+
to plan (drift detection) and, when auto-apply is enabled, to apply. The state is
36+
computed from conditions such as *is a run in flight*, *is a sync scheduled*, *is
37+
the last plan too old*, *is the last relevant commit planned*, *is the apply up to
38+
date*, and *has the last run reached its retry limit*.
39+
40+
Because the first `switch` case is "a run is in flight → `Idle`", a layer with a
41+
running plan/apply is reported as `Idle`. `PlanNeeded` and `ApplyNeeded` are
42+
transient: their handler creates the run and the next reconcile returns to `Idle`.
43+
44+
- `Idle` — nothing to do; requeue at the drift-detection interval.
45+
- `PlanNeeded` — create a plan `TerraformRun`.
46+
- `ApplyNeeded` — create an apply `TerraformRun` (only if auto-apply is enabled).
47+
- `MaxRetriesReached` — the last run exhausted its retries; manual intervention
48+
needed.
49+
50+
```mermaid
51+
stateDiagram-v2
52+
[*] --> Idle
53+
Idle --> PlanNeeded: plan outdated / new relevant commit / sync scheduled
54+
Idle --> ApplyNeeded: plan up to date but apply needed (autoApply on)
55+
PlanNeeded --> Idle: TerraformRun (plan) created — run in flight
56+
ApplyNeeded --> Idle: TerraformRun (apply) created — run in flight
57+
Idle --> MaxRetriesReached: last run exhausted its retries
58+
MaxRetriesReached --> Idle: new relevant commit / retry window reset
59+
```
60+
61+
## TerraformRun
62+
63+
The run controller executes a plan or apply by creating runner pods, and handles
64+
retries with an exponential backoff grace period. `Succeeded` and `Failed` are
65+
terminal.
66+
67+
- `Initial` — freshly created; sets the lease and launches the first runner pod.
68+
- `Running` — a runner pod is currently running.
69+
- `FailureGracePeriod` — the runner failed; wait (exponential backoff) before retry.
70+
- `Retrying` — grace period elapsed and retry limit not reached; launch a new pod.
71+
- `Succeeded` — the runner pod exited successfully (lease released).
72+
- `Failed` — retries exhausted (lease released).
73+
74+
```mermaid
75+
stateDiagram-v2
76+
[*] --> Initial
77+
Initial --> Running: runner pod created
78+
Running --> Succeeded: pod exited successfully
79+
Running --> FailureGracePeriod: pod failed, retry limit not reached
80+
FailureGracePeriod --> Retrying: grace period elapsed
81+
FailureGracePeriod --> Failed: retry limit reached
82+
Retrying --> Running: retry pod created
83+
Succeeded --> [*]
84+
Failed --> [*]
85+
```
86+
87+
## TerraformPullRequest
88+
89+
The pull-request controller discovers layers affected by a PR/MR, waits for their
90+
plans, and posts a comment. Note that "comment up to date" is checked before
91+
"layers still planning", so a PR with an up-to-date comment is reported as `Idle`.
92+
93+
- `DiscoveryNeeded` — a new commit was detected; (re)create the temporary layers
94+
affected by the PR.
95+
- `Planning` — the temporary layers are still planning.
96+
- `CommentNeeded` — plans finished; post/update the PR comment.
97+
- `Idle` — comment is up to date; nothing to do.
98+
99+
```mermaid
100+
stateDiagram-v2
101+
[*] --> DiscoveryNeeded
102+
DiscoveryNeeded --> Planning: temp layers created, commit discovered
103+
Planning --> CommentNeeded: all layers finished planning
104+
CommentNeeded --> Idle: comment posted on PR/MR
105+
Idle --> DiscoveryNeeded: new commit on the PR branch
106+
```
107+
108+
## How the CRDs interact
109+
110+
The resources form a chain: the repository annotates layers with the latest
111+
relevant commit, layers create runs, runs create runner pods, and pods write plan
112+
and apply results back onto the layer's annotations. A pull request creates
113+
temporary layers and reports their plan results as a comment.
114+
115+
```mermaid
116+
flowchart LR
117+
Repo[TerraformRepository] -->|annotates last relevant commit| Layer[TerraformLayer]
118+
Layer -->|creates on plan/apply needed| Run[TerraformRun]
119+
Run -->|creates runner| Pod[Runner Pod]
120+
Pod -->|updates plan/apply annotations| Layer
121+
PR[TerraformPullRequest] -->|creates temp layers| Layer
122+
PR -->|reads plan results, comments| Git[Git provider]
123+
```

internal/controllers/AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ custom resource: `terraformlayer/`, `terraformrun/`, `terraformrepository/`,
1515
- Use structured logging via the `logr.Logger` carried in `ctx` (`log.FromContext(ctx)`).
1616
- Propagate `ctx` and set timeouts on every external call (GitHub, GitLab, Terraform).
1717
- CRD shapes live in `api/v1alpha1`; never edit generated deepcopy code. After API changes run `make manifests && make generate`.
18+
- **State machines are documented.** When you change a reconciler's states or transitions (its `states.go` — the `GetState` switch, state structs, or the conditions in `conditions.go` that drive them), update the matching Mermaid diagram in [`docs/operator-manual/state-machines.md`](../../docs/operator-manual/state-machines.md) so it stays in sync.

mkdocs.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ extra:
1212
markdown_extensions:
1313
- codehilite
1414
- admonition
15-
- pymdownx.superfences
15+
- pymdownx.superfences:
16+
# make exceptions to highlighting of code:
17+
custom_fences:
18+
- name: mermaid
19+
class: mermaid
20+
format: !!python/name:mermaid2.fence_mermaid_custom
1621
- toc:
1722
permalink: true
1823
nav:
@@ -30,6 +35,7 @@ nav:
3035
- Operator Manual:
3136
- operator-manual/index.md
3237
- operator-manual/architecture.md
38+
- operator-manual/state-machines.md
3339
- operator-manual/user-authentication.md
3440
- operator-manual/repository-controller.md
3541
- Git Authentication:

requirements.txt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
babel==2.18.0
2+
backrefs==7.0
3+
beautifulsoup4==4.15.0
4+
certifi==2026.6.17
5+
charset-normalizer==3.4.9
6+
click==8.4.2
7+
colorama==0.4.6
8+
editorconfig==0.17.1
9+
ghp-import==2.1.0
10+
idna==3.18
11+
jinja2==3.1.6
12+
jsbeautifier==2.0.3
13+
markdown==3.10.2
14+
markupsafe==3.0.3
15+
mergedeep==1.3.4
16+
mike==2.2.0
17+
mkdocs==1.6.1
18+
mkdocs-get-deps==0.2.2
19+
mkdocs-material==9.7.6
20+
mkdocs-material-extensions==1.3.1
21+
mkdocs-mermaid2-plugin==1.2.3
22+
packaging==26.2
23+
paginate==0.5.7
24+
pathspec==1.1.1
25+
platformdirs==4.10.0
26+
pygments==2.20.0
27+
pymdown-extensions==11.0.1
28+
pyparsing==3.3.2
29+
python-dateutil==2.9.0.post0
30+
pyyaml==6.0.3
31+
pyyaml-env-tag==1.1
32+
requests==2.34.2
33+
setuptools==83.0.0
34+
six==1.17.0
35+
soupsieve==2.8.4
36+
typing-extensions==4.16.0
37+
urllib3==2.7.0
38+
verspec==0.1.0
39+
watchdog==6.0.0

0 commit comments

Comments
 (0)