diff options
author | 2021-07-17 20:10:11 +0100 | |
---|---|---|
committer | 2021-07-17 20:10:11 +0100 | |
commit | d3d81582cd7005df16fe3f5306e6afbed75bdb74 (patch) | |
tree | 323a7441f6c4d32833fee922208121497c77a9de /arthur/exts/kubernetes/deployments.py |
Initial commit
Diffstat (limited to 'arthur/exts/kubernetes/deployments.py')
-rw-r--r-- | arthur/exts/kubernetes/deployments.py | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/arthur/exts/kubernetes/deployments.py b/arthur/exts/kubernetes/deployments.py new file mode 100644 index 0000000..0a5595d --- /dev/null +++ b/arthur/exts/kubernetes/deployments.py @@ -0,0 +1,119 @@ +"""The Deployments cog helps with managing Kubernetes deployments.""" +from datetime import datetime, timezone + +from discord import Colour, Embed +from discord.ext import commands +from kubernetes_asyncio import client +from kubernetes_asyncio.client.api_client import ApiClient +from kubernetes_asyncio.client.rest import ApiException +from tabulate import tabulate + +from arthur.bot import KingArthur +from arthur.utils import generate_error_embed + + +class Deployments(commands.Cog): + """Commands for working with Kubernetes Deployments.""" + + def __init__(self, bot: KingArthur) -> None: + self.bot = bot + + @commands.group(name="deployments", aliases=["deploy"], invoke_without_command=True) + async def deployments(self, ctx: commands.Context) -> None: + """Commands for working with Kubernetes Deployments.""" + await ctx.send_help(ctx.command) + + @deployments.command(name="list") + async def deployments_list(self, ctx: commands.Context, namespace: str = "default") -> None: + """List deployments in the selected namespace (defaults to default).""" + async with ApiClient() as api: + v1 = client.AppsV1Api(api) + ret = await v1.list_namespaced_deployment(namespace=namespace) + + table_data = [] + + for deployment in ret.items: + if deployment.status.available_replicas == deployment.spec.replicas: + emote = "\N{LARGE GREEN CIRCLE}" + elif ( + deployment.status.available_replicas == 0 + or not deployment.status.available_replicas + ): + emote = "\N{LARGE RED CIRCLE}" + else: + emote = "\N{LARGE YELLOW CIRCLE}" + + table_data.append( + [ + emote, + deployment.metadata.name, + f"{deployment.status.available_replicas or 0}/{deployment.spec.replicas}", + ] + ) + + table = tabulate( + table_data, + headers=["Status", "Deployment", "Replicas"], + tablefmt="psql", + colalign=("center", "left", "center"), + ) + + return_embed = Embed( + title=f"Deployments in namespace {namespace}", description=f"```\n{table}\n```" + ) + + await ctx.send(embed=return_embed) + + @deployments.command(name="restart") + async def deployments_restart( + self, ctx: commands.Context, deployment: str, namespace: str = "default" + ) -> None: + """Restart the specified deployment in the selected namespace (defaults to default).""" + async with ApiClient() as api: + v1 = client.AppsV1Api(api) + + try: + await v1.patch_namespaced_deployment( + name=deployment, + namespace=namespace, + body={ + "spec": { + "template": { + "metadata": { + "annotations": { + "king-arthur.pythondiscord.com/restartedAt": datetime.now( + timezone.utc + ).isoformat() + } + } + } + } + }, + field_manager="King Arthur", + ) + except ApiException as e: + if e.status == 404: + return await ctx.send( + embed=generate_error_embed( + description="Could not find deployment, check the namespace." + ) + ) + + return await ctx.send( + embed=generate_error_embed( + description=f"Unexpected error occurred, error code {e.status}" + ) + ) + + return_embed = Embed( + title="Deployment restarted", + description=f"Restarted deployment `{deployment}` in the `{namespace}` namespace.", + colour=Colour.blurple(), + ) + + await ctx.send(embed=return_embed) + + +def setup(bot: KingArthur) -> None: + """Add the extension to the bot.""" + bot.add_cog(Deployments(bot)) |