loadrunner 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ed96c8d23afef79bc83474cdeb980cdea9a27177
4
+ data.tar.gz: 0bf010a529fad2cb7b1aa6b701be77e719e8c213
5
+ SHA512:
6
+ metadata.gz: 0c32f1097bcee3743adc63cb1148fecf533b4e78fdd24e1a2f1ead011f7e9301431f4894bb1fa30e3db081c3badb100c252a9389961fba5f09c17406ea3f4537
7
+ data.tar.gz: 2c47338a7941d91f70b91b260fa0806f6cc97694f6520f9d1e02c68f71a9b27884befdd9c0ae9000bc3a1c18c4d30483d04c0a943c286340e68006adf8324357
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ LoadRunner - GitHub Webhook Server and Event Simulator
2
+ ======================================================
3
+
4
+ [![Gem](https://img.shields.io/gem/v/loadrunner.svg?style=flat-square)](https://rubygems.org/gems/loadrunner)
5
+ [![Travis](https://img.shields.io/travis/DannyBen/loadrunner.svg?style=flat-square)](https://travis-ci.org/DannyBen/loadrunner)
6
+ [![Code Climate](https://img.shields.io/codeclimate/github/DannyBen/loadrunner.svg?style=flat-square)](https://codeclimate.com/github/DannyBen/loadrunner)
7
+ [![Gemnasium](https://img.shields.io/gemnasium/DannyBen/loadrunner.svg?style=flat-square)](https://gemnasium.com/DannyBen/loadrunner)
8
+
9
+ ---
10
+
11
+ LoadRunner is a dual-purpose utility for working with GitHub webhooks.
12
+
13
+ It provides these features:
14
+
15
+ - A webserver that responds to GitHub webhook events and can run any
16
+ arbitrary script writtn in any language.
17
+ - A command line utility for testing your webhook server configuration.
18
+
19
+ ---
20
+
21
+ Install
22
+ --------------------------------------------------
23
+
24
+ ```
25
+ $ gem install loadrunner
26
+ ```
27
+
28
+ Getting Started
29
+ --------------------------------------------------
30
+
31
+ Soon
32
+
33
+ Usage
34
+ --------------------------------------------------
35
+
36
+ Soon
data/bin/loadrunner ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'loadrunner'
4
+
5
+ begin
6
+ LoadRunner::CommandLine.instance.execute ARGV
7
+ rescue => e
8
+ STDERR.puts "#{e.class} - #{e.message}"
9
+ end
@@ -0,0 +1,44 @@
1
+ require 'sinatra'
2
+ require "sinatra/reloader" if development?
3
+ require 'json'
4
+ require 'active_support/core_ext/hash/indifferent_access'
5
+ require 'byebug'
6
+
7
+ set port: 3000
8
+ set bind: '0.0.0.0'
9
+
10
+ ENV['SECRET_TOKEN'] = '123'
11
+
12
+ post '/payload' do
13
+ request.env['HTTP_X_HUB_SIGNATURE']
14
+
15
+ request.body.rewind
16
+ payload_body = request.body.read
17
+
18
+ verify_signature payload_body
19
+
20
+ push = ActiveSupport::HashWithIndifferentAccess.new JSON.parse payload_body
21
+
22
+ branch = push[:ref] =~ /refs\/head/ ? push[:ref].sub('refs/heads/', '') : nil;
23
+ tag = push[:ref] =~ /refs\/tags/ ? push[:ref].sub('refs/tags/', '') : nil;
24
+
25
+ result = {
26
+ event: request.env['HTTP_X_GITHUB_EVENT'],
27
+ repo: push[:repository][:name],
28
+ branch: branch,
29
+ tag: tag
30
+ # payload: push
31
+ }
32
+
33
+ result.inspect
34
+ end
35
+
36
+ post '/debug' do
37
+ request.env['HTTP_X_HUB_SIGNATURE']
38
+ end
39
+
40
+ def verify_signature(payload_body)
41
+ signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), ENV['SECRET_TOKEN'], payload_body)
42
+ signature_match = Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE'])
43
+ return halt 401, "Bad Signature" unless signature_match
44
+ end
@@ -0,0 +1,70 @@
1
+ require 'httparty'
2
+
3
+ module LoadRunner
4
+ class Client
5
+ include HTTParty
6
+ attr_accessor :secret_token, :base_url, :payload
7
+
8
+ def initialize(opts={})
9
+ @secret_token = opts[:secret_token]
10
+ @base_url = opts[:base_url] || 'localhost:3000'
11
+ self.class.base_uri base_url
12
+ end
13
+
14
+ def send(event=:push, opts={})
15
+ payload = build_payload opts
16
+ send_payload event, payload
17
+ end
18
+
19
+ def send_payload(event=:push, payload)
20
+ @payload = payload.is_a?(String) ? payload : payload.to_json
21
+ self.class.post "/payload", body: @payload, headers: headers(:push)
22
+ end
23
+
24
+ private
25
+
26
+ def headers(event=:push)
27
+ {}.tap do |header|
28
+ header['X_GITHUB_EVENT'] = event.to_s if event
29
+ header['X_HUB_SIGNATURE'] = signature if secret_token
30
+ end
31
+ end
32
+
33
+ def signature
34
+ return nil unless secret_token
35
+ signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), secret_token, payload)
36
+ end
37
+
38
+ def build_payload(opts={})
39
+ {}.tap do |result|
40
+ result[:ref] = ref_from_opts opts
41
+ result[:repository] = repo_from_opts opts
42
+ end
43
+ end
44
+
45
+ def ref_from_opts(opts)
46
+ if opts[:ref]
47
+ opts[:ref]
48
+ elsif opts[:branch]
49
+ "refs/heads/#{opts[:branch]}"
50
+ elsif opts[:tag]
51
+ "refs/tags/#{opts[:tag]}"
52
+ end
53
+ end
54
+
55
+ def repo_from_opts(opts)
56
+ if opts[:repo] =~ /.+\/.+/
57
+ _owner, name = opts[:repo].split '/'
58
+ {
59
+ name: name,
60
+ full_name: opts[:repo]
61
+ }
62
+ else
63
+ {
64
+ name: opts[:repo]
65
+ }
66
+ end
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,62 @@
1
+ require 'singleton'
2
+ require 'docopt'
3
+
4
+ module LoadRunner
5
+
6
+ # Handles the command line interface
7
+ class CommandLine
8
+ include Singleton
9
+
10
+ attr_reader :args
11
+
12
+ # Gets an array of arguments (e.g. ARGV), executes the command if valid
13
+ # and shows usage patterns / help otherwise.
14
+ def execute(argv=[])
15
+ doc = File.read File.dirname(__FILE__) + '/docopt.txt'
16
+ begin
17
+ @args = Docopt::docopt(doc, argv: argv, version: VERSION)
18
+ handle
19
+ rescue Docopt::Exit => e
20
+ puts e.message
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ # Called when the arguments match one of the usage patterns. Will
27
+ # delegate action to other, more specialized methods.
28
+ def handle
29
+ return send if args['send']
30
+ return server if args['server']
31
+ end
32
+
33
+ def send
34
+ client = Client.new client_opts
35
+ response = client.send args['EVENT'], payload_opts
36
+ puts response
37
+ end
38
+
39
+ def server
40
+ Server.prepare port: args['--port'], bind: args['--bind']
41
+ Server.run!
42
+ end
43
+
44
+ def client_opts
45
+ {
46
+ base_url: args['URL'],
47
+ secret_token: '123'
48
+ }
49
+ end
50
+
51
+ def payload_opts
52
+ result = { repo: args['REPO'] }
53
+
54
+ ref = args['REF']
55
+ ref = "refs/tags/#{$1}" if ref =~ /^tag:(.+)/
56
+ ref = "refs/heads/#{$1}" if ref =~ /^branch:(.+)/
57
+
58
+ result[:ref] = ref
59
+ result
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,55 @@
1
+ LoadRunner
2
+
3
+ Usage:
4
+ loadrunner server [--port N --bind IP]
5
+ loadrunner send URL REPO EVENT REF
6
+ loadrunner (-h|--help|--version)
7
+
8
+ Commands:
9
+ server
10
+ Start the webhook server.
11
+
12
+ send
13
+ Send a simulated GitHub event to a webhook server.
14
+
15
+ Parameters:
16
+ URL
17
+ The URL of the webhook server. This server should have a /payload
18
+ endpoint that responds to POST requests.
19
+
20
+ REPO
21
+ The name of the repository. This can be either the short name
22
+ (my_repo), or the full name (my_name/my_repo).
23
+
24
+ EVENT
25
+ Any GitHub event, for example: push or ping.
26
+
27
+ REF
28
+ A branch or tag specifier. This parameter supports three formats:
29
+ * tag:tag_name
30
+ * branch:branch_name
31
+ * raw ref string (for example /refs/tags/tagname)
32
+
33
+ Options:
34
+ --port N
35
+ Set the port of the webhook server.
36
+
37
+ --bind IP
38
+ Set the listening address of the webhook server.
39
+
40
+
41
+ Environment Variables:
42
+ GITHUB_SECRET_TOKEN=y0urAP1k3y
43
+ Set Your GitHub secret token.
44
+
45
+ Examples:
46
+ # Simulate push events
47
+ loadrunner send localhost:3000 my_repo push branch:master
48
+ loadrunner send localhost:3000 my_repo push tag:staging
49
+ loadrunner send localhost:3000 my_repo push refs/tags/staging
50
+
51
+ # Start the server
52
+ loadrunner server
53
+ loadrunner server --bind 0.0.0.0 --port 3000
54
+
55
+
@@ -0,0 +1,3 @@
1
+ module LoadRunner
2
+ class Error < StandardError; end
3
+ end
@@ -0,0 +1,35 @@
1
+ module LoadRunner
2
+
3
+ # The Sinatra server
4
+ class Server < ServerBase
5
+ post '/payload' do
6
+ request.body.rewind
7
+ payload_body = request.body.read
8
+
9
+ verify_signature payload_body if secret_token
10
+
11
+ push = ActiveSupport::HashWithIndifferentAccess.new JSON.parse payload_body
12
+
13
+ branch = push[:ref] =~ /refs\/heads/ ? push[:ref].sub('refs/heads/', '') : nil
14
+ tag = push[:ref] =~ /refs\/tags/ ? push[:ref].sub('refs/tags/', '') : nil
15
+ repo = push[:repository][:name]
16
+ event = request.env['HTTP_X_GITHUB_EVENT']
17
+
18
+ result = { event: event, repo: repo, branch: branch, tag: tag }
19
+ result.inspect
20
+
21
+ file = "handlers/#{repo}/#{event}"
22
+ File.exist?(file) ? `#{file}` : result.inspect
23
+ end
24
+
25
+ def verify_signature(payload_body)
26
+ signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), secret_token, payload_body)
27
+ signature_match = Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE'])
28
+ return halt 401, "Bad Signature" unless signature_match
29
+ end
30
+
31
+ def secret_token
32
+ ENV['GITHUB_SECRET_TOKEN']
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ require 'sinatra/base'
2
+ require "sinatra/reloader"
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+
5
+ module LoadRunner
6
+
7
+ # The base class for the sinatra server.
8
+ # Initialize what we can here, but since there are values that will
9
+ # become known only later, the #prepare method is provided.
10
+ class ServerBase < Sinatra::Application
11
+ set :root, File.expand_path('../../', __dir__)
12
+ set :server, :puma
13
+
14
+ configure :development do
15
+ register Sinatra::Reloader if defined? Sinatra::Reloader
16
+ end
17
+
18
+ # Since we cannot use any config values in the main body of the class,
19
+ # since they will be updated later, we need to set anything that relys
20
+ # on the config values just before running the server.
21
+ # The CommandLine class and the test suite should both call
22
+ # `Server.prepare` before calling Server.run!
23
+ def self.prepare(opts={})
24
+ set :bind, opts[:bind] || '0.0.0.0'
25
+ set :port, opts[:port] || '3000'
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,3 @@
1
+ module LoadRunner
2
+ VERSION = "0.0.1"
3
+ end
data/lib/loadrunner.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'load_runner/version'
2
+ require 'load_runner/exceptions'
3
+ require 'load_runner/server_base'
4
+ require 'load_runner/server'
5
+ require 'load_runner/client'
6
+ require 'load_runner/command_line'
metadata ADDED
@@ -0,0 +1,195 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: loadrunner
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Danny Ben Shitrit
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.14'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sinatra
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: puma
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.9'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: runfile
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.10'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.10'
83
+ - !ruby/object:Gem::Dependency
84
+ name: runfile-tasks
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.4'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.4'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.4'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.14'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.14'
125
+ - !ruby/object:Gem::Dependency
126
+ name: byebug
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '9.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '9.0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: sinatra-contrib
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '2.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '2.0'
153
+ description: Run your GitHub webhook server and Send simulated github events
154
+ email: db@dannyben.com
155
+ executables:
156
+ - loadrunner
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - README.md
161
+ - bin/loadrunner
162
+ - lib/load_runner/_server.rb
163
+ - lib/load_runner/client.rb
164
+ - lib/load_runner/command_line.rb
165
+ - lib/load_runner/docopt.txt
166
+ - lib/load_runner/exceptions.rb
167
+ - lib/load_runner/server.rb
168
+ - lib/load_runner/server_base.rb
169
+ - lib/load_runner/version.rb
170
+ - lib/loadrunner.rb
171
+ homepage: https://github.com/DannyBen/loadrunner
172
+ licenses:
173
+ - MIT
174
+ metadata: {}
175
+ post_install_message:
176
+ rdoc_options: []
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: 2.0.0
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ requirements: []
190
+ rubyforge_project:
191
+ rubygems_version: 2.6.11
192
+ signing_key:
193
+ specification_version: 4
194
+ summary: GitHub Webhook Server and Simulator
195
+ test_files: []