debox 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +20 -0
- data/.rspec +1 -0
- data/Gemfile +15 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/debox +15 -0
- data/debox.gemspec +23 -0
- data/lib/debox.rb +9 -0
- data/lib/debox/api.rb +261 -0
- data/lib/debox/cli.rb +111 -0
- data/lib/debox/command.rb +48 -0
- data/lib/debox/command/apps.rb +14 -0
- data/lib/debox/command/auth.rb +29 -0
- data/lib/debox/command/base.rb +57 -0
- data/lib/debox/command/cap.rb +16 -0
- data/lib/debox/command/deploy.rb +18 -0
- data/lib/debox/command/key.rb +20 -0
- data/lib/debox/command/live.rb +14 -0
- data/lib/debox/command/log.rb +17 -0
- data/lib/debox/command/login.rb +18 -0
- data/lib/debox/command/logs.rb +27 -0
- data/lib/debox/command/recipes.rb +65 -0
- data/lib/debox/config.rb +59 -0
- data/lib/debox/utils.rb +77 -0
- data/lib/debox/version.rb +3 -0
- data/spec/integration/api/api_key_spec.rb +20 -0
- data/spec/integration/api/apps_spec.rb +9 -0
- data/spec/integration/api/cap/cap_spec.rb +58 -0
- data/spec/integration/api/live/log_spec.rb +12 -0
- data/spec/integration/api/logs/index_spec.rb +54 -0
- data/spec/integration/api/logs/show_spec.rb +27 -0
- data/spec/integration/api/public_key_spec.rb +15 -0
- data/spec/integration/api/recipes/create_spec.rb +10 -0
- data/spec/integration/api/recipes/destroy_spec.rb +14 -0
- data/spec/integration/api/recipes/index_spec.rb +17 -0
- data/spec/integration/api/recipes/new_spec.rb +10 -0
- data/spec/integration/api/recipes/show_spec.rb +10 -0
- data/spec/integration/api/recipes/update_spec.rb +11 -0
- data/spec/integration/api/users/create_spec.rb +8 -0
- data/spec/integration/api/users/delete_spec.rb +11 -0
- data/spec/integration/api/users/index_spec.rb +10 -0
- data/spec/integration/commands/apps_spec.rb +8 -0
- data/spec/integration/commands/cap_spec.rb +17 -0
- data/spec/integration/commands/key_spec.rb +24 -0
- data/spec/integration/commands/login_spec.rb +11 -0
- data/spec/integration/commands/logs/index_spec.rb +14 -0
- data/spec/integration/commands/logs/show_spec.rb +14 -0
- data/spec/integration/commands/recipes/delete_spec.rb +8 -0
- data/spec/integration/commands/recipes/edit_spec.rb +23 -0
- data/spec/integration/commands/recipes/index_spec.rb +8 -0
- data/spec/integration/commands/recipes/show_spec.rb +8 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/support/herlpers.rb +43 -0
- metadata +209 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
gem 'json'
|
3
|
+
gem 'netrc'
|
4
|
+
gem 'highline'
|
5
|
+
gem 'eventmachine'
|
6
|
+
gem 'em-eventsource'
|
7
|
+
|
8
|
+
# Specify your gem's dependencies in debox.gemspec
|
9
|
+
gemspec
|
10
|
+
|
11
|
+
group :test do
|
12
|
+
gem 'debox_server'
|
13
|
+
gem 'rspec'
|
14
|
+
gem 'thin'
|
15
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Eloy Gomez
|
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,29 @@
|
|
1
|
+
# Debox
|
2
|
+
|
3
|
+
Command line tool for Debox
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'debox'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install debox
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/debox
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
|
5
|
+
require "pathname"
|
6
|
+
bin_file = Pathname.new(__FILE__).realpath
|
7
|
+
|
8
|
+
# add self to libpath
|
9
|
+
$:.unshift File.expand_path("../../lib", bin_file)
|
10
|
+
|
11
|
+
# start up the CLI
|
12
|
+
require "debox"
|
13
|
+
require "debox/cli"
|
14
|
+
|
15
|
+
Debox::CLI.new(*ARGV).start
|
data/debox.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/debox/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Eloy Gomez"]
|
6
|
+
gem.email = ["eloy@indeos.es"]
|
7
|
+
gem.description = "CLI for debox"
|
8
|
+
gem.summary = "Debox is a deploy manager"
|
9
|
+
gem.homepage = "https://github.com/harlock/debox"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "debox"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Debox::VERSION
|
17
|
+
|
18
|
+
gem.add_runtime_dependency 'json'
|
19
|
+
gem.add_runtime_dependency 'netrc'
|
20
|
+
gem.add_runtime_dependency 'highline'
|
21
|
+
gem.add_runtime_dependency 'eventmachine'
|
22
|
+
gem.add_runtime_dependency 'em-eventsource'
|
23
|
+
end
|
data/lib/debox.rb
ADDED
data/lib/debox/api.rb
ADDED
@@ -0,0 +1,261 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require 'base64'
|
3
|
+
require 'eventmachine'
|
4
|
+
require 'em-eventsource'
|
5
|
+
|
6
|
+
module Debox
|
7
|
+
module API
|
8
|
+
|
9
|
+
# Return the api_key for the user
|
10
|
+
# @params:
|
11
|
+
# :host
|
12
|
+
# :user
|
13
|
+
# :password
|
14
|
+
def self.api_key(opt)
|
15
|
+
get_raw '/v1/api_key', opt, skip_basic_auth: true
|
16
|
+
end
|
17
|
+
|
18
|
+
# apps
|
19
|
+
#----------------------------------------------------------------------
|
20
|
+
def self.apps
|
21
|
+
get '/v1/apps'
|
22
|
+
end
|
23
|
+
|
24
|
+
# users
|
25
|
+
#----------------------------------------------------------------------
|
26
|
+
|
27
|
+
# Create a new user
|
28
|
+
# @params:
|
29
|
+
# :host
|
30
|
+
# :user
|
31
|
+
# :password
|
32
|
+
def self.users_create(opt)
|
33
|
+
post_raw '/v1/users/create', user: opt[:user], password: opt[:password]
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.users_delete(opt)
|
37
|
+
delete_raw '/v1/users/destroy', user: opt[:user]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return existing users
|
41
|
+
def self.users
|
42
|
+
get '/v1/users'
|
43
|
+
end
|
44
|
+
|
45
|
+
# recipes
|
46
|
+
#----------------------------------------------------------------------
|
47
|
+
|
48
|
+
def self.recipes(opt)
|
49
|
+
get("/v1/recipes/#{opt[:app]}")
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.recipes_new(opt)
|
53
|
+
get_raw("/v1/recipes/#{opt[:app]}/#{opt[:env]}/new").body
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.recipes_show(opt)
|
57
|
+
get_raw("/v1/recipes/#{opt[:app]}/#{opt[:env]}").body
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.recipes_create(opt)
|
61
|
+
post_raw "/v1/recipes/#{opt[:app]}/#{opt[:env]}", content: opt[:content]
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.recipes_update(opt)
|
65
|
+
put_raw "/v1/recipes/#{opt[:app]}/#{opt[:env]}", content: opt[:content]
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.recipes_destroy(opt)
|
69
|
+
delete_raw("/v1/recipes/#{opt[:app]}/#{opt[:env]}")
|
70
|
+
end
|
71
|
+
|
72
|
+
# Cap
|
73
|
+
#----------------------------------------------------------------------
|
74
|
+
|
75
|
+
def self.cap(opt, &block)
|
76
|
+
path = "/v1/cap/#{opt[:app]}"
|
77
|
+
path += "/#{opt[:env]}" if opt[:env]
|
78
|
+
path += "?task=#{opt[:task]}" if opt[:task]
|
79
|
+
get(path)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Public key
|
83
|
+
#----------------------------------------------------------------------
|
84
|
+
|
85
|
+
def self.public_key
|
86
|
+
get_raw('/v1/public_key').body
|
87
|
+
end
|
88
|
+
|
89
|
+
# logs
|
90
|
+
#----------------------------------------------------------------------
|
91
|
+
|
92
|
+
def self.live(opt, &block)
|
93
|
+
path = "/v1/live/log/#{opt[:app]}"
|
94
|
+
path += "/#{opt[:env]}" if opt[:env]
|
95
|
+
|
96
|
+
# stream path, nil, { }, block
|
97
|
+
eventSource path, { }, block
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.logs(opt)
|
101
|
+
path = "/v1/logs/#{opt[:app]}"
|
102
|
+
path += "/#{opt[:env]}" if opt[:env]
|
103
|
+
get path
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.log(opt)
|
107
|
+
path = "/v1/log/#{opt[:app]}"
|
108
|
+
path += "/#{opt[:env]}" if opt[:env]
|
109
|
+
path += "?index=#{opt[:index]}" if opt[:index]
|
110
|
+
get_raw(path).body
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
# HTTP helpers
|
116
|
+
#----------------------------------------------------------------------
|
117
|
+
|
118
|
+
def self.post_raw(path, request_params=nil, options={})
|
119
|
+
run_request :post, path, request_params, options
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.post(path, request_params=nil, options={})
|
123
|
+
JSON.parse post_raw(path, request_params, options).body, symbolize_names: true
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.get_raw(path, request_params=nil, options={})
|
127
|
+
run_request :get, path, request_params, options
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.get(path, request_params=nil, options={})
|
131
|
+
JSON.parse get_raw(path, request_params, options).body, symbolize_names: true
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.put_raw(path, request_params=nil, options={})
|
135
|
+
run_request :put, path, request_params, options
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.put(path, request_params=nil, options={})
|
139
|
+
JSON.parse put_raw(path, request_params, options).body, symbolize_names: true
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.delete_raw(path, request_params=nil, options={})
|
143
|
+
run_request :delete, path, request_params, options
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.delete(path, request_params=nil, options={})
|
147
|
+
JSON.parse delete_raw(path, request_params, options).body, symbolize_names: true
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
# Create a new Net::HTTP request
|
152
|
+
def self.new_request(type, path, request_params=nil, options={})
|
153
|
+
if type == :post
|
154
|
+
request = Net::HTTP::Post.new(path)
|
155
|
+
elsif type == :put
|
156
|
+
request = Net::HTTP::Put.new(path)
|
157
|
+
elsif type == :get
|
158
|
+
request = Net::HTTP::Get.new(path)
|
159
|
+
elsif type == :delete
|
160
|
+
request = Net::HTTP::Delete.new(path)
|
161
|
+
else
|
162
|
+
error_and_exit "Invalid request type: #{type}"
|
163
|
+
end
|
164
|
+
|
165
|
+
request.set_form_data(request_params) if request_params
|
166
|
+
|
167
|
+
if !options[:skip_basic_auth] && Debox::Config.logged_in?
|
168
|
+
request.basic_auth Debox.config[:user], Debox.config[:api_key]
|
169
|
+
end
|
170
|
+
return request
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.is_valid_response?(response)
|
174
|
+
!response.code.match(/^20/).nil?
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.check_errors(response)
|
178
|
+
unauthorized_error if response.code == "401"
|
179
|
+
server_error if response.code == "500"
|
180
|
+
not_found_error if response.code == "404"
|
181
|
+
error_and_exit("#{response.code}: #{response.body}") unless is_valid_response? response
|
182
|
+
end
|
183
|
+
|
184
|
+
# Run request
|
185
|
+
def self.run_request(type, path, request_params=nil, options={})
|
186
|
+
request = new_request type, path, request_params, options
|
187
|
+
http = Net::HTTP.new Debox.config[:host], Debox.config[:port]
|
188
|
+
response = http.request(request)
|
189
|
+
# Process errors
|
190
|
+
check_errors response
|
191
|
+
return response
|
192
|
+
end
|
193
|
+
|
194
|
+
# Run request and read run block with the chunk
|
195
|
+
def self.stream(path, request_params=nil, options={ }, block)
|
196
|
+
request = new_request :get, path, request_params, options
|
197
|
+
http = http_connection
|
198
|
+
http.request(request) do | response|
|
199
|
+
check_errors response
|
200
|
+
response.read_body do |chunk|
|
201
|
+
block.call chunk
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
def self.eventSource(path, query={ }, block)
|
208
|
+
host = "http://#{Debox.config[:host]}:#{Debox.config[:port]}"
|
209
|
+
auth = [Debox.config[:user], Debox.config[:api_key]].join(':')
|
210
|
+
headers = { 'Authorization' => "Basic #{Base64.strict_encode64 auth}" }
|
211
|
+
|
212
|
+
EM.run do
|
213
|
+
source = EventMachine::EventSource.new("#{host}#{path}", query, headers )
|
214
|
+
source.message do |m|
|
215
|
+
block.call m
|
216
|
+
end
|
217
|
+
|
218
|
+
source.error do |error|
|
219
|
+
puts "ERROR: #{error}"
|
220
|
+
EM.stop
|
221
|
+
end
|
222
|
+
|
223
|
+
source.on 'finish' do
|
224
|
+
puts "Job finished"
|
225
|
+
source.close
|
226
|
+
EM.stop
|
227
|
+
end
|
228
|
+
|
229
|
+
source.start
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
def self.http_connection
|
236
|
+
http = Net::HTTP.start(Debox.config[:host], Debox.config[:port])
|
237
|
+
# Set timeout to 30 min
|
238
|
+
http.read_timeout = 180000
|
239
|
+
return http
|
240
|
+
end
|
241
|
+
|
242
|
+
def self.unauthorized_error
|
243
|
+
error_and_exit "Access denied. Please login first."
|
244
|
+
end
|
245
|
+
|
246
|
+
def self.server_error
|
247
|
+
error_and_exit "Ups, something went wrong. Please, try later."
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.not_found_error
|
251
|
+
error_and_exit "Ups, something went wrong (not found). Please, try to update your client."
|
252
|
+
end
|
253
|
+
|
254
|
+
def self.error_and_exit(msg)
|
255
|
+
raise DeboxServerException.new msg
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
class DeboxServerException < Exception
|
260
|
+
end
|
261
|
+
end
|
data/lib/debox/cli.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
class Debox::CLI
|
2
|
+
include Debox::Utils
|
3
|
+
|
4
|
+
def initialize(*args)
|
5
|
+
@args = args
|
6
|
+
option_parser.parse! args
|
7
|
+
end
|
8
|
+
|
9
|
+
# Run the command requested in the command line
|
10
|
+
def run_command(args)
|
11
|
+
Debox::Command.load
|
12
|
+
given_command = args.shift.strip rescue "help"
|
13
|
+
if given_command == 'help' || options[:show_help]
|
14
|
+
help_and_exit
|
15
|
+
end
|
16
|
+
command = Debox::Command.get_command given_command
|
17
|
+
unless command
|
18
|
+
puts "Invalid command #{given_command}"
|
19
|
+
help_and_error
|
20
|
+
end
|
21
|
+
|
22
|
+
Debox::Config.merge_command_line_options! options
|
23
|
+
Debox::Command.run(command, options, args)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Prepare the console and run the command
|
27
|
+
def start
|
28
|
+
begin
|
29
|
+
$stdin.sync = true if $stdin.isatty
|
30
|
+
$stdout.sync = true if $stdout.isatty
|
31
|
+
|
32
|
+
# Run the commands
|
33
|
+
run_command @args
|
34
|
+
|
35
|
+
rescue Interrupt => error
|
36
|
+
puts 'stty icanon echo, Command cancelled.'
|
37
|
+
puts error.backtrace
|
38
|
+
rescue => error
|
39
|
+
puts error
|
40
|
+
puts "--------------------------------------------------------------------------------"
|
41
|
+
puts error.backtrace
|
42
|
+
exit(1)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def option_parser
|
47
|
+
@option_parser ||= new_option_parser
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def help_show
|
53
|
+
puts "Usage: debox command [options]"
|
54
|
+
puts "Commands:"
|
55
|
+
Debox::Command.commands.values.each do |cmd|
|
56
|
+
puts help_content cmd
|
57
|
+
end
|
58
|
+
puts option_parser.help
|
59
|
+
end
|
60
|
+
|
61
|
+
def help_content(cmd)
|
62
|
+
help = cmd[:help]
|
63
|
+
command = cmd[:command]
|
64
|
+
params_str = help[:params].join ' '
|
65
|
+
opt_params_str = help[:opt_params].map { |p| "[#{p}]"}.join ' '
|
66
|
+
help_text = help[:text]
|
67
|
+
command_str = " #{command} #{params_str} #{opt_params_str}".ljust(50)
|
68
|
+
"#{command_str} # #{help_text}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def help_and_exit
|
72
|
+
help_show
|
73
|
+
exit 0
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def help_and_error
|
78
|
+
help_show
|
79
|
+
exit 1
|
80
|
+
end
|
81
|
+
|
82
|
+
def options
|
83
|
+
@options ||= {}
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def new_option_parser
|
88
|
+
OptionParser.new do |opts|
|
89
|
+
opts.banner = "Options:"
|
90
|
+
opts.on("-h", "--host SERVER_HOST", "Debox server url") do |host|
|
91
|
+
options[:host] = host
|
92
|
+
# If host option, set default port unless defined
|
93
|
+
options[:port] = 80 unless options[:port]
|
94
|
+
end
|
95
|
+
|
96
|
+
opts.on("-p", "--port PORT", "Debox server port") do |port|
|
97
|
+
options[:port] = port
|
98
|
+
end
|
99
|
+
|
100
|
+
opts.on("-u", "--user EMAIL", "User name") do |u|
|
101
|
+
options[:user] = u
|
102
|
+
end
|
103
|
+
|
104
|
+
opts.on("-?", "--help", "Show this help") do |v|
|
105
|
+
options[:show_help] = true
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|