hubic 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +34 -0
- data/Gemfile +10 -0
- data/LICENSE +21 -0
- data/README.md +30 -0
- data/Rakefile +11 -0
- data/hubic.gemspec +22 -0
- data/lib/hubic.rb +219 -0
- data/lib/hubic/file_ops.rb +54 -0
- data/lib/hubic/openstack.rb +222 -0
- data/lib/hubic/store.rb +40 -0
- data/lib/hubic/version.rb +3 -0
- metadata +110 -0
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
# .ruby-version
|
31
|
+
# .ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 sdalu
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
Hubic
|
2
|
+
=====
|
3
|
+
|
4
|
+
Requirement
|
5
|
+
-----------
|
6
|
+
You need to retrieve the client id, secret key, and redirection url.
|
7
|
+
For that if not alredy done you need to register an application into your account.
|
8
|
+
To start the registration process you will need to go to ``My Account``, select ``Your application``, and click on ``Add an application``.
|
9
|
+
|
10
|
+
Quick example
|
11
|
+
-------------
|
12
|
+
```ruby
|
13
|
+
require 'hubic'
|
14
|
+
|
15
|
+
# Configure the client
|
16
|
+
Hubic.default_redirect_uri = '** your redirect_uri **'
|
17
|
+
Hubic.default_client_id = '** your client_id **'
|
18
|
+
Hubic.default_client_secret = '** your client_secret **'
|
19
|
+
|
20
|
+
# Create a hubic handler for the desired user
|
21
|
+
h = Hubic.for_user('** your login **', '** your password **')
|
22
|
+
|
23
|
+
# Download file hubic-foo.txt and save it to local-foo.txt
|
24
|
+
h.download("hubic-foo.txt", "local-foo.txt")
|
25
|
+
|
26
|
+
# Upload file local-foo.txt and save it to hubic with the name hubic-bar.txt
|
27
|
+
h.upload("local-foo.txt", "hubic-bar.txt")
|
28
|
+
```
|
29
|
+
|
30
|
+
|
data/Rakefile
ADDED
data/hubic.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.unshift File.expand_path("../lib", __FILE__)
|
3
|
+
require "hubic/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "hubic"
|
7
|
+
s.version = Hubic::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = [ "Stephane D'Alu" ]
|
10
|
+
s.email = ["sdalu@sdalu.com" ]
|
11
|
+
s.homepage = "http://github.com/sdalu/ruby-hubic"
|
12
|
+
s.summary = "Manage your Hubic account from Ruby"
|
13
|
+
s.description = "Manage your Hubic account from Ruby"
|
14
|
+
|
15
|
+
s.add_dependency "faraday", "~>0.9"
|
16
|
+
s.add_dependency "nokogiri"
|
17
|
+
s.add_development_dependency "rake"
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
21
|
+
s.require_path = 'lib'
|
22
|
+
end
|
data/lib/hubic.rb
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
# https://api.hubic.com/docs/#/server-side
|
2
|
+
# http://docs.openstack.org/api/openstack-object-storage/1.0/content/authentication-examples-curl.html
|
3
|
+
|
4
|
+
|
5
|
+
require 'pathname'
|
6
|
+
require 'uri'
|
7
|
+
require 'time'
|
8
|
+
|
9
|
+
require 'json'
|
10
|
+
require 'faraday'
|
11
|
+
require 'nokogiri'
|
12
|
+
|
13
|
+
# TODO
|
14
|
+
# - verify scope (requested == granted)
|
15
|
+
# - better scope handling
|
16
|
+
# - deal with state string
|
17
|
+
# - refresh token and credentials
|
18
|
+
|
19
|
+
# X-Storage-Url
|
20
|
+
# X-Auth-Token
|
21
|
+
|
22
|
+
require_relative 'hubic/version'
|
23
|
+
require_relative 'hubic/store'
|
24
|
+
require_relative 'hubic/openstack'
|
25
|
+
require_relative 'hubic/file_ops'
|
26
|
+
|
27
|
+
class Hubic
|
28
|
+
class Error < StandardError
|
29
|
+
class Auth < Error
|
30
|
+
end
|
31
|
+
class NotFound < Error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def self.default_client_id=(client_id)
|
37
|
+
@@client_id = client_id
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.default_client_secret=(client_secret)
|
41
|
+
@@client_secret = client_secret
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.default_redirect_uri=(redirect_uri)
|
45
|
+
@@redirect_uri = redirect_uri
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def self.for_user(user, password=nil, store: Store[user],
|
50
|
+
&password_requester)
|
51
|
+
h = Hubic.new(@@client_id, @@client_secret, @@redirect_uri)
|
52
|
+
h.for_user(user, password, store: store, &password_requester)
|
53
|
+
h
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def initialize(client_id = @@client_id,
|
58
|
+
client_secret = @@client_secret,
|
59
|
+
redirect_uri = @@redirect_uri)
|
60
|
+
@store = nil
|
61
|
+
@client_id = client_id
|
62
|
+
@client_secret = client_secret
|
63
|
+
@redirect_uri = redirect_uri
|
64
|
+
@conn = Faraday.new('https://api.hubic.com') do |faraday|
|
65
|
+
faraday.request :url_encoded
|
66
|
+
faraday.adapter :net_http
|
67
|
+
faraday.options.params_encoder = Faraday::FlatParamsEncoder
|
68
|
+
end
|
69
|
+
@default_container = "default"
|
70
|
+
end
|
71
|
+
|
72
|
+
def for_user(user, password=nil, store: Store[user], &password_requester)
|
73
|
+
@store = store
|
74
|
+
@refresh_token = @store['refresh_token'] if @store
|
75
|
+
|
76
|
+
if @refresh_token
|
77
|
+
data = refresh_access_token
|
78
|
+
@access_token = data[:access_token]
|
79
|
+
@expires_at = data[:expires_at ]
|
80
|
+
else
|
81
|
+
password ||= password_requester.call(user) if password_requester
|
82
|
+
if password.nil?
|
83
|
+
raise ArgumentError, "password requiered for user authorization"
|
84
|
+
end
|
85
|
+
code = get_request_code(user, password)
|
86
|
+
data = get_access_token(code)
|
87
|
+
@access_token = data[:access_token ]
|
88
|
+
@expires_at = data[:expires_in ]
|
89
|
+
@refresh_token = data[:refresh_token]
|
90
|
+
if @store
|
91
|
+
@store['refresh_token'] = @refresh_token
|
92
|
+
@store.save
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def account
|
99
|
+
api_hubic(:get, '/1.0/account')
|
100
|
+
end
|
101
|
+
|
102
|
+
def credentials
|
103
|
+
api_hubic(:get, '/1.0/account/credentials')
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
def [](path=nil, container=@default_container)
|
109
|
+
# objects(path, container: container)
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
def api_hubic(method, path, params=nil)
|
115
|
+
r = @conn.method(method).call(path) do |req|
|
116
|
+
req.headers['Authorization'] = "Bearer #{@access_token}"
|
117
|
+
req.params = params if params
|
118
|
+
end
|
119
|
+
JSON.parse(r.body)
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
|
125
|
+
def get_request_code(user, password)
|
126
|
+
# Request code (retrieve user confirmation form)
|
127
|
+
r = @conn.get '/oauth/auth', {
|
128
|
+
:client_id => @client_id,
|
129
|
+
:response_type => 'code',
|
130
|
+
:redirect_uri => 'http://localhost/',
|
131
|
+
:scope => 'account.r,usage.r,links.drw,credentials.r',
|
132
|
+
:state => 'random'
|
133
|
+
}
|
134
|
+
|
135
|
+
# Autofill confirmation
|
136
|
+
params = {}
|
137
|
+
doc = Nokogiri::HTML(r.body)
|
138
|
+
doc.css('input').each {|i|
|
139
|
+
case i[:name]
|
140
|
+
when 'login'
|
141
|
+
params[:login ] = user
|
142
|
+
next
|
143
|
+
when 'user_pwd'
|
144
|
+
params[:user_pwd] = password
|
145
|
+
next
|
146
|
+
end
|
147
|
+
|
148
|
+
case i[:type]
|
149
|
+
when 'checkbox', 'hidden', 'text'
|
150
|
+
(params[i[:name]] ||= []) << i[:value] if i[:name]
|
151
|
+
end
|
152
|
+
}
|
153
|
+
if params.empty?
|
154
|
+
raise Error, "unable to autofill confirmation form"
|
155
|
+
end
|
156
|
+
|
157
|
+
# Confirm and get code
|
158
|
+
r = @conn.post '/oauth/auth', params
|
159
|
+
q = Hash[URI.decode_www_form(URI(r[:location]).query)]
|
160
|
+
|
161
|
+
case r.status
|
162
|
+
when 302
|
163
|
+
q['code']
|
164
|
+
when 400, 401, 500
|
165
|
+
raise Error::Auth, "#{q['error']} #{q['error_description']}"
|
166
|
+
else
|
167
|
+
raise Error::Auth, "unhandled response code (#{r.status})"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def get_access_token(code)
|
172
|
+
r = @conn.post '/oauth/token', {
|
173
|
+
:code => code,
|
174
|
+
:redirect_uri => 'http://localhost/',
|
175
|
+
:grant_type => 'authorization_code',
|
176
|
+
:client_id => @client_id,
|
177
|
+
:client_secret => @client_secret
|
178
|
+
}
|
179
|
+
j = JSON.parse(r.body)
|
180
|
+
case r.status
|
181
|
+
when 200
|
182
|
+
{ :acces_token => j['access_token'],
|
183
|
+
:expires_at => Time.parse(r[:date]) + j['expires_in'].to_i,
|
184
|
+
:refresh_token => j['refresh_token']
|
185
|
+
}
|
186
|
+
when 400, 401, 500
|
187
|
+
raise Error::Auth, "#{j['error']} #{j['error_description']}"
|
188
|
+
else
|
189
|
+
raise Error::Auth, "unhandled response code (#{r.status})"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def refresh_access_token
|
194
|
+
if @refresh_token.nil?
|
195
|
+
raise Error, "refresh_token was not previously acquiered"
|
196
|
+
end
|
197
|
+
r = @conn.post '/oauth/token', {
|
198
|
+
:refresh_token => @refresh_token,
|
199
|
+
:grant_type => 'refresh_token',
|
200
|
+
:client_id => @client_id,
|
201
|
+
:client_secret => @client_secret
|
202
|
+
}
|
203
|
+
j = JSON.parse(r.body)
|
204
|
+
case r.status
|
205
|
+
when 200
|
206
|
+
{ :access_token => j['access_token'],
|
207
|
+
:expires_at => Time.parse(r[:date]) + j['expires_in'].to_i
|
208
|
+
}
|
209
|
+
when 400, 401, 500
|
210
|
+
raise Error::Auth, "#{j['error']} #{j['error_description']}"
|
211
|
+
else
|
212
|
+
raise Error::Auth, "unhandled response code (#{r.status})"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class Hubic
|
2
|
+
|
3
|
+
# File operations
|
4
|
+
#######################################################################
|
5
|
+
|
6
|
+
def download(obj, dst=nil, size: nil, offset: 0, &block)
|
7
|
+
io = nil
|
8
|
+
dst = Pathname(dst) if String === dst
|
9
|
+
_dst = case dst
|
10
|
+
when Pathname then io = dst.open('w')
|
11
|
+
when NilClass then io = StringIO.new
|
12
|
+
else dst
|
13
|
+
end
|
14
|
+
|
15
|
+
meta = get_object(obj, _dst, size: size, offset: offset, &block)
|
16
|
+
|
17
|
+
if (Pathname === dst) && meta[:lastmod]
|
18
|
+
dst.utime(meta[:lastmod], meta[:lastmod])
|
19
|
+
end
|
20
|
+
|
21
|
+
if dst.nil?
|
22
|
+
then io.flush.string
|
23
|
+
else meta
|
24
|
+
end
|
25
|
+
ensure
|
26
|
+
io.close unless io.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
def upload(src, obj, &block)
|
30
|
+
put_object(src, obj, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def copy(src, dst)
|
34
|
+
raise "not implemented yet"
|
35
|
+
end
|
36
|
+
|
37
|
+
def move(src, dst)
|
38
|
+
raise "not implemented yet"
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(src)
|
42
|
+
raise "not implemented yet"
|
43
|
+
end
|
44
|
+
|
45
|
+
def checksum(src)
|
46
|
+
raise "not implemented yet"
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def list(path = '/', container = @default_container)
|
51
|
+
objects(container, path: path)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
|
7
|
+
class Hubic
|
8
|
+
class Container
|
9
|
+
attr_reader :name, :count, :bytes, :metadata
|
10
|
+
def initialize(hubic, name)
|
11
|
+
@hubic = hubic
|
12
|
+
@name = name.dup.freeze
|
13
|
+
j, h = @hubic.api_openstack(:head, @name)
|
14
|
+
@count = h['x-container-object-count'].to_i
|
15
|
+
@bytes = h['x-container-bytes-used' ].to_i
|
16
|
+
@etag = h['etag' ]
|
17
|
+
@metadata = Hash[h.map {|k,v|
|
18
|
+
if k =~ /^x-container-meta-(.*)/
|
19
|
+
[ $1, v ]
|
20
|
+
end
|
21
|
+
}.compact].freeze
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
@metadata[key]
|
26
|
+
end
|
27
|
+
|
28
|
+
def empty?
|
29
|
+
@count == 0
|
30
|
+
end
|
31
|
+
|
32
|
+
def destroy!
|
33
|
+
j, h = @hubic.api_openstack(:delete, @name)
|
34
|
+
j
|
35
|
+
end
|
36
|
+
|
37
|
+
def object(path)
|
38
|
+
end
|
39
|
+
|
40
|
+
def objects()
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
class Object
|
47
|
+
def initialize(hubic)
|
48
|
+
@hubic = hubic
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_object(obj, dst=nil, size: nil, offset: 0, &block)
|
53
|
+
container, path, uri = normalize_object(obj)
|
54
|
+
|
55
|
+
if !(IO === dst) && !dst.respond_to?(:write) &&
|
56
|
+
!(Proc === dst) && !dst.respond_to?(:call)
|
57
|
+
raise ArgumentError, "unsupported destination"
|
58
|
+
end
|
59
|
+
|
60
|
+
meta = {}
|
61
|
+
|
62
|
+
hdrs = {}
|
63
|
+
hdrs['X-Auth-Token'] = @os[:token]
|
64
|
+
|
65
|
+
if size
|
66
|
+
hdrs['Range'] = sprintf("bytes=%d-%d", offset, offset + size - 1)
|
67
|
+
end
|
68
|
+
|
69
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
70
|
+
if uri.scheme == 'https'
|
71
|
+
http.use_ssl = true
|
72
|
+
# http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
73
|
+
end
|
74
|
+
http.start
|
75
|
+
|
76
|
+
|
77
|
+
http.request_get(uri.request_uri, hdrs) {|response|
|
78
|
+
case response
|
79
|
+
when Net::HTTPSuccess
|
80
|
+
when Net::HTTPRedirection
|
81
|
+
fail "http redirect is not currently handled"
|
82
|
+
when Net::HTTPUnauthorized
|
83
|
+
# TODO: Need to refresh token
|
84
|
+
else
|
85
|
+
fail "resource unavailable: #{uri} (#{response.class})"
|
86
|
+
end
|
87
|
+
|
88
|
+
lastmod = Time.parse(response['last-modified']) rescue nil
|
89
|
+
length = response.content_length
|
90
|
+
type = response['content-type']
|
91
|
+
meta = { :lastmod => lastmod,
|
92
|
+
:length => length,
|
93
|
+
:type => type
|
94
|
+
}
|
95
|
+
|
96
|
+
if block
|
97
|
+
block.call(meta)
|
98
|
+
end
|
99
|
+
|
100
|
+
response.read_body {|segment|
|
101
|
+
if IO === dst then dst.write(segment)
|
102
|
+
elsif Proc === dst then dst.call(segment)
|
103
|
+
elsif dst.respond_to?(:write) then dst.write(segment)
|
104
|
+
elsif dst.respond_to?(:call ) then dst.call(segment)
|
105
|
+
end
|
106
|
+
|
107
|
+
if block
|
108
|
+
block.call(segment)
|
109
|
+
end
|
110
|
+
}
|
111
|
+
}
|
112
|
+
if block
|
113
|
+
block.call(:done)
|
114
|
+
end
|
115
|
+
|
116
|
+
meta
|
117
|
+
ensure
|
118
|
+
http.finish unless http.nil?
|
119
|
+
end
|
120
|
+
|
121
|
+
def put_object(src, obj, &block)
|
122
|
+
container, path, uri = normalize_object(obj)
|
123
|
+
|
124
|
+
r = @conn.put(uri) do |req|
|
125
|
+
req.headers['X-Auth-Token'] = @os[:token]
|
126
|
+
req.params[:file] = Faraday::UploadIO.new(src, 'text/plain')
|
127
|
+
end
|
128
|
+
puts r.inspect
|
129
|
+
end
|
130
|
+
|
131
|
+
def objects(container = @default_container,
|
132
|
+
path: nil, limit: nil, gt: nil, lt: nil)
|
133
|
+
path = path[1..-1] if path && path[0] == ?/
|
134
|
+
p = { path: path, limit: limit, marker: gt, end_marker: lt
|
135
|
+
}.delete_if {|k,v| v.nil? }
|
136
|
+
j, = api_openstack(:get, container, p)
|
137
|
+
Hash[j.map {|o| [ o['name'], {
|
138
|
+
:hash => o['hash'],
|
139
|
+
:lastmod => Time.parse(o['last_modified']),
|
140
|
+
:size => o['bytes'].to_i,
|
141
|
+
:type => o['content_type']
|
142
|
+
} ] } ]
|
143
|
+
end
|
144
|
+
|
145
|
+
def containers
|
146
|
+
j, = api_openstack(:get, '/')
|
147
|
+
Hash[j.map {|c| [ c['name'], { :size => c['bytes'].to_i,
|
148
|
+
:count => c['count'].to_i } ] } ]
|
149
|
+
end
|
150
|
+
|
151
|
+
def container(name)
|
152
|
+
Container.new(self, name)
|
153
|
+
end
|
154
|
+
|
155
|
+
def default_container=(name)
|
156
|
+
@default_container = name
|
157
|
+
end
|
158
|
+
|
159
|
+
def api_openstack(method, path, params=nil)
|
160
|
+
openstack_setup_refresh
|
161
|
+
|
162
|
+
params ||= {}
|
163
|
+
params[:format] ||= :json
|
164
|
+
|
165
|
+
p = "#{@os[:endpoint]}#{'/' if path[0] != ?/}#{path}"
|
166
|
+
r = @conn.method(method).call(p) do |req|
|
167
|
+
req.headers['X-Auth-Token'] = @os[:token]
|
168
|
+
req.params = params
|
169
|
+
end
|
170
|
+
|
171
|
+
if r.body.nil? || r.body.empty?
|
172
|
+
then [ nil, r.headers ]
|
173
|
+
else [ JSON.parse(r.body), r.headers ]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
def openstack_setup_refresh(force: false)
|
179
|
+
return unless force || @os.nil? || @os[:expires] <= Time.now
|
180
|
+
|
181
|
+
data = self.credentials
|
182
|
+
endpoint = data['endpoint']
|
183
|
+
token = data['token']
|
184
|
+
expires = Time.parse(data['expires'])
|
185
|
+
|
186
|
+
openstack_setup(endpoint, token, expires)
|
187
|
+
end
|
188
|
+
|
189
|
+
def openstack_setup(endpoint, token, expires)
|
190
|
+
conn = Faraday.new do |faraday|
|
191
|
+
faraday.request :url_encoded
|
192
|
+
faraday.adapter :net_http
|
193
|
+
faraday.options.params_encoder = Faraday::FlatParamsEncoder
|
194
|
+
end
|
195
|
+
@os = {
|
196
|
+
:expires => expires,
|
197
|
+
:token => token,
|
198
|
+
:endpoint => endpoint,
|
199
|
+
:conn => conn
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
def normalize_object(obj)
|
204
|
+
c, p = case obj
|
205
|
+
when String
|
206
|
+
[ @default_container, obj ]
|
207
|
+
when Hash
|
208
|
+
[ obj[:name] || obj[:path],
|
209
|
+
(obj[:container] || @default_container).to_s ]
|
210
|
+
when Array
|
211
|
+
case obj.length
|
212
|
+
when 1 then [ @default_container, obj ]
|
213
|
+
when 2 then Symbol === obj[1] ? [ obj[1], obj[0] ] : obj
|
214
|
+
else raise ArguementError
|
215
|
+
end
|
216
|
+
end
|
217
|
+
c = c.to_s
|
218
|
+
p = p[1..-1] if p[0] == ?/
|
219
|
+
[ c, p, URI("#{@os[:endpoint]}/#{c}/#{p}") ]
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
data/lib/hubic/store.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
class Hubic
|
4
|
+
class Store
|
5
|
+
FILE = "#{ENV['HOME']}/.hubic"
|
6
|
+
|
7
|
+
extend Forwardable
|
8
|
+
def_delegator :@data, :[]
|
9
|
+
def_delegator :@data, :[]=
|
10
|
+
|
11
|
+
def initialize(file = FILE, user = nil)
|
12
|
+
@file = file
|
13
|
+
@user = user
|
14
|
+
@data = Hash(begin
|
15
|
+
if data = YAML.load_file(@file)
|
16
|
+
@user.nil? ? data : data[@user]
|
17
|
+
end
|
18
|
+
rescue Errno::ENOENT
|
19
|
+
end)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.[](user)
|
23
|
+
self.new(file = FILE, user)
|
24
|
+
end
|
25
|
+
|
26
|
+
def save
|
27
|
+
data = if @user
|
28
|
+
( begin
|
29
|
+
YAML.load_file(@file)
|
30
|
+
rescue Errno::ENOENT
|
31
|
+
end || {} ).merge(@user => @data).to_yaml
|
32
|
+
else
|
33
|
+
@data
|
34
|
+
end
|
35
|
+
File.open(@file, 'w', 0600) {|io|
|
36
|
+
io.write(data.to_yaml)
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hubic
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Stephane D'Alu
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-05-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: faraday
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.9'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.9'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: nokogiri
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Manage your Hubic account from Ruby
|
63
|
+
email:
|
64
|
+
- sdalu@sdalu.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- hubic.gemspec
|
75
|
+
- lib/hubic.rb
|
76
|
+
- lib/hubic/file_ops.rb
|
77
|
+
- lib/hubic/openstack.rb
|
78
|
+
- lib/hubic/store.rb
|
79
|
+
- lib/hubic/version.rb
|
80
|
+
homepage: http://github.com/sdalu/ruby-hubic
|
81
|
+
licenses: []
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
hash: 4363560357882864147
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
hash: 4363560357882864147
|
104
|
+
requirements: []
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 1.8.29
|
107
|
+
signing_key:
|
108
|
+
specification_version: 3
|
109
|
+
summary: Manage your Hubic account from Ruby
|
110
|
+
test_files: []
|