aboutsummaryrefslogtreecommitdiffstats
path: root/arthur/exts/grafana/team_sync.py
blob: 501f891ca2a7b49a0b860ca01aab555ec96dc845 (plain) (blame)
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
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))