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)
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.'))
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.'))
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 ])
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)
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_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_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)
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 ])
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)
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 ])
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()
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 ])
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()