azuki 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 (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