From cca46276cfc7bce2f9bb88b7e3c35a948931ced3 Mon Sep 17 00:00:00 2001 From: Nova Date: Thu, 16 Jul 2020 20:16:42 +0530 Subject: [PATCH] #19 Added validation and user informative logs & errors --- lib/bundle.js | 69 ++++++++++++++++++++++++++++++++++++++++---------- test/helper.js | 8 ++++-- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/lib/bundle.js b/lib/bundle.js index e0f3281..34ea64e 100644 --- a/lib/bundle.js +++ b/lib/bundle.js @@ -13,7 +13,10 @@ function runCommand(cmd,args,options,cli) { if (ps.stderr){ cli.log(ps.stderr.toString()) } - if (ps.error && !ps.error.code == 'ENOENT') { + if (ps.error && ps.error.code == 'ENOENT'){ + return ps; + } + if (ps.error) { throw new Error(ps.error); } else if (ps.status !== 0) { throw new Error(ps.stderr); @@ -27,29 +30,39 @@ function docker(args, options,cli){ function cleanBuild(){ this.cli = this.serverless.cli - this.cli.log("Cleaning ruby layer build") + this.cli.log("Clearing previous build ruby layer build") this.ruby_layer = path.join(this.servicePath,'.serverless','ruby_layer') if (fs.pathExistsSync(this.ruby_layer)){ fs.removeSync(this.ruby_layer) } } + function bundleInstall(){ - this.cli.log(this.ruby_layer) - const gem_path = path.join(this.ruby_layer,'Gemfile') - fs.copySync(path.join(this.servicePath,'Gemfile'), gem_path ) + this.debug = process.env.SLS_DEBUG; + const gem_file_path = path.join(this.servicePath,'Gemfile') + if (!fs.pathExistsSync(gem_file_path)){ + throw new Error("No Gemfile found in the path "+gem_file_path+"\n Please add a Gemfile in the path"); + } + + fs.copySync(path.join(this.servicePath,'Gemfile'), path.join(this.ruby_layer,'Gemfile') ) const bundle_args = ['bundle', 'install', '--path=build','--without', 'test', 'development'] - const options={cwd : this.ruby_layer, encoding : 'utf8'} this.build_path = path.join(this.ruby_layer, 'build') fs.mkdirSync(this.build_path) + const options = {cwd : this.ruby_layer, encoding : 'utf8'} if (this.options.use_docker) { docker_name = 'lambci/lambda:build-'+this.serverless.service.provider.runtime ps=docker(['version'], options,this.cli) if (ps.error && ps.error.code === 'ENOENT') { - throw new Error('docker command not found'); + throw new Error('docker command not found. Please install docker https://www.docker.com/products/docker-desktop'); } let buildDocker = false; if (this.options.docker_file) { - fs.copySync(path.join(this.servicePath, this.options.docker_file), + const docker_file_path = path.join(this.servicePath, this.options.docker_file) + if (!fs.pathExistsSync(docker_file_path)){ + throw new Error("No Dockerfile found in the path "+docker_file_path); + } + + fs.copySync(docker_file_path, path.join(this.ruby_layer,'Dockerfile')) buildDocker = true }else if (this.options.docker_yums) { @@ -63,10 +76,13 @@ function bundleInstall(){ buildDocker = true } if (buildDocker) { + this.cli.log("Building docker for bundle install") docker_name ='ruby-layer:docker' docker(['build', '-t', docker_name, '-f', 'Dockerfile', '.'], options,this.cli) } + if (this.options.native_libs) { + this.cli.log("Packing the native libraries from the specified path") ps=docker(['run', '-d', docker_name, 'false'], options,this.cli) container_id = ps.stdout.toString().trim() const lib_path = path.join(this.build_path,'lib') @@ -75,15 +91,27 @@ function bundleInstall(){ ps=docker(['cp','-L', container_id+':'+lib_to_copy, lib_path],options,this.cli) }) } + + this.cli.log("Installing gem using docker bundler") args=['run','--rm', '-i','-v', `${this.ruby_layer}:/var/gem_build`, '-w', '/var/gem_build'] args.push(docker_name) + docker(args.concat(bundle_args), options,this.cli) } else { - ps = runCommand("bundle",['-v'],options,this.cli) + ps = runCommand("bundle",['-v'], options, this.cli) if (ps.error && ps.error.code === 'ENOENT') { - throw new Error('bundle command not found in local'); + throw new Error('bundle command not found in local. Please install ruby. https://www.ruby-lang.org/en/downloads/'); + } + this.cli.log("Installing gem using local bundler") + if (this.debug) { + this.cli.log("Ruby layer Path: \n "+ this.ruby_layer) + ps = runCommand("ruby",['--version'], options, this.cli) + this.cli.log("Ruby version: "+ ps.stdout.toString().trim()) + ps = runCommand("bundle",['-v'],options,this.cli) + this.cli.log("Bundler version: "+ ps.stdout.toString().trim()) + this.cli.log(bundle_args.join(" ")) } - this.cli.log(bundle_args.slice(1,bundle_args.length)) + runCommand(bundle_args[0],bundle_args.slice(1,bundle_args.length),options,this.cli) } } @@ -125,8 +153,13 @@ function zipBundleFolder() { this.gem_folder= fs.readdirSync(path.join(this.build_path,'ruby'))[0] fs.removeSync(path.join(this.build_path,'ruby',this.gem_folder,'cache')) const platform = process.platform == 'win32' ? 'DOS' : 'UNIX' - return zipDir(this.build_path, - path.join(this.ruby_layer, 'gemLayer.zip'), + zipping_message = "Zipping the gemfiles" + if (this.options.native_libs) { + zipping_message+=" and native libs" + } + this.gemLayer_zip_path = path.join(this.ruby_layer, 'gemLayer.zip') + this.cli.log(zipping_message+ ' to '+ this.gemLayer_zip_path) + return zipDir(this.build_path, this.gemLayer_zip_path, { platform: platform, compression: 'DEFLATE', compressionOptions: { level: 9 }}); } @@ -143,12 +176,17 @@ function excludePackage(){ } function configureLayer() { + this.cli.log("Configuring Layer and GEM_PATH to the functions") + if(this.debug){ + this.cli.log("GEM_PATH:" + "/opt/ruby/"+this.gem_folder) + this.cli.log("Zip Path:" + this.gemLayer_zip_path ) + } if (!this.serverless.service.layers) { this.serverless.service.layers = {}; } this.serverless.service.layers['gemLayer'] = Object.assign( { - package: {artifact: path.join(this.ruby_layer, 'gemLayer.zip')}, + package: {artifact: this.gemLayer_zip_path }, name: `${ this.serverless.service.service }-${this.serverless.providers.aws.getStage()}-ruby-bundle`, @@ -160,6 +198,9 @@ function configureLayer() { ); Object.keys(this.serverless.service.functions).forEach(funcName => { + if(this.debug){ + this.cli.log("Configuring Layer for function: " + funcName) + } const function_ = this.serverless.service.getFunction(funcName) function_.environment={} function_.environment["GEM_PATH"]="/opt/ruby/"+this.gem_folder diff --git a/test/helper.js b/test/helper.js index 0420203..99177f2 100644 --- a/test/helper.js +++ b/test/helper.js @@ -6,8 +6,12 @@ const path = require('path'); function runCommand(cmd,args,options) { const ps = spawnSync(cmd, args,options); - console.log(ps.stderr.toString()) - console.log(ps.stdout.toString()); + if (ps.stdout){ + console.log(ps.stdout.toString()) + } + if (ps.stderr){ + console.log(ps.stderr.toString()) + } if (ps.error) { throw new Error(ps.error); } else if (ps.status !== 0) {