Do not create new deployment if build already exist
This commit is contained in:
parent
c2b2d88f4c
commit
1f137bbdc8
8 changed files with 71 additions and 27 deletions
|
|
@ -3,6 +3,7 @@ disable_existing_loggers: false
|
||||||
formatters:
|
formatters:
|
||||||
rich:
|
rich:
|
||||||
datefmt: "[%X]"
|
datefmt: "[%X]"
|
||||||
|
format: "%(name)25s %(message)s"
|
||||||
handlers:
|
handlers:
|
||||||
console:
|
console:
|
||||||
class: rich.logging.RichHandler
|
class: rich.logging.RichHandler
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ class Build(BaseModel):
|
||||||
repo: str
|
repo: str
|
||||||
target_branch: str
|
target_branch: str
|
||||||
pr: Optional[int]
|
pr: Optional[int]
|
||||||
commit: str
|
git_commit: str
|
||||||
image: str
|
image: str
|
||||||
link: str
|
link: str
|
||||||
status: models.BuildStatus
|
status: models.BuildStatus
|
||||||
|
|
@ -92,11 +92,11 @@ async def trigger_branch(org: str, repo: str, branch: str):
|
||||||
"""Trigger build for a branch."""
|
"""Trigger build for a branch."""
|
||||||
# TODO async github call
|
# TODO async github call
|
||||||
branch_info = github.get_branch_info(org, repo, branch)
|
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}",
|
repo=f"{branch_info.org}/{branch_info.repo}",
|
||||||
target_branch=branch_info.name,
|
target_branch=branch_info.name,
|
||||||
pr=None,
|
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."""
|
"""Trigger build for a pull request."""
|
||||||
# TODO async github call
|
# TODO async github call
|
||||||
pull_info = github.get_pull_info(org, repo, pr)
|
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}",
|
repo=f"{pull_info.org}/{pull_info.repo}",
|
||||||
target_branch=pull_info.target_branch,
|
target_branch=pull_info.target_branch,
|
||||||
pr=pull_info.number,
|
pr=pull_info.number,
|
||||||
commit=pull_info.head_sha,
|
git_commit=pull_info.head_sha,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,25 @@ class Controller:
|
||||||
def max_deployed(self) -> int:
|
def max_deployed(self) -> int:
|
||||||
return settings.max_deployed
|
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:
|
def _wakeup(self) -> None:
|
||||||
self._wakeup_event.set()
|
self._wakeup_event.set()
|
||||||
self._wakeup_event.clear()
|
self._wakeup_event.clear()
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class BuildsDb:
|
||||||
" repo TEXT NOT NULL, "
|
" repo TEXT NOT NULL, "
|
||||||
" target_branch TEXT NOT NULL, "
|
" target_branch TEXT NOT NULL, "
|
||||||
" pr INTEGER, "
|
" pr INTEGER, "
|
||||||
" 'commit' TEXT NOT NULL, "
|
" git_commit TEXT NOT NULL, "
|
||||||
" image TEXT NOT NULL,"
|
" image TEXT NOT NULL,"
|
||||||
" status TEXT NOT NULL, "
|
" status TEXT NOT NULL, "
|
||||||
" todo TEXT, "
|
" todo TEXT, "
|
||||||
|
|
@ -51,6 +51,21 @@ class BuildsDb:
|
||||||
return None
|
return None
|
||||||
return self._build_from_row(row)
|
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:
|
def remove(self, name: str) -> None:
|
||||||
with self._con:
|
with self._con:
|
||||||
self._con.execute("DELETE FROM builds WHERE name=?", (name,))
|
self._con.execute("DELETE FROM builds WHERE name=?", (name,))
|
||||||
|
|
@ -65,7 +80,7 @@ class BuildsDb:
|
||||||
" repo,"
|
" repo,"
|
||||||
" target_branch,"
|
" target_branch,"
|
||||||
" pr,"
|
" pr,"
|
||||||
" 'commit',"
|
" git_commit,"
|
||||||
" image,"
|
" image,"
|
||||||
" status,"
|
" status,"
|
||||||
" todo, "
|
" todo, "
|
||||||
|
|
@ -79,7 +94,7 @@ class BuildsDb:
|
||||||
build.repo,
|
build.repo,
|
||||||
build.target_branch,
|
build.target_branch,
|
||||||
build.pr,
|
build.pr,
|
||||||
build.commit,
|
build.git_commit,
|
||||||
build.image,
|
build.image,
|
||||||
build.status,
|
build.status,
|
||||||
build.todo,
|
build.todo,
|
||||||
|
|
@ -99,6 +114,7 @@ class BuildsDb:
|
||||||
|
|
||||||
def to_start(self, limit: int) -> list[Build]:
|
def to_start(self, limit: int) -> list[Build]:
|
||||||
"""Return the list of builds to start, ordered by todo timestamp."""
|
"""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(
|
rows = self._con.execute(
|
||||||
"SELECT * FROM builds WHERE todo=? ORDER BY last_scaled LIMIT ?",
|
"SELECT * FROM builds WHERE todo=? ORDER BY last_scaled LIMIT ?",
|
||||||
(BuildTodo.start, limit),
|
(BuildTodo.start, limit),
|
||||||
|
|
@ -127,7 +143,7 @@ class BuildsDb:
|
||||||
for row in self._con.execute(
|
for row in self._con.execute(
|
||||||
"SELECT * FROM builds WHERE repo=?"
|
"SELECT * FROM builds WHERE repo=?"
|
||||||
"ORDER BY target_branch, pr, created DESC",
|
"ORDER BY target_branch, pr, created DESC",
|
||||||
(repo,),
|
(repo.lower(),),
|
||||||
).fetchall():
|
).fetchall():
|
||||||
build = self._build_from_row(row)
|
build = self._build_from_row(row)
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ class DeploymentVars(BaseModel):
|
||||||
repo: str
|
repo: str
|
||||||
target_branch: str
|
target_branch: str
|
||||||
pr: Optional[int]
|
pr: Optional[int]
|
||||||
commit: str
|
git_commit: str
|
||||||
image_name: str
|
image_name: str
|
||||||
image_tag: str
|
image_tag: str
|
||||||
pghost: str
|
pghost: str
|
||||||
|
|
@ -71,7 +71,7 @@ def make_deployment_vars(
|
||||||
repo: str,
|
repo: str,
|
||||||
target_branch: str,
|
target_branch: str,
|
||||||
pr: int | None,
|
pr: int | None,
|
||||||
commit: str,
|
git_commit: str,
|
||||||
image: str,
|
image: str,
|
||||||
) -> DeploymentVars:
|
) -> DeploymentVars:
|
||||||
image_name, image_tag = _split_image_name_tag(image)
|
image_name, image_tag = _split_image_name_tag(image)
|
||||||
|
|
@ -81,7 +81,7 @@ def make_deployment_vars(
|
||||||
repo=repo,
|
repo=repo,
|
||||||
target_branch=target_branch,
|
target_branch=target_branch,
|
||||||
pr=pr,
|
pr=pr,
|
||||||
commit=commit,
|
git_commit=git_commit,
|
||||||
image_name=image_name,
|
image_name=image_name,
|
||||||
image_tag=image_tag,
|
image_tag=image_tag,
|
||||||
pghost=settings.build_pghost,
|
pghost=settings.build_pghost,
|
||||||
|
|
@ -118,6 +118,14 @@ async def _kubectl(args: list[str]) -> None:
|
||||||
|
|
||||||
async def deploy(deployment_vars: DeploymentVars) -> None:
|
async def deploy(deployment_vars: DeploymentVars) -> None:
|
||||||
with _render_kubefiles(deployment_vars) as tmp_path:
|
with _render_kubefiles(deployment_vars) as tmp_path:
|
||||||
|
await _kubectl(
|
||||||
|
[
|
||||||
|
"apply",
|
||||||
|
"--dry-run=server",
|
||||||
|
"-k",
|
||||||
|
str(tmp_path),
|
||||||
|
]
|
||||||
|
)
|
||||||
await _kubectl(
|
await _kubectl(
|
||||||
[
|
[
|
||||||
"apply",
|
"apply",
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ commonAnnotations:
|
||||||
runboat/repo: "{{ repo }}"
|
runboat/repo: "{{ repo }}"
|
||||||
runboat/target-branch: "{{ target_branch }}"
|
runboat/target-branch: "{{ target_branch }}"
|
||||||
runboat/pr: "{{ pr if pr else '' }}"
|
runboat/pr: "{{ pr if pr else '' }}"
|
||||||
runboat/commit: "{{ commit }}"
|
runboat/git-commit: "{{ git_commit }}"
|
||||||
|
|
||||||
images:
|
images:
|
||||||
- name: odoo
|
- name: odoo
|
||||||
|
|
@ -37,7 +37,7 @@ configMapGenerator:
|
||||||
- PGDATABASE={{ pgdatabase }}
|
- PGDATABASE={{ pgdatabase }}
|
||||||
- ADDONS_DIR=/build
|
- ADDONS_DIR=/build
|
||||||
- RUNBOAT_GIT_REPO=https://github.com/{{ repo }}
|
- RUNBOAT_GIT_REPO=https://github.com/{{ repo }}
|
||||||
- RUNBOAT_GIT_REF={{ commit }}
|
- RUNBOAT_GIT_REF={{ git_commit }}
|
||||||
- name: runboat-scripts
|
- name: runboat-scripts
|
||||||
files:
|
files:
|
||||||
- runboat-clone-and-install.sh
|
- runboat-clone-and-install.sh
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class Build(BaseModel):
|
||||||
repo: str
|
repo: str
|
||||||
target_branch: str
|
target_branch: str
|
||||||
pr: Optional[int]
|
pr: Optional[int]
|
||||||
commit: str
|
git_commit: str
|
||||||
image: str
|
image: str
|
||||||
status: BuildStatus
|
status: BuildStatus
|
||||||
todo: Optional[BuildTodo]
|
todo: Optional[BuildTodo]
|
||||||
|
|
@ -46,7 +46,7 @@ class Build(BaseModel):
|
||||||
repo=deployment.metadata.annotations["runboat/repo"],
|
repo=deployment.metadata.annotations["runboat/repo"],
|
||||||
target_branch=deployment.metadata.annotations["runboat/target-branch"],
|
target_branch=deployment.metadata.annotations["runboat/target-branch"],
|
||||||
pr=deployment.metadata.annotations["runboat/pr"] or None,
|
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,
|
image=deployment.spec.template.spec.containers[0].image,
|
||||||
status=cls._status_from_deployment(deployment),
|
status=cls._status_from_deployment(deployment),
|
||||||
todo=deployment.metadata.annotations["runboat/todo"] or None,
|
todo=deployment.metadata.annotations["runboat/todo"] or None,
|
||||||
|
|
@ -71,17 +71,17 @@ class Build(BaseModel):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_slug(
|
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:
|
) -> str:
|
||||||
slug = f"{slugify(repo)}-{slugify(target_branch)}"
|
slug = f"{slugify(repo)}-{slugify(target_branch)}"
|
||||||
if pr:
|
if pr:
|
||||||
slug = f"{slug}-pr{slugify(pr)}"
|
slug = f"{slug}-pr{slugify(pr)}"
|
||||||
slug = f"{slug}-{commit[:12]}"
|
slug = f"{slug}-{git_commit[:12]}"
|
||||||
return slug
|
return slug
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def slug(self) -> str:
|
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
|
@property
|
||||||
def link(self) -> str:
|
def link(self) -> str:
|
||||||
|
|
@ -147,11 +147,11 @@ class Build(BaseModel):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def deploy(
|
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:
|
) -> None:
|
||||||
"""Deploy a build, without starting it."""
|
"""Deploy a build, without starting it."""
|
||||||
name = str(uuid.uuid4())
|
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})")
|
_logger.info(f"Deploying {slug} ({name})")
|
||||||
image = get_build_image(target_branch)
|
image = get_build_image(target_branch)
|
||||||
deployment_vars = k8s.make_deployment_vars(
|
deployment_vars = k8s.make_deployment_vars(
|
||||||
|
|
@ -160,7 +160,7 @@ class Build(BaseModel):
|
||||||
repo.lower(),
|
repo.lower(),
|
||||||
target_branch,
|
target_branch,
|
||||||
pr,
|
pr,
|
||||||
commit,
|
git_commit,
|
||||||
image,
|
image,
|
||||||
)
|
)
|
||||||
await k8s.deploy(deployment_vars)
|
await k8s.deploy(deployment_vars)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import logging
|
||||||
|
|
||||||
from fastapi import APIRouter, BackgroundTasks, Header, Request
|
from fastapi import APIRouter, BackgroundTasks, Header, Request
|
||||||
|
|
||||||
from . import models
|
from .controller import controller
|
||||||
from .settings import settings
|
from .settings import settings
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
@ -29,17 +29,17 @@ async def receive_payload(
|
||||||
if x_github_event == "pull_request":
|
if x_github_event == "pull_request":
|
||||||
if action in ("opened", "synchronize"):
|
if action in ("opened", "synchronize"):
|
||||||
background_tasks.add_task(
|
background_tasks.add_task(
|
||||||
models.Build.deploy,
|
controller.deploy_or_delay_start,
|
||||||
repo=repo,
|
repo=repo,
|
||||||
target_branch=payload["pull_request"]["base"]["ref"],
|
target_branch=payload["pull_request"]["base"]["ref"],
|
||||||
pr=payload["pull_request"]["number"],
|
pr=payload["pull_request"]["number"],
|
||||||
commit=payload["pull_request"]["head"]["sha"],
|
git_commit=payload["pull_request"]["head"]["sha"],
|
||||||
)
|
)
|
||||||
elif x_github_event == "push":
|
elif x_github_event == "push":
|
||||||
background_tasks.add_task(
|
background_tasks.add_task(
|
||||||
models.Build.deploy,
|
controller.deploy_or_delay_start,
|
||||||
repo=repo,
|
repo=repo,
|
||||||
target_branch=payload["ref"].split("/")[-1],
|
target_branch=payload["ref"].split("/")[-1],
|
||||||
pr=None,
|
pr=None,
|
||||||
commit=payload["after"],
|
git_commit=payload["after"],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue