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,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