Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/tests/unit/customizations/codedeploy/test_push.py
2637 views
1
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License"). You
4
# may not use this file except in compliance with the License. A copy of
5
# the License is located at
6
#
7
# http://aws.amazon.com/apache2.0/
8
#
9
# or in the "license" file accompanying this file. This file is
10
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
# ANY KIND, either express or implied. See the License for the specific
12
# language governing permissions and limitations under the License.
13
14
import awscli
15
16
from argparse import Namespace
17
from botocore.exceptions import ClientError
18
19
from awscli.customizations.codedeploy.push import Push
20
from awscli.testutils import mock, unittest
21
from awscli.compat import StringIO, ZIP_COMPRESSION_MODE
22
23
24
class TestPush(unittest.TestCase):
25
def setUp(self):
26
self.application_name = 'MyApp'
27
self.description = 'MyApp revision'
28
self.source = '/tmp'
29
self.appspec = 'appspec.yml'
30
self.appspec_path = '{0}/{1}'.format(self.source, self.appspec)
31
self.bucket = 'foo'
32
self.key = 'bar/baz.zip'
33
self.s3_location = 's3://' + self.bucket + '/' + self.key
34
self.eTag = '"1a2b3cd45e"'
35
self.version_id = '12341234-1234-1234-1234-123412341234'
36
self.upload_id = 'upload_id'
37
self.region = 'us-east-1'
38
self.endpoint_url = 'https://codedeploy.aws.amazon.com'
39
40
self.args = Namespace()
41
self.args.application_name = self.application_name
42
self.args.s3_location = self.s3_location
43
self.args.ignore_hidden_files = False
44
self.args.no_ignore_hidden_files = False
45
self.args.description = self.description
46
self.args.source = self.source
47
48
self.globals = Namespace()
49
self.globals.region = self.region
50
self.globals.endpoint_url = self.endpoint_url
51
self.globals.verify_ssl = False
52
53
self.upload_response = {
54
'ETag': self.eTag,
55
'VersionId': self.version_id
56
}
57
self.revision = {
58
'revisionType': 'S3',
59
's3Location': {
60
'bucket': self.bucket,
61
'key': self.key,
62
'bundleType': 'zip',
63
'eTag': self.eTag,
64
'version': self.version_id
65
}
66
}
67
68
self.bundle_mock = mock.MagicMock()
69
self.bundle_mock.tell.return_value = (5 << 20)
70
self.bundle_mock.read.return_value = b'a' * (5 << 20)
71
self.bundle_mock.__enter__.return_value = self.bundle_mock
72
self.bundle_mock.__exit__.return_value = None
73
74
self.zipfile_mock = mock.MagicMock()
75
self.zipfile_mock.write.return_value = None
76
self.zipfile_mock.close.return_value = None
77
self.zipfile_mock.__enter__.return_value = self.zipfile_mock
78
self.zipfile_mock.__exit__.return_value = None
79
80
self.session = mock.MagicMock()
81
82
self.push = Push(self.session)
83
self.push.s3 = mock.MagicMock()
84
self.push.s3.put_object.return_value = self.upload_response
85
self.push.s3.create_multipart_upload.return_value = {
86
'UploadId': self.upload_id
87
}
88
self.push.s3.upload_part.return_value = {
89
'ETag': self.eTag
90
}
91
self.push.s3.complete_multipart_upload\
92
.return_value = self.upload_response
93
self.push.codedeploy = mock.MagicMock()
94
95
def test_run_main_throws_on_invalid_args(self):
96
self.push._validate_args = mock.MagicMock()
97
self.push._validate_args.side_effect = RuntimeError()
98
with self.assertRaises(RuntimeError):
99
self.push._run_main(self.args, self.globals)
100
101
def test_run_main_creates_clients(self):
102
self.push._validate_args = mock.MagicMock()
103
self.push._push = mock.MagicMock()
104
self.push._run_main(self.args, self.globals)
105
self.session.create_client.assert_has_calls([
106
mock.call(
107
'codedeploy',
108
region_name=self.region,
109
endpoint_url=self.endpoint_url,
110
verify=self.globals.verify_ssl
111
),
112
mock.call('s3', region_name=self.region)
113
])
114
115
def test_run_main_calls_push(self):
116
self.push._validate_args = mock.MagicMock()
117
self.push._push = mock.MagicMock()
118
self.push._run_main(self.args, self.globals)
119
self.push._push.assert_called_with(self.args)
120
121
@mock.patch.object(
122
awscli.customizations.codedeploy.push,
123
'validate_s3_location'
124
)
125
def test_validate_args_throws_on_invalid_s3_url(
126
self, validate_s3_location
127
):
128
self.args.s3_location = 's3:/foo/bar/baz'
129
validate_s3_location.side_effect = RuntimeError()
130
with self.assertRaises(RuntimeError):
131
self.push._validate_args(self.args)
132
133
def test_validate_args_throws_on_ignore_and_no_ignore_hidden_files(self):
134
self.args.ignore_hidden_files = True
135
self.args.no_ignore_hidden_files = True
136
with self.assertRaises(RuntimeError):
137
self.push._validate_args(self.args)
138
139
def test_validate_args_default_description(self):
140
self.args.description = None
141
self.push._validate_args(self.args)
142
self.assertRegex(
143
self.args.description,
144
'Uploaded by AWS CLI .* UTC'
145
)
146
147
def test_push_throws_on_upload_to_s3_error(self):
148
self.args.bucket = self.bucket
149
self.args.key = self.key
150
self.push._compress = mock.MagicMock(return_value=self.bundle_mock)
151
self.push._upload_to_s3 = mock.MagicMock()
152
self.push._upload_to_s3.side_effect = RuntimeError()
153
with self.assertRaises(RuntimeError):
154
self.push._push(self.args)
155
156
def test_push_strips_quotes_from_etag(self):
157
self.args.bucket = self.bucket
158
self.args.key = self.key
159
self.push._compress = mock.MagicMock(return_value=self.bundle_mock)
160
self.push._upload_to_s3 = mock.MagicMock(return_value=self.upload_response)
161
self.push._register_revision = mock.MagicMock()
162
self.push._push(self.args)
163
self.push._register_revision.assert_called_with(self.args)
164
self.assertEqual(str(self.args.eTag), self.upload_response['ETag'].replace('"',""))
165
166
@mock.patch('sys.stdout', new_callable=StringIO)
167
def test_push_output_message(self, stdout_mock):
168
self.args.bucket = self.bucket
169
self.args.key = self.key
170
self.push._compress = mock.MagicMock(return_value=self.bundle_mock)
171
self.push._upload_to_s3 = mock.MagicMock(return_value=self.upload_response)
172
self.push._register_revision = mock.MagicMock()
173
self.push._push(self.args)
174
output = stdout_mock.getvalue().strip()
175
expected_revision_output = (
176
'--s3-location bucket={0},key={1},'
177
'bundleType=zip,eTag={2},version={3}'.format(
178
self.bucket,
179
self.key,
180
self.eTag.replace('"',""),
181
self.version_id)
182
)
183
expected_output = (
184
'To deploy with this revision, run:\n'
185
'aws deploy create-deployment '
186
'--application-name {0} {1} '
187
'--deployment-group-name <deployment-group-name> '
188
'--deployment-config-name <deployment-config-name> '
189
'--description <description>'.format(
190
self.application_name,
191
expected_revision_output
192
)
193
)
194
self.assertEqual(expected_output, output)
195
196
@mock.patch('zipfile.ZipFile')
197
@mock.patch('tempfile.TemporaryFile')
198
@mock.patch('os.path')
199
@mock.patch('os.walk')
200
def test_compress_throws_when_no_appspec(self, walk, path, tf, zf):
201
walk.return_value = [(self.source, [], ['noappspec.yml'])]
202
noappsec_path = self.source + '/noappspec.yml'
203
path.join.return_value = noappsec_path
204
path.sep = '/'
205
path.abspath.side_effect = [self.source, noappsec_path]
206
tf.return_value = self.bundle_mock
207
zf.return_value = self.zipfile_mock
208
with self.assertRaises(RuntimeError):
209
with self.push._compress(
210
self.args.source,
211
self.args.ignore_hidden_files):
212
pass
213
214
@mock.patch('zipfile.ZipFile')
215
@mock.patch('tempfile.TemporaryFile')
216
@mock.patch('os.path')
217
@mock.patch('os.walk')
218
def test_compress_writes_to_zip_file(self, walk, path, tf, zf):
219
walk.return_value = [(self.source, [], [self.appspec])]
220
path.join.return_value = self.appspec_path
221
path.sep = '/'
222
path.abspath.side_effect = [self.source, self.appspec_path]
223
tf.return_value = self.bundle_mock
224
zf.return_value = self.zipfile_mock
225
with self.push._compress(
226
self.args.source,
227
self.args.ignore_hidden_files):
228
zf.assert_called_with(mock.ANY, 'w', allowZip64=True)
229
zf().write.assert_called_with(
230
'/tmp/appspec.yml',
231
self.appspec,
232
ZIP_COMPRESSION_MODE
233
)
234
235
def test_upload_to_s3_with_put_object(self):
236
self.args.bucket = self.bucket
237
self.args.key = self.key
238
response = self.push._upload_to_s3(self.args, self.bundle_mock)
239
self.assertDictEqual(self.upload_response, response)
240
self.push.s3.put_object.assert_called_with(
241
Bucket=self.bucket,
242
Key=self.key,
243
Body=self.bundle_mock
244
)
245
self.assertFalse(self.push.s3.create_multipart_upload.called)
246
self.assertFalse(self.push.s3.upload_part.called)
247
self.assertFalse(self.push.s3.complete_multipart_upload.called)
248
self.assertFalse(self.push.s3.abort_multipart_upload.called)
249
250
def test_upload_to_s3_with_multipart_upload(self):
251
self.args.bucket = self.bucket
252
self.args.key = self.key
253
self.bundle_mock.tell.return_value = (6 << 20)
254
self.bundle_mock.read.return_value = b'a' * (6 << 20)
255
response = self.push._upload_to_s3(self.args, self.bundle_mock)
256
self.assertDictEqual(self.upload_response, response)
257
self.assertFalse(self.push.s3.put_object.called)
258
self.push.s3.create_multipart_upload.assert_called_with(
259
Bucket=self.bucket,
260
Key=self.key
261
)
262
self.push.s3.upload_part.assert_called_with(
263
Bucket=self.bucket,
264
Key=self.key,
265
UploadId=self.upload_id,
266
PartNumber=1,
267
Body=mock.ANY
268
)
269
self.push.s3.complete_multipart_upload.assert_called_with(
270
Bucket=self.bucket,
271
Key=self.key,
272
UploadId=self.upload_id,
273
MultipartUpload={'Parts': [{'PartNumber': 1, 'ETag': self.eTag}]}
274
)
275
self.assertFalse(self.push.s3.abort_multipart_upload.called)
276
277
def test_upload_to_s3_with_multipart_upload_aborted_on_error(self):
278
self.args.bucket = self.bucket
279
self.args.key = self.key
280
self.bundle_mock.tell.return_value = (6 << 20)
281
self.bundle_mock.read.return_value = b'a' * (6 << 20)
282
self.push.s3.upload_part.side_effect = ClientError(
283
{'Error': {'Code': 'Error', 'Message': 'Error'}},
284
'UploadPart'
285
)
286
with self.assertRaises(ClientError):
287
self.push._upload_to_s3(self.args, self.bundle_mock)
288
self.assertFalse(self.push.s3.put_object.called)
289
self.push.s3.create_multipart_upload.assert_called_with(
290
Bucket=self.bucket,
291
Key=self.key
292
)
293
self.assertTrue(self.push.s3.upload_part.called)
294
self.assertFalse(self.push.s3.complete_multipart_upload.called)
295
self.push.s3.abort_multipart_upload.assert_called_with(
296
Bucket=self.bucket,
297
Key=self.key,
298
UploadId=self.upload_id
299
)
300
301
def test_register_revision(self):
302
self.args.bucket = self.bucket
303
self.args.key = self.key
304
self.args.eTag = self.eTag
305
self.args.version = self.version_id
306
self.push._register_revision(self.args)
307
self.push.codedeploy.register_application_revision.assert_called_with(
308
applicationName=self.application_name,
309
description=self.description,
310
revision=self.revision
311
)
312
313
314
if __name__ == "__main__":
315
unittest.main()
316
317