yao 0.0.2.rc3 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|