Display stack owner when formatting stacks

Stacks are launched by users but scoped to tenants, so users in the same
tenant currently have no way to know owns each stack.  The same is
especially true to unscoped stack lists.  Since humans are much better
with names than they are with numbers, this adds the stack owner
username to the data returned with each stack.

Co-Authored-By: Anderson Mesquita <andersonvom@gmail.com>
Implements: blueprint stack-display-fields
Change-Id: Ia1386531025096e0eeaf9e98b478c4453fc8cb01
This commit is contained in:
Richard Lee 2014-07-15 10:30:27 -04:00 committed by Anderson Mesquita
parent 227c659a50
commit 25788a13c2
6 changed files with 55 additions and 14 deletions

View File

@ -19,14 +19,17 @@ from heat.rpc import api as engine_api
_collection_name = 'stacks'
basic_keys = (engine_api.STACK_ID,
engine_api.STACK_NAME,
engine_api.STACK_DESCRIPTION,
engine_api.STACK_STATUS,
engine_api.STACK_STATUS_DATA,
engine_api.STACK_CREATION_TIME,
engine_api.STACK_DELETION_TIME,
engine_api.STACK_UPDATED_TIME)
basic_keys = (
engine_api.STACK_ID,
engine_api.STACK_NAME,
engine_api.STACK_DESCRIPTION,
engine_api.STACK_STATUS,
engine_api.STACK_STATUS_DATA,
engine_api.STACK_CREATION_TIME,
engine_api.STACK_DELETION_TIME,
engine_api.STACK_UPDATED_TIME,
engine_api.STACK_OWNER,
)
def format_stack(req, stack, keys=None, tenant_safe=True):

View File

@ -102,6 +102,7 @@ def format_stack(stack):
api.STACK_CAPABILITIES: [], # TODO Not implemented yet
api.STACK_DISABLE_ROLLBACK: stack.disable_rollback,
api.STACK_TIMEOUT: stack.timeout_mins,
api.STACK_OWNER: stack.username,
}
# allow users to view the outputs of stacks

View File

@ -64,7 +64,7 @@ class Stack(collections.Mapping):
adopt_stack_data=None, stack_user_project_id=None,
created_time=None, updated_time=None,
user_creds_id=None, tenant_id=None,
use_stored_context=False):
use_stored_context=False, username=None):
'''
Initialise from a context, name, Template object and (optionally)
Environment object. The database ID may also be initialised, if the
@ -107,6 +107,7 @@ class Stack(collections.Mapping):
# This will use the provided tenant ID when loading the stack
# from the DB or get it from the context for new stacks.
self.tenant_id = tenant_id or self.context.tenant_id
self.username = username or self.context.username
resources.initialise()
@ -266,7 +267,8 @@ class Stack(collections.Mapping):
created_time=stack.created_at,
updated_time=stack.updated_at,
user_creds_id=stack.user_creds_id, tenant_id=stack.tenant,
use_stored_context=use_stored_context)
use_stored_context=use_stored_context,
username=stack.username)
def store(self, backup=False):
'''
@ -278,7 +280,7 @@ class Stack(collections.Mapping):
'raw_template_id': self.t.store(self.context),
'parameters': self.env.user_env_as_dict(),
'owner_id': self.owner_id,
'username': self.context.username,
'username': self.username,
'tenant': self.tenant_id,
'action': self.action,
'status': self.status,

View File

@ -28,7 +28,7 @@ STACK_KEYS = (
STACK_DESCRIPTION, STACK_TMPL_DESCRIPTION,
STACK_PARAMETERS, STACK_OUTPUTS, STACK_ACTION,
STACK_STATUS, STACK_STATUS_DATA, STACK_CAPABILITIES,
STACK_DISABLE_ROLLBACK, STACK_TIMEOUT,
STACK_DISABLE_ROLLBACK, STACK_TIMEOUT, STACK_OWNER,
) = (
'stack_name', 'stack_identity',
'creation_time', 'updated_time', 'deletion_time',
@ -36,7 +36,7 @@ STACK_KEYS = (
'description', 'template_description',
'parameters', 'outputs', 'stack_action',
'stack_status', 'stack_status_reason', 'capabilities',
'disable_rollback', 'timeout_mins',
'disable_rollback', 'timeout_mins', 'stack_owner',
)
STACK_OUTPUT_KEYS = (

View File

@ -233,6 +233,7 @@ class FormatTest(HeatTestCase):
'outputs': [],
'stack_action': '',
'stack_name': 'test_stack',
'stack_owner': 'test_username',
'stack_status': '',
'stack_status_reason': '',
'template_description': 'No description',

View File

@ -929,6 +929,17 @@ class StackTest(HeatTestCase):
tenant_id=None)
self.assertEqual('foo', stack.tenant_id)
def test_stack_reads_username(self):
stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
username='bar')
self.assertEqual('bar', stack.username)
def test_stack_reads_username_from_context_if_empty(self):
self.ctx.username = 'foo'
stack = parser.Stack(self.ctx, 'test_stack', self.tmpl,
username=None)
self.assertEqual('foo', stack.username)
def test_stack_string_repr(self):
stack = parser.Stack(self.ctx, 'test_stack', self.tmpl)
expected = 'Stack "%s" [%s]' % (stack.name, stack.id)
@ -1107,7 +1118,8 @@ class StackTest(HeatTestCase):
updated_time=None,
user_creds_id=stack.user_creds_id,
tenant_id='test_tenant_id',
use_stored_context=False)
use_stored_context=False,
username=IgnoreArg())
self.m.ReplayAll()
parser.Stack.load(self.ctx, stack_id=self.stack.id,
@ -1215,6 +1227,20 @@ class StackTest(HeatTestCase):
stack = parser.Stack.load(self.ctx, stack_id=stack_id)
self.assertEqual('foobar', stack.tenant_id)
def test_load_reads_username_from_db(self):
self.ctx.username = 'foobar'
self.stack = parser.Stack(self.ctx, 'stack_name', self.tmpl)
self.stack.store()
stack_id = self.stack.id
self.ctx.username = None
stack = parser.Stack.load(self.ctx, stack_id=stack_id)
self.assertEqual('foobar', stack.username)
self.ctx.username = 'not foobar'
stack = parser.Stack.load(self.ctx, stack_id=stack_id)
self.assertEqual('foobar', stack.username)
def test_created_time(self):
self.stack = parser.Stack(self.ctx, 'creation_time_test',
self.tmpl)
@ -3272,6 +3298,14 @@ class StackTest(HeatTestCase):
expected_err = 'Attempt to use stored_context with no user_creds'
self.assertEqual(expected_err, six.text_type(ex))
def test_store_gets_username_from_stack(self):
self.stack = parser.Stack(self.ctx, 'username_stack',
self.tmpl, username='foobar')
self.ctx.username = 'not foobar'
self.stack.store()
db_stack = db_api.stack_get(self.ctx, self.stack.id)
self.assertEqual('foobar', db_stack.username)
def test_init_stored_context_false(self):
ctx_init = utils.dummy_context(user='mystored_user',
password='mystored_pass')