예제 #1
0
    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)
예제 #2
0
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
예제 #3
0
    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)
예제 #4
0
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