diff --git a/log-config-dev.yaml b/log-config-dev.yaml index 604be08..8075887 100644 --- a/log-config-dev.yaml +++ b/log-config-dev.yaml @@ -3,6 +3,7 @@ disable_existing_loggers: false formatters: rich: datefmt: "[%X]" + format: "%(name)25s %(message)s" handlers: console: class: rich.logging.RichHandler diff --git a/src/runboat/api.py b/src/runboat/api.py index bfb797c..7e8fa25 100644 --- a/src/runboat/api.py +++ b/src/runboat/api.py @@ -40,7 +40,7 @@ class Build(BaseModel): repo: str target_branch: str pr: Optional[int] - commit: str + git_commit: str image: str link: str status: models.BuildStatus @@ -92,11 +92,11 @@ async def trigger_branch(org: str, repo: str, branch: str): """Trigger build for a branch.""" # TODO async github call branch_info = github.get_branch_info(org, repo, branch) - await models.Build.deploy( + await controller.deploy_or_delay_start( repo=f"{branch_info.org}/{branch_info.repo}", target_branch=branch_info.name, pr=None, - commit=branch_info.head_sha, + git_commit=branch_info.head_sha, ) @@ -109,11 +109,11 @@ async def trigger_pull(org: str, repo: str, pr: int): """Trigger build for a pull request.""" # TODO async github call pull_info = github.get_pull_info(org, repo, pr) - await models.Build.deploy( + await controller.deploy_or_delay_start( repo=f"{pull_info.org}/{pull_info.repo}", target_branch=pull_info.target_branch, pr=pull_info.number, - commit=pull_info.head_sha, + git_commit=pull_info.head_sha, ) diff --git a/src/runboat/controller.py b/src/runboat/controller.py index 52a197c..9112493 100644 --- a/src/runboat/controller.py +++ b/src/runboat/controller.py @@ -60,6 +60,25 @@ class Controller: def max_deployed(self) -> int: return settings.max_deployed + async def deploy_or_delay_start( + self, repo: str, target_branch: str, pr: int | None, git_commit: str + ) -> None: + build = self.db.get_for_commit( + repo=repo, + target_branch=target_branch, + pr=pr, + git_commit=git_commit, + ) + if build is not None: + await build.delay_start() + return + await Build.deploy( + repo=repo, + target_branch=target_branch, + pr=pr, + git_commit=git_commit, + ) + def _wakeup(self) -> None: self._wakeup_event.set() self._wakeup_event.clear() diff --git a/src/runboat/db.py b/src/runboat/db.py index a0a568e..36c690f 100644 --- a/src/runboat/db.py +++ b/src/runboat/db.py @@ -33,7 +33,7 @@ class BuildsDb: " repo TEXT NOT NULL, " " target_branch TEXT NOT NULL, " " pr INTEGER, " - " 'commit' TEXT NOT NULL, " + " git_commit TEXT NOT NULL, " " image TEXT NOT NULL," " status TEXT NOT NULL, " " todo TEXT, " @@ -51,6 +51,21 @@ class BuildsDb: return None return self._build_from_row(row) + def get_for_commit( + self, repo: str, target_branch: str, pr: int | None, git_commit: str + ) -> Build | None: + query = "SELECT * FROM builds WHERE repo=? AND target_branch=? AND git_commit=?" + params = [repo.lower(), target_branch, git_commit] + if pr: + query += " AND pr=?" + params.append(pr) + else: + query += " AND pr IS NULL" + row = self._con.execute(query, params).fetchone() + if not row: + return None + return self._build_from_row(row) + def remove(self, name: str) -> None: with self._con: self._con.execute("DELETE FROM builds WHERE name=?", (name,)) @@ -65,7 +80,7 @@ class BuildsDb: " repo," " target_branch," " pr," - " 'commit'," + " git_commit," " image," " status," " todo, " @@ -79,7 +94,7 @@ class BuildsDb: build.repo, build.target_branch, build.pr, - build.commit, + build.git_commit, build.image, build.status, build.todo, @@ -99,6 +114,7 @@ class BuildsDb: def to_start(self, limit: int) -> list[Build]: """Return the list of builds to start, ordered by todo timestamp.""" + # TODO ordering is not correct as setting todo does not set last_scaled rows = self._con.execute( "SELECT * FROM builds WHERE todo=? ORDER BY last_scaled LIMIT ?", (BuildTodo.start, limit), @@ -127,7 +143,7 @@ class BuildsDb: for row in self._con.execute( "SELECT * FROM builds WHERE repo=?" "ORDER BY target_branch, pr, created DESC", - (repo,), + (repo.lower(),), ).fetchall(): build = self._build_from_row(row) if ( diff --git a/src/runboat/k8s.py b/src/runboat/k8s.py index 526e739..3cd0100 100644 --- a/src/runboat/k8s.py +++ b/src/runboat/k8s.py @@ -53,7 +53,7 @@ class DeploymentVars(BaseModel): repo: str target_branch: str pr: Optional[int] - commit: str + git_commit: str image_name: str image_tag: str pghost: str @@ -71,7 +71,7 @@ def make_deployment_vars( repo: str, target_branch: str, pr: int | None, - commit: str, + git_commit: str, image: str, ) -> DeploymentVars: image_name, image_tag = _split_image_name_tag(image) @@ -81,7 +81,7 @@ def make_deployment_vars( repo=repo, target_branch=target_branch, pr=pr, - commit=commit, + git_commit=git_commit, image_name=image_name, image_tag=image_tag, pghost=settings.build_pghost, @@ -118,6 +118,14 @@ async def _kubectl(args: list[str]) -> None: async def deploy(deployment_vars: DeploymentVars) -> None: with _render_kubefiles(deployment_vars) as tmp_path: + await _kubectl( + [ + "apply", + "--dry-run=server", + "-k", + str(tmp_path), + ] + ) await _kubectl( [ "apply", diff --git a/src/runboat/kubefiles/kustomization.yaml.jinja b/src/runboat/kubefiles/kustomization.yaml.jinja index 10f175f..e75a9a5 100644 --- a/src/runboat/kubefiles/kustomization.yaml.jinja +++ b/src/runboat/kubefiles/kustomization.yaml.jinja @@ -14,7 +14,7 @@ commonAnnotations: runboat/repo: "{{ repo }}" runboat/target-branch: "{{ target_branch }}" runboat/pr: "{{ pr if pr else '' }}" - runboat/commit: "{{ commit }}" + runboat/git-commit: "{{ git_commit }}" images: - name: odoo @@ -37,7 +37,7 @@ configMapGenerator: - PGDATABASE={{ pgdatabase }} - ADDONS_DIR=/build - RUNBOAT_GIT_REPO=https://github.com/{{ repo }} - - RUNBOAT_GIT_REF={{ commit }} + - RUNBOAT_GIT_REF={{ git_commit }} - name: runboat-scripts files: - runboat-clone-and-install.sh diff --git a/src/runboat/models.py b/src/runboat/models.py index 1c5cea8..51e08ef 100644 --- a/src/runboat/models.py +++ b/src/runboat/models.py @@ -31,7 +31,7 @@ class Build(BaseModel): repo: str target_branch: str pr: Optional[int] - commit: str + git_commit: str image: str status: BuildStatus todo: Optional[BuildTodo] @@ -46,7 +46,7 @@ class Build(BaseModel): repo=deployment.metadata.annotations["runboat/repo"], target_branch=deployment.metadata.annotations["runboat/target-branch"], pr=deployment.metadata.annotations["runboat/pr"] or None, - commit=deployment.metadata.annotations["runboat/commit"], + git_commit=deployment.metadata.annotations["runboat/git-commit"], image=deployment.spec.template.spec.containers[0].image, status=cls._status_from_deployment(deployment), todo=deployment.metadata.annotations["runboat/todo"] or None, @@ -71,17 +71,17 @@ class Build(BaseModel): @classmethod def make_slug( - cls, repo: str, target_branch: str, pr: int | None, commit: str + cls, repo: str, target_branch: str, pr: int | None, git_commit: str ) -> str: slug = f"{slugify(repo)}-{slugify(target_branch)}" if pr: slug = f"{slug}-pr{slugify(pr)}" - slug = f"{slug}-{commit[:12]}" + slug = f"{slug}-{git_commit[:12]}" return slug @property def slug(self) -> str: - return self.make_slug(self.repo, self.target_branch, self.pr, self.commit) + return self.make_slug(self.repo, self.target_branch, self.pr, self.git_commit) @property def link(self) -> str: @@ -147,11 +147,11 @@ class Build(BaseModel): @classmethod async def deploy( - cls, repo: str, target_branch: str, pr: int | None, commit: str + cls, repo: str, target_branch: str, pr: int | None, git_commit: str ) -> None: """Deploy a build, without starting it.""" name = str(uuid.uuid4()) - slug = cls.make_slug(repo, target_branch, pr, commit) + slug = cls.make_slug(repo, target_branch, pr, git_commit) _logger.info(f"Deploying {slug} ({name})") image = get_build_image(target_branch) deployment_vars = k8s.make_deployment_vars( @@ -160,7 +160,7 @@ class Build(BaseModel): repo.lower(), target_branch, pr, - commit, + git_commit, image, ) await k8s.deploy(deployment_vars) diff --git a/src/runboat/webhooks.py b/src/runboat/webhooks.py index 885df06..c45c409 100644 --- a/src/runboat/webhooks.py +++ b/src/runboat/webhooks.py @@ -2,7 +2,7 @@ import logging from fastapi import APIRouter, BackgroundTasks, Header, Request -from . import models +from .controller import controller from .settings import settings _logger = logging.getLogger(__name__) @@ -29,17 +29,17 @@ async def receive_payload( if x_github_event == "pull_request": if action in ("opened", "synchronize"): background_tasks.add_task( - models.Build.deploy, + controller.deploy_or_delay_start, repo=repo, target_branch=payload["pull_request"]["base"]["ref"], pr=payload["pull_request"]["number"], - commit=payload["pull_request"]["head"]["sha"], + git_commit=payload["pull_request"]["head"]["sha"], ) elif x_github_event == "push": background_tasks.add_task( - models.Build.deploy, + controller.deploy_or_delay_start, repo=repo, target_branch=payload["ref"].split("/")[-1], pr=None, - commit=payload["after"], + git_commit=payload["after"], )