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
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'debox/utils'
|
3
|
+
require 'debox/api'
|
4
|
+
require "highline/import"
|
5
|
+
|
6
|
+
module Debox
|
7
|
+
|
8
|
+
module Command
|
9
|
+
include Debox::Utils
|
10
|
+
|
11
|
+
attr_reader :options
|
12
|
+
|
13
|
+
def self.run(command, options={}, args=[])
|
14
|
+
begin
|
15
|
+
command_instance = command[:klass].new(args, options)
|
16
|
+
method = command[:method]
|
17
|
+
command_instance.send(method)
|
18
|
+
rescue Debox::DeboxServerException => error
|
19
|
+
puts error
|
20
|
+
rescue Interrupt, StandardError=> error
|
21
|
+
puts error
|
22
|
+
puts error.backtrace
|
23
|
+
rescue SystemExit => error
|
24
|
+
puts "Bye bye"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.commands
|
29
|
+
@@commands ||= {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.register_command(command)
|
33
|
+
commands[command[:command]] = command
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.get_command(cmd)
|
37
|
+
commands[cmd]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Require all commands
|
41
|
+
def self.load
|
42
|
+
Dir[File.join(File.dirname(__FILE__), "command", "*.rb")].each do |file|
|
43
|
+
require file
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'debox/command/base'
|
2
|
+
|
3
|
+
class Debox::Command::Apps < Debox::Command::Base
|
4
|
+
include Debox::Utils
|
5
|
+
|
6
|
+
help :index, "List apps and envs"
|
7
|
+
def index
|
8
|
+
Debox::API.apps.each do |app|
|
9
|
+
name = app[:app]
|
10
|
+
envs = app[:envs].join ', '
|
11
|
+
notice "* #{name} [#{envs}]"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'debox/command/base'
|
2
|
+
|
3
|
+
class Debox::Command::Users < Debox::Command::Base
|
4
|
+
include Debox::Utils
|
5
|
+
|
6
|
+
help :index, "List all users in the debox server"
|
7
|
+
def index
|
8
|
+
users = Debox::API.users
|
9
|
+
users.each do |user|
|
10
|
+
notice user
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
help :new, 'Create user'
|
15
|
+
def new
|
16
|
+
email = args.first unless args.empty?
|
17
|
+
email = ask_email unless email
|
18
|
+
password = ask_password_with_confirmation
|
19
|
+
respose = Debox::API.users_create user: email, password: password
|
20
|
+
end
|
21
|
+
|
22
|
+
help :delete, params: ['email'], text: 'Delete user with a given email'
|
23
|
+
def delete
|
24
|
+
email = args.first
|
25
|
+
Debox::API.users_delete user: email
|
26
|
+
notice 'User deleted'
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class Debox::Command::Base
|
2
|
+
include Debox::Utils
|
3
|
+
|
4
|
+
attr_reader :args
|
5
|
+
attr_reader :options
|
6
|
+
|
7
|
+
def initialize(args=[], options={})
|
8
|
+
@args = args
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
# Register help for given namespace
|
13
|
+
def self.namespace_help(help_txt)
|
14
|
+
@@namespace_help = help_txt
|
15
|
+
end
|
16
|
+
|
17
|
+
# Register help for given method
|
18
|
+
def self.help(method, options)
|
19
|
+
if options.is_a? Hash
|
20
|
+
help_text = options[:text]
|
21
|
+
params = options[:params] || []
|
22
|
+
opt_params = options[:opt_params] || []
|
23
|
+
else
|
24
|
+
help_text = options
|
25
|
+
params = []
|
26
|
+
opt_params = []
|
27
|
+
end
|
28
|
+
help_methods[method] = { text: help_text, params: params, opt_params: opt_params }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Help for methods
|
32
|
+
def self.help_methods
|
33
|
+
@@help_methods ||= { }
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def self.namespace
|
39
|
+
self.to_s.split("::").last.downcase
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.method_added(method)
|
43
|
+
return if self == Debox::Command::Base
|
44
|
+
return if private_method_defined?(method)
|
45
|
+
return if protected_method_defined?(method)
|
46
|
+
resolved_method = (method.to_s == "index") ? nil : method.to_s
|
47
|
+
command = [ self.namespace, resolved_method ].compact.join(":")
|
48
|
+
cmd = {
|
49
|
+
klass: self,
|
50
|
+
method: method,
|
51
|
+
command: command,
|
52
|
+
help: help_methods[method]
|
53
|
+
}
|
54
|
+
Debox::Command.register_command cmd
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'debox/command/base'
|
2
|
+
|
3
|
+
class Debox::Command::Cap < Debox::Command::Base
|
4
|
+
|
5
|
+
help :index, params: ['task', 'application'], opt_params: ['environment'], text: 'Deploy application'
|
6
|
+
def index
|
7
|
+
deploy_params = { task: args[0], app: args[1] }
|
8
|
+
deploy_params[:env] = args[2] if args.count == 3
|
9
|
+
cap_request = Debox::API.cap(deploy_params)
|
10
|
+
sleep 1
|
11
|
+
Debox::API.live(deploy_params) do |chunk|
|
12
|
+
puts chunk
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'debox/command/base'
|
2
|
+
|
3
|
+
class Debox::Command::Deploy < Debox::Command::Base
|
4
|
+
|
5
|
+
help :index, params: ['application', 'enviroment'], text: 'Deploy application'
|
6
|
+
def index
|
7
|
+
deploy_params = { app: args[0], env: args[1] }
|
8
|
+
if args.length > 2
|
9
|
+
deploy_params[:task] = args[2]
|
10
|
+
end
|
11
|
+
Debox::API.deploy(deploy_params)
|
12
|
+
sleep 1
|
13
|
+
Debox::API.live_log(deploy_params) do |chunk|
|
14
|
+
puts chunk
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'debox/command/base'
|
2
|
+
|
3
|
+
class Debox::Command::Key < Debox::Command::Base
|
4
|
+
include Debox::Utils
|
5
|
+
|
6
|
+
help :show, 'Show the server ssh public key'
|
7
|
+
def show
|
8
|
+
notice Debox::API.public_key
|
9
|
+
end
|
10
|
+
|
11
|
+
help :copy, params: ['target_host'], text: 'Copy the server ssh public key to the target host'
|
12
|
+
def copy
|
13
|
+
host = args.first
|
14
|
+
error_and_exit 'No target host' unless host
|
15
|
+
key = Debox::API.public_key
|
16
|
+
str = %{echo "#{key}" | ssh #{host} "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys" || exit 1 }
|
17
|
+
system str
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'debox/command/base'
|
2
|
+
|
3
|
+
class Debox::Command::Live < Debox::Command::Base
|
4
|
+
|
5
|
+
help :index, params: ['application', 'enviroment'], text: 'Live log for application'
|
6
|
+
def index
|
7
|
+
opt = { app: args[0] }
|
8
|
+
opt[:env] = args[1] if args[1]
|
9
|
+
Debox::API.live(opt) do |chunk|
|
10
|
+
puts chunk
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'debox/command/base'
|
2
|
+
|
3
|
+
class Debox::Command::Log < Debox::Command::Base
|
4
|
+
include Debox::Utils
|
5
|
+
|
6
|
+
help :index, params: ['application'],opt_params: ['environment', 'index'],
|
7
|
+
text: "Show log. Last by default"
|
8
|
+
|
9
|
+
def index
|
10
|
+
opt = { app: args[0] }
|
11
|
+
opt[:env] = args[1] if args.length > 1
|
12
|
+
opt[:index] = args[2] if args.length > 2
|
13
|
+
|
14
|
+
puts Debox::API.log opt
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'debox/command/base'
|
2
|
+
|
3
|
+
class Debox::Command::Login < Debox::Command::Base
|
4
|
+
include Debox::Utils
|
5
|
+
|
6
|
+
namespace_help 'Login.'
|
7
|
+
|
8
|
+
help :index, 'Login in the debox server. Require -h param.'
|
9
|
+
def index
|
10
|
+
email = options[:user] || ask_email
|
11
|
+
password = ask_password
|
12
|
+
response = Debox::API.api_key user: email, password: password
|
13
|
+
error_and_exit "Invalid login." unless response.code == "200"
|
14
|
+
Debox.config[:api_key] = response.body
|
15
|
+
Debox::Config.update_login_config
|
16
|
+
notice 'Login successful'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'debox/command/base'
|
2
|
+
|
3
|
+
class Debox::Command::Logs < Debox::Command::Base
|
4
|
+
include Debox::Utils
|
5
|
+
|
6
|
+
help :index, params: ['application'], opt_params: ['environment'],text: "List logs for application and env"
|
7
|
+
def index
|
8
|
+
opt = { app: args[0] }
|
9
|
+
opt[:env] = args[1] if args[1]
|
10
|
+
|
11
|
+
Debox::API.logs(opt).each do |log|
|
12
|
+
puts format_log_info(log)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def format_log_info(log)
|
19
|
+
l = []
|
20
|
+
l << log[:job_id]
|
21
|
+
l << log[:status]
|
22
|
+
l << log[:task]
|
23
|
+
l << DateTime.parse(log[:time]).to_s
|
24
|
+
l << log[:error]
|
25
|
+
l.join "\t"
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'debox/command/base'
|
2
|
+
|
3
|
+
class Debox::Command::Recipes < Debox::Command::Base
|
4
|
+
include Debox::Utils
|
5
|
+
|
6
|
+
help :index, params: ['application'], text: 'List recipes for the application'
|
7
|
+
def index
|
8
|
+
app = args.first
|
9
|
+
Debox::API.recipes(app: app).each do |recipe|
|
10
|
+
puts recipe
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
help :show, params: ['application', 'environment'], text: 'Show a new capistrano recipe'
|
15
|
+
def show
|
16
|
+
app = args.first
|
17
|
+
env = args.last
|
18
|
+
puts Debox::API.recipes_show app: app, env: env
|
19
|
+
end
|
20
|
+
|
21
|
+
help :new, params: ['application', 'environment'], text: 'Create a new capistrano recipe'
|
22
|
+
def new
|
23
|
+
app = args.first
|
24
|
+
env = args.last
|
25
|
+
new_recipe = Debox::API.recipes_new app: app, env: env
|
26
|
+
md5 = md5_str new_recipe
|
27
|
+
edited_recipe = edit_file new_recipe
|
28
|
+
edited_md5 = md5_str edited_recipe
|
29
|
+
if md5 == edited_md5
|
30
|
+
exit_ok "No changes detected, nothing to update."
|
31
|
+
end
|
32
|
+
|
33
|
+
# Send changes back to the server
|
34
|
+
notice "Updating changes on the server"
|
35
|
+
Debox::API.recipes_create app: app, env: env, content: edited_recipe
|
36
|
+
exit_ok "Recipe created"
|
37
|
+
end
|
38
|
+
|
39
|
+
help :edit, params: ['application', 'environment'], text: 'Edit a capistrano recipe'
|
40
|
+
def edit
|
41
|
+
app = args.first
|
42
|
+
env = args.last
|
43
|
+
recipe = Debox::API.recipes_show app: app, env: env
|
44
|
+
md5 = md5_str recipe
|
45
|
+
edited_recipe = edit_file recipe
|
46
|
+
edited_md5 = md5_str edited_recipe
|
47
|
+
if md5 == edited_md5
|
48
|
+
exit_ok "No changes detected, nothing to update."
|
49
|
+
end
|
50
|
+
|
51
|
+
# Send changes back to the server
|
52
|
+
notice "Updating changes on the server"
|
53
|
+
Debox::API.recipes_update app: app, env: env, content: edited_recipe
|
54
|
+
exit_ok "Recipe updated"
|
55
|
+
end
|
56
|
+
|
57
|
+
help :delete, params: ['application', 'environment'], text: 'Delete a capistrano recipe'
|
58
|
+
def delete
|
59
|
+
app = args.first
|
60
|
+
env = args.last
|
61
|
+
Debox::API.recipes_destroy app: app, env: env
|
62
|
+
exit_ok "Recipe deleted"
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/lib/debox/config.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Debox
|
4
|
+
|
5
|
+
# Convenient shortcut to Debox::Config.config
|
6
|
+
def self.config
|
7
|
+
Debox::Config.config
|
8
|
+
end
|
9
|
+
|
10
|
+
module Config
|
11
|
+
|
12
|
+
CONFIG_FILE_NAME=".deboxrc"
|
13
|
+
|
14
|
+
def self.config
|
15
|
+
@@config ||= read_config
|
16
|
+
end
|
17
|
+
|
18
|
+
# Merge given options with current config
|
19
|
+
def self.merge_command_line_options!(options)
|
20
|
+
config.merge! options
|
21
|
+
end
|
22
|
+
|
23
|
+
# Update login values in the config
|
24
|
+
def self.update_login_config
|
25
|
+
current = read_config
|
26
|
+
current.merge! host: config[:host], port: config[:port], user: config[:user], api_key: config[:api_key]
|
27
|
+
save_config current
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return true if the user has login information
|
31
|
+
def self.logged_in?
|
32
|
+
config[:api_key] && config[:api_key].length > 0
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Read the config from the filesystem
|
38
|
+
def self.read_config
|
39
|
+
return defaults unless File.exists? user_config_file
|
40
|
+
YAML.load_file user_config_file
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.save_config(current=config)
|
44
|
+
f = File.open(user_config_file, 'w')
|
45
|
+
f.write(current.to_yaml)
|
46
|
+
f.close
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return defaults values
|
50
|
+
def self.defaults
|
51
|
+
{ host: 'localhost', port: 80 }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Return config file full path
|
55
|
+
def self.user_config_file
|
56
|
+
File.join ENV['HOME'], CONFIG_FILE_NAME
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/debox/utils.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
module Debox
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
def email_regex
|
5
|
+
/^([^@\s]+)@((?:[-a-z0-9_]+\.)+[a-z]{2,})$/i
|
6
|
+
end
|
7
|
+
|
8
|
+
# md5 and hash
|
9
|
+
#----------------------------------------------------------------------
|
10
|
+
|
11
|
+
def md5_str(str)
|
12
|
+
Digest::MD5.hexdigest str
|
13
|
+
end
|
14
|
+
|
15
|
+
def md5_file(filename)
|
16
|
+
Digest::MD5.file(filename)
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# file edit
|
21
|
+
#----------------------------------------------------------------------
|
22
|
+
def edit_file(content)
|
23
|
+
tmp_file = Tempfile.new "debox"
|
24
|
+
tmp_file.write content
|
25
|
+
tmp_file.close
|
26
|
+
editor = ENV['EDITOR'] || 'nano'
|
27
|
+
system "#{editor} #{tmp_file.path}"
|
28
|
+
edited_file = File.open tmp_file.path
|
29
|
+
edited_file.read
|
30
|
+
end
|
31
|
+
|
32
|
+
# Ask for console input
|
33
|
+
#----------------------------------------------------------------------
|
34
|
+
|
35
|
+
def ask_email
|
36
|
+
ask("email: ") do |q|
|
37
|
+
q.validate = email_regex
|
38
|
+
q.responses[:not_valid] = "<%= color('Email is not well formed', :red) %>"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def ask_password
|
43
|
+
ask("password: "){ |q| q.echo = '*' }
|
44
|
+
end
|
45
|
+
|
46
|
+
def ask_password_with_confirmation
|
47
|
+
ask("password: ") do |q|
|
48
|
+
q.echo = '*'
|
49
|
+
q.verify_match = true
|
50
|
+
q.gather = {"password: " => '',
|
51
|
+
"password confirmation: " => ''}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# Errors and warnings
|
57
|
+
#----------------------------------------------------------------------
|
58
|
+
|
59
|
+
def notice(msg)
|
60
|
+
puts msg
|
61
|
+
end
|
62
|
+
|
63
|
+
def alert(msg)
|
64
|
+
end
|
65
|
+
|
66
|
+
def error_and_exit(msg)
|
67
|
+
puts msg
|
68
|
+
exit 1
|
69
|
+
end
|
70
|
+
|
71
|
+
def exit_ok(msg=nil)
|
72
|
+
puts msg if msg
|
73
|
+
exit 0
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|