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
3 changes: 2 additions & 1 deletion roles/github_runner_cluster/tasks/mesh/tailscale_api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@
tags: ["{{ github_runner_cluster_mesh_tag }}"]
# 90 days: comfortably under this tailnet's own observed maxKeyDuration (180 days), and short enough that a compromised key doesn't stay valid indefinitely - rotation itself is the manual step documented above.
expirySeconds: 7776000
description: "github-runner fleet vpn-auth join key (Ansible-managed)"
# Letters, digits, spaces and hyphens only: the API refuses a description with any other character ("keys: description had invalid characters"), parentheses, commas and colons included.
description: "github-runner fleet vpn-auth join key Ansible-managed"
status_code: [200]
delegate_to: localhost
no_log: true
Expand Down
35 changes: 35 additions & 0 deletions tests/unit/test_tailscale_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Tests for what the tailscale mesh provider's API tasks (roles/github_runner_cluster/tasks/mesh/tailscale_api.yml) send to Tailscale's API."""

from __future__ import annotations

import re
import unittest
from pathlib import Path
from typing import Any

import yaml

TASKS = Path(__file__).resolve().parents[2] / "roles" / "github_runner_cluster" / "tasks" / "mesh" / "tailscale_api.yml"


def key_request() -> dict[str, Any]:
"""The body of the request that creates the join key."""
tasks: list[dict[str, Any]] = yaml.safe_load(TASKS.read_text())
task = next(task for task in tasks if task.get("name", "").startswith("Create a reusable, pre-authorized Tailscale join key"))
body: dict[str, Any] = task["ansible.builtin.uri"]["body"]
return body


class JoinKeyRequestTest(unittest.TestCase):
def test_the_description_uses_only_characters_the_api_accepts(self) -> None:
# The API answers 400 "keys: description had invalid characters" for parentheses, commas and colons; letters, digits, spaces and hyphens were accepted.
self.assertRegex(key_request()["description"], re.compile(r"\A[A-Za-z0-9 -]+\Z"))

def test_the_key_is_reusable_preauthorised_tagged_and_not_ephemeral(self) -> None:
create = key_request()["capabilities"]["devices"]["create"]
self.assertEqual((create["reusable"], create["ephemeral"], create["preauthorized"]), (True, False, True))
self.assertEqual(create["tags"], ["{{ github_runner_cluster_mesh_tag }}"])


if __name__ == "__main__":
unittest.main()
Loading