Add start and stop deployment modes

Apply patches before start and after stop. This is useful to bring up / tear down
additional resources.
This commit is contained in:
Stéphane Bidoul 2025-11-29 19:35:09 +01:00
parent d2b90e5368
commit 4412618307
3 changed files with 49 additions and 11 deletions

View file

@ -104,15 +104,21 @@ kubernetes cluster, or even a different one, depending on your taste.
All resources to be deployed in kubernetes for a build are in
[src/runboat/kubefiles](./src/runboat/kubefiles). They are gathered together from a
`kustomization.yaml` jinja template that leads to three possible resource groups
`kustomization.yaml` jinja template that leads to 5 possible resource groups
depending on a `mode` variable in the jinja rendering context:
- `deployment` creates a kubernetes deployment with its associated resources (pvc,
service, ingress, ...);
- `initialization` creates a job that creates the database;
- `cleanup` creates a job that drops the database;
service, ingress, ...).
- `initialization` creates a job that performs installation and initializes the database;
- `start` updates the resources before scaling up the deployment to 1. This can be
useful to scale up other resources that must only be present while the main
deployment is running.
- `stop` updates the resources after initialization and before scaling down the
deployment to 0. This can be useful to scale down other resources that must
only be present while the main deployment is running.
- `cleanup` creates a job that perform cleanup before tearing down all resources.
Besides the three modes, the controller has limited knowledge of what the kubefiles
Besides the 5 modes, the controller has limited knowledge of what the kubefiles
actually deploy. It expects the following to hold true:
- the `runboat/build` label is set on all resources, with the unique build name as

View file

@ -138,6 +138,8 @@ class DeploymentMode(str, Enum):
deployment = "deployment"
initialize = "initialize"
cleanup = "cleanup"
start = "start"
stop = "stop"
class DeploymentVars(BaseModel):

View file

@ -195,12 +195,6 @@ class Build(BaseModel):
build_settings,
)
await k8s.deploy(kubefiles_path, deployment_vars)
await github.notify_status(
commit_info.repo,
commit_info.git_commit,
GitHubStatusState.pending,
target_url=None,
)
@classmethod
async def deploy(cls, commit_info: CommitInfo) -> None:
@ -211,6 +205,12 @@ class Build(BaseModel):
await cls._deploy(
commit_info, name, slug, job_kind=k8s.DeploymentMode.deployment
)
await github.notify_status(
commit_info.repo,
commit_info.git_commit,
GitHubStatusState.pending,
target_url=None,
)
async def start(self) -> None:
"""Start build if init succeeded, or reinitialize if failed."""
@ -218,6 +218,12 @@ class Build(BaseModel):
_logger.info(f"Ignoring start command for {self} that is {self.status}.")
return
_logger.info(f"Starting {self} that was last scaled on {self.last_scaled}.")
await self._deploy(
self.commit_info,
self.name,
self.slug,
job_kind=k8s.DeploymentMode.start,
)
await self._patch(desired_replicas=1)
async def stop(self) -> None:
@ -226,6 +232,12 @@ class Build(BaseModel):
return
_logger.info(f"Stopping {self} that was last scaled on {self.last_scaled}.")
await self._patch(desired_replicas=0)
await self._deploy(
self.commit_info,
self.name,
self.slug,
job_kind=k8s.DeploymentMode.stop,
)
async def undeploy(self) -> None:
# To undeploy, we delete the deployment. Due to the finalizer, the deletion
@ -246,6 +258,12 @@ class Build(BaseModel):
self.slug,
job_kind=k8s.DeploymentMode.deployment,
)
await github.notify_status(
self.commit_info.repo,
self.commit_info.git_commit,
GitHubStatusState.pending,
target_url=None,
)
async def initialize(self) -> None:
"""Launch the initialization job."""
@ -300,6 +318,12 @@ class Build(BaseModel):
# is restarting, and is notified of existing initialization jobs.
return
_logger.info(f"Initialization job succeded for {self}, ready to start.")
await self._deploy(
self.commit_info,
self.name,
self.slug,
job_kind=k8s.DeploymentMode.stop,
)
if await self._patch(init_status=BuildInitStatus.succeeded):
await github.notify_status(
self.commit_info.repo,
@ -314,6 +338,12 @@ class Build(BaseModel):
# restarting, and is notified of existing initialization jobs.
return
_logger.info(f"Initialization job failed for {self}.")
await self._deploy(
self.commit_info,
self.name,
self.slug,
job_kind=k8s.DeploymentMode.stop,
)
if await self._patch(init_status=BuildInitStatus.failed, desired_replicas=0):
await github.notify_status(
self.commit_info.repo,