yao 0.0.2.rc3 → 0.0.2
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 +4 -4
- data/.travis.yml +16 -0
- data/Gemfile +2 -0
- data/README.md +1 -1
- data/Rakefile +10 -0
- data/lib/yao/auth.rb +22 -58
- data/lib/yao/client.rb +21 -4
- data/lib/yao/config.rb +35 -2
- data/lib/yao/faraday_middlewares.rb +63 -1
- data/lib/yao/resources.rb +1 -0
- data/lib/yao/resources/restfully_accessible.rb +4 -2
- data/lib/yao/resources/server.rb +9 -0
- data/lib/yao/resources/tenant.rb +9 -0
- data/lib/yao/server_error.rb +81 -0
- data/lib/yao/token.rb +63 -0
- data/lib/yao/version.rb +1 -1
- data/test/config.rb +9 -0
- data/test/yao/test_auth.rb +152 -0
- data/test/yao/test_client.rb +49 -0
- data/test/yao/test_config.rb +65 -0
- data/test/yao/test_server_error.rb +25 -0
- data/test/yao/test_token.rb +74 -0
- data/yao.gemspec +1 -0
- metadata +35 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4efa85ede55eb2f96de87fba1af6fcbcb3ec3228
|
4
|
+
data.tar.gz: 860deb926f0bd3dd5fe4addca1ff152ee7902e18
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3db94bfb4a5c854c186a63521371a7ef00be604080a2ebf932dd54af4cec5ef6e0eee1cce840c98453a3e055b051921eb5e33ae9cf587db7dc5aa29314dd0a2
|
7
|
+
data.tar.gz: a661e89dec7b324abe55fd5c7c74affe9406767a32fb2a1b92a0e64d9d7bc9f6fa48c78ab701c3bfbbd79298db0178c162997b43a45e7f46998f2849123e236a
|
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
language: ruby
|
2
|
+
|
3
|
+
rvm:
|
4
|
+
- 2.1.7
|
5
|
+
- 2.2.3
|
6
|
+
|
7
|
+
install: bundle install --path vendor/bundle
|
8
|
+
|
9
|
+
cache:
|
10
|
+
directories: vendor/bundle
|
11
|
+
|
12
|
+
script: bundle exec rake test
|
13
|
+
|
14
|
+
notifications:
|
15
|
+
slack:
|
16
|
+
secure: XhJ3SUF4I7a+MleHLEokgNrqROeCiTfVGzVOwFCTqVETurarb0s4FA5MGWgu7C9MCkIAVFLATwgIZQ9lo9MWFujVTh4Qj+rOdjI08c2ehVxwPyGMXbvkvqMRzvBbJuswnrXuJGUzxdCCjea64cdiZxguDiODp+cfjtKGSB59aNZoMsgwqVTppGDyXAThqZx/l2UKYjzONbnuCI9T9wt+UWW8nAMDn5zSTLVqVyK4GXs2ofGXswQlHvc7VlPLkmHVGm0ywazCyct+lo/Y9x1ofOeHZJllLRc5wccLxciCJBLJDrm/28LHy1PoidR5TXNRjp5ioJGVMwqvsBY8z3OwD8XVv6CPPGRYnEsJVT90R9HJoQ5YFHX5sDeGVbHSaX63eK4W7bn8MxqvEuQF5XU86B2xpLFOTKMK7HRKYjbToRKGMolj+1zv65orHM2Ts0ZtCuwa41oiYkRLWzSc9UDODn67OF+pJlEEYouljsGfcRYcv7JJ2rhFnFXi/9uISxzQMsP4L8dwEEJHxSkaSwJBMJf/it5hD4m5oO0oBQ1DRd3qg2ljpLPuzuY+pW1/R1JY1Z/DA4Kw7dvr8R0DJmXdDiWI9EYilYcP8Wk5qYlJdDGLEUfL8I+tz+e67wAzpqm9okE4gNiJ4HsS2MtztXCJgoSkBJVvhoEE9SXsYNICAno=
|
data/Gemfile
CHANGED
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -1,2 +1,12 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
2
3
|
|
4
|
+
desc 'Run yao\'s test suite'
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
# To run test for only one file (or file path pattern)
|
7
|
+
# $ bundle exec rake test TEST=test/test_specified_path.rb
|
8
|
+
t.libs.concat ["test"]
|
9
|
+
t.test_files = Dir["test/**/test_*.rb"]
|
10
|
+
t.verbose = true
|
11
|
+
t.ruby_opts = ["-r config"]
|
12
|
+
end
|
data/lib/yao/auth.rb
CHANGED
@@ -2,73 +2,37 @@ require 'yao'
|
|
2
2
|
require 'json'
|
3
3
|
require 'time'
|
4
4
|
|
5
|
-
|
5
|
+
require 'yao/token'
|
6
|
+
|
7
|
+
module Yao
|
6
8
|
%i(tenant_name username password).each do |name|
|
7
|
-
Yao.config.param name, nil
|
8
|
-
Yao::Auth.try_new
|
9
|
-
end
|
9
|
+
Yao.config.param name, nil
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Yao
|
12
|
+
module Auth
|
13
|
+
class << self
|
14
|
+
def try_new
|
15
|
+
if Yao.config.tenant_name && Yao.config.username && Yao.config.password && Yao.default_client
|
16
|
+
Yao::Auth.new
|
17
|
+
end
|
16
18
|
end
|
17
|
-
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
20
|
+
def new(
|
21
|
+
tenant_name: Yao.config.tenant_name,
|
22
|
+
username: Yao.config.username,
|
23
|
+
password: Yao.config.password
|
24
|
+
)
|
25
|
+
auth_info = {
|
26
|
+
auth: {
|
27
|
+
passwordCredentials: {
|
28
|
+
username: username, password: password
|
29
|
+
}
|
28
30
|
}
|
29
31
|
}
|
30
|
-
|
31
|
-
authinfo[:auth][:tenantName] = tenant_name if tenant_name
|
32
|
+
auth_info[:auth][:tenantName] = tenant_name if tenant_name
|
32
33
|
|
33
|
-
|
34
|
-
req.body = authinfo.to_json
|
35
|
-
req.headers['Content-Type'] = 'application/json'
|
36
|
-
end
|
37
|
-
|
38
|
-
body = reply.body["access"]
|
39
|
-
|
40
|
-
token = Token.new(body["token"])
|
41
|
-
token.register_endpoints(body["serviceCatalog"])
|
42
|
-
return token
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class Token
|
47
|
-
def initialize(token_data)
|
48
|
-
@token = token_data["id"]
|
49
|
-
@issued_at = Time.parse token_data["issued_at"]
|
50
|
-
@expire_at = Time.parse token_data["expires"]
|
51
|
-
|
52
|
-
@endpoints = {}
|
53
|
-
end
|
54
|
-
attr_accessor :token, :issued_at, :expire_at, :endpoints
|
55
|
-
alias expires expire_at
|
56
|
-
|
57
|
-
def register_endpoints(_endpoints)
|
58
|
-
return unless _endpoints
|
59
|
-
|
60
|
-
_endpoints.each do |endpoint_data|
|
61
|
-
key = endpoint_data["type"]
|
62
|
-
value = if d = endpoint_data["endpoints"].first
|
63
|
-
d["publicURL"]
|
64
|
-
else
|
65
|
-
nil
|
66
|
-
end
|
67
|
-
if value
|
68
|
-
@endpoints[key] = value
|
69
|
-
end
|
34
|
+
return Token.issue(Yao.default_client.default, auth_info)
|
70
35
|
end
|
71
|
-
Yao.default_client.register_endpoints(@endpoints, token: @token)
|
72
36
|
end
|
73
37
|
end
|
74
38
|
end
|
data/lib/yao/client.rb
CHANGED
@@ -33,8 +33,8 @@ module Yao
|
|
33
33
|
class << self
|
34
34
|
attr_accessor :default_client
|
35
35
|
|
36
|
-
def
|
37
|
-
|
36
|
+
def client_generator_hook
|
37
|
+
lambda do |f, token|
|
38
38
|
f.request :url_encoded
|
39
39
|
f.request :json
|
40
40
|
|
@@ -42,13 +42,28 @@ module Yao
|
|
42
42
|
f.request :os_token, token
|
43
43
|
end
|
44
44
|
|
45
|
+
f.response :os_error_detector
|
45
46
|
f.response :json, :content_type => /\bjson$/
|
46
|
-
|
47
|
+
|
48
|
+
if Yao.config.debug
|
49
|
+
f.response :logger
|
50
|
+
f.response :os_dumper
|
51
|
+
end
|
52
|
+
|
53
|
+
if Yao.config.debug_record_response
|
54
|
+
f.response :os_response_recorder
|
55
|
+
end
|
47
56
|
|
48
57
|
f.adapter Faraday.default_adapter
|
49
58
|
end
|
50
59
|
end
|
51
60
|
|
61
|
+
def gen_client(endpoint, token: nil)
|
62
|
+
Faraday.new( endpoint ) do |f|
|
63
|
+
client_generator_hook.call(f, token)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
52
67
|
def reset_client(new_endpoint=nil)
|
53
68
|
set = ClientSet.new
|
54
69
|
set.register_endpoints("default" => (new_endpoint || Yao.config.endpoint))
|
@@ -59,7 +74,6 @@ module Yao
|
|
59
74
|
Yao.config.param :auth_url, nil do |endpoint|
|
60
75
|
if endpoint
|
61
76
|
Yao::Client.reset_client(endpoint)
|
62
|
-
Yao::Auth.try_new
|
63
77
|
end
|
64
78
|
end
|
65
79
|
end
|
@@ -67,4 +81,7 @@ module Yao
|
|
67
81
|
def self.default_client
|
68
82
|
Yao::Client.default_client
|
69
83
|
end
|
84
|
+
|
85
|
+
Yao.config.param :debug, false
|
86
|
+
Yao.config.param :debug_record_response, false
|
70
87
|
end
|
data/lib/yao/config.rb
CHANGED
@@ -10,10 +10,35 @@ module Yao
|
|
10
10
|
@_configuration_hooks ||= {}
|
11
11
|
end
|
12
12
|
|
13
|
+
def _configuration_hooks_queue
|
14
|
+
@_configuration_hooks_queue ||= []
|
15
|
+
end
|
16
|
+
|
13
17
|
def configuration
|
14
18
|
@configuration ||= {}
|
15
19
|
end
|
16
20
|
|
21
|
+
HOOK_RENEW_CLIENT_KEYS = %i(tenant_name username password auth_url debug debug_record_response)
|
22
|
+
def delay_hook=(v)
|
23
|
+
@delay_hook = v
|
24
|
+
if !v and !_configuration_hooks_queue.empty?
|
25
|
+
_configuration_hooks_queue.each do |n, val|
|
26
|
+
_configuration_hooks[n].call(val) if _configuration_hooks[n]
|
27
|
+
end
|
28
|
+
# Authorization process should have special hook
|
29
|
+
# and should run last
|
30
|
+
unless (_configuration_hooks_queue.map(&:first) & HOOK_RENEW_CLIENT_KEYS).empty?
|
31
|
+
Yao::Auth.try_new
|
32
|
+
end
|
33
|
+
|
34
|
+
_configuration_hooks_queue.clear
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def delay_hook?
|
39
|
+
@delay_hook
|
40
|
+
end
|
41
|
+
|
17
42
|
def param(name, default, &hook)
|
18
43
|
raise("Duplicate definition of #{name}") if self.respond_to?(name)
|
19
44
|
|
@@ -35,14 +60,22 @@ module Yao
|
|
35
60
|
def set(name, value)
|
36
61
|
raise("Undefined config key #{name}") unless self.respond_to?(name)
|
37
62
|
configuration[name.to_sym] = value
|
38
|
-
|
63
|
+
if delay_hook?
|
64
|
+
_configuration_hooks_queue.push([name, value])
|
65
|
+
else
|
66
|
+
_configuration_hooks[name].call(value) if _configuration_hooks[name]
|
67
|
+
end
|
39
68
|
value
|
40
69
|
end
|
41
70
|
end
|
42
71
|
|
43
72
|
def self.config(&blk)
|
44
73
|
@__config ||= Config.new
|
45
|
-
|
74
|
+
if blk
|
75
|
+
@__config.delay_hook = true
|
76
|
+
@__config.instance_eval(&blk)
|
77
|
+
@__config.delay_hook = false
|
78
|
+
end
|
46
79
|
@__config
|
47
80
|
end
|
48
81
|
|
@@ -7,8 +7,70 @@ class Faraday::Request::OSToken
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(env)
|
10
|
-
|
10
|
+
if @token.expired?
|
11
|
+
@token.reflesh(Yao.default_client.default)
|
12
|
+
end
|
13
|
+
|
14
|
+
env[:request_headers]['X-Auth-Token'] = @token.to_s
|
11
15
|
@app.call(env)
|
12
16
|
end
|
13
17
|
end
|
14
18
|
Faraday::Request.register_middleware os_token: -> { Faraday::Request::OSToken }
|
19
|
+
|
20
|
+
class Faraday::Response::OSDumper < Faraday::Response::Middleware
|
21
|
+
def on_complete(env)
|
22
|
+
require 'pp'
|
23
|
+
params = [
|
24
|
+
env.url.to_s,
|
25
|
+
JSON.parse(env.body),
|
26
|
+
env.request_headers,
|
27
|
+
env.response_headers,
|
28
|
+
env.method,
|
29
|
+
env.status
|
30
|
+
].map(&:pretty_inspect)
|
31
|
+
$stdout.puts(<<-FMT % params)
|
32
|
+
================================
|
33
|
+
OpenStack response inspection:
|
34
|
+
================================
|
35
|
+
Requested To: %s
|
36
|
+
|
37
|
+
Body:
|
38
|
+
%s
|
39
|
+
Request Headers:
|
40
|
+
%s
|
41
|
+
Response Headers:
|
42
|
+
%s
|
43
|
+
|
44
|
+
Method: %s
|
45
|
+
Status Code: %s
|
46
|
+
================================
|
47
|
+
|
48
|
+
FMT
|
49
|
+
end
|
50
|
+
end
|
51
|
+
Faraday::Response.register_middleware os_dumper: -> { Faraday::Response::OSDumper }
|
52
|
+
|
53
|
+
class Faraday::Response::OSResponseRecorder < Faraday::Response::Middleware
|
54
|
+
def on_complete(env)
|
55
|
+
require 'pathname'
|
56
|
+
root = Pathname.new(File.expand_path('../../../tmp', __FILE__))
|
57
|
+
path = [env.method.to_s.upcase, env.url.path.gsub('/', '-')].join("-") + ".json"
|
58
|
+
|
59
|
+
puts root.join(path)
|
60
|
+
File.open(root.join(path), 'w') do |f|
|
61
|
+
f.write env.body
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
Faraday::Response.register_middleware os_response_recorder: -> { Faraday::Response::OSResponseRecorder }
|
66
|
+
|
67
|
+
require 'yao/server_error'
|
68
|
+
class Faraday::Response::OSErrorDetector < Faraday::Response::Middleware
|
69
|
+
# TODO: Better handling, respecting official doc
|
70
|
+
def on_complete(env)
|
71
|
+
return if env.success?
|
72
|
+
|
73
|
+
raise Yao::ServerError.detect(env)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
Faraday::Response.register_middleware os_error_detector: -> { Faraday::Response::OSErrorDetector }
|
data/lib/yao/resources.rb
CHANGED
@@ -5,7 +5,6 @@ module Yao::Resources
|
|
5
5
|
def self.extended(base)
|
6
6
|
base.class_eval do
|
7
7
|
class << self
|
8
|
-
attr_reader :client
|
9
8
|
attr_accessor :resource_name, :resources_name
|
10
9
|
|
11
10
|
extend Forwardable
|
@@ -18,13 +17,16 @@ module Yao::Resources
|
|
18
17
|
|
19
18
|
def service=(name)
|
20
19
|
@service = name
|
21
|
-
@client = Yao.default_client.pool[name]
|
22
20
|
end
|
23
21
|
|
24
22
|
def service
|
25
23
|
@service
|
26
24
|
end
|
27
25
|
|
26
|
+
def client
|
27
|
+
Yao.default_client.pool[service]
|
28
|
+
end
|
29
|
+
|
28
30
|
# restful methods
|
29
31
|
def list
|
30
32
|
return_resources(GET(resources_name).body[resources_name_in_json])
|
data/lib/yao/resources/server.rb
CHANGED
@@ -8,6 +8,15 @@ module Yao::Resources
|
|
8
8
|
map_attribute_to_resource :image => Image
|
9
9
|
map_attribute_to_resources :security_groups => SecurityGroup
|
10
10
|
|
11
|
+
map_attribute_to_attribute 'OS-EXT-AZ:availability_zone' => :availability_zone
|
12
|
+
map_attribute_to_attribute 'OS-DCF:diskConfig' => :dcf_disk_config
|
13
|
+
map_attribute_to_attribute 'OS-EXT-SRV-ATTR:host' => :ext_srv_attr_host
|
14
|
+
map_attribute_to_attribute 'OS-EXT-SRV-ATTR:hypervisor_hostname' => :ext_srv_attr_hypervisor_hostname
|
15
|
+
map_attribute_to_attribute 'OS-EXT-SRV-ATTR:instance_name' => :ext_srv_attr_instance_name
|
16
|
+
map_attribute_to_attribute 'OS-EXT-STS:power_state' => :ext_sts_power_state
|
17
|
+
map_attribute_to_attribute 'OS-EXT-STS:task_state' => :ext_sts_task_state
|
18
|
+
map_attribute_to_attribute 'OS-EXT-STS:vm_state' => :ext_sts_vm_state
|
19
|
+
|
11
20
|
self.service = "compute"
|
12
21
|
self.resource_name = "server"
|
13
22
|
self.resources_name = "servers"
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Yao
|
2
|
+
class ServerError < ::StandardError
|
3
|
+
def initialize(message, requested_env)
|
4
|
+
@status = requested_env.status
|
5
|
+
@env = requested_env
|
6
|
+
super(message)
|
7
|
+
end
|
8
|
+
attr_reader :status, :env
|
9
|
+
|
10
|
+
def self.detect(env)
|
11
|
+
case env.status
|
12
|
+
when 400
|
13
|
+
if env.body && env.body["computeFault"]
|
14
|
+
ComputeFault.new(extract_message(env.body), env)
|
15
|
+
elsif env.body && env.body.to_a.join.include?('NetworkNotFound')
|
16
|
+
NetworkNotFound.new(extract_message(env.body), env)
|
17
|
+
else
|
18
|
+
BadRequest.new(extract_message(env.body), env)
|
19
|
+
end
|
20
|
+
when 401
|
21
|
+
Unauthorized.new(extract_message(env.body), env)
|
22
|
+
when 404
|
23
|
+
if env.body && env.body["itemNotFound"]
|
24
|
+
ItemNotFound.new(extract_message(env.body), env)
|
25
|
+
else
|
26
|
+
NotFound.new("The resource could not be found.", env)
|
27
|
+
end
|
28
|
+
when 405
|
29
|
+
BadMethod.new(extract_message(env.body), env)
|
30
|
+
when 409
|
31
|
+
if env.body && env.body["buildInProgress"]
|
32
|
+
BuildInProgress.new(extract_message(env.body), env)
|
33
|
+
else
|
34
|
+
Conflict.new(extract_message(env.body), env)
|
35
|
+
end
|
36
|
+
when 413
|
37
|
+
OverLimit.new(extract_message(env.body), env)
|
38
|
+
when 415
|
39
|
+
BadMediaType.new(extract_message(env.body), env)
|
40
|
+
when 422
|
41
|
+
UnprocessableEntity.new(extract_message(env.body), env)
|
42
|
+
when 500
|
43
|
+
ComputeFault.new(extract_message(env.body), env)
|
44
|
+
when 503
|
45
|
+
if env.body && env.body[" serverCapacityUnavailable"]
|
46
|
+
ServerCapacityUnavailable.new(extract_message(env.body), env)
|
47
|
+
else
|
48
|
+
ServiceUnavailable.new(extract_message(env.body), env)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
new(extract_message(env.body), env)
|
52
|
+
end
|
53
|
+
rescue => e
|
54
|
+
new("Detection failed - %s" % e.message, env)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.extract_message(body)
|
58
|
+
body.values.first["message"] or raise
|
59
|
+
rescue
|
60
|
+
"Something is wrong. - %s" % body.inspect
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Errors as OpenStack error type
|
65
|
+
class ComputeFault < ServerError; end
|
66
|
+
class UnprocessableEntity < ServerError; end
|
67
|
+
class ItemNotFound < ServerError; end
|
68
|
+
class ServiceUnavailable < ServerError; end
|
69
|
+
class NotFound < ServerError; end
|
70
|
+
class BadRequest < ServerError; end
|
71
|
+
class Unauthorized < ServerError; end
|
72
|
+
class Forbidden < ServerError; end
|
73
|
+
class BadMethod < ServerError; end
|
74
|
+
class OverLimit < ServerError; end
|
75
|
+
class BadMediaType < ServerError; end
|
76
|
+
class NetworkNotFound < ServerError; end
|
77
|
+
class BuildInProgress < ServerError; end
|
78
|
+
class Conflict < ServerError; end
|
79
|
+
class ServerCapacityUnavailable < ServerError; end
|
80
|
+
|
81
|
+
end
|
data/lib/yao/token.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'yao/client'
|
2
|
+
|
3
|
+
module Yao
|
4
|
+
class Token
|
5
|
+
def self.issue(cli, auth_info)
|
6
|
+
t = new(auth_info)
|
7
|
+
t.reflesh(cli)
|
8
|
+
t
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(auth_info, token_data=nil)
|
12
|
+
@auth_info = auth_info
|
13
|
+
|
14
|
+
@endpoints = {}
|
15
|
+
end
|
16
|
+
attr_accessor :auth_info, :token, :issued_at, :expire_at, :endpoints
|
17
|
+
alias expires expire_at
|
18
|
+
alias to_s token
|
19
|
+
|
20
|
+
def register(token_data)
|
21
|
+
@token = token_data["id"]
|
22
|
+
@issued_at = Time.parse(token_data["issued_at"]).localtime
|
23
|
+
@expire_at = Time.parse(token_data["expires"]).localtime
|
24
|
+
end
|
25
|
+
|
26
|
+
def expired?
|
27
|
+
return true unless self.expire_at
|
28
|
+
Time.now >= self.expire_at
|
29
|
+
end
|
30
|
+
|
31
|
+
def reflesh(cli)
|
32
|
+
@endpoints.clear
|
33
|
+
|
34
|
+
res = cli.post('/v2.0/tokens') do |req|
|
35
|
+
req.body = auth_info.to_json
|
36
|
+
req.headers['Content-Type'] = 'application/json'
|
37
|
+
end
|
38
|
+
|
39
|
+
body = res.body["access"]
|
40
|
+
|
41
|
+
register(body["token"])
|
42
|
+
register_endpoints(body["serviceCatalog"])
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def register_endpoints(_endpoints)
|
47
|
+
return unless _endpoints
|
48
|
+
|
49
|
+
_endpoints.each do |endpoint_data|
|
50
|
+
key = endpoint_data["type"]
|
51
|
+
value = if d = endpoint_data["endpoints"].first
|
52
|
+
d["publicURL"]
|
53
|
+
else
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
if value
|
57
|
+
@endpoints[key] = value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
Yao.default_client.register_endpoints(@endpoints, token: self)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/yao/version.rb
CHANGED
data/test/config.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
class TestAuth < Test::Unit::TestCase
|
2
|
+
|
3
|
+
AUTH_JSON = \
|
4
|
+
"{\"auth\":{\"passwordCredentials\":{\"username\":\"udzura\",\"password\":\"XXXXXXXX\"},\"tenantName\":\"example\"}}"
|
5
|
+
|
6
|
+
RESPONSE_JSON = <<-JSON
|
7
|
+
{
|
8
|
+
"access": {
|
9
|
+
"token": {
|
10
|
+
"issued_at": "2015-08-31T03:58:36.073232",
|
11
|
+
"expires": "2015-09-01T03:58:36Z",
|
12
|
+
"id": "aaaa166533fd49f3b11b1cdce2430000",
|
13
|
+
"tenant": {
|
14
|
+
"description": "Testing",
|
15
|
+
"enabled": true,
|
16
|
+
"id": "aaaa166533fd49f3b11b1cdce2430000",
|
17
|
+
"name": "example"
|
18
|
+
}
|
19
|
+
},
|
20
|
+
"serviceCatalog": [
|
21
|
+
{
|
22
|
+
"endpoints": [
|
23
|
+
{
|
24
|
+
"adminURL": "http://nova-endpoint.example.com:8774/v2/b598bf98671c47e1b955f8c9660e3c44",
|
25
|
+
"region": "RegionOne",
|
26
|
+
"internalURL": "http://nova-endpoint.example.com:8774/v2/b598bf98671c47e1b955f8c9660e3c44",
|
27
|
+
"id": "1a66e6af97c440b2a7bbc4f9735923d9",
|
28
|
+
"publicURL": "http://nova-endpoint.example.com:8774/v2/b598bf98671c47e1b955f8c9660e3c44"
|
29
|
+
}
|
30
|
+
],
|
31
|
+
"endpoints_links": [],
|
32
|
+
"type": "compute",
|
33
|
+
"name": "nova"
|
34
|
+
},
|
35
|
+
{
|
36
|
+
"endpoints": [
|
37
|
+
{
|
38
|
+
"adminURL": "http://neutron-endpoint.example.com:9696/",
|
39
|
+
"region": "RegionOne",
|
40
|
+
"internalURL": "http://neutron-endpoint.example.com:9696/",
|
41
|
+
"id": "0418104da877468ca65d739142fa3454",
|
42
|
+
"publicURL": "http://neutron-endpoint.example.com:9696/"
|
43
|
+
}
|
44
|
+
],
|
45
|
+
"endpoints_links": [],
|
46
|
+
"type": "network",
|
47
|
+
"name": "neutron"
|
48
|
+
},
|
49
|
+
{
|
50
|
+
"endpoints": [
|
51
|
+
{
|
52
|
+
"adminURL": "http://glance-endpoint.example.com:9292",
|
53
|
+
"region": "RegionOne",
|
54
|
+
"internalURL": "http://glance-endpoint.example.com:9292",
|
55
|
+
"id": "246f33509ff64802b86eb081307ecec0",
|
56
|
+
"publicURL": "http://glance-endpoint.example.com:9292"
|
57
|
+
}
|
58
|
+
],
|
59
|
+
"endpoints_links": [],
|
60
|
+
"type": "image",
|
61
|
+
"name": "glance"
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"endpoints": [
|
65
|
+
{
|
66
|
+
"adminURL": "http://endpoint.example.com:12345/v2.0",
|
67
|
+
"region": "RegionOne",
|
68
|
+
"internalURL": "http://endpoint.example.com:5000/v2.0",
|
69
|
+
"id": "2b982236cc084128bf42b647c1b7fb49",
|
70
|
+
"publicURL": "http://endpoint.example.com:5000/v2.0"
|
71
|
+
}
|
72
|
+
],
|
73
|
+
"endpoints_links": [],
|
74
|
+
"type": "identity",
|
75
|
+
"name": "keystone"
|
76
|
+
}
|
77
|
+
],
|
78
|
+
"user": {
|
79
|
+
"username": "udzura",
|
80
|
+
"roles_links": [],
|
81
|
+
"id": "a9994b2dee82423da7da572397d3157a",
|
82
|
+
"roles": [
|
83
|
+
{
|
84
|
+
"name": "admin"
|
85
|
+
}
|
86
|
+
],
|
87
|
+
"name": "udzura"
|
88
|
+
},
|
89
|
+
"metadata": {
|
90
|
+
"is_admin": 0,
|
91
|
+
"roles": [
|
92
|
+
"ce5330c512cc4bd289b3a725ad1106b7"
|
93
|
+
]
|
94
|
+
}
|
95
|
+
}
|
96
|
+
}
|
97
|
+
JSON
|
98
|
+
|
99
|
+
def setup
|
100
|
+
stub_request(:post, "http://endpoint.example.com:12345/v2.0/tokens").with(body: AUTH_JSON)
|
101
|
+
.to_return(:status => 200, :body => RESPONSE_JSON, :headers => {'Content-Type' => 'application/json'})
|
102
|
+
|
103
|
+
Yao.config.set :auth_url, "http://endpoint.example.com:12345"
|
104
|
+
@token = Yao::Auth.new(tenant_name: "example", username: "udzura", password: "XXXXXXXX")
|
105
|
+
end
|
106
|
+
|
107
|
+
def teardown
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_auth_successful
|
111
|
+
cli = Yao.default_client.pool["default"]
|
112
|
+
assert { cli.url_prefix.to_s == "http://endpoint.example.com:12345/" }
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_service_sclients_initialized
|
116
|
+
%w(compute network image identity).each do |service|
|
117
|
+
cli = Yao.default_client.pool[service]
|
118
|
+
assert { !cli.nil? }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_nova_tenant_logged_in
|
123
|
+
tenant_id = "b598bf98671c47e1b955f8c9660e3c44"
|
124
|
+
cli = Yao.default_client.compute
|
125
|
+
assert { cli.url_prefix.to_s == "http://nova-endpoint.example.com:8774/v2/#{tenant_id}" }
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_neutron_prefix_added
|
129
|
+
cli = Yao.default_client.network
|
130
|
+
assert { cli.url_prefix.to_s == "http://neutron-endpoint.example.com:9696/v2.0" }
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_token_is_valid
|
134
|
+
assert { @token.token == "aaaa166533fd49f3b11b1cdce2430000" }
|
135
|
+
assert { @token.issued_at == Time.parse("2015-08-31T03:58:36.073232") }
|
136
|
+
assert { @token.expire_at == Time.parse("2015-09-01T03:58:36Z") }
|
137
|
+
assert { @token.endpoints.size == 4 }
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_hooked_by_configure_block
|
141
|
+
auth = Yao::Auth
|
142
|
+
stub(auth).new
|
143
|
+
|
144
|
+
Yao.configure do
|
145
|
+
auth_url "http://endpoint.example.com:12345"
|
146
|
+
tenant_name "example"
|
147
|
+
username "udzura"
|
148
|
+
password "XXXXXXXX"
|
149
|
+
end
|
150
|
+
assert_received(auth) {|a| a.new }
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class TestClient < Test::Unit::TestCase
|
2
|
+
def setup
|
3
|
+
stub(Yao.config).debug { false }
|
4
|
+
stub(Yao.config).debug_record_response { false }
|
5
|
+
end
|
6
|
+
|
7
|
+
def test_gen_client
|
8
|
+
cli = Yao::Client.gen_client("http://cool-api.example.com:12345/v3.0")
|
9
|
+
assert { cli.url_prefix.to_s == "http://cool-api.example.com:12345/v3.0" }
|
10
|
+
|
11
|
+
handlers = [
|
12
|
+
Faraday::Request::UrlEncoded,
|
13
|
+
FaradayMiddleware::EncodeJson,
|
14
|
+
Faraday::Response::OSErrorDetector,
|
15
|
+
FaradayMiddleware::ParseJson,
|
16
|
+
Faraday::Adapter::NetHttp
|
17
|
+
]
|
18
|
+
assert { cli.builder.handlers == handlers }
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_gen_with_token
|
22
|
+
cli = Yao::Client.gen_client("http://cool-api.example.com:12345/v3.0", token: "deadbeaf")
|
23
|
+
handlers = [
|
24
|
+
Faraday::Request::UrlEncoded,
|
25
|
+
FaradayMiddleware::EncodeJson,
|
26
|
+
Faraday::Request::OSToken,
|
27
|
+
Faraday::Response::OSErrorDetector,
|
28
|
+
FaradayMiddleware::ParseJson,
|
29
|
+
Faraday::Adapter::NetHttp
|
30
|
+
]
|
31
|
+
assert { cli.builder.handlers == handlers }
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_debug_mode
|
35
|
+
stub(Yao.config).debug { true }
|
36
|
+
|
37
|
+
cli = Yao::Client.gen_client("http://cool-api.example.com:12345/v3.0")
|
38
|
+
handlers = [
|
39
|
+
Faraday::Request::UrlEncoded,
|
40
|
+
FaradayMiddleware::EncodeJson,
|
41
|
+
Faraday::Response::OSErrorDetector,
|
42
|
+
FaradayMiddleware::ParseJson,
|
43
|
+
Faraday::Response::Logger,
|
44
|
+
Faraday::Response::OSDumper,
|
45
|
+
Faraday::Adapter::NetHttp
|
46
|
+
]
|
47
|
+
assert { cli.builder.handlers == handlers }
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class TestConfig < Test::Unit::TestCase
|
2
|
+
def test_define_param
|
3
|
+
assert { !Yao.config.respond_to?(:foobar) }
|
4
|
+
|
5
|
+
Yao.config.param :foobar, nil
|
6
|
+
assert { Yao.config.respond_to?(:foobar) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_raise_undefined_key
|
10
|
+
e = nil
|
11
|
+
begin
|
12
|
+
Yao.config.set :some_imaginary_key, 123
|
13
|
+
rescue => e
|
14
|
+
end
|
15
|
+
assert { e.message == "Undefined config key some_imaginary_key" }
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_define_hook
|
19
|
+
hooked_value = nil
|
20
|
+
|
21
|
+
Yao.config.param :do_hook, "sample" do |v|
|
22
|
+
hooked_value = v
|
23
|
+
end
|
24
|
+
assert("param definition should not hook callbacks") do
|
25
|
+
hooked_value.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
Yao.config.set :do_hook, "true value"
|
29
|
+
assert { hooked_value = "true value" }
|
30
|
+
|
31
|
+
Yao.config.do_hook("next value")
|
32
|
+
assert { Yao.config.do_hook == "next value" }
|
33
|
+
assert { hooked_value == "next value" }
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_delayed_hook
|
37
|
+
hooked_value = nil
|
38
|
+
Yao.config.param :def_hook1, "test" do |v|
|
39
|
+
if Yao.config.def_hook2
|
40
|
+
hooked_value = v
|
41
|
+
end
|
42
|
+
end
|
43
|
+
Yao.config.param :def_hook2, false
|
44
|
+
|
45
|
+
Yao.configure do
|
46
|
+
def_hook1 "test 2"
|
47
|
+
def_hook2 true
|
48
|
+
end
|
49
|
+
|
50
|
+
assert { hooked_value == "test 2" }
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_auth_is_hooked
|
54
|
+
auth = Yao::Auth
|
55
|
+
count = Yao::Config::HOOK_RENEW_CLIENT_KEYS.size
|
56
|
+
mock(auth).try_new.times(count)
|
57
|
+
Yao::Config::HOOK_RENEW_CLIENT_KEYS.each do |key|
|
58
|
+
Yao.configure do
|
59
|
+
set key, "dummy"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
assert_received(auth) {|a| a.try_new.times(count) }
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class TestServerError < Test::Unit::TestCase
|
2
|
+
def test_detects_error_with_env
|
3
|
+
env = Faraday::Env.new
|
4
|
+
env.body = {"itemNotFound"=>{"message"=>"Image not found.", "code"=>404}}
|
5
|
+
env.status = 404
|
6
|
+
|
7
|
+
error = Yao::ServerError.detect(env)
|
8
|
+
assert { error.is_a? Yao::ItemNotFound }
|
9
|
+
assert { error.env.is_a? Faraday::Env }
|
10
|
+
assert { error.status == 404 }
|
11
|
+
assert { error.env.body["itemNotFound"]["message"] == "Image not found." }
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_anyway_returns_error
|
15
|
+
env = Faraday::Env.new
|
16
|
+
env.body = "<html>Not found.</html>"
|
17
|
+
env.status = 599
|
18
|
+
|
19
|
+
error = Yao::ServerError.detect(env)
|
20
|
+
assert { error.is_a? Yao::ServerError }
|
21
|
+
assert { error.env.is_a? Faraday::Env }
|
22
|
+
assert { error.status == 599 }
|
23
|
+
assert { error.message == "Something is wrong. - \"<html>Not found.</html>\"" }
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class TestToken < Test::Unit::TestCase
|
2
|
+
def setup
|
3
|
+
stub(Yao.config).debug { false }
|
4
|
+
stub(Yao.config).debug_record_response { false }
|
5
|
+
end
|
6
|
+
|
7
|
+
def test_expired
|
8
|
+
t = Yao::Token.new({})
|
9
|
+
t.register({
|
10
|
+
"id" => "aaaa166533fd49f3b11b1cdce2430000",
|
11
|
+
"issued_at" => Time.now.iso8601,
|
12
|
+
"expires" => (Time.now - 3600).utc.iso8601
|
13
|
+
})
|
14
|
+
|
15
|
+
assert { t.expired? }
|
16
|
+
|
17
|
+
t.register({
|
18
|
+
"id" => "aaaa166533fd49f3b11b1cdce2430000",
|
19
|
+
"issued_at" => Time.now.iso8601,
|
20
|
+
"expires" => (Time.now + 3600).utc.iso8601
|
21
|
+
})
|
22
|
+
assert { ! t.expired? }
|
23
|
+
end
|
24
|
+
|
25
|
+
AUTH_JSON = \
|
26
|
+
"{\"auth\":{\"passwordCredentials\":{\"username\":\"udzura\",\"password\":\"XXXXXXXX\"},\"tenantName\":\"example\"}}"
|
27
|
+
|
28
|
+
def gen_response_json
|
29
|
+
<<-JSON
|
30
|
+
{
|
31
|
+
"access": {
|
32
|
+
"token": {
|
33
|
+
"issued_at": "#{Time.now.iso8601}",
|
34
|
+
"expires": "#{(Time.now + 3600).utc.iso8601}",
|
35
|
+
"id": "aaaa166533fd49f3b11b1cdce2430000",
|
36
|
+
"tenant": {
|
37
|
+
"description": "Testing",
|
38
|
+
"enabled": true,
|
39
|
+
"id": "aaaa166533fd49f3b11b1cdce2430000",
|
40
|
+
"name": "example"
|
41
|
+
}
|
42
|
+
},
|
43
|
+
"serviceCatalog": []
|
44
|
+
}
|
45
|
+
}
|
46
|
+
JSON
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_reflesh
|
50
|
+
auth_info = {
|
51
|
+
auth: {
|
52
|
+
passwordCredentials: {
|
53
|
+
username: "udzura", password: "XXXXXXXX"
|
54
|
+
},
|
55
|
+
tenantName: "example"
|
56
|
+
}
|
57
|
+
}
|
58
|
+
t = Yao::Token.new(auth_info)
|
59
|
+
t.register({
|
60
|
+
"id" => "old_token",
|
61
|
+
"issued_at" => Time.now.iso8601,
|
62
|
+
"expires" => (Time.now - 3600).utc.iso8601
|
63
|
+
})
|
64
|
+
assert { t.token == "old_token" }
|
65
|
+
|
66
|
+
stub_request(:post, "http://endpoint.example.com:12345/v2.0/tokens").with(body: AUTH_JSON)
|
67
|
+
.to_return(:status => 200, :body => gen_response_json, :headers => {'Content-Type' => 'application/json'})
|
68
|
+
|
69
|
+
Yao.config.auth_url "http://endpoint.example.com:12345"
|
70
|
+
t.reflesh(Yao.default_client.default)
|
71
|
+
|
72
|
+
assert { t.token == "aaaa166533fd49f3b11b1cdce2430000" }
|
73
|
+
end
|
74
|
+
end
|
data/yao.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yao
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.2
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Uchio, KONDO
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -136,6 +136,20 @@ dependencies:
|
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: webmock
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
139
153
|
description: YAO is a Yet Another OpenStack API Wrapper that rocks!!
|
140
154
|
email:
|
141
155
|
- udzura@udzura.jp
|
@@ -144,6 +158,7 @@ extensions: []
|
|
144
158
|
extra_rdoc_files: []
|
145
159
|
files:
|
146
160
|
- ".gitignore"
|
161
|
+
- ".travis.yml"
|
147
162
|
- Gemfile
|
148
163
|
- LICENSE.txt
|
149
164
|
- README.md
|
@@ -169,7 +184,16 @@ files:
|
|
169
184
|
- lib/yao/resources/security_group_rule.rb
|
170
185
|
- lib/yao/resources/server.rb
|
171
186
|
- lib/yao/resources/subnet.rb
|
187
|
+
- lib/yao/resources/tenant.rb
|
188
|
+
- lib/yao/server_error.rb
|
189
|
+
- lib/yao/token.rb
|
172
190
|
- lib/yao/version.rb
|
191
|
+
- test/config.rb
|
192
|
+
- test/yao/test_auth.rb
|
193
|
+
- test/yao/test_client.rb
|
194
|
+
- test/yao/test_config.rb
|
195
|
+
- test/yao/test_server_error.rb
|
196
|
+
- test/yao/test_token.rb
|
173
197
|
- yao.gemspec
|
174
198
|
homepage: https://github.com/udzura/yao
|
175
199
|
licenses:
|
@@ -186,13 +210,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
186
210
|
version: '0'
|
187
211
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
188
212
|
requirements:
|
189
|
-
- - "
|
213
|
+
- - ">="
|
190
214
|
- !ruby/object:Gem::Version
|
191
|
-
version:
|
215
|
+
version: '0'
|
192
216
|
requirements: []
|
193
217
|
rubyforge_project:
|
194
218
|
rubygems_version: 2.2.3
|
195
219
|
signing_key:
|
196
220
|
specification_version: 4
|
197
221
|
summary: Yet Another OpenStack API Wrapper that rocks!!
|
198
|
-
test_files:
|
222
|
+
test_files:
|
223
|
+
- test/config.rb
|
224
|
+
- test/yao/test_auth.rb
|
225
|
+
- test/yao/test_client.rb
|
226
|
+
- test/yao/test_config.rb
|
227
|
+
- test/yao/test_server_error.rb
|
228
|
+
- test/yao/test_token.rb
|