shelly 0.0.30 → 0.0.31
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/shelly/app.rb +26 -6
- data/lib/shelly/cli/config.rb +47 -0
- data/lib/shelly/cli/deploys.rb +43 -2
- data/lib/shelly/cli/main.rb +24 -0
- data/lib/shelly/client.rb +13 -1
- data/lib/shelly/cloudfile.rb +1 -0
- data/lib/shelly/version.rb +1 -1
- data/spec/shelly/app_spec.rb +36 -4
- data/spec/shelly/cli/config_spec.rb +55 -0
- data/spec/shelly/cli/deploys_spec.rb +78 -5
- data/spec/shelly/cli/main_spec.rb +108 -0
- data/spec/shelly/client_spec.rb +28 -2
- metadata +173 -115
data/lib/shelly/app.rb
CHANGED
@@ -50,16 +50,24 @@ module Shelly
|
|
50
50
|
File.open(cloudfile_path, "a+") { |f| f << content }
|
51
51
|
end
|
52
52
|
|
53
|
-
def
|
54
|
-
shelly.
|
53
|
+
def deploy_logs
|
54
|
+
shelly.deploy_logs(code_name)
|
55
|
+
end
|
56
|
+
|
57
|
+
def deploy_log(log)
|
58
|
+
shelly.deploy_log(code_name, log)
|
59
|
+
end
|
60
|
+
|
61
|
+
def application_logs
|
62
|
+
shelly.application_logs(self.code_name)
|
55
63
|
end
|
56
64
|
|
57
65
|
def start
|
58
|
-
shelly.start_cloud(
|
66
|
+
shelly.start_cloud(code_name)
|
59
67
|
end
|
60
68
|
|
61
69
|
def stop
|
62
|
-
shelly.stop_cloud(
|
70
|
+
shelly.stop_cloud(code_name)
|
63
71
|
end
|
64
72
|
|
65
73
|
def cloudfile_path
|
@@ -71,11 +79,23 @@ module Shelly
|
|
71
79
|
end
|
72
80
|
|
73
81
|
def ips
|
74
|
-
shelly.app_ips(
|
82
|
+
shelly.app_ips(code_name)
|
75
83
|
end
|
76
84
|
|
77
85
|
def users
|
78
|
-
shelly.app_users(
|
86
|
+
shelly.app_users(code_name)
|
87
|
+
end
|
88
|
+
|
89
|
+
def configs
|
90
|
+
@configs ||= shelly.app_configs(code_name)
|
91
|
+
end
|
92
|
+
|
93
|
+
def user_configs
|
94
|
+
configs.find_all { |config| config["created_by_user"] }
|
95
|
+
end
|
96
|
+
|
97
|
+
def shelly_generated_configs
|
98
|
+
configs.find_all { |config| config["created_by_user"] == false }
|
79
99
|
end
|
80
100
|
|
81
101
|
def open_billing_page
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "shelly/cli/command"
|
2
|
+
|
3
|
+
module Shelly
|
4
|
+
module CLI
|
5
|
+
class Config < Command
|
6
|
+
include Thor::Actions
|
7
|
+
include Helpers
|
8
|
+
|
9
|
+
desc "list", "List configuration files"
|
10
|
+
def list
|
11
|
+
logged_in?
|
12
|
+
say_error "No Cloudfile found" unless Cloudfile.present?
|
13
|
+
cloudfile = Cloudfile.new
|
14
|
+
cloudfile.clouds.each do |cloud|
|
15
|
+
@app = App.new(cloud)
|
16
|
+
begin
|
17
|
+
configs = @app.configs
|
18
|
+
unless configs.empty?
|
19
|
+
say "Configuration files for #{cloud}", :green
|
20
|
+
user_configs = @app.user_configs
|
21
|
+
unless user_configs.empty?
|
22
|
+
say "Custom configuration files:"
|
23
|
+
user_configs.each { |config| say " * #{config["path"]}" }
|
24
|
+
else
|
25
|
+
say "You have no custom configuration files."
|
26
|
+
end
|
27
|
+
shelly_configs = @app.shelly_generated_configs
|
28
|
+
unless shelly_configs.empty?
|
29
|
+
say "Following files are created by Shelly Cloud:"
|
30
|
+
shelly_configs.each { |config| say " * #{config["path"]}" }
|
31
|
+
end
|
32
|
+
else
|
33
|
+
say "Cloud #{cloud} has no configuration files"
|
34
|
+
end
|
35
|
+
rescue Client::APIError => e
|
36
|
+
if e.unauthorized?
|
37
|
+
say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
|
38
|
+
else
|
39
|
+
say_error e.message
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/shelly/cli/deploys.rb
CHANGED
@@ -11,8 +11,8 @@ module Shelly
|
|
11
11
|
def list(cloud = nil)
|
12
12
|
logged_in?
|
13
13
|
say_error "No Cloudfile found" unless Cloudfile.present?
|
14
|
-
multiple_clouds(cloud, "
|
15
|
-
logs = @app.
|
14
|
+
multiple_clouds(cloud, "deploys list", "Select cloud to view deploy logs using:")
|
15
|
+
logs = @app.deploy_logs
|
16
16
|
unless logs.empty?
|
17
17
|
say "Available deploy logs", :green
|
18
18
|
logs.each do |log|
|
@@ -29,6 +29,47 @@ module Shelly
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
desc "show LOG", "Show specific deploy log"
|
33
|
+
def show(log = nil, cloud = nil)
|
34
|
+
say_error "No Cloudfile found" unless Cloudfile.present?
|
35
|
+
specify_log(log)
|
36
|
+
multiple_clouds(cloud, "deploys show #{log}", "Select log and cloud to view deploy logs using:")
|
37
|
+
content = @app.deploy_log(log)
|
38
|
+
say "Log for deploy done on #{content["created_at"]}", :green
|
39
|
+
if content["bundle_install"]
|
40
|
+
say("Starting bundle install", :green); say(content["bundle_install"])
|
41
|
+
end
|
42
|
+
if content["whenever"]
|
43
|
+
say("Starting whenever", :green); say(content["whenever"])
|
44
|
+
end
|
45
|
+
if content["callbacks"]
|
46
|
+
say("Starting callbacks", :green); say(content["callbacks"])
|
47
|
+
end
|
48
|
+
if content["delayed_job"]
|
49
|
+
say("Starting delayed job", :green); say(content["delayed_job"])
|
50
|
+
end
|
51
|
+
if content["thin_restart"]
|
52
|
+
say("Starting thin", :green); say(content["thin_restart"])
|
53
|
+
end
|
54
|
+
rescue Client::APIError => e
|
55
|
+
if e.unauthorized?
|
56
|
+
say_error "You have no access to '#{@app.code_name}' cloud defined in Cloudfile"
|
57
|
+
elsif e.message == "Log not found"
|
58
|
+
say_error "Log not found, list all deploy logs using:", :with_exit => false
|
59
|
+
say " shelly deploys list #{cloud}"
|
60
|
+
exit 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
no_tasks do
|
65
|
+
def specify_log(log)
|
66
|
+
unless log
|
67
|
+
say_error "Specify log by passing date value or to see last log use:", :with_exit => false
|
68
|
+
say " shelly deploys show last"
|
69
|
+
exit 1
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
32
73
|
end
|
33
74
|
end
|
34
75
|
end
|
data/lib/shelly/cli/main.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "shelly/cli/command"
|
2
2
|
require "shelly/cli/user"
|
3
3
|
require "shelly/cli/deploys"
|
4
|
+
require "shelly/cli/config"
|
4
5
|
|
5
6
|
module Shelly
|
6
7
|
module CLI
|
@@ -9,6 +10,7 @@ module Shelly
|
|
9
10
|
include Helpers
|
10
11
|
register(User, "user", "user <command>", "Manages users using this cloud")
|
11
12
|
register(Deploys, "deploys", "deploys <command>", "View cloud deploy logs")
|
13
|
+
register(Config, "config", "config <command>", "Manages cloud configuration files")
|
12
14
|
check_unknown_options!
|
13
15
|
|
14
16
|
map %w(-v --version) => :version
|
@@ -234,6 +236,28 @@ module Shelly
|
|
234
236
|
say_error e.message
|
235
237
|
end
|
236
238
|
|
239
|
+
desc "logs", "Show latest application logs from each instance"
|
240
|
+
method_option :cloud, :type => :string, :aliases => "-c",
|
241
|
+
:desc => "Specify which cloud to show logs for"
|
242
|
+
def logs
|
243
|
+
cloud = options[:cloud]
|
244
|
+
logged_in?
|
245
|
+
say_error "No Cloudfile found" unless Cloudfile.present?
|
246
|
+
multiple_clouds(cloud, "logs --cloud", "Select which to show logs for using:")
|
247
|
+
begin
|
248
|
+
logs = @app.application_logs
|
249
|
+
say "Cloud #{@app.code_name}:", :green
|
250
|
+
logs.each_with_index do |log, i|
|
251
|
+
say "Instance #{i+1}:"
|
252
|
+
print_wrapped log, :ident => 2
|
253
|
+
end
|
254
|
+
rescue Client::APIError => e
|
255
|
+
if e.unauthorized?
|
256
|
+
say_error "You have no access to cloud '#{cloud || @app.code_name}'"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
237
261
|
# FIXME: move to helpers
|
238
262
|
no_tasks do
|
239
263
|
def check_options(options)
|
data/lib/shelly/client.rb
CHANGED
@@ -58,6 +58,10 @@ module Shelly
|
|
58
58
|
get("/token")
|
59
59
|
end
|
60
60
|
|
61
|
+
def app_configs(cloud)
|
62
|
+
get("/apps/#{cloud}/configs")
|
63
|
+
end
|
64
|
+
|
61
65
|
def send_invitation(cloud, email)
|
62
66
|
post("/apps/#{cloud}/collaborations", :email => email)
|
63
67
|
end
|
@@ -86,10 +90,18 @@ module Shelly
|
|
86
90
|
get("/apps")
|
87
91
|
end
|
88
92
|
|
89
|
-
def
|
93
|
+
def deploy_logs(cloud)
|
90
94
|
get("/apps/#{cloud}/deploys")
|
91
95
|
end
|
92
96
|
|
97
|
+
def deploy_log(cloud, log)
|
98
|
+
get("/apps/#{cloud}/deploys/#{log}")
|
99
|
+
end
|
100
|
+
|
101
|
+
def application_logs(cloud)
|
102
|
+
get("/apps/#{cloud}/logs")
|
103
|
+
end
|
104
|
+
|
93
105
|
def ssh_key_available?(ssh_key)
|
94
106
|
get("/users/new", :ssh_key => ssh_key)
|
95
107
|
end
|
data/lib/shelly/cloudfile.rb
CHANGED
data/lib/shelly/version.rb
CHANGED
data/spec/shelly/app_spec.rb
CHANGED
@@ -48,6 +48,23 @@ describe Shelly::App do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
describe "#configs" do
|
52
|
+
it "should get configs from client" do
|
53
|
+
@client.should_receive(:app_configs).with("foo-staging")
|
54
|
+
@app.configs
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should return only user config files" do
|
58
|
+
@client.should_receive(:app_configs).with("foo-staging").and_return([])
|
59
|
+
@app.user_configs
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should return only shelly genereted config files" do
|
63
|
+
@client.should_receive(:app_configs).with("foo-staging").and_return([])
|
64
|
+
@app.shelly_generated_configs
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
51
68
|
describe "#generate_cloudfile" do
|
52
69
|
it "should return generated cloudfile" do
|
53
70
|
user = mock(:email => "bob@example.com")
|
@@ -128,10 +145,25 @@ config
|
|
128
145
|
end
|
129
146
|
end
|
130
147
|
|
131
|
-
describe "#
|
132
|
-
it "should list
|
133
|
-
@client.should_receive(:
|
134
|
-
@app.
|
148
|
+
describe "#deploy_logs" do
|
149
|
+
it "should list deploy_logs" do
|
150
|
+
@client.should_receive(:deploy_logs).with("foo-staging")
|
151
|
+
@app.deploy_logs
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "#application_logs" do
|
156
|
+
it "should list application_logs" do
|
157
|
+
@client.should_receive(:application_logs).with("foo-staging").
|
158
|
+
and_return({"logs" => ["log1", "log2"]})
|
159
|
+
@app.application_logs
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe "#deploy_log" do
|
164
|
+
it "should show log" do
|
165
|
+
@client.should_receive(:deploy_log).with("foo-staging", "2011-11-29-11-50-16")
|
166
|
+
@app.deploy_log("2011-11-29-11-50-16")
|
135
167
|
end
|
136
168
|
end
|
137
169
|
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "shelly/cli/config"
|
3
|
+
|
4
|
+
describe Shelly::CLI::Config do
|
5
|
+
|
6
|
+
before do
|
7
|
+
FileUtils.stub(:chmod)
|
8
|
+
@config = Shelly::CLI::Config.new
|
9
|
+
@client = mock
|
10
|
+
Shelly::Client.stub(:new).and_return(@client)
|
11
|
+
$stdout.stub(:puts)
|
12
|
+
$stdout.stub(:print)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#list" do
|
16
|
+
before do
|
17
|
+
FileUtils.mkdir_p("/projects/foo")
|
18
|
+
Dir.chdir("/projects/foo")
|
19
|
+
File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\nfoo-production:\n") }
|
20
|
+
@client.stub(:token).and_return("abc")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should exit with message if there is no Cloudfile" do
|
24
|
+
File.delete("Cloudfile")
|
25
|
+
$stdout.should_receive(:puts).with(red "No Cloudfile found")
|
26
|
+
lambda {
|
27
|
+
@config.list
|
28
|
+
}.should raise_error(SystemExit)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should exit if user doesn't have access to cloud in Cloudfile" do
|
32
|
+
response = {"message" => "Cloud foo-staging not found"}
|
33
|
+
exception = Shelly::Client::APIError.new(response.to_json)
|
34
|
+
@client.stub(:app_configs).and_raise(exception)
|
35
|
+
$stdout.should_receive(:puts).with(red "You have no access to 'foo-production' cloud defined in Cloudfile")
|
36
|
+
lambda { @config.list }.should raise_error(SystemExit)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should list available configuration files for clouds" do
|
40
|
+
@client.should_receive(:app_configs).with("foo-staging").and_return([{"created_by_user" => true, "path" => "config/settings.yml"}])
|
41
|
+
@client.should_receive(:app_configs).with("foo-production").and_return([{"created_by_user" => false, "path" => "config/app.yml"}])
|
42
|
+
$stdout.should_receive(:puts).with(green "Configuration files for foo-production")
|
43
|
+
$stdout.should_receive(:puts).with("You have no custom configuration files.")
|
44
|
+
$stdout.should_receive(:puts).with("Following files are created by Shelly Cloud:")
|
45
|
+
$stdout.should_receive(:puts).with(" * config/app.yml")
|
46
|
+
$stdout.should_receive(:puts).with(green "Configuration files for foo-staging")
|
47
|
+
$stdout.should_receive(:puts).with("Custom configuration files:")
|
48
|
+
$stdout.should_receive(:puts).with(" * config/settings.yml")
|
49
|
+
|
50
|
+
@config.list
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -30,7 +30,7 @@ describe Shelly::CLI::Deploys do
|
|
30
30
|
it "should exit if user doesn't have access to cloud in Cloudfile" do
|
31
31
|
response = {"message" => "Cloud foo-staging not found"}
|
32
32
|
exception = Shelly::Client::APIError.new(response.to_json)
|
33
|
-
@client.stub(:
|
33
|
+
@client.stub(:deploy_logs).and_raise(exception)
|
34
34
|
$stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
|
35
35
|
lambda { @deploys.list }.should raise_error(SystemExit)
|
36
36
|
end
|
@@ -42,7 +42,7 @@ describe Shelly::CLI::Deploys do
|
|
42
42
|
|
43
43
|
it "should show information to select specific cloud and exit" do
|
44
44
|
$stdout.should_receive(:puts).with("You have multiple clouds in Cloudfile. Select cloud to view deploy logs using:")
|
45
|
-
$stdout.should_receive(:puts).with(" shelly
|
45
|
+
$stdout.should_receive(:puts).with(" shelly deploys list foo-production")
|
46
46
|
$stdout.should_receive(:puts).with("Available clouds:")
|
47
47
|
$stdout.should_receive(:puts).with(" * foo-production")
|
48
48
|
$stdout.should_receive(:puts).with(" * foo-staging")
|
@@ -50,7 +50,7 @@ describe Shelly::CLI::Deploys do
|
|
50
50
|
end
|
51
51
|
|
52
52
|
it "should take cloud from command line for which to show logs" do
|
53
|
-
@client.should_receive(:
|
53
|
+
@client.should_receive(:deploy_logs).with("foo-staging").and_return([{"failed" => false, "created_at" => "2011-12-12-14-14-59"}])
|
54
54
|
$stdout.should_receive(:puts).with(green "Available deploy logs")
|
55
55
|
$stdout.should_receive(:puts).with(" * 2011-12-12-14-14-59")
|
56
56
|
@deploys.list("foo-staging")
|
@@ -59,15 +59,88 @@ describe Shelly::CLI::Deploys do
|
|
59
59
|
|
60
60
|
context "single cloud" do
|
61
61
|
it "should display available logs" do
|
62
|
-
@client.should_receive(:
|
62
|
+
@client.should_receive(:deploy_logs).with("foo-staging").and_return([{"failed" => false, "created_at" => "2011-12-12-14-14-59"}, {"failed" => true, "created_at" => "2011-12-12-15-14-59"}])
|
63
63
|
$stdout.should_receive(:puts).with(green "Available deploy logs")
|
64
64
|
$stdout.should_receive(:puts).with(" * 2011-12-12-14-14-59")
|
65
65
|
$stdout.should_receive(:puts).with(" * 2011-12-12-15-14-59 (failed)")
|
66
66
|
@deploys.list
|
67
67
|
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#show" do
|
72
|
+
before do
|
73
|
+
FileUtils.mkdir_p("/projects/foo")
|
74
|
+
Dir.chdir("/projects/foo")
|
75
|
+
File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\n") }
|
76
|
+
@client.stub(:token).and_return("abc")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should exit with message if there is no Cloudfile" do
|
80
|
+
File.delete("Cloudfile")
|
81
|
+
$stdout.should_receive(:puts).with("\e[31mNo Cloudfile found\e[0m")
|
82
|
+
lambda {
|
83
|
+
@deploys.show
|
84
|
+
}.should raise_error(SystemExit)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should exit if user doesn't have access to cloud in Cloudfile" do
|
88
|
+
response = {"message" => "Cloud foo-staging not found"}
|
89
|
+
exception = Shelly::Client::APIError.new(response.to_json)
|
90
|
+
@client.stub(:deploy_log).and_raise(exception)
|
91
|
+
$stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
|
92
|
+
lambda { @deploys.show("last") }.should raise_error(SystemExit)
|
93
|
+
end
|
94
|
+
|
95
|
+
context "multiple clouds" do
|
96
|
+
before do
|
97
|
+
File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\nfoo-production:\n") }
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should show information to select specific cloud and exit" do
|
101
|
+
$stdout.should_receive(:puts).with("You have multiple clouds in Cloudfile. Select log and cloud to view deploy logs using:")
|
102
|
+
$stdout.should_receive(:puts).with(" shelly deploys show last foo-production")
|
103
|
+
$stdout.should_receive(:puts).with("Available clouds:")
|
104
|
+
$stdout.should_receive(:puts).with(" * foo-production")
|
105
|
+
$stdout.should_receive(:puts).with(" * foo-staging")
|
106
|
+
lambda { @deploys.show("last") }.should raise_error(SystemExit)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should render the logs" do
|
110
|
+
@client.should_receive(:deploy_log).with("foo-staging", "last").and_return(response)
|
111
|
+
expected_output
|
112
|
+
@deploys.show("last", "foo-staging")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "single cloud" do
|
117
|
+
it "should render logs without passing cloud" do
|
118
|
+
@client.should_receive(:deploy_log).with("foo-staging", "last").and_return(response)
|
119
|
+
expected_output
|
120
|
+
@deploys.show("last")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def expected_output
|
125
|
+
$stdout.should_receive(:puts).with(green "Log for deploy done on 2011-12-12 at 14:14:59")
|
126
|
+
$stdout.should_receive(:puts).with(green "Starting bundle install")
|
127
|
+
$stdout.should_receive(:puts).with("Installing gems")
|
128
|
+
$stdout.should_receive(:puts).with(green "Starting whenever")
|
129
|
+
$stdout.should_receive(:puts).with("Looking up schedule.rb")
|
130
|
+
$stdout.should_receive(:puts).with(green "Starting callbacks")
|
131
|
+
$stdout.should_receive(:puts).with("rake db:migrate")
|
132
|
+
$stdout.should_receive(:puts).with(green "Starting delayed job")
|
133
|
+
$stdout.should_receive(:puts).with("delayed jobs")
|
134
|
+
$stdout.should_receive(:puts).with(green "Starting thin")
|
135
|
+
$stdout.should_receive(:puts).with("thins up and running")
|
136
|
+
end
|
68
137
|
|
138
|
+
def response
|
139
|
+
{"created_at" => "2011-12-12 at 14:14:59", "bundle_install" => "Installing gems",
|
140
|
+
"whenever" => "Looking up schedule.rb", "thin_restart" => "thins up and running",
|
141
|
+
"delayed_job" => "delayed jobs", "callbacks" => "rake db:migrate"}
|
69
142
|
end
|
70
143
|
|
71
144
|
end
|
72
145
|
|
73
|
-
end
|
146
|
+
end
|
@@ -24,12 +24,14 @@ describe Shelly::CLI::Main do
|
|
24
24
|
expected = <<-OUT
|
25
25
|
Tasks:
|
26
26
|
shelly add # Adds new cloud to Shelly Cloud
|
27
|
+
shelly config <command> # Manages cloud configuration files
|
27
28
|
shelly delete CODE-NAME # Delete cloud from Shelly Cloud
|
28
29
|
shelly deploys <command> # View cloud deploy logs
|
29
30
|
shelly help [TASK] # Describe available tasks or one specific task
|
30
31
|
shelly ip # Lists clouds IP's
|
31
32
|
shelly list # Lists all your clouds
|
32
33
|
shelly login [EMAIL] # Logs user in to Shelly Cloud
|
34
|
+
shelly logs # Show latest application logs from each instance
|
33
35
|
shelly register [EMAIL] # Registers new user account on Shelly Cloud
|
34
36
|
shelly start [CODE-NAME] # Starts specific cloud
|
35
37
|
shelly stop [CODE-NAME] # Stops specific cloud
|
@@ -42,6 +44,21 @@ OUT
|
|
42
44
|
out = IO.popen("bin/shelly").read.strip
|
43
45
|
out.should == expected.strip
|
44
46
|
end
|
47
|
+
|
48
|
+
it "should display options in help for logs" do
|
49
|
+
expected = <<-OUT
|
50
|
+
Usage:
|
51
|
+
shelly logs
|
52
|
+
|
53
|
+
Options:
|
54
|
+
-c, [--cloud=CLOUD] # Specify which cloud to show logs for
|
55
|
+
[--debug] # Show debug information
|
56
|
+
|
57
|
+
Show latest application logs from each instance
|
58
|
+
OUT
|
59
|
+
out = IO.popen("bin/shelly help logs").read.strip
|
60
|
+
out.should == expected.strip
|
61
|
+
end
|
45
62
|
end
|
46
63
|
|
47
64
|
describe "#register" do
|
@@ -775,4 +792,95 @@ OUT
|
|
775
792
|
end
|
776
793
|
end
|
777
794
|
end
|
795
|
+
|
796
|
+
describe "#logs" do
|
797
|
+
before do
|
798
|
+
@user = Shelly::User.new
|
799
|
+
@client.stub(:token).and_return("abc")
|
800
|
+
FileUtils.mkdir_p("/projects/foo")
|
801
|
+
Dir.chdir("/projects/foo")
|
802
|
+
File.open("Cloudfile", 'w') {|f| f.write("foo-production:\n") }
|
803
|
+
Shelly::User.stub(:new).and_return(@user)
|
804
|
+
@client.stub(:apps).and_return([{"code_name" => "foo-production"},
|
805
|
+
{"code_name" => "foo-staging"}])
|
806
|
+
@app = Shelly::App.new
|
807
|
+
Shelly::App.stub(:new).and_return(@app)
|
808
|
+
end
|
809
|
+
|
810
|
+
it "should exit if there is no Cloudfile" do
|
811
|
+
File.delete("Cloudfile")
|
812
|
+
$stdout.should_receive(:puts).with("\e[31mNo Cloudfile found\e[0m")
|
813
|
+
lambda {
|
814
|
+
@main.logs
|
815
|
+
}.should raise_error(SystemExit)
|
816
|
+
end
|
817
|
+
|
818
|
+
it "should exit if user doesn't have access to clouds in Cloudfile" do
|
819
|
+
response = {"message" => "Cloud foo-production not found"}
|
820
|
+
exception = Shelly::Client::APIError.new(response.to_json)
|
821
|
+
@client.stub(:application_logs).and_raise(exception)
|
822
|
+
$stdout.should_receive(:puts).
|
823
|
+
with(red "You have no access to cloud 'foo-production'")
|
824
|
+
lambda { @main.logs }.should raise_error(SystemExit)
|
825
|
+
end
|
826
|
+
|
827
|
+
it "should exit if user is not logged in" do
|
828
|
+
response = {"message" => "Unauthorized"}
|
829
|
+
exception = Shelly::Client::APIError.new(response.to_json)
|
830
|
+
@client.stub(:token).and_raise(exception)
|
831
|
+
$stdout.should_receive(:puts).
|
832
|
+
with(red "You are not logged in. To log in use:")
|
833
|
+
$stdout.should_receive(:puts).with(" shelly login")
|
834
|
+
lambda { @main.logs }.should raise_error(SystemExit)
|
835
|
+
end
|
836
|
+
|
837
|
+
context "single cloud in Cloudfile" do
|
838
|
+
it "should show logs for the cloud" do
|
839
|
+
@client.stub(:application_logs).and_return(["log1"])
|
840
|
+
$stdout.should_receive(:puts).with(green "Cloud foo-production:")
|
841
|
+
$stdout.should_receive(:puts).with("Instance 1:")
|
842
|
+
$stdout.should_receive(:puts).with(" log1")
|
843
|
+
@main.logs
|
844
|
+
end
|
845
|
+
end
|
846
|
+
|
847
|
+
context "multiple clouds in Cloudfile" do
|
848
|
+
before do
|
849
|
+
File.open("Cloudfile", 'w') {|f|
|
850
|
+
f.write("foo-staging:\nfoo-production:\n") }
|
851
|
+
end
|
852
|
+
|
853
|
+
it "should show information to print logs for specific cloud and exit" do
|
854
|
+
$stdout.should_receive(:puts).
|
855
|
+
with("You have multiple clouds in Cloudfile. Select which to show logs for using:")
|
856
|
+
$stdout.should_receive(:puts).with(" shelly logs --cloud foo-production")
|
857
|
+
$stdout.should_receive(:puts).with("Available clouds:")
|
858
|
+
$stdout.should_receive(:puts).with(" * foo-production")
|
859
|
+
$stdout.should_receive(:puts).with(" * foo-staging")
|
860
|
+
lambda { @main.logs }.should raise_error(SystemExit)
|
861
|
+
end
|
862
|
+
|
863
|
+
it "should fetch from command line which cloud to start" do
|
864
|
+
@client.should_receive(:application_logs).with("foo-staging").
|
865
|
+
and_return(["log1"])
|
866
|
+
$stdout.should_receive(:puts).with(green "Cloud foo-staging:")
|
867
|
+
$stdout.should_receive(:puts).with("Instance 1:")
|
868
|
+
$stdout.should_receive(:puts).with(" log1")
|
869
|
+
@main.options = {:cloud => "foo-staging"}
|
870
|
+
@main.logs
|
871
|
+
end
|
872
|
+
end
|
873
|
+
|
874
|
+
context "multiple instances" do
|
875
|
+
it "should show logs from each instance" do
|
876
|
+
@client.stub(:application_logs).and_return(["log1", "log2"])
|
877
|
+
$stdout.should_receive(:puts).with(green "Cloud foo-production:")
|
878
|
+
$stdout.should_receive(:puts).with("Instance 1:")
|
879
|
+
$stdout.should_receive(:puts).with(" log1")
|
880
|
+
$stdout.should_receive(:puts).with("Instance 2:")
|
881
|
+
$stdout.should_receive(:puts).with(" log2")
|
882
|
+
@main.logs
|
883
|
+
end
|
884
|
+
end
|
885
|
+
end
|
778
886
|
end
|
data/spec/shelly/client_spec.rb
CHANGED
@@ -100,17 +100,43 @@ describe Shelly::Client do
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
-
describe "#
|
103
|
+
describe "#deploy_logs" do
|
104
104
|
it "should send get request" do
|
105
105
|
time = Time.now
|
106
106
|
FakeWeb.register_uri(:get, @url + "/apps/staging-foo/deploys", :body => [{:failed => false, :created_at => time},
|
107
107
|
{:failed => true, :created_at => time+1}].to_json)
|
108
|
-
response = @client.
|
108
|
+
response = @client.deploy_logs("staging-foo")
|
109
109
|
response.should == [{"failed"=>false, "created_at"=>time.to_s},
|
110
110
|
{"failed"=>true, "created_at"=>(time+1).to_s}]
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
114
|
+
describe "#deploy_log" do
|
115
|
+
it "should send get request with cloud and log" do
|
116
|
+
FakeWeb.register_uri(:get, @url + "/apps/staging-foo/deploys/2011-11-29-11-50-16", :body => {:content => "Log"}.to_json)
|
117
|
+
response = @client.deploy_log("staging-foo", "2011-11-29-11-50-16")
|
118
|
+
response.should == {"content" => "Log"}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "#app_configs" do
|
123
|
+
it "should send get request" do
|
124
|
+
FakeWeb.register_uri(:get, @url + "/apps/staging-foo/configs", :body => [{:created_by_user => true, :path => "config/app.yml"}].to_json)
|
125
|
+
response = @client.app_configs("staging-foo")
|
126
|
+
response.should == [{"created_by_user" => true, "path" => "config/app.yml"}]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "#application_logs" do
|
131
|
+
it "should send get request" do
|
132
|
+
time = Time.now
|
133
|
+
FakeWeb.register_uri(:get, @url + "/apps/staging-foo/logs",
|
134
|
+
:body => {:logs => ["application_log_1", "application_log_2"]}.to_json)
|
135
|
+
response = @client.application_logs("staging-foo")
|
136
|
+
response.should == {"logs" => ["application_log_1", "application_log_2"]}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
114
140
|
describe "#create_app" do
|
115
141
|
it "should send post with app's attributes" do
|
116
142
|
@client.should_receive(:post).with("/apps", :app => {:code_name => "foo", :ruby_version => "1.9.2"})
|
metadata
CHANGED
@@ -1,156 +1,202 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: shelly
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 33
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 31
|
10
|
+
version: 0.0.31
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Shelly Cloud team
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
|
18
|
+
date: 2011-12-19 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
15
21
|
name: rspec
|
16
|
-
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
17
24
|
none: false
|
18
|
-
requirements:
|
19
|
-
- -
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
22
32
|
type: :development
|
23
|
-
|
24
|
-
|
25
|
-
- !ruby/object:Gem::Dependency
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
26
35
|
name: rake
|
27
|
-
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
28
38
|
none: false
|
29
|
-
requirements:
|
30
|
-
- -
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
33
46
|
type: :development
|
34
|
-
|
35
|
-
|
36
|
-
- !ruby/object:Gem::Dependency
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
37
49
|
name: guard
|
38
|
-
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
39
52
|
none: false
|
40
|
-
requirements:
|
41
|
-
- -
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
44
60
|
type: :development
|
45
|
-
|
46
|
-
|
47
|
-
- !ruby/object:Gem::Dependency
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
48
63
|
name: guard-rspec
|
49
|
-
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
50
66
|
none: false
|
51
|
-
requirements:
|
52
|
-
- -
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
55
74
|
type: :development
|
56
|
-
|
57
|
-
|
58
|
-
- !ruby/object:Gem::Dependency
|
75
|
+
version_requirements: *id004
|
76
|
+
- !ruby/object:Gem::Dependency
|
59
77
|
name: growl_notify
|
60
|
-
|
78
|
+
prerelease: false
|
79
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
61
80
|
none: false
|
62
|
-
requirements:
|
63
|
-
- -
|
64
|
-
- !ruby/object:Gem::Version
|
65
|
-
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
hash: 3
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
version: "0"
|
66
88
|
type: :development
|
67
|
-
|
68
|
-
|
69
|
-
- !ruby/object:Gem::Dependency
|
89
|
+
version_requirements: *id005
|
90
|
+
- !ruby/object:Gem::Dependency
|
70
91
|
name: rb-fsevent
|
71
|
-
|
92
|
+
prerelease: false
|
93
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
72
94
|
none: false
|
73
|
-
requirements:
|
74
|
-
- -
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
hash: 3
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
version: "0"
|
77
102
|
type: :development
|
78
|
-
|
79
|
-
|
80
|
-
- !ruby/object:Gem::Dependency
|
103
|
+
version_requirements: *id006
|
104
|
+
- !ruby/object:Gem::Dependency
|
81
105
|
name: fakefs
|
82
|
-
|
106
|
+
prerelease: false
|
107
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
83
108
|
none: false
|
84
|
-
requirements:
|
85
|
-
- -
|
86
|
-
- !ruby/object:Gem::Version
|
87
|
-
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
hash: 3
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
88
116
|
type: :development
|
89
|
-
|
90
|
-
|
91
|
-
- !ruby/object:Gem::Dependency
|
117
|
+
version_requirements: *id007
|
118
|
+
- !ruby/object:Gem::Dependency
|
92
119
|
name: fakeweb
|
93
|
-
|
120
|
+
prerelease: false
|
121
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
94
122
|
none: false
|
95
|
-
requirements:
|
96
|
-
- -
|
97
|
-
- !ruby/object:Gem::Version
|
98
|
-
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
hash: 3
|
127
|
+
segments:
|
128
|
+
- 0
|
129
|
+
version: "0"
|
99
130
|
type: :development
|
100
|
-
|
101
|
-
|
102
|
-
- !ruby/object:Gem::Dependency
|
131
|
+
version_requirements: *id008
|
132
|
+
- !ruby/object:Gem::Dependency
|
103
133
|
name: wijet-thor
|
104
|
-
|
134
|
+
prerelease: false
|
135
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
105
136
|
none: false
|
106
|
-
requirements:
|
137
|
+
requirements:
|
107
138
|
- - ~>
|
108
|
-
- !ruby/object:Gem::Version
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
hash: 41
|
141
|
+
segments:
|
142
|
+
- 0
|
143
|
+
- 14
|
144
|
+
- 7
|
109
145
|
version: 0.14.7
|
110
146
|
type: :runtime
|
111
|
-
|
112
|
-
|
113
|
-
- !ruby/object:Gem::Dependency
|
147
|
+
version_requirements: *id009
|
148
|
+
- !ruby/object:Gem::Dependency
|
114
149
|
name: rest-client
|
115
|
-
|
150
|
+
prerelease: false
|
151
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
116
152
|
none: false
|
117
|
-
requirements:
|
118
|
-
- -
|
119
|
-
- !ruby/object:Gem::Version
|
120
|
-
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
hash: 3
|
157
|
+
segments:
|
158
|
+
- 0
|
159
|
+
version: "0"
|
121
160
|
type: :runtime
|
122
|
-
|
123
|
-
|
124
|
-
- !ruby/object:Gem::Dependency
|
161
|
+
version_requirements: *id010
|
162
|
+
- !ruby/object:Gem::Dependency
|
125
163
|
name: json
|
126
|
-
|
164
|
+
prerelease: false
|
165
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
127
166
|
none: false
|
128
|
-
requirements:
|
129
|
-
- -
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
|
167
|
+
requirements:
|
168
|
+
- - ">="
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
hash: 3
|
171
|
+
segments:
|
172
|
+
- 0
|
173
|
+
version: "0"
|
132
174
|
type: :runtime
|
133
|
-
|
134
|
-
|
135
|
-
- !ruby/object:Gem::Dependency
|
175
|
+
version_requirements: *id011
|
176
|
+
- !ruby/object:Gem::Dependency
|
136
177
|
name: wijet-launchy
|
137
|
-
|
178
|
+
prerelease: false
|
179
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
138
180
|
none: false
|
139
|
-
requirements:
|
140
|
-
- -
|
141
|
-
- !ruby/object:Gem::Version
|
142
|
-
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
hash: 3
|
185
|
+
segments:
|
186
|
+
- 0
|
187
|
+
version: "0"
|
143
188
|
type: :runtime
|
144
|
-
|
145
|
-
version_requirements: *70153945365700
|
189
|
+
version_requirements: *id012
|
146
190
|
description: Tool for managing applications and clouds at shellycloud.com
|
147
|
-
email:
|
191
|
+
email:
|
148
192
|
- support@shellycloud.com
|
149
|
-
executables:
|
193
|
+
executables:
|
150
194
|
- shelly
|
151
195
|
extensions: []
|
196
|
+
|
152
197
|
extra_rdoc_files: []
|
153
|
-
|
198
|
+
|
199
|
+
files:
|
154
200
|
- .gitignore
|
155
201
|
- .travis.yml
|
156
202
|
- Gemfile
|
@@ -163,6 +209,7 @@ files:
|
|
163
209
|
- lib/shelly.rb
|
164
210
|
- lib/shelly/app.rb
|
165
211
|
- lib/shelly/cli/command.rb
|
212
|
+
- lib/shelly/cli/config.rb
|
166
213
|
- lib/shelly/cli/deploys.rb
|
167
214
|
- lib/shelly/cli/main.rb
|
168
215
|
- lib/shelly/cli/runner.rb
|
@@ -181,6 +228,7 @@ files:
|
|
181
228
|
- spec/helpers.rb
|
182
229
|
- spec/input_faker.rb
|
183
230
|
- spec/shelly/app_spec.rb
|
231
|
+
- spec/shelly/cli/config_spec.rb
|
184
232
|
- spec/shelly/cli/deploys_spec.rb
|
185
233
|
- spec/shelly/cli/main_spec.rb
|
186
234
|
- spec/shelly/cli/runner_spec.rb
|
@@ -193,32 +241,42 @@ files:
|
|
193
241
|
- spec/thor/options_spec.rb
|
194
242
|
homepage: http://shellycloud.com
|
195
243
|
licenses: []
|
244
|
+
|
196
245
|
post_install_message:
|
197
246
|
rdoc_options: []
|
198
|
-
|
247
|
+
|
248
|
+
require_paths:
|
199
249
|
- lib
|
200
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
250
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
201
251
|
none: false
|
202
|
-
requirements:
|
203
|
-
- -
|
204
|
-
- !ruby/object:Gem::Version
|
205
|
-
|
206
|
-
|
252
|
+
requirements:
|
253
|
+
- - ">="
|
254
|
+
- !ruby/object:Gem::Version
|
255
|
+
hash: 3
|
256
|
+
segments:
|
257
|
+
- 0
|
258
|
+
version: "0"
|
259
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
207
260
|
none: false
|
208
|
-
requirements:
|
209
|
-
- -
|
210
|
-
- !ruby/object:Gem::Version
|
211
|
-
|
261
|
+
requirements:
|
262
|
+
- - ">="
|
263
|
+
- !ruby/object:Gem::Version
|
264
|
+
hash: 3
|
265
|
+
segments:
|
266
|
+
- 0
|
267
|
+
version: "0"
|
212
268
|
requirements: []
|
269
|
+
|
213
270
|
rubyforge_project: shelly
|
214
271
|
rubygems_version: 1.8.10
|
215
272
|
signing_key:
|
216
273
|
specification_version: 3
|
217
274
|
summary: Shelly Cloud command line tool
|
218
|
-
test_files:
|
275
|
+
test_files:
|
219
276
|
- spec/helpers.rb
|
220
277
|
- spec/input_faker.rb
|
221
278
|
- spec/shelly/app_spec.rb
|
279
|
+
- spec/shelly/cli/config_spec.rb
|
222
280
|
- spec/shelly/cli/deploys_spec.rb
|
223
281
|
- spec/shelly/cli/main_spec.rb
|
224
282
|
- spec/shelly/cli/runner_spec.rb
|