azuki 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +71 -0
  3. data/bin/azuki +17 -0
  4. data/data/cacert.pem +3988 -0
  5. data/lib/azuki.rb +17 -0
  6. data/lib/azuki/auth.rb +339 -0
  7. data/lib/azuki/cli.rb +38 -0
  8. data/lib/azuki/client.rb +764 -0
  9. data/lib/azuki/client/azuki_postgresql.rb +141 -0
  10. data/lib/azuki/client/cisaurus.rb +26 -0
  11. data/lib/azuki/client/pgbackups.rb +113 -0
  12. data/lib/azuki/client/rendezvous.rb +108 -0
  13. data/lib/azuki/client/ssl_endpoint.rb +25 -0
  14. data/lib/azuki/command.rb +294 -0
  15. data/lib/azuki/command/account.rb +23 -0
  16. data/lib/azuki/command/accounts.rb +34 -0
  17. data/lib/azuki/command/addons.rb +305 -0
  18. data/lib/azuki/command/apps.rb +393 -0
  19. data/lib/azuki/command/auth.rb +86 -0
  20. data/lib/azuki/command/base.rb +230 -0
  21. data/lib/azuki/command/certs.rb +209 -0
  22. data/lib/azuki/command/config.rb +137 -0
  23. data/lib/azuki/command/db.rb +218 -0
  24. data/lib/azuki/command/domains.rb +85 -0
  25. data/lib/azuki/command/drains.rb +46 -0
  26. data/lib/azuki/command/fork.rb +164 -0
  27. data/lib/azuki/command/git.rb +64 -0
  28. data/lib/azuki/command/help.rb +179 -0
  29. data/lib/azuki/command/keys.rb +115 -0
  30. data/lib/azuki/command/labs.rb +147 -0
  31. data/lib/azuki/command/logs.rb +45 -0
  32. data/lib/azuki/command/maintenance.rb +61 -0
  33. data/lib/azuki/command/pg.rb +269 -0
  34. data/lib/azuki/command/pgbackups.rb +329 -0
  35. data/lib/azuki/command/plugins.rb +110 -0
  36. data/lib/azuki/command/ps.rb +232 -0
  37. data/lib/azuki/command/regions.rb +22 -0
  38. data/lib/azuki/command/releases.rb +124 -0
  39. data/lib/azuki/command/run.rb +180 -0
  40. data/lib/azuki/command/sharing.rb +89 -0
  41. data/lib/azuki/command/ssl.rb +43 -0
  42. data/lib/azuki/command/stack.rb +62 -0
  43. data/lib/azuki/command/status.rb +51 -0
  44. data/lib/azuki/command/update.rb +47 -0
  45. data/lib/azuki/command/version.rb +23 -0
  46. data/lib/azuki/deprecated.rb +5 -0
  47. data/lib/azuki/deprecated/help.rb +38 -0
  48. data/lib/azuki/distribution.rb +9 -0
  49. data/lib/azuki/excon.rb +9 -0
  50. data/lib/azuki/helpers.rb +517 -0
  51. data/lib/azuki/helpers/azuki_postgresql.rb +165 -0
  52. data/lib/azuki/helpers/log_displayer.rb +70 -0
  53. data/lib/azuki/plugin.rb +163 -0
  54. data/lib/azuki/updater.rb +171 -0
  55. data/lib/azuki/version.rb +3 -0
  56. data/lib/vendor/azuki/okjson.rb +598 -0
  57. data/spec/azuki/auth_spec.rb +256 -0
  58. data/spec/azuki/client/azuki_postgresql_spec.rb +71 -0
  59. data/spec/azuki/client/pgbackups_spec.rb +43 -0
  60. data/spec/azuki/client/rendezvous_spec.rb +62 -0
  61. data/spec/azuki/client/ssl_endpoint_spec.rb +48 -0
  62. data/spec/azuki/client_spec.rb +564 -0
  63. data/spec/azuki/command/addons_spec.rb +601 -0
  64. data/spec/azuki/command/apps_spec.rb +351 -0
  65. data/spec/azuki/command/auth_spec.rb +38 -0
  66. data/spec/azuki/command/base_spec.rb +109 -0
  67. data/spec/azuki/command/certs_spec.rb +178 -0
  68. data/spec/azuki/command/config_spec.rb +144 -0
  69. data/spec/azuki/command/db_spec.rb +110 -0
  70. data/spec/azuki/command/domains_spec.rb +87 -0
  71. data/spec/azuki/command/drains_spec.rb +34 -0
  72. data/spec/azuki/command/fork_spec.rb +56 -0
  73. data/spec/azuki/command/git_spec.rb +144 -0
  74. data/spec/azuki/command/help_spec.rb +93 -0
  75. data/spec/azuki/command/keys_spec.rb +120 -0
  76. data/spec/azuki/command/labs_spec.rb +100 -0
  77. data/spec/azuki/command/logs_spec.rb +60 -0
  78. data/spec/azuki/command/maintenance_spec.rb +51 -0
  79. data/spec/azuki/command/pg_spec.rb +236 -0
  80. data/spec/azuki/command/pgbackups_spec.rb +307 -0
  81. data/spec/azuki/command/plugins_spec.rb +104 -0
  82. data/spec/azuki/command/ps_spec.rb +195 -0
  83. data/spec/azuki/command/releases_spec.rb +130 -0
  84. data/spec/azuki/command/run_spec.rb +83 -0
  85. data/spec/azuki/command/sharing_spec.rb +59 -0
  86. data/spec/azuki/command/stack_spec.rb +46 -0
  87. data/spec/azuki/command/status_spec.rb +48 -0
  88. data/spec/azuki/command/version_spec.rb +16 -0
  89. data/spec/azuki/command_spec.rb +211 -0
  90. data/spec/azuki/helpers/azuki_postgresql_spec.rb +155 -0
  91. data/spec/azuki/helpers_spec.rb +48 -0
  92. data/spec/azuki/plugin_spec.rb +172 -0
  93. data/spec/azuki/updater_spec.rb +44 -0
  94. data/spec/helper/legacy_help.rb +16 -0
  95. data/spec/spec.opts +1 -0
  96. data/spec/spec_helper.rb +224 -0
  97. data/spec/support/display_message_matcher.rb +49 -0
  98. data/spec/support/openssl_mock_helper.rb +8 -0
  99. metadata +211 -0
@@ -0,0 +1,22 @@
1
+ require "azuki/command/base"
2
+
3
+ # HIDDEN: get info on available regions
4
+ #
5
+ class Azuki::Command::Regions < Azuki::Command::Base
6
+
7
+ # regions
8
+ #
9
+ # HIDDEN: List available regions for deployment
10
+ #
11
+ #Example:
12
+ #
13
+ # $ azuki regions
14
+ # === Regions
15
+ # us
16
+ def index
17
+ regions = json_decode(azuki.get("/regions"))
18
+ styled_header("Regions")
19
+ styled_array(regions.map { |region| [region["slug"], region["name"]] })
20
+ end
21
+ end
22
+
@@ -0,0 +1,124 @@
1
+ require "azuki/command/base"
2
+
3
+ # manage app releases
4
+ #
5
+ class Azuki::Command::Releases < Azuki::Command::Base
6
+
7
+ # releases
8
+ #
9
+ # list releases
10
+ #
11
+ #Example:
12
+ #
13
+ # $ azuki releases
14
+ # === example Releases
15
+ # v1 Config add FOO_BAR by email@example.com 0s ago
16
+ # v2 Config add BAR_BAZ by email@example.com 0s ago
17
+ # v3 Config add BAZ_QUX by email@example.com 0s ago
18
+ #
19
+ def index
20
+ validate_arguments!
21
+
22
+ releases_data = api.get_releases(app).body.sort_by do |release|
23
+ release["name"][1..-1].to_i
24
+ end.reverse.slice(0, 15)
25
+
26
+ unless releases_data.empty?
27
+ releases = releases_data.map do |release|
28
+ [
29
+ release["name"],
30
+ truncate(release["descr"], 40),
31
+ release["user"],
32
+ time_ago(release['created_at'])
33
+ ]
34
+ end
35
+
36
+ styled_header("#{app} Releases")
37
+ styled_array(releases, :sort => false)
38
+ else
39
+ display("#{app} has no releases.")
40
+ end
41
+ end
42
+
43
+ # releases:info RELEASE
44
+ #
45
+ # view detailed information for a release
46
+ # find latest release details by passing 'current' as the release
47
+ #
48
+ # -s, --shell # output config vars in shell format
49
+ #
50
+ #Example:
51
+ #
52
+ # $ azuki releases:info v10
53
+ # === Release v10
54
+ # Addons: deployhooks:http
55
+ # By: email@example.com
56
+ # Change: deploy ABCDEFG
57
+ # When: 2012-01-01 12:00:00
58
+ #
59
+ # === v10 Config Vars
60
+ # EXAMPLE: foo
61
+ #
62
+ def info
63
+ unless release = shift_argument
64
+ error("Usage: azuki releases:info RELEASE")
65
+ end
66
+ validate_arguments!
67
+
68
+ release_data = api.get_release(app, release).body
69
+
70
+ data = {
71
+ 'By' => release_data['user'],
72
+ 'Change' => release_data['descr'],
73
+ 'When' => time_ago(release_data["created_at"])
74
+ }
75
+
76
+ unless release_data['addons'].empty?
77
+ data['Addons'] = release_data['addons']
78
+ end
79
+
80
+ styled_header("Release #{release}")
81
+ styled_hash(data)
82
+
83
+ display
84
+
85
+ styled_header("#{release} Config Vars")
86
+ unless release_data['env'].empty?
87
+ if options[:shell]
88
+ release_data['env'].keys.sort.each do |key|
89
+ display("#{key}=#{release_data['env'][key]}")
90
+ end
91
+ else
92
+ styled_hash(release_data['env'])
93
+ end
94
+ else
95
+ display("#{release} has no config vars.")
96
+ end
97
+ end
98
+
99
+ # releases:rollback [RELEASE]
100
+ #
101
+ # roll back to an older release
102
+ #
103
+ # if RELEASE is not specified, will roll back one step
104
+ #
105
+ #Example:
106
+ #
107
+ # $ azuki releases:rollback
108
+ # Rolling back example... done, v122
109
+ #
110
+ # $ azuki releases:rollback v42
111
+ # Rolling back example to v42... done
112
+ #
113
+ def rollback
114
+ release = shift_argument
115
+ validate_arguments!
116
+
117
+ action("Rolling back #{app}") do
118
+ status(api.post_release(app, release).body)
119
+ end
120
+ end
121
+
122
+ alias_command "rollback", "releases:rollback"
123
+
124
+ end
@@ -0,0 +1,180 @@
1
+ require "readline"
2
+ require "azuki/command/base"
3
+ require "azuki/helpers/log_displayer"
4
+
5
+ # run one-off commands (console, rake)
6
+ #
7
+ class Azuki::Command::Run < Azuki::Command::Base
8
+
9
+ # run COMMAND
10
+ #
11
+ # run an attached process
12
+ #
13
+ #Example:
14
+ #
15
+ # $ azuki run bash
16
+ # Running `bash` attached to terminal... up, run.1
17
+ # ~ $
18
+ #
19
+ def index
20
+ command = args.join(" ")
21
+ error("Usage: azuki run COMMAND") if command.empty?
22
+ run_attached(command)
23
+ end
24
+
25
+ # run:detached COMMAND
26
+ #
27
+ # run a detached process, where output is sent to your logs
28
+ #
29
+ # -t, --tail # stream logs for the process
30
+ #
31
+ #Example:
32
+ #
33
+ # $ azuki run:detached ls
34
+ # Running `ls` detached... up, run.1
35
+ # Use `azuki logs -p run.1` to view the output.
36
+ #
37
+ def detached
38
+ command = args.join(" ")
39
+ error("Usage: azuki run COMMAND") if command.empty?
40
+ opts = { :attach => false, :command => command }
41
+ app_name = app
42
+ process_data = action("Running `#{command}` detached", :success => "up") do
43
+ process_data = api.post_ps(app_name, command, { :attach => false }).body
44
+ status(process_data['process'])
45
+ process_data
46
+ end
47
+ if options[:tail]
48
+ opts = []
49
+ opts << "tail=1"
50
+ opts << "ps=#{process_data['process']}"
51
+ log_displayer = ::Azuki::Helpers::LogDisplayer.new(azuki, app, opts)
52
+ log_displayer.display_logs
53
+ else
54
+ display("Use `azuki logs -p #{process_data['process']}` to view the output.")
55
+ end
56
+ end
57
+
58
+ # run:rake COMMAND
59
+ #
60
+ # WARNING: `azuki run:rake` has been deprecated. Please use `azuki run rake` instead."
61
+ #
62
+ # remotely execute a rake command
63
+ #
64
+ #Example:
65
+ #
66
+ # $ azuki run:rake -T
67
+ # Running `rake -T` attached to terminal... up, run.1
68
+ # (in /app)
69
+ # rake test # run tests
70
+ #
71
+ def rake
72
+ deprecate("`azuki #{current_command}` has been deprecated. Please use `azuki run rake` instead.")
73
+ command = "rake #{args.join(' ')}"
74
+ run_attached(command)
75
+ end
76
+
77
+ alias_command "rake", "run:rake"
78
+
79
+ # run:console [COMMAND]
80
+ #
81
+ # open a remote console session
82
+ #
83
+ # if COMMAND is specified, run the command and exit
84
+ #
85
+ # NOTE: For Cedar apps, use `azuki run console`
86
+ #
87
+ #Examples:
88
+ #
89
+ # $ azuki console
90
+ # Ruby console for example.azukiapp.com
91
+ # >>
92
+ #
93
+ def console
94
+ puts "`azuki #{current_command}` has been removed. Please use: `azuki run` instead."
95
+ puts "For more information, please see:"
96
+ puts " * https://devcenter.azukiapp.com/articles/one-off-dynos"
97
+ puts " * https://devcenter.azukiapp.com/articles/rails3#console"
98
+ puts " * https://devcenter.azukiapp.com/articles/console-bamboo"
99
+ end
100
+ alias_command "console", "run:console"
101
+
102
+ protected
103
+
104
+ def run_attached(command)
105
+ app_name = app
106
+ process_data = action("Running `#{command}` attached to terminal", :success => "up") do
107
+ process_data = api.post_ps(app_name, command, { :attach => true, :ps_env => get_terminal_environment }).body
108
+ status(process_data["process"])
109
+ process_data
110
+ end
111
+ rendezvous_session(process_data["rendezvous_url"])
112
+ end
113
+
114
+ def rendezvous_session(rendezvous_url, &on_connect)
115
+ begin
116
+ set_buffer(false)
117
+ rendezvous = Azuki::Client::Rendezvous.new(
118
+ :rendezvous_url => rendezvous_url,
119
+ :connect_timeout => (ENV["AZUKI_CONNECT_TIMEOUT"] || 120).to_i,
120
+ :activity_timeout => nil,
121
+ :input => $stdin,
122
+ :output => $stdout)
123
+ rendezvous.on_connect(&on_connect)
124
+ rendezvous.start
125
+ rescue Timeout::Error
126
+ error "\nTimeout awaiting process"
127
+ rescue OpenSSL::SSL::SSLError
128
+ error "Authentication error"
129
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET
130
+ error "\nError connecting to process"
131
+ rescue Interrupt
132
+ ensure
133
+ set_buffer(true)
134
+ end
135
+ end
136
+
137
+ def console_history_dir
138
+ FileUtils.mkdir_p(path = "#{home_directory}/.azuki/console_history")
139
+ path
140
+ end
141
+
142
+ def console_session(app)
143
+ azuki.console(app) do |console|
144
+ console_history_read(app)
145
+
146
+ display "Ruby console for #{app}.#{azuki.host}"
147
+ while cmd = Readline.readline('>> ')
148
+ unless cmd.nil? || cmd.strip.empty?
149
+ console_history_add(app, cmd)
150
+ break if cmd.downcase.strip == 'exit'
151
+ display console.run(cmd)
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ def console_history_file(app)
158
+ "#{console_history_dir}/#{app}"
159
+ end
160
+
161
+ def console_history_read(app)
162
+ history = File.read(console_history_file(app)).split("\n")
163
+ if history.size > 50
164
+ history = history[(history.size - 51),(history.size - 1)]
165
+ File.open(console_history_file(app), "w") { |f| f.puts history.join("\n") }
166
+ end
167
+ history.each { |cmd| Readline::HISTORY.push(cmd) }
168
+ rescue Errno::ENOENT
169
+ rescue Exception => ex
170
+ display "Error reading your console history: #{ex.message}"
171
+ if confirm("Would you like to clear it? (y/N):")
172
+ FileUtils.rm(console_history_file(app)) rescue nil
173
+ end
174
+ end
175
+
176
+ def console_history_add(app, cmd)
177
+ Readline::HISTORY.push(cmd)
178
+ File.open(console_history_file(app), "a") { |f| f.puts cmd + "\n" }
179
+ end
180
+ end
@@ -0,0 +1,89 @@
1
+ require "azuki/command/base"
2
+
3
+ module Azuki::Command
4
+
5
+ # manage collaborators on an app
6
+ #
7
+ class Sharing < Base
8
+
9
+ # sharing
10
+ #
11
+ # list collaborators on an app
12
+ #
13
+ #Example:
14
+ #
15
+ # $ azuki sharing
16
+ # === example Collaborators
17
+ # collaborator@example.com
18
+ # email@example.com
19
+ #
20
+ def index
21
+ validate_arguments!
22
+
23
+ # this is never empty, as it always includes the owner
24
+ collaborators = api.get_collaborators(app).body
25
+ styled_header("#{app} Collaborators")
26
+ styled_array(collaborators.map {|collaborator| collaborator["email"]})
27
+ end
28
+
29
+ # sharing:add EMAIL
30
+ #
31
+ # add a collaborator to an app
32
+ #
33
+ #Example:
34
+ #
35
+ # $ azuki sharing:add collaborator@example.com
36
+ # Adding collaborator@example.com to example collaborators... done
37
+ #
38
+ def add
39
+ unless email = shift_argument
40
+ error("Usage: azuki sharing:add EMAIL\nMust specify EMAIL to add sharing.")
41
+ end
42
+ validate_arguments!
43
+
44
+ action("Adding #{email} to #{app} collaborators") do
45
+ api.post_collaborator(app, email)
46
+ end
47
+ end
48
+
49
+ # sharing:remove EMAIL
50
+ #
51
+ # remove a collaborator from an app
52
+ #
53
+ #Example:
54
+ #
55
+ # $ azuki sharing:remove collaborator@example.com
56
+ # Removing collaborator@example.com to example collaborators... done
57
+ #
58
+ def remove
59
+ unless email = shift_argument
60
+ error("Usage: azuki sharing:remove EMAIL\nMust specify EMAIL to remove sharing.")
61
+ end
62
+ validate_arguments!
63
+
64
+ action("Removing #{email} from #{app} collaborators") do
65
+ api.delete_collaborator(app, email)
66
+ end
67
+ end
68
+
69
+ # sharing:transfer EMAIL
70
+ #
71
+ # transfer an app to a new owner
72
+ #
73
+ #Example:
74
+ #
75
+ # $ azuki sharing:transfer collaborator@example.com
76
+ # Transferring example to collaborator@example.com... done
77
+ #
78
+ def transfer
79
+ unless email = shift_argument
80
+ error("Usage: azuki sharing:transfer EMAIL\nMust specify EMAIL to transfer an app.")
81
+ end
82
+ validate_arguments!
83
+
84
+ action("Transferring #{app} to #{email}") do
85
+ api.put_app(app, "transfer_owner" => email)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,43 @@
1
+ require "azuki/command/base"
2
+
3
+ module Azuki::Command
4
+
5
+ # DEPRECATED: see `azuki certs` instead
6
+ #
7
+ # manage ssl certificates for an app
8
+ #
9
+ class Ssl < Base
10
+
11
+ # ssl
12
+ #
13
+ # list legacy certificates for an app
14
+ #
15
+ def index
16
+ api.get_domains(app).body.each do |domain|
17
+ if cert = domain['cert']
18
+ display "#{domain['domain']} has a SSL certificate registered to #{cert['subject']} which expires on #{format_date(cert['expires_at'])}"
19
+ else
20
+ display "#{domain['domain']} has no certificate"
21
+ end
22
+ end
23
+ end
24
+
25
+ # ssl:add PEM KEY
26
+ #
27
+ # DEPRECATED: see `azuki certs:add` instead
28
+ #
29
+ def add
30
+ $stderr.puts " ! `azuki ssl:add` has been deprecated. Please use the SSL Endpoint add-on and the `azuki certs` commands instead."
31
+ $stderr.puts " ! SSL Endpoint documentation is available at: https://devcenter.azukiapp.com/articles/ssl-endpoint"
32
+ end
33
+
34
+ # ssl:clear
35
+ #
36
+ # remove legacy ssl certificates from an app
37
+ #
38
+ def clear
39
+ azuki.clear_ssl(app)
40
+ display "Cleared certificates for #{app}"
41
+ end
42
+ end
43
+ end