Repository: serverless/serverless (JavaScript)
Date: 2018-08-28 16:40:08+00:00
Task type: Bug Fix

Issue:
An error occurred: ApiGatewayDeploymentxxxxx - No integration defined for method - when v1.28 was used
# This is a (Bug Report)
This error started July 4 and coincidentally 1.28.0 was released.
our CodeBuild is failing since then.

In our CodeBuild spec...

------------------------ code build spec ---------------

version: 0.2

phases:
  install:
      commands:
        - ls -a
        - npm install -g serverless
  build:
    commands:
        - npm install --only=dev
  post_build:
    commands:
        - serverless deploy --stage $APP_DEPLOYMENT_ENVIRONMENT --package serverless

---------------------------------------------

## Description

For bug reports:
* What went wrong? - CodeBuild failed. This also impacted our update on existing API G where it somehow deleted method (OPTIONS) on some api endpoints.
* What did you expect should have happened? - our build was successful before, there are no other config or yml changes implemented except on business application code.
* What was the config you used? - no update on config
* What stacktrace or error message from your provider did you see?


## Additional Data - Error Stack in Code Build

Serverless: Checking Stack update progress...
....................................................................................................................................
Serverless: Operation failed!
 
  Serverless Error ---------------------------------------
 
  An error occurred: ApiGatewayDeploymentxxxxxxx - No integration defined for method (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: xxxxxxxxxxxxxxxxxxx).
 
  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Issues:        forum.serverless.com
 
  Your Environment Information -----------------------------
     OS:                     linux
     Node Version:           6.3.1
     Serverless Version:     1.27.3
 

[Container] 2018/07/06 04:46:55 Command did not exit successfully serverless deploy --stage $APP_DEPLOYMENT_ENVIRONMENT --package serverless exit status 1
[Container] 2018/07/06 04:46:55 Phase complete: POST_BUILD Success: false
[Container] 2018/07/06 04:46:55 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: serverless deploy --stage $APP_DEPLOYMENT_ENVIRONMENT --package serverless. Reason: exit status 1


Issue discussion:
@densbozz the output references that you're still using v1.27.3. That rules out the newer version as a probable cause.
@densbozz @drexler I was experiencing the same issue, the exact same error output and resources missing the OPTIONS method. It appears to be related to a change in 1.28.0, because when deploying after the switch there are create method events being output in the event log despite no changes to my configuration. 

It seems like serverless was attempting to create OPTIONS methods that were already existent. 

**I was able to resolve it** by removing `cors: true` from all of my API Gateway resources in my serverless.yml, deploying, then adding `cors: true` back and deploying once more. I would presume that setting `cors: false` instead of removing/commenting them out would also suffice.

If this doesn't work and the problem is being more persistent, I was able to resolve the issue on one of my service's stages by removing all lambdas with http events (alternatively you can fully remove the service) from your serverless config, deploying, replacing, and deploying once again.
@mitchelljfs - thanks for the response. this makes sense now. can you confirm the workaround you did is just one time, and your succeeding serverless deployments have been successful?
@densbozz no problem. I have not run into the problem more than once per stage, after using one of the two methods I described. So, yes.
We're also experiencing this issue. The workaround is less than ideal, so hopefully this'll be resolved in 1.28.1
simply removing `cors: true` from my http events didn't seem to do it.

What solved it for me was deleting all my http events (except for one test event so that an API Gateway deployment would still be created), deploying, restoring all http events, deploying again. NOTE: It appears this completely re-creates the Lambda function instead of just updating it as all settings (environment variables, VPCs, roles, etc) were lost, which doesn't happen on a normal deploy. 
Hey @densbozz @drexler @mitchelljfs @dschep and @skylarmb!
Thanks for reporting and discussing solutions for this issue 👍

So I tried to reproduce this today with the following (super simple) service:

```yml
service: regression

provider:
  name: aws
  runtime: nodejs6.10

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: test
          method: POST
          cors: true
```

I started with a deployment via `1.27.3`, after that I re-deployed (still using `1.27.3`). Then I updated to `1.28.0` and re-deployed again. Unfortunately I'm not able to trigger the described error. I'm pretty sure that there might be something more I need to configure on the API Gateway end to force the error to pop up.

Can anyone of you share a simple (redacted) `serverless.yml` so that we can reproduce this on our end? Thanks in advance!
@pmuens here is a redacted version of our `serverless.yml`. We have many more http and scheduled events than these but this is just a sample

```
plugins:
  - serverless-apigw-binary
  - serverless-api-stage
  - serverless-plugin-split-stacks
  - serverless-offline

service: my-service
package:
  exclude:
    - ".env"
    - "coverage/**"
    - "test/**"
    - "scripts/**"
    - "ddl/**"
    - "docs/**"
    - "./*.json"
provider:
  timeout: 300
  name: aws
  runtime: nodejs8.10
  stage: ${opt:stage}
  region: us-east-1
  profile: foobar
  role: arn:aws:iam::123456789000:role/foobar-api-executor
  apiKeys:
    - ${opt:stage}-someApiKey
custom:
  apigwBinary:
    types:
      - 'application/octet-stream'
      - 'application/pdf'
  stageSettings:
    MethodSettings:
        DataTraceEnabled: true
        HttpMethod: "*"
        LoggingLevel: INFO
        ResourcePath: "/*"
        MetricsEnabled: true
  development:
    eventsEnabled: true
functions:
  root:
    handler: index.proxyRouter
    events:
      - schedule:
          name: 'X${opt:stage}SomeEventName'
          rate: cron(0 13 ? * MON *)
          enabled: ${self:custom.${opt:stage}.eventsEnabled}
          input:
            name: 'someEventName'
            stage: ${opt:stage}
      - http:
          cors: true
          method: get
          path: /api/baz
          authorizer:
            arn: ${file(./.serverless.env.yml):COGNITO_POOL_ARN}
      - http:
          cors: true
          method: post
          path: '/api/public/{id}/qux'

```
We just updated from version 1.26.1 (pretty old...) to version 1.30.1 and we just this same problem with our http event driven lambdas... This happens when attempting to create the web hook:
`No integration defined for method (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: ******some ID*******`
We tried redeploying with cors=true commented and then redeploying uncommenting it again in our lambdas and it worked... in case you guys are wondering if the workaround works.  It worked for us at least in some of our lambdas, still need to try it in all our others. Will report here if we experience something different.  Thanks

Solution patch:
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/index.js b/lib/plugins/aws/package/compile/events/apiGateway/index.js
index 3fe07915678..12dd5a692e1 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/index.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/index.js
@@ -23,6 +23,9 @@ class AwsCompileApigEvents {
     this.options = options;
     this.provider = this.serverless.getProvider('aws');
 
+    // used for the generated method logical ids (GET, PATCH, PUT, DELETE, OPTIONS, ...)
+    this.apiGatewayMethodLogicalIds = [];
+
     Object.assign(
       this,
       validate,
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js
index 99797e4cfa8..4fe96e7f484 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js
@@ -4,7 +4,6 @@ const _ = require('lodash');
 const BbPromise = require('bluebird');
 
 module.exports = {
-
   compileCors() {
     _.forEach(this.validated.corsPreflight, (config, path) => {
       const resourceName = this.getResourceName(path);
@@ -41,6 +40,8 @@ module.exports = {
             .replace('ANY', 'DELETE,GET,HEAD,PATCH,POST,PUT');
       }
 
+      this.apiGatewayMethodLogicalIds.push(corsMethodLogicalId);
+
       _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, {
         [corsMethodLogicalId]: {
           Type: 'AWS::ApiGateway::Method',
@@ -97,5 +98,4 @@ module.exports = {
       },
     ];
   },
-
 };
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js
index c0c084852fe..77fd557c18f 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js
@@ -4,9 +4,7 @@ const BbPromise = require('bluebird');
 const _ = require('lodash');
 
 module.exports = {
-
   compileMethods() {
-    this.apiGatewayMethodLogicalIds = [];
     this.permissionMapping = [];
 
     this.validated.events.forEach((event) => {


Test patch:
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/index.test.js b/lib/plugins/aws/package/compile/events/apiGateway/index.test.js
index 571e0b5e111..ee08997f45c 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/index.test.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/index.test.js
@@ -75,6 +75,9 @@ describe('AwsCompileApigEvents', () => {
     it('should set the provider variable to be an instanceof AwsProvider', () =>
       expect(awsCompileApigEvents.provider).to.be.instanceof(AwsProvider));
 
+    it('should setup an empty array to gather the method logical ids', () =>
+      expect(awsCompileApigEvents.apiGatewayMethodLogicalIds).to.deep.equal([]));
+
     it('should run "package:compileEvents" promise chain in order', () => {
       const validateStub = sinon
         .stub(awsCompileApigEvents, 'validate').returns({
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.test.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.test.js
index 64f65624b4c..128b0375eea 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.test.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.test.js
@@ -33,13 +33,13 @@ describe('#compileCors()', () => {
       },
     };
     awsCompileApigEvents = new AwsCompileApigEvents(serverless, options);
+    awsCompileApigEvents.apiGatewayMethodLogicalIds = [];
     awsCompileApigEvents.apiGatewayRestApiLogicalId = 'ApiGatewayRestApi';
     awsCompileApigEvents.apiGatewayResources = {
       'users/create': {
         name: 'UsersCreate',
         resourceLogicalId: 'ApiGatewayResourceUsersCreate',
       },
-
       'users/list': {
         name: 'UsersList',
         resourceLogicalId: 'ApiGatewayResourceUsersList',
@@ -252,4 +252,29 @@ describe('#compileCors()', () => {
     expect(() => awsCompileApigEvents.compileCors())
       .to.throw(Error, 'maxAge should be an integer over 0');
   });
+
+  it('should add the methods resource logical id to the array of method logical ids', () => {
+    awsCompileApigEvents.validated.corsPreflight = {
+      'users/create': {
+        origins: ['*', 'http://example.com'],
+        headers: ['*'],
+        methods: ['OPTIONS', 'POST'],
+        allowCredentials: true,
+        maxAge: 86400,
+      },
+      'users/any': {
+        origins: ['http://example.com'],
+        headers: ['*'],
+        methods: ['OPTIONS', 'ANY'],
+        allowCredentials: false,
+      },
+    };
+    return awsCompileApigEvents.compileCors().then(() => {
+      expect(awsCompileApigEvents.apiGatewayMethodLogicalIds)
+        .to.deep.equal([
+          'ApiGatewayMethodUsersCreateOptions',
+          'ApiGatewayMethodUsersAnyOptions',
+        ]);
+    });
+  });
 });
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
index 5b985331a9c..d558150bd98 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
@@ -34,6 +34,7 @@ describe('#compileMethods()', () => {
     };
     awsCompileApigEvents = new AwsCompileApigEvents(serverless, options);
     awsCompileApigEvents.validated = {};
+    awsCompileApigEvents.apiGatewayMethodLogicalIds = [];
     awsCompileApigEvents.apiGatewayRestApiLogicalId = 'ApiGatewayRestApi';
     awsCompileApigEvents.apiGatewayResources = {
       'users/create': {
@@ -609,7 +610,7 @@ describe('#compileMethods()', () => {
     });
   });
 
-  it('should create methodLogicalIds array', () => {
+  it('should update the method logical ids array', () => {
     awsCompileApigEvents.validated.events = [
       {
         functionName: 'First',
@@ -628,6 +629,10 @@ describe('#compileMethods()', () => {
     ];
     return awsCompileApigEvents.compileMethods().then(() => {
       expect(awsCompileApigEvents.apiGatewayMethodLogicalIds.length).to.equal(2);
+      expect(awsCompileApigEvents.apiGatewayMethodLogicalIds).to.deep.equal([
+        'ApiGatewayMethodUsersCreatePost',
+        'ApiGatewayMethodUsersListGet',
+      ]);
     });
   });