diff options
| -rw-r--r-- | arthur/apis/kubernetes/jobs.py | 32 | ||||
| -rw-r--r-- | arthur/bot.py | 1 | ||||
| -rw-r--r-- | arthur/exts/kubernetes/jobs.py | 81 |
3 files changed, 114 insertions, 0 deletions
diff --git a/arthur/apis/kubernetes/jobs.py b/arthur/apis/kubernetes/jobs.py new file mode 100644 index 0000000..c079da0 --- /dev/null +++ b/arthur/apis/kubernetes/jobs.py @@ -0,0 +1,32 @@ +"""APIs for interacting with Kubernetes Jobs & Cronjobs.""" +from typing import Any, Optional + +from kubernetes_asyncio import client +from kubernetes_asyncio.client.api_client import ApiClient +from kubernetes_asyncio.client.models import V1beta1CronJob, V1beta1CronJobList, V1Job + + +async def list_cronjobs(namespace: Optional[str] = None) -> V1beta1CronJobList: + """Query the Kubernetes API for a list of cronjobss in the provided namespace.""" + async with ApiClient() as api: + api = client.BatchV1beta1Api(api) + if namespace: + return await api.list_namespaced_cron_job(namespace) + else: + return await api.list_cron_job_for_all_namespaces() + + +async def get_cronjob(namespace: str, cronjob_name: str) -> V1beta1CronJob: + """Fetch a cronjob given the name and namespace.""" + async with ApiClient() as api: + api = client.BatchV1beta1Api(api) + return await api.read_namespaced_cron_job(cronjob_name, namespace) + + +async def create_job(namespace: str, job_name: str, cron_spec: dict[str, Any]) -> V1Job: + """Create a job in the specified namespace with the given specification and name.""" + async with ApiClient() as api: + api = client.BatchV1Api(api) + return await api.create_namespaced_job( + namespace, V1Job(metadata={"name": job_name}, spec=cron_spec) + ) diff --git a/arthur/bot.py b/arthur/bot.py index 05da078..5ba93a5 100644 --- a/arthur/bot.py +++ b/arthur/bot.py @@ -75,6 +75,7 @@ class KingArthur(Bot): logger.info("Loaded <red>jishaku</red>") async def is_owner(self, user: Union[User, Member]) -> bool: + """Check if the invoker is a bot owner.""" if not user.guild_id: return False diff --git a/arthur/exts/kubernetes/jobs.py b/arthur/exts/kubernetes/jobs.py new file mode 100644 index 0000000..6f7a1fe --- /dev/null +++ b/arthur/exts/kubernetes/jobs.py @@ -0,0 +1,81 @@ +"""The zones cog helps with managing Cloudflare zones.""" +import discord +from discord.ext import commands +from kubernetes_asyncio.client.models import V1beta1CronJobList + +from arthur.apis.kubernetes import jobs +from arthur.bot import KingArthur +from arthur.config import CONFIG + +# from arthur.utils import generate_error_message + + +class CronJobView(discord.ui.View): + """This view allows users to select and trigger a CronJob.""" + + def __init__(self, cron_jobs: V1beta1CronJobList) -> None: + super().__init__() + + self.cron_jobs = cron_jobs + + for cron_job in self.cron_jobs.items: + cj = cron_job.metadata.name + ns = cron_job.metadata.namespace + self.children[0].add_option( + label=cron_job.metadata.name, value=f"{ns}/{cj}", description=ns, emoji="🛠️" + ) + + def disable_select(self) -> None: + """Disable the select button.""" + self.children[0].disabled = True + + async def interaction_check(self, interaction: discord.Interaction) -> bool: + """Ensure the user has the DevOps role.""" + return CONFIG.devops_role in [r.id for r in interaction.user.roles] + + @discord.ui.select( + placeholder="Select a CronJob to trigger...", + ) + async def select_job( + self, dropdown: discord.ui.Select, interaction: discord.Interaction + ) -> None: + """Drop down menu contains the list of cronjobsb.""" + cronjob_namespace, cronjob_name = dropdown.values[0].split("/") + + cronjob = await jobs.get_cronjob(cronjob_namespace, cronjob_name) + + new_job = await jobs.create_job( + cronjob_namespace, + f"{cronjob_name}-{interaction.message.id}", + cronjob.spec.job_template.spec, + ) + + self.disable_select() + + await interaction.message.edit(view=self) + await interaction.response.send_message(f"🌬️ Spawned job `{new_job.metadata.name}`") + + +class Jobs(commands.Cog): + """Commands for working with Kubernetes Jobs & CronJobs.""" + + def __init__(self, bot: KingArthur) -> None: + self.bot = bot + + @commands.group(name="cronjob", aliases=["cronjobs", "cj"], invoke_without_command=True) + async def cronjob(self, ctx: commands.Context) -> None: + """Commands for working with Kubernetes CronJobs.""" + await ctx.send_help(ctx.command) + + @cronjob.command(name="trigger") + async def trigger(self, ctx: commands.Context) -> None: + """Command to trigger a Kubernetes cronjob now.""" + cronjobs = await jobs.list_cronjobs() + + view = CronJobView(cronjobs) + await ctx.send(":tools: Pick a CronJob to trigger", view=view) + + +def setup(bot: KingArthur) -> None: + """Add the extension to the bot.""" + bot.add_cog(Jobs(bot)) |