add CLI for cloud accounts
continuous-integration/drone/pr Build is passing Details

This commit is contained in:
Max Erenberg 2021-11-21 10:15:14 -05:00
parent bb7818e77d
commit 89fa65261c
12 changed files with 88 additions and 11 deletions

46
ceo/cli/cloud.py Normal file
View File

@ -0,0 +1,46 @@
import click
from zope import component
from ceo_common.interfaces import IConfig
from ..utils import http_post
from .utils import handle_sync_response
@click.group(short_help='Perform operations on the CSC cloud')
def cloud():
pass
@cloud.group(short_help='Manage your cloud account')
def account():
pass
@account.command(short_help='Activate your cloud account')
def activate():
cfg = component.getUtility(IConfig)
base_domain = cfg.get('base_domain')
resp = http_post('/api/cloud/accounts/create')
handle_sync_response(resp)
lines = [
'Congratulations! Your cloud account has been activated.',
f'You may now login into https://cloud.{base_domain} with your CSC credentials.',
"Make sure to enter 'Members' for the domain (no quotes).",
]
for line in lines:
click.echo(line)
@cloud.group(short_help='Manage cloud accounts')
def accounts():
pass
@accounts.command(short_help='Purge expired cloud accounts')
def purge():
resp = http_post('/api/cloud/accounts/purge')
result = handle_sync_response(resp)
click.echo('Accounts to be deleted: ' + ','.join(result['accounts_to_be_deleted']))
click.echo('Accounts which were deleted: ' + ','.join(result['accounts_deleted']))

View File

@ -7,6 +7,7 @@ from .updateprograms import updateprograms
from .mysql import mysql
from .postgresql import postgresql
from .mailman import mailman
from .cloud import cloud
@click.group()
@ -21,3 +22,4 @@ cli.add_command(updateprograms)
cli.add_command(mysql)
cli.add_command(postgresql)
cli.add_command(mailman)
cli.add_command(cloud)

View File

@ -18,6 +18,8 @@ def http_request(method: str, path: str, **kwargs) -> requests.Response:
host = cfg.get('ceod_database_host')
elif path.startswith('/api/mailman'):
host = cfg.get('ceod_mailman_host')
elif path.startswith('/api/cloud'):
host = cfg.get('ceod_cloud_host')
else:
host = cfg.get('ceod_admin_host')
return client.request(

View File

@ -28,7 +28,7 @@ class CloudService:
self.api_key = cfg.get('cloudstack_api_key')
self.secret_key = cfg.get('cloudstack_secret_key')
self.base_url = cfg.get('cloudstack_base_url')
self.members_domain = cfg.get('cloudstack_members_domain')
self.members_domain = 'Members'
state_dir = '/run/ceod'
if not os.path.isdir(state_dir):

View File

@ -9,6 +9,8 @@ admin_host = phosphoric-acid
database_host = caffeine
# this is the host which can make API requests to Mailman
mailman_host = mail
# this is the host running a CloudStack management server
cloud_host = biloba
use_https = true
port = 9987

View File

@ -79,4 +79,3 @@ host = localhost
api_key = REPLACE_ME
secret_key = REPLACE_ME
base_url = http://localhost:8080/client/api
members_domain = Members

View File

@ -0,0 +1,29 @@
from click.testing import CliRunner
import requests
from ...utils import gssapi_token_ctx
from ceo.cli import cli
def test_cloud_account_activate(cli_setup, mock_cloud_server, new_user, cfg):
base_domain = cfg.get('base_domain')
requests.post('http://localhost:8080/reset')
runner = CliRunner()
with gssapi_token_ctx(new_user.uid):
result = runner.invoke(cli, ['cloud', 'account', 'activate'])
expected = (
'Congratulations! Your cloud account has been activated.\n'
f'You may now login into https://cloud.{base_domain} with your CSC credentials.\n'
"Make sure to enter 'Members' for the domain (no quotes).\n"
)
assert result.exit_code == 0
assert result.output == expected
def test_cloud_accounts_purge(cli_setup, mock_cloud_server):
requests.post('http://localhost:8080/reset')
runner = CliRunner()
result = runner.invoke(cli, ['cloud', 'accounts', 'purge'])
assert result.exit_code == 0

View File

@ -7,6 +7,7 @@ uw_domain = uwaterloo.internal
admin_host = phosphoric-acid
database_host = coffee
mailman_host = mail
cloud_host = mail
use_https = false
port = 9987

View File

@ -73,4 +73,3 @@ host = localhost
api_key = REPLACE_ME
secret_key = REPLACE_ME
base_url = http://localhost:8080/client/api
members_domain = Members

View File

@ -72,4 +72,3 @@ host = coffee
api_key = REPLACE_ME
secret_key = REPLACE_ME
base_url = http://localhost:8080/client/api
members_domain = Members

View File

@ -346,9 +346,12 @@ def krb_user(simple_user):
simple_user.remove_from_kerberos()
@pytest.fixture
_new_user_id_counter = 10001
@pytest.fixture # noqa: E302
def new_user(client, g_admin_ctx, ldap_srv_session): # noqa: F811
uid = 'test_10001'
global _new_user_id_counter
uid = 'test_' + str(_new_user_id_counter)
_new_user_id_counter += 1
status, data = client.post('/api/members', json={
'uid': uid,
'cn': 'John Doe',

View File

@ -1,5 +1,3 @@
import os
import pytest
from .utils import gssapi_token_ctx
@ -7,9 +5,6 @@ from .utils import gssapi_token_ctx
@pytest.fixture(scope='module')
def cli_setup(app_process):
# This tells the CLI entrypoint not to register additional zope services.
os.environ['PYTEST'] = '1'
# Running the client and the server in the same process would be very
# messy because they would be sharing the same environment variables,
# Kerberos cache, and registered utilities (via zope). So we're just