debox 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.
- 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
|