def do_command(self): # get arguments self.interactive = self.app.pargs.interactive self.region = self.app.pargs.region self.noverify = self.app.pargs.no_verify_ssl self.force_non_interactive = False # Determine if the customer is avoiding interactive mode by setting the platform flag if self.app.pargs.platform: self.force_non_interactive = True # Code Commit integration self.source = self.app.pargs.source source_location = None branch = None repository = None if self.source is not None: source_location, repository, branch = utils.parse_source( self.source) # The user specifies directories to initialize self.modules = self.app.pargs.modules if self.modules and len(self.modules) > 0: self.initialize_multiple_directories() return default_env = self.get_old_values() fileoperations.touch_config_folder() if self.interactive: self.region = get_region(self.region, self.interactive, self.force_non_interactive) else: self.region = get_region_from_inputs(self.app.pargs.region) aws.set_region(self.region) # Warn the customer if they picked a region that CodeCommit is not supported codecommit_region_supported = codecommit.region_supported(self.region) if self.source is not None and not codecommit_region_supported: io.log_warning(strings['codecommit.badregion']) self.region = set_up_credentials(self.app.pargs.profile, self.region, self.interactive) self.solution = self.get_solution_stack() self.app_name = self.get_app_name() if self.noverify: fileoperations.write_config_setting('global', 'no-verify-ssl', True) if not default_env and not self.interactive: # try to get default env from config file if exists try: default_env = commonops.get_current_branch_environment() except NotInitializedError: default_env = None elif self.interactive: default_env = None if self.force_non_interactive: default_env = '/ni' # Create application sstack, key = commonops.pull_down_app_info(self.app_name, default_env=default_env) if elasticbeanstalk.application_exist(self.app_name) \ else commonops.create_app(self.app_name, default_env=default_env) if not self.solution: self.solution = sstack platform_set = False if not self.solution or \ (self.interactive and not self.app.pargs.platform): if fileoperations.env_yaml_exists(): env_yaml_platform = fileoperations.get_platform_from_env_yaml() if env_yaml_platform: platform = solutionstack.SolutionStack( env_yaml_platform).version self.solution = platform platform_set = True if not platform_set: result = commonops.prompt_for_solution_stack() self.solution = result.version # Select CodeBuild image if BuildSpec is present do not prompt or show if we are non-interactive if fileoperations.build_spec_exists( ) and not self.force_non_interactive: build_spec = fileoperations.get_build_configuration() if build_spec is not None and build_spec.image is None: LOG.debug( "Buildspec file is present but image is does not exist. Attempting to fill best guess." ) platform_image = initializeops.get_codebuild_image_from_platform( self.solution) # If the return is a dictionary then it must be a single image and we can use that automatically if type(platform_image) is dict: io.echo('codebuild.latestplatform'.replace( '{platform}', self.solution)) else: # Otherwise we have an array for images which we must prompt the customer to pick from io.echo(prompts['codebuild.getplatform'].replace( '{platform}', self.solution)) selected = utils.prompt_for_index_in_list( map(lambda image: image['description'], platform_image)) platform_image = platform_image[selected] platform_image['name'] = utils.decode_bytes( platform_image['name']) # Finally write the CodeBuild image back to the buildspec file fileoperations.write_config_setting( fileoperations.buildspec_config_header, 'Image', platform_image['name'], file=fileoperations.buildspec_name) # Setup code commit integration # Ensure that git is setup source_control = SourceControl.get_source_control() try: source_control_setup = source_control.is_setup() if source_control_setup is None: source_control_setup = False except CommandError: source_control_setup = False default_branch_exists = False if gitops.git_management_enabled() and not self.interactive: default_branch_exists = True prompt_codecommit = True # Do not prompt if we are in non-interactive mode, the region is not supported for CodeCommit, # the specified source is from CodeCommit OR we already have default CodeCommit values set. if self.force_non_interactive \ or not codecommit.region_supported(self.region) \ or self.source is not None \ or default_branch_exists: prompt_codecommit = False # Prompt for interactive CodeCommit if prompt_codecommit: if not source_control_setup: io.echo(strings['codecommit.nosc']) else: io.echo(strings['codecommit.ccwarning']) try: io.validate_action(prompts['codecommit.usecc'], "y") # Setup git config settings for code commit credentials source_control.setup_codecommit_cred_config() # Get user specified repository if repository is None: repository = get_repository_interactive() else: try: result = codecommit.get_repository(repository) source_control.setup_codecommit_remote_repo( remote_url=result['repositoryMetadata'] ['cloneUrlHttp']) except ServiceError as ex: io.log_error(strings['codecommit.norepo']) raise ex # Get user specified branch if branch is None: branch = get_branch_interactive(repository) else: try: codecommit.get_branch(repository, branch) except ServiceError as ex: io.log_error(strings['codecommit.nobranch']) raise ex source_control.setup_existing_codecommit_branch(branch) except ValidationError: LOG.debug( "Denied option to use CodeCommit, continuing initialization" ) # Initialize the whole setup initializeops.setup(self.app_name, self.region, self.solution, dir_path=None, repository=repository, branch=branch) if 'IIS' not in self.solution: self.keyname = self.get_keyname(default=key) if self.keyname == -1: self.keyname = None fileoperations.write_config_setting('global', 'default_ec2_keyname', self.keyname) # Default to including git submodules when creating zip files through `eb create`/`eb deploy`. fileoperations.write_config_setting('global', 'include_git_submodules', True)
def extract_solution_stack_from_env_yaml(): env_yaml_platform = fileoperations.get_platform_from_env_yaml() if env_yaml_platform: platform = solutionstack.SolutionStack( env_yaml_platform).platform_shorthand return platform
def initialize_multiple_directories(self): application_created = False self.app_name = None cwd = os.getcwd() for module in self.modules: if os.path.exists(module) and os.path.isdir(module): os.chdir(module) fileoperations.touch_config_folder() # Region should be set once for all modules if not self.region: if self.interactive: self.region = get_region(self.region, self.interactive, self.force_non_interactive) else: self.region = get_region_from_inputs( self.app.pargs.region) aws.set_region(self.region) self.region = set_up_credentials(self.app.pargs.profile, self.region, self.interactive) solution = self.get_solution_stack() # App name should be set once for all modules if not self.app_name: # Switching back to the root dir will suggest the root dir name # as the application name os.chdir(cwd) self.app_name = self.get_app_name() os.chdir(module) if self.noverify: fileoperations.write_config_setting( 'global', 'no-verify-ssl', True) default_env = None if self.force_non_interactive: default_env = '/ni' if not application_created: sstack, key = commonops.create_app(self.app_name, default_env=default_env) application_created = True else: sstack, key = commonops.pull_down_app_info( self.app_name, default_env=default_env) io.echo('\n--- Configuring module: {0} ---'.format(module)) if not solution: solution = sstack platform_set = False if not solution or \ (self.interactive and not self.app.pargs.platform): if fileoperations.env_yaml_exists(): env_yaml_platform = fileoperations.get_platform_from_env_yaml( ) if env_yaml_platform: platform = solutionstack.SolutionStack( env_yaml_platform).version solution = platform io.echo( strings['init.usingenvyamlplatform'].replace( '{platform}', platform)) platform_set = True if not platform_set: result = commonops.prompt_for_solution_stack() solution = result.version initializeops.setup(self.app_name, self.region, solution) if 'IIS' not in solution: keyname = self.get_keyname(default=key) if keyname == -1: self.keyname = None fileoperations.write_config_setting( 'global', 'default_ec2_keyname', keyname) os.chdir(cwd)
class TestRequests(unittest.TestCase): maxDiff = None solution = solutionstack.SolutionStack( '64bit Amazon Linux 2014.03 v1.0.6 running PHP 5.5') def test_create_environment_request_init(self): request_args = { 'app_name': 'ebcli-intTest-app', 'cname': None, 'env_name': 'my-awesome-env', 'instance_profile': None, 'instance_type': None, 'key_name': None, 'platform': self.solution, 'sample_application': False, 'service_role': None, 'single_instance': False, 'template_name': None, 'tier': None, 'version_label': None, 'group_name': None, 'tags': [], 'database': { 'username': '******', 'password': '******', 'engine': 'mysql', 'size': '10', 'instance': 'db.t2.micro', 'version': '5.6.35' }, 'vpc': { 'id': 'my-vpc-id', 'ec2subnets': 'subnet-1,subnet-2,subnet-3', 'elbsubnets': 'subnet-1,subnet-2,subnet-3', 'elbscheme': 'public', 'publicip': 'true', 'securitygroups': 'security-group-1,security-group-2', 'dbsubnets': 'subnet-1,subnet-2,subnet-3' }, 'elb_type': None, 'scale': None, } create_environment_request = requests.CreateEnvironmentRequest( **request_args) self.assertFalse(create_environment_request.compiled) self.assertEqual([], create_environment_request.option_settings) self.assertEqual( 'Environment created from the EB CLI using "eb create"', create_environment_request.description) self.assertIsNone(create_environment_request.scale) def test_create_environment__app_name_not_passed_in(self): with self.assertRaises(TypeError) as context_manager: requests.CreateEnvironmentRequest(env_name='my-env') self.assertEqual( 'CreateEnvironmentRequest requires key-word argument app_name', str(context_manager.exception)) def test_create_environment__env_name_not_passed_in(self): with self.assertRaises(TypeError) as context_manager: requests.CreateEnvironmentRequest(app_name='my-app') self.assertEqual( 'CreateEnvironmentRequest requires key-word argument env_name', str(context_manager.exception)) def test_create_environment__scale_is_not_an_int(self): with self.assertRaises(TypeError) as context_manager: requests.CreateEnvironmentRequest(app_name='my-app', env_name='my-env', scale='1') self.assertEqual('key-word argument scale must be of type int', str(context_manager.exception)) def test_add_option_setting(self): request = requests.CreateEnvironmentRequest(app_name='my-app', env_name='my-env') request.add_option_setting('MyNameSpace', 'MyOptionName', 'MyValue') self.assertEqual([{ 'Namespace': 'MyNameSpace', 'OptionName': 'MyOptionName', 'Value': 'MyValue' }], request.option_settings) def test_add_option_setting__with_resource(self): request = requests.CreateEnvironmentRequest(app_name='my-app', env_name='my-env') request.add_option_setting('MyNameSpace', 'MyOptionName', 'MyValue', resource='MyResource') self.assertEqual([{ 'Namespace': 'MyNameSpace', 'OptionName': 'MyOptionName', 'ResourceName': 'MyResource', 'Value': 'MyValue' }], request.option_settings) def test_compile_vpc_options__no_vpc_args(self): request = requests.CreateEnvironmentRequest(app_name='my-app', env_name='my-env') request_copy = copy.copy(request) request_copy.compile_vpc_options() self.assertEqual(request, request_copy) def test_compile_vpc_options(self): request_args = { 'app_name': 'ebcli-intTest-app', 'env_name': 'my-awesome-env', 'vpc': { 'id': 'my-vpc-id', 'ec2subnets': 'subnet-1,subnet-2,subnet-3', 'elbsubnets': 'subnet-1,subnet-2,subnet-3', 'elbscheme': 'public', 'publicip': 'true', 'securitygroups': 'security-group-1,security-group-2', 'dbsubnets': 'subnet-1,subnet-2,subnet-3' } } request = requests.CreateEnvironmentRequest(**request_args) self.assertEqual([], request.option_settings) request.compile_vpc_options() self.assertEqual([{ 'Namespace': 'aws:ec2:vpc', 'OptionName': 'VPCId', 'Value': 'my-vpc-id' }, { 'Namespace': 'aws:ec2:vpc', 'OptionName': 'AssociatePublicIpAddress', 'Value': 'true' }, { 'Namespace': 'aws:ec2:vpc', 'OptionName': 'ELBScheme', 'Value': 'public' }, { 'Namespace': 'aws:ec2:vpc', 'OptionName': 'ELBSubnets', 'Value': 'subnet-1,subnet-2,subnet-3' }, { 'Namespace': 'aws:ec2:vpc', 'OptionName': 'Subnets', 'Value': 'subnet-1,subnet-2,subnet-3' }, { 'Namespace': 'aws:autoscaling:launchconfiguration', 'OptionName': 'SecurityGroups', 'Value': 'security-group-1,security-group-2' }, { 'Namespace': 'aws:ec2:vpc', 'OptionName': 'DBSubnets', 'Value': 'subnet-1,subnet-2,subnet-3' }], request.option_settings) def test_compile_database_options__no_database_args(self): request = requests.CreateEnvironmentRequest(app_name='my-app', env_name='my-env') request_copy = copy.copy(request) request_copy.compile_database_options() self.assertEqual(request, request_copy) def test_compile_database_options(self): request_args = { 'app_name': 'ebcli-intTest-app', 'env_name': 'my-awesome-env', 'database': { 'username': '******', 'password': '******', 'engine': 'mysql', 'size': '10', 'instance': 'db.t2.micro', 'version': '5.6.35' } } request = requests.CreateEnvironmentRequest(**request_args) self.assertEqual([], request.option_settings) request.compile_database_options() self.assertEqual([{ 'Namespace': 'aws:rds:dbinstance', 'OptionName': 'DBPassword', 'Value': 'password' }, { 'Namespace': 'aws:rds:dbinstance', 'OptionName': 'DBUser', 'Value': 'root' }, { 'Namespace': 'aws:rds:dbinstance', 'OptionName': 'DBInstanceClass', 'Value': 'db.t2.micro' }, { 'Namespace': 'aws:rds:dbinstance', 'OptionName': 'DBAllocatedStorage', 'Value': '10' }, { 'Namespace': 'aws:rds:dbinstance', 'OptionName': 'DBEngine', 'Value': 'mysql' }, { 'Namespace': 'aws:rds:dbinstance', 'OptionName': 'DBEngineVersion', 'Value': '5.6.35' }, { 'Namespace': 'aws:rds:dbinstance', 'OptionName': 'DBDeletionPolicy', 'Value': 'Snapshot' }], request.option_settings) def test_compile_spot_options(self): request_args = { 'app_name': 'ebcli-intTest-app', 'env_name': 'my-awesome-env', 'enable_spot': 'true', 'instance_types': 't2.micro, t2.large', 'spot_max_price': '.5', } request = requests.CreateEnvironmentRequest(**request_args) self.assertEqual([], request.option_settings) request.compile_spot_options() self.assertEqual([ { 'Namespace': 'aws:ec2:instances', 'OptionName': 'EnableSpot', 'Value': 'true' }, { 'Namespace': 'aws:ec2:instances', 'OptionName': 'InstanceTypes', 'Value': 't2.micro, t2.large' }, { 'Namespace': 'aws:ec2:instances', 'OptionName': 'SpotMaxPrice', 'Value': '.5' }, ], request.option_settings) def test_common_options(self): request_args = { 'app_name': 'ebcli-intTest-app', 'cname': None, 'env_name': 'my-awesome-env', 'instance_profile': 'my-instance-profile', 'instance_type': 't2.micro', 'key_name': 'my-key-name', 'platform': self.solution, 'sample_application': False, 'service_role': 'my-service-role', 'single_instance': False, 'template_name': None, 'tier': 'webserver', 'version_label': None, 'group_name': None, 'tags': [], 'elb_type': 'application', 'scale': 10, } request = requests.CreateEnvironmentRequest(**request_args) request.compile_common_options() self.assertEqual([{ 'Namespace': 'aws:autoscaling:launchconfiguration', 'OptionName': 'IamInstanceProfile', 'Value': 'my-instance-profile' }, { 'Namespace': 'aws:elasticbeanstalk:environment', 'OptionName': 'ServiceRole', 'Value': 'my-service-role' }, { 'Namespace': 'aws:autoscaling:launchconfiguration', 'OptionName': 'InstanceType', 'Value': 't2.micro' }, { 'Namespace': 'aws:autoscaling:launchconfiguration', 'OptionName': 'EC2KeyName', 'Value': 'my-key-name' }, { 'Namespace': 'aws:autoscaling:asg', 'OptionName': 'MaxSize', 'Value': '10' }, { 'Namespace': 'aws:autoscaling:asg', 'OptionName': 'MinSize', 'Value': '10' }, { 'Namespace': 'aws:elasticbeanstalk:environment', 'OptionName': 'LoadBalancerType', 'Value': 'application' }], request.option_settings) def test_common_options__single_instance(self): request_args = { 'app_name': 'ebcli-intTest-app', 'env_name': 'my-awesome-env', 'single_instance': True, } request = requests.CreateEnvironmentRequest(**request_args) request.compile_common_options() self.assertEqual([{ 'Namespace': 'aws:elasticbeanstalk:environment', 'OptionName': 'EnvironmentType', 'Value': 'SingleInstance' }], request.option_settings) def test_add_client_defaults__uses_template_name_if_one_exists(self): request_args = { 'app_name': 'ebcli-intTest-app', 'env_name': 'my-awesome-env', 'template_name': 'my-saved-configuration-template-name', } request = requests.CreateEnvironmentRequest(**request_args) request_copy = copy.copy(request) request.add_client_defaults() self.assertEqual(request_copy, request) @mock.patch('ebcli.lib.ec2.has_default_vpc') def test_add_client_defaults(self, has_default_vpc_mock): has_default_vpc_mock.return_value = True request_args = { 'app_name': 'ebcli-intTest-app', 'cname': None, 'env_name': 'my-awesome-env', 'instance_profile': 'my-instance-profile', 'key_name': 'my-key-name', 'platform': self.solution, } request = requests.CreateEnvironmentRequest(**request_args) request.add_client_defaults() self.assertEqual([{ 'Namespace': 'aws:elasticbeanstalk:command', 'OptionName': 'BatchSize', 'Value': '30' }, { 'Namespace': 'aws:elasticbeanstalk:command', 'OptionName': 'BatchSizeType', 'Value': 'Percentage' }, { 'Namespace': 'aws:elb:policies', 'OptionName': 'ConnectionDrainingEnabled', 'Value': 'true' }, { 'Namespace': 'aws:elb:loadbalancer', 'OptionName': 'CrossZone', 'Value': 'true' }, { 'Namespace': 'aws:autoscaling:updatepolicy:rollingupdate', 'OptionName': 'RollingUpdateEnabled', 'Value': 'true' }, { 'Namespace': 'aws:autoscaling:updatepolicy:rollingupdate', 'OptionName': 'RollingUpdateType', 'Value': 'Health' }], request.option_settings) @mock.patch('ebcli.lib.ec2.has_default_vpc') def test_add_client_defaults__worker_tier(self, has_default_vpc_mock): has_default_vpc_mock.return_value = True request_args = { 'app_name': 'ebcli-intTest-app', 'cname': None, 'tier': tier.Tier('Worker', 'SQS/HTTP', ''), 'env_name': 'my-awesome-env', 'instance_profile': 'my-instance-profile', 'key_name': 'my-key-name', 'platform': self.solution, } request = requests.CreateEnvironmentRequest(**request_args) request.add_client_defaults() self.assertEqual([{ 'Namespace': 'aws:elasticbeanstalk:command', 'OptionName': 'BatchSize', 'Value': '30' }, { 'Namespace': 'aws:elasticbeanstalk:command', 'OptionName': 'BatchSizeType', 'Value': 'Percentage' }], request.option_settings) def test_compile_shared_lb_options(self): request_args = { 'elb_type': 'application', 'app_name': 'ebcli-intTest-app', 'env_name': 'my-awesome-env', 'shared_lb': 'arn:aws:elasticloadbalancing:us-east-1:881508045124:loadbalancer/app/alb-1/72074d479748b405', 'shared_lb_port': '100' } request = requests.CreateEnvironmentRequest(**request_args) self.assertEqual([], request.option_settings) request.compile_shared_lb_options() self.assertEqual([{ 'Namespace': 'aws:elasticbeanstalk:environment', 'OptionName': 'LoadBalancerIsShared', 'Value': 'true' }, { 'Namespace': 'aws:elbv2:loadbalancer', 'OptionName': 'SharedLoadBalancer', 'Value': 'arn:aws:elasticloadbalancing:us-east-1:881508045124:loadbalancer/app/alb-1/72074d479748b405' }, { 'Namespace': 'aws:elbv2:listener:100', 'OptionName': 'Rules', 'Value': 'default' }], request.option_settings) def test_get_standard_kwargs(self): pass