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
18 changes: 18 additions & 0 deletions roles/github_runner_cluster/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,25 @@
mode: "0600"
no_log: true

- name: Read whether the agent's Compose project directory exists
# Under --check on a first run the directory was not created above, and Compose cannot read a project directory that does not exist.
ansible.builtin.stat:
path: "{{ github_runner_cluster_dir }}"
register: github_runner_cluster_dir_state
check_mode: false

- name: Report that the agent would be started in a project directory this run creates
ansible.builtin.debug:
msg: >-
Would create {{ github_runner_cluster_dir }} and start the k3s agent there, as {{ github_runner_cluster_node_container }}
in the Compose project {{ github_runner_cluster_compose_project }}.
changed_when: true
when:
- ansible_check_mode
- not github_runner_cluster_dir_state.stat.exists

- name: Join the cluster as an agent
when: not (ansible_check_mode and not github_runner_cluster_dir_state.stat.exists)
community.docker.docker_compose_v2:
project_src: "{{ github_runner_cluster_dir }}"
project_name: "{{ github_runner_cluster_compose_project }}"
Expand Down
18 changes: 18 additions & 0 deletions roles/github_runner_cluster/tasks/start_server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,25 @@
- name: Check whether Compose would recreate this server's container, and refuse if that would take quorum away
ansible.builtin.include_tasks: server_recreate_check.yml

- name: Read whether this server's Compose project directory exists
# Under --check on a first run the role has created no directory, and Compose cannot read a project directory that does not exist, so the start below is skipped and reported instead.
ansible.builtin.stat:
path: "{{ github_runner_cluster_dir }}"
register: github_runner_cluster_dir_state
check_mode: false

- name: Report that the server would be started in a project directory this run creates
ansible.builtin.debug:
msg: >-
Would create {{ github_runner_cluster_dir }} and start the k3s server there, as {{ github_runner_cluster_node_container }}
in the Compose project {{ github_runner_cluster_compose_project }}.
changed_when: true
when:
- ansible_check_mode
- not github_runner_cluster_dir_state.stat.exists

- name: Start k3s via docker compose (bootstrapping or joining, per this host's own .env)
when: not (ansible_check_mode and not github_runner_cluster_dir_state.stat.exists)
community.docker.docker_compose_v2:
project_src: "{{ github_runner_cluster_dir }}"
project_name: "{{ github_runner_cluster_compose_project }}"
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/cluster_stand_ins.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ def hosts(self, servers: tuple[str, ...], agents: tuple[str, ...] = (), **extra:
hosts: dict[str, Any] = {}
for name in (*servers, *agents):
server = name in servers
# Every earlier run of the role created the node's Compose project directory, which the role reads before starting Compose under --check.
(self.work / "nodes" / name).mkdir(parents=True, exist_ok=True)
hosts[name] = {
"github_runner_cluster_effective_node_role": "server" if server else "agent",
"github_runner_cluster_effective_node_name": name,
Expand Down
28 changes: 24 additions & 4 deletions tests/unit/test_server_recreate.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,26 @@ def test_check_mode_reports_every_recreate_and_changes_nothing(self) -> None:
self.assertEqual([call for call in self.calls() if " kubectl get lease " in call], [])
self.assertEqual((self.stub / "cluster.nodes").read_text().split(), ["node1", "True", "node2", "True", "node3", "True"])

def test_check_mode_on_a_first_run_reports_the_start_it_cannot_ask_compose_about(self) -> None:
# A first run's dry run creates no project directory, and Compose cannot read a project directory that does not exist.
hosts, group_vars = self.hosts(("node1", "node2", "node3"))
for name in hosts:
(self.work / "nodes" / name).rmdir()
result = self.run_tasks("start_servers", hosts, group_vars, check=True)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
for n in (1, 2, 3):
self.assertIn(f"Would create {self.work / 'nodes' / f'node{n}'} and start the k3s server there, as node{n}-k3s", result.stdout.replace("\n", " "))
self.assertEqual([call for call in self.calls() if call.startswith("module up")], [])
self.assertFalse((self.work / "nodes").joinpath("node1").exists())

def test_check_mode_asks_compose_once_the_project_directory_exists(self) -> None:
for n in (1, 2, 3):
self.rendered(f"node{n}", HASH_NEW)
result = self.start(check=True)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertCountEqual([call for call in self.calls() if call.startswith("module up")], [f"module up node{n} profiles=server check=True" for n in (1, 2, 3)])
self.assertNotIn("Would create", result.stdout)

def test_check_mode_refuses_what_a_run_would_refuse(self) -> None:
self.running_cluster({"node1": HASH_OLD, "node2": HASH_NEW, "node3": HASH_NEW})
self.nodes(node1="True", node2="False", node3="True")
Expand Down Expand Up @@ -468,10 +488,10 @@ def test_servers_start_through_their_turns_and_agents_do_not(self) -> None:

def test_a_turn_checks_before_it_starts_and_waits_only_afterwards(self) -> None:
tasks = role_tasks("start_server.yml")
order = [task.get("ansible.builtin.include_tasks") or next(key for key in task if key.startswith("community.docker")) for task in tasks]
self.assertEqual(order, ["server_recreate_check.yml", "community.docker.docker_compose_v2", "server_ready.yml"])
self.assertEqual(tasks[1]["community.docker.docker_compose_v2"]["profiles"], ["server"])
self.assertIn("not ansible_check_mode", tasks[2]["when"])
order = [task.get("ansible.builtin.include_tasks") or next(key for key in task if key.startswith(("community.docker", "ansible.builtin."))) for task in tasks]
self.assertEqual(order, ["server_recreate_check.yml", "ansible.builtin.stat", "ansible.builtin.debug", "community.docker.docker_compose_v2", "server_ready.yml"])
self.assertEqual(tasks[3]["community.docker.docker_compose_v2"]["profiles"], ["server"])
self.assertIn("not ansible_check_mode", tasks[4]["when"])

def test_the_turns_loop_over_the_servers_in_the_play_and_run_for_one_host_each(self) -> None:
(task,) = role_tasks("start_servers.yml")
Expand Down
Loading