shelly 0.2.28 → 0.3.0
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 +8 -8
- data/.travis.yml +3 -0
- data/CHANGELOG.md +4 -0
- data/lib/shelly/cli/main.rb +8 -8
- data/lib/shelly/client.rb +20 -206
- data/lib/shelly/client/application_logs.rb +26 -0
- data/lib/shelly/client/apps.rb +40 -0
- data/lib/shelly/client/auth.rb +69 -0
- data/lib/shelly/client/configs.rb +21 -0
- data/lib/shelly/client/database_backups.rb +21 -0
- data/lib/shelly/client/deployment_logs.rb +9 -0
- data/lib/shelly/client/deploys.rb +9 -0
- data/lib/shelly/client/errors.rb +49 -0
- data/lib/shelly/client/organizations.rb +21 -0
- data/lib/shelly/client/shellyapp.rb +5 -0
- data/lib/shelly/client/ssh_keys.rb +9 -0
- data/lib/shelly/client/users.rb +5 -0
- data/lib/shelly/helpers.rb +1 -2
- data/lib/shelly/model.rb +2 -4
- data/lib/shelly/user.rb +21 -32
- data/lib/shelly/version.rb +1 -1
- data/shelly.gemspec +1 -0
- data/spec/shelly/cli/backup_spec.rb +1 -1
- data/spec/shelly/cli/config_spec.rb +1 -1
- data/spec/shelly/cli/deploy_spec.rb +2 -2
- data/spec/shelly/cli/file_spec.rb +1 -1
- data/spec/shelly/cli/logs_spec.rb +1 -1
- data/spec/shelly/cli/main_spec.rb +27 -48
- data/spec/shelly/cli/organization_spec.rb +1 -1
- data/spec/shelly/cli/runner_spec.rb +1 -1
- data/spec/shelly/cli/user_spec.rb +1 -1
- data/spec/shelly/client_spec.rb +27 -16
- data/spec/shelly/model_spec.rb +2 -4
- data/spec/shelly/user_spec.rb +45 -83
- metadata +28 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OTBkYWQ5MTBjMzM5ODk3NzA2MjUxNmI1YmYxNjFkYjQ5ZDA3YmIzMA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZDEyM2ZiYzNhNjJjYWUwMmMxMjEzNWIzZDk3ZjI2NjEyMGUwN2RiMg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NDYyMGIzMDdhMDFkNTEzNjk0MzUyNjlkYTU5ZjViYjkzZTM4ZmVmN2E1OGNk
|
10
|
+
MmQwYjg5MDQ3MTVmMGRiZDFlMGFkNDhmNmMzMTdmZjc1ZWU0NTcyMTQ0NDE0
|
11
|
+
MDI1NWRlYjJiYTVkODcyODFkM2YxNWU1YzU5NmJlMzJjM2Q2ZjI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODk0MDZkZjRiOWIwYWM5YzNmNGU3YjU5MDZmYzE2OWJiYjc4ZTg4OWE2NjFk
|
14
|
+
MDQ3MWI3M2VhOGIwYmE4ODc1ODJlODk1M2ExZmQ1YzgxM2ZmZjNkNDliYzJh
|
15
|
+
MGFjYTg4NDkxMjcwZjgxMmM2NTdkMWJmM2I1ZmQ2ZDNiNzY3ZDA=
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/lib/shelly/cli/main.rb
CHANGED
@@ -33,13 +33,13 @@ module Shelly
|
|
33
33
|
|
34
34
|
desc "register [EMAIL]", "Register new account"
|
35
35
|
def register(email = nil)
|
36
|
-
user = Shelly::User.new
|
37
36
|
say "Your public SSH key will be uploaded to Shelly Cloud after registration."
|
38
37
|
say "Registering with email: #{email}" if email
|
39
|
-
user
|
40
|
-
|
38
|
+
user = Shelly::User.new
|
39
|
+
email ||= ask_for_email
|
40
|
+
password = ask_for_password
|
41
41
|
ask_for_acceptance_of_terms
|
42
|
-
user.register
|
42
|
+
user.register(email, password)
|
43
43
|
if user.ssh_key_exists?
|
44
44
|
say "Uploading your public SSH key from #{user.ssh_key_path}"
|
45
45
|
else
|
@@ -58,9 +58,9 @@ module Shelly
|
|
58
58
|
user = Shelly::User.new
|
59
59
|
say "Your public SSH key will be uploaded to Shelly Cloud after login."
|
60
60
|
raise Errno::ENOENT, user.ssh_key_path unless user.ssh_key_exists?
|
61
|
-
|
62
|
-
|
63
|
-
user.login
|
61
|
+
email ||= ask_for_email
|
62
|
+
password = ask_for_password(:with_confirmation => false)
|
63
|
+
user.login(email, password)
|
64
64
|
say "Login successful", :green
|
65
65
|
user.upload_ssh_key
|
66
66
|
say "Uploading your public SSH key"
|
@@ -320,7 +320,7 @@ Wait until cloud is in 'turned off' state and try again.}
|
|
320
320
|
def logout
|
321
321
|
user = Shelly::User.new
|
322
322
|
say "Your public SSH key has been removed from Shelly Cloud" if user.delete_ssh_key
|
323
|
-
say "You have been successfully logged out" if user.
|
323
|
+
say "You have been successfully logged out" if user.logout
|
324
324
|
end
|
325
325
|
|
326
326
|
desc "rake TASK", "Run rake task"
|
data/lib/shelly/client.rb
CHANGED
@@ -1,219 +1,29 @@
|
|
1
1
|
require "rest_client"
|
2
2
|
require "json"
|
3
3
|
require "cgi"
|
4
|
+
require "netrc"
|
4
5
|
|
5
6
|
module Shelly
|
6
7
|
class Client
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
class UnauthorizedException < APIException; end
|
22
|
-
class ForbiddenException < APIException; end
|
23
|
-
class ConflictException < APIException; end
|
24
|
-
class GemVersionException < APIException; end
|
25
|
-
class GatewayTimeoutException < APIException; end
|
26
|
-
class LockedException < APIException; end
|
27
|
-
class ValidationException < APIException
|
28
|
-
def errors
|
29
|
-
self[:errors]
|
30
|
-
end
|
31
|
-
|
32
|
-
def each_error
|
33
|
-
errors.each do |field, message|
|
34
|
-
yield [field.gsub('_',' ').capitalize, message].join(" ")
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
class NotFoundException < APIException
|
39
|
-
def resource
|
40
|
-
self[:resource].to_sym
|
41
|
-
end
|
42
|
-
|
43
|
-
def id
|
44
|
-
self[:id]
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
attr_reader :email, :password
|
49
|
-
|
50
|
-
def initialize(email = nil, password = nil)
|
51
|
-
@email = email
|
52
|
-
@password = password
|
53
|
-
end
|
8
|
+
require 'shelly/client/errors'
|
9
|
+
require 'shelly/client/shellyapp'
|
10
|
+
require 'shelly/client/users'
|
11
|
+
require 'shelly/client/apps'
|
12
|
+
require 'shelly/client/configs'
|
13
|
+
require 'shelly/client/deployment_logs'
|
14
|
+
require 'shelly/client/application_logs'
|
15
|
+
require 'shelly/client/database_backups'
|
16
|
+
require 'shelly/client/deploys'
|
17
|
+
require 'shelly/client/ssh_keys'
|
18
|
+
require 'shelly/client/organizations'
|
19
|
+
require 'shelly/client/auth'
|
54
20
|
|
55
21
|
def api_url
|
56
22
|
ENV["SHELLY_URL"] || "https://api.shellycloud.com/apiv2"
|
57
23
|
end
|
58
24
|
|
59
|
-
def
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
def register_user(email, password, ssh_key)
|
64
|
-
post("/users", :user => {:email => email, :password => password, :ssh_key => ssh_key})
|
65
|
-
end
|
66
|
-
|
67
|
-
def token
|
68
|
-
get("/token")
|
69
|
-
end
|
70
|
-
|
71
|
-
def app_configs(cloud)
|
72
|
-
get("/apps/#{cloud}/configs")
|
73
|
-
end
|
74
|
-
|
75
|
-
def app_config(cloud, path)
|
76
|
-
get("/apps/#{cloud}/configs/#{CGI.escape(path)}")
|
77
|
-
end
|
78
|
-
|
79
|
-
def app_create_config(cloud, path, content)
|
80
|
-
post("/apps/#{cloud}/configs", :config => {:path => path, :content => content})
|
81
|
-
end
|
82
|
-
|
83
|
-
def app_update_config(cloud, path, content)
|
84
|
-
put("/apps/#{cloud}/configs/#{CGI.escape(path)}", :config => {:content => content})
|
85
|
-
end
|
86
|
-
|
87
|
-
def app_delete_config(cloud, path)
|
88
|
-
delete("/apps/#{cloud}/configs/#{CGI.escape(path)}")
|
89
|
-
end
|
90
|
-
|
91
|
-
def send_invitation(name, email, owner = false)
|
92
|
-
post("/organizations/#{name}/memberships", :email => email, :owner => owner)
|
93
|
-
end
|
94
|
-
|
95
|
-
def delete_member(name, email)
|
96
|
-
delete("/organizations/#{name}/memberships/#{email}")
|
97
|
-
end
|
98
|
-
|
99
|
-
def create_app(attributes)
|
100
|
-
organization = attributes.delete(:organization_name)
|
101
|
-
zone = attributes.delete(:zone_name)
|
102
|
-
post("/apps", :app => attributes, :organization_name => organization,
|
103
|
-
:zone_name => zone)
|
104
|
-
end
|
105
|
-
|
106
|
-
def delete_app(code_name)
|
107
|
-
delete("/apps/#{code_name}")
|
108
|
-
end
|
109
|
-
|
110
|
-
def add_ssh_key(ssh_key)
|
111
|
-
post("/ssh_keys", :ssh_key => ssh_key)
|
112
|
-
end
|
113
|
-
|
114
|
-
def logout(ssh_key)
|
115
|
-
delete("/ssh_keys", :ssh_key => ssh_key)
|
116
|
-
end
|
117
|
-
|
118
|
-
def start_cloud(cloud)
|
119
|
-
put("/apps/#{cloud}/start")
|
120
|
-
end
|
121
|
-
|
122
|
-
def stop_cloud(cloud)
|
123
|
-
put("/apps/#{cloud}/stop")
|
124
|
-
end
|
125
|
-
|
126
|
-
def apps
|
127
|
-
get("/apps")
|
128
|
-
end
|
129
|
-
|
130
|
-
def app(code_name)
|
131
|
-
get("/apps/#{code_name}")
|
132
|
-
end
|
133
|
-
|
134
|
-
def organizations
|
135
|
-
get("/organizations")
|
136
|
-
end
|
137
|
-
|
138
|
-
def organization(name)
|
139
|
-
get("/organizations/#{name}")
|
140
|
-
end
|
141
|
-
|
142
|
-
def statistics(code_name)
|
143
|
-
get("/apps/#{code_name}/statistics")
|
144
|
-
end
|
145
|
-
|
146
|
-
def command(cloud, body, type)
|
147
|
-
post("/apps/#{cloud}/command", {:body => body, :type => type})
|
148
|
-
end
|
149
|
-
|
150
|
-
def console(code_name, server = nil)
|
151
|
-
get("/apps/#{code_name}/console", {:server => server})
|
152
|
-
end
|
153
|
-
|
154
|
-
def deploy_logs(cloud)
|
155
|
-
get("/apps/#{cloud}/deployment_logs")
|
156
|
-
end
|
157
|
-
|
158
|
-
def deploy_log(cloud, log)
|
159
|
-
get("/apps/#{cloud}/deployment_logs/#{log}")
|
160
|
-
end
|
161
|
-
|
162
|
-
def application_logs(cloud, options = {})
|
163
|
-
get("/apps/#{cloud}/application_logs#{query(options)}")
|
164
|
-
end
|
165
|
-
|
166
|
-
def application_logs_tail(cloud)
|
167
|
-
url = get("/apps/#{cloud}/application_logs/tail")["url"]
|
168
|
-
options = {
|
169
|
-
:url => url,
|
170
|
-
:method => :get,
|
171
|
-
:timeout => 60 * 60 * 24,
|
172
|
-
:block_response => Proc.new { |r| r.read_body { |c| yield(c) } }
|
173
|
-
}.merge(http_basic_auth_options)
|
174
|
-
RestClient::Request.execute(options)
|
175
|
-
end
|
176
|
-
|
177
|
-
def download_application_logs_attributes(code_name, options)
|
178
|
-
attributes = get("/apps/#{code_name}/application_logs/download#{query(options)}")
|
179
|
-
headers = RestClient::Request.execute({
|
180
|
-
:method => :get,
|
181
|
-
:url => "#{attributes["url"]}/headers"
|
182
|
-
}.merge(http_basic_auth_options)).headers
|
183
|
-
|
184
|
-
attributes.merge("size" => headers[:content_lenght].to_i)
|
185
|
-
end
|
186
|
-
|
187
|
-
def database_backups(code_name)
|
188
|
-
get("/apps/#{code_name}/database_backups")
|
189
|
-
end
|
190
|
-
|
191
|
-
def database_backup(code_name, handler)
|
192
|
-
get("/apps/#{code_name}/database_backups/#{handler}")
|
193
|
-
end
|
194
|
-
|
195
|
-
def restore_backup(code_name, filename)
|
196
|
-
put("/apps/#{code_name}/database_backups/#{filename}/restore")
|
197
|
-
end
|
198
|
-
|
199
|
-
def request_backup(code_name, kind = nil)
|
200
|
-
post("/apps/#{code_name}/database_backups", :kind => kind)
|
201
|
-
end
|
202
|
-
|
203
|
-
def download_backup_url(code_name, filename)
|
204
|
-
get("/apps/#{code_name}/database_backups/#{filename}/download_url")["url"]
|
205
|
-
end
|
206
|
-
|
207
|
-
def members(name)
|
208
|
-
get("/organizations/#{name}/memberships")
|
209
|
-
end
|
210
|
-
|
211
|
-
def redeploy(cloud)
|
212
|
-
post("/apps/#{cloud}/deploys")
|
213
|
-
end
|
214
|
-
|
215
|
-
def deployment(cloud, deployment_id)
|
216
|
-
get("/apps/#{cloud}/deploys/#{deployment_id}")
|
25
|
+
def api_host
|
26
|
+
URI.parse(api_url).host
|
217
27
|
end
|
218
28
|
|
219
29
|
def query(options = {})
|
@@ -271,7 +81,11 @@ module Shelly
|
|
271
81
|
end
|
272
82
|
|
273
83
|
def http_basic_auth_options
|
274
|
-
|
84
|
+
if @email && @password
|
85
|
+
{:user => @email, :password => @password}
|
86
|
+
else
|
87
|
+
basic_auth_from_netrc
|
88
|
+
end
|
275
89
|
end
|
276
90
|
|
277
91
|
def request_parameters(path, method, params = {})
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Shelly::Client
|
2
|
+
def application_logs(cloud, options = {})
|
3
|
+
get("/apps/#{cloud}/application_logs#{query(options)}")
|
4
|
+
end
|
5
|
+
|
6
|
+
def application_logs_tail(cloud)
|
7
|
+
url = get("/apps/#{cloud}/application_logs/tail")["url"]
|
8
|
+
options = {
|
9
|
+
:url => url,
|
10
|
+
:method => :get,
|
11
|
+
:timeout => 60 * 60 * 24,
|
12
|
+
:block_response => Proc.new { |r| r.read_body { |c| yield(c) } }
|
13
|
+
}.merge(http_basic_auth_options)
|
14
|
+
RestClient::Request.execute(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def download_application_logs_attributes(code_name, options)
|
18
|
+
attributes = get("/apps/#{code_name}/application_logs/download#{query(options)}")
|
19
|
+
headers = RestClient::Request.execute({
|
20
|
+
:method => :get,
|
21
|
+
:url => "#{attributes["url"]}/headers"
|
22
|
+
}.merge(http_basic_auth_options)).headers
|
23
|
+
|
24
|
+
attributes.merge("size" => headers[:content_lenght].to_i)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Shelly::Client
|
2
|
+
def create_app(attributes)
|
3
|
+
organization = attributes.delete(:organization_name)
|
4
|
+
zone = attributes.delete(:zone_name)
|
5
|
+
post("/apps", :app => attributes, :organization_name => organization,
|
6
|
+
:zone_name => zone)
|
7
|
+
end
|
8
|
+
|
9
|
+
def delete_app(code_name)
|
10
|
+
delete("/apps/#{code_name}")
|
11
|
+
end
|
12
|
+
|
13
|
+
def start_cloud(cloud)
|
14
|
+
put("/apps/#{cloud}/start")
|
15
|
+
end
|
16
|
+
|
17
|
+
def stop_cloud(cloud)
|
18
|
+
put("/apps/#{cloud}/stop")
|
19
|
+
end
|
20
|
+
|
21
|
+
def apps
|
22
|
+
get("/apps")
|
23
|
+
end
|
24
|
+
|
25
|
+
def app(code_name)
|
26
|
+
get("/apps/#{code_name}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def statistics(code_name)
|
30
|
+
get("/apps/#{code_name}/statistics")
|
31
|
+
end
|
32
|
+
|
33
|
+
def command(cloud, body, type)
|
34
|
+
post("/apps/#{cloud}/command", {:body => body, :type => type})
|
35
|
+
end
|
36
|
+
|
37
|
+
def console(code_name, server = nil)
|
38
|
+
get("/apps/#{code_name}/console", {:server => server})
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class Shelly::Client
|
2
|
+
def authorize_with_email_and_password(email, password)
|
3
|
+
forget_authorization
|
4
|
+
@email = email; @password = password
|
5
|
+
api_key = get_token
|
6
|
+
store_api_key_in_netrc(email, api_key)
|
7
|
+
end
|
8
|
+
|
9
|
+
def user_email
|
10
|
+
@email || email_from_netrc
|
11
|
+
end
|
12
|
+
|
13
|
+
def authorize!
|
14
|
+
get_token
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def forget_authorization
|
19
|
+
remove_api_key_from_netrc
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_token
|
23
|
+
get("/token")["token"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def basic_auth_from_netrc
|
27
|
+
if netrc
|
28
|
+
user, password = netrc[api_host]
|
29
|
+
{:user => user, :password => password}
|
30
|
+
else
|
31
|
+
{}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def store_api_key_in_netrc(email, api_key)
|
36
|
+
FileUtils.mkdir_p(File.dirname(netrc_path))
|
37
|
+
FileUtils.touch(netrc_path)
|
38
|
+
FileUtils.chmod(0600, netrc_path)
|
39
|
+
|
40
|
+
netrc[api_host] = [email, api_key]
|
41
|
+
netrc.save
|
42
|
+
end
|
43
|
+
|
44
|
+
def remove_api_key_from_netrc
|
45
|
+
if netrc
|
46
|
+
netrc.delete(api_host)
|
47
|
+
netrc.save
|
48
|
+
true # must return a truthy value to print logout confirmation
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def email_from_netrc
|
53
|
+
netrc[api_host].first if netrc
|
54
|
+
end
|
55
|
+
|
56
|
+
def netrc
|
57
|
+
@netrc ||= File.exists?(netrc_path) && Netrc.read(netrc_path)
|
58
|
+
end
|
59
|
+
|
60
|
+
def netrc_path
|
61
|
+
default = Netrc.default_path
|
62
|
+
encrypted = default + ".gpg"
|
63
|
+
if File.exists?(encrypted)
|
64
|
+
encrypted
|
65
|
+
else
|
66
|
+
default
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|