Skip to content

Commit

Permalink
Merge pull request #8290 from tasdomas/d021-budget-cmd-support-for-mo…
Browse files Browse the repository at this point in the history
…ving-wallets

Budget command supports moving budgets between wallets.

This is a forward of #8262

## Description of change
This change enables users to use the `juju budget` command to move a budget to a different wallet (as well as update the budget's limit).

This change was requested by beta users.
## QA steps
Running the `juju budget` command with a parameter referencing an existing wallet should move the budget to the new wallet.

## Documentation changes
The change augments the `juju budget` command.
  • Loading branch information
jujubot committed Jan 16, 2018
2 parents 7ce5eb7 + 05f6704 commit 5d42958
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 120 deletions.
202 changes: 101 additions & 101 deletions acceptancetests/assess_budget.py → acceptancetests/assess_wallet.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
"""
This tests the budget commands utilized for commercial charm billing.
This tests the wallet commands utilized for commercial charm billing.
These commands are linked to a ubuntu sso account, and as such, require the
user account to be setup before test execution (including authentication).
You can use charm login to do this, or let juju authenticate with a browser.
Expand Down Expand Up @@ -34,81 +34,81 @@
__metaclass__ = type


log = logging.getLogger("assess_budget")
log = logging.getLogger("assess_wallet")


def _get_new_budget_limit(client):
"""Return availible limit for new budget"""
budgets = json.loads(list_budgets(client))
limit = int(budgets['total']['limit'])
credit = int(budgets['credit'])
def _get_new_wallet_limit(client):
"""Return availible limit for new wallet"""
wallets = json.loads(list_wallets(client))
limit = int(wallets['total']['limit'])
credit = int(wallets['credit'])
log.debug('Found credit limit {}, currently used {}'.format(
credit, limit))
return credit - limit


def _get_budgets(client):
return json.loads(list_budgets(client))['wallets']
def _get_wallets(client):
return json.loads(list_wallets(client))['wallets']


def _set_budget_value_expectations(expected_budgets, name, value):
def _set_wallet_value_expectations(expected_wallets, name, value):
# Update our expectations accordingly
for budget in expected_budgets:
if budget['wallet'] == name:
# For now, we assume we aren't spending down the budget
budget['limit'] = value
budget['unallocated'] = value
for wallet in expected_wallets:
if wallet['wallet'] == name:
# For now, we assume we aren't spending down the wallet
wallet['limit'] = value
wallet['unallocated'] = value
# .00 is appended to availible for some reason
budget['available'] = '{:.2f}'.format(float(value))
log.info('Expected budget updated: "{}" to {}'.format(name, value))
wallet['available'] = '{:.2f}'.format(float(value))
log.info('Expected wallet updated: "{}" to {}'.format(name, value))


def _try_setting_budget(client, name, value):
def _try_setting_wallet(client, name, value):
try:
output = set_budget(client, name, value)
output = set_wallet(client, name, value)
except subprocess.CalledProcessError as e:
output = [e.output, getattr(e, 'stderr', '')]
raise JujuAssertionError('Could not set budget {}'.format(output))
raise JujuAssertionError('Could not set wallet {}'.format(output))

if 'wallet limit updated' not in output:
raise JujuAssertionError('Error calling set-wallet {}'.format(output))


def _try_creating_budget(client, name, value):
def _try_creating_wallet(client, name, value):
try:
create_budget(client, name, value)
log.info('Created new budget "{}" with value {}'.format(name,
create_wallet(client, name, value)
log.info('Created new wallet "{}" with value {}'.format(name,
value))
except subprocess.CalledProcessError as e:
output = [e.output, getattr(e, 'stderr', '')]
if any('already exists' in message for message in output):
log.info('Reusing budget "{}" with value {}'.format(name, value))
log.info('Reusing wallet "{}" with value {}'.format(name, value))
pass # this will be a failure once lp:1663258 is fixed
else:
raise JujuAssertionError(
'Error testing create-budget: {}'.format(output))
'Error testing create-wallet: {}'.format(output))
except:
raise JujuAssertionError('Added duplicate budget')
raise JujuAssertionError('Added duplicate wallet')


def _try_greater_than_limit_budget(client, name, limit):
def _try_greater_than_limit_wallet(client, name, limit):
error_strings = {
'pass': 'exceed the credit limit',
'unknown': 'Error testing budget greater than credit limit',
'unknown': 'Error testing wallet greater than credit limit',
'fail': 'Credit limit exceeded'
}
over_limit_value = str(limit + randint(1, 100))
assert_set_budget(client, name, over_limit_value, error_strings)
assert_set_wallet(client, name, over_limit_value, error_strings)


def _try_negative_budget(client, name):
def _try_negative_wallet(client, name):
error_strings = {
'pass': 'Could not set budget',
'unknown': 'Error testing negative budget',
'fail': 'Negative budget allowed'
'pass': 'Could not set wallet',
'unknown': 'Error testing negative wallet',
'fail': 'Negative wallet allowed'
}
negative_budget_value = str(randint(-1000, -1))
assert_set_budget(client, name, negative_budget_value, error_strings)
negative_wallet_value = str(randint(-1000, -1))
assert_set_wallet(client, name, negative_wallet_value, error_strings)


def assert_sorted_equal(found, expected):
Expand All @@ -119,9 +119,9 @@ def assert_sorted_equal(found, expected):
'Found: {}\nExpected: {}'.format(found, expected))


def assert_set_budget(client, name, limit, error_strings):
def assert_set_wallet(client, name, limit, error_strings):
try:
_try_setting_budget(client, name, limit)
_try_setting_wallet(client, name, limit)
except JujuAssertionError as e:
if error_strings['pass'] not in e.message:
raise JujuAssertionError(
Expand All @@ -130,120 +130,120 @@ def assert_set_budget(client, name, limit, error_strings):
raise JujuAssertionError(error_strings['fail'])


def create_budget(client, name, value):
"""Create a budget"""
def create_wallet(client, name, value):
"""Create a wallet"""
return client.get_juju_output('create-wallet', name, value,
include_e=False)


def list_budgets(client):
"""Return defined budgets as json."""
def list_wallets(client):
"""Return defined wallets as json."""
return client.get_juju_output('list-wallets', '--format', 'json',
include_e=False)


def set_budget(client, name, value):
"""Change an existing budgets allocation."""
def set_wallet(client, name, value):
"""Change an existing wallet's allocation."""
return client.get_juju_output('set-wallet', name, value, include_e=False)


def show_budget(client, name):
"""Return specified budget as json."""
def show_wallet(client, name):
"""Return specified wallet as json."""
return client.get_juju_output('show-wallet', name, '--format', 'json',
include_e=False)


def assess_budget(client):
# Since we can't remove budgets until lp:1663258
# is fixed, we avoid creating new random budgets and hardcode.
# We also, zero out the previous budget
budget_name = 'personal'
_try_setting_budget(client, budget_name, '0')
def assess_wallet(client):
# Since we can't remove wallets until lp:1663258
# is fixed, we avoid creating new random wallets and hardcode.
# We also, zero out the previous wallet
wallet_name = 'personal'
_try_setting_wallet(client, wallet_name, '0')

budget_limit = _get_new_budget_limit(client)
assess_budget_limit(budget_limit)
wallet_limit = _get_new_wallet_limit(client)
assess_wallet_limit(wallet_limit)

expected_budgets = _get_budgets(client)
budget_value = str(randint(1, budget_limit / 2))
assess_create_budget(client, budget_name, budget_value, budget_limit)
expected_wallets = _get_wallets(client)
wallet_value = str(randint(1, wallet_limit / 2))
assess_create_wallet(client, wallet_name, wallet_value, wallet_limit)

budget_value = str(randint(budget_limit / 2 + 1, budget_limit))
assess_set_budget(client, budget_name, budget_value, budget_limit)
assess_show_budget(client, budget_name, budget_value)
wallet_value = str(randint(wallet_limit / 2 + 1, wallet_limit))
assess_set_wallet(client, wallet_name, wallet_value, wallet_limit)
assess_show_wallet(client, wallet_name, wallet_value)

_set_budget_value_expectations(expected_budgets, budget_name, budget_value)
assess_list_budgets(client, expected_budgets)
_set_wallet_value_expectations(expected_wallets, wallet_name, wallet_value)
assess_list_wallets(client, expected_wallets)


def assess_budget_limit(budget_limit):
log.info('Assessing budget limit {}'.format(budget_limit))
def assess_wallet_limit(wallet_limit):
log.info('Assessing wallet limit {}'.format(wallet_limit))

if budget_limit < 0:
if wallet_limit < 0:
raise JujuAssertionError(
'Negative Budget Limit {}'.format(budget_limit))
'Negative Wallet Limit {}'.format(wallet_limit))


def assess_create_budget(client, budget_name, budget_value, budget_limit):
"""Test create-budget command"""
log.info('create-budget "{}" with value {}, limit {}'.format(budget_name,
budget_value,
budget_limit))
def assess_create_wallet(client, wallet_name, wallet_value, wallet_limit):
"""Test create-wallet command"""
log.info('create-wallet "{}" with value {}, limit {}'.format(wallet_name,
wallet_value,
wallet_limit))

# Do this twice, to ensure budget exists and we can check for
# Do this twice, to ensure wallet exists and we can check for
# duplicate message. Ideally, once lp:1663258 is fixed, we will
# assert on initial budget creation as well.
_try_creating_budget(client, budget_name, budget_value)
# assert on initial wallet creation as well.
_try_creating_wallet(client, wallet_name, wallet_value)

log.info('Trying duplicate create-budget')
_try_creating_budget(client, budget_name, budget_value)
log.info('Trying duplicate create-wallet')
_try_creating_wallet(client, wallet_name, wallet_value)


def assess_list_budgets(client, expected_budgets):
def assess_list_wallets(client, expected_wallets):
log.info('list-wallets testing expected values')
# Since we can't remove budgets until lp:1663258
# Since we can't remove wallets until lp:1663258
# is fixed, we don't modify the list contents or count
# Nonetheless, we assert on it for future use
budgets = _get_budgets(client)
assert_sorted_equal(budgets, expected_budgets)
wallets = _get_wallets(client)
assert_sorted_equal(wallets, expected_wallets)


def assess_set_budget(client, budget_name, budget_value, budget_limit):
def assess_set_wallet(client, wallet_name, wallet_value, wallet_limit):
"""Test set-wallet command"""
log.info('set-wallet "{}" with value {}, limit {}'.format(budget_name,
budget_value,
budget_limit))
_try_setting_budget(client, budget_name, budget_value)
log.info('set-wallet "{}" with value {}, limit {}'.format(wallet_name,
wallet_value,
wallet_limit))
_try_setting_wallet(client, wallet_name, wallet_value)

# Check some bounds
# Since budgetting is important, and the functional test is cheap,
# Since walletting is important, and the functional test is cheap,
# let's test some basic bounds
log.info('Trying set-wallet with value greater than budget limit')
_try_greater_than_limit_budget(client, budget_name, budget_limit)
log.info('Trying set-wallet with value greater than wallet limit')
_try_greater_than_limit_wallet(client, wallet_name, wallet_limit)

log.info('Trying set-wallet with negative value')
_try_negative_budget(client, budget_name)
_try_negative_wallet(client, wallet_name)


def assess_show_budget(client, budget_name, budget_value):
log.info('show-wallet "{}" with value {}'.format(budget_name,
budget_value))
def assess_show_wallet(client, wallet_name, wallet_value):
log.info('show-wallet "{}" with value {}'.format(wallet_name,
wallet_value))

budget = json.loads(show_budget(client, budget_name))
wallet = json.loads(show_wallet(client, wallet_name))

# assert budget value
if budget['limit'] != budget_value:
raise JujuAssertionError('Budget limit found {}, expected {}'.format(
budget['limit'], budget_value))
# assert wallet value
if wallet['limit'] != wallet_value:
raise JujuAssertionError('Wallet limit found {}, expected {}'.format(
wallet['limit'], wallet_value))

# assert on usage (0% until we use it)
if budget['total']['usage'] != '0%':
raise JujuAssertionError('Budget usage found {}, expected {}'.format(
budget['total']['usage'], '0%'))
if wallet['total']['usage'] != '0%':
raise JujuAssertionError('Wallet usage found {}, expected {}'.format(
wallet['total']['usage'], '0%'))


def parse_args(argv):
"""Parse all arguments."""
parser = argparse.ArgumentParser(description="Test budget commands")
parser = argparse.ArgumentParser(description="Test wallet commands")
add_basic_testing_arguments(parser)
return parser.parse_args(argv)

Expand Down Expand Up @@ -297,7 +297,7 @@ def main(argv=None):
bs_manager = BootstrapManager.from_args(args)
with bs_manager.booted_context(args.upload_tools):
set_controller_cookie_file(bs_manager.client)
assess_budget(bs_manager.client)
assess_wallet(bs_manager.client)
return 0


Expand Down

0 comments on commit 5d42958

Please sign in to comment.