def test_can_create_default_deployer(): session = botocore.session.get_session() deployer = create_default_deployer(session, Config.create( project_dir='.', chalice_stage='dev', ), UI()) assert isinstance(deployer, Deployer)
def test_managed_layer_removed_if_no_deps(self): function = create_function_resource('myfunction') function.managed_layer = models.LambdaLayer( resource_name='managed-layer', layer_name='appname-dev-managed-layer', runtime='python2.7', deployment_package=models.DeploymentPackage( models.Placeholder.BUILD_STAGE ) ) lambda_packager = mock.Mock(spec=packager.BaseLambdaDeploymentPackager) layer_packager = mock.Mock(spec=packager.BaseLambdaDeploymentPackager) lambda_packager.create_deployment_package.return_value = 'package.zip' layer_packager.create_deployment_package.side_effect = \ packager.EmptyPackageError() config = Config.create(project_dir='.') p = ManagedLayerDeploymentPackager(lambda_packager, layer_packager) p.handle(config, function.managed_layer) p.handle(config, function) # If the deployment package for layers would result in an empty # deployment package, we expect that resource to be removed, it can't # be created on the service. assert function.managed_layer is None
def test_load_policy_from_disk_when_file_exists(app_policy, in_memory_osutils): previous_policy = '{"Statement": ["foo"]}' config = Config.create(project_dir='.') filename = os.path.join('.', '.chalice', 'policy-dev.json') in_memory_osutils.filemap[filename] = previous_policy loaded = app_policy.load_last_policy(config) assert loaded == json.loads(previous_policy)
def test_single_role_generated_for_default_config(self, sample_app_lambda_only): # The sample_app has one lambda function. # We'll add a few more and verify they all share the same role. @sample_app_lambda_only.lambda_function() def second(event, context): pass @sample_app_lambda_only.lambda_function() def third(event, context): pass config = Config.create(chalice_app=sample_app_lambda_only, project_dir='.', autogen_policy=True, api_gateway_stage='api') template = self.generate_template(config, 'dev') roles = [resource for resource in template['Resources'].values() if resource['Type'] == 'AWS::IAM::Role'] assert len(roles) == 1 # The lambda functions should all reference this role. functions = [ resource for resource in template['Resources'].values() if resource['Type'] == 'AWS::Serverless::Function' ] role_names = [ function['Properties']['Role'] for function in functions ] assert role_names == [ {'Fn::GetAtt': ['DefaultRole', 'Arn']}, {'Fn::GetAtt': ['DefaultRole', 'Arn']}, {'Fn::GetAtt': ['DefaultRole', 'Arn']}, ]
def test_can_generate_rest_api(self, sample_app_with_auth): config = Config.create(chalice_app=sample_app_with_auth, project_dir='.', api_gateway_stage='api', minimum_compression_size=100, ) template = self.generate_template(config, 'dev') resources = template['Resources'] # Lambda function should be created. assert resources['APIHandler']['Type'] == 'AWS::Serverless::Function' # Along with permission to invoke from API Gateway. assert resources['APIHandlerInvokePermission'] == { 'Type': 'AWS::Lambda::Permission', 'Properties': { 'Action': 'lambda:InvokeFunction', 'FunctionName': {'Ref': 'APIHandler'}, 'Principal': 'apigateway.amazonaws.com', 'SourceArn': { 'Fn::Sub': [ ('arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}' ':${RestAPIId}/*'), {'RestAPIId': {'Ref': 'RestAPI'}}]}}, } assert resources['RestAPI']['Type'] == 'AWS::Serverless::Api' assert resources['RestAPI']['Properties']['MinimumCompressionSize'] \ == 100 # We should also create the auth lambda function. assert resources['Myauth']['Type'] == 'AWS::Serverless::Function' # Along with permission to invoke from API Gateway. assert resources['MyauthInvokePermission'] == { 'Type': 'AWS::Lambda::Permission', 'Properties': { 'Action': 'lambda:InvokeFunction', 'FunctionName': {'Fn::GetAtt': ['Myauth', 'Arn']}, 'Principal': 'apigateway.amazonaws.com', 'SourceArn': { 'Fn::Sub': [ ('arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}' ':${RestAPIId}/*'), {'RestAPIId': {'Ref': 'RestAPI'}}]}}, } # Also verify we add the expected outputs when using # a Rest API. assert template['Outputs'] == { 'APIHandlerArn': { 'Value': { 'Fn::GetAtt': ['APIHandler', 'Arn'] } }, 'APIHandlerName': {'Value': {'Ref': 'APIHandler'}}, 'EndpointURL': { 'Value': { 'Fn::Sub': ( 'https://${RestAPI}.execute-api.' '${AWS::Region}.amazonaws.com/api/' ) } }, 'RestAPIId': {'Value': {'Ref': 'RestAPI'}} }
def test_deploy_delegates_properly(self): app = mock.Mock(spec=models.Application) resources = [mock.Mock(spec=models.Model)] api_calls = [mock.Mock(spec=APICall)] self.resource_builder.build.return_value = app self.deps_builder.build_dependencies.return_value = resources self.plan_stage.execute.return_value = api_calls self.executor.resource_values = {'foo': {'name': 'bar'}} deployer = self.create_deployer() config = Config.create(project_dir='.', chalice_app=self.chalice_app) result = deployer.deploy(config, 'dev') self.resource_builder.build.assert_called_with(config, 'dev') self.deps_builder.build_dependencies.assert_called_with(app) self.build_stage.execute.assert_called_with(config, resources) self.plan_stage.execute.assert_called_with(resources) self.sweeper.execute.assert_called_with(api_calls, config) self.executor.execute.assert_called_with(api_calls) expected_result = { 'resources': { 'foo': { 'name': 'bar' } }, 'schema_version': '2.0', 'backend': 'api', } self.recorder.record_results.assert_called_with( expected_result, 'dev', '.') assert result == expected_result
def test_deploy_errors_raises_chalice_error(self): self.resource_builder.build.side_effect = AWSClientError() deployer = self.create_deployer() config = Config.create(project_dir='.', chalice_app=self.chalice_app) with pytest.raises(ChaliceDeploymentError): deployer.deploy(config, 'dev')
def test_can_generate_layer_package(self): function = create_function_resource('myfunction') function.managed_layer = models.LambdaLayer( resource_name='managed-layer', layer_name='appname-dev-managed-layer', runtime='python2.7', deployment_package=models.DeploymentPackage( models.Placeholder.BUILD_STAGE)) lambda_packager = mock.Mock(spec=packager.BaseLambdaDeploymentPackager) layer_packager = mock.Mock(spec=packager.BaseLambdaDeploymentPackager) lambda_packager.create_deployment_package.return_value = 'package.zip' layer_packager.create_deployment_package.return_value = ( 'package-layer.zip') config = Config.create(project_dir='.') p = ManagedLayerDeploymentPackager(lambda_packager, layer_packager) p.handle(config, function.managed_layer) p.handle(config, function) assert function.deployment_package.filename == 'package.zip' lambda_packager.create_deployment_package.assert_called_with( '.', config.lambda_python_version) assert function.managed_layer.deployment_package.filename == ( 'package-layer.zip') layer_packager.create_deployment_package.assert_called_with( '.', config.lambda_python_version)
def config_obj(sample_app): config = Config.create( chalice_app=sample_app, stage='dev', api_gateway_stage='api', ) return config
def test_no_injection_when_values_are_set(self): injector = InjectDefaults( lambda_timeout=100, lambda_memory_size=512, ) function = models.LambdaFunction( # The timeout/memory_size are set to # None, so the injector should fill them # in the with the default values above. timeout=1, memory_size=1, resource_name='foo', function_name='app-stage-foo', environment_variables={}, runtime='python2.7', handler='app.app', tags={}, deployment_package=None, role=None, security_group_ids=[], subnet_ids=[], layers=[], reserved_concurrency=None, ) config = Config.create() injector.handle(config, function) assert function.timeout == 1 assert function.memory_size == 1
def test_config_create_method(): c = Config.create(app_name='foo') assert c.app_name == 'foo' # Otherwise attributes default to None meaning 'not set'. assert c.lambda_arn is None assert c.profile is None assert c.stage is None
def test_deployer_returns_deployed_resources(sample_app): cfg = Config.create( chalice_stage='dev', chalice_app=sample_app, project_dir='.', ) lambda_deploy = mock.Mock(spec=LambdaDeployer) apig_deploy = mock.Mock(spec=APIGatewayDeployer) apig_deploy.deploy.return_value = ('api_id', 'region', 'stage') lambda_deploy.deploy.return_value = { 'api_handler_name': 'lambda_function', 'api_handler_arn': 'my_lambda_arn', } d = Deployer(apig_deploy, lambda_deploy) deployed_values = d.deploy(cfg) assert deployed_values == { 'dev': { 'backend': 'api', 'api_handler_arn': 'my_lambda_arn', 'api_handler_name': 'lambda_function', 'rest_api_id': 'api_id', 'api_gateway_stage': 'stage', 'region': 'region', 'chalice_version': chalice_version, } }
def test_tags_specified_does_not_override_chalice_tag(self): c = Config.create(chalice_stage='dev', app_name='myapp', tags={'aws-chalice': 'attempted-override'}) assert c.tags == { 'aws-chalice': 'version=%s:stage=dev:app=myapp' % chalice_version, }
def test_can_create_app_packager_with_no_autogen(): # We can't actually observe a change here, but we want # to make sure the function can handle this param being # False. config = Config.create(autogen_policy=False) packager = package.create_app_packager(config) assert isinstance(packager, package.AppPackager)
def test_validation_error_if_no_role_provided_when_manage_false(sample_app): # We're indicating that we should not be managing the # IAM role, but we're not giving a role ARN to use. # This is a validation error. config = Config.create(chalice_app=sample_app, manage_iam_role=False) with pytest.raises(ValueError): validate_configuration(config)
def test_tags_specified_does_not_override_chalice_tag(self): c = Config.create( chalice_stage='dev', app_name='myapp', tags={'aws-chalice': 'attempted-override'}) assert c.tags == { 'aws-chalice': 'version=%s:stage=dev:app=myapp' % chalice_version, }
def test_lambda_deployer_with_tags(self, sample_app): cfg = Config.create(chalice_stage='dev', app_name='myapp', chalice_app=sample_app, manage_iam_role=False, iam_role_arn='role-arn', project_dir='.', tags={'mykey': 'myvalue'}) deployer = LambdaDeployer(self.aws_client, self.packager, None, self.osutils, self.app_policy) deployer.deploy(cfg, None, 'dev') self.aws_client.create_function.assert_called_with( function_name='myapp-dev', role_arn='role-arn', zip_contents=b'package contents', runtime=cfg.lambda_python_version, tags={ 'aws-chalice': 'version=%s:stage=dev:app=myapp' % (chalice_version), 'mykey': 'myvalue' }, environment_variables={}, timeout=60, memory_size=128)
def test_lambda_deployer_initial_deploy(app_policy, sample_app): osutils = InMemoryOSUtils({'packages.zip': b'package contents'}) aws_client = mock.Mock(spec=TypedAWSClient) aws_client.create_function.return_value = 'lambda-arn' packager = mock.Mock(LambdaDeploymentPackager) packager.create_deployment_package.return_value = 'packages.zip' cfg = Config.create(chalice_stage='dev', app_name='myapp', chalice_app=sample_app, manage_iam_role=False, iam_role_arn='role-arn', project_dir='.', environment_variables={"FOO": "BAR"}, lambda_timeout=120, lambda_memory_size=256, tags={'mykey': 'myvalue'}) d = LambdaDeployer(aws_client, packager, None, osutils, app_policy) deployed = d.deploy(cfg, None, 'dev') assert deployed == { 'api_handler_arn': 'lambda-arn', 'api_handler_name': 'myapp-dev', } aws_client.create_function.assert_called_with( function_name='myapp-dev', role_arn='role-arn', zip_contents=b'package contents', environment_variables={"FOO": "BAR"}, runtime=cfg.lambda_python_version, tags={ 'aws-chalice': 'version=%s:stage=dev:app=myapp' % chalice_version, 'mykey': 'myvalue' }, timeout=120, memory_size=256)
def test_prompted_on_runtime_change_can_reject_change(app_policy, sample_app): osutils = InMemoryOSUtils({'packages.zip': b'package contents'}) aws_client = mock.Mock(spec=TypedAWSClient) packager = mock.Mock(spec=LambdaDeploymentPackager) packager.deployment_package_filename.return_value = 'packages.zip' aws_client.lambda_function_exists.return_value = True aws_client.get_function_configuration.return_value = { 'Runtime': 'python1.0', } aws_client.update_function.return_value = {"FunctionArn": "myarn"} cfg = Config.create( chalice_stage='dev', chalice_app=sample_app, manage_iam_role=False, app_name='appname', iam_role_arn=True, project_dir='./myproject', environment_variables={"FOO": "BAR"}, ) prompter = mock.Mock(spec=NoPrompt) prompter.confirm.side_effect = RuntimeError("Aborted") d = LambdaDeployer(aws_client, packager, prompter, osutils, app_policy) # Doing a lambda deploy with a different runtime: lambda_function_name = 'lambda_function_name' deployed = DeployedResources('api', 'api_handler_arn', lambda_function_name, None, 'dev', None, None) with pytest.raises(RuntimeError): d.deploy(cfg, deployed, 'dev') assert not packager.inject_latest_app.called assert not aws_client.update_function.called assert prompter.confirm.called message = prompter.confirm.call_args[0][0] assert 'runtime will change' in message
def test_app_policy_generator_vpc_policy(): config = Config.create(subnet_ids=['sn1', 'sn2'], security_group_ids=['sg1', 'sg2'], project_dir='.') generator = AppPolicyGenerator(OsUtilsMock()) policy = generator.generate_policy(config) assert policy == { 'Statement': [ { 'Action': [ 'logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents' ], 'Effect': 'Allow', 'Resource': 'arn:aws:logs:*:*:*' }, { 'Action': [ 'ec2:CreateNetworkInterface', 'ec2:DescribeNetworkInterfaces', 'ec2:DetachNetworkInterface', 'ec2:DeleteNetworkInterface' ], 'Effect': 'Allow', 'Resource': '*' }, ], 'Version': '2012-10-17' }
def test_can_create_deployer_with_layer_builds(): session = botocore.session.get_session() deployer = create_default_deployer(session, Config.create( project_dir='.', chalice_stage='dev', automatic_layer=True, ), UI()) assert isinstance(deployer, Deployer)
def test_error_raised_if_file_policy_not_exists(self): p = self.create_policy_generator() policy = models.FileBasedIAMPolicy( filename='foo.json', document=models.Placeholder.BUILD_STAGE) self.osutils.get_file_contents.side_effect = IOError() with pytest.raises(RuntimeError): p.handle(Config.create(), policy)
def test_policy_autogenerated_when_enabled(app_policy, in_memory_osutils): cfg = Config.create(autogen_policy=True, project_dir='.') in_memory_osutils.filemap['./app.py'] = '' generated = app_policy.generate_policy_from_app_source(cfg) # We don't actually need to validate the exact policy, we'll just # check that it looks ok. assert 'Statement' in generated assert 'Version' in generated
def test_no_policy_generated_when_disabled_in_config(app_policy, in_memory_osutils): previous_policy = '{"Statement": ["foo"]}' filename = os.path.join('.', '.chalice', 'policy.json') in_memory_osutils.filemap[filename] = previous_policy cfg = Config.create(autogen_policy=False, project_dir='.') generated = app_policy.generate_policy_from_app_source(cfg) assert generated == json.loads(previous_policy)
def test_can_generate_rest_api(self, sample_app_with_auth): config = Config.create(chalice_app=sample_app_with_auth, project_dir='.', minimum_compression_size=8192, api_gateway_endpoint_type='PRIVATE', api_gateway_endpoint_vpce='vpce-abc123', app_name='sample_app', api_gateway_stage='api') template = self.generate_template(config, 'dev') resources = template['resource'] # Lambda function should be created. assert resources['aws_lambda_function'] # Along with permission to invoke from API Gateway. assert list(resources['aws_lambda_permission'].values())[0] == { 'function_name': 'sample_app-dev', 'action': 'lambda:InvokeFunction', 'principal': 'apigateway.amazonaws.com', 'source_arn': ('${aws_api_gateway_rest_api.rest_api.execution_arn}/*') } assert resources['aws_api_gateway_rest_api'] assert resources['aws_api_gateway_rest_api']['rest_api']['policy'] assert resources['aws_api_gateway_rest_api']['rest_api'][ 'minimum_compression_size'] == 8192 assert resources['aws_api_gateway_rest_api']['rest_api'][ 'endpoint_configuration'] == { 'types': ['PRIVATE'] } assert 'aws_api_gateway_stage' not in resources assert resources['aws_api_gateway_deployment']['rest_api'] == { 'rest_api_id': '${aws_api_gateway_rest_api.rest_api.id}', 'stage_description': ('${md5(data.template_file.chalice_api_swagger.rendered)}'), 'stage_name': 'api' } # We should also create the auth lambda function. assert 'myauth' in resources['aws_lambda_function'] # Along with permission to invoke from API Gateway. assert resources['aws_lambda_permission']['myauth_invoke'] == { 'action': 'lambda:InvokeFunction', 'function_name': 'sample_app-dev-myauth', 'principal': 'apigateway.amazonaws.com', 'source_arn': ('${aws_api_gateway_rest_api.rest_api.execution_arn}/*') } # Also verify we add the expected outputs when using # a Rest API. assert template['output'] == { 'EndpointURL': { 'value': '${aws_api_gateway_deployment.rest_api.invoke_url}' } }
def new_project(project_name, profile): # type: (str, str) -> None if project_name is None: project_name = getting_started_prompt(click) if os.path.isdir(project_name): click.echo("Directory already exists: %s" % project_name, err=True) raise click.Abort() create_new_project_skeleton(project_name, profile) validate_python_version(Config.create())
def test_queue_arn_must_be_arn(sample_app): @sample_app.on_sqs_message( queue_arn='https://sqs.us-west-2.amazonaws.com/12345/myqueue') def handler(event): pass config = Config.create(chalice_app=sample_app) with pytest.raises(ValueError): validate_configuration(config)
def test_validation_errors_raise_failure(self): @self.chalice_app.route('') def bad_route_empty_string(): return {} deployer = self.create_deployer() config = Config.create(project_dir='.', chalice_app=self.chalice_app) with pytest.raises(ChaliceDeploymentError): deployer.deploy(config, 'dev')
def test_will_create_outdir_if_needed(tmpdir): appdir = _create_app_structure(tmpdir) outdir = str(appdir.join('outdir')) config = Config.create(project_dir=str(appdir), chalice_app=sample_app()) p = package.create_app_packager(config) p.package_app(config, str(outdir)) contents = os.listdir(str(outdir)) assert 'deployment.zip' in contents assert 'sam.json' in contents
def test_preconfigured_policy_proxies(): policy_gen = mock.Mock(spec=ApplicationPolicyHandler) config = Config.create(project_dir='project_dir', autogen_policy=False) generator = package.PreconfiguredPolicyGenerator(config, policy_gen=policy_gen) policy_gen.generate_policy_from_app_source.return_value = {'policy': True} policy = generator.generate_policy_from_app_source() policy_gen.generate_policy_from_app_source.assert_called_with(config) assert policy == {'policy': True}
def test_can_generate_rest_api(self, sample_app_with_auth): config = Config.create(chalice_app=sample_app_with_auth, project_dir='.', api_gateway_stage='api') template = self.generate_template(config, 'dev') resources = template['Resources'] # Lambda function should be created. assert resources['APIHandler']['Type'] == 'AWS::Serverless::Function' # Along with permission to invoke from API Gateway. assert resources['APIHandlerInvokePermission'] == { 'Type': 'AWS::Lambda::Permission', 'Properties': { 'Action': 'lambda:InvokeFunction', 'FunctionName': {'Ref': 'APIHandler'}, 'Principal': 'apigateway.amazonaws.com', 'SourceArn': { 'Fn::Sub': [ ('arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}' ':${RestAPIId}/*'), {'RestAPIId': {'Ref': 'RestAPI'}}]}}, } assert resources['RestAPI']['Type'] == 'AWS::Serverless::Api' # We should also create the auth lambda function. assert resources['Myauth']['Type'] == 'AWS::Serverless::Function' # Along with permission to invoke from API Gateway. assert resources['MyauthInvokePermission'] == { 'Type': 'AWS::Lambda::Permission', 'Properties': { 'Action': 'lambda:InvokeFunction', 'FunctionName': {'Fn::GetAtt': ['Myauth', 'Arn']}, 'Principal': 'apigateway.amazonaws.com', 'SourceArn': { 'Fn::Sub': [ ('arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}' ':${RestAPIId}/*'), {'RestAPIId': {'Ref': 'RestAPI'}}]}}, } # Also verify we add the expected outputs when using # a Rest API. assert template['Outputs'] == { 'APIHandlerArn': { 'Value': { 'Fn::GetAtt': ['APIHandler', 'Arn'] } }, 'APIHandlerName': {'Value': {'Ref': 'APIHandler'}}, 'EndpointURL': { 'Value': { 'Fn::Sub': ( 'https://${RestAPI}.execute-api.' '${AWS::Region}.amazonaws.com/api/' ) } }, 'RestAPIId': {'Value': {'Ref': 'RestAPI'}} }
def test_policy_loaded_from_file_if_needed(self): p = self.create_policy_generator() policy = models.FileBasedIAMPolicy( filename='foo.json', document=models.Placeholder.BUILD_STAGE) self.osutils.get_file_contents.return_value = '{"iam": "policy"}' p.handle(Config.create(), policy) assert policy.document == {'iam': 'policy'} self.osutils.get_file_contents.assert_called_with('foo.json')
def test_invokes_policy_generator(self): generator = mock.Mock(spec=AppPolicyGenerator) generator.generate_policy.return_value = {'policy': 'doc'} policy = models.AutoGenIAMPolicy(models.Placeholder.BUILD_STAGE) config = Config.create() p = self.create_policy_generator(generator) p.handle(config, policy) assert policy.document == {'policy': 'doc'}
def test_preconfigured_policy_proxies(): policy_gen = mock.Mock(spec=ApplicationPolicyHandler) config = Config.create(project_dir='project_dir', autogen_policy=False) generator = package.PreconfiguredPolicyGenerator( config, policy_gen=policy_gen) policy_gen.generate_policy_from_app_source.return_value = { 'policy': True} policy = generator.generate_policy_from_app_source() policy_gen.generate_policy_from_app_source.assert_called_with(config) assert policy == {'policy': True}
def test_validate_names_using_name_kwarg(sample_app): @sample_app.authorizer(name='duplicate') def foo(auth_request): pass @sample_app.lambda_function(name='duplicate') def bar(event): pass config = Config.create(chalice_app=sample_app, manage_iam_role=False) with pytest.raises(ValueError): validate_unique_function_names(config)
def test_validate_names_across_function_types(sample_app): @sample_app.lambda_function() def foo(event, context): pass @sample_app.schedule('rate(1 hour)', name='foo') def bar(event): pass config = Config.create(chalice_app=sample_app, manage_iam_role=False) with pytest.raises(ValueError): validate_unique_function_names(config)
def test_default_function_memory_size(sample_app, mock_swagger_generator, mock_policy_generator): p = package.SAMTemplateGenerator( mock_swagger_generator, mock_policy_generator) mock_swagger_generator.generate_swagger.return_value = { 'swagger': 'document' } config = Config.create(chalice_app=sample_app, api_gateway_stage='dev') template = p.generate_sam_template(config) properties = template['Resources']['APIHandler']['Properties'] assert properties['MemorySize'] == 128
def test_will_create_outdir_if_needed(tmpdir): appdir = _create_app_structure(tmpdir) outdir = str(appdir.join('outdir')) default_params = {'autogen_policy': True} config = Config.create(project_dir=str(appdir), chalice_app=sample_app(), **default_params) p = package.create_app_packager(config) p.package_app(config, str(outdir), 'dev') contents = os.listdir(str(outdir)) assert 'deployment.zip' in contents assert 'sam.json' in contents
def test_sam_injects_swagger_doc(sample_app, mock_swagger_generator, mock_policy_generator): p = package.SAMTemplateGenerator(mock_swagger_generator, mock_policy_generator) mock_swagger_generator.generate_swagger.return_value = { 'swagger': 'document' } config = Config.create(chalice_app=sample_app, api_gateway_stage='dev') template = p.generate_sam_template(config) properties = template['Resources']['RestAPI']['Properties'] assert properties['DefinitionBody'] == {'swagger': 'document'}
def test_timeout_added_to_function(sample_app, mock_swagger_generator, mock_policy_generator): p = package.SAMTemplateGenerator( mock_swagger_generator, mock_policy_generator) mock_swagger_generator.generate_swagger.return_value = { 'swagger': 'document' } config = Config.create(chalice_app=sample_app, api_gateway_stage='dev', app_name='myapp', lambda_timeout=240) template = p.generate_sam_template(config) properties = template['Resources']['APIHandler']['Properties'] assert properties['Timeout'] == 240
def test_can_create_app_packager_with_no_autogen(tmpdir): appdir = _create_app_structure(tmpdir) outdir = tmpdir.mkdir('outdir') config = Config.create(project_dir=str(appdir), chalice_app=sample_app()) p = package.create_app_packager(config) p.package_app(config, str(outdir)) # We're not concerned with the contents of the files # (those are tested in the unit tests), we just want to make # sure they're written to disk and look (mostly) right. contents = os.listdir(str(outdir)) assert 'deployment.zip' in contents assert 'sam.json' in contents
def test_sam_generates_sam_template_basic(sample_app, mock_swagger_generator, mock_policy_generator): p = package.SAMTemplateGenerator(mock_swagger_generator, mock_policy_generator) config = Config.create(chalice_app=sample_app, api_gateway_stage='dev') template = p.generate_sam_template(config, 'code-uri') # Verify the basic structure is in place. The specific parts # are validated in other tests. assert template['AWSTemplateFormatVersion'] == '2010-09-09' assert template['Transform'] == 'AWS::Serverless-2016-10-31' assert 'Outputs' in template assert 'Resources' in template
def test_validate_unique_lambda_function_names(sample_app): @sample_app.lambda_function() def foo(event, context): pass # This will cause a validation error because # 'foo' is already registered as a lambda function. @sample_app.lambda_function(name='foo') def bar(event, context): pass config = Config.create(chalice_app=sample_app, manage_iam_role=False) with pytest.raises(ValueError): validate_unique_function_names(config)
def test_helpful_error_message_on_s3_event(self, sample_app): @sample_app.on_s3_event(bucket='foo') def handler(event): pass config = Config.create(chalice_app=sample_app, project_dir='.', api_gateway_stage='api') with pytest.raises(NotImplementedError) as excinfo: self.generate_template(config, 'dev') # Should mention the decorator name. assert '@app.on_s3_event' in str(excinfo.value) # Should mention you can use `chalice deploy`. assert 'chalice deploy' in str(excinfo.value)
def test_chalice_tag_added_to_function(sample_app, mock_swagger_generator, mock_policy_generator): p = package.SAMTemplateGenerator( mock_swagger_generator, mock_policy_generator) mock_swagger_generator.generate_swagger.return_value = { 'swagger': 'document' } config = Config.create(chalice_app=sample_app, api_gateway_stage='dev', app_name='myapp') template = p.generate_sam_template(config) properties = template['Resources']['APIHandler']['Properties'] assert properties['Tags'] == { 'aws-chalice': 'version=%s:stage=dev:app=myapp' % chalice_version}
def test_env_vars_set_in_local(runner, mock_cli_factory, monkeypatch): local_server = mock.Mock(spec=local.LocalDevServer) mock_cli_factory.create_local_server.return_value = local_server mock_cli_factory.create_config_obj.return_value = Config.create( project_dir='.', environment_variables={'foo': 'bar'}) actual_env = {} monkeypatch.setattr(os, 'environ', actual_env) with runner.isolated_filesystem(): cli.create_new_project_skeleton('testproject') os.chdir('testproject') _run_cli_command(runner, cli.local, [], cli_factory=mock_cli_factory) assert actual_env['foo'] == 'bar'
def test_can_lazy_load_chalice_app(): app = Chalice(app_name='foo') calls = [] def call_recorder(*args, **kwargs): calls.append((args, kwargs)) return app c = Config.create(chalice_app=call_recorder) # Accessing the property multiple times will only # invoke the call once. assert isinstance(c.chalice_app, Chalice) assert isinstance(c.chalice_app, Chalice) assert len(calls) == 1
def test_role_arn_added_to_function(sample_app, mock_swagger_generator, mock_policy_generator): p = package.SAMTemplateGenerator( mock_swagger_generator, mock_policy_generator) mock_swagger_generator.generate_swagger.return_value = { 'swagger': 'document' } config = Config.create( chalice_app=sample_app, api_gateway_stage='dev', app_name='myapp', manage_iam_role=False, iam_role_arn='role-arn') template = p.generate_sam_template(config) properties = template['Resources']['APIHandler']['Properties'] assert properties['Role'] == 'role-arn' assert 'Policies' not in properties
def test_vpc_config_added_to_function(self, sample_app_lambda_only): config = Config.create(chalice_app=sample_app_lambda_only, project_dir='.', autogen_policy=True, api_gateway_stage='api', security_group_ids=['sg1', 'sg2'], subnet_ids=['sn1', 'sn2']) template = self.generate_template(config, 'dev') resources = template['Resources'].values() lambda_fns = [resource for resource in resources if resource['Type'] == 'AWS::Serverless::Function'] assert len(lambda_fns) == 1 vpc_config = lambda_fns[0]['Properties']['VpcConfig'] assert vpc_config['SubnetIds'] == ['sn1', 'sn2'] assert vpc_config['SecurityGroupIds'] == ['sg1', 'sg2']
def test_endpoint_url_reflects_apig_stage(sample_app, mock_swagger_generator, mock_policy_generator): p = package.SAMTemplateGenerator( mock_swagger_generator, mock_policy_generator) mock_swagger_generator.generate_swagger.return_value = { 'swagger': 'document' } config = Config.create( chalice_app=sample_app, api_gateway_stage='prod', ) template = p.generate_sam_template(config) endpoint_url = template['Outputs']['EndpointURL']['Value']['Fn::Sub'] assert endpoint_url == ( 'https://${RestAPI}.execute-api.${AWS::Region}.amazonaws.com/prod/')
def test_sam_injects_policy(sample_app, mock_swagger_generator, mock_policy_generator): p = package.SAMTemplateGenerator(mock_swagger_generator, mock_policy_generator) mock_policy_generator.generate_policy_from_app_source.return_value = { 'iam': 'policy', } config = Config.create(chalice_app=sample_app, api_gateway_stage='dev') template = p.generate_sam_template(config) assert template['Resources']['APIHandler']['Properties']['Policies'] == [{ 'iam': 'policy', }] assert 'Role' not in template['Resources']['APIHandler']['Properties']
def test_maps_python_version(sample_app, mock_swagger_generator, mock_policy_generator): p = package.SAMTemplateGenerator( mock_swagger_generator, mock_policy_generator) mock_swagger_generator.generate_swagger.return_value = { 'swagger': 'document' } config = Config.create( chalice_app=sample_app, api_gateway_stage='dev', ) template = p.generate_sam_template(config) expected = config.lambda_python_version actual = template['Resources']['APIHandler']['Properties']['Runtime'] assert actual == expected
def test_sam_generates_sam_template_basic(self, sample_app): config = Config.create(chalice_app=sample_app, project_dir='.', api_gateway_stage='api') template = self.generate_template(config, 'dev') # Verify the basic structure is in place. The specific parts # are validated in other tests. assert template['AWSTemplateFormatVersion'] == '2010-09-09' assert template['Transform'] == 'AWS::Serverless-2016-10-31' assert 'Outputs' in template assert 'Resources' in template assert list(sorted(template['Resources'])) == [ 'APIHandler', 'APIHandlerInvokePermission', # This casing on the ApiHandlerRole name is unfortunate, but the 3 # other resources in this list are hardcoded from the old deployer. 'ApiHandlerRole', 'RestAPI', ]
def _deploy_app(): if not os.path.isdir(CHALICE_DIR): os.makedirs(CHALICE_DIR) session = botocore.session.get_session() config = Config.create( project_dir=PROJECT_DIR, app_name='smoketestapp', stage_name='dev', autogen_policy=True, chalice_app=load_chalice_app(PROJECT_DIR), ) d = deployer.create_default_deployer(session=session) rest_api_id, region_name, stage = d.deploy(config) url = ( "https://{api_id}.execute-api.{region}.amazonaws.com/{stage}/".format( api_id=rest_api_id, region=region_name, stage=stage)) application = SmokeTestApplication(url, rest_api_id, region_name, 'smoketestapp') return application
def test_can_inject_environment_vars(sample_app, mock_swagger_generator, mock_policy_generator): p = package.SAMTemplateGenerator( mock_swagger_generator, mock_policy_generator) mock_swagger_generator.generate_swagger.return_value = { 'swagger': 'document' } config = Config.create( chalice_app=sample_app, api_gateway_stage='dev', environment_variables={ 'FOO': 'BAR' } ) template = p.generate_sam_template(config) properties = template['Resources']['APIHandler']['Properties'] assert 'Environment' in properties assert properties['Environment']['Variables'] == {'FOO': 'BAR'}
def test_can_package_sns_arn_handler(self, sample_app): arn = 'arn:aws:sns:space-leo-1:1234567890:foo' @sample_app.on_sns_message(topic=arn) def handler(event): pass config = Config.create(chalice_app=sample_app, project_dir='.', api_gateway_stage='api') template = self.generate_template(config, 'dev') sns_handler = template['Resources']['Handler'] assert sns_handler['Properties']['Events'] == { 'HandlerSnsSubscription': { 'Type': 'SNS', 'Properties': { 'Topic': arn, } } }