Repository: aliyun/fun
Pull request #154 (2019-02-18 07:44:54)

refactor env from array to obj
1. refactor env from array to obj 
2. optimize docker stop operation
3. fix InitializationTimeout bug when using fun local
4. refactor nas browser example using fun install


Patch:
diff --git a/examples/nas_browser/fun.yml b/examples/nas_browser/fun.yml
new file mode 100644
index 0000000..831beb8
--- /dev/null
+++ b/examples/nas_browser/fun.yml
@@ -0,0 +1,3 @@
+runtime: python3
+tasks:
+  - pip: Flask
diff --git a/examples/nas_browser/src/index.py b/examples/nas_browser/index.py
similarity index 100%
rename from examples/nas_browser/src/index.py
rename to examples/nas_browser/index.py
diff --git a/examples/nas_browser/template.yml b/examples/nas_browser/template.yml
index 0e07ed9..4e74372 100644
--- a/examples/nas_browser/template.yml
+++ b/examples/nas_browser/template.yml
@@ -23,8 +23,8 @@ Resources:
       Type: 'Aliyun::Serverless::Function'
       Properties:
         Handler: index.handler
-        Runtime: python2.7
-        CodeUri: './src'
+        Runtime: python3
+        CodeUri: './'
         Timeout: 100
         EnvironmentVariables:
           ROOT_DIR: /mnt/nas
diff --git a/lib/debug.js b/lib/debug.js
index 4997184..041963d 100644
--- a/lib/debug.js
+++ b/lib/debug.js
@@ -150,17 +150,17 @@ function generateDebugEnv(runtime, debugPort) {
 
   switch (runtime) {
   case 'nodejs8':
-    return `DEBUG_OPTIONS=--inspect-brk=0.0.0.0:${debugPort}`;
+    return { 'DEBUG_OPTIONS': `--inspect-brk=0.0.0.0:${debugPort}` };
   case 'nodejs6':
-    return `DEBUG_OPTIONS=--debug-brk=${debugPort}`;
+    return { 'DEBUG_OPTIONS': `--debug-brk=${debugPort}` };
   case 'python2.7':
   case 'python3':
-    return `DEBUG_OPTIONS=-m ptvsd --host 0.0.0.0 --port ${debugPort} --wait`;
+    return { 'DEBUG_OPTIONS': `-m ptvsd --host 0.0.0.0 --port ${debugPort} --wait` };
   case 'java8':
-    return `DEBUG_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=${debugPort}`;
+    return { 'DEBUG_OPTIONS': `-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=${debugPort}` };
   case 'php7.2':
     console.log(`using remote_ip ${remoteIp}`);
-    return `XDEBUG_CONFIG=remote_enable=1 remote_autostart=1 remote_port=${debugPort} remote_host=${remoteIp}`;
+    return { 'XDEBUG_CONFIG': `remote_enable=1 remote_autostart=1 remote_port=${debugPort} remote_host=${remoteIp}` };
   default:
     console.error(red('could not found runtime.'));
     process.exit(-1);
diff --git a/lib/docker-opts.js b/lib/docker-opts.js
index 6953d07..156e809 100644
--- a/lib/docker-opts.js
+++ b/lib/docker-opts.js
@@ -6,6 +6,9 @@ const {
 
 const debug = require('debug')('docker-opts');
 const nestedObjectAssign = require('nested-object-assign');
+const { addEnv } = require('./install/env');
+
+const _ = require('lodash');
 
 const { red } = require('colors');
 
@@ -18,6 +21,10 @@ const runtimeImageMap = {
   'php7.2': 'php7.2'
 };
 
+function resolveDockerEnv(envs = {}) {
+  return _.map(addEnv(envs || {}), (v, k) => `${k}=${v}`);
+}
+
 function resolveRuntimeToDockerImage(runtime, isBuild) {
   if (runtimeImageMap[runtime]) {
     const name = runtimeImageMap[runtime];
@@ -79,7 +86,7 @@ async function generateLocalInvokeOpts(runtime, containerName, mounts, cmd, debu
 
   const opts = nestedObjectAssign(
     {
-      Env: envs,
+      Env: resolveDockerEnv(envs),
       Image: imageName,
       name: containerName,
       Cmd: cmd,
@@ -118,7 +125,7 @@ async function generateLocalStartRunOpts(runtime, name, mounts, cmd, debugPort,
 
   const opts = nestedObjectAssign(
     {
-      Env: envs,
+      Env: resolveDockerEnv(envs),
       Image: imageName,
       name,
       Cmd: cmd,
@@ -136,5 +143,5 @@ async function generateLocalStartRunOpts(runtime, name, mounts, cmd, debugPort,
 module.exports = {
   generateLocalInvokeOpts, resolveRuntimeToDockerImage, 
   generateInstallOpts, generateLocalStartRunOpts,
-  resolveMockScript
+  resolveMockScript, resolveDockerEnv
 };
\ No newline at end of file
diff --git a/lib/docker.js b/lib/docker.js
index a2f3501..1fff0af 100644
--- a/lib/docker.js
+++ b/lib/docker.js
@@ -16,7 +16,6 @@ const dockerOpts = require('./docker-opts');
 
 var containers = new Set();
 
-const { addEnv } = require('./install/env');
 const devnull = require('dev-null');
 
 // exit container, when use ctrl + c
@@ -35,8 +34,7 @@ function waitingForContainerStopped() {
   }
 
   let stopping = false;
-  let success = true;
-
+  
   process.on('SIGINT', async () => {
 
     debug('containers length: ', containers.length);
@@ -60,18 +58,17 @@ function waitingForContainerStopped() {
 
     console.log(`\nreceived canncel request, stopping running containers.....`);
 
+    const jobs = [];
+  
     for (let container of containers) {
       console.log(`stopping container ${container}`);
-
-      try {
-        await docker.getContainer(container).stop();
-      } catch (error) {
-        success = false;
-        console.error(error);
-      }
+      jobs.push(docker.getContainer(container).stop());
     }
 
-    if (!success) {
+    try {
+      await Promise.all(jobs);
+    } catch (error) {
+      console.error(error);
       process.exit(-1);
     }
   });
@@ -197,8 +194,9 @@ function generateDockerCmd(functionProps, httpMode) {
 
   const initializationTimeout = functionProps.InitializationTimeout;
 
+  // initializationTimeout is defined as integer, see lib/validate/schema/function.js
   if (initializationTimeout) {
-    cmd.push('--initializationTimeout', initializationTimeout);
+    cmd.push('--initializationTimeout', initializationTimeout.toString());
   }
 
   debug(`docker cmd: ${cmd}`);
@@ -258,19 +256,11 @@ async function pullImage(imageName) {
 }
 
 function generateFunctionEnvs(functionProps) {
-  const environmentVariables = addEnv(functionProps.EnvironmentVariables);
-
-  if (!environmentVariables) { return []; }
-
-  let envs = [];
-
-  for (const [envName, envValue] of Object.entries(environmentVariables)) {
-    envs.push(`${envName}=${envValue}`);
-  }
+  const environmentVariables = functionProps.EnvironmentVariables;
 
-  debug(`load function env: ${envs}`);
+  if (!environmentVariables) { return {}; }
 
-  return envs;
+  return Object.assign({}, environmentVariables);
 }
 
 function generateRamdomContainerName() {
@@ -278,24 +268,31 @@ function generateRamdomContainerName() {
 }
 
 async function generateDockerEnvs(functionProps, debugPort, httpParams) {
-  const envs = [];
+  const envs = {};
 
   if (httpParams) {
-    envs.push(`FC_HTTP_PARAMS=${httpParams}`);
+    Object.assign(envs, {
+      'FC_HTTP_PARAMS': httpParams
+    });
   }
 
   if (debugPort) {
-    const env = generateDebugEnv(functionProps.Runtime, debugPort);
+    const debugEnv = generateDebugEnv(functionProps.Runtime, debugPort);
 
-    debug('debug env: ' + env);
-    envs.push(env);
-  }
+    debug('debug env: ' + debugEnv);
 
-  envs.push(...generateFunctionEnvs(functionProps));
+    Object.assign(envs, debugEnv);
+  }
 
+  Object.assign(envs, generateFunctionEnvs(functionProps));
+  
   const profile = await getProfile();
 
-  envs.push('local=true', `FC_ACCESS_KEY_ID=${profile.accessKeyId}`, `FC_ACCESS_KEY_SECRET=${profile.accessKeySecret}`);
+  Object.assign(envs, {
+    'local': true,
+    'FC_ACCESS_KEY_ID': profile.accessKeyId,
+    'FC_ACCESS_KEY_SECRET': profile.accessKeySecret
+  });
 
   return envs;
 }
@@ -417,10 +414,10 @@ async function startContainer(opts) {
       containers.delete(container.id);
     },
 
-    exec: async (cmd, {cwd = '', env = [], event, outputStream, errorStream, verbose = false} = {}) => {      
+    exec: async (cmd, {cwd = '', env = {}, event, outputStream, errorStream, verbose = false} = {}) => {      
       const options = {
         Cmd: cmd,
-        Env: env || [],
+        Env: dockerOpts.resolveDockerEnv(env),
         Tty: false,
         AttachStdin: true,  
         AttachStdout: true,
diff --git a/lib/install/install.js b/lib/install/install.js
index 139a238..1792790 100644
--- a/lib/install/install.js
+++ b/lib/install/install.js
@@ -4,25 +4,21 @@ const { AptTask, PipTask, ShellTask } = require('./task');
 const Context = require('./context');
 const { FunModule } = require('./module');
 const path = require('path');
-const { addEnv } = require('../install/env');
-const _ = require('lodash');
 
 async function installPackage(runtime, pkgType, pkgName, options) {
 
   const ctx = await new Context(runtime, options.codeUri);
 
   try {
-    const envs = _.map(addEnv(options.env || {}), (v, k) => `${k}=${v}`);
-
     switch (pkgType) {
 
     case 'apt':
       await new AptTask(options.name, runtime, options.codeUri, 
-        pkgName, options.local, envs, ctx, options.verbose).run();
+        pkgName, options.local, options.env, ctx, options.verbose).run();
       break;
     case 'pip':
       await new PipTask(options.name, runtime, options.codeUri, 
-        pkgName, options.local, envs, ctx, options.verbose).run();
+        pkgName, options.local, options.env, ctx, options.verbose).run();
       break;
     case 'module':
       // TODO
@@ -46,19 +42,18 @@ async function installFromYaml(file, verbose) {
   try {
     for (const t of funModule.tasks) {
 
-      const envs = _.map(addEnv(t.attrs.env || {}), (v, k) => `${k}=${v}`);
 
       if (t.type === 'pip') {
         const pipTask = new PipTask(t.attrs.name, runtime, codeUri, 
-          t.attrs.pip, t.attrs.local, envs,  ctx, verbose);
+          t.attrs.pip, t.attrs.local, t.attrs.env,  ctx, verbose);
         await pipTask.run();
       } else if (t.type === 'apt') {
         const aptTask = new AptTask(t.attrs.name, runtime, codeUri, 
-          t.attrs.apt, t.attrs.local, envs, ctx, verbose);
+          t.attrs.apt, t.attrs.local, t.attrs.env, ctx, verbose);
         await aptTask.run();
       } else if (t.type === 'shell') {
         const shellTask = new ShellTask(t.attrs.name, runtime, codeUri, 
-          t.attrs.shell, t.attrs.cwd, envs, ctx, verbose);
+          t.attrs.shell, t.attrs.cwd, t.attrs.env, ctx, verbose);
         await shellTask.run();
       } else {
         console.error('unkown task %s', t);
diff --git a/lib/install/task.js b/lib/install/task.js
index bfb4b66..cbbe44a 100644
--- a/lib/install/task.js
+++ b/lib/install/task.js
@@ -4,7 +4,7 @@ const { startInstallationContainer } = require('../docker');
 const { cyan, green } = require('colors');
 
 class Task {
-  constructor(name, runtime, codeUri, env = [], context = null, verbose = false) {
+  constructor(name, runtime, codeUri, env = {}, context = null, verbose = false) {
     this.name = name;
     this.runtime = runtime;
     this.codeUri = codeUri;
@@ -62,7 +62,9 @@ class PipTask extends InstallTask {
       console.log(green('     => ') + cyan('PYTHONUSERBASE=/code/.fun/python pip install --user %s'), this.pkgName);
 
       await this._exec(['pip', 'install', '--user', '--no-warn-script-location', this.pkgName],
-        ['PIP_DISABLE_PIP_VERSION_CHECK=1', ...this.env]);
+        Object.assign({
+          'PIP_DISABLE_PIP_VERSION_CHECK': '1',
+        }, this.env));
     } else {
       console.log(green('     => ') + cyan('pip install %s'), this.pkgName);
       await this._exec(['pip', 'install', this.pkgName]);
diff --git a/lib/local/http-invoke.js b/lib/local/http-invoke.js
index 163d9e3..eeb56a4 100644
--- a/lib/local/http-invoke.js
+++ b/lib/local/http-invoke.js
@@ -36,7 +36,7 @@ class HttpInvoke extends Invoke {
       }
 
       console.log(`detect code changes, file is ${name}, event is ${evt}, auto reloading...`);
-      
+
       oldRunner = this.runner;
 
       this.runner = null;
@@ -102,38 +102,40 @@ class HttpInvoke extends Invoke {
   }
 
   async doInvoke(req, res) {
-    const outputStream = new streams.WritableStream();
-    const errorStream = new streams.WritableStream();
+    // only one invoke can be processed
+    await lock.acquire('invoke', async () => {
+      debug('http doInvoke, aquire invoke lock success, processing...');
 
-    const event = await getHttpRawBody(req);
+      const outputStream = new streams.WritableStream();
+      const errorStream = new streams.WritableStream();
 
-    const httpParams = generateHttpParams(req, this.endpointPrefix);
+      const event = await getHttpRawBody(req);
 
-    const envs = await docker.generateDockerEnvs(this.functionProps, this.debugPort, httpParams);
+      const httpParams = generateHttpParams(req, this.endpointPrefix);
 
-    if (this.debugPort) {
-      // don't reuse container
-      const cmd = docker.generateDockerCmd(this.functionProps, true);
+      const envs = await docker.generateDockerEnvs(this.functionProps, this.debugPort, httpParams);
 
-      const opts = await dockerOpts.generateLocalInvokeOpts(
-        this.runtime,
-        this.containerName,
-        this.mounts,
-        cmd,
-        this.debugPort,
-        envs,
-        this.dockerUser);
+      if (this.debugPort) {
+        // don't reuse container
+        const cmd = docker.generateDockerCmd(this.functionProps, true);
 
-      await docker.run(opts,
-        event,
-        outputStream, errorStream);
-    } else {
-      // reuse container
-      debug('http doInvoke, acquire invoke lock');
+        this.containerName = docker.generateRamdomContainerName();
 
-      // only one invoke can be processed
-      await lock.acquire('invoke', async () => {
-        debug('http doInvoke, aquire invoke lock success, processing...');
+        const opts = await dockerOpts.generateLocalInvokeOpts(
+          this.runtime,
+          this.containerName,
+          this.mounts,
+          cmd,
+          this.debugPort,
+          envs,
+          this.dockerUser);
+
+        await docker.run(opts,
+          event,
+          outputStream, errorStream);
+      } else {
+        // reuse container
+        debug('http doInvoke, acquire invoke lock');
 
         const cmd = [dockerOpts.resolveMockScript(this.runtime), ...docker.generateDockerCmd(this.functionProps, true)];
 
@@ -144,19 +146,19 @@ class HttpInvoke extends Invoke {
           if (!await validateSignature(req, res, req.method)) { return; }
         }
 
-        await this.runner.exec(cmd, { 
-          env: envs, 
+        await this.runner.exec(cmd, {
+          env: envs,
           event,
-          outputStream, 
-          errorStream, 
+          outputStream,
+          errorStream,
           verbose: true
         });
 
         debug('http doInvoke exec end, begin to response');
-      });
-    }
+      }
 
-    this.response(outputStream, errorStream, res);
+      this.response(outputStream, errorStream, res);
+    });
   }
 
   // responseHttpTriggers


Test patch:
diff --git a/test/commands/install.test.js b/test/commands/install.test.js
index 3a0178c..0f91773 100644
--- a/test/commands/install.test.js
+++ b/test/commands/install.test.js
@@ -53,7 +53,6 @@ chai.use(require('chai-fs'));
     expect(ymlPath).to.be.a.file().with.content(`runtime: python2.7
 tasks:
   - pip: pymssql
-    local: true
 `);
 
 
@@ -65,7 +64,6 @@ tasks:
 runtime: python2.7
 tasks:
   - pip: pymssql
-    local: true
 `);
 
 
@@ -79,9 +77,7 @@ tasks:
     expect(ymlPath).to.be.a.file().with.content(`runtime: python2.7
 tasks:
   - pip: pymssql
-    local: true
   - apt: libzbar0
-    local: true
 `);
   });
 });
\ No newline at end of file
diff --git a/test/debug.test.js b/test/debug.test.js
index b6f7d01..a14fc77 100644
--- a/test/debug.test.js
+++ b/test/debug.test.js
@@ -148,32 +148,32 @@ describe('test generateVscodeDebugConfig', () => {
 describe('test generateDebugEnv', () => {
   it('test python2.7', async function () {
     const env = await generateDebugEnv('python2.7', 9000);
-    expect(env).to.be('DEBUG_OPTIONS=-m ptvsd --host 0.0.0.0 --port 9000 --wait');
+    expect(env).to.eql({ 'DEBUG_OPTIONS': '-m ptvsd --host 0.0.0.0 --port 9000 --wait' });
   });
 
   it('test python3', async function () {
     const env = await generateDebugEnv('python3', 9000);
-    expect(env).to.be('DEBUG_OPTIONS=-m ptvsd --host 0.0.0.0 --port 9000 --wait');
+    expect(env).to.eql({ 'DEBUG_OPTIONS': '-m ptvsd --host 0.0.0.0 --port 9000 --wait' });
   });
 
   it('test nodejs6', async function () {
     const env = await generateDebugEnv('nodejs6', 9000);
-    expect(env).to.be('DEBUG_OPTIONS=--debug-brk=9000');
+    expect(env).to.eql({ 'DEBUG_OPTIONS': '--debug-brk=9000' });
   });
 
   it('test nodejs8', async function () {
     const env = await generateDebugEnv('nodejs8', 9000);
-    expect(env).to.be('DEBUG_OPTIONS=--inspect-brk=0.0.0.0:9000');
+    expect(env).to.eql({ 'DEBUG_OPTIONS': '--inspect-brk=0.0.0.0:9000' });
   });
 
   it('test php7.2', async function () {
     const env = await generateDebugEnv('php7.2', 9000);
-    expect(env).to.contain('XDEBUG_CONFIG=remote_enable=1 remote_autostart=1 remote_port=9000 remote_host=');
+    expect(env.XDEBUG_CONFIG).to.contain('remote_enable=1 remote_autostart=1 remote_port=9000 remote_host=');
   });
 
   it('test java8', async function () {
     const env = await generateDebugEnv('java8', 9000);
-    expect(env).to.be('DEBUG_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=9000');
+    expect(env).to.eql({ 'DEBUG_OPTIONS': '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=9000' });
   });
 
 });
diff --git a/test/docker-opts.test.js b/test/docker-opts.test.js
index a4c5102..70a4622 100644
--- a/test/docker-opts.test.js
+++ b/test/docker-opts.test.js
@@ -38,10 +38,12 @@ describe('test generateLocalInvokeOpts', () => {
   });
 
   it('test generate docker opts', async () => {
-    const envs = ['local=true',
-      'FC_ACCESS_KEY_ID=testKeyId',
-      'FC_ACCESS_KEY_SECRET=testKeySecret',
-      'DEBUG_OPTIONS=--inspect-brk=0.0.0.0:9000'];
+    const envs = {
+      'local': true,
+      'FC_ACCESS_KEY_ID': 'testKeyId',
+      'FC_ACCESS_KEY_SECRET': 'testKeySecret',
+      'DEBUG_OPTIONS': '--inspect-brk=0.0.0.0:9000'
+    };
 
     const opts = await dockerOpts.generateLocalInvokeOpts('nodejs8', 'test', [{
       Type: 'bind',
@@ -58,7 +60,10 @@ describe('test generateLocalInvokeOpts', () => {
         'local=true',
         'FC_ACCESS_KEY_ID=testKeyId',
         'FC_ACCESS_KEY_SECRET=testKeySecret',
-        'DEBUG_OPTIONS=--inspect-brk=0.0.0.0:9000'
+        'DEBUG_OPTIONS=--inspect-brk=0.0.0.0:9000',
+        'LD_LIBRARY_PATH=/code/.fun/root/usr/lib:/code/.fun/root/usr/lib/x86_64-linux-gnu:/code:/code/lib:/usr/local/lib',
+        'PATH=/code/.fun/root/usr/local/bin:/code/.fun/root/usr/local/sbin:/code/.fun/root/usr/bin:/code/.fun/root/usr/sbin:/code/.fun/root/sbin:/code/.fun/root/bin:/code/.fun/python/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin',
+        'PYTHONUSERBASE=/code/.fun/python'
       ],
       'AttachStderr': true,
       'AttachStdin': true,
@@ -102,7 +107,6 @@ describe('test generateLocalInvokeOpts', () => {
 
     expect(opts).to.eql({
       'name': 'test',
-      'Env': null,
       'Cmd': null,
       'AttachStderr': true,
       'AttachStdin': true,
@@ -112,6 +116,11 @@ describe('test generateLocalInvokeOpts', () => {
       'Tty': false,
       'User': null,
       'Image': 'aliyunfc/runtime-nodejs8:1.5.0',
+      'Env': [
+        'LD_LIBRARY_PATH=/code/.fun/root/usr/lib:/code/.fun/root/usr/lib/x86_64-linux-gnu:/code:/code/lib:/usr/local/lib',
+        'PATH=/code/.fun/root/usr/local/bin:/code/.fun/root/usr/local/sbin:/code/.fun/root/usr/bin:/code/.fun/root/usr/sbin:/code/.fun/root/sbin:/code/.fun/root/bin:/code/.fun/python/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin',
+        'PYTHONUSERBASE=/code/.fun/python'
+      ],
       'HostConfig': {
         'AutoRemove': true,
         'Mounts': [
@@ -125,4 +134,45 @@ describe('test generateLocalInvokeOpts', () => {
       }
     });
   });
+});
+
+describe('test resolveDockerEnv', () => {
+  it('test empty', () => {
+    const envs = [];
+    const resolved = dockerOpts.resolveDockerEnv(envs);
+
+    expect(resolved).to.eql([
+      'LD_LIBRARY_PATH=/code/.fun/root/usr/lib:/code/.fun/root/usr/lib/x86_64-linux-gnu:/code:/code/lib:/usr/local/lib',
+      'PATH=/code/.fun/root/usr/local/bin:/code/.fun/root/usr/local/sbin:/code/.fun/root/usr/bin:/code/.fun/root/usr/sbin:/code/.fun/root/sbin:/code/.fun/root/bin:/code/.fun/python/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin',
+      'PYTHONUSERBASE=/code/.fun/python'
+    ]);
+  });
+
+  it('test one env', () => {
+    const envs = { 'key': 'value' };
+
+    const resolved = dockerOpts.resolveDockerEnv(envs);
+
+    expect(resolved).to.eql([
+      'key=value',
+      'LD_LIBRARY_PATH=/code/.fun/root/usr/lib:/code/.fun/root/usr/lib/x86_64-linux-gnu:/code:/code/lib:/usr/local/lib',
+      'PATH=/code/.fun/root/usr/local/bin:/code/.fun/root/usr/local/sbin:/code/.fun/root/usr/bin:/code/.fun/root/usr/sbin:/code/.fun/root/sbin:/code/.fun/root/bin:/code/.fun/python/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin',
+      'PYTHONUSERBASE=/code/.fun/python'
+    ]);
+  });
+
+  it('test two env', () => {
+    const envs = { 'key1': 'value1', 'key2': 'value2' };
+
+    const resolved = dockerOpts.resolveDockerEnv(envs);
+
+    expect(resolved).to.eql([
+      'key1=value1', 
+      'key2=value2',
+      'LD_LIBRARY_PATH=/code/.fun/root/usr/lib:/code/.fun/root/usr/lib/x86_64-linux-gnu:/code:/code/lib:/usr/local/lib',
+      'PATH=/code/.fun/root/usr/local/bin:/code/.fun/root/usr/local/sbin:/code/.fun/root/usr/bin:/code/.fun/root/usr/sbin:/code/.fun/root/sbin:/code/.fun/root/bin:/code/.fun/python/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin',
+      'PYTHONUSERBASE=/code/.fun/python'
+    ]);
+  });
+
 });
\ No newline at end of file
diff --git a/test/docker.test.js b/test/docker.test.js
index 27baeb8..bcbab63 100644
--- a/test/docker.test.js
+++ b/test/docker.test.js
@@ -30,7 +30,8 @@ describe('test generateDockerCmd', () => {
     'CodeUri': 'python3',
     'Initializer': 'index.initializer',
     'Description': 'Hello world with python3!',
-    'Runtime': 'python3'
+    'Runtime': 'python3',
+    'InitializationTimeout': 3
   };
 
   it('test generate docker cmd', () => {
@@ -42,7 +43,9 @@ describe('test generateDockerCmd', () => {
       'index.handler',
       '--stdin',
       '-i',
-      'index.initializer'
+      'index.initializer',
+      '--initializationTimeout',
+      '3'
     ]);
   });
 
@@ -55,7 +58,9 @@ describe('test generateDockerCmd', () => {
       '--stdin',
       '--http',
       '-i',
-      'index.initializer'
+      'index.initializer',
+      '--initializationTimeout',
+      '3'
     ]);
   });
 });
@@ -166,24 +171,17 @@ describe('test generateFunctionEnvs', () => {
     };
 
     const envs = docker.generateFunctionEnvs(functionProps);
-    expect(envs).to.eql([
-      'TestKey1=TestValue1',
-      'TestKey2=TestValue2',
-      'LD_LIBRARY_PATH=/code/.fun/root/usr/lib:/code/.fun/root/usr/lib/x86_64-linux-gnu:/code:/code/lib:/usr/local/lib',
-      'PATH=/code/.fun/root/usr/local/bin:/code/.fun/root/usr/local/sbin:/code/.fun/root/usr/bin:/code/.fun/root/usr/sbin:/code/.fun/root/sbin:/code/.fun/root/bin:/code/.fun/python/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin',
-      'PYTHONUSERBASE=/code/.fun/python'
-    ]);
+    expect(envs).to.eql({
+      'TestKey1': 'TestValue1',
+      'TestKey2': 'TestValue2'
+    });
   });
 
   it('test generate empty function env', () => {
     const functionProps = {};
 
     const envs = docker.generateFunctionEnvs(functionProps);
-    expect(envs).to.eql([
-      'LD_LIBRARY_PATH=/code/.fun/root/usr/lib:/code/.fun/root/usr/lib/x86_64-linux-gnu:/code:/code/lib:/usr/local/lib',
-      'PATH=/code/.fun/root/usr/local/bin:/code/.fun/root/usr/local/sbin:/code/.fun/root/usr/bin:/code/.fun/root/usr/sbin:/code/.fun/root/sbin:/code/.fun/root/bin:/code/.fun/python/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin',
-      'PYTHONUSERBASE=/code/.fun/python'
-    ]);
+    expect(envs).to.eql([]);
   });
 });
 
@@ -424,7 +422,7 @@ describe('InstallationContainer', async () => {
     });
 
     await runner.exec(['/bin/bash', '-c', 'echo test $VAR'], {
-      env: ['VAR=sdfasdfasdf']
+      env: { 'VAR': 'sdfasdfasdf' }
     });
 
     await runner.stop();
diff --git a/test/install/install.test.js b/test/install/install.test.js
index e1712c2..420fdc1 100644
--- a/test/install/install.test.js
+++ b/test/install/install.test.js
@@ -38,7 +38,7 @@ chai.use(require('chai-fs'));
       local: true
     });
 
-    expect(path.join(funTempDir, '.fun/root/usr/lib/x86_64-linux-gnu/libzbar.so.0')).to.be.a.path();
+    expect(path.join(funTempDir, '.fun/root/usr/lib/libzbar.so.0')).to.be.a.path();
 
   });
 
@@ -71,7 +71,7 @@ tasks:
 
     await installFromYaml(ymlPath);
 
-    expect(path.join(funTempDir, '.fun/root/usr/lib/x86_64-linux-gnu/libzbar.so.0')).to.be.a.path();
+    expect(path.join(funTempDir, '.fun/root/usr/lib/libzbar.so.0')).to.be.a.path();
     expect(path.join(funTempDir, '1.txt')).to.be.a.path();
 
   });
diff --git a/test/install/module.test.js b/test/install/module.test.js
index 9c8b7e7..f1e2b63 100644
--- a/test/install/module.test.js
+++ b/test/install/module.test.js
@@ -75,7 +75,6 @@ modules:
 tasks:
   - name: install pymssql localy by pip
     pip: pymssql
-    local: true
 `);
   });