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.
- checksums.yaml +7 -0
- data/README.md +71 -0
- data/bin/azuki +17 -0
- data/data/cacert.pem +3988 -0
- data/lib/azuki.rb +17 -0
- data/lib/azuki/auth.rb +339 -0
- data/lib/azuki/cli.rb +38 -0
- data/lib/azuki/client.rb +764 -0
- data/lib/azuki/client/azuki_postgresql.rb +141 -0
- data/lib/azuki/client/cisaurus.rb +26 -0
- data/lib/azuki/client/pgbackups.rb +113 -0
- data/lib/azuki/client/rendezvous.rb +108 -0
- data/lib/azuki/client/ssl_endpoint.rb +25 -0
- data/lib/azuki/command.rb +294 -0
- data/lib/azuki/command/account.rb +23 -0
- data/lib/azuki/command/accounts.rb +34 -0
- data/lib/azuki/command/addons.rb +305 -0
- data/lib/azuki/command/apps.rb +393 -0
- data/lib/azuki/command/auth.rb +86 -0
- data/lib/azuki/command/base.rb +230 -0
- data/lib/azuki/command/certs.rb +209 -0
- data/lib/azuki/command/config.rb +137 -0
- data/lib/azuki/command/db.rb +218 -0
- data/lib/azuki/command/domains.rb +85 -0
- data/lib/azuki/command/drains.rb +46 -0
- data/lib/azuki/command/fork.rb +164 -0
- data/lib/azuki/command/git.rb +64 -0
- data/lib/azuki/command/help.rb +179 -0
- data/lib/azuki/command/keys.rb +115 -0
- data/lib/azuki/command/labs.rb +147 -0
- data/lib/azuki/command/logs.rb +45 -0
- data/lib/azuki/command/maintenance.rb +61 -0
- data/lib/azuki/command/pg.rb +269 -0
- data/lib/azuki/command/pgbackups.rb +329 -0
- data/lib/azuki/command/plugins.rb +110 -0
- data/lib/azuki/command/ps.rb +232 -0
- data/lib/azuki/command/regions.rb +22 -0
- data/lib/azuki/command/releases.rb +124 -0
- data/lib/azuki/command/run.rb +180 -0
- data/lib/azuki/command/sharing.rb +89 -0
- data/lib/azuki/command/ssl.rb +43 -0
- data/lib/azuki/command/stack.rb +62 -0
- data/lib/azuki/command/status.rb +51 -0
- data/lib/azuki/command/update.rb +47 -0
- data/lib/azuki/command/version.rb +23 -0
- data/lib/azuki/deprecated.rb +5 -0
- data/lib/azuki/deprecated/help.rb +38 -0
- data/lib/azuki/distribution.rb +9 -0
- data/lib/azuki/excon.rb +9 -0
- data/lib/azuki/helpers.rb +517 -0
- data/lib/azuki/helpers/azuki_postgresql.rb +165 -0
- data/lib/azuki/helpers/log_displayer.rb +70 -0
- data/lib/azuki/plugin.rb +163 -0
- data/lib/azuki/updater.rb +171 -0
- data/lib/azuki/version.rb +3 -0
- data/lib/vendor/azuki/okjson.rb +598 -0
- data/spec/azuki/auth_spec.rb +256 -0
- data/spec/azuki/client/azuki_postgresql_spec.rb +71 -0
- data/spec/azuki/client/pgbackups_spec.rb +43 -0
- data/spec/azuki/client/rendezvous_spec.rb +62 -0
- data/spec/azuki/client/ssl_endpoint_spec.rb +48 -0
- data/spec/azuki/client_spec.rb +564 -0
- data/spec/azuki/command/addons_spec.rb +601 -0
- data/spec/azuki/command/apps_spec.rb +351 -0
- data/spec/azuki/command/auth_spec.rb +38 -0
- data/spec/azuki/command/base_spec.rb +109 -0
- data/spec/azuki/command/certs_spec.rb +178 -0
- data/spec/azuki/command/config_spec.rb +144 -0
- data/spec/azuki/command/db_spec.rb +110 -0
- data/spec/azuki/command/domains_spec.rb +87 -0
- data/spec/azuki/command/drains_spec.rb +34 -0
- data/spec/azuki/command/fork_spec.rb +56 -0
- data/spec/azuki/command/git_spec.rb +144 -0
- data/spec/azuki/command/help_spec.rb +93 -0
- data/spec/azuki/command/keys_spec.rb +120 -0
- data/spec/azuki/command/labs_spec.rb +100 -0
- data/spec/azuki/command/logs_spec.rb +60 -0
- data/spec/azuki/command/maintenance_spec.rb +51 -0
- data/spec/azuki/command/pg_spec.rb +236 -0
- data/spec/azuki/command/pgbackups_spec.rb +307 -0
- data/spec/azuki/command/plugins_spec.rb +104 -0
- data/spec/azuki/command/ps_spec.rb +195 -0
- data/spec/azuki/command/releases_spec.rb +130 -0
- data/spec/azuki/command/run_spec.rb +83 -0
- data/spec/azuki/command/sharing_spec.rb +59 -0
- data/spec/azuki/command/stack_spec.rb +46 -0
- data/spec/azuki/command/status_spec.rb +48 -0
- data/spec/azuki/command/version_spec.rb +16 -0
- data/spec/azuki/command_spec.rb +211 -0
- data/spec/azuki/helpers/azuki_postgresql_spec.rb +155 -0
- data/spec/azuki/helpers_spec.rb +48 -0
- data/spec/azuki/plugin_spec.rb +172 -0
- data/spec/azuki/updater_spec.rb +44 -0
- data/spec/helper/legacy_help.rb +16 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +224 -0
- data/spec/support/display_message_matcher.rb +49 -0
- data/spec/support/openssl_mock_helper.rb +8 -0
- metadata +211 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "azuki/command/releases"
|
|
3
|
+
|
|
4
|
+
describe Azuki::Command::Releases do
|
|
5
|
+
|
|
6
|
+
before(:each) do
|
|
7
|
+
stub_core
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe "releases" do
|
|
11
|
+
|
|
12
|
+
before(:each) do
|
|
13
|
+
api.post_app("name" => "example", "stack" => "cedar")
|
|
14
|
+
api.put_config_vars("example", { 'FOO_BAR' => 'BAZ' })
|
|
15
|
+
api.put_config_vars("example", { 'BAR_BAZ' => 'QUX' })
|
|
16
|
+
api.put_config_vars("example", { 'BAZ_QUX' => 'QUUX' })
|
|
17
|
+
api.put_config_vars("example", { 'QUX_QUUX' => 'XYZZY' })
|
|
18
|
+
api.put_config_vars("example", { 'SUPER_LONG_CONFIG_VAR_TO_GET_PAST_THE_TRUNCATION_LIMIT' => 'VALUE' })
|
|
19
|
+
Azuki::Command::Releases.any_instance.should_receive(:time_ago).exactly(5).times.and_return('2012/09/10 11:36:44 (~ 0s ago)', '2012/09/10 11:36:43 (~ 1s ago)', '2012/09/10 11:35:44 (~ 1m ago)', '2012/09/10 10:36:44 (~ 1h ago)', '2012/01/02 12:34:56')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
after(:each) do
|
|
23
|
+
api.delete_app("example")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should list releases" do
|
|
27
|
+
@stderr, @stdout = execute("releases")
|
|
28
|
+
@stderr.should == ""
|
|
29
|
+
@stdout.should == <<-STDOUT
|
|
30
|
+
=== example Releases
|
|
31
|
+
v5 Config add SUPER_LONG_CONFIG_VAR_TO_GE.. email@example.com 2012/09/10 11:36:44 (~ 0s ago)
|
|
32
|
+
v4 Config add QUX_QUUX email@example.com 2012/09/10 11:36:43 (~ 1s ago)
|
|
33
|
+
v3 Config add BAZ_QUX email@example.com 2012/09/10 11:35:44 (~ 1m ago)
|
|
34
|
+
v2 Config add BAR_BAZ email@example.com 2012/09/10 10:36:44 (~ 1h ago)
|
|
35
|
+
v1 Config add FOO_BAR email@example.com 2012/01/02 12:34:56
|
|
36
|
+
|
|
37
|
+
STDOUT
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe "releases:info" do
|
|
43
|
+
before(:each) do
|
|
44
|
+
api.post_app("name" => "example", "stack" => "cedar")
|
|
45
|
+
api.put_config_vars("example", { 'FOO_BAR' => 'BAZ' })
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
after(:each) do
|
|
49
|
+
api.delete_app("example")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "requires a release to be specified" do
|
|
53
|
+
stderr, stdout = execute("releases:info")
|
|
54
|
+
stderr.should == <<-STDERR
|
|
55
|
+
! Usage: azuki releases:info RELEASE
|
|
56
|
+
STDERR
|
|
57
|
+
stdout.should == ""
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "shows info for a single release" do
|
|
61
|
+
Azuki::Command::Releases.any_instance.should_receive(:time_ago).and_return("2012/09/11 12:34:56 (~ 0s ago)")
|
|
62
|
+
stderr, stdout = execute("releases:info v1")
|
|
63
|
+
stderr.should == ""
|
|
64
|
+
stdout.should == <<-STDOUT
|
|
65
|
+
=== Release v1
|
|
66
|
+
By: email@example.com
|
|
67
|
+
Change: Config add FOO_BAR
|
|
68
|
+
When: 2012/09/11 12:34:56 (~ 0s ago)
|
|
69
|
+
|
|
70
|
+
=== v1 Config Vars
|
|
71
|
+
BUNDLE_WITHOUT: development:test
|
|
72
|
+
DATABASE_URL: postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username
|
|
73
|
+
LANG: en_US.UTF-8
|
|
74
|
+
RACK_ENV: production
|
|
75
|
+
SHARED_DATABASE_URL: postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username
|
|
76
|
+
STDOUT
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "shows info for a single release in shell compatible format" do
|
|
80
|
+
Azuki::Command::Releases.any_instance.should_receive(:time_ago).and_return("2012/09/11 12:34:56 (~ 0s ago)")
|
|
81
|
+
stderr, stdout = execute("releases:info v1 --shell")
|
|
82
|
+
stderr.should == ""
|
|
83
|
+
stdout.should == <<-STDOUT
|
|
84
|
+
=== Release v1
|
|
85
|
+
By: email@example.com
|
|
86
|
+
Change: Config add FOO_BAR
|
|
87
|
+
When: 2012/09/11 12:34:56 (~ 0s ago)
|
|
88
|
+
|
|
89
|
+
=== v1 Config Vars
|
|
90
|
+
BUNDLE_WITHOUT=development:test
|
|
91
|
+
DATABASE_URL=postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username
|
|
92
|
+
LANG=en_US.UTF-8
|
|
93
|
+
RACK_ENV=production
|
|
94
|
+
SHARED_DATABASE_URL=postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username
|
|
95
|
+
STDOUT
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe "rollback" do
|
|
100
|
+
before(:each) do
|
|
101
|
+
api.post_app("name" => "example", "stack" => "cedar")
|
|
102
|
+
api.put_config_vars("example", { 'FOO_BAR' => 'BAZ' })
|
|
103
|
+
api.put_config_vars("example", { 'BAR_BAZ' => 'QUX' })
|
|
104
|
+
api.put_config_vars("example", { 'BAZ_QUX' => 'QUUX' })
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
after(:each) do
|
|
108
|
+
api.delete_app("example")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "rolls back to the latest release with no argument" do
|
|
112
|
+
stderr, stdout = execute("releases:rollback")
|
|
113
|
+
stderr.should == ""
|
|
114
|
+
stdout.should == <<-STDOUT
|
|
115
|
+
Rolling back example... done, v2
|
|
116
|
+
STDOUT
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "rolls back to the specified release" do
|
|
120
|
+
stderr, stdout = execute("releases:rollback v1")
|
|
121
|
+
stderr.should == ""
|
|
122
|
+
stdout.should == <<-STDOUT
|
|
123
|
+
Rolling back example... done, v1
|
|
124
|
+
STDOUT
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "azuki/command/run"
|
|
3
|
+
require "azuki/helpers"
|
|
4
|
+
|
|
5
|
+
describe Azuki::Command::Run do
|
|
6
|
+
|
|
7
|
+
include Azuki::Helpers
|
|
8
|
+
|
|
9
|
+
before(:each) do
|
|
10
|
+
stub_core
|
|
11
|
+
api.post_app("name" => "example", "stack" => "cedar")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
after(:each) do
|
|
15
|
+
api.delete_app("example")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "run" do
|
|
19
|
+
it "runs a command" do
|
|
20
|
+
stub_rendezvous.start { $stdout.puts "output" }
|
|
21
|
+
|
|
22
|
+
stderr, stdout = execute("run bin/foo")
|
|
23
|
+
stderr.should == ""
|
|
24
|
+
stdout.should == <<-STDOUT
|
|
25
|
+
Running `bin/foo` attached to terminal... up, run.1
|
|
26
|
+
output
|
|
27
|
+
STDOUT
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe "run:detached" do
|
|
32
|
+
it "runs a command detached" do
|
|
33
|
+
stderr, stdout = execute("run:detached bin/foo")
|
|
34
|
+
stderr.should == ""
|
|
35
|
+
stdout.should == <<-STDOUT
|
|
36
|
+
Running `bin/foo` detached... up, run.1
|
|
37
|
+
Use `azuki logs -p run.1` to view the output.
|
|
38
|
+
STDOUT
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "runs with options" do
|
|
42
|
+
stub_core.read_logs("example", [
|
|
43
|
+
"tail=1",
|
|
44
|
+
"ps=run.1"
|
|
45
|
+
])
|
|
46
|
+
execute "run:detached bin/foo --tail"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe "run:rake" do
|
|
51
|
+
it "runs a rake command" do
|
|
52
|
+
stub_rendezvous.start { $stdout.puts("rake_output") }
|
|
53
|
+
|
|
54
|
+
stderr, stdout = execute("run:rake foo")
|
|
55
|
+
stderr.should == ""
|
|
56
|
+
stdout.should == <<-STDOUT
|
|
57
|
+
WARNING: `azuki run:rake` has been deprecated. Please use `azuki run rake` instead.
|
|
58
|
+
Running `rake foo` attached to terminal... up, run.1
|
|
59
|
+
rake_output
|
|
60
|
+
STDOUT
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "shows the proper command in the deprecation warning" do
|
|
64
|
+
stub_rendezvous.start { $stdout.puts("rake_output") }
|
|
65
|
+
|
|
66
|
+
stderr, stdout = execute("rake foo")
|
|
67
|
+
stderr.should == ""
|
|
68
|
+
stdout.should == <<-STDOUT
|
|
69
|
+
WARNING: `azuki rake` has been deprecated. Please use `azuki run rake` instead.
|
|
70
|
+
Running `rake foo` attached to terminal... up, run.1
|
|
71
|
+
rake_output
|
|
72
|
+
STDOUT
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe "run:console" do
|
|
77
|
+
it "has been removed" do
|
|
78
|
+
stderr, stdout = execute("run:console")
|
|
79
|
+
stderr.should == ""
|
|
80
|
+
stdout.should =~ /has been removed/
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "azuki/command/sharing"
|
|
3
|
+
|
|
4
|
+
module Azuki::Command
|
|
5
|
+
describe Sharing do
|
|
6
|
+
|
|
7
|
+
before(:each) do
|
|
8
|
+
stub_core
|
|
9
|
+
api.post_app("name" => "example")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
after(:each) do
|
|
13
|
+
api.delete_app("example")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
context("list") do
|
|
17
|
+
|
|
18
|
+
it "lists collaborators" do
|
|
19
|
+
api.post_collaborator("example", "collaborator@example.com")
|
|
20
|
+
stderr, stdout = execute("sharing")
|
|
21
|
+
stderr.should == ""
|
|
22
|
+
stdout.should == <<-STDOUT
|
|
23
|
+
=== example Collaborators
|
|
24
|
+
collaborator@example.com
|
|
25
|
+
email@example.com
|
|
26
|
+
|
|
27
|
+
STDOUT
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "adds collaborators with default access to view only" do
|
|
33
|
+
stderr, stdout = execute("sharing:add collaborator@example.com")
|
|
34
|
+
stderr.should == ""
|
|
35
|
+
stdout.should == <<-STDOUT
|
|
36
|
+
Adding collaborator@example.com to example collaborators... done
|
|
37
|
+
STDOUT
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "removes collaborators" do
|
|
41
|
+
api.post_collaborator("example", "collaborator@example.com")
|
|
42
|
+
stderr, stdout = execute("sharing:remove collaborator@example.com")
|
|
43
|
+
stderr.should == ""
|
|
44
|
+
stdout.should == <<-STDOUT
|
|
45
|
+
Removing collaborator@example.com from example collaborators... done
|
|
46
|
+
STDOUT
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "transfers ownership" do
|
|
50
|
+
api.post_collaborator("example", "collaborator@example.com")
|
|
51
|
+
stderr, stdout = execute("sharing:transfer collaborator@example.com")
|
|
52
|
+
stderr.should == ""
|
|
53
|
+
stdout.should == <<-STDOUT
|
|
54
|
+
Transferring example to collaborator@example.com... done
|
|
55
|
+
STDOUT
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "azuki/command/stack"
|
|
3
|
+
|
|
4
|
+
module Azuki::Command
|
|
5
|
+
describe Stack do
|
|
6
|
+
describe "index" do
|
|
7
|
+
before(:each) do
|
|
8
|
+
stub_core
|
|
9
|
+
api.post_app("name" => "example", "stack" => "bamboo-mri-1.9.2")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
after(:each) do
|
|
13
|
+
api.delete_app("example")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "index should provide list" do
|
|
17
|
+
stderr, stdout = execute("stack")
|
|
18
|
+
stderr.should == ""
|
|
19
|
+
stdout.should == <<-STDOUT
|
|
20
|
+
=== example Available Stacks
|
|
21
|
+
aspen-mri-1.8.6
|
|
22
|
+
bamboo-ree-1.8.7
|
|
23
|
+
cedar (beta)
|
|
24
|
+
* bamboo-mri-1.9.2
|
|
25
|
+
|
|
26
|
+
STDOUT
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "migrate should succeed" do
|
|
30
|
+
stderr, stdout = execute("stack:migrate bamboo-ree-1.8.7")
|
|
31
|
+
stderr.should == ""
|
|
32
|
+
stdout.should == <<-STDOUT
|
|
33
|
+
-----> Preparing to migrate example
|
|
34
|
+
bamboo-mri-1.9.2 -> bamboo-ree-1.8.7
|
|
35
|
+
|
|
36
|
+
NOTE: Additional details here
|
|
37
|
+
|
|
38
|
+
-----> Migration prepared.
|
|
39
|
+
Run 'git push azuki master' to execute migration.
|
|
40
|
+
STDOUT
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "azuki/command/status"
|
|
3
|
+
|
|
4
|
+
module Azuki::Command
|
|
5
|
+
describe Status do
|
|
6
|
+
|
|
7
|
+
before(:each) do
|
|
8
|
+
stub_core
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "displays status information" do
|
|
12
|
+
Excon.stub(
|
|
13
|
+
{
|
|
14
|
+
:host => 'status.azukiapp.com',
|
|
15
|
+
:method => :get,
|
|
16
|
+
:path => '/api/v3/current-status.json'
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
:body => Azuki::API::OkJson.encode({"status"=>{"Production"=>"red", "Development"=>"red"}, "issues"=>[{"created_at"=>"2012-06-07T15:55:51Z", "id"=>372, "resolved"=>false, "title"=>"HTTP Routing Errors", "updated_at"=>"2012-06-07T16:14:37Z", "href"=>"https://status.azukiapp.com/api/v3/issues/372", "updates"=>[{"contents"=>"The number of applications seeing H99 errors is continuing to decrease as we continue to work toward a full resolution of the HTTP routing issues. The API is back online now as well. ", "created_at"=>"2012-06-07T17:47:26Z", "id"=>1088, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T17:47:26Z"}, {"contents"=>"Our engineers are continuing to work toward a full resolution of the HTTP routing issues. The API is currently in maintenance mode intentionally as we restore application operations. ", "created_at"=>"2012-06-07T17:16:40Z", "id"=>1086, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T17:26:55Z"}, {"contents"=>"Most applications are back online at this time. Our engineers are working on getting the remaining apps back online. ", "created_at"=>"2012-06-07T16:50:21Z", "id"=>1085, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T16:50:21Z"}, {"contents"=>"Our routing engineers have pushed out a patch to our routing tier. The platform is recovering and applications are coming back online. Our engineers are continuing to fully restore service.", "created_at"=>"2012-06-07T16:36:37Z", "id"=>1084, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T16:36:37Z"}, {"contents"=>"We have identified an issue with our routers that is causing errors on HTTP requests to applications. Engineers are working to resolve the issue.\r\n", "created_at"=>"2012-06-07T16:15:25Z", "id"=>1083, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T16:15:28Z"}, {"contents"=>"We have confirmed widespread errors on the platform. Our engineers are continuing to investigate.\r\n", "created_at"=>"2012-06-07T15:58:56Z", "id"=>1082, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T15:58:58Z"}, {"contents"=>"Our automated systems have detected potential platform errors. We are investigating.\r\n", "created_at"=>"2012-06-07T15:55:51Z", "id"=>1081, "incident_id"=>372, "status_dev"=>"yellow", "status_prod"=>"yellow", "update_type"=>"issue", "updated_at"=>"2012-06-07T15:55:55Z"}]}]}),
|
|
20
|
+
:status => 200
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
Azuki::Command::Status.any_instance.should_receive(:time_ago).and_return('2012/09/11 09:34:56 (~ 3h ago)', '2012/09/11 12:33:56 (~ 1m ago)', '2012/09/11 12:29:56 (~ 5m ago)', '2012/09/11 12:24:56 (~ 10m ago)', '2012/09/11 12:04:56 (~ 30m ago)', '2012/09/11 11:34:56 (~ 1h ago)', '2012/09/11 10:34:56 (~ 2h ago)', '2012/09/11 09:34:56 (~ 3h ago)')
|
|
25
|
+
|
|
26
|
+
stderr, stdout = execute("status")
|
|
27
|
+
stderr.should == ''
|
|
28
|
+
stdout.should == <<-STDOUT
|
|
29
|
+
=== Azuki Status
|
|
30
|
+
Development: red
|
|
31
|
+
Production: red
|
|
32
|
+
|
|
33
|
+
=== HTTP Routing Errors 2012/09/11 09:34:56 (~ 3h+)
|
|
34
|
+
2012/09/11 12:33:56 (~ 1m ago) update The number of applications seeing H99 errors is continuing to decrease as we continue to work toward a full resolution of the HTTP routing issues. The API is back online now as well.
|
|
35
|
+
2012/09/11 12:29:56 (~ 5m ago) update Our engineers are continuing to work toward a full resolution of the HTTP routing issues. The API is currently in maintenance mode intentionally as we restore application operations.
|
|
36
|
+
2012/09/11 12:24:56 (~ 10m ago) update Most applications are back online at this time. Our engineers are working on getting the remaining apps back online.
|
|
37
|
+
2012/09/11 12:04:56 (~ 30m ago) update Our routing engineers have pushed out a patch to our routing tier. The platform is recovering and applications are coming back online. Our engineers are continuing to fully restore service.
|
|
38
|
+
2012/09/11 11:34:56 (~ 1h ago) update We have identified an issue with our routers that is causing errors on HTTP requests to applications. Engineers are working to resolve the issue.
|
|
39
|
+
2012/09/11 10:34:56 (~ 2h ago) update We have confirmed widespread errors on the platform. Our engineers are continuing to investigate.
|
|
40
|
+
2012/09/11 09:34:56 (~ 3h ago) issue Our automated systems have detected potential platform errors. We are investigating.
|
|
41
|
+
|
|
42
|
+
STDOUT
|
|
43
|
+
|
|
44
|
+
Excon.stubs.shift
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "azuki/command/version"
|
|
3
|
+
|
|
4
|
+
module Azuki::Command
|
|
5
|
+
describe Version do
|
|
6
|
+
|
|
7
|
+
it "shows version info" do
|
|
8
|
+
stderr, stdout = execute("version")
|
|
9
|
+
stderr.should == ""
|
|
10
|
+
stdout.should == <<-STDOUT
|
|
11
|
+
#{Azuki.user_agent}
|
|
12
|
+
STDOUT
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "azuki/command"
|
|
3
|
+
require 'json' #FOR WEBMOCK
|
|
4
|
+
|
|
5
|
+
class FakeResponse
|
|
6
|
+
|
|
7
|
+
attr_accessor :body, :headers
|
|
8
|
+
|
|
9
|
+
def initialize(attributes)
|
|
10
|
+
self.body, self.headers = attributes[:body], attributes[:headers]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_s
|
|
14
|
+
body
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe Azuki::Command do
|
|
20
|
+
before {
|
|
21
|
+
Azuki::Command.load
|
|
22
|
+
stub_core # setup fake auth
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe "when the command requires confirmation" do
|
|
26
|
+
|
|
27
|
+
let(:response_that_requires_confirmation) do
|
|
28
|
+
{:status => 423,
|
|
29
|
+
:headers => { :x_confirmation_required => 'my_addon' },
|
|
30
|
+
:body => 'terms of service required'}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context "when the app is unknown" do
|
|
34
|
+
context "and the user includes --confirm APP" do
|
|
35
|
+
it "should set --app to APP and not ask for confirmation" do
|
|
36
|
+
stub_request(:post, %r{apps/XXX/addons/my_addon$}).
|
|
37
|
+
with(:body => {:confirm => "XXX"})
|
|
38
|
+
run "addons:add my_addon --confirm XXX"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
context "and the user includes --confirm APP --app APP2" do
|
|
43
|
+
it "should warn that the app and confirm do not match and not continue" do
|
|
44
|
+
capture_stderr do
|
|
45
|
+
run "addons:add my_addon --confirm APP --app APP2"
|
|
46
|
+
end.should == " ! Mismatch between --app and --confirm\n"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context "and the app is known" do
|
|
52
|
+
before do
|
|
53
|
+
any_instance_of(Azuki::Command::Base) do |base|
|
|
54
|
+
stub(base).app.returns("example")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context "and the user includes --confirm WRONGAPP" do
|
|
59
|
+
it "should not allow include the option" do
|
|
60
|
+
stub_request(:post, %r{apps/example/addons/my_addon$}).
|
|
61
|
+
with(:body => "")
|
|
62
|
+
run "addons:add my_addon --confirm XXX"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context "and the user includes --confirm APP" do
|
|
67
|
+
it "should set --app to APP and not ask for confirmation" do
|
|
68
|
+
stub_request(:post, %r{apps/example/addons/my_addon$}).
|
|
69
|
+
with(:body => {:confirm => 'example'})
|
|
70
|
+
|
|
71
|
+
run "addons:add my_addon --confirm example"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
context "and the user didn't include a confirm flag" do
|
|
76
|
+
it "should ask the user for confirmation" do
|
|
77
|
+
stub(Azuki::Command).confirm_command.returns(true)
|
|
78
|
+
stub_request(:post, %r{apps/example/addons/my_addon$}).
|
|
79
|
+
to_return(response_that_requires_confirmation).then.
|
|
80
|
+
to_return({:status => 200})
|
|
81
|
+
|
|
82
|
+
run "addons:add my_addon"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "should not continue if the confirmation does not match" do
|
|
86
|
+
Azuki::Command.stub(:current_options).and_return(:confirm => 'not_example')
|
|
87
|
+
|
|
88
|
+
lambda do
|
|
89
|
+
Azuki::Command.confirm_command('example')
|
|
90
|
+
end.should raise_error(Azuki::Command::CommandFailed)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "should not continue if the user doesn't confirm" do
|
|
94
|
+
stub(Azuki::Command).confirm_command.returns(false)
|
|
95
|
+
stub_request(:post, %r{apps/example/addons/my_addon$}).
|
|
96
|
+
to_return(response_that_requires_confirmation).then.
|
|
97
|
+
to_raise(Azuki::Command::CommandFailed)
|
|
98
|
+
|
|
99
|
+
run "addons:add my_addon"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe "parsing errors" do
|
|
106
|
+
it "extracts error messages from response when available in XML" do
|
|
107
|
+
Azuki::Command.extract_error('<errors><error>Invalid app name</error></errors>').should == 'Invalid app name'
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "extracts error messages from response when available in JSON" do
|
|
111
|
+
Azuki::Command.extract_error("{\"error\":\"Invalid app name\"}").should == 'Invalid app name'
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "extracts error messages from response when available in plain text" do
|
|
115
|
+
response = FakeResponse.new(:body => "Invalid app name", :headers => { :content_type => "text/plain; charset=UTF8" })
|
|
116
|
+
Azuki::Command.extract_error(response).should == 'Invalid app name'
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "shows Internal Server Error when the response doesn't contain a XML or JSON" do
|
|
120
|
+
Azuki::Command.extract_error('<h1>HTTP 500</h1>').should == "Internal server error.\nRun `azuki status` to check for known platform issues."
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "shows Internal Server Error when the response is not plain text" do
|
|
124
|
+
response = FakeResponse.new(:body => "Foobar", :headers => { :content_type => "application/xml" })
|
|
125
|
+
Azuki::Command.extract_error(response).should == "Internal server error.\nRun `azuki status` to check for known platform issues."
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it "allows a block to redefine the default error" do
|
|
129
|
+
Azuki::Command.extract_error("Foobar") { "Ok!" }.should == 'Ok!'
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it "doesn't format the response if set to raw" do
|
|
133
|
+
Azuki::Command.extract_error("Foobar", :raw => true) { "Ok!" }.should == 'Ok!'
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "handles a nil body in parse_error_xml" do
|
|
137
|
+
lambda { Azuki::Command.parse_error_xml(nil) }.should_not raise_error
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "handles a nil body in parse_error_json" do
|
|
141
|
+
lambda { Azuki::Command.parse_error_json(nil) }.should_not raise_error
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "correctly resolves commands" do
|
|
146
|
+
class Azuki::Command::Test; end
|
|
147
|
+
class Azuki::Command::Test::Multiple; end
|
|
148
|
+
|
|
149
|
+
require "azuki/command/help"
|
|
150
|
+
require "azuki/command/apps"
|
|
151
|
+
|
|
152
|
+
Azuki::Command.parse("unknown").should be_nil
|
|
153
|
+
Azuki::Command.parse("list").should include(:klass => Azuki::Command::Apps, :method => :index)
|
|
154
|
+
Azuki::Command.parse("apps").should include(:klass => Azuki::Command::Apps, :method => :index)
|
|
155
|
+
Azuki::Command.parse("apps:create").should include(:klass => Azuki::Command::Apps, :method => :create)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
context "help" do
|
|
159
|
+
it "works as a prefix" do
|
|
160
|
+
azuki("help ps:scale").should =~ /scale processes by/
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it "works as an option" do
|
|
164
|
+
azuki("ps:scale -h").should =~ /scale processes by/
|
|
165
|
+
azuki("ps:scale --help").should =~ /scale processes by/
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
context "when no commands match" do
|
|
170
|
+
|
|
171
|
+
it "displays the version if --version is used" do
|
|
172
|
+
azuki("--version").should == <<-STDOUT
|
|
173
|
+
#{Azuki.user_agent}
|
|
174
|
+
STDOUT
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "suggests similar commands if there are any" do
|
|
178
|
+
original_stderr, original_stdout = $stderr, $stdout
|
|
179
|
+
$stderr = captured_stderr = StringIO.new
|
|
180
|
+
$stdout = captured_stdout = StringIO.new
|
|
181
|
+
begin
|
|
182
|
+
execute("aps")
|
|
183
|
+
rescue SystemExit
|
|
184
|
+
end
|
|
185
|
+
captured_stderr.string.should == <<-STDERR
|
|
186
|
+
! `aps` is not a azuki command.
|
|
187
|
+
! Perhaps you meant `apps` or `ps`.
|
|
188
|
+
! See `azuki help` for a list of available commands.
|
|
189
|
+
STDERR
|
|
190
|
+
captured_stdout.string.should == ""
|
|
191
|
+
$stderr, $stdout = original_stderr, original_stdout
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "does not suggest similar commands if there are none" do
|
|
195
|
+
original_stderr, original_stdout = $stderr, $stdout
|
|
196
|
+
$stderr = captured_stderr = StringIO.new
|
|
197
|
+
$stdout = captured_stdout = StringIO.new
|
|
198
|
+
begin
|
|
199
|
+
execute("sandwich")
|
|
200
|
+
rescue SystemExit
|
|
201
|
+
end
|
|
202
|
+
captured_stderr.string.should == <<-STDERR
|
|
203
|
+
! `sandwich` is not a azuki command.
|
|
204
|
+
! See `azuki help` for a list of available commands.
|
|
205
|
+
STDERR
|
|
206
|
+
captured_stdout.string.should == ""
|
|
207
|
+
$stderr, $stdout = original_stderr, original_stdout
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
end
|
|
211
|
+
end
|