コード例 #1
0
    def test_should_not_raise_exceptions_when_executing_all_bootstrap_steps(
            self):
        senza_mock = SenzaWrapper(CONFIG)
        senza_create_mock = senza_mock.create_stack = MagicMock()
        senza_switch_mock = senza_mock.switch_traffic = MagicMock(
            return_value=True)
        os.listdir = MagicMock(side_effect=self.__side_effect_config_list)
        controller = ClusterBootstrapController(
            base_url=BASE_URL,
            stack_name=STACK_NAME,
            sharding_level=SHARDING_LEVEL,
            replication_factor=REPLICATION_FACTOR,
            image_version=IMAGE_VERSION,
            oauth_token=OAUTH_TOKEN,
            senza_wrapper=senza_mock)
        controller.set_retry_count(1)
        controller.set_retry_wait(0)

        urlopen_mock = MagicMock(
            side_effect=self.__side_effect_return_cluster_state)
        urllib.request.urlopen = urlopen_mock

        controller.bootstrap_cluster()

        senza_create_mock.assert_called_once_with(STACK_NAME,
                                                  INITIAL_BOOTSTRAP_VERSION,
                                                  IMAGE_VERSION)
        senza_switch_mock.assert_called_once_with(STACK_NAME,
                                                  INITIAL_BOOTSTRAP_VERSION,
                                                  100)
コード例 #2
0
    def test_should_write_warning_when_waiting_for_cluster_to_be_ready_and_timeout_on_cluster_state(
            self, log):
        senza_mock = SenzaWrapper(CONFIG)
        controller = ClusterBootstrapController(
            base_url=BASE_URL,
            stack_name=STACK_NAME,
            sharding_level=SHARDING_LEVEL,
            replication_factor=REPLICATION_FACTOR,
            image_version=IMAGE_VERSION,
            oauth_token=OAUTH_TOKEN,
            senza_wrapper=senza_mock)
        controller.set_retry_count(1)
        controller.set_retry_wait(0)

        urlopen_mock = MagicMock(side_effect=self.__side_effect_timeout)
        urllib.request.urlopen = urlopen_mock

        controller.wait_for_cluster_to_be_ready()

        log.check(
            ('root', 'WARNING',
             'Clould not get cluster state: Failed sending request to Solr '
             '[' + BASE_URL +
             '/admin/collections?action=CLUSTERSTATUS&wt=json]: '
             'HTTP Error 504: None'),
            ('root', 'WARNING', 'Cluster is not ready, yet, retrying ...'),
            ('root', 'WARNING',
             'Clould not get cluster state: Failed sending request to Solr '
             '[' + BASE_URL +
             '/admin/collections?action=CLUSTERSTATUS&wt=json]: '
             'HTTP Error 504: None'),
            ('root', 'WARNING', 'Cluster is not ready, yet, retrying ...'),
            ('root', 'WARNING', 'Cluster did not become ready in time.'))
コード例 #3
0
    def test_should_write_warning_when_waiting_for_cluster_to_be_ready_and_not_enough_nodes_are_available(
            self, log):
        senza_mock = SenzaWrapper(CONFIG)
        sharding_level = SHARDING_LEVEL + 1
        controller = ClusterBootstrapController(
            base_url=BASE_URL,
            stack_name=STACK_NAME,
            sharding_level=sharding_level,
            replication_factor=REPLICATION_FACTOR,
            image_version=IMAGE_VERSION,
            oauth_token=OAUTH_TOKEN,
            senza_wrapper=senza_mock)
        controller.set_retry_count(1)
        controller.set_retry_wait(0)

        urlopen_mock = MagicMock(
            side_effect=self.__side_effect_return_cluster_state)
        urllib.request.urlopen = urlopen_mock

        controller.wait_for_cluster_to_be_ready()

        log.check(
            ('root', 'WARNING', 'Cluster is not ready, yet, retrying ...'),
            ('root', 'WARNING', 'Cluster is not ready, yet, retrying ...'),
            ('root', 'WARNING', 'Cluster did not become ready in time.'))
コード例 #4
0
    def test_should_create_stack_with_additional_senza_parameter(self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'
        timeout = 1

        senza_wrapper = SenzaWrapper(TEST_CONFIG)
        senza_wrapper.set_stack_creation_retry_timeout(timeout)

        senza_wrapper.add_parameter("test-key", "test-value")

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events = [
            {
                'stack_name': stack_name,
                'version': stack_version,
                'resource_type': 'CloudFormation::Stack',
                'event_time': '0',
                'ResourceStatus': 'CREATE_COMPLETE'
            }
        ]
        events_mock = MagicMock(return_value=bytes(json.dumps(events), encoding='utf-8'))
        subprocess.check_output = events_mock

        senza_wrapper.create_stack(stack_name, stack_version, image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback', TEST_CONFIG, stack_version,
            'ImageVersion=' + image_version, 'test-key=test-value'
        ])
        events_mock.assert_called_once_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json', stack_name, stack_version
        ])
コード例 #5
0
 def setUp(self):
     senza_wrapper = SenzaWrapper(CONFIG)
     self.__controller = ClusterBootstrapController(
         base_url=BASE_URL,
         stack_name=STACK_NAME,
         sharding_level=SHARDING_LEVEL,
         replication_factor=REPLICATION_FACTOR,
         image_version=IMAGE_VERSION,
         oauth_token=OAUTH_TOKEN,
         senza_wrapper=senza_wrapper)
     self.__controller.set_retry_count(1)
     self.__controller.set_retry_wait(0)
コード例 #6
0
    def test_should_raise_exception_if_stack_creation_lasts_longer_than_timeout(
            self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'
        timeout = 0

        senza_wrapper = SenzaWrapper(TEST_CONFIG)
        senza_wrapper.set_stack_creation_retry_timeout(timeout)

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events_mock = MagicMock()
        subprocess.check_output = events_mock

        with self.assertRaisesRegex(
                Exception, 'Timeout while creating new stack version'):
            senza_wrapper.create_stack(stack_name, stack_version,
                                       image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback',
            TEST_CONFIG, stack_version, 'ImageVersion=' + image_version
        ])

        events_mock.assert_not_called()
コード例 #7
0
    def test_should_raise_exception_if_stack_creation_lasts_longer_than_timeout(self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'
        timeout = 0

        senza_wrapper = SenzaWrapper(TEST_CONFIG)
        senza_wrapper.set_stack_creation_retry_timeout(timeout)

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events_mock = MagicMock()
        subprocess.check_output = events_mock

        with self.assertRaisesRegex(Exception, 'Timeout while creating new stack version'):
            senza_wrapper.create_stack(stack_name, stack_version, image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback', TEST_CONFIG, stack_version,
            'ImageVersion=' + image_version
        ])

        events_mock.assert_not_called()
コード例 #8
0
    def test_should_not_raise_any_exception_when_creating_a_new_cluster(self):
        senza_mock = SenzaWrapper(CONFIG)
        senza_create_mock = senza_mock.create_stack = MagicMock()

        controller = ClusterBootstrapController(
            base_url=BASE_URL,
            stack_name=STACK_NAME,
            sharding_level=SHARDING_LEVEL,
            replication_factor=REPLICATION_FACTOR,
            image_version=IMAGE_VERSION,
            oauth_token=OAUTH_TOKEN,
            senza_wrapper=senza_mock)
        controller.create_cluster()

        senza_create_mock.assert_called_once_with(STACK_NAME,
                                                  INITIAL_BOOTSTRAP_VERSION,
                                                  IMAGE_VERSION)
コード例 #9
0
    def test_should_create_stack_with_additional_senza_parameter(self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'
        timeout = 1

        senza_wrapper = SenzaWrapper(TEST_CONFIG)
        senza_wrapper.set_stack_creation_retry_timeout(timeout)

        senza_wrapper.add_parameter("test-key", "test-value")

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events = [{
            'stack_name': stack_name,
            'version': stack_version,
            'resource_type': 'CloudFormation::Stack',
            'event_time': '0',
            'ResourceStatus': 'CREATE_COMPLETE'
        }]
        events_mock = MagicMock(
            return_value=bytes(json.dumps(events), encoding='utf-8'))
        subprocess.check_output = events_mock

        senza_wrapper.create_stack(stack_name, stack_version, image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback',
            TEST_CONFIG, stack_version, 'ImageVersion=' + image_version,
            'test-key=test-value'
        ])
        events_mock.assert_called_once_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json',
            stack_name, stack_version
        ])
コード例 #10
0
 def setup_method(self, method):
     self.__senza_wrapper = SenzaWrapper(TEST_CONFIG)
     self.__senza_wrapper.set_retry_wait(0)
     self.__senza_wrapper.set_stack_creation_retry_timeout(1)
     self.__senza_wrapper.set_stack_creation_retry_wait(0)
コード例 #11
0
class TestSenzaWrapper(TestCase):

    __senza_wrapper = None

    def setup_method(self, method):
        self.__senza_wrapper = SenzaWrapper(TEST_CONFIG)
        self.__senza_wrapper.set_retry_wait(0)
        self.__senza_wrapper.set_stack_creation_retry_timeout(1)
        self.__senza_wrapper.set_stack_creation_retry_wait(0)

    def test_should_return_success_when_deleting_stack_version_with_one_retry(
            self):
        delete_mock = MagicMock(return_value=0)

        list_output_side_effects = [
            bytes(json.dumps({'output': 'test'}), encoding='utf-8'), None
        ]
        list_mock = MagicMock(side_effect=list_output_side_effects)

        subprocess.call = delete_mock
        subprocess.check_output = list_mock

        self.__senza_wrapper.delete_stack_version('test', 'test')

        delete_mock.assert_called_once_with(
            ['senza', 'delete', '--region', 'eu-west-1', 'test', 'test'])
        list_mock.assert_called_with([
            'senza', 'list', '--region', 'eu-west-1', '--output', 'json',
            'test', 'test'
        ])
        self.assertEquals(2, len(list_mock.call_args_list),
                          'Unexpected number of senza list calls')

    def test_should_return_all_instances_of_one_stack_version(self):
        instances = [{
            'stack_name': 'test-stack',
            'stack_version': 'test-version',
            'private_ip': '0.0.0.0'
        }, {
            'stack_name': 'test-stack',
            'stack_version': 'test-version',
            'private_ip': '1.1.1.1'
        }]
        instances_mock = MagicMock(
            return_value=bytes(json.dumps(instances), encoding='utf-8'))
        subprocess.check_output = instances_mock

        result = self.__senza_wrapper.get_stack_instances(
            'test-stack', 'test-version')
        self.assertListEqual(['0.0.0.0', '1.1.1.1'], result,
                             "Getting stack instances failed.")
        instances_mock.assert_called_once_with([
            'senza', 'instances', '--region', 'eu-west-1', '--output', 'json',
            'test-stack', 'test-version'
        ])

    def test_should_return_active_stack_version(self):
        active_version = 'active'
        passive_version = 'passive'
        stack_versions = [{
            'identifier': 'test-' + passive_version,
            'stack_name': 'test',
            'version': passive_version,
            'weight%': NO_TRAFFIC
        }, {
            'identifier': 'test-' + active_version,
            'stack_name': 'test',
            'version': active_version,
            'weight%': ALL_TRAFFIC
        }]
        versions_mock = MagicMock(
            return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_active_stack_version('test')
        self.assertEquals(active_version, result,
                          'Result is not the active stack version')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            'test'
        ])

    def test_should_return_active_stack_version_when_only_one_version_exists(
            self):
        active_version = 'active'
        stack_versions = [{
            'identifier': 'test-' + active_version,
            'stack_name': 'test',
            'version': active_version,
            'weight%': ALL_TRAFFIC
        }]
        versions_mock = MagicMock(
            return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_active_stack_version('test')
        self.assertEquals(active_version, result,
                          'Result is not the active stack version')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            'test'
        ])

    def test_should_return_none_if_no_active_version_exists(self):
        passive_version = 'passive'
        stack_versions = [{
            'identifier': 'test-' + passive_version,
            'stack_name': 'test',
            'version': passive_version,
            'weight%': 0.0
        }]
        versions_mock = MagicMock(
            return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_active_stack_version('test')
        self.assertIsNone(
            result, 'Active stack version returned although there is none')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            'test'
        ])

    def test_should_return_passive_stack_version(self):
        active_version = 'active'
        passive_version = 'passive'
        stack_versions = [{
            'identifier': 'test-' + passive_version,
            'stack_name': 'test',
            'version': passive_version,
            'weight%': NO_TRAFFIC
        }, {
            'identifier': 'test-' + active_version,
            'stack_name': 'test',
            'version': active_version,
            'weight%': ALL_TRAFFIC
        }]
        versions_mock = MagicMock(
            return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_passive_stack_version('test')
        self.assertEquals(passive_version, result,
                          'Result is not the passive stack version')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            'test'
        ])

    def test_should_return_passive_stack_version_when_only_one_version_exists(
            self):
        passive_version = 'passive'
        stack_versions = [{
            'identifier': 'test-' + passive_version,
            'stack_name': 'test',
            'version': passive_version,
            'weight%': NO_TRAFFIC
        }]
        versions_mock = MagicMock(
            return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_passive_stack_version('test')
        self.assertEquals(passive_version, result,
                          'Result is not the passive stack version')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            'test'
        ])

    def test_should_return_none_if_no_passive_version_exists(self):
        active_version = 'active'
        stack_versions = [{
            'identifier': 'test-' + active_version,
            'stack_name': 'test',
            'version': active_version,
            'weight%': ALL_TRAFFIC
        }]
        versions_mock = MagicMock(
            return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_passive_stack_version('test')
        self.assertIsNone(
            result, 'Passive stack version returned although there is none')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            'test'
        ])

    def test_should_return_all_events_for_stack_version(self):
        stack_name = 'test-stack'
        stack_version_name = 'test-version'

        events = [{
            'stack_name': stack_name,
            'version': stack_version_name,
            'resource_type': 'test-event1'
        }, {
            'stack_name': stack_name,
            'version': stack_version_name,
            'resource_type': 'test-event2'
        }]
        events_mock = MagicMock(
            return_value=bytes(json.dumps(events), encoding='utf-8'))
        subprocess.check_output = events_mock

        result = self.__senza_wrapper.get_events(stack_name,
                                                 stack_version_name)
        self.assertListEqual(events, result,
                             'Received unexpected list of events')
        events_mock.assert_called_once_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json',
            stack_name, stack_version_name
        ])

    def test_should_switch_traffic_between_versions(self):
        stack_name = 'test'
        active_version = 'active'
        passive_version = 'passive'
        switch_result = [{
            'identifier': stack_name + '-' + passive_version,
            'stack_name': stack_name,
            'version': passive_version,
            'old_weight%': NO_TRAFFIC,
            'new_weight%': ALL_TRAFFIC
        }, {
            'identifier': stack_name + '-' + active_version,
            'stack_name': stack_name,
            'version': active_version,
            'old_weight%': ALL_TRAFFIC,
            'new_weight%': NO_TRAFFIC
        }]
        switch_mock = MagicMock(
            return_value=bytes(json.dumps(switch_result), encoding='utf-8'))
        subprocess.check_output = switch_mock

        self.__senza_wrapper.switch_traffic(stack_name, passive_version,
                                            int(ALL_TRAFFIC))
        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            stack_name, passive_version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_raise_exception_if_new_weight_equals_old_weight_after_switching_traffic_between_versions(
            self):
        stack_name = 'test'
        active_version = 'active'
        passive_version = 'passive'
        switch_result = [{
            'identifier': stack_name + '-' + passive_version,
            'stack_name': stack_name,
            'version': passive_version,
            'old_weight%': NO_TRAFFIC,
            'new_weight%': NO_TRAFFIC
        }, {
            'identifier': stack_name + '-' + active_version,
            'stack_name': stack_name,
            'version': active_version,
            'old_weight%': ALL_TRAFFIC,
            'new_weight%': ALL_TRAFFIC
        }]
        switch_mock = MagicMock(
            return_value=bytes(json.dumps(switch_result), encoding='utf-8'))
        subprocess.check_output = switch_mock

        with self.assertRaisesRegex(
                Exception,
                'Switching of \[{}\]% traffic to stack \[{}\] version \[{}\] failed'
                .format('100', stack_name, passive_version)):
            self.__senza_wrapper.switch_traffic(stack_name, passive_version,
                                                int(ALL_TRAFFIC))
        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            stack_name, passive_version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_raise_exception_if_weight_was_already_new_weight_before_switching_traffic_between_versions(
            self):
        stack_name = 'test'
        active_version = 'active'
        passive_version = 'passive'
        switch_result = [{
            'identifier': stack_name + '-' + passive_version,
            'stack_name': stack_name,
            'version': passive_version,
            'old_weight%': ALL_TRAFFIC,
            'new_weight%': ALL_TRAFFIC
        }, {
            'identifier': stack_name + '-' + active_version,
            'stack_name': stack_name,
            'version': active_version,
            'old_weight%': NO_TRAFFIC,
            'new_weight%': NO_TRAFFIC
        }]
        switch_mock = MagicMock(
            return_value=bytes(json.dumps(switch_result), encoding='utf-8'))
        subprocess.check_output = switch_mock

        with self.assertRaisesRegex(
                Exception,
                'Traffic weight did not change, traffic for stack \[{}\] version \[{}\] '
                'is still at \[{}\]%'.format(stack_name, passive_version,
                                             '100')):
            self.__senza_wrapper.switch_traffic(stack_name, passive_version,
                                                int(ALL_TRAFFIC))
        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            stack_name, passive_version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_raise_exception_if_new_weight_is_unexpected_after_switching_traffic_between_versions(
            self):
        stack_name = 'test'
        active_version = 'active'
        passive_version = 'passive'
        switch_result = [{
            'identifier': stack_name + '-' + passive_version,
            'stack_name': stack_name,
            'version': passive_version,
            'old_weight%': NO_TRAFFIC,
            'new_weight%': ALL_TRAFFIC - 1
        }, {
            'identifier': stack_name + '-' + active_version,
            'stack_name': stack_name,
            'version': active_version,
            'old_weight%': ALL_TRAFFIC,
            'new_weight%': NO_TRAFFIC + 1
        }]
        switch_mock = MagicMock(
            return_value=bytes(json.dumps(switch_result), encoding='utf-8'))
        subprocess.check_output = switch_mock

        with self.assertRaisesRegex(
                Exception,
                'Switching of \[{}\]% traffic to stack \[{}\] version \[{}\] failed'
                .format('100', stack_name, passive_version)):
            self.__senza_wrapper.switch_traffic(stack_name, passive_version,
                                                int(ALL_TRAFFIC))
        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            stack_name, passive_version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_succeed_if_switching_traffic_between_versions_returns_no_output(
            self):
        stack_name = 'test'
        passive_version = 'passive'
        switch_mock = MagicMock(return_value=None)
        subprocess.check_output = switch_mock

        self.__senza_wrapper.switch_traffic(stack_name, passive_version,
                                            int(ALL_TRAFFIC))

        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            stack_name, passive_version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_raise_exception_if_switching_traffic_between_versions_returns_error_code(
            self):
        stack_name = 'test'
        version = 'unknown'
        switch_mock = MagicMock(side_effect=subprocess.CalledProcessError(
            cmd='test', returncode=2))
        subprocess.check_output = switch_mock

        with self.assertRaises(subprocess.CalledProcessError):
            self.__senza_wrapper.switch_traffic(stack_name, version,
                                                int(ALL_TRAFFIC))

        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            stack_name, version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_raise_exception_if_switching_traffic_between_versions_returns_no_list(
            self):
        stack_name = 'test'
        version = 'unknown'
        switch_result = 'test output'
        switch_mock = MagicMock(
            return_value=bytes(json.dumps(switch_result), encoding='utf-8'))
        subprocess.check_output = switch_mock

        with self.assertRaisesRegex(
                Exception, 'Unexpected output: \[{}\]'.format(switch_result)):
            self.__senza_wrapper.switch_traffic(stack_name, version,
                                                int(ALL_TRAFFIC))

        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json',
            stack_name, version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_create_new_stack_version(self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events = [{
            'stack_name': stack_name,
            'version': stack_version,
            'resource_type': 'CloudFormation::Stack',
            'event_time': '0',
            'ResourceStatus': 'CREATE_COMPLETE'
        }]
        events_mock = MagicMock(
            return_value=bytes(json.dumps(events), encoding='utf-8'))
        subprocess.check_output = events_mock

        self.__senza_wrapper.create_stack(stack_name, stack_version,
                                          image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback',
            TEST_CONFIG, stack_version, 'ImageVersion=' + image_version
        ])
        events_mock.assert_called_once_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json',
            stack_name, stack_version
        ])

    def test_should_wait_until_success_event_comes_when_creating_a_new_stack_version(
            self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        success_event = {
            'stack_name': stack_name,
            'version': stack_version,
            'resource_type': 'CloudFormation::Stack',
            'event_time': '1',
            'ResourceStatus': 'CREATE_COMPLETE'
        }

        other_event = {
            'stack_name': stack_name,
            'version': stack_version,
            'resource_type': 'Other',
            'event_time': '0',
            'ResourceStatus': 'Other'
        }

        side_effect_events = [
            bytes(json.dumps([other_event]), encoding='utf-8'),
            bytes(json.dumps([other_event, success_event]), encoding='utf-8'),
        ]
        events_mock = MagicMock(side_effect=side_effect_events)
        subprocess.check_output = events_mock

        self.__senza_wrapper.create_stack(stack_name, stack_version,
                                          image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback',
            TEST_CONFIG, stack_version, 'ImageVersion=' + image_version
        ])
        events_mock.assert_called_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json',
            stack_name, stack_version
        ])
        self.assertEquals(2, len(events_mock.call_args_list),
                          'Unexpected number of calls for senza events')

    def test_should_raise_exception_if_senza_execution_failed_on_creating_a_new_stack_version(
            self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'
        error_code = 1

        create_mock = MagicMock(return_value=error_code)
        subprocess.call = create_mock

        with self.assertRaisesRegex(
                Exception,
                'Failed to create new cluster with error code \[{}\]'.format(
                    str(error_code))):

            self.__senza_wrapper.create_stack(stack_name, stack_version,
                                              image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback',
            TEST_CONFIG, stack_version, 'ImageVersion=' + image_version
        ])

    def test_should_raise_exception_if_creation_of_new_stack_version_failed(
            self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events = [{
            'stack_name': stack_name,
            'version': stack_version,
            'resource_type': 'CloudFormation::Stack',
            'event_time': '0',
            'ResourceStatus': 'CREATE_FAILED'
        }]
        events_mock = MagicMock(
            return_value=bytes(json.dumps(events), encoding='utf-8'))
        subprocess.check_output = events_mock

        with self.assertRaisesRegex(
                Exception,
                'Creation of stack \[{}\] version \[{}\] with image version \[{}\] '
                'failed'.format(stack_name, stack_version, image_version)):

            self.__senza_wrapper.create_stack(stack_name, stack_version,
                                              image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback',
            TEST_CONFIG, stack_version, 'ImageVersion=' + image_version
        ])
        events_mock.assert_called_once_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json',
            stack_name, stack_version
        ])

    def test_should_raise_exception_if_stack_creation_lasts_longer_than_timeout(
            self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'
        timeout = 0

        senza_wrapper = SenzaWrapper(TEST_CONFIG)
        senza_wrapper.set_stack_creation_retry_timeout(timeout)

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events_mock = MagicMock()
        subprocess.check_output = events_mock

        with self.assertRaisesRegex(
                Exception, 'Timeout while creating new stack version'):
            senza_wrapper.create_stack(stack_name, stack_version,
                                       image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback',
            TEST_CONFIG, stack_version, 'ImageVersion=' + image_version
        ])

        events_mock.assert_not_called()

    def test_should_create_stack_with_additional_senza_parameter(self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'
        timeout = 1

        senza_wrapper = SenzaWrapper(TEST_CONFIG)
        senza_wrapper.set_stack_creation_retry_timeout(timeout)

        senza_wrapper.add_parameter("test-key", "test-value")

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events = [{
            'stack_name': stack_name,
            'version': stack_version,
            'resource_type': 'CloudFormation::Stack',
            'event_time': '0',
            'ResourceStatus': 'CREATE_COMPLETE'
        }]
        events_mock = MagicMock(
            return_value=bytes(json.dumps(events), encoding='utf-8'))
        subprocess.check_output = events_mock

        senza_wrapper.create_stack(stack_name, stack_version, image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback',
            TEST_CONFIG, stack_version, 'ImageVersion=' + image_version,
            'test-key=test-value'
        ])
        events_mock.assert_called_once_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json',
            stack_name, stack_version
        ])
コード例 #12
0
def solrcloud_cli(cli_args):
    parser = build_args_parser()
    args = parser.parse_args(cli_args)

    if not args.config:
        args.config = os.path.expanduser(DEFAULT_CONF_FILE)

    if not os.path.exists(args.config):
        print('Configuration file does not exist:', args.config)
        parser.print_usage()
        return

    with open(args.config, 'rb') as fd:
        settings = yaml.load(fd)

    senza_wrapper = SenzaWrapper(args.senza_configuration)
    if args.region:
        senza_wrapper.set_region(args.region)

    for key, value in settings.items():
        senza_wrapper.add_parameter(key, value)

    if args.command in ['bootstrap']:
        controller = ClusterBootstrapController(
            base_url=settings['SolrBaseUrl'],
            stack_name=settings['ApplicationId'],
            sharding_level=args.sharding_level,
            replication_factor=args.replication_level,
            image_version=args.image_version,
            oauth_token=args.token,
            senza_wrapper=senza_wrapper)
    elif args.command in [
            'deploy', 'create-new-cluster', 'delete-old-cluster',
            'add-new-nodes', 'delete-old-nodes', 'switch'
    ]:
        controller = ClusterDeploymentController(
            base_url=settings['SolrBaseUrl'],
            stack_name=settings['ApplicationId'],
            image_version=args.image_version,
            oauth_token=args.token,
            senza_wrapper=senza_wrapper)
    elif args.command in ['delete']:
        controller = ClusterDeleteController(
            base_url=settings['SolrBaseUrl'],
            stack_name=settings['ApplicationId'],
            oauth_token=args.token,
            senza_wrapper=senza_wrapper)
    else:
        print('Unknown command:', args.command)
        parser.print_usage()
        return

    if args.command == 'bootstrap':
        controller.bootstrap_cluster()
    elif args.command == 'deploy':
        controller.deploy_new_version()
    elif args.command == 'delete':
        controller.delete_cluster()
    elif args.command == 'create-new-cluster':
        controller.create_cluster()
    elif args.command == 'delete-old-cluster':
        controller.delete_cluster()
    elif args.command == 'add-new-nodes':
        controller.add_new_nodes_to_cluster()
    elif args.command == 'delete-old-nodes':
        controller.delete_old_nodes_from_cluster()
    elif args.command == 'switch':
        controller.switch_traffic()
コード例 #13
0
 def setup_method(self, method):
     self.__senza_wrapper = SenzaWrapper(TEST_CONFIG)
     self.__senza_wrapper.set_retry_wait(0)
     self.__senza_wrapper.set_stack_creation_retry_timeout(1)
     self.__senza_wrapper.set_stack_creation_retry_wait(0)
コード例 #14
0
class TestSenzaWrapper(TestCase):

    __senza_wrapper = None

    def setup_method(self, method):
        self.__senza_wrapper = SenzaWrapper(TEST_CONFIG)
        self.__senza_wrapper.set_retry_wait(0)
        self.__senza_wrapper.set_stack_creation_retry_timeout(1)
        self.__senza_wrapper.set_stack_creation_retry_wait(0)

    def test_should_return_success_when_deleting_stack_version_with_one_retry(self):
        delete_mock = MagicMock(return_value=0)

        list_output_side_effects = [
            bytes(json.dumps({'output': 'test'}), encoding='utf-8'),
            None
        ]
        list_mock = MagicMock(side_effect=list_output_side_effects)

        subprocess.call = delete_mock
        subprocess.check_output = list_mock

        self.__senza_wrapper.delete_stack_version('test', 'test')

        delete_mock.assert_called_once_with(['senza', 'delete', '--region', 'eu-west-1', 'test', 'test'])
        list_mock.assert_called_with(['senza', 'list', '--region', 'eu-west-1', '--output', 'json', 'test', 'test'])
        self.assertEquals(2, len(list_mock.call_args_list), 'Unexpected number of senza list calls')

    def test_should_return_all_instances_of_one_stack_version(self):
        instances = [
            {'stack_name': 'test-stack', 'stack_version': 'test-version', 'private_ip': '0.0.0.0'},
            {'stack_name': 'test-stack', 'stack_version': 'test-version', 'private_ip': '1.1.1.1'}
        ]
        instances_mock = MagicMock(return_value=bytes(json.dumps(instances), encoding='utf-8'))
        subprocess.check_output = instances_mock

        result = self.__senza_wrapper.get_stack_instances('test-stack', 'test-version')
        self.assertListEqual(['0.0.0.0', '1.1.1.1'], result, "Getting stack instances failed.")
        instances_mock.assert_called_once_with([
            'senza', 'instances', '--region', 'eu-west-1', '--output', 'json', 'test-stack', 'test-version'
        ])

    def test_should_return_active_stack_version(self):
        active_version = 'active'
        passive_version = 'passive'
        stack_versions = [
            {
                'identifier': 'test-' + passive_version,
                'stack_name': 'test',
                'version': passive_version,
                'weight%': NO_TRAFFIC
            },
            {
                'identifier': 'test-' + active_version,
                'stack_name': 'test',
                'version': active_version,
                'weight%': ALL_TRAFFIC}
        ]
        versions_mock = MagicMock(return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_active_stack_version('test')
        self.assertEquals(active_version, result, 'Result is not the active stack version')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', 'test'
        ])

    def test_should_return_active_stack_version_when_only_one_version_exists(self):
        active_version = 'active'
        stack_versions = [
            {
                'identifier': 'test-' + active_version,
                'stack_name': 'test',
                'version': active_version,
                'weight%': ALL_TRAFFIC
            }
        ]
        versions_mock = MagicMock(return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_active_stack_version('test')
        self.assertEquals(active_version, result, 'Result is not the active stack version')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', 'test'
        ])

    def test_should_return_none_if_no_active_version_exists(self):
        passive_version = 'passive'
        stack_versions = [
            {'identifier': 'test-' + passive_version, 'stack_name': 'test', 'version': passive_version, 'weight%': 0.0}
        ]
        versions_mock = MagicMock(return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_active_stack_version('test')
        self.assertIsNone(result, 'Active stack version returned although there is none')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', 'test'
        ])

    def test_should_return_passive_stack_version(self):
        active_version = 'active'
        passive_version = 'passive'
        stack_versions = [
            {
                'identifier': 'test-' + passive_version,
                'stack_name': 'test',
                'version': passive_version,
                'weight%': NO_TRAFFIC
            },
            {
                'identifier': 'test-' + active_version,
                'stack_name': 'test',
                'version': active_version,
                'weight%': ALL_TRAFFIC
            }
        ]
        versions_mock = MagicMock(return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_passive_stack_version('test')
        self.assertEquals(passive_version, result, 'Result is not the passive stack version')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', 'test'
        ])

    def test_should_return_passive_stack_version_when_only_one_version_exists(self):
        passive_version = 'passive'
        stack_versions = [
            {
                'identifier': 'test-' + passive_version,
                'stack_name': 'test',
                'version': passive_version,
                'weight%': NO_TRAFFIC
            }
        ]
        versions_mock = MagicMock(return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_passive_stack_version('test')
        self.assertEquals(passive_version, result, 'Result is not the passive stack version')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', 'test'
        ])

    def test_should_return_none_if_no_passive_version_exists(self):
        active_version = 'active'
        stack_versions = [
            {
                'identifier': 'test-' + active_version,
                'stack_name': 'test',
                'version': active_version,
                'weight%': ALL_TRAFFIC
            }
        ]
        versions_mock = MagicMock(return_value=bytes(json.dumps(stack_versions), encoding='utf-8'))
        subprocess.check_output = versions_mock

        result = self.__senza_wrapper.get_passive_stack_version('test')
        self.assertIsNone(result, 'Passive stack version returned although there is none')
        versions_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', 'test'
        ])

    def test_should_return_all_events_for_stack_version(self):
        stack_name = 'test-stack'
        stack_version_name = 'test-version'

        events = [
            {'stack_name': stack_name, 'version': stack_version_name, 'resource_type': 'test-event1'},
            {'stack_name': stack_name, 'version': stack_version_name, 'resource_type': 'test-event2'}
        ]
        events_mock = MagicMock(return_value=bytes(json.dumps(events), encoding='utf-8'))
        subprocess.check_output = events_mock

        result = self.__senza_wrapper.get_events(stack_name, stack_version_name)
        self.assertListEqual(events, result, 'Received unexpected list of events')
        events_mock.assert_called_once_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json', stack_name, stack_version_name
        ])

    def test_should_switch_traffic_between_versions(self):
        stack_name = 'test'
        active_version = 'active'
        passive_version = 'passive'
        switch_result = [
            {
                'identifier': stack_name + '-' + passive_version,
                'stack_name': stack_name,
                'version': passive_version,
                'old_weight%': NO_TRAFFIC,
                'new_weight%': ALL_TRAFFIC
            },
            {
                'identifier': stack_name + '-' + active_version,
                'stack_name': stack_name,
                'version': active_version,
                'old_weight%': ALL_TRAFFIC,
                'new_weight%': NO_TRAFFIC
            }
        ]
        switch_mock = MagicMock(return_value=bytes(json.dumps(switch_result), encoding='utf-8'))
        subprocess.check_output = switch_mock

        self.__senza_wrapper.switch_traffic(stack_name, passive_version, int(ALL_TRAFFIC))
        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', stack_name, passive_version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_raise_exception_if_new_weight_equals_old_weight_after_switching_traffic_between_versions(self):
        stack_name = 'test'
        active_version = 'active'
        passive_version = 'passive'
        switch_result = [
            {
                'identifier': stack_name + '-' + passive_version,
                'stack_name': stack_name,
                'version': passive_version,
                'old_weight%': NO_TRAFFIC,
                'new_weight%': NO_TRAFFIC
            },
            {
                'identifier': stack_name + '-' + active_version,
                'stack_name': stack_name,
                'version': active_version,
                'old_weight%': ALL_TRAFFIC,
                'new_weight%': ALL_TRAFFIC
            }
        ]
        switch_mock = MagicMock(return_value=bytes(json.dumps(switch_result), encoding='utf-8'))
        subprocess.check_output = switch_mock

        with self.assertRaisesRegex(Exception, 'Switching of \[{}\]% traffic to stack \[{}\] version \[{}\] failed'
                                               .format('100', stack_name, passive_version)):
            self.__senza_wrapper.switch_traffic(stack_name, passive_version, int(ALL_TRAFFIC))
        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', stack_name, passive_version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_raise_exception_if_weight_was_already_new_weight_before_switching_traffic_between_versions(self):
        stack_name = 'test'
        active_version = 'active'
        passive_version = 'passive'
        switch_result = [
            {
                'identifier': stack_name + '-' + passive_version,
                'stack_name': stack_name,
                'version': passive_version,
                'old_weight%': ALL_TRAFFIC,
                'new_weight%': ALL_TRAFFIC
            },
            {
                'identifier': stack_name + '-' + active_version,
                'stack_name': stack_name,
                'version': active_version,
                'old_weight%': NO_TRAFFIC,
                'new_weight%': NO_TRAFFIC
            }
        ]
        switch_mock = MagicMock(return_value=bytes(json.dumps(switch_result), encoding='utf-8'))
        subprocess.check_output = switch_mock

        with self.assertRaisesRegex(Exception, 'Traffic weight did not change, traffic for stack \[{}\] version \[{}\] '
                                               'is still at \[{}\]%'.format(stack_name, passive_version, '100')):
            self.__senza_wrapper.switch_traffic(stack_name, passive_version, int(ALL_TRAFFIC))
        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', stack_name, passive_version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_raise_exception_if_new_weight_is_unexpected_after_switching_traffic_between_versions(self):
        stack_name = 'test'
        active_version = 'active'
        passive_version = 'passive'
        switch_result = [
            {
                'identifier': stack_name + '-' + passive_version,
                'stack_name': stack_name,
                'version': passive_version,
                'old_weight%': NO_TRAFFIC,
                'new_weight%': ALL_TRAFFIC - 1
            },
            {
                'identifier': stack_name + '-' + active_version,
                'stack_name': stack_name,
                'version': active_version,
                'old_weight%': ALL_TRAFFIC,
                'new_weight%': NO_TRAFFIC + 1
            }
        ]
        switch_mock = MagicMock(return_value=bytes(json.dumps(switch_result), encoding='utf-8'))
        subprocess.check_output = switch_mock

        with self.assertRaisesRegex(Exception, 'Switching of \[{}\]% traffic to stack \[{}\] version \[{}\] failed'
                                               .format('100', stack_name, passive_version)):
            self.__senza_wrapper.switch_traffic(stack_name, passive_version, int(ALL_TRAFFIC))
        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', stack_name, passive_version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_succeed_if_switching_traffic_between_versions_returns_no_output(self):
        stack_name = 'test'
        passive_version = 'passive'
        switch_mock = MagicMock(return_value=None)
        subprocess.check_output = switch_mock

        self.__senza_wrapper.switch_traffic(stack_name, passive_version, int(ALL_TRAFFIC))

        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', stack_name, passive_version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_raise_exception_if_switching_traffic_between_versions_returns_error_code(self):
        stack_name = 'test'
        version = 'unknown'
        switch_mock = MagicMock(side_effect=subprocess.CalledProcessError(cmd='test', returncode=2))
        subprocess.check_output = switch_mock

        with self.assertRaises(subprocess.CalledProcessError):
            self.__senza_wrapper.switch_traffic(stack_name, version, int(ALL_TRAFFIC))

        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', stack_name, version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_raise_exception_if_switching_traffic_between_versions_returns_no_list(self):
        stack_name = 'test'
        version = 'unknown'
        switch_result = 'test output'
        switch_mock = MagicMock(return_value=bytes(json.dumps(switch_result), encoding='utf-8'))
        subprocess.check_output = switch_mock

        with self.assertRaisesRegex(Exception, 'Unexpected output: \[{}\]'.format(switch_result)):
            self.__senza_wrapper.switch_traffic(stack_name, version, int(ALL_TRAFFIC))

        switch_mock.assert_called_once_with([
            'senza', 'traffic', '--region', 'eu-west-1', '--output', 'json', stack_name, version,
            str(int(ALL_TRAFFIC))
        ])

    def test_should_create_new_stack_version(self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events = [
            {
                'stack_name': stack_name,
                'version': stack_version,
                'resource_type': 'CloudFormation::Stack',
                'event_time': '0',
                'ResourceStatus': 'CREATE_COMPLETE'
            }
        ]
        events_mock = MagicMock(return_value=bytes(json.dumps(events), encoding='utf-8'))
        subprocess.check_output = events_mock

        self.__senza_wrapper.create_stack(stack_name, stack_version, image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback', TEST_CONFIG, stack_version,
            'ImageVersion=' + image_version
        ])
        events_mock.assert_called_once_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json', stack_name, stack_version
        ])

    def test_should_wait_until_success_event_comes_when_creating_a_new_stack_version(self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        success_event = {
            'stack_name': stack_name,
            'version': stack_version,
            'resource_type': 'CloudFormation::Stack',
            'event_time': '1',
            'ResourceStatus': 'CREATE_COMPLETE'
        }

        other_event = {
            'stack_name': stack_name,
            'version': stack_version,
            'resource_type': 'Other',
            'event_time': '0',
            'ResourceStatus': 'Other'
        }

        side_effect_events = [
            bytes(json.dumps([other_event]), encoding='utf-8'),
            bytes(json.dumps([other_event, success_event]), encoding='utf-8'),
        ]
        events_mock = MagicMock(side_effect=side_effect_events)
        subprocess.check_output = events_mock

        self.__senza_wrapper.create_stack(stack_name, stack_version, image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback', TEST_CONFIG, stack_version,
            'ImageVersion=' + image_version
        ])
        events_mock.assert_called_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json', stack_name, stack_version
        ])
        self.assertEquals(2, len(events_mock.call_args_list), 'Unexpected number of calls for senza events')

    def test_should_raise_exception_if_senza_execution_failed_on_creating_a_new_stack_version(self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'
        error_code = 1

        create_mock = MagicMock(return_value=error_code)
        subprocess.call = create_mock

        with self.assertRaisesRegex(Exception, 'Failed to create new cluster with error code \[{}\]'
                                               .format(str(error_code))):

            self.__senza_wrapper.create_stack(stack_name, stack_version, image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback', TEST_CONFIG, stack_version,
            'ImageVersion=' + image_version
        ])

    def test_should_raise_exception_if_creation_of_new_stack_version_failed(self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events = [
            {
                'stack_name': stack_name,
                'version': stack_version,
                'resource_type': 'CloudFormation::Stack',
                'event_time': '0',
                'ResourceStatus': 'CREATE_FAILED'
            }
        ]
        events_mock = MagicMock(return_value=bytes(json.dumps(events), encoding='utf-8'))
        subprocess.check_output = events_mock

        with self.assertRaisesRegex(Exception, 'Creation of stack \[{}\] version \[{}\] with image version \[{}\] '
                                               'failed'.format(stack_name, stack_version, image_version)):

            self.__senza_wrapper.create_stack(stack_name, stack_version, image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback', TEST_CONFIG, stack_version,
            'ImageVersion=' + image_version
        ])
        events_mock.assert_called_once_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json', stack_name, stack_version
        ])

    def test_should_raise_exception_if_stack_creation_lasts_longer_than_timeout(self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'
        timeout = 0

        senza_wrapper = SenzaWrapper(TEST_CONFIG)
        senza_wrapper.set_stack_creation_retry_timeout(timeout)

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events_mock = MagicMock()
        subprocess.check_output = events_mock

        with self.assertRaisesRegex(Exception, 'Timeout while creating new stack version'):
            senza_wrapper.create_stack(stack_name, stack_version, image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback', TEST_CONFIG, stack_version,
            'ImageVersion=' + image_version
        ])

        events_mock.assert_not_called()

    def test_should_create_stack_with_additional_senza_parameter(self):
        stack_name = 'test-stack'
        stack_version = 'test-version'
        image_version = '0.0.0'
        timeout = 1

        senza_wrapper = SenzaWrapper(TEST_CONFIG)
        senza_wrapper.set_stack_creation_retry_timeout(timeout)

        senza_wrapper.add_parameter("test-key", "test-value")

        create_mock = MagicMock(return_value=0)
        subprocess.call = create_mock

        events = [
            {
                'stack_name': stack_name,
                'version': stack_version,
                'resource_type': 'CloudFormation::Stack',
                'event_time': '0',
                'ResourceStatus': 'CREATE_COMPLETE'
            }
        ]
        events_mock = MagicMock(return_value=bytes(json.dumps(events), encoding='utf-8'))
        subprocess.check_output = events_mock

        senza_wrapper.create_stack(stack_name, stack_version, image_version)

        create_mock.assert_called_once_with([
            'senza', 'create', '--region', 'eu-west-1', '--disable-rollback', TEST_CONFIG, stack_version,
            'ImageVersion=' + image_version, 'test-key=test-value'
        ])
        events_mock.assert_called_once_with([
            'senza', 'events', '--region', 'eu-west-1', '--output', 'json', stack_name, stack_version
        ])
コード例 #15
0
def solrcloud_cli(cli_args):
    parser = build_args_parser()
    args = parser.parse_args(cli_args)

    if not args.config:
        args.config = os.path.expanduser(DEFAULT_CONF_FILE)

    if not os.path.exists(args.config):
        print("Configuration file does not exist:", args.config)
        parser.print_usage()
        return

    with open(args.config, "rb") as fd:
        settings = yaml.load(fd)

    senza_wrapper = SenzaWrapper(args.senza_configuration)
    for key, value in settings.items():
        senza_wrapper.add_parameter(key, value)

    if args.command in ["bootstrap"]:
        controller = ClusterBootstrapController(
            base_url=settings["SolrBaseUrl"],
            stack_name=settings["ApplicationId"],
            sharding_level=args.sharding_level,
            replication_factor=args.replication_level,
            image_version=args.image_version,
            oauth_token=args.token,
            senza_wrapper=senza_wrapper,
        )
    elif args.command in [
        "deploy",
        "create-new-cluster",
        "delete-old-cluster",
        "add-new-nodes",
        "delete-old-nodes",
        "switch",
    ]:
        controller = ClusterDeploymentController(
            base_url=settings["SolrBaseUrl"],
            stack_name=settings["ApplicationId"],
            image_version=args.image_version,
            oauth_token=args.token,
            senza_wrapper=senza_wrapper,
        )
    elif args.command in ["delete"]:
        controller = ClusterDeleteController(
            base_url=settings["SolrBaseUrl"],
            stack_name=settings["ApplicationId"],
            oauth_token=args.token,
            senza_wrapper=senza_wrapper,
        )
    else:
        print("Unknown command:", args.command)
        parser.print_usage()
        return

    if args.command == "bootstrap":
        controller.bootstrap_cluster()
    elif args.command == "deploy":
        controller.deploy_new_version()
    elif args.command == "delete":
        controller.delete_cluster()
    elif args.command == "create-new-cluster":
        controller.create_cluster()
    elif args.command == "delete-old-cluster":
        controller.delete_cluster()
    elif args.command == "add-new-nodes":
        controller.add_new_nodes_to_cluster()
    elif args.command == "delete-old-nodes":
        controller.delete_old_nodes_from_cluster()
    elif args.command == "switch":
        controller.switch_traffic()