shelly 0.0.33 → 0.0.34
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/shelly/app.rb +12 -4
- data/lib/shelly/backup.rb +16 -0
- data/lib/shelly/cli/backup.rb +29 -5
- data/lib/shelly/client.rb +34 -4
- data/lib/shelly/download_progress_bar.rb +14 -0
- data/lib/shelly/version.rb +1 -1
- data/shelly.gemspec +1 -0
- data/spec/shelly/app_spec.rb +24 -0
- data/spec/shelly/backup_spec.rb +34 -0
- data/spec/shelly/cli/backup_spec.rb +67 -9
- data/spec/shelly/cli/config_spec.rb +1 -1
- data/spec/shelly/cli/deploys_spec.rb +2 -2
- data/spec/shelly/cli/main_spec.rb +12 -12
- data/spec/shelly/cli/user_spec.rb +2 -2
- data/spec/shelly/client_spec.rb +71 -14
- data/spec/shelly/download_progress_bar_spec.rb +28 -0
- metadata +24 -4
data/lib/shelly/app.rb
CHANGED
@@ -8,6 +8,8 @@ module Shelly
|
|
8
8
|
attr_accessor :code_name, :databases, :ruby_version, :environment,
|
9
9
|
:git_url, :domains
|
10
10
|
|
11
|
+
autoload :Backup, "shelly/backup"
|
12
|
+
|
11
13
|
def initialize(code_name = nil)
|
12
14
|
self.code_name = code_name
|
13
15
|
end
|
@@ -59,15 +61,22 @@ module Shelly
|
|
59
61
|
end
|
60
62
|
|
61
63
|
def application_logs
|
62
|
-
shelly.application_logs(
|
64
|
+
shelly.application_logs(code_name)
|
63
65
|
end
|
64
66
|
|
65
67
|
def database_backups
|
66
|
-
shelly.database_backups(code_name)
|
68
|
+
shelly.database_backups(code_name).map do |attributes|
|
69
|
+
Shelly::Backup.new(attributes.merge("code_name" => code_name))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def database_backup(handler)
|
74
|
+
attributes = shelly.database_backup(code_name, handler)
|
75
|
+
Shelly::Backup.new(attributes.merge("code_name" => code_name))
|
67
76
|
end
|
68
77
|
|
69
78
|
def logs
|
70
|
-
shelly.cloud_logs(
|
79
|
+
shelly.cloud_logs(code_name)
|
71
80
|
end
|
72
81
|
|
73
82
|
def start
|
@@ -132,4 +141,3 @@ module Shelly
|
|
132
141
|
end
|
133
142
|
end
|
134
143
|
end
|
135
|
-
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Shelly
|
2
|
+
class Backup < Model
|
3
|
+
attr_reader :filename, :size, :human_size, :code_name
|
4
|
+
|
5
|
+
def initialize(attributes = {})
|
6
|
+
@filename = attributes["filename"]
|
7
|
+
@size = attributes["size"]
|
8
|
+
@human_size = attributes["human_size"]
|
9
|
+
@code_name = attributes["code_name"]
|
10
|
+
end
|
11
|
+
|
12
|
+
def download(callback)
|
13
|
+
shelly.download_backup(code_name, filename, callback)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/shelly/cli/backup.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require "shelly/cli/command"
|
2
|
+
require "shelly/backup"
|
3
|
+
require "shelly/download_progress_bar"
|
2
4
|
|
3
5
|
module Shelly
|
4
6
|
module CLI
|
@@ -15,13 +17,15 @@ module Shelly
|
|
15
17
|
say_error "No Cloudfile found" unless Cloudfile.present?
|
16
18
|
multiple_clouds(options[:cloud], "backup list", "Select cloud to view database backups for using:")
|
17
19
|
backups = @app.database_backups
|
18
|
-
|
19
|
-
|
20
|
+
if backups.present?
|
21
|
+
to_display = [["Filename", "| Size"]]
|
22
|
+
backups.each do |backup|
|
23
|
+
to_display << [backup.filename, "| #{backup.human_size}"]
|
24
|
+
end
|
25
|
+
|
20
26
|
say "Available backups:", :green
|
21
27
|
say_new_line
|
22
|
-
print_table(
|
23
|
-
[backup['filename'], "| #{backup['size']}"]
|
24
|
-
end, :ident => 2)
|
28
|
+
print_table(to_display, :ident => 2)
|
25
29
|
else
|
26
30
|
say "No database backups available"
|
27
31
|
end
|
@@ -32,6 +36,26 @@ module Shelly
|
|
32
36
|
say_error e.message
|
33
37
|
end
|
34
38
|
end
|
39
|
+
|
40
|
+
method_option :cloud, :type => :string, :aliases => "-c", :desc => "Specify which cloud list backups for"
|
41
|
+
desc "get [FILENAME]", "Downloads specified or last backup to current directory"
|
42
|
+
def get(handler = "last")
|
43
|
+
multiple_clouds(options[:cloud], "backup get [FILENAME]", "Select cloud for which you want download backup")
|
44
|
+
|
45
|
+
backup = @app.database_backup(handler)
|
46
|
+
bar = Shelly::DownloadProgressBar.new(backup.size)
|
47
|
+
backup.download(bar.progress_callback)
|
48
|
+
|
49
|
+
say_new_line
|
50
|
+
say "Backup file saved to #{backup.filename}", :green
|
51
|
+
rescue Client::APIError => e
|
52
|
+
if e.not_found?
|
53
|
+
say_error "Backup not found", :with_exit => false
|
54
|
+
say "You can list available backups with 'shelly backup list' command"
|
55
|
+
else
|
56
|
+
raise e
|
57
|
+
end
|
58
|
+
end
|
35
59
|
end
|
36
60
|
end
|
37
61
|
end
|
data/lib/shelly/client.rb
CHANGED
@@ -4,8 +4,11 @@ require "json"
|
|
4
4
|
module Shelly
|
5
5
|
class Client
|
6
6
|
class APIError < Exception
|
7
|
-
|
7
|
+
attr_reader :status_code
|
8
|
+
|
9
|
+
def initialize(response_body, status_code)
|
8
10
|
@response = JSON.parse(response_body)
|
11
|
+
@status_code = status_code
|
9
12
|
end
|
10
13
|
|
11
14
|
def message
|
@@ -23,10 +26,14 @@ module Shelly
|
|
23
26
|
def validation?
|
24
27
|
message == "Validation Failed"
|
25
28
|
end
|
29
|
+
|
30
|
+
def not_found?
|
31
|
+
status_code == 404
|
32
|
+
end
|
26
33
|
|
27
34
|
def unauthorized?
|
28
|
-
# FIXME: Return unauthorized if user has no access to cloud
|
29
|
-
# Return 404 if
|
35
|
+
# FIXME: Return unauthorized if user has no access to cloud, checked by 401 status code
|
36
|
+
# Return 404 if child of app doesn't exist, should be fixed in API
|
30
37
|
message == "Unauthorized" || message =~ /Cloud .+ not found/
|
31
38
|
end
|
32
39
|
|
@@ -37,6 +44,8 @@ module Shelly
|
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
47
|
+
attr_reader :email, :password
|
48
|
+
|
40
49
|
def initialize(email = nil, password = nil)
|
41
50
|
@email = email
|
42
51
|
@password = password
|
@@ -121,6 +130,10 @@ module Shelly
|
|
121
130
|
def database_backups(code_name)
|
122
131
|
get("/apps/#{code_name}/database_backups")
|
123
132
|
end
|
133
|
+
|
134
|
+
def database_backup(code_name, handler)
|
135
|
+
get("/apps/#{code_name}/database_backups/#{handler}")
|
136
|
+
end
|
124
137
|
|
125
138
|
def ssh_key_available?(ssh_key)
|
126
139
|
get("/users/new", :ssh_key => ssh_key)
|
@@ -149,6 +162,23 @@ module Shelly
|
|
149
162
|
def delete(path, params = {})
|
150
163
|
request(path, :delete, params)
|
151
164
|
end
|
165
|
+
|
166
|
+
def download_backup(cloud, filename, progress_callback = nil)
|
167
|
+
File.open(filename, "w") do |out|
|
168
|
+
process_response = lambda do |response|
|
169
|
+
response.read_body do |chunk|
|
170
|
+
out.write(chunk)
|
171
|
+
progress_callback.call(chunk.size) if progress_callback
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
options = request_parameters("/apps/#{cloud}/database_backups/#{filename}", :get)
|
176
|
+
options = options.merge(:block_response => process_response,
|
177
|
+
:headers => {:accept => "application/x-gzip"})
|
178
|
+
|
179
|
+
RestClient::Request.execute(options)
|
180
|
+
end
|
181
|
+
end
|
152
182
|
|
153
183
|
def request(path, method, params = {})
|
154
184
|
options = request_parameters(path, method, params)
|
@@ -177,7 +207,7 @@ module Shelly
|
|
177
207
|
|
178
208
|
def process_response(response)
|
179
209
|
if [401, 404, 422, 500].include?(response.code)
|
180
|
-
raise APIError.new(response.body)
|
210
|
+
raise APIError.new(response.body, response.code)
|
181
211
|
end
|
182
212
|
|
183
213
|
response.return!
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "progressbar"
|
2
|
+
|
3
|
+
module Shelly
|
4
|
+
class DownloadProgressBar < ProgressBar
|
5
|
+
def initialize(total)
|
6
|
+
super("Progress", total)
|
7
|
+
self.format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
|
8
|
+
end
|
9
|
+
|
10
|
+
def progress_callback
|
11
|
+
lambda { |size| inc(size) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/shelly/version.rb
CHANGED
data/shelly.gemspec
CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
|
|
26
26
|
s.add_runtime_dependency "rest-client"
|
27
27
|
s.add_runtime_dependency "json"
|
28
28
|
s.add_runtime_dependency "wijet-launchy"
|
29
|
+
s.add_runtime_dependency "progressbar"
|
29
30
|
|
30
31
|
s.files = `git ls-files`.split("\n")
|
31
32
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/spec/shelly/app_spec.rb
CHANGED
@@ -167,6 +167,30 @@ config
|
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
170
|
+
describe "#database_backup" do
|
171
|
+
before do
|
172
|
+
@description = {
|
173
|
+
"filename" => "backup.tar.gz",
|
174
|
+
"size" => 1234,
|
175
|
+
"human_size" => "2KB"
|
176
|
+
}
|
177
|
+
@client.stub(:database_backup).and_return(@description)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should fetch backup from API" do
|
181
|
+
@client.should_receive(:database_backup).with("foo-staging", "backup.tar.gz")
|
182
|
+
@app.database_backup("backup.tar.gz")
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should initialize backup object" do
|
186
|
+
backup = @app.database_backup("backup.tar.gz")
|
187
|
+
backup.code_name.should == "foo-staging"
|
188
|
+
backup.size.should == 1234
|
189
|
+
backup.human_size.should == "2KB"
|
190
|
+
backup.filename.should == "backup.tar.gz"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
170
194
|
describe "#create" do
|
171
195
|
context "without providing domain" do
|
172
196
|
it "should create the app on shelly cloud via API client" do
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "shelly/backup"
|
3
|
+
|
4
|
+
describe Shelly::Backup do
|
5
|
+
before do
|
6
|
+
@client = mock
|
7
|
+
Shelly::Client.stub(:new).and_return(@client)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should assign attributes" do
|
11
|
+
backup = Shelly::Backup.new(attributes)
|
12
|
+
|
13
|
+
backup.code_name.should == "foo"
|
14
|
+
backup.filename.should == "backup.tar.gz"
|
15
|
+
backup.human_size.should == "2KB"
|
16
|
+
backup.size.should == 2048
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#download" do
|
20
|
+
it "should download given backup via API file with filename to which backup will be downloaded" do
|
21
|
+
callback = lambda {}
|
22
|
+
@client.should_receive(:download_backup).with("foo", "backup.tar.gz", callback)
|
23
|
+
backup = Shelly::Backup.new(attributes)
|
24
|
+
backup.download(callback)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def attributes
|
29
|
+
{"code_name" => "foo",
|
30
|
+
"filename" => "backup.tar.gz",
|
31
|
+
"human_size" => "2KB",
|
32
|
+
"size" => 2048}
|
33
|
+
end
|
34
|
+
end
|
@@ -1,21 +1,19 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "shelly/cli/backup"
|
3
|
+
require "shelly/download_progress_bar"
|
3
4
|
|
4
5
|
describe Shelly::CLI::Backup do
|
5
6
|
before do
|
6
7
|
@backup = Shelly::CLI::Backup.new
|
7
8
|
@client = mock
|
9
|
+
@client.stub(:token).and_return("abc")
|
8
10
|
Shelly::Client.stub(:new).and_return(@client)
|
11
|
+
FileUtils.mkdir_p("/projects/foo")
|
12
|
+
Dir.chdir("/projects/foo")
|
13
|
+
File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\n") }
|
9
14
|
end
|
10
15
|
|
11
16
|
describe "#list" do
|
12
|
-
before do
|
13
|
-
FileUtils.mkdir_p("/projects/foo")
|
14
|
-
Dir.chdir("/projects/foo")
|
15
|
-
File.open("Cloudfile", 'w') {|f| f.write("foo-staging:\n") }
|
16
|
-
@client.stub(:token).and_return("abc")
|
17
|
-
end
|
18
|
-
|
19
17
|
it "should exit with message if there is no Cloudfile" do
|
20
18
|
File.delete("Cloudfile")
|
21
19
|
$stdout.should_receive(:puts).with("\e[31mNo Cloudfile found\e[0m")
|
@@ -26,7 +24,7 @@ describe Shelly::CLI::Backup do
|
|
26
24
|
|
27
25
|
it "should exit if user doesn't have access to cloud in Cloudfile" do
|
28
26
|
response = {"message" => "Cloud foo-staging not found"}
|
29
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
27
|
+
exception = Shelly::Client::APIError.new(response.to_json, 401)
|
30
28
|
@client.stub(:database_backups).and_raise(exception)
|
31
29
|
$stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
|
32
30
|
lambda { @backup.list }.should raise_error(SystemExit)
|
@@ -47,7 +45,7 @@ describe Shelly::CLI::Backup do
|
|
47
45
|
end
|
48
46
|
|
49
47
|
it "should take cloud from command line for which to show backups" do
|
50
|
-
@client.should_receive(:database_backups).with("foo-staging").and_return([{"filename" => "backup.postgre.tar.gz", "
|
48
|
+
@client.should_receive(:database_backups).with("foo-staging").and_return([{"filename" => "backup.postgre.tar.gz", "human_size" => "10kb", "size" => 12345},{"filename" => "backup.mongo.tar.gz", "human_size" => "22kb", "size" => 333}])
|
51
49
|
$stdout.should_receive(:puts).with(green "Available backups:")
|
52
50
|
$stdout.should_receive(:puts).with("\n")
|
53
51
|
$stdout.should_receive(:puts).with(" Filename | Size")
|
@@ -57,5 +55,65 @@ describe Shelly::CLI::Backup do
|
|
57
55
|
@backup.list
|
58
56
|
end
|
59
57
|
end
|
58
|
+
|
59
|
+
describe "#get" do
|
60
|
+
before do
|
61
|
+
@client.stub(:download_backup)
|
62
|
+
@bar = mock(:progress_callback => @callback)
|
63
|
+
Shelly::DownloadProgressBar.stub(:new).and_return(@bar)
|
64
|
+
@client.stub(:database_backup).and_return({"filename" => "better.tar.gz", "size" => 12345})
|
65
|
+
$stdout.stub(:puts)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should make sure that cloud is choosen" do
|
69
|
+
@client.should_receive(:database_backup).with("foo-staging", "last")
|
70
|
+
@backup.get
|
71
|
+
|
72
|
+
@backup.options = {:cloud => "other"}
|
73
|
+
@client.should_receive(:database_backup).with("other", "last")
|
74
|
+
@backup.get
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should fetch backup size and initialize download progress bar" do
|
78
|
+
@client.stub(:database_backup).and_return({"filename" => "backup.postgres.tar.gz", "size" => 333})
|
79
|
+
Shelly::DownloadProgressBar.should_receive(:new).with(333).and_return(@bar)
|
80
|
+
|
81
|
+
@backup.get
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should fetch given backup file itself" do
|
85
|
+
@client.should_receive(:download_backup).with("foo-staging", "better.tar.gz", @bar.progress_callback)
|
86
|
+
@backup.get("better.tar.gz")
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should show info where file has been saved" do
|
90
|
+
$stdout.should_receive(:puts)
|
91
|
+
$stdout.should_receive(:puts).with(green "Backup file saved to better.tar.gz")
|
92
|
+
@client.should_receive(:download_backup).with("foo-staging", "better.tar.gz", @bar.progress_callback)
|
93
|
+
@backup.get("last")
|
94
|
+
end
|
95
|
+
|
96
|
+
context "on backup not found" do
|
97
|
+
it "it should display error message" do
|
98
|
+
exception = Shelly::Client::APIError.new({}.to_json, 404)
|
99
|
+
@client.stub(:database_backup).and_raise(exception)
|
100
|
+
$stdout.should_receive(:puts).with(red "Backup not found")
|
101
|
+
$stdout.should_receive(:puts).with("You can list available backups with 'shelly backup list' command")
|
102
|
+
@backup.get("better.tar.gz")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "on unsupported exception" do
|
107
|
+
it "should re-raise it" do
|
108
|
+
exception = Shelly::Client::APIError.new({}.to_json, 500)
|
109
|
+
@client.stub(:database_backup).and_raise(exception)
|
110
|
+
$stdout.should_not_receive(:puts).with(red "Backup not found")
|
111
|
+
$stdout.should_not_receive(:puts).with("You can list available backups with 'shelly backup list' command")
|
112
|
+
lambda {
|
113
|
+
@backup.get("better.tar.gz")
|
114
|
+
}.should raise_error(Shelly::Client::APIError)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
60
118
|
end
|
61
119
|
end
|
@@ -34,7 +34,7 @@ describe Shelly::CLI::Config do
|
|
34
34
|
|
35
35
|
it "should exit if user doesn't have access to cloud in Cloudfile" do
|
36
36
|
response = {"message" => "Cloud foo-staging not found"}
|
37
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
37
|
+
exception = Shelly::Client::APIError.new(response.to_json, 404)
|
38
38
|
@client.stub(:app_configs).and_raise(exception)
|
39
39
|
$stdout.should_receive(:puts).with(red "You have no access to 'foo-production' cloud defined in Cloudfile")
|
40
40
|
lambda { @config.list }.should raise_error(SystemExit)
|
@@ -29,7 +29,7 @@ describe Shelly::CLI::Deploys do
|
|
29
29
|
|
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
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
32
|
+
exception = Shelly::Client::APIError.new(response.to_json, 404)
|
33
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)
|
@@ -87,7 +87,7 @@ describe Shelly::CLI::Deploys do
|
|
87
87
|
|
88
88
|
it "should exit if user doesn't have access to cloud in Cloudfile" do
|
89
89
|
response = {"message" => "Cloud foo-staging not found"}
|
90
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
90
|
+
exception = Shelly::Client::APIError.new(response.to_json, 404)
|
91
91
|
@client.stub(:deploy_log).and_raise(exception)
|
92
92
|
$stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
|
93
93
|
lambda { @deploys.show("last") }.should raise_error(SystemExit)
|
@@ -172,7 +172,7 @@ OUT
|
|
172
172
|
context "on unsuccessful registration" do
|
173
173
|
it "should display errors and exit with 1" do
|
174
174
|
response = {"message" => "Validation Failed", "errors" => [["email", "has been already taken"]]}
|
175
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
175
|
+
exception = Shelly::Client::APIError.new(response.to_json, 422)
|
176
176
|
@client.stub(:register_user).and_raise(exception)
|
177
177
|
$stdout.should_receive(:puts).with("\e[31mEmail has been already taken\e[0m")
|
178
178
|
lambda {
|
@@ -259,7 +259,7 @@ OUT
|
|
259
259
|
context "on unauthorized user" do
|
260
260
|
it "should exit with 1 and display error message" do
|
261
261
|
response = {"message" => "Unauthorized", "url" => "https://admin.winniecloud.com/users/password/new"}
|
262
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
262
|
+
exception = Shelly::Client::APIError.new(response.to_json, 401)
|
263
263
|
@client.stub(:token).and_raise(exception)
|
264
264
|
$stdout.should_receive(:puts).with("\e[31mWrong email or password\e[0m")
|
265
265
|
$stdout.should_receive(:puts).with("\e[31mYou can reset password by using link:\e[0m")
|
@@ -386,7 +386,7 @@ OUT
|
|
386
386
|
|
387
387
|
it "should display validation errors if they are any" do
|
388
388
|
response = {"message" => "Validation Failed", "errors" => [["code_name", "has been already taken"]]}
|
389
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
389
|
+
exception = Shelly::Client::APIError.new(response.to_json, 422)
|
390
390
|
@app.should_receive(:create).and_raise(exception)
|
391
391
|
$stdout.should_receive(:puts).with("\e[31mCode name has been already taken\e[0m")
|
392
392
|
$stdout.should_receive(:puts).with("\e[31mFix erros in the below command and type it again to create your cloud\e[0m")
|
@@ -475,7 +475,7 @@ OUT
|
|
475
475
|
context "on failure" do
|
476
476
|
it "should display info that user is not logged in" do
|
477
477
|
body = {"message" => "Unauthorized"}
|
478
|
-
error = Shelly::Client::APIError.new(body.to_json)
|
478
|
+
error = Shelly::Client::APIError.new(body.to_json, 401)
|
479
479
|
@client.stub(:token).and_raise(error)
|
480
480
|
$stdout.should_receive(:puts).with("\e[31mYou are not logged in, use `shelly login`\e[0m")
|
481
481
|
lambda {
|
@@ -508,7 +508,7 @@ OUT
|
|
508
508
|
|
509
509
|
it "should exit if user doesn't have access to clouds in Cloudfile" do
|
510
510
|
response = {"message" => "Cloud foo-production not found"}
|
511
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
511
|
+
exception = Shelly::Client::APIError.new(response.to_json, 404)
|
512
512
|
@client.stub(:start_cloud).and_raise(exception)
|
513
513
|
$stdout.should_receive(:puts).with(red "You have no access to 'foo-production' cloud defined in Cloudfile")
|
514
514
|
lambda { @main.start }.should raise_error(SystemExit)
|
@@ -516,7 +516,7 @@ OUT
|
|
516
516
|
|
517
517
|
it "should exit if user is not logged in" do
|
518
518
|
response = {"message" => "Unauthorized"}
|
519
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
519
|
+
exception = Shelly::Client::APIError.new(response.to_json, 401)
|
520
520
|
@client.stub(:token).and_raise(exception)
|
521
521
|
$stdout.should_receive(:puts).with(red "You are not logged in. To log in use:")
|
522
522
|
$stdout.should_receive(:puts).with(" shelly login")
|
@@ -626,7 +626,7 @@ OUT
|
|
626
626
|
|
627
627
|
it "should exit if user doesn't have access to clouds in Cloudfile" do
|
628
628
|
response = {"message" => "Cloud foo-production not found"}
|
629
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
629
|
+
exception = Shelly::Client::APIError.new(response.to_json, 404)
|
630
630
|
@client.stub(:stop_cloud).and_raise(exception)
|
631
631
|
$stdout.should_receive(:puts).with(red "You have no access to 'foo-production' cloud defined in Cloudfile")
|
632
632
|
lambda { @main.stop }.should raise_error(SystemExit)
|
@@ -634,7 +634,7 @@ OUT
|
|
634
634
|
|
635
635
|
it "should exit if user is not logged in" do
|
636
636
|
response = {"message" => "Unauthorized"}
|
637
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
637
|
+
exception = Shelly::Client::APIError.new(response.to_json, 401)
|
638
638
|
@client.stub(:token).and_raise(exception)
|
639
639
|
$stdout.should_receive(:puts).with(red "You are not logged in. To log in use:")
|
640
640
|
$stdout.should_receive(:puts).with(" shelly login")
|
@@ -717,7 +717,7 @@ OUT
|
|
717
717
|
|
718
718
|
it "should raise an error if user does not have access to cloud" do
|
719
719
|
response = {"message" => "Cloud foo-staging not found"}
|
720
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
720
|
+
exception = Shelly::Client::APIError.new(response.to_json, 404)
|
721
721
|
@client.stub(:app_ips).and_raise(exception)
|
722
722
|
$stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
|
723
723
|
@main.ip
|
@@ -794,7 +794,7 @@ OUT
|
|
794
794
|
|
795
795
|
it "should raise Client::APIError" do
|
796
796
|
response = {:message => "Application not found"}
|
797
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
797
|
+
exception = Shelly::Client::APIError.new(response.to_json, 404)
|
798
798
|
@app.stub(:delete).and_raise(exception)
|
799
799
|
$stdout.should_receive(:puts).with("\e[31mApplication not found\e[0m")
|
800
800
|
lambda{
|
@@ -854,7 +854,7 @@ OUT
|
|
854
854
|
|
855
855
|
it "should exit if user doesn't have access to clouds in Cloudfile" do
|
856
856
|
response = {"message" => "Cloud foo-production not found"}
|
857
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
857
|
+
exception = Shelly::Client::APIError.new(response.to_json, 404)
|
858
858
|
@client.stub(:application_logs).and_raise(exception)
|
859
859
|
$stdout.should_receive(:puts).
|
860
860
|
with(red "You have no access to cloud 'foo-production'")
|
@@ -863,7 +863,7 @@ OUT
|
|
863
863
|
|
864
864
|
it "should exit if user is not logged in" do
|
865
865
|
response = {"message" => "Unauthorized"}
|
866
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
866
|
+
exception = Shelly::Client::APIError.new(response.to_json, 401)
|
867
867
|
@client.stub(:token).and_raise(exception)
|
868
868
|
$stdout.should_receive(:puts).
|
869
869
|
with(red "You are not logged in. To log in use:")
|
@@ -69,7 +69,7 @@ describe Shelly::CLI::User do
|
|
69
69
|
context "on failure" do
|
70
70
|
it "should raise an error if user does not have access to cloud" do
|
71
71
|
response = {"message" => "Cloud foo-staging not found"}
|
72
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
72
|
+
exception = Shelly::Client::APIError.new(response.to_json, 404)
|
73
73
|
@client.stub(:app_users).and_raise(exception)
|
74
74
|
$stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
|
75
75
|
@cli_user.list
|
@@ -130,7 +130,7 @@ describe Shelly::CLI::User do
|
|
130
130
|
context "on failure" do
|
131
131
|
it "should raise error if user doesnt have access to cloud" do
|
132
132
|
response = {"message" => "Cloud foo-staging not found"}
|
133
|
-
exception = Shelly::Client::APIError.new(response.to_json)
|
133
|
+
exception = Shelly::Client::APIError.new(response.to_json, 404)
|
134
134
|
@client.stub(:send_invitation).and_raise(exception)
|
135
135
|
$stdout.should_receive(:puts).with(red "You have no access to 'foo-staging' cloud defined in Cloudfile")
|
136
136
|
@cli_user.add("megan@example.com")
|
data/spec/shelly/client_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require "spec_helper"
|
|
3
3
|
describe Shelly::Client::APIError do
|
4
4
|
before do
|
5
5
|
body = {"message" => "something went wrong", "errors" => [["first", "foo"]], "url" => "https://foo.bar"}
|
6
|
-
@error = Shelly::Client::APIError.new(body.to_json)
|
6
|
+
@error = Shelly::Client::APIError.new(body.to_json, 404)
|
7
7
|
end
|
8
8
|
|
9
9
|
it "should return error message" do
|
@@ -21,12 +21,23 @@ describe Shelly::Client::APIError do
|
|
21
21
|
it "should return user friendly string" do
|
22
22
|
@error.each_error{|error| error.should == "First foo"}
|
23
23
|
end
|
24
|
+
|
25
|
+
describe "#not_found?" do
|
26
|
+
it "should return true if response status code is 404" do
|
27
|
+
@error.should be_not_found
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return false if response status code is not 404" do
|
31
|
+
error = Shelly::Client::APIError.new({}.to_json, 500)
|
32
|
+
error.should_not be_not_found
|
33
|
+
end
|
34
|
+
end
|
24
35
|
|
25
36
|
describe "#validation?" do
|
26
37
|
context "when error is caused by validation errors" do
|
27
38
|
it "should return true" do
|
28
39
|
body = {"message" => "Validation Failed"}
|
29
|
-
error = Shelly::Client::APIError.new(body.to_json)
|
40
|
+
error = Shelly::Client::APIError.new(body.to_json, 422)
|
30
41
|
error.should be_validation
|
31
42
|
end
|
32
43
|
end
|
@@ -42,7 +53,7 @@ describe Shelly::Client::APIError do
|
|
42
53
|
context "when error is caused by unauthorized error" do
|
43
54
|
it "should return true" do
|
44
55
|
body = {"message" => "Unauthorized"}
|
45
|
-
error = Shelly::Client::APIError.new(body.to_json)
|
56
|
+
error = Shelly::Client::APIError.new(body.to_json, 401)
|
46
57
|
error.should be_unauthorized
|
47
58
|
end
|
48
59
|
end
|
@@ -59,7 +70,11 @@ describe Shelly::Client do
|
|
59
70
|
before do
|
60
71
|
ENV['SHELLY_URL'] = nil
|
61
72
|
@client = Shelly::Client.new("bob@example.com", "secret")
|
62
|
-
|
73
|
+
end
|
74
|
+
|
75
|
+
def api_url(resource = "")
|
76
|
+
auth = "#{CGI.escape(@client.email)}:#{@client.password}@"
|
77
|
+
"https://#{auth}admin.winniecloud.com/apiv2/#{resource}"
|
63
78
|
end
|
64
79
|
|
65
80
|
describe "#api_url" do
|
@@ -103,7 +118,7 @@ describe Shelly::Client do
|
|
103
118
|
describe "#deploy_logs" do
|
104
119
|
it "should send get request" do
|
105
120
|
time = Time.now
|
106
|
-
FakeWeb.register_uri(:get,
|
121
|
+
FakeWeb.register_uri(:get, api_url("apps/staging-foo/deploys"), :body => [{:failed => false, :created_at => time},
|
107
122
|
{:failed => true, :created_at => time+1}].to_json)
|
108
123
|
response = @client.deploy_logs("staging-foo")
|
109
124
|
response.should == [{"failed"=>false, "created_at"=>time.to_s},
|
@@ -113,7 +128,7 @@ describe Shelly::Client do
|
|
113
128
|
|
114
129
|
describe "#deploy_log" do
|
115
130
|
it "should send get request with cloud and log" do
|
116
|
-
FakeWeb.register_uri(:get,
|
131
|
+
FakeWeb.register_uri(:get, api_url("apps/staging-foo/deploys/2011-11-29-11-50-16"), :body => {:content => "Log"}.to_json)
|
117
132
|
response = @client.deploy_log("staging-foo", "2011-11-29-11-50-16")
|
118
133
|
response.should == {"content" => "Log"}
|
119
134
|
end
|
@@ -121,7 +136,7 @@ describe Shelly::Client do
|
|
121
136
|
|
122
137
|
describe "#app_configs" do
|
123
138
|
it "should send get request" do
|
124
|
-
FakeWeb.register_uri(:get,
|
139
|
+
FakeWeb.register_uri(:get, api_url("apps/staging-foo/configs"), :body => [{:created_by_user => true, :path => "config/app.yml"}].to_json)
|
125
140
|
response = @client.app_configs("staging-foo")
|
126
141
|
response.should == [{"created_by_user" => true, "path" => "config/app.yml"}]
|
127
142
|
end
|
@@ -130,7 +145,7 @@ describe Shelly::Client do
|
|
130
145
|
describe "#application_logs" do
|
131
146
|
it "should send get request" do
|
132
147
|
time = Time.now
|
133
|
-
FakeWeb.register_uri(:get,
|
148
|
+
FakeWeb.register_uri(:get, api_url("apps/staging-foo/logs"),
|
134
149
|
:body => {:logs => ["application_log_1", "application_log_2"]}.to_json)
|
135
150
|
response = @client.application_logs("staging-foo")
|
136
151
|
response.should == {"logs" => ["application_log_1", "application_log_2"]}
|
@@ -146,7 +161,7 @@ describe Shelly::Client do
|
|
146
161
|
|
147
162
|
describe "#app_users" do
|
148
163
|
it "should send get request with app code_names" do
|
149
|
-
FakeWeb.register_uri(:get,
|
164
|
+
FakeWeb.register_uri(:get, api_url("apps/staging-foo/users"), :body => [{:email => "test@example.com"},
|
150
165
|
{:email => "test2@example.com"}].to_json)
|
151
166
|
response = @client.app_users("staging-foo")
|
152
167
|
response.should == [{"email" => "test@example.com"}, {"email" => "test2@example.com"}]
|
@@ -155,7 +170,7 @@ describe Shelly::Client do
|
|
155
170
|
|
156
171
|
describe "#app_ips" do
|
157
172
|
it "should send get request with app code_name" do
|
158
|
-
FakeWeb.register_uri(:get,
|
173
|
+
FakeWeb.register_uri(:get, api_url("apps/staging-foo/ips"), :body => {:mail_server_ip => "10.0.1.1", :web_server_ip => "88.198.21.187"}.to_json)
|
159
174
|
response = @client.app_ips("staging-foo")
|
160
175
|
response.should == {"mail_server_ip" => "10.0.1.1", "web_server_ip" => "88.198.21.187"}
|
161
176
|
end
|
@@ -163,8 +178,8 @@ describe Shelly::Client do
|
|
163
178
|
|
164
179
|
describe "#send_invitation" do
|
165
180
|
it "should send post with developer's email" do
|
166
|
-
FakeWeb.register_uri(:post,
|
167
|
-
FakeWeb.register_uri(:post,
|
181
|
+
FakeWeb.register_uri(:post, api_url("apps/staging-foo/collaborations"), :body => {}.to_json)
|
182
|
+
FakeWeb.register_uri(:post, api_url("apps/production-foo/collaborations"), :body => {}.to_json)
|
168
183
|
response = @client.send_invitation("staging-foo", "megan@example.com")
|
169
184
|
response.should == {}
|
170
185
|
end
|
@@ -186,7 +201,7 @@ describe Shelly::Client do
|
|
186
201
|
|
187
202
|
describe "#start_cloud" do
|
188
203
|
it "should sent post request with cloud's code_name" do
|
189
|
-
FakeWeb.register_uri(:put,
|
204
|
+
FakeWeb.register_uri(:put, api_url("apps/staging-foo/start"), :body => {}.to_json)
|
190
205
|
response = @client.start_cloud("staging-foo")
|
191
206
|
response.should == {}
|
192
207
|
end
|
@@ -194,7 +209,7 @@ describe Shelly::Client do
|
|
194
209
|
|
195
210
|
describe "#stop_cloud" do
|
196
211
|
it "should sent delete request with cloud's code_name" do
|
197
|
-
FakeWeb.register_uri(:put,
|
212
|
+
FakeWeb.register_uri(:put, api_url("apps/staging-foo/stop"), :body => {}.to_json)
|
198
213
|
response = @client.stop_cloud("staging-foo")
|
199
214
|
response.should == {}
|
200
215
|
end
|
@@ -206,6 +221,48 @@ describe Shelly::Client do
|
|
206
221
|
@client.ssh_key_available?("ssh-key Abb")
|
207
222
|
end
|
208
223
|
end
|
224
|
+
|
225
|
+
describe "#database_backup" do
|
226
|
+
it "should fetch backup description from API" do
|
227
|
+
expected = {
|
228
|
+
"filename" => @filename,
|
229
|
+
"size" => 1234,
|
230
|
+
"human_size" => "2KB"
|
231
|
+
}
|
232
|
+
filename = "2011.11.26.04.00.10.foo.postgres.tar.gz"
|
233
|
+
url = api_url("apps/foo/database_backups/#{filename}")
|
234
|
+
FakeWeb.register_uri(:get, url, :body => expected.to_json)
|
235
|
+
|
236
|
+
@client.database_backup("foo", filename).should == expected
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
describe "#download_backup" do
|
241
|
+
before do
|
242
|
+
@filename = "2011.11.26.04.00.10.foo.postgres.tar.gz"
|
243
|
+
url = api_url("apps/foo/database_backups/#{@filename}")
|
244
|
+
response = Net::HTTPResponse.new('', '', '')
|
245
|
+
# Streaming
|
246
|
+
response.stub(:read_body).and_yield("aaa").and_yield("bbbbb").and_yield("dddf")
|
247
|
+
FakeWeb.register_uri(:get, url, :response => response)
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should write streamed database backup to file" do
|
251
|
+
@client.download_backup("foo", @filename)
|
252
|
+
File.read(@filename).should == %w(aaa bbbbb dddf).join
|
253
|
+
end
|
254
|
+
|
255
|
+
it "should execute progress_callback with size of every chunk" do
|
256
|
+
progress = mock(:update => true)
|
257
|
+
progress.should_receive(:update).with(3)
|
258
|
+
progress.should_receive(:update).with(5)
|
259
|
+
progress.should_receive(:update).with(4)
|
260
|
+
|
261
|
+
callback = lambda { |size| progress.update(size) }
|
262
|
+
|
263
|
+
@client.download_backup("foo", @filename, callback)
|
264
|
+
end
|
265
|
+
end
|
209
266
|
|
210
267
|
describe "#request_parameters" do
|
211
268
|
it "should return hash of resquest parameters" do
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "shelly/download_progress_bar"
|
3
|
+
|
4
|
+
describe Shelly::DownloadProgressBar do
|
5
|
+
before do
|
6
|
+
$stderr.stub(:print)
|
7
|
+
@bar = Shelly::DownloadProgressBar.new(4444)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should inherith from ProgressBar" do
|
11
|
+
@bar.should be_kind_of(ProgressBar)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should initialize parent with header and given size" do
|
15
|
+
@bar.title.should == "Progress"
|
16
|
+
@bar.total.should == 4444
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#progress_callback" do
|
20
|
+
it "should return callback for updating progress bar" do
|
21
|
+
@bar.should_receive(:inc).with(10)
|
22
|
+
@bar.should_receive(:inc).with(20)
|
23
|
+
|
24
|
+
@bar.progress_callback.call(10)
|
25
|
+
@bar.progress_callback.call(20)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shelly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 91
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 34
|
10
|
+
version: 0.0.34
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Shelly Cloud team
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-12-
|
18
|
+
date: 2011-12-27 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rspec
|
@@ -187,6 +187,20 @@ dependencies:
|
|
187
187
|
version: "0"
|
188
188
|
type: :runtime
|
189
189
|
version_requirements: *id012
|
190
|
+
- !ruby/object:Gem::Dependency
|
191
|
+
name: progressbar
|
192
|
+
prerelease: false
|
193
|
+
requirement: &id013 !ruby/object:Gem::Requirement
|
194
|
+
none: false
|
195
|
+
requirements:
|
196
|
+
- - ">="
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
hash: 3
|
199
|
+
segments:
|
200
|
+
- 0
|
201
|
+
version: "0"
|
202
|
+
type: :runtime
|
203
|
+
version_requirements: *id013
|
190
204
|
description: Tool for managing applications and clouds at shellycloud.com
|
191
205
|
email:
|
192
206
|
- support@shellycloud.com
|
@@ -208,6 +222,7 @@ files:
|
|
208
222
|
- lib/core_ext/object.rb
|
209
223
|
- lib/shelly.rb
|
210
224
|
- lib/shelly/app.rb
|
225
|
+
- lib/shelly/backup.rb
|
211
226
|
- lib/shelly/cli/backup.rb
|
212
227
|
- lib/shelly/cli/command.rb
|
213
228
|
- lib/shelly/cli/config.rb
|
@@ -217,6 +232,7 @@ files:
|
|
217
232
|
- lib/shelly/cli/user.rb
|
218
233
|
- lib/shelly/client.rb
|
219
234
|
- lib/shelly/cloudfile.rb
|
235
|
+
- lib/shelly/download_progress_bar.rb
|
220
236
|
- lib/shelly/helpers.rb
|
221
237
|
- lib/shelly/model.rb
|
222
238
|
- lib/shelly/templates/Cloudfile.erb
|
@@ -229,6 +245,7 @@ files:
|
|
229
245
|
- spec/helpers.rb
|
230
246
|
- spec/input_faker.rb
|
231
247
|
- spec/shelly/app_spec.rb
|
248
|
+
- spec/shelly/backup_spec.rb
|
232
249
|
- spec/shelly/cli/backup_spec.rb
|
233
250
|
- spec/shelly/cli/config_spec.rb
|
234
251
|
- spec/shelly/cli/deploys_spec.rb
|
@@ -237,6 +254,7 @@ files:
|
|
237
254
|
- spec/shelly/cli/user_spec.rb
|
238
255
|
- spec/shelly/client_spec.rb
|
239
256
|
- spec/shelly/cloudfile_spec.rb
|
257
|
+
- spec/shelly/download_progress_bar_spec.rb
|
240
258
|
- spec/shelly/model_spec.rb
|
241
259
|
- spec/shelly/user_spec.rb
|
242
260
|
- spec/spec_helper.rb
|
@@ -278,6 +296,7 @@ test_files:
|
|
278
296
|
- spec/helpers.rb
|
279
297
|
- spec/input_faker.rb
|
280
298
|
- spec/shelly/app_spec.rb
|
299
|
+
- spec/shelly/backup_spec.rb
|
281
300
|
- spec/shelly/cli/backup_spec.rb
|
282
301
|
- spec/shelly/cli/config_spec.rb
|
283
302
|
- spec/shelly/cli/deploys_spec.rb
|
@@ -286,6 +305,7 @@ test_files:
|
|
286
305
|
- spec/shelly/cli/user_spec.rb
|
287
306
|
- spec/shelly/client_spec.rb
|
288
307
|
- spec/shelly/cloudfile_spec.rb
|
308
|
+
- spec/shelly/download_progress_bar_spec.rb
|
289
309
|
- spec/shelly/model_spec.rb
|
290
310
|
- spec/shelly/user_spec.rb
|
291
311
|
- spec/spec_helper.rb
|