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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 04c45b85c7164bc9cd7bac93189eec8c2aff4712
4
- data.tar.gz: cb05d894031f89862beba5e27c372ba7ec09d8e3
3
+ metadata.gz: 4efa85ede55eb2f96de87fba1af6fcbcb3ec3228
4
+ data.tar.gz: 860deb926f0bd3dd5fe4addca1ff152ee7902e18
5
5
  SHA512:
6
- metadata.gz: a4745f172f77fd7fa273f2253ab94394b6223d5d26fd0d10c42cd809bea46b05b3059cf9cb260acb8fdc7eb6cad0303aff87c68321ebbb15dda2a86b615a47ec
7
- data.tar.gz: 415e8fcd67ca260f14ec1105d83197680c0c999a7d1c5f07ee2ea00263a0c99d9c5401aaddde7b7d77b7d99153d2c858914481f885d9ed98fb3ab0fa83b6b258
6
+ metadata.gz: a3db94bfb4a5c854c186a63521371a7ef00be604080a2ebf932dd54af4cec5ef6e0eee1cce840c98453a3e055b051921eb5e33ae9cf587db7dc5aa29314dd0a2
7
+ data.tar.gz: a661e89dec7b324abe55fd5c7c74affe9406767a32fb2a1b92a0e64d9d7bc9f6fa48c78ab701c3bfbbd79298db0178c162997b43a45e7f46998f2849123e236a
@@ -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
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in yao.gemspec
4
4
  gemspec
5
+
6
+ gem 'travis'
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Yao
2
2
 
3
- YAO is a Yet Another OpenStack API Wrapper that rocks!!
3
+ YAO is a Yet Another OpenStack API Wrapper that rocks!! [![Build Status](https://travis-ci.org/udzura/yao.svg)](https://travis-ci.org/udzura/yao)
4
4
 
5
5
  ## Installation
6
6
 
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
@@ -2,73 +2,37 @@ require 'yao'
2
2
  require 'json'
3
3
  require 'time'
4
4
 
5
- module Yao::Auth
5
+ require 'yao/token'
6
+
7
+ module Yao
6
8
  %i(tenant_name username password).each do |name|
7
- Yao.config.param name, nil do |_|
8
- Yao::Auth.try_new
9
- end
9
+ Yao.config.param name, nil
10
10
  end
11
11
 
12
- class << self
13
- def try_new
14
- if Yao.config.tenant_name && Yao.config.username && Yao.config.password && Yao.default_client
15
- Yao::Auth.new
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
- def new(
20
- tenant_name: Yao.config.tenant_name,
21
- username: Yao.config.username,
22
- password: Yao.config.password
23
- )
24
- authinfo = {
25
- auth: {
26
- passwordCredentials: {
27
- username: username, password: password
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
- reply = Yao.default_client.default.post('/v2.0/tokens') do |req|
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
@@ -33,8 +33,8 @@ module Yao
33
33
  class << self
34
34
  attr_accessor :default_client
35
35
 
36
- def gen_client(endpoint, token: nil)
37
- Faraday.new( endpoint ) do |f|
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
- f.response :logger
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
@@ -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
- _configuration_hooks[name].call(value) if _configuration_hooks[name]
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
- @__config.instance_eval(&blk) if blk
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
- env[:request_headers]['X-Auth-Token'] = @token
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 }
@@ -13,6 +13,7 @@ module Yao
13
13
  autoload :Network, "yao/resources/network"
14
14
  autoload :Subnet, "yao/resources/subnet"
15
15
  autoload :Port, "yao/resources/port"
16
+ autoload :Tenant, "yao/resources/tenant"
16
17
 
17
18
  end
18
19
 
@@ -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])
@@ -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,9 @@
1
+ module Yao::Resources
2
+ class Tenant < Base
3
+ friendly_attributes :id, :name, :description, :enabled
4
+
5
+ self.service = "identity"
6
+ self.resource_name = "tenant"
7
+ self.resources_name = "tenants"
8
+ end
9
+ end
@@ -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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Yao
2
- VERSION = "0.0.2.rc3"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,9 @@
1
+ require 'test/unit'
2
+ require 'test/unit/rr'
3
+ require 'power_assert'
4
+ require 'yao'
5
+
6
+ require 'webmock/test_unit'
7
+
8
+ WebMock.disable_net_connect!
9
+ #WebMock.allow_net_connect!
@@ -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
@@ -28,4 +28,5 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency "test-unit-rr"
29
29
  spec.add_development_dependency "power_assert"
30
30
  spec.add_development_dependency "pry"
31
+ spec.add_development_dependency "webmock"
31
32
  end
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.rc3
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-08-26 00:00:00.000000000 Z
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: 1.3.1
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