webcmd 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.
@@ -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