Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add command to get github api key statuses #2986

Merged
merged 4 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions augur/application/cli/github.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# SPDX-License-Identifier: MIT
from os import walk, chdir, environ, chmod, path

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused walk imported from os (unused-import)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused chdir imported from os (unused-import)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused environ imported from os (unused-import)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused chmod imported from os (unused-import)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused path imported from os (unused-import)

import os

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused import os (unused-import)

import logging
from sys import exit

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused exit imported from sys (unused-import)

import stat

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused import stat (unused-import)

from collections import OrderedDict

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused OrderedDict imported from collections (unused-import)

from subprocess import call

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused call imported from subprocess (unused-import)

import random

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused import random (unused-import)

import string

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused import string (unused-import)

import csv

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused import csv (unused-import)

import click
import sqlalchemy as s
import pandas as pd

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused pandas imported as pd (unused-import)

import requests

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused import requests (unused-import)

import json

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused import json (unused-import)

import sqlalchemy as s
import re

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused import re (unused-import)


from augur.application.cli import test_connection, test_db_connection

from augur.application.db.session import DatabaseSession

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused DatabaseSession imported from augur.application.db.session (unused-import)

from augur.application.logs import AugurLogger

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused AugurLogger imported from augur.application.logs (unused-import)

from augur.application.db.engine import DatabaseEngine
from sqlalchemy import update

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused update imported from sqlalchemy (unused-import)

from datetime import datetime
from augur.application.db.models import Repo

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused Repo imported from augur.application.db.models (unused-import)



import httpx
from collections import Counter


from augur.tasks.github.util.github_api_key_handler import GithubApiKeyHandler


logger = logging.getLogger(__name__)

@click.group("github", short_help="Github utilities")
def cli():
pass

@cli.command("api-keys")
@test_connection
@test_db_connection
def update_api_key():
"""
Get the ratelimit of Github API keys
"""

with DatabaseEngine() as engine, engine.connect() as connection:

get_api_keys_sql = s.sql.text(
"""
SELECT value as github_key from config Where section_name='Keys' AND setting_name='github_api_key'
UNION All
SELECT access_token as github_key from worker_oauth ORDER BY github_key DESC;
"""
)

result = connection.execute(get_api_keys_sql).fetchall()
keys = [x[0] for x in result]

with httpx.Client() as client:

invalid_keys = []
valid_key_data = []
for key in keys:
core_key_data, graphql_key_data = GithubApiKeyHandler.get_key_rate_limit(client, key)
if core_key_data is None or graphql_key_data is None:
invalid_keys.append(key)
else:
valid_key_data.append((key, core_key_data, graphql_key_data))

valid_key_data = sorted(valid_key_data, key=lambda x: x[1]["requests_remaining"])

core_request_header = "Core Requests Left"
core_reset_header = "Core Reset Time"
graphql_request_header = "Graphql Requests Left"
graphql_reset_header = "Graphql Reset Time"
print(f"{'Key'.center(40)} {core_request_header} {core_reset_header} {graphql_request_header} {graphql_reset_header}")
for key, core_key_data, graphql_key_data in valid_key_data:
core_requests = str(core_key_data['requests_remaining']).center(len(core_request_header))
core_reset_time = str(epoch_to_local_time_with_am_pm(core_key_data["reset_epoch"])).center(len(core_reset_header))

graphql_requests = str(graphql_key_data['requests_remaining']).center(len(graphql_request_header))
graphql_reset_time = str(epoch_to_local_time_with_am_pm(graphql_key_data["reset_epoch"])).center(len(graphql_reset_header))

print(f"{key} | {core_requests} | {core_reset_time} | {graphql_requests} | {graphql_reset_time} |")

valid_key_list = [x[0] for x in valid_key_data]
duplicate_keys = find_duplicates(valid_key_list)
if len(duplicate_keys) > 0:
print("\n\nWARNING: There are duplicate keys this will slow down collection")
print("Duplicate keys".center(40))
for key in duplicate_keys:
print(key)


if len(invalid_keys) > 0:
invalid_key_header = "Invalid Keys".center(40)
print("\n")
print(invalid_key_header)
for key in invalid_keys:
print(key)
print("")



engine.dispose()


def epoch_to_local_time_with_am_pm(epoch):
local_time = datetime.fromtimestamp(epoch)
formatted_time = local_time.strftime('%I:%M %p') # This format includes the date as well
return formatted_time


def find_duplicates(lst):
counter = Counter(lst)
return [item for item, count in counter.items() if count > 1]

30 changes: 25 additions & 5 deletions augur/tasks/github/util/github_api_key_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import random

from typing import Optional, List

Check warning on line 6 in augur/tasks/github/util/github_api_key_handler.py

View workflow job for this annotation

GitHub Actions / runner / pylint

[pylint] reported by reviewdog 🐶 W0611: Unused Session imported from sqlalchemy.orm (unused-import) Raw Output: augur/tasks/github/util/github_api_key_handler.py:6:0: W0611: Unused Session imported from sqlalchemy.orm (unused-import)
from augur.tasks.util.redis_list import RedisList
from augur.application.db.session import DatabaseSession
from augur.application.config import AugurConfig

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[pylint] reported by reviewdog 🐶
W0611: Unused func imported from sqlalchemy (unused-import)

RATE_LIMIT_URL = "https://api.github.com/rate_limit"


class NoValidKeysError(Exception):
pass
Expand Down Expand Up @@ -96,7 +98,7 @@
attempts = 0
while attempts < 3:

try:

Check warning on line 101 in augur/tasks/github/util/github_api_key_handler.py

View workflow job for this annotation

GitHub Actions / runner / pylint

[pylint] reported by reviewdog 🐶 W0702: No exception type(s) specified (bare-except) Raw Output: augur/tasks/github/util/github_api_key_handler.py:101:12: W0702: No exception type(s) specified (bare-except)
keys = self.get_api_keys_from_database()
break
except:
Expand Down Expand Up @@ -143,17 +145,35 @@
True if key is bad. False if the key is good
"""

# this endpoint allows us to check the rate limit, but it does not use one of our 5000 requests
url = "https://api.github.com/rate_limit"

headers = {'Authorization': f'token {oauth_key}'}

data = client.request(method="GET", url=url, headers=headers, timeout=180).json()
data = client.request(method="GET", url=RATE_LIMIT_URL, headers=headers, timeout=180).json()

try:
if data["message"] == "Bad credentials":
return True
except KeyError:
pass

return False
return False

@staticmethod
def get_key_rate_limit(client, github_key):

headers = {'Authorization': f'token {github_key}'}

data = client.request(method="GET", url=RATE_LIMIT_URL, headers=headers, timeout=180).json()

if "message" in data:
return None, None

def convert_rate_limit_request(data):
return {
"requests_remaining": data["remaining"],
"reset_epoch": data["reset"]
}

core_data = convert_rate_limit_request(data["resources"]["core"])
graphql_data = convert_rate_limit_request(data["resources"]["graphql"])

return core_data, graphql_data
Loading