diff --git a/tools/dashboard-deploy-test.py b/tools/dashboard-deploy-test.py index c6f8f1aa3515e02ddd68c6e938a26c887c26cf1a..677d6ad20d6f9392ebad13dd45eb538720706eb7 100644 --- a/tools/dashboard-deploy-test.py +++ b/tools/dashboard-deploy-test.py @@ -4,6 +4,7 @@ import json import os from pathlib import Path import subprocess +import shutil import tempfile import unittest import uuid @@ -58,36 +59,9 @@ class DeploymentBoundary(unittest.TestCase): def call(self, operation, **kwargs): return self.host.handle({"operation": operation, **kwargs}) - def test_iam_requests_are_bounded_before_any_worker_starts(self): - user = "/users/" + str(uuid.uuid4()) - bad = [ - ("/clients", "GET", None), ("/roles", "POST", {}), - ("/users?username=fixture&exact=true&first=0", "GET", None), - ("/users?username=fixture&exact=true&exact=true", "GET", None), - (user + "/../../clients", "GET", None), (user + "/users", "POST", {}), - (user, "PUT", {"realmRoles": ["admin"]}), - (user, "PUT", {"attributes": {"admin": ["true"]}}), - (user + "/role-mappings/realm", "POST", [{"id": "fixture", "name": "admin"}]), - (user + "/role-mappings/realm", "POST", [{"id": "fixture", "name": []}]), - (user + "/reset-password", "PUT", {"type": "password", "value": "password", "temporary": 1}), - (user + "/execute-actions-email", "PUT", ["arbitrary"]), - (user + "/logout", "POST", {"redirect": "https://elsewhere.invalid"}), - ] - with patch.object(runs.subprocess, "run") as process: - for path, method, body in bad: - with self.subTest(path=path), self.assertRaises(runs.Error): - self.call("iam.request", path=path, method=method, body=body) - for operation in ["get", "set", "rotate"]: - fields = {"service": "keycloak", "key": "password"} - if operation == "set": - fields["value"] = "known-password" - with self.assertRaises(runs.Error) as denied: - self.call("deploy.secret." + operation, **fields) - self.assertEqual(denied.exception.status, 403) - process.assert_not_called() - def test_requests_cannot_select_commands_paths_or_environment(self): - requests = [{"operation": "deploy.start", "action": "destroy", "target": self.stage, "argv": ["sh"]}, + requests = [{"operation": "iam.request", "path": "/users", "method": "GET", "body": None}, + {"operation": "deploy.start", "action": "destroy", "target": self.stage, "argv": ["sh"]}, {"operation": "deploy.start", "action": "deploy", "target": self.version, "env": {"PATH": "/tmp"}}, {"operation": "deploy.release", "release": "../private"}, {"operation": "deploy.run", "id": "../../nomad.token"}, @@ -113,6 +87,19 @@ class DeploymentBoundary(unittest.TestCase): with self.subTest(action=action), self.assertRaisesRegex(ValueError, "contents changed"): runs.command(action, target) + def test_destroy_verifies_releases_from_before_guest_sources_were_added(self): + with patch.object(release, "SOURCES", tuple(name for name in release.SOURCES if name != "guest")): + digest = release.tree_digest(self.snapshot) + shutil.rmtree(self.snapshot / "guest") + manifest = self.snapshot / ".studio-release.json" + details = json.loads(manifest.read_text()) + details["digest"] = digest + manifest.write_text(json.dumps(details)) + self.assertEqual(runs.command("destroy", self.stage)[1:], [str(self.snapshot / "tools/studio.py"), "destroy", self.stage]) + shutil.rmtree(self.snapshot / "tools") + with self.assertRaisesRegex(ValueError, "contents changed"): + runs.command("destroy", self.stage) + def test_service_control_is_limited_to_managed_names(self): self.assertEqual(self.call("deploy.managed"), ["fixture"]) for action in ["start", "stop", "restart"]: diff --git a/tools/release.py b/tools/release.py index 484240ff894b3ae90d4e85b24ab20d20fdd72163..20115985fc33e7f2360ff5c9218a108ee0feca72 100644 --- a/tools/release.py +++ b/tools/release.py @@ -29,6 +29,8 @@ def files(root): excluded = excluded_services(root) for name in SOURCES: source = root / name + if not source.exists() and not source.is_symlink(): + continue if name == "service" and source.is_dir(): paths = [] for directory, directories, filenames in os.walk(source):