def test_set_service_and_task_roles(self): service = ServiceConfiguration('test-service', 'test') try: service._validate_changes({ 'cloudlift_version': 'test', 'ecr_repo': { 'name': 'test-service-repo' }, 'service_role_arn': 'foo', 'services': { 'TestService': { 'memory_reservation': 1000, 'command': None, 'secrets_name': 'secret-config', 'http_interface': { 'internal': True, 'container_port': 8080, 'restrict_access_to': ['0.0.0.0/0'], } }, 'task_arn': 'task_arn', 'task_execution_arn': 'task_execution_arn' } }) except UnrecoverableException as e: self.fail('Exception thrown: {}'.format(e))
def test_default_service_configuration_ecr_repo(self, getcwd): getcwd.return_value = "/path/to/dummy" service = ServiceConfiguration('test-service', 'test') conf = service._default_service_configuration() self.assertEqual({'name': 'dummy-repo'}, conf.get('ecr_repo'))
def test_sidecars(self): service = ServiceConfiguration('test-service', 'test') try: service._validate_changes({ 'cloudlift_version': 'test', 'ecr_repo': { 'name': 'test-service-repo' }, 'services': { 'TestService': { 'memory_reservation': 1000, 'command': None, 'secrets_name': 'secret-config', 'sidecars': [{ 'name': 'redis', 'image': 'redis:latest', 'memory_reservation': 128 }, { 'name': 'envoy', 'image': 'envoy:latest', 'memory_reservation': 256, 'command': ['./start'] }] } } }) except UnrecoverableException as e: self.fail('Exception thrown: {}'.format(e))
def __init__(self, name, environment='', env_sample_file='', timeout_seconds=None, version=None, build_args=None, dockerfile=None, ssh=None, cache_from=None, deployment_identifier=None, working_dir='.'): self.name = name self.environment = environment self.deployment_identifier = deployment_identifier self.env_sample_file = env_sample_file self.timeout_seconds = timeout_seconds self.version = version self.ecr_client = boto3.session.Session(region_name=self.region).client('ecr') self.cluster_name = get_cluster_name(environment) self.service_configuration = ServiceConfiguration(service_name=name, environment=environment).get_config() self.service_info_fetcher = ServiceInformationFetcher(self.name, self.environment, self.service_configuration) if not self.service_info_fetcher.stack_found: raise UnrecoverableException( "error finding stack in ServiceUpdater: {}-{}".format(self.name, self.environment)) ecr_repo_config = self.service_configuration.get('ecr_repo') self.ecr = ECR( self.region, ecr_repo_config.get('name', spinalcase(self.name + '-repo')), ecr_repo_config.get('account_id', get_account_id()), ecr_repo_config.get('assume_role_arn', None), version, build_args, dockerfile, working_dir, ssh, cache_from )
def __init__(self, name, environment): self.name = name self.environment = environment self.stack_name = get_service_stack_name(environment, name) self.client = get_client_for('cloudformation', self.environment) self.environment_stack = self._get_environment_stack() self.existing_events = get_stack_events(self.client, self.stack_name) self.service_configuration = ServiceConfiguration( self.name, self.environment)
def get_version(name, environment, image, git): ServiceInformationFetcher( name, environment, ServiceConfiguration(service_name=name, environment=environment).get_config(), ).get_version(print_image=image, print_git=git)
def test_get_random_available_listener_rule_priority(self): testing_rules = set( list(range(1, 500)) + list(range(534, 678)) + list(range(1000, 30000)) + list(range(40000, 48000))) available_rules = set(range(1, 50001)) - testing_rules testing_listener_rules = [{"Priority": rule} for rule in testing_rules] for test_iteration in range(1, 100): assert ServiceConfiguration._get_random_available_listener_rule_priority( testing_listener_rules, "abcd") not in testing_rules
def test_get_config(self): self.setup_existing_params() store_object = ServiceConfiguration('test-service', 'dummy-staging') response = store_object.get_config() assert response == { "services": { "TestService": { "memory_reservation": 1000, "command": None, "http_interface": { "internal": True, "container_port": 80, "restrict_access_to": [u'0.0.0.0/0'] } } } }
def test_set_config_with_random_listner_priority( self, mock_get_listener_rule_priority): self.setup_params_for_listener_selection() mock_get_listener_rule_priority.return_value = 23 store_object = ServiceConfiguration('test-service', 'dummy-staging') get_response = store_object.get_config() get_response["services"]["TestService"]["http_interface"][ "restrict_access_to"] = [u"123.123.123.123/32"] store_object.set_config(get_response) update_response = store_object.get_config() expected_response = { 'ecr_repo': { 'name': 'test-service-repo' }, 'services': { 'TestService': { 'memory_reservation': 1000, 'command': None, 'http_interface': { 'alb': { 'create_new': False, 'listener_arn': 'random-listener-arn', 'host': 'host-address.com', 'priority': 23, 'target_5xx_error_threshold': 5 }, 'internal': True, 'container_port': 80, 'restrict_access_to': ['123.123.123.123/32'] } } } }
def test_set_config_stop_timeout(self): self.setup_existing_params() store_object = ServiceConfiguration('test-service', 'dummy-staging') get_response = store_object.get_config() get_response["services"]["TestService"]["stop_timeout"] = 120 store_object.set_config(get_response) update_response = store_object.get_config() assert update_response == { "ecr_repo": { "name": "test-service-repo" }, "services": { "TestService": { "memory_reservation": 1000, 'secrets_name': 'secret-config', "command": None, "http_interface": { "internal": True, "container_port": 80, "restrict_access_to": [u'0.0.0.0/0'], }, "stop_timeout": 120 } } }
def test_set_config(self): self.setup_existing_params() store_object = ServiceConfiguration('test-service', 'dummy-staging') get_response = store_object.get_config() get_response["services"]["TestService"]["http_interface"][ "restrict_access_to"] = [u"123.123.123.123/32"] store_object.set_config(get_response) update_response = store_object.get_config() assert update_response == { 'ecr_repo': { 'name': 'test-service-repo' }, "services": { "TestService": { "memory_reservation": 1000, 'secrets_name': 'secret-config', "command": None, "http_interface": { "internal": True, "container_port": 80, "restrict_access_to": [u"123.123.123.123/32"], } } } }
def test_initialization(self): with patch.object(ServiceConfiguration, 'get_config', new=mocked_service_config): with patch.object(ServiceInformationFetcher, 'get_current_version', new=mocked_service_information): service_config = ServiceConfiguration("test-service", "staging") generator = ServiceTemplateGenerator(service_config, None) assert generator.env == 'staging' assert generator.application_name == 'test-service' assert generator.environment_stack == None
def test_set_config_health_check_command(self): service = ServiceConfiguration('test-service', 'test') try: service._validate_changes({ 'cloudlift_version': 'test', 'ecr_repo': { 'name': 'test-service-repo' }, 'services': { 'TestService': { 'memory_reservation': 1000, 'command': None, 'secrets_name': 'secret-config', 'http_interface': { 'internal': True, 'container_port': 8080, 'restrict_access_to': ['0.0.0.0/0'], }, "container_health_check": { "command": "echo 'Working'", "start_period": 30, "retries": 4, "interval": 5, "timeout": 30, } } } }) except UnrecoverableException as e: self.fail('Exception thrown: {}'.format(e)) try: service._validate_changes({ 'cloudlift_version': 'test', 'ecr_repo': { 'name': 'test-service-repo' }, 'services': { 'TestService': { 'memory_reservation': 1000, 'command': None, 'secrets_name': 'secret-config', 'http_interface': { 'internal': True, 'container_port': 8080, 'restrict_access_to': ['0.0.0.0/0'], }, "container_health_check": { "start_period": 123, } } } }) self.fail('Exception expected but did not fail') except UnrecoverableException as e: self.assertTrue(True)
def test_set_config_placement_constraints(self): service = ServiceConfiguration('test-service', 'test') try: service._validate_changes({ 'cloudlift_version': 'test', 'ecr_repo': { 'name': 'test-service-repo' }, 'services': { 'TestService': { 'memory_reservation': 1000, 'secrets_name': 'secret-config', 'command': None, 'placement_constraints': [{ 'type': 'memberOf', 'expression': 'expr' }] } } }) except UnrecoverableException as e: self.fail('Exception thrown: {}'.format(e)) try: service._validate_changes({ 'cloudlift_version': 'test', 'ecr_repo': { 'name': 'test-service-repo' }, 'services': { 'TestService': { 'memory_reservation': 1000, 'secrets_name': 'secret-config', 'command': None, 'placement_constraints': [{ 'type': 'invalid' }] } } }) self.fail('Validation error expected but validation passed') except UnrecoverableException as e: self.assertTrue( "'invalid' is not one of ['memberOf', 'distinctInstance']" in str(e))
def test_set_config_system_controls(self): service = ServiceConfiguration('test-service', 'test') try: service._validate_changes({ 'cloudlift_version': 'test', 'ecr_repo': { 'name': 'test-service-repo' }, 'services': { 'TestService': { 'memory_reservation': 1000, 'command': None, 'secrets_name': 'secret-config', 'system_controls': [{ 'namespace': 'ns', 'value': 'val' }] } } }) except UnrecoverableException as e: self.fail('Exception thrown: {}'.format(e)) try: service._validate_changes({ 'cloudlift_version': 'test', 'ecr_repo': { 'name': 'test-service-repo' }, 'services': { 'TestService': { 'memory_reservation': 1000, 'command': None, 'secrets_name': 'secret-config', 'system_controls': "invalid" } } }) self.fail('Validation error expected but validation passed') except UnrecoverableException as e: self.assertTrue("'invalid' is not of type 'array'" in str(e))
def test_container_labels(self): service = ServiceConfiguration('test-service', 'test') with self.assertRaises(UnrecoverableException) as error: service._validate_changes({ 'cloudlift_version': 'test', 'ecr_repo': { 'name': 'test-service-repo' }, 'services': { 'TestService': { 'memory_reservation': 1000, 'command': None, 'secrets_name': 'secret-config', "container_labels": { "key": 1 } } } }) self.assertEqual( "1 is not of type 'string' in services.TestService.container_labels.key", error.exception.value) service._validate_changes({ 'cloudlift_version': 'test', 'ecr_repo': { 'name': 'test-service-repo' }, 'services': { 'TestService': { 'memory_reservation': 1000, 'command': None, 'secrets_name': 'secret-config', "container_labels": { "key": "value" } } } })
def verify_env_sample(name, environment, env_sample_directory_path): ServiceInformationFetcher( name, environment, ServiceConfiguration(service_name=name, environment=environment).get_config(), ).verify_env_sample(env_sample_directory_path)
def test_generate_service(self): environment = 'staging' application_name = 'dummy' env_stack = { u'StackId': 'arn:aws:cloudformation:ap-south-1:725827686899:stack/cluster-staging/65410f80-d21c-11e8-913a-503a56826a2a', u'LastUpdatedTime': datetime.datetime(2018, 11, 9, 5, 22, 30, 691000), u'Parameters': [{ u'ParameterValue': 'staging-cluster-v3', u'ParameterKey': 'KeyPair' }, { u'ParameterValue': '1', u'ParameterKey': 'ClusterSize' }, { u'ParameterValue': 'arn:aws:sns:ap-south-1:725827686899:non-prod-mumbai', u'ParameterKey': 'NotificationSnsArn' }, { u'ParameterValue': 'staging', u'ParameterKey': 'Environment' }, { u'ParameterValue': 'm5.xlarge', u'ParameterKey': 'InstanceType' }, { u'ParameterValue': '50', u'ParameterKey': 'MaxSize' }], u'Tags': [], u'Outputs': [{ u'Description': 'VPC in which environment is setup', u'OutputKey': 'VPC', u'OutputValue': 'vpc-00f07c5a6b6c9abdb' }, { u'Description': 'Options used with cloudlift when building this cluster', u'OutputKey': 'CloudliftOptions', u'OutputValue': '{"min_instances": 1, "key_name": "staging-cluster-v3", "max_instances": 50, "instance_type": "m5.xlarge", "cloudlift_version": "0.9.4", "env": "staging"}' }, { u'Description': 'Maximum instances in cluster', u'OutputKey': 'MaxInstances', u'OutputValue': '50' }, { u'Description': 'Security group ID for ALB', u'OutputKey': 'SecurityGroupAlb', u'OutputValue': 'sg-095dbeb511019cfd8' }, { u'Description': 'Key Pair name for accessing the instances', u'OutputKey': 'KeyName', u'OutputValue': 'staging-cluster-v3' }, { u'Description': 'ID of the 1st subnet', u'OutputKey': 'PrivateSubnet1', u'OutputValue': 'subnet-09b6cd23af94861cc' }, { u'Description': 'ID of the 2nd subnet', u'OutputKey': 'PrivateSubnet2', u'OutputValue': 'subnet-0657bc2faa99ce5f7' }, { u'Description': 'Minimum instances in cluster', u'OutputKey': 'MinInstances', u'OutputValue': '1' }, { u'Description': 'ID of the 2nd subnet', u'OutputKey': 'PublicSubnet2', u'OutputValue': 'subnet-096377a44ccb73aca' }, { u'Description': 'EC2 instance type', u'OutputKey': 'InstanceType', u'OutputValue': 'm5.xlarge' }, { u'Description': 'ID of the 1st subnet', u'OutputKey': 'PublicSubnet1', u'OutputValue': 'subnet-0aeae8fe5e13a7ff7' }, { u'Description': 'The name of the stack', u'OutputKey': 'StackName', u'OutputValue': 'cluster-staging' }, { u'Description': 'The unique ID of the stack. To be supplied to circle CI environment variables to validate during deployment.', u'OutputKey': 'StackId', u'OutputValue': 'arn:aws:cloudformation:ap-south-1:725827686899:stack/cluster-staging/65410f80-d21c-11e8-913a-503a56826a2a' }], u'CreationTime': datetime.datetime(2018, 10, 17, 14, 53, 23, 469000), u'Capabilities': ['CAPABILITY_NAMED_IAM'], u'StackName': 'cluster-staging', u'NotificationARNs': [], u'StackStatus': 'UPDATE_COMPLETE', u'DisableRollback': True, u'ChangeSetId': 'arn:aws:cloudformation:ap-south-1:725827686899:changeSet/cg901a2f5dbf984b9e9807a21da1ac7d12/7588cd05-1e2d-4dd6-85ab-12b921baa814', u'RollbackConfiguration': {} } with patch.object(ServiceConfiguration, 'get_config', new=mocked_service_config): with patch.object(ParameterStore, 'get_existing_config', new=mocked_environment_config): with patch.object(ServiceInformationFetcher, 'get_current_version', new=mocked_service_information): service_config = ServiceConfiguration( application_name, environment) template_generator = ServiceTemplateGenerator( service_config, env_stack) template_generator.env_sample_file_path = './test/templates/test_env.sample' generated_template = template_generator.generate_service() assert to_json(''.join( open('./test/templates/expected_service_template.yml').readlines()) ) == to_json(generated_template)
class ServiceUpdater(object): def __init__(self, name, environment='', env_sample_file='', timeout_seconds=None, version=None, build_args=None, dockerfile=None, ssh=None, cache_from=None, deployment_identifier=None, working_dir='.'): self.name = name self.environment = environment self.deployment_identifier = deployment_identifier self.env_sample_file = env_sample_file self.timeout_seconds = timeout_seconds self.version = version self.ecr_client = boto3.session.Session( region_name=self.region).client('ecr') self.cluster_name = get_cluster_name(environment) self.service_configuration = ServiceConfiguration( service_name=name, environment=environment).get_config() self.service_info_fetcher = ServiceInformationFetcher( self.name, self.environment, self.service_configuration) if not self.service_info_fetcher.stack_found: raise UnrecoverableException( "error finding stack in ServiceUpdater: {}-{}".format( self.name, self.environment)) ecr_repo_config = self.service_configuration.get('ecr_repo') self.ecr = ECR( self.region, ecr_repo_config.get('name', spinalcase(self.name + '-repo')), ecr_repo_config.get('account_id', get_account_id()), ecr_repo_config.get('assume_role_arn', None), version, build_args, dockerfile, working_dir, ssh, cache_from) def run(self): log_warning("Deploying to {self.region}".format(**locals())) if not os.path.exists(self.env_sample_file): raise UnrecoverableException('env.sample not found. Exiting.') log_intent("name: " + self.name + " | environment: " + self.environment + " | version: " + str(self.version) + " | deployment_identifier: " + str(self.deployment_identifier)) log_bold("Checking image in ECR") self.ecr.upload_artefacts() log_bold("Initiating deployment\n") image_url = self.ecr.image_uri target = deployer.deploy_new_version kwargs = dict( cluster_name=self.cluster_name, service_name=self.name, sample_env_file_path=self.env_sample_file, timeout_seconds=self.timeout_seconds, env_name=self.environment, ecr_image_uri=image_url, deployment_identifier=self.deployment_identifier, ) self.run_job_for_all_services("Deploy", target, kwargs) def revert(self): target = deployer.revert_deployment kwargs = dict(cluster_name=self.cluster_name, timeout_seconds=self.timeout_seconds, deployment_identifier=self.deployment_identifier) self.run_job_for_all_services("Revert", target, kwargs) def upload_to_ecr(self, additional_tags): self.ecr.upload_artefacts() self.ecr.add_tags(additional_tags) def run_job_for_all_services(self, job_name, target, kwargs): log_bold("{} concurrency: {}".format(job_name, DEPLOYMENT_CONCURRENCY)) jobs = [] service_info = self.service_info_fetcher.service_info for index, ecs_service_logical_name in enumerate(service_info): ecs_service_info = service_info[ecs_service_logical_name] log_bold(f"Queueing {job_name} of " + ecs_service_info['ecs_service_name']) color = DEPLOYMENT_COLORS[index % 3] services_configuration = self.service_configuration['services'] kwargs.update( dict( ecs_service_name=ecs_service_info['ecs_service_name'], secrets_name=ecs_service_info.get('secrets_name'), ecs_service_logical_name=ecs_service_logical_name, color=color, service_configuration=services_configuration.get( ecs_service_logical_name), region=self.region, )) process = multiprocessing.Process(target=target, kwargs=kwargs) jobs.append(process) all_exit_codes = [] for chunk_of_jobs in chunks(jobs, DEPLOYMENT_CONCURRENCY): for process in chunk_of_jobs: process.start() while True: sleep(1) exit_codes = [proc.exitcode for proc in chunk_of_jobs] if None not in exit_codes: break for exit_code in exit_codes: all_exit_codes.append(exit_code) if any(all_exit_codes) != 0: raise UnrecoverableException(f"{job_name} failed") @property def region(self): return get_region_for_environment(self.environment)
class ServiceCreator(object): ''' Create and execute CloudFormation template or changeset for existing CloudFormation template for ECS service and related dependencies ''' def __init__(self, name, environment): self.name = name self.environment = environment self.stack_name = get_service_stack_name(environment, name) self.client = get_client_for('cloudformation', self.environment) self.environment_stack = self._get_environment_stack() self.existing_events = get_stack_events(self.client, self.stack_name) self.service_configuration = ServiceConfiguration( self.name, self.environment) def create(self): ''' Create and execute CloudFormation template for ECS service and related dependencies ''' log_bold("Initiating service creation") self.service_configuration.edit_config() template_generator = ServiceTemplateGenerator( self.service_configuration, self.environment_stack) service_template_body = template_generator.generate_service() try: self.client.create_stack( StackName=self.stack_name, TemplateBody=service_template_body, Parameters=[{ 'ParameterKey': 'Environment', 'ParameterValue': self.environment, }], OnFailure='DO_NOTHING', Capabilities=['CAPABILITY_NAMED_IAM'], ) log_bold("Submitted to cloudformation. Checking progress...") self._print_progress() except ClientError as boto_client_error: error_code = boto_client_error.response['Error']['Code'] if error_code == 'AlreadyExistsException': log_err("Stack " + self.stack_name + " already exists.") exit(1) else: raise boto_client_error def update(self): ''' Create and execute changeset for existing CloudFormation template for ECS service and related dependencies ''' log_bold("Starting to update service") self.service_configuration.edit_config() try: template_generator = ServiceTemplateGenerator( self.service_configuration, self.environment_stack) service_template_body = template_generator.generate_service() change_set = create_change_set(self.client, service_template_body, self.stack_name, "", self.environment) self.service_configuration.update_cloudlift_version() log_bold("Executing changeset. Checking progress...") self.client.execute_change_set( ChangeSetName=change_set['ChangeSetId']) self._print_progress() except ClientError as exc: if "No updates are to be performed." in str(exc): log_err("No updates are to be performed") else: raise exc def _get_environment_stack(self): try: log("Looking for " + self.environment + " cluster.") environment_stack = self.client.describe_stacks( StackName=get_cluster_name(self.environment))['Stacks'][0] log_bold(self.environment + " stack found. Using stack with ID: " + environment_stack['StackId']) except ClientError: log_err(self.environment + " cluster not found. Create the environment \ cluster using `create_environment` command.") exit(1) return environment_stack def _print_progress(self): while True: response = self.client.describe_stacks(StackName=self.stack_name) if "IN_PROGRESS" not in response['Stacks'][0]['StackStatus']: break all_events = get_stack_events(self.client, self.stack_name) print_new_events(all_events, self.existing_events) self.existing_events = all_events sleep(5) final_status = response['Stacks'][0]['StackStatus'] if "FAIL" in final_status: log_err("Finished with status: %s" % (final_status)) else: log_bold("Finished with status: %s" % (final_status))
class ServiceCreator(object): ''' Create and execute CloudFormation template or changeset for existing CloudFormation template for ECS service and related dependencies ''' def __init__(self, name, environment, env_sample_file): self.name = name self.environment = environment self.stack_name = get_service_stack_name(environment, name) self.client = get_client_for('cloudformation', self.environment) self.environment_stack = self._get_environment_stack() self.existing_events = get_stack_events(self.client, self.stack_name) self.service_configuration = ServiceConfiguration( self.name, self.environment) self.env_sample_file = env_sample_file def create(self, config_body=None, version=None, build_arg=None, dockerfile=None, ssh=None, cache_from=None): ''' Create and execute CloudFormation template for ECS service and related dependencies ''' log_bold("Initiating service creation") if config_body is None: self.service_configuration.edit_config() else: self.service_configuration.set_config(config_body) self.service_configuration.validate() ecr_repo_config = self.service_configuration.get_config().get( 'ecr_repo') ecr = ECR( region=get_region_for_environment(self.environment), repo_name=ecr_repo_config.get('name'), account_id=ecr_repo_config.get('account_id', None), assume_role_arn=ecr_repo_config.get('assume_role_arn', None), version=version, build_args=build_arg, dockerfile=dockerfile, ssh=ssh, cache_from=cache_from, ) ecr.upload_artefacts() template_generator = ServiceTemplateGenerator( self.service_configuration, self.environment_stack, self.env_sample_file, ecr.image_uri) service_template_body = template_generator.generate_service() try: options = prepare_stack_options_for_template( service_template_body, self.environment, self.stack_name) self.client.create_stack( StackName=self.stack_name, Parameters=[{ 'ParameterKey': 'Environment', 'ParameterValue': self.environment, }], OnFailure='DO_NOTHING', Capabilities=['CAPABILITY_NAMED_IAM'], **options, ) log_bold("Submitted to cloudformation. Checking progress...") self._print_progress() except ClientError as boto_client_error: error_code = boto_client_error.response['Error']['Code'] if error_code == 'AlreadyExistsException': raise UnrecoverableException("Stack " + self.stack_name + " already exists.") else: raise boto_client_error def update(self): ''' Create and execute changeset for existing CloudFormation template for ECS service and related dependencies ''' log_bold("Starting to update service") self.service_configuration.edit_config() self.service_configuration.validate() information_fetcher = ServiceInformationFetcher( self.service_configuration.service_name, self.environment, self.service_configuration.get_config(), ) try: current_image_uri = information_fetcher.get_current_image_uri() desired_counts = information_fetcher.fetch_current_desired_count() current_deployment_identifier = information_fetcher.get_current_deployment_identifier( ) template_generator = ServiceTemplateGenerator( self.service_configuration, self.environment_stack, self.env_sample_file, current_image_uri, desired_counts, current_deployment_identifier) service_template_body = template_generator.generate_service() change_set = create_change_set(self.client, service_template_body, self.stack_name, None, self.environment) if change_set is None: return self.service_configuration.update_cloudlift_version() log_bold("Executing changeset. Checking progress...") self.client.execute_change_set( ChangeSetName=change_set['ChangeSetId']) self._print_progress() except ClientError as exc: if "No updates are to be performed." in str(exc): log_err("No updates are to be performed") else: raise exc def _get_environment_stack(self): try: log("Looking for " + self.environment + " cluster.") environment_stack = self.client.describe_stacks( StackName=get_cluster_name(self.environment))['Stacks'][0] log_bold(self.environment + " stack found. Using stack with ID: " + environment_stack['StackId']) except ClientError: raise UnrecoverableException( self.environment + " cluster not found. Create the environment \ cluster using `create_environment` command.") return environment_stack def _print_progress(self): while True: response = self.client.describe_stacks(StackName=self.stack_name) if "IN_PROGRESS" not in response['Stacks'][0]['StackStatus']: break all_events = get_stack_events(self.client, self.stack_name) print_new_events(all_events, self.existing_events) self.existing_events = all_events sleep(5) final_status = response['Stacks'][0]['StackStatus'] if "FAIL" in final_status: log_err("Finished with status: %s" % (final_status)) else: log_bold("Finished with status: %s" % (final_status))
def test_initialization(self): store_object = ServiceConfiguration('test-service', 'dummy-staging') assert store_object.environment == 'dummy-staging' assert store_object.service_name == 'test-service' assert store_object.table is not None