misty 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '0108d5bb9451b9500d689c21f6cf43b4ae90186c'
4
- data.tar.gz: a6d0744f4806007d55044ad7fe3f96e4be645ec0
3
+ metadata.gz: 8f77fca7e84ccf06eb7cf92b3c2ee1a9233d0427
4
+ data.tar.gz: d8d73c45718a708d0da3f5bf076aa0cc802c0f4c
5
5
  SHA512:
6
- metadata.gz: b73c8d875c03a7495c5e84be31ca00e9b6b13667d0a576334e710f76a6586ec47a7400022ef29f6e896cea866793ac2d41f174cbfb9b81b6b016137875957335
7
- data.tar.gz: 8df0ce513d52b08551537a0fe6dc702ce1d6f7e9f06ac43082c1f14a7c27d33952d136273592a8eb8ba6a936077c9eca8450bc49a897ef3dfcc85caae4d5a943
6
+ metadata.gz: 5a98bcc69158a796d55ee9ce9ba7ccb5cc6d6f99bc0fc9217137e00905a243cc1608aea11dd9dc996423dcfdc9c2ad25a1435bc17cbbe66cfc03c1c9e491ed78
7
+ data.tar.gz: 72475d27ad43c025522654ce010b520a837cfd70c403ae8ac70b632300ab7c900e03eda7f860d2fe53b095ce6324e3438304143e9957ff4866ee8c7849b68b84
data/README.md CHANGED
@@ -152,6 +152,10 @@ The following parameters can be used:
152
152
  User domain id
153
153
  * `:user_domain`
154
154
  User domain name
155
+ * `:password`
156
+ Password for user. Cannot be used together with `:token`.
157
+ * `:token`
158
+ Previous Keystone v3 token for user. Can only be used with Keystone v3. Overrides all user and password parameters.
155
159
 
156
160
  #### Keystone v3
157
161
  Keystone v3 is default recommended version:
@@ -212,10 +216,6 @@ The following options are applied to each service unless specifically provided f
212
216
  Type: String
213
217
  Allowed values: "public", "internal", "admin"
214
218
  Default: "public"
215
- * :http_proxy
216
- For example: `"http://userid:password@somewhere.com:8080/"` or `ENV["http_proxy"]`
217
- Type: String
218
- Default: `""`
219
219
  * :ssl_verify_mode
220
220
  When using SSL mode (defined by URI scheme => "https://")
221
221
  Type: Boolean
@@ -276,8 +276,12 @@ openstack.network.post("/v2.0/qos/policies/48985e6b8da145699d411f12a3459fca/dscp
276
276
  # Requirements
277
277
 
278
278
  ## Ruby versions tested
279
+ * Ruby 2.4.1
279
280
  * Ruby 2.4.0
281
+ * Ruby 2.3.4
280
282
  * Ruby 2.3.3
283
+ * Ruby 2.3.2
284
+ * Ruby 2.3.1
281
285
  * Ruby 2.3.0
282
286
 
283
287
  # Contributing
@@ -14,31 +14,27 @@ module Misty
14
14
 
15
15
  include Misty::HTTP::NetHTTP
16
16
 
17
- def self.factory(options, *args)
18
- if options[:tenant_id] || options[:tenant]
19
- return Misty::AuthV2.new(options, *args)
17
+ attr_reader :catalog
18
+
19
+ def self.factory(auth, config)
20
+ if auth[:tenant_id] || auth[:tenant]
21
+ return Misty::AuthV2.new(auth, config)
20
22
  else
21
- return Misty::AuthV3.new(options, *args)
23
+ return Misty::AuthV3.new(auth, config)
22
24
  end
23
25
  end
24
26
 
25
- attr_reader :catalog
26
-
27
- def initialize(options, proxy, ssl_verify_mode, log)
28
- @ssl_verify_mode, @log = ssl_verify_mode, log
29
- @credentials = scoped_authentication
30
-
31
- raise URLError, "No URL provided" unless options[:url] && !options[:url].empty?
32
- @uri = URI.parse(options[:url])
33
- @proxy = proxy
27
+ def initialize(auth, config)
28
+ raise URLError, "No URL provided" unless auth[:url] && !auth[:url].empty?
29
+ @credentials = set_credentials(auth)
30
+ @http = net_http(URI.parse(auth[:url]), config.ssl_verify_mode, config.log)
34
31
  @token = nil
35
- setup(authenticate)
32
+ @token, @catalog, @expires = set(authenticate)
36
33
  raise CatalogError, "No catalog provided during authentication" if @catalog.empty?
37
34
  end
38
35
 
39
36
  def authenticate
40
- http = net_http(@uri, @proxy, @ssl_verify_mode, @log)
41
- response = http.post(self.class.path, @credentials.to_json, Misty::HEADER_JSON)
37
+ response = @http.post(self.class.path, @credentials.to_json, Misty::HEADER_JSON)
42
38
  raise AuthenticationError, "Response code=#{response.code}, Msg=#{response.msg}" unless response.code =~ /200|201/
43
39
  response
44
40
  end
@@ -58,7 +54,7 @@ module Misty
58
54
  end
59
55
 
60
56
  def get_token
61
- authenticate if expired?
57
+ @token, @catalog, @expires = set(authenticate) if expired?
62
58
  @token
63
59
  end
64
60
  end
@@ -6,13 +6,6 @@ module Misty
6
6
  "/v2.0/tokens"
7
7
  end
8
8
 
9
- def initialize(options, *args)
10
- @user = Misty::Auth::User.new(options[:user_id], options[:user])
11
- @user.password = options[:password]
12
- @tenant = Misty::Auth::Name.new(options[:tenant_id], options[:tenant])
13
- super(options, *args)
14
- end
15
-
16
9
  def catalog_endpoints(endpoints, region, interface)
17
10
  endpoints.each do |endpoint|
18
11
  if endpoint["region"] == region && endpoint["#{interface}URL"]
@@ -21,50 +14,58 @@ module Misty
21
14
  end
22
15
  end
23
16
 
24
- def get_endpoint_url(endpoints, region, interface)
25
- endpoint = endpoints.select { |ep| !ep[interface].empty? }
26
- raise CatalogError, "No endpoint available for region '#{region}' and interface '#{interface}'" unless endpoint
27
- endpoint[0][interface]
28
- end
29
-
30
- def setup(response)
31
- payload = JSON.load(response.body)
32
- @token = payload["access"]["token"]["id"]
33
- @catalog = payload["access"]["serviceCatalog"]
34
- @expires = payload["access"]["token"]["expires"]
35
- end
36
-
37
- def scoped_authentication
17
+ def credentials
38
18
  raise Misty::Auth::CredentialsError, "#{self.class}: User name is required" if @user.name.nil?
39
19
  raise Misty::Auth::CredentialsError, "#{self.class}: User password is required" if @user.password.nil?
40
- return auth_by_id if @tenant.id
41
- return auth_by_name if @tenant.name
20
+ return creds_by_id if @tenant.id
21
+ return creds_by_name if @tenant.name
42
22
  raise Misty::Auth::CredentialsError, "#{self.class}: No tenant available"
43
23
  end
44
24
 
45
- def auth_by_name
25
+ def credentials_data
26
+ {
27
+ "username": @user.name,
28
+ "password": @user.password
29
+ }
30
+ end
31
+
32
+ def creds_by_name
46
33
  {
47
34
  "auth": {
48
- "passwordCredentials": credentials,
35
+ "passwordCredentials": credentials_data,
49
36
  "tenantName": @tenant.name
50
37
  }
51
38
  }
52
39
  end
53
40
 
54
- def auth_by_id
41
+ def creds_by_id
55
42
  {
56
43
  "auth": {
57
- "passwordCredentials": credentials,
44
+ "passwordCredentials": credentials_data,
58
45
  "tenantId": @tenant.id
59
46
  }
60
47
  }
61
48
  end
62
49
 
63
- def credentials
64
- {
65
- "username": @user.name,
66
- "password": @user.password
67
- }
50
+ def get_endpoint_url(endpoints, region, interface)
51
+ endpoint = endpoints.select { |ep| !ep[interface].empty? }
52
+ raise CatalogError, "No endpoint available for region '#{region}' and interface '#{interface}'" unless endpoint
53
+ endpoint[0][interface]
54
+ end
55
+
56
+ def set(response)
57
+ payload = JSON.load(response.body)
58
+ token = payload["access"]["token"]["id"]
59
+ catalog = payload["access"]["serviceCatalog"]
60
+ expires = payload["access"]["token"]["expires"]
61
+ [token, catalog, expires]
62
+ end
63
+
64
+ def set_credentials(auth)
65
+ @user = Misty::Auth::User.new(auth[:user_id], auth[:user])
66
+ @user.password = auth[:password]
67
+ @tenant = Misty::Auth::Name.new(auth[:tenant_id], auth[:tenant])
68
+ credentials
68
69
  end
69
70
  end
70
71
  end
@@ -2,26 +2,6 @@ require 'misty/auth'
2
2
 
3
3
  module Misty
4
4
  class AuthV3 < Misty::Auth
5
- def initialize(options, *args)
6
- if options[:project_id] || options[:project]
7
- # scope: project
8
- project_domain_id = options[:project_domain_id] ? options[:project_domain_id] : Misty::DOMAIN_ID
9
- @project = Misty::Auth::ProjectScope.new(options[:project_id], options[:project])
10
- @project.domain = Misty::Auth::Name.new(project_domain_id, options[:user_domain])
11
- else
12
- # scope: domain
13
- domain_id = options[:domain_id] ? options[:domain_id] : Misty::DOMAIN_ID
14
- @domain = Misty::Auth::DomainScope.new(domain_id, options[:domain]) if domain_id || options[:domain]
15
- end
16
-
17
- user_domain_id = options[:user_domain_id] ? options[:user_domain_id] : Misty::DOMAIN_ID
18
- @user = Misty::Auth::User.new(options[:user_id], options[:user])
19
- @user.password = options[:password]
20
- @user.domain = Misty::Auth::Name.new(user_domain_id, options[:user_domain])
21
-
22
- super(options, *args)
23
- end
24
-
25
5
  def self.path
26
6
  "/v3/auth/tokens"
27
7
  end
@@ -34,35 +14,68 @@ module Misty
34
14
  end
35
15
  end
36
16
 
37
- def get_endpoint_url(endpoints, region, interface)
38
- endpoint = endpoints.select { |ep| ep["region_id"] == region && ep["interface"] == interface }
39
- raise CatalogError, "No endpoint available for region '#{region}' and interface '#{interface}'" unless endpoint
40
- endpoint[0]["url"]
41
- end
42
-
43
- def scoped_authentication
17
+ def credentials
18
+ if @token
19
+ identity = {
20
+ "methods": ["token"],
21
+ "token": { "id": @token }
22
+ }
23
+ else
24
+ identity = {
25
+ "methods": ["password"],
26
+ "password": @user.identity
27
+ }
28
+ end
44
29
  {
45
30
  "auth": {
46
- "identity": {
47
- "methods": ["password"],
48
- "password": @user.identity
49
- },
31
+ "identity": identity,
50
32
  "scope": scope
51
33
  }
52
34
  }
53
35
  end
54
36
 
37
+ def get_endpoint_url(endpoints, region, interface)
38
+ endpoint = endpoints.select { |ep| ep["region_id"] == region && ep["interface"] == interface }
39
+ raise CatalogError, "No endpoint available for region '#{region}' and interface '#{interface}'" unless endpoint
40
+ endpoint[0]["url"]
41
+ end
42
+
55
43
  def scope
56
44
  return @project.identity if @project
57
45
  return @domain.identity if @domain
58
46
  raise Misty::Auth::CredentialsError, "#{self.class}: No scope available"
59
47
  end
60
48
 
61
- def setup(response)
49
+ def set(response)
62
50
  payload = JSON.load(response.body)
63
- @token = response["x-subject-token"]
64
- @catalog = payload["token"]["catalog"]
65
- @expires = payload["token"]["expires_at"]
51
+ token = response["x-subject-token"]
52
+ catalog = payload["token"]["catalog"]
53
+ expires = payload["token"]["expires_at"]
54
+ [token, catalog, expires]
55
+ end
56
+
57
+ def set_credentials(auth)
58
+ if auth[:project_id] || auth[:project]
59
+ # scope: project
60
+ project_domain_id = auth[:project_domain_id] ? auth[:project_domain_id] : Misty::DOMAIN_ID
61
+ @project = Misty::Auth::ProjectScope.new(auth[:project_id], auth[:project])
62
+ @project.domain = Misty::Auth::Name.new(project_domain_id, auth[:user_domain])
63
+ else
64
+ # scope: domain
65
+ domain_id = auth[:domain_id] ? auth[:domain_id] : Misty::DOMAIN_ID
66
+ @domain = Misty::Auth::DomainScope.new(domain_id, auth[:domain]) if domain_id || auth[:domain]
67
+ end
68
+
69
+ if auth[:token]
70
+ @token = auth[:token]
71
+ else
72
+ user_domain_id = auth[:user_domain_id] ? auth[:user_domain_id] : Misty::DOMAIN_ID
73
+ @user = Misty::Auth::User.new(auth[:user_id], auth[:user])
74
+ @user.password = auth[:password]
75
+ @user.domain = Misty::Auth::Name.new(user_domain_id, auth[:user_domain])
76
+ end
77
+
78
+ credentials
66
79
  end
67
80
  end
68
81
  end
@@ -3,62 +3,39 @@ require 'misty/auth/auth_v3'
3
3
 
4
4
  module Misty
5
5
  class Cloud
6
- class Setup
7
- attr_accessor :auth, :content_type, :log, :interface, :proxy, :region_id, :ssl_verify_mode
6
+ class Config
7
+ attr_accessor :auth, :content_type, :interface, :log, :region_id, :ssl_verify_mode
8
8
  end
9
9
 
10
- Options = Struct.new(:alarming, :baremetal, :block_storage, :clustering, :compute, :container, :data_processing,
11
- :database, :data_protection, :dns, :identity, :image, :messaging, :metering, :network, :object_storage,
12
- :orchestration, :search, :shared_file_systems)
13
-
14
- attr_reader :services
15
-
16
- def initialize(params = {:auth => {}})
17
- @setup = self.class.setup(params)
18
- @options = Options.new
19
- @services = setup_services(params)
10
+ def self.dot_to_underscore(val)
11
+ val.gsub(/\./,'_')
20
12
  end
21
13
 
22
- def self.setup(params)
23
- setup = Setup.new
24
- setup.content_type = params[:content_type] ? params[:content_type] : Misty::CONTENT_TYPE
25
- setup.interface = params[:interface] ? params[:interface] : Misty::INTERFACE
26
- setup.log = Logger.new(params[:log_file] ? params[:log_file] : Misty::LOG_FILE)
27
- setup.log.level = params[:log_level] ? params[:log_level] : Misty::LOG_LEVEL
28
- setup.region_id = params[:region_id] ? params[:region_id] : Misty::REGION_ID
29
- setup.ssl_verify_mode = params.key?(:ssl_verify_mode) ? params[:ssl_verify_mode] : Misty::SSL_VERIFY_MODE
30
- http_proxy = params[:http_proxy] ? params[:http_proxy] : ""
31
- setup.proxy = URI.parse(http_proxy)
32
- setup.auth = Misty::Auth.factory(params[:auth], setup.proxy, setup.ssl_verify_mode, setup.log)
33
- setup
14
+ def initialize(params)# = {:auth => {}})
15
+ @params = params
16
+ @config = self.class.set_configuration(params)
17
+ @services = Misty.services
18
+ @auth = Misty::Auth.factory(params[:auth], @config)
34
19
  end
35
20
 
36
- def setup_services(params)
37
- services = {}
38
- Misty.services.each do |service|
39
- @options.send("#{service.name}=".to_sym, params[service.name] ? params[service.name] : {})
40
-
41
- if params[service.name] && params[service.name][:api_version] \
42
- && service.versions.include?(params[service.name][:api_version])
43
- services.merge!(service.name => {service.project => params[service.name][:api_version]})
44
- else
45
- # Highest version is used by default!
46
- services.merge!(service.name => {service.project => service.versions.sort[-1]})
47
- end
48
- end
49
- services
21
+ def self.set_configuration(params)
22
+ config = Config.new
23
+ config.content_type = params[:content_type] ? params[:content_type] : Misty::CONTENT_TYPE
24
+ config.interface = params[:interface] ? params[:interface] : Misty::INTERFACE
25
+ config.log = Logger.new(params[:log_file] ? params[:log_file] : Misty::LOG_FILE)
26
+ config.log.level = params[:log_level] ? params[:log_level] : Misty::LOG_LEVEL
27
+ config.region_id = params[:region_id] ? params[:region_id] : Misty::REGION_ID
28
+ config.ssl_verify_mode = params.key?(:ssl_verify_mode) ? params[:ssl_verify_mode] : Misty::SSL_VERIFY_MODE
29
+ config
50
30
  end
51
31
 
52
32
  def build_service(service_name)
53
- project = @services[service_name].keys[0]
54
- version = @services[service_name].fetch(project)
55
- version = self.class.dot_to_underscore(version)
56
- klass = Object.const_get("Misty::Openstack::#{project.capitalize}::#{version.capitalize}")
57
- klass.new(@setup, @options[service_name])
58
- end
59
-
60
- def self.dot_to_underscore(val)
61
- val.gsub(/\./,'_')
33
+ service = @services.find {|service| service.name == service_name}
34
+ service.options = @params[service.name] if @params[service.name]
35
+ service.version = service.options[:api_version]
36
+ version = self.class.dot_to_underscore(service.version)
37
+ klass = Object.const_get("Misty::Openstack::#{service.project.capitalize}::#{version.capitalize}")
38
+ klass.new(@auth, @config, service.options)
62
39
  end
63
40
 
64
41
  def alarming
@@ -6,6 +6,10 @@ require 'misty/http/direct'
6
6
  module Misty
7
7
  module HTTP
8
8
  class Client
9
+ class Options
10
+ attr_accessor :base_path, :base_url, :interface, :region_id, :service_names, :ssl_verify_mode, :version
11
+ end
12
+
9
13
  class InvalidDataError < StandardError; end
10
14
 
11
15
  include Misty::HTTP::NetHTTP
@@ -17,8 +21,6 @@ module Misty
17
21
 
18
22
  attr_reader :microversion
19
23
 
20
- Options = Struct.new(:base_path, :base_url, :interface, :region_id, :service_names, :ssl_verify_mode, :version)
21
-
22
24
  def requests
23
25
  list = []
24
26
  self.class.api.each do |_path, verbs|
@@ -45,13 +47,14 @@ module Misty
45
47
  # :ssl_verify_mode => true
46
48
  # (micro)version: Can be numbered (3.1) or by state (CURRENT, LATEST or SUPPORTED)
47
49
  # :version => "CURRENT"
48
- def initialize(setup, options)
49
- @setup = setup
50
+ def initialize(auth, config, options)
51
+ @auth = auth
52
+ @config = config
50
53
  @options = setup(options)
51
- @uri = URI.parse(@setup.auth.get_endpoint(@options.service_names, @options.region_id, @options.interface))
54
+ @uri = URI.parse(@auth.get_endpoint(@options.service_names, @options.region_id, @options.interface))
52
55
  @base_path = @options.base_path ? @options.base_path : @uri.path
53
56
  @base_path = @base_path.chomp("/")
54
- @http = net_http(@uri, @setup.proxy, @options[:ssl_verify_mode], @setup.log)
57
+ @http = net_http(@uri, @options.ssl_verify_mode, @config.log)
55
58
  @version = nil
56
59
  @microversion = false
57
60
  end
@@ -72,7 +75,7 @@ module Misty
72
75
  end
73
76
 
74
77
  def headers
75
- h = headers_default.merge("X-Auth-Token" => "#{@setup.auth.get_token}")
78
+ h = headers_default.merge("X-Auth-Token" => "#{@auth.get_token}")
76
79
  h.merge!(microversion_header) if microversion
77
80
  h
78
81
  end
@@ -87,10 +90,10 @@ module Misty
87
90
  options = Options.new()
88
91
  options.base_path = params[:base_path] ? params[:base_path] : nil
89
92
  options.base_url = params[:base_url] ? params[:base_url] : nil
90
- options.interface = params[:interface] ? params[:interface] : @setup.interface
91
- options.region_id = params[:region_id] ? params[:region_id] : @setup.region_id
93
+ options.interface = params[:interface] ? params[:interface] : @config.interface
94
+ options.region_id = params[:region_id] ? params[:region_id] : @config.region_id
92
95
  options.service_names = params[:service_name] ? self.class.service_names << params[:service_name] : self.class.service_names
93
- options.ssl_verify_mode = params[:ssl_verify_mode] ? params[:ssl_verify_mode] : @setup.ssl_verify_mode
96
+ options.ssl_verify_mode = params[:ssl_verify_mode] ? params[:ssl_verify_mode] : @config.ssl_verify_mode
94
97
  options.version = params[:version] ? params[:version] : "CURRENT"
95
98
 
96
99
  unless INTERFACES.include?(options.interface)
@@ -85,11 +85,18 @@ module Misty
85
85
  end
86
86
 
87
87
  def query_param(data)
88
+ result = nil
88
89
  if data.is_a? String
89
- str = '?'
90
- str.force_encoding('ASCII-8BIT')
91
- str << data unless data.empty?
90
+ result = ''
91
+ if data != ''
92
+ result = '?'
93
+ result.force_encoding('ASCII-8BIT')
94
+ result << data
95
+ end
96
+ elsif data.is_a? Hash
97
+ result = data.empty? ? '' : '?' + URI.encode_www_form(data)
92
98
  end
99
+ return result
93
100
  end
94
101
  end
95
102
  end
@@ -1,8 +1,8 @@
1
1
  module Misty
2
2
  module HTTP
3
3
  module NetHTTP
4
- def net_http(endpoint, proxy, ssl_verify_mode, log)
5
- http = Net::HTTP.new(endpoint.host, endpoint.port, proxy.host, proxy.port, proxy.user, proxy.password)
4
+ def net_http(endpoint, ssl_verify_mode, log)
5
+ http = Net::HTTP.new(endpoint.host, endpoint.port)
6
6
  http.set_debug_output(log) if log.level == Logger::DEBUG
7
7
  if endpoint.scheme == "https"
8
8
  http.use_ssl = true
@@ -2,7 +2,7 @@ module Misty
2
2
  module HTTP
3
3
  module Request
4
4
  def decode?(response)
5
- if @setup.content_type != :json && response.code =~ /2??/ && !response.is_a?(Net::HTTPNoContent) \
5
+ if @config.content_type != :json && response.code =~ /2??/ && !response.is_a?(Net::HTTPNoContent) \
6
6
  && !response.is_a?(Net::HTTPResetContent) && response.header["content-type"] \
7
7
  && response.header["content-type"].include?("application/json")
8
8
  true
@@ -16,51 +16,51 @@ module Misty
16
16
  end
17
17
 
18
18
  def http_delete(path, headers)
19
- @setup.log.info(http_to_s(path, headers))
19
+ @config.log.info(http_to_s(path, headers))
20
20
  request = Net::HTTP::Delete.new(path, headers)
21
21
  http(request)
22
22
  end
23
23
 
24
24
  def http_copy(path, headers)
25
- @setup.log.info(http_to_s(path, headers))
25
+ @config.log.info(http_to_s(path, headers))
26
26
  request = Net::HTTP::Copy.new(path, headers)
27
27
  http(request)
28
28
  end
29
29
 
30
30
  def http_get(path, headers)
31
- @setup.log.info(http_to_s(path, headers))
31
+ @config.log.info(http_to_s(path, headers))
32
32
  request = Net::HTTP::Get.new(path, headers)
33
33
  http(request)
34
34
  end
35
35
 
36
36
  def http_head(path, headers)
37
- @setup.log.info(http_to_s(path, headers))
37
+ @config.log.info(http_to_s(path, headers))
38
38
  request = Net::HTTP::Head.new(path, headers)
39
39
  http(request)
40
40
  end
41
41
 
42
42
  def http_options(path, headers)
43
- @setup.log.info(http_to_s(path, headers))
43
+ @config.log.info(http_to_s(path, headers))
44
44
  request = Net::HTTP::Options.new(path, headers)
45
45
  http(request)
46
46
  end
47
47
 
48
48
  def http_patch(path, headers, data)
49
- @setup.log.info(http_to_s(path, headers, data))
49
+ @config.log.info(http_to_s(path, headers, data))
50
50
  request = Net::HTTP::Patch.new(path, headers)
51
51
  request.body = Misty.to_json(data)
52
52
  http(request)
53
53
  end
54
54
 
55
55
  def http_post(path, headers, data)
56
- @setup.log.info(http_to_s(path, headers, data))
56
+ @config.log.info(http_to_s(path, headers, data))
57
57
  request = Net::HTTP::Post.new(path, headers)
58
58
  request.body = Misty.to_json(data)
59
59
  http(request)
60
60
  end
61
61
 
62
62
  def http_put(path, headers, data)
63
- @setup.log.info(http_to_s(path, headers, data))
63
+ @config.log.info(http_to_s(path, headers, data))
64
64
  request = Net::HTTP::Put.new(path, headers)
65
65
  request.body = Misty.to_json(data)
66
66
  http(request)
@@ -1,3 +1,5 @@
1
+ require 'misty/services'
2
+
1
3
  module Misty
2
4
  HEADER_JSON = {
3
5
  "Content-Type" => "application/json",
@@ -26,36 +28,28 @@ module Misty
26
28
  # Default mode when SSL is used (uri.scheme == "https")
27
29
  SSL_VERIFY_MODE = true
28
30
 
29
- Service = Struct.new(:name, :project, :versions) do
30
- def to_s
31
- "#{name}: #{project} => #{versions}"
32
- end
33
- end
34
-
35
- SERVICES = []
36
- SERVICES << Service.new(:alarming, :aodh, ["v2"])
37
- SERVICES << Service.new(:baremetal, :ironic, ["v1"])
38
- SERVICES << Service.new(:block_storage, :cinder, ["v3", "v1"])
39
- SERVICES << Service.new(:clustering, :senlin, ["v1"])
40
- SERVICES << Service.new(:compute, :nova, ["v2.1"])
41
- SERVICES << Service.new(:container, :magnum, ["v1"])
42
- SERVICES << Service.new(:data_processing, :sahara, ["v1.1"])
43
- SERVICES << Service.new(:data_protection, :karbor, ["v1"])
44
- SERVICES << Service.new(:database, :trove, ["v1.0"])
45
- SERVICES << Service.new(:dns, :designate, ["v2"])
46
- SERVICES << Service.new(:identity, :keystone, ["v3", "v2.0"])
47
- SERVICES << Service.new(:image, :glance, ["v2", "v1"])
48
- SERVICES << Service.new(:messaging, :zaqar, ["v2"])
49
- SERVICES << Service.new(:metering, :ceilometer, ["v2"])
50
- SERVICES << Service.new(:network, :neutron, ["v2.0"])
51
- SERVICES << Service.new(:object_storage, :swift, ["v1"])
52
- SERVICES << Service.new(:orchestration, :heat, ["v1"])
53
- SERVICES << Service.new(:search, :searchlight, ["v1"])
54
- SERVICES << Service.new(:shared_file_systems, :manila, ["v2"])
55
- SERVICES.freeze
56
-
57
31
  def self.services
58
- SERVICES
32
+ services = Misty::Services.new
33
+ services.add(:alarming, :aodh, ["v2"])
34
+ services.add(:baremetal, :ironic, ["v1"])
35
+ services.add(:block_storage, :cinder, ["v3", "v1"])
36
+ services.add(:clustering, :senlin, ["v1"])
37
+ services.add(:compute, :nova, ["v2.1"])
38
+ services.add(:container, :magnum, ["v1"])
39
+ services.add(:data_processing, :sahara, ["v1.1"])
40
+ services.add(:data_protection, :karbor, ["v1"])
41
+ services.add(:database, :trove, ["v1.0"])
42
+ services.add(:dns, :designate, ["v2"])
43
+ services.add(:identity, :keystone, ["v3", "v2.0"])
44
+ services.add(:image, :glance, ["v2", "v1"])
45
+ services.add(:messaging, :zaqar, ["v2"])
46
+ services.add(:metering, :ceilometer, ["v2"])
47
+ services.add(:network, :neutron, ["v2.0"])
48
+ services.add(:object_storage, :swift, ["v1"])
49
+ services.add(:orchestration, :heat, ["v1"])
50
+ services.add(:search, :searchlight, ["v1"])
51
+ services.add(:shared_file_systems, :manila, ["v2"])
52
+ services
59
53
  end
60
54
 
61
55
  def self.to_json(data)
@@ -6,7 +6,7 @@ module Misty
6
6
 
7
7
  VERSION_STATES = %w{CURRENT LATEST SUPPORTED}
8
8
 
9
- def initialize(cloud, options)
9
+ def initialize(auth, cloud, options)
10
10
  super
11
11
  @microversion = true
12
12
  @version = version_get(@options.version)
@@ -0,0 +1,49 @@
1
+ module Misty
2
+ class Services
3
+ class Service
4
+ attr_reader :name, :options, :project, :versions, :version
5
+
6
+ def initialize(name, project, versions)
7
+ @name = name
8
+ @project = project
9
+ @versions = versions
10
+ @options = {}
11
+ end
12
+
13
+ def options=(val)
14
+ @options = val
15
+ end
16
+
17
+ def version=(val)
18
+ if @versions.include?(val)
19
+ @version = val
20
+ else
21
+ # Use highest version
22
+ @version = versions.sort[-1]
23
+ end
24
+ end
25
+
26
+ def to_s
27
+ "#{name}: #{project} => #{versions}"
28
+ end
29
+ end
30
+
31
+ include Enumerable
32
+
33
+ attr_reader :services
34
+
35
+ def initialize
36
+ @services = []
37
+ end
38
+
39
+ def add(*args)
40
+ @services << Service.new(*args)
41
+ end
42
+
43
+ def each
44
+ @services.each do |service|
45
+ yield service
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module Misty
2
- VERSION = "0.5.1"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -2,11 +2,16 @@ require 'test_helper'
2
2
  require 'auth_helper'
3
3
 
4
4
  describe Misty::Auth do
5
- let(:log) { Logger.new('/dev/null') }
5
+ let(:config) do
6
+ config = Misty::Cloud::Config.new
7
+ config.log = Logger.new('/dev/null')
8
+ config.ssl_verify_mode = false
9
+ config
10
+ end
6
11
 
7
12
  describe Misty::AuthV3 do
8
13
  describe "#new" do
9
- it "authenticates using project scoped authorization" do
14
+ it "authenticates with password using project scoped authorization" do
10
15
  auth = {
11
16
  :url => "http://localhost:5000",
12
17
  :user_id => "user_id",
@@ -19,10 +24,10 @@ describe Misty::Auth do
19
24
  :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json'}).
20
25
  to_return(:status => 200, :body => JSON.dump(auth_response_v3("identity", "keystone")), :headers => {"x-subject-token"=>"token_data"})
21
26
 
22
- Misty::AuthV3.new(auth, URI.parse(""), false, log)
27
+ Misty::AuthV3.new(auth, config)
23
28
  end
24
29
 
25
- it "authenticates using domain scoped authorization" do
30
+ it "authenticates with password using domain scoped authorization" do
26
31
  auth = {
27
32
  :url => "http://localhost:5000",
28
33
  :user_id => "user_id",
@@ -34,7 +39,36 @@ describe Misty::Auth do
34
39
  :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json'}).
35
40
  to_return(:status => 200, :body => JSON.dump(auth_response_v3("identity", "keystone")), :headers => {"x-subject-token"=>"token_data"})
36
41
 
37
- Misty::AuthV3.new(auth, URI.parse(""), false, log)
42
+ Misty::AuthV3.new(auth, config)
43
+ end
44
+
45
+ it "authenticates with token using project scoped authorization" do
46
+ auth = {
47
+ :url => "http://localhost:5000",
48
+ :token => "exampletoken",
49
+ :project_id => "project_id"
50
+ }
51
+
52
+ stub_request(:post, "http://localhost:5000/v3/auth/tokens").
53
+ with(:body => "{\"auth\":{\"identity\":{\"methods\":[\"token\"],\"token\":{\"id\":\"exampletoken\"}},\"scope\":{\"project\":{\"id\":\"project_id\"}}}}",
54
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json'}).
55
+ to_return(:status => 200, :body => JSON.dump(auth_response_v3("identity", "keystone")), :headers => {"x-subject-token"=>"token_data"})
56
+
57
+ Misty::AuthV3.new(auth, config)
58
+ end
59
+
60
+ it "authenticates with token using domain scoped authorization" do
61
+ auth = {
62
+ :url => "http://localhost:5000",
63
+ :token => "exampletoken",
64
+ }
65
+
66
+ stub_request(:post, "http://localhost:5000/v3/auth/tokens").
67
+ with(:body => "{\"auth\":{\"identity\":{\"methods\":[\"token\"],\"token\":{\"id\":\"exampletoken\"}},\"scope\":{\"domain\":{\"id\":\"default\"}}}}",
68
+ :headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json'}).
69
+ to_return(:status => 200, :body => JSON.dump(auth_response_v3("identity", "keystone")), :headers => {"x-subject-token"=>"token_data"})
70
+
71
+ Misty::AuthV3.new(auth, config)
38
72
  end
39
73
  end
40
74
 
@@ -53,15 +87,15 @@ describe Misty::Auth do
53
87
  to_return(:status => 200, :body => "{\"token\":{\"catalog\":[]}}", :headers => {"x-subject-token"=>"token_data"})
54
88
 
55
89
  proc do
56
- Misty::AuthV3.new({}, URI.parse(""), false, log)
57
- end.must_raise Misty::Auth::CredentialsError
90
+ Misty::AuthV3.new({}, config)
91
+ end.must_raise Misty::Auth::URLError
58
92
  end
59
93
 
60
94
  it "#get_token" do
61
95
  stub_request(:post, "http://localhost:5000/v3/auth/tokens").
62
96
  to_return(:status => 200, :body => "{\"token\":{\"catalog\":[\"catalog_data\"]}}", :headers => {"x-subject-token"=>"token_data"})
63
97
 
64
- auth = Misty::AuthV3.new(authv3_creds, URI.parse(""), false, log)
98
+ auth = Misty::AuthV3.new(authv3_creds, config)
65
99
  auth.stub :expired?, false do
66
100
  auth.get_token.must_equal "token_data"
67
101
  end
@@ -71,7 +105,7 @@ describe Misty::Auth do
71
105
  stub_request(:post, "http://localhost:5000/v3/auth/tokens").
72
106
  to_return(:status => 200, :body => "{\"token\":{\"catalog\":[\"catalog_data\"]}}", :headers => {"x-subject-token"=>"token_data"})
73
107
 
74
- auth = Misty::AuthV3.new(authv3_creds, URI.parse(""), false, log)
108
+ auth = Misty::AuthV3.new(authv3_creds, config)
75
109
  auth.catalog.must_equal ["catalog_data"]
76
110
  end
77
111
 
@@ -79,7 +113,7 @@ describe Misty::Auth do
79
113
  stub_request(:post, "http://localhost:5000/v3/auth/tokens").
80
114
  to_return(:status => 200, :body => JSON.dump(auth_response_v3("identity", "keystone")), :headers => {"x-subject-token"=>"token_data"})
81
115
 
82
- auth = Misty::AuthV3.new(authv3_creds, URI.parse(""), false, log)
116
+ auth = Misty::AuthV3.new(authv3_creds, config)
83
117
  auth.get_endpoint(%w{identity}, "regionOne", "public").must_equal "http://localhost"
84
118
  end
85
119
  end
@@ -92,8 +126,8 @@ describe Misty::Auth do
92
126
  to_return(:status => 200, :body => "{\"access\":{\"token\":{\"id\":\"token_data\"}}}", :headers => {})
93
127
 
94
128
  proc do
95
- Misty::AuthV2.new({}, URI.parse(""), false, log)
96
- end.must_raise Misty::Auth::CredentialsError
129
+ Misty::AuthV2.new({}, config)
130
+ end.must_raise Misty::Auth::URLError
97
131
  end
98
132
  end
99
133
 
@@ -111,7 +145,7 @@ describe Misty::Auth do
111
145
  stub_request(:post, "http://localhost:5000/v2.0/tokens").
112
146
  to_return(:status => 200, :body => "{\"access\":{\"token\":{\"id\":\"token_data\"},\"serviceCatalog\":[\"catalog_data\"]}}", :headers => {})
113
147
 
114
- auth = Misty::AuthV2.new(authv2_creds, URI.parse(""), false, log)
148
+ auth = Misty::AuthV2.new(authv2_creds, config)
115
149
  auth.stub :expired?, false do
116
150
  auth.get_token.must_equal "token_data"
117
151
  end
@@ -121,7 +155,7 @@ describe Misty::Auth do
121
155
  stub_request(:post, "http://localhost:5000/v2.0/tokens").
122
156
  to_return(:status => 200, :body => "{\"access\":{\"token\":{\"id\":\"token_data\"},\"serviceCatalog\":[\"catalog_data\"]}}", :headers => {})
123
157
 
124
- auth = Misty::AuthV2.new(authv2_creds, URI.parse(""), false, log)
158
+ auth = Misty::AuthV2.new(authv2_creds, config)
125
159
  auth.catalog.must_equal ["catalog_data"]
126
160
  end
127
161
 
@@ -129,7 +163,7 @@ describe Misty::Auth do
129
163
  stub_request(:post, "http://localhost:5000/v2.0/tokens").
130
164
  to_return(:status => 200, :body => JSON.dump(auth_response_v2("identity", "keystone")), :headers => {"x-subject-token"=>"token_data"})
131
165
 
132
- auth = Misty::AuthV2.new(authv2_creds, URI.parse(""), false, log)
166
+ auth = Misty::AuthV2.new(authv2_creds, config)
133
167
  auth.get_endpoint(%w{identity}, "regionOne", "public").must_equal "http://localhost"
134
168
  end
135
169
  end
@@ -27,57 +27,55 @@ describe Misty::Cloud do
27
27
 
28
28
  it "uses default version" do
29
29
  cloud = Misty::Cloud.new(:auth => auth)
30
- cloud.services[:identity].must_equal ({:keystone => "v3"})
31
- end
32
-
33
- it "uses provided version" do
34
- cloud = Misty::Cloud.new(:auth => auth, :identity => {:api_version => "v2.0"})
35
- cloud.services[:identity].must_equal ({:keystone => "v2.0"})
30
+ cloud.identity.must_be_kind_of Misty::Openstack::Keystone::V3
36
31
  end
37
32
 
38
33
  it "uses default version when provided version is out of range" do
39
34
  cloud = Misty::Cloud.new(:auth => auth, :identity => {:api_version => "v1"})
40
- cloud.services[:identity].must_equal ({:keystone => "v3"})
35
+ cloud.identity.must_be_kind_of Misty::Openstack::Keystone::V3
36
+ end
37
+
38
+ it "uses provided version" do
39
+ cloud = Misty::Cloud.new(:auth => auth, :identity => {:api_version => "v2.0"})
40
+ cloud.identity.must_be_kind_of Misty::Openstack::Keystone::V2_0
41
41
  end
42
42
  end
43
43
 
44
- describe "All SERVICES" do
45
- it "have a method defined" do
46
- Misty::SERVICES.each do |service|
44
+ describe "Each service" do
45
+ it "has a method defined" do
46
+ Misty::services.each do |service|
47
47
  method = Misty::Cloud.method_defined?(service.name)
48
48
  method.must_equal true
49
49
  end
50
50
  end
51
51
  end
52
52
 
53
- describe "#setup" do
53
+ describe "#config" do
54
54
  it "sets up default values" do
55
55
  Misty::Auth.stub :factory, nil do
56
- setup = Misty::Cloud.setup({})
57
- setup.must_be_kind_of Misty::Cloud::Setup
58
- setup.content_type.must_equal Misty::CONTENT_TYPE
59
- setup.log.must_be_kind_of Logger
60
- setup.interface.must_equal Misty::INTERFACE
61
- setup.proxy.must_be_kind_of URI
62
- setup.proxy.host.must_be_nil
63
- setup.region_id.must_equal Misty::REGION_ID
64
- setup.ssl_verify_mode.must_equal Misty::SSL_VERIFY_MODE
56
+ config = Misty::Cloud.set_configuration({})
57
+ config.must_be_kind_of Misty::Cloud::Config
58
+ config.content_type.must_equal Misty::CONTENT_TYPE
59
+ config.log.must_be_kind_of Logger
60
+ config.interface.must_equal Misty::INTERFACE
61
+ config.region_id.must_equal Misty::REGION_ID
62
+ config.ssl_verify_mode.must_equal Misty::SSL_VERIFY_MODE
65
63
  end
66
64
  end
67
65
  end
68
66
 
69
67
  describe "#new" do
70
68
  describe "fails" do
71
- it "when no credentials" do
69
+ it "when no parameters" do
72
70
  proc do
73
71
  Misty::Cloud.new
74
- end.must_raise Misty::Auth::CredentialsError
72
+ end.must_raise ArgumentError
75
73
  end
76
74
 
77
75
  it "with empty credentials" do
78
76
  proc do
79
77
  Misty::Cloud.new(:auth => {})
80
- end.must_raise Misty::Auth::CredentialsError
78
+ end.must_raise Misty::Auth::URLError
81
79
  end
82
80
 
83
81
  it "with incomplete credentials" do
@@ -42,8 +42,7 @@ describe Misty::HTTP::Client do
42
42
  describe "#net_http" do
43
43
  it "returns a Net/http instance" do
44
44
  endpoint = URI.parse("http://localhost")
45
- proxy = URI.parse("")
46
- service.send(:net_http, endpoint, proxy, false, Logger.new("/dev/null")).must_be_instance_of Net::HTTP
45
+ service.send(:net_http, endpoint, false, Logger.new("/dev/null")).must_be_instance_of Net::HTTP
47
46
  end
48
47
  end
49
48
 
@@ -122,12 +122,23 @@ describe Misty::HTTP::MethodBuilder do
122
122
  service.send(:query_param, "name=foobar").must_equal "?name=foobar"
123
123
  end
124
124
 
125
- it "returns nil when passing an empty String" do
126
- service.send(:query_param, "").must_be_nil
125
+ it "returns empty string when passing an empty String" do
126
+ service.send(:query_param, "").must_equal ""
127
127
  end
128
128
 
129
- it "returns nil unless passing a String" do
130
- service.send(:query_param, "name" => "foobar").must_be_nil
129
+ it "returns a query string when passing in a Hash" do
130
+ service.send(:query_param, {}).must_equal ''
131
+
132
+ service.send(:query_param, {:foo => 'bar'}).must_equal '?foo=bar'
133
+ service.send(:query_param, {'foo' => 'bar'}).must_equal '?foo=bar'
134
+
135
+ service.send(:query_param, {:foo => ['bar', 'baz'], :value => 42, :flag => nil }).must_equal '?foo=bar&foo=baz&value=42&flag'
136
+
137
+ service.send(:query_param, {'===' => 'Ëncøding is hárd!'}).must_equal '?%3D%3D%3D=%C3%8Bnc%C3%B8ding+is+h%C3%A1rd%21'
138
+ end
139
+
140
+ it "returns nil unless passing a String or Hash" do
141
+ service.send(:query_param, 42).must_be_nil
131
142
  end
132
143
  end
133
144
  end
@@ -1,15 +1,116 @@
1
1
  require 'test_helper'
2
2
 
3
+ def validate(service)
4
+ service.must_be_kind_of Misty::Services::Service
5
+ service.name.must_be_kind_of Symbol
6
+ service.project.must_be_kind_of Symbol
7
+ service.versions.must_be_kind_of Array
8
+ service.versions.each do |version|
9
+ version.must_be_kind_of String
10
+ end
11
+ end
12
+
3
13
  describe Misty do
4
- describe "SERVICES" do
5
- it "has expected structure" do
6
- Misty::SERVICES.each do |service|
7
- service.name.must_be_kind_of Symbol
8
- service.project.must_be_kind_of Symbol
9
- service.versions.must_be_kind_of Array
10
- service.versions.each do |version|
11
- version.must_be_kind_of String
12
- end
14
+ describe "#set_services" do
15
+ it "has alarming service" do
16
+ service = Misty::services.find { |s| s.name == :alarming}
17
+ validate(service)
18
+ end
19
+
20
+ it "has baremetal service" do
21
+ service = Misty::services.find { |s| s.name == :baremetal}
22
+ validate(service)
23
+ end
24
+
25
+ it "has block_storage service" do
26
+ service = Misty::services.find { |s| s.name == :block_storage}
27
+ validate(service)
28
+ end
29
+
30
+ it "has clustering service" do
31
+ service = Misty::services.find { |s| s.name == :clustering}
32
+ validate(service)
33
+ end
34
+
35
+ it "has compute service" do
36
+ service = Misty::services.find { |s| s.name == :compute}
37
+ validate(service)
38
+ end
39
+
40
+ it "has container service" do
41
+ service = Misty::services.find { |s| s.name == :container}
42
+ validate(service)
43
+ end
44
+
45
+ it "has data_processing service" do
46
+ service = Misty::services.find { |s| s.name == :data_processing}
47
+ validate(service)
48
+ end
49
+
50
+ it "has data_protection service" do
51
+ service = Misty::services.find { |s| s.name == :data_protection}
52
+ validate(service)
53
+ end
54
+
55
+ it "has database service" do
56
+ service = Misty::services.find { |s| s.name == :database}
57
+ validate(service)
58
+ end
59
+
60
+ it "has dns service" do
61
+ service = Misty::services.find { |s| s.name == :dns}
62
+ validate(service)
63
+ end
64
+
65
+ it "has identity service" do
66
+ service = Misty::services.find { |s| s.name == :identity}
67
+ validate(service)
68
+ end
69
+
70
+ it "has image service" do
71
+ service = Misty::services.find { |s| s.name == :image}
72
+ validate(service)
73
+ end
74
+
75
+ it "has messaging service" do
76
+ service = Misty::services.find { |s| s.name == :messaging}
77
+ validate(service)
78
+ end
79
+
80
+ it "has metering service" do
81
+ service = Misty::services.find { |s| s.name == :metering}
82
+ validate(service)
83
+ end
84
+
85
+ it "has network service" do
86
+ service = Misty::services.find { |s| s.name == :network}
87
+ validate(service)
88
+ end
89
+
90
+ it "has object_storage service" do
91
+ service = Misty::services.find { |s| s.name == :object_storage}
92
+ validate(service)
93
+ end
94
+
95
+ it "has orchestration service" do
96
+ service = Misty::services.find { |s| s.name == :orchestration}
97
+ validate(service)
98
+ end
99
+
100
+ it "has search service" do
101
+ service = Misty::services.find { |s| s.name == :search}
102
+ validate(service)
103
+ end
104
+
105
+ it "has shared_file_systems service" do
106
+ service = Misty::services.find { |s| s.name == :shared_file_systems}
107
+ validate(service)
108
+ end
109
+
110
+ describe "#services" do
111
+ it "returns Services" do
112
+ services = Misty::services
113
+ services.must_be_kind_of Misty::Services
13
114
  end
14
115
  end
15
116
  end
@@ -31,12 +31,10 @@ describe Misty::HTTP::Microversion do
31
31
  "token_id"
32
32
  end
33
33
 
34
- setup = Misty::Cloud::Setup.new
35
- setup.auth = auth
34
+ setup = Misty::Cloud::Config.new
36
35
  setup.content_type = :ruby
37
36
  setup.log = Logger.new('/dev/null')
38
37
  setup.interface = Misty::INTERFACE
39
- setup.proxy = URI.parse('')
40
38
  setup.region_id = Misty::REGION_ID
41
39
  setup.ssl_verify_mode = Misty::SSL_VERIFY_MODE
42
40
 
@@ -44,7 +42,7 @@ describe Misty::HTTP::Microversion do
44
42
  with(:headers => {'Accept'=>'application/json', 'Content-Type'=>'application/json'}).
45
43
  to_return(:status => 200, :body => JSON.dump(versions_data), :headers => {})
46
44
 
47
- Misty::Openstack::Nova::V2_1.new(setup, {})
45
+ Misty::Openstack::Nova::V2_1.new(auth, setup, {})
48
46
  end
49
47
 
50
48
  describe "#version_get" do
@@ -15,12 +15,10 @@ def service(content_type = :ruby)
15
15
  "token_id"
16
16
  end
17
17
 
18
- setup = Misty::Cloud::Setup.new
19
- setup.auth = auth
18
+ setup = Misty::Cloud::Config.new
20
19
  setup.content_type = content_type
21
20
  setup.log = Logger.new('/dev/null')
22
21
  setup.interface = Misty::INTERFACE
23
- setup.proxy = URI.parse("")
24
22
  setup.region_id = Misty::REGION_ID
25
23
  setup.ssl_verify_mode = Misty::SSL_VERIFY_MODE
26
24
 
@@ -28,5 +26,5 @@ def service(content_type = :ruby)
28
26
  with(:headers => request_header).
29
27
  to_return(:status => 200, :body => "", :headers => {})
30
28
 
31
- Misty::Openstack::Neutron::V2_0.new(setup, {})
29
+ Misty::Openstack::Neutron::V2_0.new(auth, setup, {})
32
30
  end
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+
3
+ describe "Services" do
4
+ describe "#add" do
5
+ it "Adds a service" do
6
+ services = Misty::Services.new
7
+ services.add(:name, :project, ["v1", "v2.0"])
8
+ services.services.size.must_equal 1
9
+ service = services.services[0]
10
+ service.must_be_kind_of Misty::Services::Service
11
+ service.name.must_equal :name
12
+ service.project.must_equal :project
13
+ service.versions.must_include "v1"
14
+ end
15
+ end
16
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: misty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gilles Dubreuil
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-04-18 00:00:00.000000000 Z
11
+ date: 2017-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -186,6 +186,7 @@ files:
186
186
  - lib/misty/openstack/trove/v1_0.rb
187
187
  - lib/misty/openstack/zaqar/v2.rb
188
188
  - lib/misty/openstack/zaqar/zaqar_v2.rb
189
+ - lib/misty/services.rb
189
190
  - lib/misty/version.rb
190
191
  - test/integration/compute_test.rb
191
192
  - test/integration/network_test.rb
@@ -208,6 +209,7 @@ files:
208
209
  - test/unit/openstack/APIs_test.rb
209
210
  - test/unit/openstack/microversion_test.rb
210
211
  - test/unit/service_helper.rb
212
+ - test/unit/services_test.rb
211
213
  - test/unit/test_helper.rb
212
214
  homepage: https://github.com/flystack/misty
213
215
  licenses: