|
|
|
from typing import List
|
|
|
|
|
|
|
|
import requests
|
|
|
|
from requests.auth import HTTPBasicAuth
|
|
|
|
from zope import component
|
|
|
|
from zope.interface import implementer
|
|
|
|
|
|
|
|
from ceo_common.errors import UserNotFoundError
|
|
|
|
from ceo_common.interfaces import IContainerRegistryService, IConfig
|
|
|
|
|
|
|
|
|
|
|
|
@implementer(IContainerRegistryService)
|
|
|
|
class ContainerRegistryService:
|
|
|
|
def __init__(self):
|
|
|
|
cfg = component.getUtility(IConfig)
|
|
|
|
self.base_url = cfg.get('registry_base_url')
|
|
|
|
if self.base_url.endswith('/'):
|
|
|
|
self.base_url = self.base_url[:-1]
|
|
|
|
api_username = cfg.get('registry_username')
|
|
|
|
api_password = cfg.get('registry_password')
|
|
|
|
self.basic_auth = HTTPBasicAuth(api_username, api_password)
|
|
|
|
self.projects_to_ignore = cfg.get('registry_projects_to_ignore')
|
|
|
|
|
|
|
|
def _http_request(self, method: str, path: str, **kwargs):
|
|
|
|
return requests.request(
|
|
|
|
method, self.base_url + path, **kwargs, auth=self.basic_auth)
|
|
|
|
|
|
|
|
def _http_get(self, path: str, **kwargs):
|
|
|
|
return self._http_request('GET', path, **kwargs)
|
|
|
|
|
|
|
|
def _http_post(self, path, **kwargs):
|
|
|
|
return self._http_request('POST', path, **kwargs)
|
|
|
|
|
|
|
|
def _http_delete(self, path, **kwargs):
|
|
|
|
return self._http_request('DELETE', path, **kwargs)
|
|
|
|
|
|
|
|
def _get_account(self, username: str):
|
|
|
|
resp = self._http_get('/users', params={'username': username})
|
|
|
|
users = resp.json()
|
|
|
|
if len(users) < 1:
|
|
|
|
raise UserNotFoundError(username)
|
|
|
|
return users[0]
|
|
|
|
|
|
|
|
def get_accounts(self) -> List[str]:
|
|
|
|
# We're only interested in accounts which have a project, so
|
|
|
|
# we're actually just going to get a list of projects
|
|
|
|
resp = self._http_get('/projects')
|
|
|
|
resp.raise_for_status()
|
|
|
|
return [
|
|
|
|
project['name'] for project in resp.json()
|
|
|
|
if project['name'] not in self.projects_to_ignore
|
|
|
|
]
|
|
|
|
|
|
|
|
def create_project_for_user(self, username: str):
|
|
|
|
user_id = self._get_account(username)['user_id']
|
|
|
|
|
|
|
|
# Create the project
|
|
|
|
resp = self._http_post(
|
|
|
|
'/projects', json={'project_name': username, 'public': True})
|
|
|
|
# 409 => project already exists (that is OK)
|
|
|
|
if resp.status_code != 409:
|
|
|
|
resp.raise_for_status()
|
|
|
|
|
|
|
|
# Add the user as a project admin (role ID 1)
|
|
|
|
resp = self._http_post(
|
|
|
|
f'/projects/{username}/members',
|
|
|
|
json={'role_id': 1, 'member_user': {'user_id': user_id}})
|
|
|
|
# 409 => project member already exists (that is OK)
|
|
|
|
if resp.status_code != 409:
|
|
|
|
resp.raise_for_status()
|
|
|
|
|
|
|
|
def delete_project_for_user(self, username: str):
|
|
|
|
# Delete all of the repositories inside the project first
|
|
|
|
resp = self._http_get(f'/projects/{username}/repositories')
|
|
|
|
if resp.status_code == 403:
|
|
|
|
# For some reason a 403 is returned if the project doesn't exist
|
|
|
|
return
|
|
|
|
resp.raise_for_status()
|
|
|
|
# Each repo name has the format project/image
|
|
|
|
repositories = [repo['name'].split('/')[1] for repo in resp.json()]
|
|
|
|
for repo in repositories:
|
|
|
|
resp = self._http_delete(f'/projects/{username}/repositories/{repo}')
|
|
|
|
resp.raise_for_status()
|
|
|
|
# Delete the project now that it is empty
|
|
|
|
resp = self._http_delete(f'/projects/{username}')
|
|
|
|
resp.raise_for_status()
|