1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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))
|