Path: blob/develop/tests/unit/customizations/codedeploy/test_push.py
2637 views
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.1#2# Licensed under the Apache License, Version 2.0 (the "License"). You3# may not use this file except in compliance with the License. A copy of4# the License is located at5#6# http://aws.amazon.com/apache2.0/7#8# or in the "license" file accompanying this file. This file is9# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF10# ANY KIND, either express or implied. See the License for the specific11# language governing permissions and limitations under the License.1213import awscli1415from argparse import Namespace16from botocore.exceptions import ClientError1718from awscli.customizations.codedeploy.push import Push19from awscli.testutils import mock, unittest20from awscli.compat import StringIO, ZIP_COMPRESSION_MODE212223class TestPush(unittest.TestCase):24def setUp(self):25self.application_name = 'MyApp'26self.description = 'MyApp revision'27self.source = '/tmp'28self.appspec = 'appspec.yml'29self.appspec_path = '{0}/{1}'.format(self.source, self.appspec)30self.bucket = 'foo'31self.key = 'bar/baz.zip'32self.s3_location = 's3://' + self.bucket + '/' + self.key33self.eTag = '"1a2b3cd45e"'34self.version_id = '12341234-1234-1234-1234-123412341234'35self.upload_id = 'upload_id'36self.region = 'us-east-1'37self.endpoint_url = 'https://codedeploy.aws.amazon.com'3839self.args = Namespace()40self.args.application_name = self.application_name41self.args.s3_location = self.s3_location42self.args.ignore_hidden_files = False43self.args.no_ignore_hidden_files = False44self.args.description = self.description45self.args.source = self.source4647self.globals = Namespace()48self.globals.region = self.region49self.globals.endpoint_url = self.endpoint_url50self.globals.verify_ssl = False5152self.upload_response = {53'ETag': self.eTag,54'VersionId': self.version_id55}56self.revision = {57'revisionType': 'S3',58's3Location': {59'bucket': self.bucket,60'key': self.key,61'bundleType': 'zip',62'eTag': self.eTag,63'version': self.version_id64}65}6667self.bundle_mock = mock.MagicMock()68self.bundle_mock.tell.return_value = (5 << 20)69self.bundle_mock.read.return_value = b'a' * (5 << 20)70self.bundle_mock.__enter__.return_value = self.bundle_mock71self.bundle_mock.__exit__.return_value = None7273self.zipfile_mock = mock.MagicMock()74self.zipfile_mock.write.return_value = None75self.zipfile_mock.close.return_value = None76self.zipfile_mock.__enter__.return_value = self.zipfile_mock77self.zipfile_mock.__exit__.return_value = None7879self.session = mock.MagicMock()8081self.push = Push(self.session)82self.push.s3 = mock.MagicMock()83self.push.s3.put_object.return_value = self.upload_response84self.push.s3.create_multipart_upload.return_value = {85'UploadId': self.upload_id86}87self.push.s3.upload_part.return_value = {88'ETag': self.eTag89}90self.push.s3.complete_multipart_upload\91.return_value = self.upload_response92self.push.codedeploy = mock.MagicMock()9394def test_run_main_throws_on_invalid_args(self):95self.push._validate_args = mock.MagicMock()96self.push._validate_args.side_effect = RuntimeError()97with self.assertRaises(RuntimeError):98self.push._run_main(self.args, self.globals)99100def test_run_main_creates_clients(self):101self.push._validate_args = mock.MagicMock()102self.push._push = mock.MagicMock()103self.push._run_main(self.args, self.globals)104self.session.create_client.assert_has_calls([105mock.call(106'codedeploy',107region_name=self.region,108endpoint_url=self.endpoint_url,109verify=self.globals.verify_ssl110),111mock.call('s3', region_name=self.region)112])113114def test_run_main_calls_push(self):115self.push._validate_args = mock.MagicMock()116self.push._push = mock.MagicMock()117self.push._run_main(self.args, self.globals)118self.push._push.assert_called_with(self.args)119120@mock.patch.object(121awscli.customizations.codedeploy.push,122'validate_s3_location'123)124def test_validate_args_throws_on_invalid_s3_url(125self, validate_s3_location126):127self.args.s3_location = 's3:/foo/bar/baz'128validate_s3_location.side_effect = RuntimeError()129with self.assertRaises(RuntimeError):130self.push._validate_args(self.args)131132def test_validate_args_throws_on_ignore_and_no_ignore_hidden_files(self):133self.args.ignore_hidden_files = True134self.args.no_ignore_hidden_files = True135with self.assertRaises(RuntimeError):136self.push._validate_args(self.args)137138def test_validate_args_default_description(self):139self.args.description = None140self.push._validate_args(self.args)141self.assertRegex(142self.args.description,143'Uploaded by AWS CLI .* UTC'144)145146def test_push_throws_on_upload_to_s3_error(self):147self.args.bucket = self.bucket148self.args.key = self.key149self.push._compress = mock.MagicMock(return_value=self.bundle_mock)150self.push._upload_to_s3 = mock.MagicMock()151self.push._upload_to_s3.side_effect = RuntimeError()152with self.assertRaises(RuntimeError):153self.push._push(self.args)154155def test_push_strips_quotes_from_etag(self):156self.args.bucket = self.bucket157self.args.key = self.key158self.push._compress = mock.MagicMock(return_value=self.bundle_mock)159self.push._upload_to_s3 = mock.MagicMock(return_value=self.upload_response)160self.push._register_revision = mock.MagicMock()161self.push._push(self.args)162self.push._register_revision.assert_called_with(self.args)163self.assertEqual(str(self.args.eTag), self.upload_response['ETag'].replace('"',""))164165@mock.patch('sys.stdout', new_callable=StringIO)166def test_push_output_message(self, stdout_mock):167self.args.bucket = self.bucket168self.args.key = self.key169self.push._compress = mock.MagicMock(return_value=self.bundle_mock)170self.push._upload_to_s3 = mock.MagicMock(return_value=self.upload_response)171self.push._register_revision = mock.MagicMock()172self.push._push(self.args)173output = stdout_mock.getvalue().strip()174expected_revision_output = (175'--s3-location bucket={0},key={1},'176'bundleType=zip,eTag={2},version={3}'.format(177self.bucket,178self.key,179self.eTag.replace('"',""),180self.version_id)181)182expected_output = (183'To deploy with this revision, run:\n'184'aws deploy create-deployment '185'--application-name {0} {1} '186'--deployment-group-name <deployment-group-name> '187'--deployment-config-name <deployment-config-name> '188'--description <description>'.format(189self.application_name,190expected_revision_output191)192)193self.assertEqual(expected_output, output)194195@mock.patch('zipfile.ZipFile')196@mock.patch('tempfile.TemporaryFile')197@mock.patch('os.path')198@mock.patch('os.walk')199def test_compress_throws_when_no_appspec(self, walk, path, tf, zf):200walk.return_value = [(self.source, [], ['noappspec.yml'])]201noappsec_path = self.source + '/noappspec.yml'202path.join.return_value = noappsec_path203path.sep = '/'204path.abspath.side_effect = [self.source, noappsec_path]205tf.return_value = self.bundle_mock206zf.return_value = self.zipfile_mock207with self.assertRaises(RuntimeError):208with self.push._compress(209self.args.source,210self.args.ignore_hidden_files):211pass212213@mock.patch('zipfile.ZipFile')214@mock.patch('tempfile.TemporaryFile')215@mock.patch('os.path')216@mock.patch('os.walk')217def test_compress_writes_to_zip_file(self, walk, path, tf, zf):218walk.return_value = [(self.source, [], [self.appspec])]219path.join.return_value = self.appspec_path220path.sep = '/'221path.abspath.side_effect = [self.source, self.appspec_path]222tf.return_value = self.bundle_mock223zf.return_value = self.zipfile_mock224with self.push._compress(225self.args.source,226self.args.ignore_hidden_files):227zf.assert_called_with(mock.ANY, 'w', allowZip64=True)228zf().write.assert_called_with(229'/tmp/appspec.yml',230self.appspec,231ZIP_COMPRESSION_MODE232)233234def test_upload_to_s3_with_put_object(self):235self.args.bucket = self.bucket236self.args.key = self.key237response = self.push._upload_to_s3(self.args, self.bundle_mock)238self.assertDictEqual(self.upload_response, response)239self.push.s3.put_object.assert_called_with(240Bucket=self.bucket,241Key=self.key,242Body=self.bundle_mock243)244self.assertFalse(self.push.s3.create_multipart_upload.called)245self.assertFalse(self.push.s3.upload_part.called)246self.assertFalse(self.push.s3.complete_multipart_upload.called)247self.assertFalse(self.push.s3.abort_multipart_upload.called)248249def test_upload_to_s3_with_multipart_upload(self):250self.args.bucket = self.bucket251self.args.key = self.key252self.bundle_mock.tell.return_value = (6 << 20)253self.bundle_mock.read.return_value = b'a' * (6 << 20)254response = self.push._upload_to_s3(self.args, self.bundle_mock)255self.assertDictEqual(self.upload_response, response)256self.assertFalse(self.push.s3.put_object.called)257self.push.s3.create_multipart_upload.assert_called_with(258Bucket=self.bucket,259Key=self.key260)261self.push.s3.upload_part.assert_called_with(262Bucket=self.bucket,263Key=self.key,264UploadId=self.upload_id,265PartNumber=1,266Body=mock.ANY267)268self.push.s3.complete_multipart_upload.assert_called_with(269Bucket=self.bucket,270Key=self.key,271UploadId=self.upload_id,272MultipartUpload={'Parts': [{'PartNumber': 1, 'ETag': self.eTag}]}273)274self.assertFalse(self.push.s3.abort_multipart_upload.called)275276def test_upload_to_s3_with_multipart_upload_aborted_on_error(self):277self.args.bucket = self.bucket278self.args.key = self.key279self.bundle_mock.tell.return_value = (6 << 20)280self.bundle_mock.read.return_value = b'a' * (6 << 20)281self.push.s3.upload_part.side_effect = ClientError(282{'Error': {'Code': 'Error', 'Message': 'Error'}},283'UploadPart'284)285with self.assertRaises(ClientError):286self.push._upload_to_s3(self.args, self.bundle_mock)287self.assertFalse(self.push.s3.put_object.called)288self.push.s3.create_multipart_upload.assert_called_with(289Bucket=self.bucket,290Key=self.key291)292self.assertTrue(self.push.s3.upload_part.called)293self.assertFalse(self.push.s3.complete_multipart_upload.called)294self.push.s3.abort_multipart_upload.assert_called_with(295Bucket=self.bucket,296Key=self.key,297UploadId=self.upload_id298)299300def test_register_revision(self):301self.args.bucket = self.bucket302self.args.key = self.key303self.args.eTag = self.eTag304self.args.version = self.version_id305self.push._register_revision(self.args)306self.push.codedeploy.register_application_revision.assert_called_with(307applicationName=self.application_name,308description=self.description,309revision=self.revision310)311312313if __name__ == "__main__":314unittest.main()315316317