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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ODQ3NzU0MWM0MWJhZTU5YzZhMjg5NmY3YzhiZjQ5N2Y1NDc4YmFiZQ==
4
+ OTBkYWQ5MTBjMzM5ODk3NzA2MjUxNmI1YmYxNjFkYjQ5ZDA3YmIzMA==
5
5
  data.tar.gz: !binary |-
6
- NmE3Y2Q0NWRlOWZjMzU4Y2Y0NzRjYWI2ODM4ZWMxZmE1OThlMzNlOA==
6
+ ZDEyM2ZiYzNhNjJjYWUwMmMxMjEzNWIzZDk3ZjI2NjEyMGUwN2RiMg==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- MTAyN2EyNDFiZmExYmM1Mjg0ZWQwYTExY2IzNjQ5NGUwMzZlZTA2MDNmMTdk
10
- MWIyMzkyY2QyZWI1ZDIwNWZkMjgxNGJkNmZkNTAzMTg4YTZmNmMyNDQ5MDY4
11
- MWY4YzQ4Yjc2ZmM0ZTE3Zjc3YTgzMDAzYTg0YTU2MzU4NTc3N2I=
9
+ NDYyMGIzMDdhMDFkNTEzNjk0MzUyNjlkYTU5ZjViYjkzZTM4ZmVmN2E1OGNk
10
+ MmQwYjg5MDQ3MTVmMGRiZDFlMGFkNDhmNmMzMTdmZjc1ZWU0NTcyMTQ0NDE0
11
+ MDI1NWRlYjJiYTVkODcyODFkM2YxNWU1YzU5NmJlMzJjM2Q2ZjI=
12
12
  data.tar.gz: !binary |-
13
- Mzg4YTI5NjAyOTQwYWYzMTA1YTU0MTE4MWU1YTljMTA1Mzk0NWVmZTg3ZDRl
14
- YjZkYjEwMjhmNDM4ZDE4M2M3OGViMGJmMzEzNjAyMDQ2NTg1MzA1NmZkZmU4
15
- MjAwY2E2MThkY2NkMGI1NWVmM2E2Y2MwODYxNzFjNjU2YjI2M2E=
13
+ ODk0MDZkZjRiOWIwYWM5YzNmNGU3YjU5MDZmYzE2OWJiYjc4ZTg4OWE2NjFk
14
+ MDQ3MWI3M2VhOGIwYmE4ODc1ODJlODk1M2ExZmQ1YzgxM2ZmZjNkNDliYzJh
15
+ MGFjYTg4NDkxMjcwZjgxMmM2NTdkMWJmM2I1ZmQ2ZDNiNzY3ZDA=
data/.travis.yml CHANGED
@@ -6,3 +6,6 @@ rvm:
6
6
  - ree
7
7
  - jruby-18mode
8
8
  - jruby-19mode
9
+ notifications:
10
+ email:
11
+ - devs@shellycloud.com
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.3.0 / 2013-06-23
2
+
3
+ * [improvement] API key is now stored in .netrc
4
+
1
5
  ## 0.2.28 / 2013-06-18
2
6
 
3
7
  * [improvement] user can answer 'y' to 'yes/no' questions
@@ -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.email = (email || ask_for_email)
40
- user.password = ask_for_password
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
- user.email = email || ask_for_email
62
- user.password = ask_for_password(:with_confirmation => false)
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.delete_credentials
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
- class APIException < Exception
8
- attr_reader :status_code, :body, :request_id
9
-
10
- def initialize(body = {}, status_code = nil, request_id = nil)
11
- @status_code = status_code
12
- @body = body
13
- @request_id = request_id
14
- end
15
-
16
- def [](key)
17
- body[key.to_s]
18
- end
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 shellyapp_url
60
- get("/shellyapp")["url"]
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
- @email ? {:user => @email, :password => @password} : {}
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