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.
Files changed (55) hide show
  1. data/.gitignore +20 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +15 -0
  4. data/LICENSE +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +2 -0
  7. data/bin/debox +15 -0
  8. data/debox.gemspec +23 -0
  9. data/lib/debox.rb +9 -0
  10. data/lib/debox/api.rb +261 -0
  11. data/lib/debox/cli.rb +111 -0
  12. data/lib/debox/command.rb +48 -0
  13. data/lib/debox/command/apps.rb +14 -0
  14. data/lib/debox/command/auth.rb +29 -0
  15. data/lib/debox/command/base.rb +57 -0
  16. data/lib/debox/command/cap.rb +16 -0
  17. data/lib/debox/command/deploy.rb +18 -0
  18. data/lib/debox/command/key.rb +20 -0
  19. data/lib/debox/command/live.rb +14 -0
  20. data/lib/debox/command/log.rb +17 -0
  21. data/lib/debox/command/login.rb +18 -0
  22. data/lib/debox/command/logs.rb +27 -0
  23. data/lib/debox/command/recipes.rb +65 -0
  24. data/lib/debox/config.rb +59 -0
  25. data/lib/debox/utils.rb +77 -0
  26. data/lib/debox/version.rb +3 -0
  27. data/spec/integration/api/api_key_spec.rb +20 -0
  28. data/spec/integration/api/apps_spec.rb +9 -0
  29. data/spec/integration/api/cap/cap_spec.rb +58 -0
  30. data/spec/integration/api/live/log_spec.rb +12 -0
  31. data/spec/integration/api/logs/index_spec.rb +54 -0
  32. data/spec/integration/api/logs/show_spec.rb +27 -0
  33. data/spec/integration/api/public_key_spec.rb +15 -0
  34. data/spec/integration/api/recipes/create_spec.rb +10 -0
  35. data/spec/integration/api/recipes/destroy_spec.rb +14 -0
  36. data/spec/integration/api/recipes/index_spec.rb +17 -0
  37. data/spec/integration/api/recipes/new_spec.rb +10 -0
  38. data/spec/integration/api/recipes/show_spec.rb +10 -0
  39. data/spec/integration/api/recipes/update_spec.rb +11 -0
  40. data/spec/integration/api/users/create_spec.rb +8 -0
  41. data/spec/integration/api/users/delete_spec.rb +11 -0
  42. data/spec/integration/api/users/index_spec.rb +10 -0
  43. data/spec/integration/commands/apps_spec.rb +8 -0
  44. data/spec/integration/commands/cap_spec.rb +17 -0
  45. data/spec/integration/commands/key_spec.rb +24 -0
  46. data/spec/integration/commands/login_spec.rb +11 -0
  47. data/spec/integration/commands/logs/index_spec.rb +14 -0
  48. data/spec/integration/commands/logs/show_spec.rb +14 -0
  49. data/spec/integration/commands/recipes/delete_spec.rb +8 -0
  50. data/spec/integration/commands/recipes/edit_spec.rb +23 -0
  51. data/spec/integration/commands/recipes/index_spec.rb +8 -0
  52. data/spec/integration/commands/recipes/show_spec.rb +8 -0
  53. data/spec/spec_helper.rb +35 -0
  54. data/spec/support/herlpers.rb +43 -0
  55. 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
@@ -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
@@ -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