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,351 @@
1
+ require "spec_helper"
2
+ require "azuki/command/apps"
3
+
4
+ module Azuki::Command
5
+ describe Apps do
6
+
7
+ before(:each) do
8
+ stub_core
9
+ end
10
+
11
+ context("info") do
12
+
13
+ before(:each) do
14
+ api.post_app("name" => "example", "stack" => "cedar")
15
+ end
16
+
17
+ after(:each) do
18
+ api.delete_app("example")
19
+ end
20
+
21
+ it "displays impicit app info" do
22
+ stderr, stdout = execute("apps:info")
23
+ stderr.should == ""
24
+ stdout.should == <<-STDOUT
25
+ === example
26
+ Git URL: git@azukiapp.com:example.git
27
+ Owner Email: email@example.com
28
+ Stack: cedar
29
+ Web URL: http://example.azukiapp.com/
30
+ STDOUT
31
+ end
32
+
33
+ it "gets explicit app from --app" do
34
+ stderr, stdout = execute("apps:info --app example")
35
+ stderr.should == ""
36
+ stdout.should == <<-STDOUT
37
+ === example
38
+ Git URL: git@azukiapp.com:example.git
39
+ Owner Email: email@example.com
40
+ Stack: cedar
41
+ Web URL: http://example.azukiapp.com/
42
+ STDOUT
43
+ end
44
+
45
+ it "shows shell app info when --shell option is used" do
46
+ stderr, stdout = execute("apps:info --shell")
47
+ stderr.should == ""
48
+ stdout.should match Regexp.new(<<-STDOUT)
49
+ create_status=complete
50
+ created_at=\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2} [+-]\\d{4}
51
+ dynos=0
52
+ git_url=git@azukiapp.com:example.git
53
+ id=\\d{1,5}
54
+ name=example
55
+ owner_email=email@example.com
56
+ repo_migrate_status=complete
57
+ repo_size=
58
+ requested_stack=
59
+ slug_size=
60
+ stack=cedar
61
+ web_url=http://example.azukiapp.com/
62
+ workers=0
63
+ STDOUT
64
+ end
65
+
66
+ end
67
+
68
+ context("create") do
69
+
70
+ it "without a name" do
71
+ name = nil
72
+ with_blank_git_repository do
73
+ stderr, stdout = execute("apps:create")
74
+ name = api.get_apps.body.first["name"]
75
+ stderr.should == ""
76
+ stdout.should == <<-STDOUT
77
+ Creating #{name}... done, stack is bamboo-mri-1.9.2
78
+ http://#{name}.azukiapp.com/ | git@azukiapp.com:#{name}.git
79
+ Git remote azuki added
80
+ STDOUT
81
+ end
82
+ api.delete_app(name)
83
+ end
84
+
85
+ it "with a name" do
86
+ with_blank_git_repository do
87
+ stderr, stdout = execute("apps:create example")
88
+ stderr.should == ""
89
+ stdout.should == <<-STDOUT
90
+ Creating example... done, stack is bamboo-mri-1.9.2
91
+ http://example.azukiapp.com/ | git@azukiapp.com:example.git
92
+ Git remote azuki added
93
+ STDOUT
94
+ end
95
+ api.delete_app("example")
96
+ end
97
+
98
+ it "with -a name" do
99
+ with_blank_git_repository do
100
+ stderr, stdout = execute("apps:create -a example")
101
+ stderr.should == ""
102
+ stdout.should == <<-STDOUT
103
+ Creating example... done, stack is bamboo-mri-1.9.2
104
+ http://example.azukiapp.com/ | git@azukiapp.com:example.git
105
+ Git remote azuki added
106
+ STDOUT
107
+ end
108
+ api.delete_app("example")
109
+ end
110
+
111
+ it "with --no-remote" do
112
+ with_blank_git_repository do
113
+ stderr, stdout = execute("apps:create example --no-remote")
114
+ stderr.should == ""
115
+ stdout.should == <<-STDOUT
116
+ Creating example... done, stack is bamboo-mri-1.9.2
117
+ http://example.azukiapp.com/ | git@azukiapp.com:example.git
118
+ STDOUT
119
+ end
120
+ api.delete_app("example")
121
+ end
122
+
123
+ it "with addons" do
124
+ with_blank_git_repository do
125
+ stderr, stdout = execute("apps:create addonapp --addon custom_domains:basic,releases:basic")
126
+ stderr.should == ""
127
+ stdout.should == <<-STDOUT
128
+ Creating addonapp... done, stack is bamboo-mri-1.9.2
129
+ Adding custom_domains:basic to addonapp... done
130
+ Adding releases:basic to addonapp... done
131
+ http://addonapp.azukiapp.com/ | git@azukiapp.com:addonapp.git
132
+ Git remote azuki added
133
+ STDOUT
134
+ end
135
+ api.delete_app("addonapp")
136
+ end
137
+
138
+ it "with a buildpack" do
139
+ with_blank_git_repository do
140
+ stderr, stdout = execute("apps:create buildpackapp --buildpack http://example.org/buildpack.git")
141
+ stderr.should == ""
142
+ stdout.should == <<-STDOUT
143
+ Creating buildpackapp... done, stack is bamboo-mri-1.9.2
144
+ BUILDPACK_URL=http://example.org/buildpack.git
145
+ http://buildpackapp.azukiapp.com/ | git@azukiapp.com:buildpackapp.git
146
+ Git remote azuki added
147
+ STDOUT
148
+ end
149
+ api.delete_app("buildpackapp")
150
+ end
151
+
152
+ it "with an alternate remote name" do
153
+ with_blank_git_repository do
154
+ stderr, stdout = execute("apps:create alternate-remote --remote alternate")
155
+ stderr.should == ""
156
+ stdout.should == <<-STDOUT
157
+ Creating alternate-remote... done, stack is bamboo-mri-1.9.2
158
+ http://alternate-remote.azukiapp.com/ | git@azukiapp.com:alternate-remote.git
159
+ Git remote alternate added
160
+ STDOUT
161
+ end
162
+ api.delete_app("alternate-remote")
163
+ end
164
+
165
+ end
166
+
167
+ context("index") do
168
+
169
+ before(:each) do
170
+ api.post_app("name" => "example", "stack" => "cedar")
171
+ end
172
+
173
+ after(:each) do
174
+ api.delete_app("example")
175
+ end
176
+
177
+ it "succeeds" do
178
+ stub_core.list.returns([["example", "user"]])
179
+ stderr, stdout = execute("apps")
180
+ stderr.should == ""
181
+ stdout.should == <<-STDOUT
182
+ === Dev & Legacy Apps
183
+ example
184
+
185
+ STDOUT
186
+ end
187
+
188
+ end
189
+
190
+ context("rename") do
191
+
192
+ context("success") do
193
+
194
+ before(:each) do
195
+ api.post_app("name" => "example", "stack" => "cedar")
196
+ end
197
+
198
+ after(:each) do
199
+ api.delete_app("example2")
200
+ end
201
+
202
+ it "renames app" do
203
+ with_blank_git_repository do
204
+ stderr, stdout = execute("apps:rename example2")
205
+ stderr.should == ""
206
+ stdout.should == <<-STDOUT
207
+ Renaming example to example2... done
208
+ http://example2.azukiapp.com/ | git@azukiapp.com:example2.git
209
+ Don't forget to update your Git remotes on any local checkouts.
210
+ STDOUT
211
+ end
212
+ end
213
+
214
+ end
215
+
216
+ it "displays an error if no name is specified" do
217
+ stderr, stdout = execute("apps:rename")
218
+ stderr.should == <<-STDERR
219
+ ! Usage: azuki apps:rename NEWNAME
220
+ ! Must specify NEWNAME to rename.
221
+ STDERR
222
+ stdout.should == ""
223
+ end
224
+
225
+ end
226
+
227
+ context("destroy") do
228
+
229
+ before(:each) do
230
+ api.post_app("name" => "example", "stack" => "cedar")
231
+ end
232
+
233
+ it "succeeds with app explicitly specified with --app and user confirmation" do
234
+ stderr, stdout = execute("apps:destroy --confirm example")
235
+ stderr.should == ""
236
+ stdout.should == <<-STDOUT
237
+ Destroying example (including all add-ons)... done
238
+ STDOUT
239
+ end
240
+
241
+ context("fails") do
242
+
243
+ after(:each) do
244
+ api.delete_app("example")
245
+ end
246
+
247
+ it "fails with explicit app but no confirmation" do
248
+ stderr, stdout = execute("apps:destroy example")
249
+ stderr.should == <<-STDERR
250
+ ! Confirmation did not match example. Aborted.
251
+ STDERR
252
+ stdout.should == "
253
+ ! WARNING: Potentially Destructive Action
254
+ ! This command will destroy example (including all add-ons).
255
+ ! To proceed, type \"example\" or re-run this command with --confirm example
256
+
257
+ > "
258
+
259
+ end
260
+
261
+ it "fails without explicit app" do
262
+ stderr, stdout = execute("apps:destroy")
263
+ stderr.should == <<-STDERR
264
+ ! Usage: azuki apps:destroy --app APP
265
+ ! Must specify APP to destroy.
266
+ STDERR
267
+ stdout.should == ""
268
+ end
269
+
270
+ end
271
+
272
+ end
273
+
274
+ context "Git Integration" do
275
+
276
+ it "creates adding azuki to git remote" do
277
+ with_blank_git_repository do
278
+ stderr, stdout = execute("apps:create example")
279
+ stderr.should == ""
280
+ stdout.should == <<-STDOUT
281
+ Creating example... done, stack is bamboo-mri-1.9.2
282
+ http://example.azukiapp.com/ | git@azukiapp.com:example.git
283
+ Git remote azuki added
284
+ STDOUT
285
+ `git remote`.strip.should match(/^azuki$/)
286
+ api.delete_app("example")
287
+ end
288
+ end
289
+
290
+ it "creates adding a custom git remote" do
291
+ with_blank_git_repository do
292
+ stderr, stdout = execute("apps:create example --remote myremote")
293
+ stderr.should == ""
294
+ stdout.should == <<-STDOUT
295
+ Creating example... done, stack is bamboo-mri-1.9.2
296
+ http://example.azukiapp.com/ | git@azukiapp.com:example.git
297
+ Git remote myremote added
298
+ STDOUT
299
+ `git remote`.strip.should match(/^myremote$/)
300
+ api.delete_app("example")
301
+ end
302
+ end
303
+
304
+ it "doesn't add a git remote if it already exists" do
305
+ with_blank_git_repository do
306
+ `git remote add azuki /tmp/git_spec_#{Process.pid}`
307
+ stderr, stdout = execute("apps:create example")
308
+ stderr.should == ""
309
+ stdout.should == <<-STDOUT
310
+ Creating example... done, stack is bamboo-mri-1.9.2
311
+ http://example.azukiapp.com/ | git@azukiapp.com:example.git
312
+ STDOUT
313
+ api.delete_app("example")
314
+ end
315
+ end
316
+
317
+ it "renames updating the corresponding azuki git remote" do
318
+ with_blank_git_repository do
319
+ `git remote add github git@github.com:test/test.git`
320
+ `git remote add production git@azukiapp.com:example.git`
321
+ `git remote add staging git@azukiapp.com:example-staging.git`
322
+
323
+ api.post_app("name" => "example", "stack" => "cedar")
324
+ stderr, stdout = execute("apps:rename example2")
325
+ api.delete_app("example2")
326
+
327
+ remotes = `git remote -v`
328
+ remotes.should == <<-REMOTES
329
+ github\tgit@github.com:test/test.git (fetch)
330
+ github\tgit@github.com:test/test.git (push)
331
+ production\tgit@azukiapp.com:example2.git (fetch)
332
+ production\tgit@azukiapp.com:example2.git (push)
333
+ staging\tgit@azukiapp.com:example-staging.git (fetch)
334
+ staging\tgit@azukiapp.com:example-staging.git (push)
335
+ REMOTES
336
+ end
337
+ end
338
+
339
+ it "destroys removing any remotes pointing to the app" do
340
+ with_blank_git_repository do
341
+ `git remote add azuki git@azukiapp.com:example.git`
342
+
343
+ api.post_app("name" => "example", "stack" => "cedar")
344
+ stderr, stdout = execute("apps:destroy --confirm example")
345
+
346
+ `git remote`.strip.should_not include('azuki')
347
+ end
348
+ end
349
+ end
350
+ end
351
+ end
@@ -0,0 +1,38 @@
1
+ require "spec_helper"
2
+ require "azuki/command/auth"
3
+
4
+ describe Azuki::Command::Auth do
5
+ describe "auth" do
6
+ it "displays azuki help auth" do
7
+ stderr, stdout = execute("auth")
8
+
9
+ stderr.should == ""
10
+ stdout.should include "Additional commands"
11
+ stdout.should include "auth:login"
12
+ stdout.should include "auth:logout"
13
+ end
14
+ end
15
+
16
+ describe "auth:token" do
17
+
18
+ it "displays the user's api key" do
19
+ stderr, stdout = execute("auth:token")
20
+ stderr.should == ""
21
+ stdout.should == <<-STDOUT
22
+ apikey01
23
+ STDOUT
24
+ end
25
+ end
26
+
27
+ describe "auth:whoami" do
28
+ it "displays the user's email address" do
29
+ stderr, stdout = execute("auth:whoami")
30
+ stderr.should == ""
31
+ stdout.should == <<-STDOUT
32
+ email@example.com
33
+ STDOUT
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,109 @@
1
+ require "spec_helper"
2
+ require "azuki/command/base"
3
+
4
+ module Azuki::Command
5
+ describe Base do
6
+ before do
7
+ @base = Base.new
8
+ @base.stub!(:display)
9
+ @client = mock('azuki client', :host => 'azukiapp.com')
10
+ end
11
+
12
+ describe "confirming" do
13
+ it "confirms the app via --confirm" do
14
+ Azuki::Command.stub(:current_options).and_return(:confirm => "example")
15
+ @base.stub(:app).and_return("example")
16
+ @base.confirm_command.should be_true
17
+ end
18
+
19
+ it "does not confirms the app via --confirm on a mismatch" do
20
+ Azuki::Command.stub(:current_options).and_return(:confirm => "badapp")
21
+ @base.stub(:app).and_return("example")
22
+ lambda { @base.confirm_command}.should raise_error CommandFailed
23
+ end
24
+
25
+ it "confirms the app interactively via ask" do
26
+ @base.stub(:app).and_return("example")
27
+ @base.stub(:ask).and_return("example")
28
+ Azuki::Command.stub(:current_options).and_return({})
29
+ @base.confirm_command.should be_true
30
+ end
31
+
32
+ it "fails if the interactive confirm doesn't match" do
33
+ @base.stub(:app).and_return("example")
34
+ @base.stub(:ask).and_return("badresponse")
35
+ Azuki::Command.stub(:current_options).and_return({})
36
+ capture_stderr do
37
+ lambda { @base.confirm_command }.should raise_error(SystemExit)
38
+ end.should == <<-STDERR
39
+ ! Confirmation did not match example. Aborted.
40
+ STDERR
41
+ end
42
+ end
43
+
44
+ context "detecting the app" do
45
+ it "attempts to find the app via the --app option" do
46
+ @base.stub!(:options).and_return(:app => "example")
47
+ @base.app.should == "example"
48
+ end
49
+
50
+ it "attempts to find the app via the --confirm option" do
51
+ @base.stub!(:options).and_return(:confirm => "myconfirmapp")
52
+ @base.app.should == "myconfirmapp"
53
+ end
54
+
55
+ it "attempts to find the app via AZUKI_APP when not explicitly specified" do
56
+ ENV['AZUKI_APP'] = "myenvapp"
57
+ @base.app.should == "myenvapp"
58
+ @base.stub!(:options).and_return([])
59
+ @base.app.should == "myenvapp"
60
+ ENV.delete('AZUKI_APP')
61
+ end
62
+
63
+ it "overrides AZUKI_APP when explicitly specified" do
64
+ ENV['AZUKI_APP'] = "myenvapp"
65
+ @base.stub!(:options).and_return(:app => "example")
66
+ @base.app.should == "example"
67
+ ENV.delete('AZUKI_APP')
68
+ end
69
+
70
+ it "read remotes from git config" do
71
+ Dir.stub(:chdir)
72
+ File.should_receive(:exists?).with(".git").and_return(true)
73
+ @base.should_receive(:git).with('remote -v').and_return(<<-REMOTES)
74
+ staging\tgit@azukiapp.com:example-staging.git (fetch)
75
+ staging\tgit@azukiapp.com:example-staging.git (push)
76
+ production\tgit@azukiapp.com:example.git (fetch)
77
+ production\tgit@azukiapp.com:example.git (push)
78
+ other\tgit@other.com:other.git (fetch)
79
+ other\tgit@other.com:other.git (push)
80
+ REMOTES
81
+
82
+ @azuki = mock
83
+ @azuki.stub(:host).and_return('azukiapp.com')
84
+ @base.stub(:azuki).and_return(@azuki)
85
+
86
+ # need a better way to test internal functionality
87
+ @base.send(:git_remotes, '/home/dev/example').should == { 'staging' => 'example-staging', 'production' => 'example' }
88
+ end
89
+
90
+ it "gets the app from remotes when there's only one app" do
91
+ @base.stub!(:git_remotes).and_return({ 'azuki' => 'example' })
92
+ @base.stub!(:git).with("config azuki.remote").and_return("")
93
+ @base.app.should == 'example'
94
+ end
95
+
96
+ it "accepts a --remote argument to choose the app from the remote name" do
97
+ @base.stub!(:git_remotes).and_return({ 'staging' => 'example-staging', 'production' => 'example' })
98
+ @base.stub!(:options).and_return(:remote => "staging")
99
+ @base.app.should == 'example-staging'
100
+ end
101
+
102
+ it "raises when cannot determine which app is it" do
103
+ @base.stub!(:git_remotes).and_return({ 'staging' => 'example-staging', 'production' => 'example' })
104
+ lambda { @base.app }.should raise_error(Azuki::Command::CommandFailed)
105
+ end
106
+ end
107
+
108
+ end
109
+ end