Skip to content

Commit e396fa1

Browse files
authored
Merge pull request #13 from navarasu/fix_12_use_dockerfile
Added Option to use custom docker file for bundling gem
2 parents 04ed022 + 6b06ecf commit e396fa1

File tree

8 files changed

+83
-31
lines changed

8 files changed

+83
-31
lines changed

examples/using_docker_file/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FROM lambci/lambda:build-ruby2.5
2+
3+
RUN yum install -y postgresql-devel
4+
RUN gem update bundler
5+
6+
CMD "/bin/bash"

examples/using_docker_file/Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
source 'https://rubygems.org'
2+
gem 'pg'

examples/using_docker_file/handler.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
require 'pg'
2+
3+
def hello(event:, context:)
4+
{ statusCode: 200, body: {
5+
"pg_version": PG.library_version
6+
}
7+
}
8+
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
service: using-docker-file
2+
3+
plugins:
4+
- serverless-ruby-layer
5+
6+
custom:
7+
rubyLayer:
8+
use_docker: true
9+
docker_file: Dockerfile
10+
native_libs:
11+
- /usr/lib64/libpq.so.5
12+
13+
provider:
14+
name: aws
15+
runtime: ruby2.5
16+
17+
functions:
18+
hello:
19+
handler: handler.hello

lib/bundle.js

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,52 +5,70 @@ const Promise = require('bluebird');
55
var JSZip = require('jszip');
66
Promise.promisifyAll(fs);
77

8-
function runCommand(cmd,args,options) {
8+
function runCommand(cmd,args,options,cli) {
99
const ps = spawnSync(cmd, args,options);
10-
if (ps.error) {
10+
cli.log(ps.stdout.toString())
11+
if (ps.error && ps.error.code != 'ENOENT') {
12+
cli.log(ps.stderr.toString())
1113
throw new Error(ps.error);
1214
} else if (ps.status !== 0) {
15+
cli.log(ps.stderr.toString())
1316
throw new Error(ps.stderr);
1417
}
1518
return ps;
1619
}
1720

18-
function docker(args, options){
19-
return runCommand("docker",args,options)
21+
function docker(args, options,cli){
22+
return runCommand("docker",args,options,cli)
2023
}
2124

2225
function cleanBuild(){
23-
this.serverless.cli.log("Cleaning ruby layer build")
24-
this.build_path = path.join(this.servicePath,'.serverless','build','ruby_layer')
25-
if (fs.pathExistsSync(this.build_path)){
26-
fs.rmdirSync(this.build_path, { recursive: true })
26+
this.cli = this.serverless.cli
27+
this.cli.log("Cleaning ruby layer build")
28+
this.ruby_layer = path.join(this.servicePath,'.serverless','ruby_layer')
29+
if (fs.pathExistsSync(this.ruby_layer)){
30+
fs.rmdirSync(this.ruby_layer, { recursive: true })
2731
}
2832
}
29-
3033
function bundleInstall(){
31-
this.serverless.cli.log(this.build_path)
32-
const gem_path = path.join(this.build_path,'Gemfile')
34+
this.cli.log(this.ruby_layer)
35+
const gem_path = path.join(this.ruby_layer,'Gemfile')
3336
fs.copySync(path.join(this.servicePath,'Gemfile'), gem_path )
34-
const bundle_args = ['bundle', 'install', '--path=.','--without', 'test', 'development']
35-
const options={cwd : this.build_path, encoding : 'utf8'}
37+
const bundle_args = ['bundle', 'install', '--path=build','--without', 'test', 'development']
38+
const options={cwd : this.ruby_layer, encoding : 'utf8'}
39+
this.build_path = path.join(this.ruby_layer, 'build')
40+
fs.mkdirSync(this.build_path)
3641
if (this.options.use_docker) {
3742
docker_name = 'lambci/lambda:build-'+this.serverless.service.provider.runtime
38-
ps=docker(['version'], options)
43+
ps=docker(['version'], options,this.cli)
3944
if (ps.error && ps.error.code === 'ENOENT') {
4045
throw new Error('docker command not found');
4146
}
42-
args=['run','--rm', '-i','-v', `${this.build_path}:/var/gem_build`, '-w', '/var/gem_build']
47+
if (this.options.docker_file) {
48+
fs.copySync(path.join(this.servicePath, this.options.docker_file),
49+
path.join(this.ruby_layer,'Dockerfile'))
50+
docker_name ='ruby-layer:docker'
51+
docker(['build', '-t', docker_name, '-f', 'Dockerfile', '.'], options,this.cli)
52+
}
53+
if (this.options.native_libs) {
54+
ps=docker(['run', '-d', docker_name, 'false'], options,this.cli)
55+
container_id = ps.stdout.toString().trim()
56+
const lib_path = path.join(this.build_path,'lib')
57+
fs.mkdirSync(lib_path)
58+
this.options.native_libs.forEach(lib_to_copy => {
59+
ps=docker(['cp','-L', container_id+':'+lib_to_copy, lib_path],options,this.cli)
60+
})
61+
}
62+
args=['run','--rm', '-i','-v', `${this.ruby_layer}:/var/gem_build`, '-w', '/var/gem_build']
4363
args.push(docker_name)
44-
ps=docker(args.concat(bundle_args), options)
45-
this.serverless.cli.log(ps.stdout)
64+
docker(args.concat(bundle_args), options,this.cli)
4665
} else {
47-
ps = runCommand("bundle",['-v'])
66+
ps = runCommand("bundle",['-v'],options,this.cli)
4867
if (ps.error && ps.error.code === 'ENOENT') {
4968
throw new Error('bundle command not found in local');
5069
}
51-
this.serverless.cli.log(bundle_args.slice(1,bundle_args.length))
52-
ps=runCommand(bundle_args[0],bundle_args.slice(1,bundle_args.length),options)
53-
this.serverless.cli.log(ps.stdout)
70+
this.cli.log(bundle_args.slice(1,bundle_args.length))
71+
runCommand(bundle_args[0],bundle_args.slice(1,bundle_args.length),options,this.cli)
5472
}
5573
}
5674

@@ -91,14 +109,13 @@ function zipBundleFolder() {
91109
this.gem_folder= fs.readdirSync(path.join(this.build_path,'ruby'))[0]
92110
fs.rmdirSync(path.join(this.build_path,'ruby',this.gem_folder,'cache'),{ recursive: true })
93111
const platform = process.platform == 'win32' ? 'DOS' : 'UNIX'
94-
return zipDir(path.join(this.build_path,'ruby'),
95-
path.join(this.build_path, 'gemLayer.zip'),
112+
return zipDir(this.build_path,
113+
path.join(this.ruby_layer, 'gemLayer.zip'),
96114
{ platform: platform, compression: 'DEFLATE',
97115
compressionOptions: { level: 9 }});
98116
}
99117

100118
function excludePackage(){
101-
this.serverless.cli.log(JSON.stringify(this.serverless.service.package))
102119
if (!this.serverless.service.package){
103120
this.serverless.service.package = Object.assign({})
104121
}
@@ -115,7 +132,7 @@ function configureLayer() {
115132
}
116133
this.serverless.service.layers['gemLayer'] = Object.assign(
117134
{
118-
package: {artifact: path.join(this.build_path, 'gemLayer.zip')},
135+
package: {artifact: path.join(this.ruby_layer, 'gemLayer.zip')},
119136
name: `${
120137
this.serverless.service.service
121138
}-${this.serverless.providers.aws.getStage()}-ruby-bundle`,
@@ -129,7 +146,7 @@ function configureLayer() {
129146
Object.keys(this.serverless.service.functions).forEach(funcName => {
130147
const function_ = this.serverless.service.getFunction(funcName)
131148
function_.environment={}
132-
function_.environment["GEM_PATH"]="/opt/"+this.gem_folder
149+
function_.environment["GEM_PATH"]="/opt/ruby/"+this.gem_folder
133150
function_.layers = [{"Ref":"GemLayerLambdaLayer"}]
134151
})
135152
return Promise.resolve();

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/helper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function readZip(filePath){
2323
.then(function (zip) {
2424
let check = []
2525
zip.forEach(function (relativePath, file){
26-
if (file.dir && relativePath.split(path.sep).length <= 4){
26+
if (file.dir && relativePath.split(path.sep).length <= 5){
2727
check.push(relativePath)
2828
} else if (relativePath.split(path.sep).length <= 1) {
2929
check.push(relativePath)

test/test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('serverless package', function () {
1919
runCommand('npm',['link','serverless-ruby-layer'],options)
2020
runCommand('sls',['package'],options)
2121
let dot_serverless_path = path.join(context_path,'.serverless')
22-
let layer_zip_path = path.join(dot_serverless_path,'build','ruby_layer','gemLayer.zip')
22+
let layer_zip_path = path.join(dot_serverless_path,'ruby_layer','gemLayer.zip')
2323
let function_zip_path = path.join(dot_serverless_path,'basic.zip')
2424
value = readZip(function_zip_path)
2525
.then(function(data){
@@ -32,7 +32,7 @@ describe('serverless package', function () {
3232
'/gems/','/specifications/','/gems/httparty-0.18.1/',
3333
'/gems/mime-types-3.3.1/','/gems/multi_xml-0.6.0/',
3434
'/gems/mime-types-data-3.2020.0512/']
35-
.map(data => run_time+'.0'+data).sort(),
35+
.map(data => 'ruby/'+run_time+'.0'+data).concat(['ruby/']).sort(),
3636
data.sort())
3737
})
3838
let rawdata = fs.readFileSync(path.join(dot_serverless_path,'serverless-state.json'));
@@ -51,6 +51,6 @@ describe('serverless package', function () {
5151
})
5252
assert.deepEqual(cloud_resource['HelloLambdaFunction']['Properties']['Layers'],[ { Ref: 'GemLayerLambdaLayer' } ])
5353
assert.deepEqual(cloud_resource['HelloLambdaFunction']['Properties']['Environment'],
54-
{Variables: { GEM_PATH: '/opt/'+run_time+'.0'}})
54+
{Variables: { GEM_PATH: '/opt/ruby/'+run_time+'.0'}})
5555
});
5656
});

0 commit comments

Comments
 (0)