fog-core 1.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/lib/fog/account.rb +25 -0
- data/lib/fog/billing.rb +23 -0
- data/lib/fog/cdn.rb +23 -0
- data/lib/fog/compute.rb +80 -0
- data/lib/fog/compute/models/server.rb +104 -0
- data/lib/fog/core.rb +51 -0
- data/lib/fog/core/attributes.rb +227 -0
- data/lib/fog/core/class_from_string.rb +26 -0
- data/lib/fog/core/collection.rb +161 -0
- data/lib/fog/core/connection.rb +72 -0
- data/lib/fog/core/credentials.rb +70 -0
- data/lib/fog/core/current_machine.rb +34 -0
- data/lib/fog/core/deprecated_connection_accessors.rb +41 -0
- data/lib/fog/core/deprecation.rb +23 -0
- data/lib/fog/core/errors.rb +118 -0
- data/lib/fog/core/hmac.rb +35 -0
- data/lib/fog/core/logger.rb +44 -0
- data/lib/fog/core/mock.rb +115 -0
- data/lib/fog/core/model.rb +80 -0
- data/lib/fog/core/provider.rb +34 -0
- data/lib/fog/core/scp.rb +96 -0
- data/lib/fog/core/service.rb +223 -0
- data/lib/fog/core/ssh.rb +137 -0
- data/lib/fog/core/time.rb +32 -0
- data/lib/fog/core/uuid.rb +23 -0
- data/lib/fog/core/wait_for.rb +15 -0
- data/lib/fog/core/wait_for_defaults.rb +21 -0
- data/lib/fog/dns.rb +40 -0
- data/lib/fog/identity.rb +28 -0
- data/lib/fog/image.rb +23 -0
- data/lib/fog/metering.rb +25 -0
- data/lib/fog/monitoring.rb +24 -0
- data/lib/fog/network.rb +28 -0
- data/lib/fog/orchestration.rb +25 -0
- data/lib/fog/schema/data_validator.rb +154 -0
- data/lib/fog/storage.rb +80 -0
- data/lib/fog/support.rb +26 -0
- data/lib/fog/test_helpers.rb +12 -0
- data/lib/fog/test_helpers/collection_helper.rb +102 -0
- data/lib/fog/test_helpers/compute/flavors_helper.rb +34 -0
- data/lib/fog/test_helpers/compute/server_helper.rb +27 -0
- data/lib/fog/test_helpers/compute/servers_helper.rb +12 -0
- data/lib/fog/test_helpers/formats_helper.rb +99 -0
- data/lib/fog/test_helpers/helper.rb +25 -0
- data/lib/fog/test_helpers/mock_helper.rb +107 -0
- data/lib/fog/test_helpers/model_helper.rb +35 -0
- data/lib/fog/test_helpers/responds_to_helper.rb +13 -0
- data/lib/fog/test_helpers/succeeds_helper.rb +11 -0
- data/lib/fog/version.rb +3 -0
- data/lib/fog/volume.rb +25 -0
- data/lib/fog/vpn.rb +25 -0
- data/lib/tasks/test_task.rb +46 -0
- metadata +267 -0
data/lib/fog/core/ssh.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Fog
|
4
|
+
module SSH
|
5
|
+
|
6
|
+
def self.new(address, username, options = {})
|
7
|
+
if Fog.mocking?
|
8
|
+
Fog::SSH::Mock.new(address, username, options)
|
9
|
+
else
|
10
|
+
Fog::SSH::Real.new(address, username, options)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Mock
|
15
|
+
|
16
|
+
def self.data
|
17
|
+
@data ||= Hash.new do |hash, key|
|
18
|
+
hash[key] = []
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.reset
|
23
|
+
@data= nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(address, username, options)
|
27
|
+
@address = address
|
28
|
+
@username = username
|
29
|
+
@options = options
|
30
|
+
end
|
31
|
+
|
32
|
+
def run(commands, &blk)
|
33
|
+
self.class.data[@address] << {:commands => commands, :username => @username, :options => @options}
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class Real
|
39
|
+
|
40
|
+
def initialize(address, username, options)
|
41
|
+
require 'net/ssh'
|
42
|
+
|
43
|
+
key_manager = Net::SSH::Authentication::KeyManager.new(nil, options)
|
44
|
+
|
45
|
+
unless options[:key_data] || options[:keys] || options[:password] || key_manager.agent
|
46
|
+
raise ArgumentError.new(':key_data, :keys, :password or a loaded ssh-agent is required to initialize SSH')
|
47
|
+
end
|
48
|
+
|
49
|
+
options[:timeout] ||= 30
|
50
|
+
if options[:key_data] || options[:keys]
|
51
|
+
options[:keys_only] = true
|
52
|
+
#Explicitly set these so net-ssh doesn't add the default keys
|
53
|
+
#as seen at https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/authentication/session.rb#L131-146
|
54
|
+
options[:keys] = [] unless options[:keys]
|
55
|
+
options[:key_data] = [] unless options[:key_data]
|
56
|
+
end
|
57
|
+
|
58
|
+
@address = address
|
59
|
+
@username = username
|
60
|
+
@options = { :paranoid => false }.merge(options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def run(commands, &blk)
|
64
|
+
commands = [*commands]
|
65
|
+
results = []
|
66
|
+
begin
|
67
|
+
Net::SSH.start(@address, @username, @options) do |ssh|
|
68
|
+
commands.each do |command|
|
69
|
+
result = Result.new(command)
|
70
|
+
ssh.open_channel do |ssh_channel|
|
71
|
+
ssh_channel.request_pty
|
72
|
+
ssh_channel.exec(command) do |channel, success|
|
73
|
+
unless success
|
74
|
+
raise "Could not execute command: #{command.inspect}"
|
75
|
+
end
|
76
|
+
|
77
|
+
channel.on_data do |ch, data|
|
78
|
+
result.stdout << data
|
79
|
+
yield [data, ''] if blk
|
80
|
+
end
|
81
|
+
|
82
|
+
channel.on_extended_data do |ch, type, data|
|
83
|
+
next unless type == 1
|
84
|
+
result.stderr << data
|
85
|
+
yield ['', data] if blk
|
86
|
+
end
|
87
|
+
|
88
|
+
channel.on_request('exit-status') do |ch, data|
|
89
|
+
result.status = data.read_long
|
90
|
+
end
|
91
|
+
|
92
|
+
channel.on_request('exit-signal') do |ch, data|
|
93
|
+
result.status = 255
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
ssh.loop
|
98
|
+
results << result
|
99
|
+
end
|
100
|
+
end
|
101
|
+
rescue Net::SSH::HostKeyMismatch => exception
|
102
|
+
exception.remember_host!
|
103
|
+
sleep 0.2
|
104
|
+
retry
|
105
|
+
end
|
106
|
+
results
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
class Result
|
112
|
+
|
113
|
+
attr_accessor :command, :stderr, :stdout, :status
|
114
|
+
|
115
|
+
def display_stdout
|
116
|
+
data = stdout.split("\r\n")
|
117
|
+
if data.is_a?(String)
|
118
|
+
Formatador.display_line(data)
|
119
|
+
elsif data.is_a?(Array)
|
120
|
+
Formatador.display_lines(data)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def display_stderr
|
125
|
+
Formatador.display_line(stderr.split("\r\n"))
|
126
|
+
end
|
127
|
+
|
128
|
+
def initialize(command)
|
129
|
+
@command = command
|
130
|
+
@stderr = ''
|
131
|
+
@stdout = ''
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Fog
|
4
|
+
class Time < ::Time
|
5
|
+
|
6
|
+
DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
7
|
+
MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
8
|
+
|
9
|
+
def self.now
|
10
|
+
at((::Time.now - offset).to_i)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.now=(new_now)
|
14
|
+
old_now = ::Time.now
|
15
|
+
@offset = old_now - new_now
|
16
|
+
new_now
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.offset
|
20
|
+
@offset ||= 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_date_header
|
24
|
+
self.utc.strftime("#{DAYS[self.utc.wday]}, %d #{MONTHS[self.utc.month - 1]} %Y %H:%M:%S +0000")
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_iso8601_basic
|
28
|
+
self.utc.strftime('%Y%m%dT%H%M%SZ')
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Fog
|
4
|
+
class UUID
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def uuid
|
8
|
+
if supported?
|
9
|
+
SecureRandom.uuid
|
10
|
+
else
|
11
|
+
ary = SecureRandom.random_bytes(16).unpack("NnnnnN")
|
12
|
+
ary[2] = (ary[2] & 0x0fff) | 0x4000
|
13
|
+
ary[3] = (ary[3] & 0x3fff) | 0x8000
|
14
|
+
"%08x-%04x-%04x-%04x-%04x%08x" % ary
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def supported?
|
19
|
+
SecureRandom.respond_to?(:uuid)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Fog
|
2
|
+
def self.wait_for(timeout=Fog.timeout, interval=Fog.interval, &block)
|
3
|
+
duration = 0
|
4
|
+
start = Time.now
|
5
|
+
until yield || duration > timeout
|
6
|
+
sleep(interval.to_f)
|
7
|
+
duration = Time.now - start
|
8
|
+
end
|
9
|
+
if duration > timeout
|
10
|
+
raise Errors::TimeoutError.new("The specified wait_for timeout (#{timeout} seconds) was exceeded")
|
11
|
+
else
|
12
|
+
{ :duration => duration }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fog
|
2
|
+
@interval = 1
|
3
|
+
def self.interval
|
4
|
+
@interval
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.interval=(interval)
|
8
|
+
raise ArgumentError, "interval must be non-negative" unless interval >= 0
|
9
|
+
@interval = interval
|
10
|
+
end
|
11
|
+
|
12
|
+
@timeout = 600
|
13
|
+
def self.timeout
|
14
|
+
@timeout
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.timeout=(timeout)
|
18
|
+
raise ArgumentError, "timeout must be non-negative" unless timeout >= 0
|
19
|
+
@timeout = timeout
|
20
|
+
end
|
21
|
+
end
|
data/lib/fog/dns.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Fog
|
2
|
+
module DNS
|
3
|
+
|
4
|
+
def self.[](provider)
|
5
|
+
self.new(:provider => provider)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.new(attributes)
|
9
|
+
attributes = attributes.dup # prevent delete from having side effects
|
10
|
+
case provider = attributes.delete(:provider).to_s.downcase.to_sym
|
11
|
+
when :stormondemand
|
12
|
+
require 'fog/storm_on_demand/dns'
|
13
|
+
Fog::DNS::StormOnDemand.new(attributes)
|
14
|
+
else
|
15
|
+
if self.providers.include?(provider)
|
16
|
+
require "fog/#{provider}/dns"
|
17
|
+
return Fog::DNS.const_get(Fog.providers[provider]).new(attributes)
|
18
|
+
end
|
19
|
+
|
20
|
+
raise ArgumentError.new("#{provider} is not a recognized dns provider")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.providers
|
25
|
+
Fog.services[:dns]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.zones
|
29
|
+
zones = []
|
30
|
+
for provider in self.providers
|
31
|
+
begin
|
32
|
+
zones.concat(self[provider].zones)
|
33
|
+
rescue # ignore any missing credentials/etc
|
34
|
+
end
|
35
|
+
end
|
36
|
+
zones
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
data/lib/fog/identity.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Fog
|
2
|
+
module Identity
|
3
|
+
|
4
|
+
def self.[](provider)
|
5
|
+
self.new(:provider => provider)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.new(attributes)
|
9
|
+
attributes = attributes.dup # Prevent delete from having side effects
|
10
|
+
case provider = attributes.delete(:provider).to_s.downcase.to_sym
|
11
|
+
when :rackspace
|
12
|
+
require 'fog/rackspace/identity'
|
13
|
+
Fog::Rackspace::Identity.new(attributes)
|
14
|
+
else
|
15
|
+
if self.providers.include?(provider)
|
16
|
+
require "fog/#{provider}/identity"
|
17
|
+
return Fog::Identity.const_get(Fog.providers[provider]).new(attributes)
|
18
|
+
end
|
19
|
+
raise ArgumentError.new("#{provider} has no identity service")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.providers
|
24
|
+
Fog.services[:identity]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/fog/image.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Fog
|
2
|
+
module Image
|
3
|
+
|
4
|
+
def self.[](provider)
|
5
|
+
self.new(:provider => provider)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.new(attributes)
|
9
|
+
attributes = attributes.dup # Prevent delete from having side effects
|
10
|
+
provider = attributes.delete(:provider).to_s.downcase.to_sym
|
11
|
+
if self.providers.include?(provider)
|
12
|
+
require "fog/#{provider}/image"
|
13
|
+
return Fog::Image.const_get(Fog.providers[provider]).new(attributes)
|
14
|
+
end
|
15
|
+
raise ArgumentError.new("#{provider} has no identity service")
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.providers
|
19
|
+
Fog.services[:image]
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
data/lib/fog/metering.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Fog
|
2
|
+
module Metering
|
3
|
+
|
4
|
+
def self.[](provider)
|
5
|
+
self.new(:provider => provider)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.new(attributes)
|
9
|
+
attributes = attributes.dup # Prevent delete from having side effects
|
10
|
+
provider = attributes.delete(:provider).to_s.downcase.to_sym
|
11
|
+
if self.providers.include?(provider)
|
12
|
+
require "fog/#{provider}/metering"
|
13
|
+
return Fog::Metering.const_get(Fog.providers[provider]).new(attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
raise ArgumentError.new("#{provider} has no identity service")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.providers
|
20
|
+
Fog.services[:metering]
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Fog
|
2
|
+
module Monitoring
|
3
|
+
|
4
|
+
def self.[](provider)
|
5
|
+
self.new(:provider => provider)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.new(attributes)
|
9
|
+
attributes = attributes.dup
|
10
|
+
provider = attributes.delete(:provider).to_s.downcase.to_sym
|
11
|
+
if provider == :stormondemand
|
12
|
+
require 'fog/storm_on_demand/billing'
|
13
|
+
Fog::Monitoring::StormOnDemand.new(attributes)
|
14
|
+
else
|
15
|
+
raise ArgumentError.new("#{provider} has no monitoring service")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.providers
|
20
|
+
Fog.services[:monitoring]
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
data/lib/fog/network.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Fog
|
2
|
+
module Network
|
3
|
+
|
4
|
+
def self.[](provider)
|
5
|
+
self.new(:provider => provider)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.new(attributes)
|
9
|
+
attributes = attributes.dup # Prevent delete from having side effects
|
10
|
+
provider = attributes.delete(:provider).to_s.downcase.to_sym
|
11
|
+
|
12
|
+
if provider == :stormondemand
|
13
|
+
require "fog/storm_on_demand/network"
|
14
|
+
return Fog::Network::StormOnDemand.new(attributes)
|
15
|
+
elsif self.providers.include?(provider)
|
16
|
+
require "fog/#{provider}/network"
|
17
|
+
return Fog::Network.const_get(Fog.providers[provider]).new(attributes)
|
18
|
+
end
|
19
|
+
|
20
|
+
raise ArgumentError.new("#{provider} has no network service")
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.providers
|
24
|
+
Fog.services[:network]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Fog
|
2
|
+
module Orchestration
|
3
|
+
|
4
|
+
def self.[](provider)
|
5
|
+
self.new(:provider => provider)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.new(attributes)
|
9
|
+
attributes = attributes.dup # Prevent delete from having side effects
|
10
|
+
provider = attributes.delete(:provider).to_s.downcase.to_sym
|
11
|
+
|
12
|
+
if self.providers.include?(provider)
|
13
|
+
require "fog/#{provider}/network"
|
14
|
+
return Fog::Orchestration.const_get(Fog.providers[provider]).new(attributes)
|
15
|
+
end
|
16
|
+
|
17
|
+
raise ArgumentError.new("#{provider} has no orchestration service")
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.providers
|
21
|
+
Fog.services[:orchestration]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
module Fog
|
2
|
+
module Schema
|
3
|
+
# This validates a data object against a Ruby based schema to see
|
4
|
+
# if they match.
|
5
|
+
#
|
6
|
+
# * An object matches the schema if +==+ or +===+ returns +true+
|
7
|
+
# * Hashes match if all the key's values match the classes given
|
8
|
+
# in the schema as well. This can be configured in the options
|
9
|
+
# * Arrays match when every element in the data matches the case
|
10
|
+
# given in the schema.
|
11
|
+
#
|
12
|
+
# The schema and validation are very simple and probably not
|
13
|
+
# suitable for some cases.
|
14
|
+
#
|
15
|
+
# The following classes can be used to check for special behaviour
|
16
|
+
#
|
17
|
+
# * Fog::Boolean - value may be +true+ or +false+
|
18
|
+
# * Fog::Nullable::Boolean - value may be +true+, +false+ or +nil+
|
19
|
+
# * Fog::Nullable::Integer - value may be an Integer or +nil+
|
20
|
+
# * Fog::Nullable::String
|
21
|
+
# * Fog::Nullable::Time
|
22
|
+
# * Fog::Nullable::Float
|
23
|
+
# * Fog::Nullable::Hash
|
24
|
+
# * Fog::Nullable::Array
|
25
|
+
#
|
26
|
+
# All the "nullable" objects will pass if the value is of the class
|
27
|
+
# or if it is +nil+. This allows you to match APIs that may include
|
28
|
+
# keys when the value is not available in some cases but will
|
29
|
+
# always be a String. Such as an password that is only displayed
|
30
|
+
# on the reset action.
|
31
|
+
#
|
32
|
+
# The keys for "nullable" resources should always be present but
|
33
|
+
# original matcher had a bug that allowed these to also appear to
|
34
|
+
# work as optional keys/values.
|
35
|
+
#
|
36
|
+
# If you need the original behaviour, data with a missing key is
|
37
|
+
# still valid, then you may pass the +:allow_optional_rules+
|
38
|
+
# option to the #validate method.
|
39
|
+
#
|
40
|
+
# That is not recommended because you are describing a schema
|
41
|
+
# with optional keys in a format that does not support it.
|
42
|
+
#
|
43
|
+
# Setting +:allow_extra_keys+ as +true+ allows the data to
|
44
|
+
# contain keys not declared by the schema and still pass. This
|
45
|
+
# is useful if new attributes appear in the API in a backwards
|
46
|
+
# compatible manner and can be ignored.
|
47
|
+
#
|
48
|
+
# This is the behaviour you would have seen with +strict+ being
|
49
|
+
# +false+ in the original test helper.
|
50
|
+
#
|
51
|
+
# @example Schema example
|
52
|
+
# {
|
53
|
+
# "id" => String,
|
54
|
+
# "ram" => Integer,
|
55
|
+
# "disks" => [
|
56
|
+
# "size" => Float
|
57
|
+
# ],
|
58
|
+
# "dns_name" => Fog::Nullable::String,
|
59
|
+
# "active" => Fog::Boolean,
|
60
|
+
# "created" => DateTime
|
61
|
+
# }
|
62
|
+
#
|
63
|
+
class DataValidator
|
64
|
+
|
65
|
+
def initialize
|
66
|
+
@message = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
# Checks if the data structure matches the schema passed in and
|
70
|
+
# returns +true+ if it fits.
|
71
|
+
#
|
72
|
+
# @param [Object] data Hash or Array to check
|
73
|
+
# @param [Object] schema Schema pattern to check against
|
74
|
+
# @param [Boolean] options
|
75
|
+
# @option options [Boolean] :allow_extra_keys
|
76
|
+
# If +true+ does not fail if extra keys are in the data
|
77
|
+
# that are not in the schema.
|
78
|
+
# @option options [Boolean] :allow_optional_rules
|
79
|
+
# If +true+ does not fail if extra keys are in the schema
|
80
|
+
# that do not match the data. Not recommended!
|
81
|
+
#
|
82
|
+
# @return [Boolean] Did the data fit the schema?
|
83
|
+
def validate(data, schema, options = {})
|
84
|
+
valid = validate_value(schema, data, options)
|
85
|
+
|
86
|
+
unless valid
|
87
|
+
@message = "#{data.inspect} does not match #{schema.inspect}"
|
88
|
+
end
|
89
|
+
valid
|
90
|
+
end
|
91
|
+
|
92
|
+
# This returns the last message set by the validator
|
93
|
+
#
|
94
|
+
# @return [String]
|
95
|
+
def message
|
96
|
+
@message
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# This contains a slightly modified version of the Hashidator gem
|
102
|
+
# but unfortunately the gem does not cope with Array schemas.
|
103
|
+
#
|
104
|
+
# @see https://github.com/vangberg/hashidator/blob/master/lib/hashidator.rb
|
105
|
+
#
|
106
|
+
def validate_value(validator, value, options)
|
107
|
+
Fog::Logger.write :debug, "[yellow][DEBUG] #{value.inspect} against #{validator.inspect}[/]\n"
|
108
|
+
|
109
|
+
case validator
|
110
|
+
when Array
|
111
|
+
return false if value.is_a?(Hash)
|
112
|
+
value.respond_to?(:all?) && value.all? {|x| validate_value(validator[0], x, options)}
|
113
|
+
when Symbol
|
114
|
+
value.respond_to? validator
|
115
|
+
when Hash
|
116
|
+
return false if value.is_a?(Array)
|
117
|
+
|
118
|
+
# When being strict values not specified in the schema are fails
|
119
|
+
unless options[:allow_extra_keys]
|
120
|
+
if value.respond_to?(:empty?)
|
121
|
+
# Validator is empty but values are not
|
122
|
+
return false if !value.empty? && validator.empty?
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
unless options[:allow_optional_rules]
|
127
|
+
if value.respond_to?(:empty?)
|
128
|
+
# Validator has rules left but no more values
|
129
|
+
return false if value.empty? && !validator.empty?
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
validator.all? do |key, sub_validator|
|
134
|
+
Fog::Logger.write :debug, "[blue][DEBUG] #{key.inspect} against #{sub_validator.inspect}[/]\n"
|
135
|
+
validate_value(sub_validator, value[key], options)
|
136
|
+
end
|
137
|
+
else
|
138
|
+
result = validator == value
|
139
|
+
result = validator === value unless result
|
140
|
+
# Repeat unless we have a Boolean already
|
141
|
+
unless (result.is_a?(TrueClass) || result.is_a?(FalseClass))
|
142
|
+
result = validate_value(result, value, options)
|
143
|
+
end
|
144
|
+
if result
|
145
|
+
Fog::Logger.write :debug, "[green][DEBUG] Validation passed: #{value.inspect} against #{validator.inspect}[/]\n"
|
146
|
+
else
|
147
|
+
Fog::Logger.write :debug, "[red][DEBUG] Validation failed: #{value.inspect} against #{validator.inspect}[/]\n"
|
148
|
+
end
|
149
|
+
result
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|