~ubuntu-branches/ubuntu/trusty/heat/trusty-security

« back to all changes in this revision

Viewing changes to heat/tests/functional/test_CFN_API_Actions_Boto.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Yolanda Robla, Chuck Short
  • Date: 2013-07-22 16:22:29 UTC
  • mfrom: (1.1.2)
  • Revision ID: package-import@ubuntu.com-20130722162229-zzvfu40id94ii0hc
Tags: 2013.2~b2-0ubuntu1
[ Yolanda Robla ]
* debian/tests: added autopkg tests

[ Chuck Short ]
* New upstream release
* debian/control:
  - Add python-pbr to build-depends.
  - Add python-d2to to build-depends.
  - Dropped python-argparse.
  - Add python-six to build-depends.
  - Dropped python-sendfile.
  - Dropped python-nose.
  - Added testrepository.
  - Added python-testtools.
* debian/rules: Run testrepository instead of nosetets.
* debian/patches/removes-lxml-version-limitation-from-pip-requires.patch: Dropped
  no longer needed.
* debian/patches/fix-package-version-detection-when-building-doc.patch: Dropped
  no longer needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
 
#
3
 
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4
 
#    not use this file except in compliance with the License. You may obtain
5
 
#    a copy of the License at
6
 
#
7
 
#         http://www.apache.org/licenses/LICENSE-2.0
8
 
#
9
 
#    Unless required by applicable law or agreed to in writing, software
10
 
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
 
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
 
#    License for the specific language governing permissions and limitations
13
 
#
14
 
 
15
 
import os
16
 
import util
17
 
import verify
18
 
import re
19
 
from nose.plugins.attrib import attr
20
 
import unittest
21
 
import json
22
 
import datetime
23
 
 
24
 
 
25
 
@attr(speed='slow')
26
 
@attr(tag=['func', 'wordpress', 'api', 'cfn', 'boto'])
27
 
class CfnApiBotoFunctionalTest(unittest.TestCase):
28
 
    '''
29
 
    This test launches a wordpress stack then attempts to verify
30
 
    correct operation of all actions supported by the heat CFN API
31
 
 
32
 
    Note we use class-level fixtures to avoid setting up a new stack
33
 
    for every test method, we set up the stack once then do all the
34
 
    tests, this means all tests methods are performed on one class
35
 
    instance, instead of creating a new class for every method, which
36
 
    is the normal nose unittest.TestCase behavior.
37
 
 
38
 
    The nose docs are a bit vague on how to do this, but it seems that
39
 
    (setup|teardown)All works and they have to be classmethods.
40
 
 
41
 
    Contrary to the nose docs, the class can be a unittest.TestCase subclass
42
 
 
43
 
    This version of the test uses the boto client library, hence uses AWS auth
44
 
    and checks the boto-parsed results rather than parsing the XML directly
45
 
    '''
46
 
    @classmethod
47
 
    def setupAll(cls):
48
 
        print "SETUPALL"
49
 
        template = 'WordPress_Single_Instance.template'
50
 
 
51
 
        stack_paramstr = ';'.join(['InstanceType=m1.xlarge',
52
 
                         'DBUsername=dbuser',
53
 
                         'DBPassword=' + os.environ['OS_PASSWORD']])
54
 
 
55
 
        cls.logical_resource_name = 'WikiDatabase'
56
 
        cls.logical_resource_type = 'AWS::EC2::Instance'
57
 
 
58
 
        # Just to get the assert*() methods
59
 
        class CfnApiFunctions(unittest.TestCase):
60
 
            @unittest.skip('Not a real test case')
61
 
            def runTest(self):
62
 
                pass
63
 
 
64
 
        inst = CfnApiFunctions()
65
 
        cls.stack = util.StackBoto(inst, template, 'F17', 'x86_64', 'cfntools',
66
 
                                   stack_paramstr)
67
 
        cls.WikiDatabase = util.Instance(inst, cls.logical_resource_name)
68
 
 
69
 
        try:
70
 
            cls.stack.create()
71
 
            cls.WikiDatabase.wait_for_boot()
72
 
            cls.WikiDatabase.check_cfntools()
73
 
            cls.WikiDatabase.wait_for_provisioning()
74
 
 
75
 
            cls.logical_resource_status = "CREATE_COMPLETE"
76
 
 
77
 
            # Save some compiled regexes and strings for response validation
78
 
            cls.time_re = re.compile(
79
 
                "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$")
80
 
            cls.description_re = re.compile(
81
 
                "^AWS CloudFormation Sample Template")
82
 
            cls.stack_status = "CREATE_COMPLETE"
83
 
            cls.stack_status_reason = "Stack successfully created"
84
 
            cls.stack_timeout = 60
85
 
            cls.stack_disable_rollback = True
86
 
 
87
 
            # Match the expected format for an instance's physical resource ID
88
 
            cls.phys_res_id_re = re.compile(
89
 
                "^[0-9a-z]*-[0-9a-z]*-[0-9a-z]*-[0-9a-z]*-[0-9a-z]*$")
90
 
        except Exception as ex:
91
 
            print "setupAll failed : %s" % ex
92
 
            cls.stack.cleanup()
93
 
            raise
94
 
 
95
 
    @classmethod
96
 
    def teardownAll(cls):
97
 
        print "TEARDOWNALL"
98
 
        cls.stack.cleanup()
99
 
 
100
 
    def test_instance(self):
101
 
        # ensure wordpress was installed by checking for expected
102
 
        # configuration file over ssh
103
 
        # This is the same as the standard wordress template test
104
 
        # but we still do it to prove the stack is OK
105
 
        self.assertTrue(self.WikiDatabase.file_present
106
 
                        ('/etc/wordpress/wp-config.php'))
107
 
        print "Wordpress installation detected"
108
 
 
109
 
        # Verify the output URL parses as expected, ie check that
110
 
        # the wordpress installation is operational
111
 
        stack_url = self.stack.get_stack_output("WebsiteURL")
112
 
        print "Got stack output WebsiteURL=%s, verifying" % stack_url
113
 
        ver = verify.VerifyStack()
114
 
        self.assertTrue(ver.verify_wordpress(stack_url))
115
 
 
116
 
    def testListStacks(self):
117
 
        response = self.stack.heatclient.list_stacks()
118
 
        prefix = '/ListStacksResponse/ListStacksResult/StackSummaries/member'
119
 
 
120
 
        # Extract the StackSummary for this stack
121
 
        summary = [s for s in response
122
 
                   if s.stack_name == self.stack.stackname]
123
 
        self.assertEqual(len(summary), 1)
124
 
 
125
 
        # Note the boto StackSummary object does not contain every item
126
 
        # output by our API (ie defined in the AWS docs), we can only
127
 
        # test what boto encapsulates in the StackSummary class
128
 
        self.stack.check_stackid(summary[0].stack_id)
129
 
 
130
 
        self.assertEqual(type(summary[0].creation_time), datetime.datetime)
131
 
 
132
 
        self.assertTrue(self.description_re.match(
133
 
            summary[0].template_description) is not None)
134
 
 
135
 
        self.assertEqual(summary[0].stack_name, self.stack.stackname)
136
 
 
137
 
        self.assertEqual(summary[0].stack_status, self.stack_status)
138
 
 
139
 
        print "ListStacks : OK"
140
 
 
141
 
    def testDescribeStacks(self):
142
 
        parameters = {}
143
 
        parameters['StackName'] = self.stack.stackname
144
 
        response = self.stack.heatclient.describe_stacks(**parameters)
145
 
 
146
 
        # Extract the Stack object for this stack
147
 
        stacks = [s for s in response
148
 
                  if s.stack_name == self.stack.stackname]
149
 
        self.assertEqual(len(stacks), 1)
150
 
 
151
 
        self.assertEqual(type(stacks[0].creation_time), datetime.datetime)
152
 
 
153
 
        self.stack.check_stackid(stacks[0].stack_id)
154
 
 
155
 
        self.assertTrue(self.description_re.match(stacks[0].description)
156
 
                        is not None)
157
 
 
158
 
        self.assertEqual(stacks[0].stack_status_reason,
159
 
                         self.stack_status_reason)
160
 
 
161
 
        self.assertEqual(stacks[0].stack_name, self.stack.stackname)
162
 
 
163
 
        self.assertEqual(stacks[0].stack_status, self.stack_status)
164
 
 
165
 
        self.assertEqual(stacks[0].timeout_in_minutes, self.stack_timeout)
166
 
 
167
 
        self.assertEqual(stacks[0].disable_rollback,
168
 
                         self.stack_disable_rollback)
169
 
 
170
 
        # Create a dict to lookup the expected template parameters
171
 
        # NoEcho parameters are masked with 6 asterisks
172
 
        template_parameters = {'DBUsername': '******',
173
 
                               'LinuxDistribution': 'F17',
174
 
                               'InstanceType': 'm1.xlarge',
175
 
                               'DBRootPassword': '******',
176
 
                               'KeyName': self.stack.keyname,
177
 
                               'DBPassword': '******',
178
 
                               'DBName': 'wordpress'}
179
 
 
180
 
        for key, value in template_parameters.iteritems():
181
 
            # The parameters returned via the API include a couple
182
 
            # of fields which we don't care about (region/stackname)
183
 
            # and may possibly end up getting removed, so we just
184
 
            # look for the list of expected parameters above
185
 
            plist = [p for p in s.parameters if p.key == key]
186
 
            self.assertEqual(len(plist), 1)
187
 
            self.assertEqual(key, plist[0].key)
188
 
            self.assertEqual(value, plist[0].value)
189
 
 
190
 
        # Then to a similar lookup to verify the Outputs section
191
 
        expected_url = "http://" + self.WikiDatabase.ip + "/wordpress"
192
 
        self.assertEqual(len(s.outputs), 1)
193
 
        self.assertEqual(s.outputs[0].key, 'WebsiteURL')
194
 
        self.assertEqual(s.outputs[0].value, expected_url)
195
 
 
196
 
        print "DescribeStacks : OK"
197
 
 
198
 
    def testDescribeStackEvents(self):
199
 
        parameters = {}
200
 
        parameters['StackName'] = self.stack.stackname
201
 
        response = self.stack.heatclient.list_stack_events(**parameters)
202
 
        events = [e for e in response
203
 
                  if e.logical_resource_id == self.logical_resource_name
204
 
                  and e.resource_status == self.logical_resource_status]
205
 
 
206
 
        self.assertEqual(len(events), 1)
207
 
 
208
 
        self.stack.check_stackid(events[0].stack_id)
209
 
 
210
 
        self.assertTrue(re.match("[0-9]*$", events[0].event_id) is not None)
211
 
 
212
 
        self.assertEqual(events[0].resource_status,
213
 
                         self.logical_resource_status)
214
 
 
215
 
        self.assertEqual(events[0].resource_type, self.logical_resource_type)
216
 
 
217
 
        self.assertEqual(type(events[0].timestamp), datetime.datetime)
218
 
 
219
 
        self.assertEqual(events[0].resource_status_reason, "state changed")
220
 
 
221
 
        self.assertEqual(events[0].stack_name, self.stack.stackname)
222
 
 
223
 
        self.assertEqual(events[0].logical_resource_id,
224
 
                         self.logical_resource_name)
225
 
 
226
 
        self.assertTrue(self.phys_res_id_re.match(
227
 
                        events[0].physical_resource_id) is not None)
228
 
 
229
 
        # Check ResourceProperties, skip pending resolution of #245
230
 
        properties = json.loads(events[0].resource_properties)
231
 
        self.assertEqual(properties["InstanceType"], "m1.xlarge")
232
 
 
233
 
        print "DescribeStackEvents : OK"
234
 
 
235
 
    def testGetTemplate(self):
236
 
        parameters = {}
237
 
        parameters['StackName'] = self.stack.stackname
238
 
        response = self.stack.heatclient.get_template(**parameters)
239
 
        self.assertTrue(response is not None)
240
 
 
241
 
        result = response['GetTemplateResponse']['GetTemplateResult']
242
 
        self.assertTrue(result is not None)
243
 
        template = result['TemplateBody']
244
 
        self.assertTrue(template is not None)
245
 
 
246
 
        # Then sanity check content - I guess we could diff
247
 
        # with the template file but for now just check the
248
 
        # description looks sane..
249
 
        description = template['Description']
250
 
        self.assertTrue(self.description_re.match(description) is not None)
251
 
 
252
 
        print "GetTemplate : OK"
253
 
 
254
 
    def testDescribeStackResource(self):
255
 
        parameters = {'StackName': self.stack.stackname,
256
 
                      'LogicalResourceId': self.logical_resource_name}
257
 
        response = self.stack.heatclient.describe_stack_resource(**parameters)
258
 
 
259
 
        # Note boto_client response for this is a dict, if upstream
260
 
        # pull request ever gets merged, this will change, see note/
261
 
        # link in boto_client.py
262
 
        desc_resp = response['DescribeStackResourceResponse']
263
 
        self.assertTrue(desc_resp is not None)
264
 
        desc_result = desc_resp['DescribeStackResourceResult']
265
 
        self.assertTrue(desc_result is not None)
266
 
        res = desc_result['StackResourceDetail']
267
 
        self.assertTrue(res is not None)
268
 
 
269
 
        self.stack.check_stackid(res['StackId'])
270
 
 
271
 
        self.assertEqual(res['ResourceStatus'], self.logical_resource_status)
272
 
 
273
 
        self.assertEqual(res['ResourceType'], self.logical_resource_type)
274
 
 
275
 
        # Note due to issue mentioned above timestamp is a string in this case
276
 
        # not a datetime.datetime object
277
 
        self.assertTrue(self.time_re.match(res['LastUpdatedTimestamp'])
278
 
                        is not None)
279
 
 
280
 
        self.assertEqual(res['ResourceStatusReason'], 'state changed')
281
 
 
282
 
        self.assertEqual(res['StackName'], self.stack.stackname)
283
 
 
284
 
        self.assertEqual(res['LogicalResourceId'], self.logical_resource_name)
285
 
 
286
 
        self.assertTrue(self.phys_res_id_re.match(res['PhysicalResourceId'])
287
 
                        is not None)
288
 
 
289
 
        self.assertTrue("AWS::CloudFormation::Init" in res['Metadata'])
290
 
 
291
 
        print "DescribeStackResource : OK"
292
 
 
293
 
    def testDescribeStackResources(self):
294
 
        parameters = {'NameOrPid': self.stack.stackname,
295
 
                      'LogicalResourceId': self.logical_resource_name}
296
 
        response = self.stack.heatclient.describe_stack_resources(**parameters)
297
 
        self.assertEqual(len(response), 1)
298
 
 
299
 
        res = response[0]
300
 
        self.assertTrue(res is not None)
301
 
 
302
 
        self.stack.check_stackid(res.stack_id)
303
 
 
304
 
        self.assertEqual(res.resource_status, self.logical_resource_status)
305
 
 
306
 
        self.assertEqual(res.resource_type, self.logical_resource_type)
307
 
 
308
 
        self.assertEqual(type(res.timestamp), datetime.datetime)
309
 
 
310
 
        self.assertEqual(res.resource_status_reason, 'state changed')
311
 
 
312
 
        self.assertEqual(res.stack_name, self.stack.stackname)
313
 
 
314
 
        self.assertEqual(res.logical_resource_id, self.logical_resource_name)
315
 
 
316
 
        self.assertTrue(self.phys_res_id_re.match(res.physical_resource_id)
317
 
                        is not None)
318
 
 
319
 
        print "DescribeStackResources : OK"
320
 
 
321
 
    def testListStackResources(self):
322
 
        parameters = {}
323
 
        parameters['StackName'] = self.stack.stackname
324
 
        response = self.stack.heatclient.list_stack_resources(**parameters)
325
 
        self.assertEqual(len(response), 1)
326
 
 
327
 
        res = response[0]
328
 
        self.assertTrue(res is not None)
329
 
 
330
 
        self.assertEqual(res.resource_status, self.logical_resource_status)
331
 
 
332
 
        self.assertEqual(res.resource_status_reason, 'state changed')
333
 
 
334
 
        self.assertEqual(type(res.last_updated_timestamp), datetime.datetime)
335
 
 
336
 
        self.assertEqual(res.resource_type, self.logical_resource_type)
337
 
 
338
 
        self.assertEqual(res.logical_resource_id, self.logical_resource_name)
339
 
 
340
 
        self.assertTrue(self.phys_res_id_re.match(res.physical_resource_id)
341
 
                        is not None)
342
 
 
343
 
        print "ListStackResources : OK"
344
 
 
345
 
    def testValidateTemplate(self):
346
 
        # Use stack.format_parameters to get the TemplateBody
347
 
        params = self.stack.format_parameters()
348
 
        val_params = {'TemplateBody': params['TemplateBody']}
349
 
        response = self.stack.heatclient.validate_template(**val_params)
350
 
        # Check the response contains all the expected paramter keys
351
 
        templ_params = ['DBUsername', 'LinuxDistribution', 'InstanceType',
352
 
                        'DBRootPassword', 'KeyName', 'DBPassword', 'DBName']
353
 
 
354
 
        resp_params = [p.parameter_key for p in response.template_parameters]
355
 
        for param in templ_params:
356
 
            self.assertTrue(param in resp_params)
357
 
        print "ValidateTemplate : OK"