Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add VPC support to lambda functions #837

Merged
merged 12 commits into from
May 17, 2018
Merged

Conversation

jamesls
Copy link
Member

@jamesls jamesls commented May 15, 2018

This adds VPC support to lambda functions in chalice. It pulls in #673 and fleshes out the rest of the deployer support for VPC. Notably it makes a few changes:

  • The VPC configuration can be added per lambda function and per stage.
  • The role management is now moved into the deployer and based on the models. In order to do this I had to add a traits flag to the AutogenPolicy which flags which policies it needs to add.
  • Update the awsclient to handle removal of VPC configuration (that is, if you configure a function for VPC and then remove it from your config.json, it will appropriately remove VPC configuration).
  • Added a few more examples and docs on how to configure VPC.

This PR also triggered a few linter warnings so I cleaned up some of the code (as a separate commit). There was a whole class (with tests) that was no longer used. It was a part of the legacy deployer.

Thanks to @lime-green for the original PR.

@codecov-io
Copy link

codecov-io commented May 15, 2018

Codecov Report

Merging #837 into master will increase coverage by 0.01%.
The diff coverage is 98.61%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #837      +/-   ##
=========================================
+ Coverage   94.79%   94.8%   +0.01%     
=========================================
  Files          21      21              
  Lines        3707    3735      +28     
  Branches      478     486       +8     
=========================================
+ Hits         3514    3541      +27     
  Misses        130     130              
- Partials       63      64       +1
Impacted Files Coverage Δ
chalice/app.py 96.86% <ø> (ø) ⬆️
chalice/deploy/planner.py 94.01% <ø> (ø) ⬆️
chalice/deploy/deployer.py 98.48% <100%> (-0.04%) ⬇️
chalice/package.py 100% <100%> (ø) ⬆️
chalice/deploy/models.py 99.15% <100%> (+0.02%) ⬆️
chalice/policy.py 92.1% <100%> (+0.21%) ⬆️
chalice/constants.py 100% <100%> (ø) ⬆️
chalice/config.py 97.98% <100%> (+0.05%) ⬆️
chalice/awsclient.py 92.14% <95.83%> (+0.18%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46cbb73...30ba203. Read the comment docs.

Copy link
Contributor

@stealthycoin stealthycoin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good to me, just a few questions.

@@ -11,6 +11,7 @@ CHANGELOG
* Update ``policies.json`` file
(`#817 <https://github.com/aws/chalice/issues/817>`__)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a changelog entry

will be applied to that function. These are the configuration options
that can be applied per function:

* ``iam_policy_file``
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be nice to have their default values listed somewhere if they are omitted.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a reference back to the stage config, these are described in the section above.

resource.document = self._policy_gen.generate_policy(config)
policy = self._policy_gen.generate_policy(config)
if models.RoleTraits.VPC_NEEDED in resource.traits:
policy['Statement'].append(VPC_ATTACH_POLICY)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about replacing the * in VPC_ATTACH_POLICY resources section with all the specific ARNs from the config?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're different ARNs. The config ARNs are for the subnet/sg ids, but the VPC policy is to CRUD ENIs.

@jamesls
Copy link
Member Author

jamesls commented May 15, 2018

@stealthycoin Feedback incorporated.

Copy link
Contributor

@kyleknap kyleknap left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. A couple of things that I ran into while playing around with this. I may have some more...

  1. We should add validation that both subnet_ids and security_group_ids are present in the config. Otherwise, chalice will it ignore it and deploy the lambda function without letting the user know about the issue.

  2. Trying it out I think there is an eventual consistency issue where: if you deploy the lambda function once (with no VPC configured), and then again (waiting a minute or so) it will error out with the following error:

ChaliceDeploymentError: ERROR - While deploying your chalice application, received the following error:

 An error occurred (InvalidParameterValueException) when calling the 
 UpdateFunctionConfiguration operation: The provided execution role does not 
 have permissions to call CreateNetworkInterface on EC2

However running deploy again succeeds.

@jamesls
Copy link
Member Author

jamesls commented May 16, 2018

So (1) ends up being a little tricker than I thought and there's a number of edge cases. I was initially adding validation to the chalice.deploy.validate module. It seemed better to fast fail before we even make it to the deployer. However, it didn't seem like the right thing to do. For example, what about this:

        {
            'app_name': 'myapp',
            'security_group_ids': ['sg-1'],
            'stages': {
                'dev': {
                    'lambda_functions': {
                        'foo': {'subnet_ids': ['sn-1']},
                        'bar': {'subnet_ids': ['sn-1']},
                    }
                },
                "prod": {},
            }
        }

Deploying to dev is fine, but the prod stage would "fail" because there's no subnet_ids defined. But it seems wrong to fail config validation because this is only an issue if you deploy to prod. The dev stage is actually fine so I would expect chalice deploy --stage dev to just work. Also, what if I had:

        {
            'app_name': 'myapp',
            'security_group_ids': ['sg-1'],
            'stages': {
                'dev': {
                    'lambda_functions': {
                        'foo': {'subnet_ids': ['sn-1']},
                        'bar': {},
                    }
                }
            }
        }

but my actual app only has a foo function:

@app.lambda_function()
def foo(...): pass

It doesn't seem like we should say the config is invalid because you chalice deploy will actually do what we want: deploy the lambda function in a VPC.

I think what I'm going to do is just make this a deployer error. If we try to create a models.LambdaFunction and we're missing a subnet id or a sg then we'll fail. The error messages won't be as good but I don't want to fail validation when it doesn't actually matter.

What do you think?

This was initially done in chalice.deploy.validate, but it
produces too many false positives.  This way, we only fail if we try
to construct a resource with invalid params.  This does mean we might
have latent issues that we'll only find about when we try to deploy our
app, but that seems better than failing validation when it wouldn't
actually affect a deploy.
@jamesls
Copy link
Member Author

jamesls commented May 16, 2018

@kyleknap feedback incorporated. I ended up just adding the validation in the AppGraphBuilder. That way you can catch the error with chalice package and you don't actually have to do a deploy. The error message you get is:

ChaliceBuildError: Invalid VPC params for function 'foo', in order to configure VPC for a Lambda function, you must provide the subnet_ids as well as the security_group_ids, got subnet_ids: [u'subnet-1234', u'subnet-2345'], security_group_ids: []

Copy link
Contributor

@kyleknap kyleknap left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Just had a small comment and a follow up question. So if the validation errors were in chalice.deploy.validate instead, what specific features would you get these false positives? Like is it related to getting an error when running chalice local for these misconfigured stages/functions?

@@ -559,9 +581,10 @@ def _build_lambda_function(self,
config.app_name, config.chalice_stage, name)
security_group_ids = config.security_group_ids
subnet_ids = config.subnet_ids
if security_group_ids is None or subnet_ids is None:
if security_group_ids is None and subnet_ids is None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this if statement still needed? It looks like it gets handled in _get_vpc_params and is not necessarily needed.

@jamesls
Copy link
Member Author

jamesls commented May 16, 2018

@kyleknap The two examples I gave in my previous comments give you false positives. One was for an invalid stage that you're not deploying to and one was config for a non-existent function (defined in config but not in app). By false positive, I mean the "config validation" would fail, but if you actually were to somehow turn off validation and just let chalice deploy (or whatever command) go through it would actually deploy correctly.
This happens anytime the config is loaded too which is just about every command, local, package, deploy, etc.

Copy link
Contributor

@kyleknap kyleknap left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh that makes more sense. Besides that I had a comment that looks like it got hidden so I posted it again. Otherwise, 🚢

@@ -555,7 +579,13 @@ def _build_lambda_function(self,
# type: (...) -> models.LambdaFunction
function_name = '%s-%s-%s' % (
config.app_name, config.chalice_stage, name)
return models.LambdaFunction(
security_group_ids = config.security_group_ids
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioned this before but I think we can remove lines 582-586 because they are handled in the _get_vpc_params() method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fixed now.

@kyleknap
Copy link
Contributor

Thanks. 🚢 again

@jamesls jamesls merged commit 30ba203 into aws:master May 17, 2018
jamesls added a commit that referenced this pull request May 17, 2018
PR #837

* lime-green-vpc-support:
  Remove unused ifcheck, not needed
  Retry lambda errors related to VPC permissions
  Raise validation error on invalid VPC params
  Extract validate tests into separate module
  Add a reference back to the stage config in the lambda config
  Add feature to changelog
  Update docs with more config examples
  Remove unused ApplicationPolicyHandler
  Flesh out the rest of the deployer
  Security groups vary per function
  Put back original linter values
  Add VPC support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants