lambchop 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 90f7063b4c0006042d5adcd83ad566cba3e68044
4
+ data.tar.gz: d7ff894f8e56e429fa0979840236109b1f445d6a
5
+ SHA512:
6
+ metadata.gz: b34f002a00f6e12631f911401b98b528841caac03f95b22e90bb4feb46148a2af5cc18fb712060e1e3322dea9625ee03bfc3b4d460bdc2bd3dc74da820513479
7
+ data.tar.gz: 872d87cca199322ac09a4af79913d322e523167a517d2ebd8ae79ddd9d6b9884e6e341f157c57237b69b0f1c563c6972f061700a0ada2d5ffbe0a2f75dddd188
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ test.rb
16
+ test.js
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lambchop.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Genki Sugawara
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # Lambchop
2
+
3
+ It is a tool that invoke AWS Lambda function from the local machine as a normally script.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'lambchop'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install lambchop
20
+
21
+ ## Usage
22
+
23
+ **Terminal 1**:
24
+ ```sh
25
+ $ cat test.js
26
+ #!/usr/bin/env lambchop
27
+ /*
28
+ function_name: test # default: file name without ext
29
+ runtime: nodejs # default: nodejs
30
+ mode: event # default: event
31
+ description: '' # default: (empty)
32
+ timeout: 3 # default: 3
33
+ memory_size: 128 # default: 128
34
+ role: arn:aws:iam::NNNNNNNNNNNN:role/lambda_exec_role
35
+ handler: test.handler
36
+ */
37
+ console.log('Loading event');
38
+
39
+ exports.handler = function(event, context) {
40
+ console.log('value1 = ' + event.key1);
41
+ console.log('value2 = ' + event.key2);
42
+ console.log('value3 = ' + event.key3);
43
+ context.done(null, 'Hello World'); // SUCCESS with message
44
+ };
45
+
46
+ $ ./test.js
47
+ (Wait event...)
48
+ ```
49
+
50
+ **Terminal 2**:
51
+ ```sh
52
+ $ lambchop-cat
53
+ usage: lambchop-cat <function-name>
54
+ $ lambchop-cat
55
+ $ echo '{"key1":100, "key2":200, "key3":300}' | bundle exec ./bin/lambchop-cat test
56
+ ```
57
+
58
+ **Terminal 1**:
59
+ ```sh
60
+ 2014-11-23T08:06:53.212Z xxxxxxxxxxxxxxxx Loading event
61
+ START RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
62
+ 2014-11-23T08:06:53.330Z xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx value1 = 100
63
+ 2014-11-23T08:06:53.330Z xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx value3 = 300
64
+ 2014-11-23T08:06:53.330Z xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx value2 = 200
65
+ END RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
66
+ REPORT RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Duration: 117.54 ms Billed Duration: 200 ms Memory Size: 128 MB Max Memory Used: 9 MB
67
+ ```
68
+
69
+ ### Dump function
70
+
71
+ ```sh
72
+ $ lambchop-dump
73
+ usage: lambchop-dump <function-name>
74
+
75
+ $ lambchop-dump test
76
+ #!/usr/bin/env lambchop
77
+ /*
78
+ function_name: test
79
+ runtime: nodejs
80
+ role: arn:aws:iam::NNNNNNNNNNNN:role/lambda_exec_role
81
+ handler: test.handler
82
+ mode: event
83
+ description: ''
84
+ timeout: 3
85
+ memory_size: 128
86
+ */
87
+ console.log('Loading event');
88
+
89
+ exports.handler = function(event, context) {
90
+ console.log('value1 = ' + event.key1);
91
+ console.log('value2 = ' + event.key2);
92
+ console.log('value3 = ' + event.key3);
93
+ context.done(null, 'Hello World'); // SUCCESS with message
94
+ };
95
+ ```
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+
data/bin/lambchop ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path("#{File.dirname __FILE__}/../lib")
3
+ require 'rubygems'
4
+ require 'lambchop'
5
+
6
+ Lambchop::Client.start(ARGF.read, ARGF.path)
data/bin/lambchop-cat ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path("#{File.dirname __FILE__}/../lib")
3
+ require 'rubygems'
4
+ require 'lambchop'
5
+
6
+ if ARGV.length != 1
7
+ puts 'usage: lambchop-cat <function-name>'
8
+ exit 1
9
+ end
10
+
11
+ Lambchop::Cat.cat(ARGV[0], $stdin)
data/bin/lambchop-dump ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path("#{File.dirname __FILE__}/../lib")
3
+ require 'rubygems'
4
+ require 'lambchop'
5
+
6
+ if ARGV.length != 1
7
+ puts 'usage: lambchop-dump <function-name>'
8
+ exit 1
9
+ end
10
+
11
+ Lambchop::Dump.dump(ARGV[0])
data/lambchop.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lambchop/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'lambchop'
8
+ spec.version = Lambchop::VERSION
9
+ spec.authors = ['Genki Sugawara']
10
+ spec.email = ['sgwr_dts@yahoo.co.jp']
11
+ spec.summary = %q{It is a tool that invoke AWS Lambda function from the local machine as a normally script.}
12
+ spec.description = %q{It is a tool that invoke AWS Lambda function from the local machine as a normally script.}
13
+ spec.homepage = 'https://github.com/winebarrel/lambchop'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+
22
+ spec.add_dependency 'aws-sdk-core', '~> 2.0.9'
23
+ spec.add_dependency 'rubyzip', '>= 1.0.0'
24
+ spec.add_development_dependency 'bundler'
25
+ spec.add_development_dependency 'rake'
26
+ end
@@ -0,0 +1,25 @@
1
+ class Lambchop::Cat
2
+ def self.cat(function_name, invoke_args, options = {})
3
+ self.new(function_name, invoke_args, options).cat
4
+ end
5
+
6
+ def initialize(function_name, invoke_args, options = {})
7
+ @function_name = function_name
8
+ @invoke_args = invoke_args
9
+ @client = options[:client] || Aws::Lambda::Client.new
10
+ @options = options
11
+ end
12
+
13
+ def cat
14
+ invoke_args = @invoke_args
15
+
16
+ if invoke_args.kind_of?(IO)
17
+ invoke_args = invoke_args.read
18
+ end
19
+
20
+ @client.invoke_async(
21
+ :function_name => @function_name,
22
+ :invoke_args => invoke_args
23
+ )
24
+ end
25
+ end
@@ -0,0 +1,67 @@
1
+ class Lambchop::Client
2
+ def self.start(source, path, options = {})
3
+ self.new(source, path, options).start
4
+ end
5
+
6
+ def initialize(source, path, options = {})
7
+ @source = source
8
+ @path = path
9
+ @client = options[:client] || Aws::Lambda::Client.new
10
+ @options = options
11
+ end
12
+
13
+ def start
14
+ src = remove_shebang(@source)
15
+ config, src = parse_magic_comment(src)
16
+
17
+ config['function_name'] ||= File.basename(@path, '.js')
18
+ function_name = config['function_name']
19
+
20
+ config['mode'] ||= 'event'
21
+ config['runtime'] ||= 'nodejs'
22
+
23
+ resp = upload_function(config, src)
24
+ $stderr.puts('Function was uploaded:')
25
+ $stderr.puts(JSON.pretty_generate(resp.to_h))
26
+
27
+ Lambchop::WatchDog.start(function_name, @options[:watch_dog] || {})
28
+
29
+ sleep
30
+ end
31
+
32
+ private
33
+
34
+ def upload_function(config, src)
35
+ params = {}
36
+ config.each {|k, v| params[k.to_sym] = v }
37
+ params[:function_zip] = zip_source(src).string
38
+ @client.upload_function(params)
39
+ end
40
+
41
+ def zip_source(src)
42
+ Zip::OutputStream.write_buffer do |out|
43
+ out.put_next_entry(File.basename(@path))
44
+ out.write(src)
45
+ end
46
+ end
47
+
48
+ def remove_shebang(src)
49
+ src.sub(/\A#![^\n]*\n/, '')
50
+ end
51
+
52
+ def parse_magic_comment(src)
53
+ ss = StringScanner.new(src)
54
+
55
+ unless ss.scan(%r|\A\s*/\*|)
56
+ raise 'Cannot find magic comment'
57
+ end
58
+
59
+ unless comment = ss.scan_until(%r|\*/|)
60
+ raise 'Cannot find magic comment'
61
+ end
62
+
63
+ comment.sub!(%r|\*/\z|, '')
64
+
65
+ [YAML.load(comment), ss.rest.sub(/\A\n/, '')]
66
+ end
67
+ end
@@ -0,0 +1,53 @@
1
+ class Lambchop::Dump
2
+ def self.dump(function_name, options = {})
3
+ self.new(function_name, options).dump
4
+ end
5
+
6
+ def initialize(function_name, options = {})
7
+ @function_name = function_name
8
+ @client = options[:client] || Aws::Lambda::Client.new
9
+ @out = options[:out] || $stdout
10
+ @options = options
11
+ end
12
+
13
+ def dump
14
+ page = @client.get_function(:function_name => @function_name).first
15
+ puts_shebang
16
+ puts_magic_comment(page.configuration)
17
+ puts_source(page.code.location)
18
+ end
19
+
20
+ def puts_shebang
21
+ @out.puts('#!/usr/bin/env lambchop')
22
+ end
23
+
24
+ def puts_magic_comment(configuration)
25
+ comment_attrs = {}
26
+
27
+ %w(
28
+ function_name
29
+ runtime
30
+ role
31
+ handler
32
+ mode
33
+ description
34
+ timeout
35
+ memory_size
36
+ ).each do |name|
37
+ comment_attrs[name] = configuration.send(name)
38
+ end
39
+
40
+ yaml = YAML.dump(comment_attrs).sub(/\A---\n/, '')
41
+ @out.puts("/*\n#{yaml}*/")
42
+ end
43
+
44
+ def puts_source(location)
45
+ open(location) do |f|
46
+ Zip::InputStream.open(f) do |zis|
47
+ while entry = zis.get_next_entry
48
+ @out.puts(entry.get_input_stream.read)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Lambchop
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,85 @@
1
+ Thread::abort_on_exception = true
2
+
3
+ class Lambchop::WatchDog
4
+ def self.start(function_name, options = {})
5
+ self.new(function_name, options).start
6
+ end
7
+
8
+ def initialize(function_name, options = {})
9
+ @function_name = function_name
10
+ @log_group_name = "/aws/lambda/#{@function_name}"
11
+ @client = options[:client] || Aws::CloudWatchLogs::Client.new(region: 'us-east-1')
12
+ @start_time = options[:start_time] || Time.now
13
+ @out = options[:out] || $stdout
14
+ @last_timestamp = time_to_timestamp(@start_time) - 1
15
+ @interval = options[:interval] || 1
16
+ @options = options
17
+ end
18
+
19
+ def start
20
+ @running = true
21
+
22
+ Thread.start do
23
+ while @running
24
+ follow_logs
25
+ sleep @interval
26
+ end
27
+ end
28
+ end
29
+
30
+ def stop
31
+ @running = false
32
+ end
33
+
34
+ private
35
+
36
+ def follow_logs
37
+ all_events = []
38
+ log_streams.each do |log_stream|
39
+ last_ingestion_time = log_stream.last_ingestion_time
40
+
41
+ if last_ingestion_time.nil? or last_ingestion_time < @last_timestamp
42
+ next
43
+ end
44
+
45
+ log_events(log_stream.log_stream_name).each do |event|
46
+ next if event.timestamp <= @last_timestamp
47
+ all_events << event
48
+ end
49
+ end
50
+
51
+ all_events.sort_by(&:timestamp).each do |event|
52
+ @out.puts(event.message)
53
+ @last_timestamp = event.timestamp
54
+ end
55
+ end
56
+
57
+ def log_streams
58
+ pages = @client.describe_log_streams(:log_group_name => @log_group_name)
59
+ pages.map(&:log_streams).flatten(1)
60
+ rescue Aws::CloudWatchLogs::Errors::ResourceNotFoundException
61
+ []
62
+ end
63
+
64
+ def log_events(log_stream_name)
65
+ events = []
66
+
67
+ pages = @client.get_log_events(
68
+ :log_group_name => @log_group_name,
69
+ :log_stream_name => log_stream_name,
70
+ :start_time => @last_timestamp
71
+ )
72
+
73
+ pages.each do |page|
74
+ break if page.events.empty?
75
+ events.concat(page.events)
76
+ end
77
+
78
+ events
79
+ end
80
+
81
+ def time_to_timestamp(time)
82
+ ts = time.to_i * 1000
83
+ ts + time.usec / 1000
84
+ end
85
+ end
data/lib/lambchop.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'open-uri'
2
+ require 'yaml'
3
+
4
+ require 'aws-sdk-core'
5
+ require 'zip'
6
+
7
+ module Lambchop; end
8
+
9
+ require 'lambchop/cat'
10
+ require 'lambchop/client'
11
+ require 'lambchop/dump'
12
+ require 'lambchop/watch_dog'
13
+ require 'lambchop/version'
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lambchop
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Genki Sugawara
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.9
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.9
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubyzip
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: It is a tool that invoke AWS Lambda function from the local machine as
70
+ a normally script.
71
+ email:
72
+ - sgwr_dts@yahoo.co.jp
73
+ executables:
74
+ - lambchop
75
+ - lambchop-cat
76
+ - lambchop-dump
77
+ extensions: []
78
+ extra_rdoc_files: []
79
+ files:
80
+ - .gitignore
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - bin/lambchop
86
+ - bin/lambchop-cat
87
+ - bin/lambchop-dump
88
+ - lambchop.gemspec
89
+ - lib/lambchop.rb
90
+ - lib/lambchop/cat.rb
91
+ - lib/lambchop/client.rb
92
+ - lib/lambchop/dump.rb
93
+ - lib/lambchop/version.rb
94
+ - lib/lambchop/watch_dog.rb
95
+ homepage: https://github.com/winebarrel/lambchop
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.0.14
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: It is a tool that invoke AWS Lambda function from the local machine as a
119
+ normally script.
120
+ test_files: []