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,100 @@
1
+ require "spec_helper"
2
+
3
+ require "azuki/command/labs"
4
+
5
+ module Azuki::Command
6
+ describe Labs do
7
+
8
+ before(:each) do
9
+ stub_core
10
+ api.post_app("name" => "example", "stack" => "cedar")
11
+ end
12
+
13
+ after(:each) do
14
+ api.delete_app("example")
15
+ end
16
+
17
+ it "lists available features" do
18
+ stderr, stdout = execute("labs:list")
19
+ stderr.should == ""
20
+ stdout.should == <<-STDOUT
21
+ === User Features (email@example.com)
22
+ [ ] sumo-rankings Azuki Sumo ranks and visualizes the scale of your app, and suggests the optimum combination of dynos and add-ons to take it to the next level.
23
+
24
+ === App Features (example)
25
+ [+] sigterm-all When stopping a dyno, send SIGTERM to all processes rather than only to the root process.
26
+ [ ] user_env_compile Add user config vars to the environment during slug compilation
27
+ STDOUT
28
+ end
29
+
30
+ it "lists enabled features" do
31
+ stub_core.list_features("example").returns([])
32
+ stderr, stdout = execute("labs")
33
+ stderr.should == ""
34
+ stdout.should == <<-STDOUT
35
+ === User Features (email@example.com)
36
+ [ ] sumo-rankings Azuki Sumo ranks and visualizes the scale of your app, and suggests the optimum combination of dynos and add-ons to take it to the next level.
37
+
38
+ === App Features (example)
39
+ [+] sigterm-all When stopping a dyno, send SIGTERM to all processes rather than only to the root process.
40
+ [ ] user_env_compile Add user config vars to the environment during slug compilation
41
+ STDOUT
42
+ end
43
+
44
+ it "displays details of a feature" do
45
+ stderr, stdout = execute("labs:info user_env_compile")
46
+ stderr.should == ""
47
+ stdout.should == <<-STDOUT
48
+ === user_env_compile
49
+ Docs: http://devcenter.azukiapp.com/articles/labs-user-env-compile
50
+ Summary: Add user config vars to the environment during slug compilation
51
+ STDOUT
52
+ end
53
+
54
+ it "shows usage if no feature name is specified for info" do
55
+ stderr, stdout = execute("labs:info")
56
+ stderr.should == <<-STDERR
57
+ ! Usage: azuki labs:info FEATURE
58
+ ! Must specify FEATURE for info.
59
+ STDERR
60
+ stdout.should == ""
61
+ end
62
+
63
+ it "enables a feature" do
64
+ stderr, stdout = execute("labs:enable user_env_compile")
65
+ stderr.should == ""
66
+ stdout.should == <<-STDOUT
67
+ Enabling user_env_compile for example... done
68
+ WARNING: This feature is experimental and may change or be removed without notice.
69
+ For more information see: http://devcenter.azukiapp.com/articles/labs-user-env-compile
70
+ STDOUT
71
+ end
72
+
73
+ it "shows usage if no feature name is specified for enable" do
74
+ stderr, stdout = execute("labs:enable")
75
+ stderr.should == <<-STDERR
76
+ ! Usage: azuki labs:enable FEATURE
77
+ ! Must specify FEATURE to enable.
78
+ STDERR
79
+ stdout.should == ""
80
+ end
81
+
82
+ it "disables a feature" do
83
+ api.post_feature('user_env_compile', 'example')
84
+ stderr, stdout = execute("labs:disable user_env_compile")
85
+ stderr.should == ""
86
+ stdout.should == <<-STDOUT
87
+ Disabling user_env_compile for example... done
88
+ STDOUT
89
+ end
90
+
91
+ it "shows usage if no feature name is specified for disable" do
92
+ stderr, stdout = execute("labs:disable")
93
+ stderr.should == <<-STDERR
94
+ ! Usage: azuki labs:disable FEATURE
95
+ ! Must specify FEATURE to disable.
96
+ STDERR
97
+ stdout.should == ""
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,60 @@
1
+ require "spec_helper"
2
+ require "azuki/command/logs"
3
+
4
+ describe Azuki::Command::Logs do
5
+ describe "logs" do
6
+ it "runs with no options" do
7
+ stub_core.read_logs("example", [])
8
+ execute "logs"
9
+ end
10
+
11
+ it "runs with options" do
12
+ stub_core.read_logs("example", [
13
+ "tail=1",
14
+ "num=2",
15
+ "ps=ps.3",
16
+ "source=source.4"
17
+ ])
18
+ execute "logs --tail --num 2 --ps ps.3 --source source.4"
19
+ end
20
+
21
+ describe "with log output" do
22
+ before(:each) do
23
+ stub_core.read_logs("example", []).yields("2011-01-01T00:00:00+00:00 app[web.1]: test")
24
+ end
25
+
26
+ it "prettifies tty output" do
27
+ old_stdout_isatty = $stdout.isatty
28
+ stub($stdout).isatty.returns(true)
29
+ stderr, stdout = execute("logs")
30
+ stderr.should == ""
31
+ stdout.should == <<-STDOUT
32
+ \e[36m2011-01-01T00:00:00+00:00 app[web.1]:\e[0m test
33
+ STDOUT
34
+ stub($stdout).isatty.returns(old_stdout_isatty)
35
+ end
36
+
37
+ it "does not use ansi if stdout is not a tty" do
38
+ old_stdout_isatty = $stdout.isatty
39
+ stub($stdout).isatty.returns(false)
40
+ stderr, stdout = execute("logs")
41
+ stderr.should == ""
42
+ stdout.should == <<-STDOUT
43
+ 2011-01-01T00:00:00+00:00 app[web.1]: test
44
+ STDOUT
45
+ stub($stdout).isatty.returns(old_stdout_isatty)
46
+ end
47
+
48
+ it "does not use ansi if TERM is not set" do
49
+ term = ENV.delete("TERM")
50
+ stderr, stdout = execute("logs")
51
+ stderr.should == ""
52
+ stdout.should == <<-STDOUT
53
+ 2011-01-01T00:00:00+00:00 app[web.1]: test
54
+ STDOUT
55
+ ENV["TERM"] = term
56
+ end
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,51 @@
1
+ require "spec_helper"
2
+ require "azuki/command/maintenance"
3
+
4
+ module Azuki::Command
5
+ describe Maintenance do
6
+
7
+ before(:each) do
8
+ stub_core
9
+ api.post_app("name" => "example", "stack" => "cedar")
10
+ end
11
+
12
+ after(:each) do
13
+ api.delete_app("example")
14
+ end
15
+
16
+ it "displays off for maintenance mode of an app" do
17
+ stderr, stdout = execute("maintenance")
18
+ stderr.should == ""
19
+ stdout.should == <<-STDOUT
20
+ off
21
+ STDOUT
22
+ end
23
+
24
+ it "displays on for maintenance mode of an app" do
25
+ api.post_app_maintenance('example', '1')
26
+
27
+ stderr, stdout = execute("maintenance")
28
+ stderr.should == ""
29
+ stdout.should == <<-STDOUT
30
+ on
31
+ STDOUT
32
+ end
33
+
34
+ it "turns on maintenance mode for the app" do
35
+ stderr, stdout = execute("maintenance:on")
36
+ stderr.should == ""
37
+ stdout.should == <<-STDOUT
38
+ Enabling maintenance mode for example... done
39
+ STDOUT
40
+ end
41
+
42
+ it "turns off maintenance mode for the app" do
43
+ stderr, stdout = execute("maintenance:off")
44
+ stderr.should == ""
45
+ stdout.should == <<-STDOUT
46
+ Disabling maintenance mode for example... done
47
+ STDOUT
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,236 @@
1
+ require "spec_helper"
2
+ require "azuki/command/pg"
3
+
4
+ module Azuki::Command
5
+ describe Pg do
6
+ before do
7
+ stub_core
8
+
9
+ api.post_app "name" => "example"
10
+ api.put_config_vars "example", {
11
+ "DATABASE_URL" => "postgres://database_url",
12
+ "SHARED_DATABASE_URL" => "postgres://shared_database_url",
13
+ "AZUKI_POSTGRESQL_IVORY_URL" => "postgres://database_url",
14
+ "AZUKI_POSTGRESQL_RONIN_URL" => "postgres://ronin_database_url"
15
+ }
16
+
17
+ any_instance_of(Azuki::Command::Pg) do |pg|
18
+ stub(pg).app_attachments.returns([
19
+ Azuki::Helpers::AzukiPostgresql::Attachment.new({
20
+ 'config_var' => 'AZUKI_POSTGRESQL_IVORY_URL',
21
+ 'resource' => {'name' => 'loudly-yelling-1232',
22
+ 'value' => 'postgres://database_url',
23
+ 'type' => 'azuki-postgresql:ronin' }}),
24
+ Azuki::Helpers::AzukiPostgresql::Attachment.new({
25
+ 'config_var' => 'AZUKI_POSTGRESQL_RONIN_URL',
26
+ 'resource' => {'name' => 'softly-mocking-123',
27
+ 'value' => 'postgres://ronin_database_url',
28
+ 'type' => 'azuki-postgresql:ronin' }}),
29
+ Azuki::Helpers::AzukiPostgresql::Attachment.new({
30
+ 'config_var' => 'AZUKI_POSTGRESQL_FOLLOW_URL',
31
+ 'resource' => {'name' => 'whatever-somethign-2323',
32
+ 'value' => 'postgres://follow_database_url',
33
+ 'type' => 'azuki-postgresql:ronin' }})
34
+ ])
35
+ end
36
+ end
37
+
38
+ after do
39
+ api.delete_app "example"
40
+ end
41
+
42
+ it "resets the app's database if user confirms" do
43
+ stub_pg.reset
44
+
45
+ stderr, stdout = execute("pg:reset RONIN --confirm example")
46
+ stderr.should == ""
47
+ stdout.should == <<-STDOUT
48
+ Resetting AZUKI_POSTGRESQL_RONIN_URL... done
49
+ STDOUT
50
+ end
51
+
52
+ it "resets shared databases" do
53
+ Azuki::Client.any_instance.should_receive(:database_reset).with('example')
54
+
55
+ stderr, stdout = execute("pg:reset SHARED_DATABASE --confirm example")
56
+ stderr.should == ''
57
+ stdout.should == <<-STDOUT
58
+ Resetting SHARED_DATABASE... done
59
+ STDOUT
60
+ end
61
+
62
+ it "doesn't reset the app's database if the user doesn't confirm" do
63
+ stub_pg.reset
64
+
65
+ stderr, stdout = execute("pg:reset RONIN")
66
+ stderr.should == <<-STDERR
67
+ ! Confirmation did not match example. Aborted.
68
+ STDERR
69
+ stdout.should == "
70
+ ! WARNING: Destructive Action
71
+ ! This command will affect the app: example
72
+ ! To proceed, type \"example\" or re-run this command with --confirm example
73
+
74
+ > "
75
+ end
76
+
77
+ context "index" do
78
+ it "requests the info from the server" do
79
+ stub_pg.get_database.returns(:info => [
80
+ {"name"=>"Plan", "values"=>["Ronin"]},
81
+ {"name"=>"Status", "values"=>["available"]},
82
+ {"name"=>"Data Size", "values"=>["1 MB"]},
83
+ {"name"=>"Tables", "values"=>[1]},
84
+ {"name"=>"PG Version", "values"=>["9.1.4"]},
85
+ {"name"=>"Fork/Follow", "values"=>["Available"]},
86
+ {"name"=>"Created", "values"=>["2011-12-13 00:00 UTC"]},
87
+ {"name"=>"Maintenance", "values"=>["not required"]}
88
+ ])
89
+
90
+ stderr, stdout = execute("pg")
91
+ stderr.should == ""
92
+ stdout.should == <<-STDOUT
93
+ === AZUKI_POSTGRESQL_FOLLOW_URL
94
+ Plan: Ronin
95
+ Status: available
96
+ Data Size: 1 MB
97
+ Tables: 1
98
+ PG Version: 9.1.4
99
+ Fork/Follow: Available
100
+ Created: 2011-12-13 00:00 UTC
101
+ Maintenance: not required
102
+
103
+ === AZUKI_POSTGRESQL_IVORY_URL (DATABASE_URL)
104
+ Plan: Ronin
105
+ Status: available
106
+ Data Size: 1 MB
107
+ Tables: 1
108
+ PG Version: 9.1.4
109
+ Fork/Follow: Available
110
+ Created: 2011-12-13 00:00 UTC
111
+ Maintenance: not required
112
+
113
+ === AZUKI_POSTGRESQL_RONIN_URL
114
+ Plan: Ronin
115
+ Status: available
116
+ Data Size: 1 MB
117
+ Tables: 1
118
+ PG Version: 9.1.4
119
+ Fork/Follow: Available
120
+ Created: 2011-12-13 00:00 UTC
121
+ Maintenance: not required
122
+
123
+ === SHARED_DATABASE
124
+ Data Size: (empty)
125
+
126
+ STDOUT
127
+ end
128
+ end
129
+
130
+ context "info" do
131
+ it "requests the info from the server" do
132
+ stub_pg.get_database.returns(:info => [
133
+ {"name"=>"Plan", "values"=>["Ronin"]},
134
+ {"name"=>"Status", "values"=>["available"]},
135
+ {"name"=>"Data Size", "values"=>["1 MB"]},
136
+ {"name"=>"Tables", "values"=>[1]},
137
+ {"name"=>"PG Version", "values"=>["9.1.4"]},
138
+ {"name"=>"Fork/Follow", "values"=>["Available"]},
139
+ {"name"=>"Forked From", "values"=>["postgres://username:password@postgreshost.com:5432/database_name"], "resolve_db_name" => "true"},
140
+ {"name"=>"Created", "values"=>["2011-12-13 00:00 UTC"]},
141
+ {"name"=>"Maintenance", "values"=>["not required"]}
142
+ ])
143
+
144
+ stderr, stdout = execute("pg:info RONIN")
145
+ stderr.should == ""
146
+ stdout.should == <<-STDOUT
147
+ === AZUKI_POSTGRESQL_RONIN_URL
148
+ Plan: Ronin
149
+ Status: available
150
+ Data Size: 1 MB
151
+ Tables: 1
152
+ PG Version: 9.1.4
153
+ Fork/Follow: Available
154
+ Forked From: Database on postgreshost.com:5432/database_name
155
+ Created: 2011-12-13 00:00 UTC
156
+ Maintenance: not required
157
+
158
+ STDOUT
159
+ end
160
+ end
161
+
162
+ context "promotion" do
163
+ it "promotes the specified database" do
164
+ stderr, stdout = execute("pg:promote RONIN --confirm example")
165
+ stderr.should == ""
166
+ stdout.should == <<-STDOUT
167
+ Promoting AZUKI_POSTGRESQL_RONIN_URL to DATABASE_URL... done
168
+ STDOUT
169
+ api.get_config_vars("example").body["DATABASE_URL"].should == "postgres://ronin_database_url"
170
+ end
171
+
172
+ it "fails if no database is specified" do
173
+ stderr, stdout = execute("pg:promote")
174
+ stderr.should == <<-STDERR
175
+ ! Usage: azuki pg:promote DATABASE
176
+ ! Must specify DATABASE to promote.
177
+ STDERR
178
+ stdout.should == ""
179
+ end
180
+ end
181
+
182
+ context "credential resets" do
183
+ it "resets credentials and promotes to DATABASE_URL if it's the main DB" do
184
+ stub_pg.rotate_credentials
185
+ stderr, stdout = execute("pg:credentials iv --reset")
186
+ stderr.should == ''
187
+ stdout.should == <<-STDOUT
188
+ Resetting credentials for AZUKI_POSTGRESQL_IVORY_URL (DATABASE_URL)... done
189
+ Promoting AZUKI_POSTGRESQL_IVORY_URL (DATABASE_URL)... done
190
+ STDOUT
191
+ end
192
+
193
+ it "does not update DATABASE_URL if it's not the main db" do
194
+ stub_pg.rotate_credentials
195
+ api.put_config_vars "example", {
196
+ "DATABASE_URL" => "postgres://to_reset_credentials",
197
+ "AZUKI_POSTGRESQL_RESETME_URL" => "postgres://something_else"
198
+ }
199
+ stderr, stdout = execute("pg:credentials follo --reset")
200
+ stderr.should == ''
201
+ stdout.should_not include("Promoting")
202
+ end
203
+
204
+ end
205
+
206
+ context "unfollow" do
207
+ it "sends request to unfollow" do
208
+ hpg_client = double('Azuki::Client::AzukiPostgresql')
209
+ Azuki::Client::AzukiPostgresql.should_receive(:new).twice.and_return(hpg_client)
210
+ hpg_client.should_receive(:unfollow)
211
+ hpg_client.should_receive(:get_database).and_return(
212
+ :following => 'postgresql://user:pass@roninhost/database',
213
+ :info => [
214
+ {"name"=>"Plan", "values"=>["Ronin"]},
215
+ {"name"=>"Status", "values"=>["available"]},
216
+ {"name"=>"Data Size", "values"=>["1 MB"]},
217
+ {"name"=>"Tables", "values"=>[1]},
218
+ {"name"=>"PG Version", "values"=>["9.1.4"]},
219
+ {"name"=>"Fork/Follow", "values"=>["Available"]},
220
+ {"name"=>"Created", "values"=>["2011-12-13 00:00 UTC"]},
221
+ {"name"=>"Conn Info", "values"=>["[Deprecated] Please use `azuki pg:credentials AZUKI_POSTGRESQL_RONIN` to view connection info"]},
222
+ {"name"=>"Maintenance", "values"=>["not required"]}
223
+ ]
224
+ )
225
+ stderr, stdout = execute("pg:unfollow AZUKI_POSTGRESQL_FOLLOW_URL --confirm example")
226
+ stderr.should == ""
227
+ stdout.should == <<-STDOUT
228
+ ! AZUKI_POSTGRESQL_FOLLOW_URL will become writable and no longer
229
+ ! follow Database on roninhost:5432/database. This cannot be undone.
230
+ Unfollowing AZUKI_POSTGRESQL_FOLLOW_URL... done
231
+ STDOUT
232
+ end
233
+ end
234
+
235
+ end
236
+ end