webcmd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ /.bundle
2
+ /pkg
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -0,0 +1,24 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ webcmd (0.0.1)
5
+ puma (~> 1.6.3)
6
+ rack (~> 1.4.1)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ puma (1.6.3)
12
+ rack (~> 1.2)
13
+ rack (1.4.1)
14
+ rack-test (0.6.1)
15
+ rack (>= 1.0)
16
+ rake (0.9.2.2)
17
+
18
+ PLATFORMS
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ rack-test
23
+ rake
24
+ webcmd!
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Sergey Nartimov
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.
@@ -0,0 +1,34 @@
1
+ # Webcmd
2
+
3
+ Run shell command through HTTP.
4
+
5
+ ## Installation
6
+
7
+ $ gem install webcmd
8
+
9
+ ## Usage
10
+
11
+ Use `COMMAND` environment variable to specify the command to be runned and `TOKEN` to protect the HTTP endpoint, e.g.:
12
+
13
+ $ COMMAND="echo it\'s working" TOKEN="1234" webcmd
14
+ Puma 1.6.3 starting...
15
+ * Min threads: 0, max threads: 1
16
+ * Environment: production
17
+ * Listening on tcp://0.0.0.0:9292
18
+
19
+ You could run `curl` in other terminal to check whether it's working:
20
+
21
+ $ curl http://localhost:9292/\?token=1234
22
+ it's working
23
+
24
+ To list available command-line options run:
25
+
26
+ $ webcmd -h
27
+
28
+ ## Contributing
29
+
30
+ 1. Fork it
31
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
32
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
33
+ 4. Push to the branch (`git push origin my-new-feature`)
34
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.pattern = 'test/*_test.rb'
7
+ t.verbose = true
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'webcmd'
4
+ Webcmd.run!
@@ -0,0 +1,7 @@
1
+ require 'webcmd/server'
2
+
3
+ module Webcmd
4
+ def self.run!
5
+ Server.new.start
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ require 'webcmd/command_runner'
2
+
3
+ module Webcmd
4
+ class App
5
+ def initialize(options={})
6
+ @command = options[:command]
7
+ @token = options[:token]
8
+ end
9
+
10
+ def call(env)
11
+ if token_valid?(env)
12
+ [200, {'Content-Type' => 'text/plain'}, CommandRunner.new(@command)]
13
+ else
14
+ [403, {'Content-Type' => 'text/plain'}, ['Token invalid']]
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def token_valid?(env)
21
+ req = Rack::Request.new(env)
22
+ req.params['token'] == @token
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ module Webcmd
2
+ class CommandRunner
3
+ def initialize(command)
4
+ @command = command
5
+ end
6
+
7
+ def each(&block)
8
+ IO.popen([shell_env, 'bash', '-l', err: [:child, :out]], 'w+') do |shell|
9
+ shell.write @command
10
+ shell.close_write
11
+ shell.each_line(&block)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def shell_env
18
+ Hash[ENV.keys.grep(/^BUNDLE_/).map { |k| [k, nil] }]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,49 @@
1
+ require 'optparse'
2
+ require 'forwardable'
3
+
4
+ module Webcmd
5
+ class Options
6
+ extend Forwardable
7
+
8
+ def initialize(argv)
9
+ @parser = OptionParser.new(argv) do |opts|
10
+ opts.banner = "Usage: webcmd [options]"
11
+
12
+ opts.on '-o', '--host HOST', 'listen on HOST (default: 0.0.0.0)' do |host|
13
+ options[:Host] = host
14
+ end
15
+
16
+ opts.on '-p', '--port PORT', 'use PORT (default: 9292)' do |port|
17
+ @options[:Port] = port
18
+ end
19
+
20
+ opts.on '-t', '--threads INT', 'min:max threads to use (default 0:1)' do |threads|
21
+ @options[:Threads] = threads
22
+ end
23
+
24
+ opts.on '-D', '--daemonize', 'run daemonized in the background' do |d|
25
+ @options[:daemonize] = !!d
26
+ end
27
+
28
+ opts.on_tail '-h', '--help', 'show this message' do
29
+ puts opts
30
+ exit
31
+ end
32
+
33
+ opts.on_tail '--version', 'show version' do
34
+ puts "webcmd #{Webcmd::VERSION}"
35
+ exit
36
+ end
37
+ end
38
+ end
39
+
40
+ def parse!
41
+ @options = { app: {} }
42
+ @parser.parse!
43
+ @options[:app][:command] = ENV['COMMAND']
44
+ @options[:app][:token] = ENV['TOKEN']
45
+
46
+ @options
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,41 @@
1
+ require 'rack'
2
+ require 'webcmd/app'
3
+ require 'webcmd/options'
4
+
5
+ module Webcmd
6
+ class Server
7
+ def wrapped_app
8
+ @wrapped_app ||= begin
9
+ app = App.new(options[:app])
10
+
11
+ Rack::Builder.new do
12
+ use Rack::Logger
13
+ use Rack::CommonLogger
14
+ use Rack::Chunked
15
+ run app
16
+ end
17
+ end
18
+ end
19
+
20
+ def default_options
21
+ {
22
+ Host: '0.0.0.0',
23
+ Port: 9292,
24
+ environment: 'production',
25
+ server: :puma,
26
+ Threads: '0:1'
27
+ }
28
+ end
29
+
30
+ def options
31
+ @options ||= begin
32
+ options = Options.new(ARGV).parse!
33
+ default_options.merge(options)
34
+ end
35
+ end
36
+
37
+ def start
38
+ Rack::Server.start options.merge(app: wrapped_app)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Webcmd
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,53 @@
1
+ require 'minitest/autorun'
2
+ require 'rack/test'
3
+ require 'webcmd'
4
+
5
+ module Webcmd
6
+ class AppTest < MiniTest::Unit::TestCase
7
+ include Rack::Test::Methods
8
+
9
+ def options
10
+ @options ||= {}
11
+ end
12
+
13
+ def app
14
+ App.new(options)
15
+ end
16
+
17
+ def test_success_response
18
+ get '/'
19
+ assert_equal 200, last_response.status
20
+ end
21
+
22
+ def test_returns_text_content_type
23
+ get '/'
24
+ assert_equal 'text/plain', last_response.content_type
25
+ end
26
+
27
+ def test_returns_command_output
28
+ options[:command] = 'echo "foobar"'
29
+ get '/'
30
+ assert_equal 200, last_response.status
31
+ assert_equal 'foobar', last_response.body.strip
32
+ end
33
+
34
+ def test_executes_command_in_bundler_free_env
35
+ options[:command] = 'export | grep BUNDLE_ | grep -v grep'
36
+ get '/'
37
+ assert_empty last_response.body.strip
38
+ end
39
+
40
+ def test_returns_command_stderr
41
+ options[:command] = 'echo "foobar" >&2'
42
+ get '/'
43
+ assert_equal 200, last_response.status
44
+ assert_equal 'foobar', last_response.body.strip
45
+ end
46
+
47
+ def test_invalid_token
48
+ options[:token] = 'asdf'
49
+ get '/', token: '1234'
50
+ assert_equal 403, last_response.status
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,27 @@
1
+ require 'minitest/autorun'
2
+ require 'open-uri'
3
+
4
+ module Webcmd
5
+ class IntegrationTest < MiniTest::Unit::TestCase
6
+ def test_run_bin_file
7
+ bin = File.expand_path('../../bin/webcmd', __FILE__)
8
+ env = {'COMMAND' => 'echo "foobar"', 'TOKEN' => '1234'}
9
+ pipe = IO.popen([env, bin, err: [:child, :out]])
10
+
11
+ assert_equal 'foobar', response.strip
12
+ ensure
13
+ Process.kill('KILL', pipe.pid)
14
+ end
15
+
16
+ private
17
+
18
+ def response
19
+ retries ||= 3
20
+ sleep 0.5
21
+ response = open("http://127.0.0.1:9292/?token=1234").read
22
+ rescue Errno::ECONNREFUSED => e
23
+ retries -= 1
24
+ retries > 0 ? retry : raise(e)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ $: << File.expand_path('../../lib', __FILE__)
2
+ require 'webcmd'
3
+ require 'rack/test'
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'webcmd/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'webcmd'
8
+ gem.version = Webcmd::VERSION
9
+ gem.authors = ['Sergey Nartimov']
10
+ gem.email = ['just.lest@gmail.com']
11
+ gem.description = %q{Run shell command through HTTP}
12
+ gem.summary = %q{Run shell command through HTTP}
13
+ gem.homepage = 'https://github.com/brainspec/webcmd'
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_dependency('rack', '~> 1.4.1')
21
+ gem.add_dependency('puma', '~> 1.6.3')
22
+
23
+ gem.add_development_dependency('rake')
24
+ gem.add_development_dependency('rack-test')
25
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: webcmd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sergey Nartimov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ type: :runtime
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.4.1
22
+ prerelease: false
23
+ name: rack
24
+ requirement: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.4.1
30
+ - !ruby/object:Gem::Dependency
31
+ type: :runtime
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.6.3
38
+ prerelease: false
39
+ name: puma
40
+ requirement: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.6.3
46
+ - !ruby/object:Gem::Dependency
47
+ type: :development
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ prerelease: false
55
+ name: rake
56
+ requirement: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ type: :development
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ prerelease: false
71
+ name: rack-test
72
+ requirement: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Run shell command through HTTP
79
+ email:
80
+ - just.lest@gmail.com
81
+ executables:
82
+ - webcmd
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - Gemfile.lock
89
+ - MIT-LICENSE
90
+ - README.md
91
+ - Rakefile
92
+ - bin/webcmd
93
+ - lib/webcmd.rb
94
+ - lib/webcmd/app.rb
95
+ - lib/webcmd/command_runner.rb
96
+ - lib/webcmd/options.rb
97
+ - lib/webcmd/server.rb
98
+ - lib/webcmd/version.rb
99
+ - test/app_test.rb
100
+ - test/integration_test.rb
101
+ - test/test_helper.rb
102
+ - webcmd.gemspec
103
+ homepage: https://github.com/brainspec/webcmd
104
+ licenses: []
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ segments:
115
+ - 0
116
+ hash: 2381652511498473721
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ segments:
124
+ - 0
125
+ hash: 2381652511498473721
126
+ version: '0'
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 1.8.23
130
+ signing_key:
131
+ specification_version: 3
132
+ summary: Run shell command through HTTP
133
+ test_files:
134
+ - test/app_test.rb
135
+ - test/integration_test.rb
136
+ - test/test_helper.rb