diff options
| -rw-r--r-- | arthur/exts/grafana/team_sync.py | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/arthur/exts/grafana/team_sync.py b/arthur/exts/grafana/team_sync.py new file mode 100644 index 0000000..501f891 --- /dev/null +++ b/arthur/exts/grafana/team_sync.py @@ -0,0 +1,89 @@ +import aiohttp +import discord +from discord.ext import commands, tasks + +from arthur import logger +from arthur.apis import github, grafana +from arthur.bot import KingArthur + + +class GrafanaTeamSync(commands.Cog): + """ + Update Grafana team membership to match Github team membership. + + Grafana team name must match Github team slug exactly. + Use `gh api orgs/{org-name}/teams` to get a list of teams in an org + """ + + def __init__(self, bot: KingArthur) -> None: + self.bot = bot + self.sync_github_grafana_teams.start() + + async def _sync_teams(self, team: dict[str, str]) -> tuple[int, int]: + """ + Ensure members in github are present in grafana teams. + + Return the number of members missing from teh grafana team, and the number of members added. + """ + github_team_members = { + member["login"] + for member in await github.list_team_members(team["name"], self.bot.http_session) + } + grafana_team_members = { + member["login"] + for member in await grafana.list_team_members(team["id"], self.bot.http_session) + if member.get("auth_module") == "oauth_github" + } + + missing_members = github_team_members - grafana_team_members + grafana_users = await grafana.get_all_users(self.bot.http_session) + added_members = set() + for grafana_user in grafana_users: + if grafana_user["login"] not in missing_members: + continue + await grafana.add_user_to_team( + grafana_user["userId"], + team["id"], + self.bot.http_session, + ) + added_members.add(grafana_user["login"]) + return len(missing_members), len(added_members) + + @tasks.loop(hours=12) + async def sync_github_grafana_teams(self, channel: discord.TextChannel | None = None) -> None: + """Update Grafana team membership to match Github team membership.""" + grafana_teams = await grafana.list_teams(self.bot.http_session) + for team in grafana_teams: + logger.debug(f"Processing {team['name']}") + try: + missing, added = await self._sync_teams(team) + except aiohttp.ClientResponseError as e: + logger.error(e) + if channel: + await channel.send(e) + continue + + if channel and missing: + await channel.send( + f"Found {missing} users not in the {team['name']} grafana team, added {added} of them." + ) + + @sync_github_grafana_teams.error + async def on_task_error(self, error: Exception) -> None: + """Ensure task errors are output.""" + logger.error(error) + + @commands.group(name="grafana", aliases=("graf",), invoke_without_command=True) + async def grafana_group(self, ctx: commands.Context) -> None: + """Commands for working with grafana API.""" + await ctx.send_help(ctx.command) + + @grafana_group.command(name="sync") + async def sync_teams(self, ctx: commands.Context) -> None: + """Sync Grafana & Github teams now.""" + await self.sync_github_grafana_teams(ctx.channel) + + +async def setup(bot: KingArthur) -> None: + """Add cog to bot.""" + await bot.add_cog(GrafanaTeamSync(bot)) |