vercel / micro

Asynchronous HTTP microservices
MIT License
10.59k stars 459 forks source link

Fail/hang deploying micro with now #386

Closed andywillis closed 5 years ago

andywillis commented 5 years ago

Hi. I've been trying to deploy a simple micro test with now. While the code works perfectly on localhost, it seems to fail/hang when I use the deploy process. I've followed what instructions I've found but I've had no success. I figured it would be simple, but I'm stumped. Perhaps someone can help. Thanks.

https://journal-k774zxbtl.now.sh/ deployed with now

The code:

module.exports = () => ({
  date: new Date
});

package.json:

{
  "name": "journal",
  "version": "1.0.0",
  "description": "Test",
  "main": "index.js",
  "scripts": {
    "start": "micro",
    "start-dev": "micro-dev"
  },
  "dependencies": {
    "micro": "^9.3.3",
    "micro-dev": "^3.0.0"
  },
  "env": {
    "NODE_ENV": "production"
  }
}

now.json

{
  "version": 2,
  "name": "journal",
  "builds": [
    { "src": "index.js", "use": "@now/node" }
  ]
}
Deploy log

01/05 08:41 PM (2m) downloading https://dmmcy0pwk6bqi.cloudfront.net/691d0d77e857af97c383959a99d948dc44f4e9a4: 19.320ms 01/05 08:41 PM (2m) downloading https://dmmcy0pwk6bqi.cloudfront.net/d869e1a67f9339a0dd108f87f94934d34608d7f9: 23.163ms 01/05 08:41 PM (2m) running npm install for user... 01/05 08:41 PM (2m) installing to /tmp/11a4af79/user 01/05 08:41 PM (2m) npm 01/05 08:41 PM (2m) WARN journal@1.0.0 No repository field. 01/05 08:41 PM (2m) npm 01/05 08:41 PM (2m) WARN journal@1.0.0 No license field. npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules/fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"}) 01/05 08:41 PM (2m) added 241 packages in 4.075s 01/05 08:41 PM (2m) npm 01/05 08:41 PM (2m) 01/05 08:41 PM (2m) WARN 01/05 08:41 PM (2m) using --force I sure hope you know what you are doing. 01/05 08:41 PM (2m) writing ncc package.json... 01/05 08:41 PM (2m) running npm install for ncc... 01/05 08:41 PM (2m) installing to /tmp/11a4af79/ncc 01/05 08:41 PM (2m) using memory-fs for yarn cache 01/05 08:41 PM (2m) yarn install v1.12.3 01/05 08:41 PM (2m) warning package.json: No license field 01/05 08:41 PM (2m) info No lockfile found. 01/05 08:41 PM (2m) warning No license field 01/05 08:41 PM (2m) [1/4] Resolving packages... 01/05 08:41 PM (2m) [2/4] Fetching packages... 01/05 08:41 PM (2m) [3/4] Linking dependencies... 01/05 08:41 PM (2m) [4/4] Building fresh packages... 01/05 08:41 PM (2m) success Saved lockfile. 01/05 08:41 PM (2m) Done in 1.44s. 01/05 08:41 PM (2m) uploading cache (3334 kb) to 2a6390fc362b43fb6594bf646a3a352051d83a573beea02feaeab72091de6b69... 01/05 08:41 PM (1m) finished

rwboyer commented 5 years ago

Hi. I've been trying to deploy a simple micro test with now. While the code works perfectly on localhost, it seems to fail/hang when I use the deploy process. I've followed what instructions I've found but I've had no success. I figured it would be simple, but I'm stumped. Perhaps someone can help. Thanks.

https://journal-k774zxbtl.now.sh/ deployed with now

The code:

module.exports = () => ({
  date: new Date
});

package.json:

{
  "name": "journal",
  "version": "1.0.0",
  "description": "Test",
  "main": "index.js",
  "scripts": {
    "start": "micro",
    "start-dev": "micro-dev"
  },
  "dependencies": {
    "micro": "^9.3.3",
    "micro-dev": "^3.0.0"
  },
  "env": {
    "NODE_ENV": "production"
  }
}

now.json

{
  "version": 2,
  "name": "journal",
  "builds": [
    { "src": "index.js", "use": "@now/node" }
  ]
}

Deploy log

the now/node builder environment is NOT micro it's raw node http so you cannot just return a value and have it sent as you are doing here. You have to explicitly do a res.send or res.end with the response. The res.end OR a return will end the lambda.

Having said that the overall semantics of micro vs the now/node builder are remarkably close so that you that res.send/res.end or anything like that can be done in both and function similarly. Also with a bit of care most of the micro body parsers etc will function fine inside the lambda as they are coded on micro's req/res which are raw node http.

andywillis commented 5 years ago

Ok. Thanks for that information.

That still leaves the question how I can get the micro server (serving JSON) deploying with now. Or isn't that possible/have I completely misunderstood how micro/now is meant to work?

I thought now was meant to deploy by installing the dependencies then calling npm start like I do on localhost? Clearly that's not the case. I can't get rid of the now.json because then the deploy fails completely. Do I change use: @now/node to something else?

rwboyer commented 5 years ago

You can't but... you don't need it. Since you are not programmatically running the micro server it won't run in the lambda so as written your index.js will be the lambda but instead of micro listening for requests and passing it to that exported function the now lambda environment will invoke that function (just make sure you have a route to it in the now.json, I forget if it will just default to / for the top level index.js). All you need to do is use res.send or res.end instead of returning the result. That will work in both the now/node builder lambda AND the micro environment which makes micro a good dev environment as long as you are careful and don't rely on micro sending the value by returning it.

rwboyer commented 5 years ago

Below is a function just like yours but a little more complicated. It runs fine in now/node lambda as well as micro. It's actually an example of something that is ugly to do in the lambda environment (a test slackbot) because it needs to send headers before doing anything else which while possible for this case it's ugly and in other cases it's impossible because you may not know the size of the response yet. what it demonstrates is that you cannot end a response anywhere or the entire lambda will end (IE if you just moved res.send at the end to where it's sending headers this would not work)

module.exports = async (req, res) => {

  console.log(req.url);

  se = await json(req)

  switch (se.event.type){
    case 'url_verification':
      console.log(se);
      r = await send(res, 200, se.challenge);
      console.log(r);
      return;
    case 'app_mention':

      console.log(se);
      r = await res.writeHead(200);
      console.log(r);
      // Write the headers to the socket
      try{
      r = await res.socket.write(res._header);
      }
      catch(err){
        console.log(err);
      }
      // Mark the headers as sent
      res._headerSent = true;

      body = {
          "text": `Hello <@${se.event.user}>!`,
          "channel": se.event.channel
      }

      try{
        r = await fetch('https://slack.com/api/chat.postMessage', 
        { 
            method: 'POST',
            body:    JSON.stringify(body),
            headers: { 
              'Content-Type': 'application/json',
              "Authorization": "Bearer " + slack_token 
            }  
        });
        j = await r.json(); // expecting a json response
        console.log(j);
      }
      catch(err){ 
        console.log(err);
      } 

      console.log("FETCH HAPPENED errors or output?")
      r = await res.end();
      console.log(r);
      return;
  }

  //res.setHeader('Content-Type', 'application/json');
  send(res, 404, "broken");
};
rwboyer commented 5 years ago

Here is something much simpler that runs in micro for dev and also in the now/node builder lambda environment.

module.exports = cors(async (req, res) => {

  //param = req.url.split('/');
  //q = await dl.find({code: param[parm.length - 1]});

  console.log(req.url);

  var params = req.url.split('/download/');

  console.log(params);

  dl_item = await dl.find({ code: params[params.length - 1] });

  if(dl_item[0] && dl_item[0].count > 0){
    dl_item[0].count = dl_item[0].count - 1;
    await dl_item[0].save();
  }
  else {
    return await send(res, 403, "Download Limit Exceeded");
  }

  console.log(dl_item);
  //AWS.config.loadFromPath('./aws.json');
  AWS.config.update({
    accessKeyId: aws_id,
    secretAccessKey: aws_secret,
    "region": "us-east-1"
  });

  s3 = new AWS.S3({apiVersion: '2006-03-01'});

//  s3.listObjects({Bucket: s3_bucket}, function(err, data){
//    if(err){
//      console.log(err);
//    }
//    else{
//      console.log(data);
//    }
//  });

  const url = s3.getSignedUrl('getObject', {
      Bucket: s3_bucket,
      Key: 'framing-resource-pdf.zip',
      Expires: 60 * 60 * 72
  });

  console.log(url);

  redirect(res, 301, url);
  //res.setHeader('Content-Type', 'application/json');
  //send(res, 200, JSON.stringify(dl_item));
});
andywillis commented 5 years ago

Thanks for that code. I've clearly misunderstood how lambdas work. I'll read through it carefully later.

rwboyer commented 5 years ago

Thanks for that code. I've clearly misunderstood how lambdas work. I'll read through it carefully later.

no problem these are some crappy things that I was debugging in porting an express server over to now lambda's I an provide something much cleaner and more example worth or something crazy simple if you need.

I think where you got off track is not understanding that in a lambda environment the environment is the server for the lambdas and you don't need anything like micro or express dispatching the requests to your functions.

Ps. the zeit now 2.0 environment does have a now/node-server builder that allows you do incorporate your own server like micro or express as a package into a lambda but that's sort of at cross purposes to why you want lambdas in the first place. It's sort of do you want a whole process up and running like Express or do you want a bunch of functions that get dispatched on demand? Express or Micro or Koa do what a lambda environment does for you inside a single process with less flexibility (but they do have some upsides like not having to be super careful about cleaning up promises and callbacks before sending a response, ability to use websockets, etc)

paulogdm commented 5 years ago

Do notice that we will be releasing @now/micro in the near future. @lucasconstantino also did a community version of our intended builder that you can check here.

jorisre commented 5 years ago

Do notice that we will be releasing @now/micro in the near future. @lucasconstantino also did a community version of our intended builder that you can check here.

Hi ! Do you have some news about official @now/micro ? Thanks

MANTENN commented 5 years ago

Do notice that we will be releasing @now/micro in the near future. @lucasconstantino also did a community version of our intended builder that you can check here.

The builder writes to the source, causing the script execution to fail.

Do notice that we will be releasing @now/micro in the near future. @lucasconstantino also did a community version of our intended builder that you can check here.

Hi ! Do you have some news about official @now/micro ? Thanks

What's the status?

paulogdm commented 5 years ago

We recommend using @now/node with the helpers ( https://zeit.co/docs/v2/advanced/builders#node.js-helpers)