fog-core 1.21.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.
Files changed (54) hide show
  1. checksums.yaml +15 -0
  2. data/lib/fog/account.rb +25 -0
  3. data/lib/fog/billing.rb +23 -0
  4. data/lib/fog/cdn.rb +23 -0
  5. data/lib/fog/compute.rb +80 -0
  6. data/lib/fog/compute/models/server.rb +104 -0
  7. data/lib/fog/core.rb +51 -0
  8. data/lib/fog/core/attributes.rb +227 -0
  9. data/lib/fog/core/class_from_string.rb +26 -0
  10. data/lib/fog/core/collection.rb +161 -0
  11. data/lib/fog/core/connection.rb +72 -0
  12. data/lib/fog/core/credentials.rb +70 -0
  13. data/lib/fog/core/current_machine.rb +34 -0
  14. data/lib/fog/core/deprecated_connection_accessors.rb +41 -0
  15. data/lib/fog/core/deprecation.rb +23 -0
  16. data/lib/fog/core/errors.rb +118 -0
  17. data/lib/fog/core/hmac.rb +35 -0
  18. data/lib/fog/core/logger.rb +44 -0
  19. data/lib/fog/core/mock.rb +115 -0
  20. data/lib/fog/core/model.rb +80 -0
  21. data/lib/fog/core/provider.rb +34 -0
  22. data/lib/fog/core/scp.rb +96 -0
  23. data/lib/fog/core/service.rb +223 -0
  24. data/lib/fog/core/ssh.rb +137 -0
  25. data/lib/fog/core/time.rb +32 -0
  26. data/lib/fog/core/uuid.rb +23 -0
  27. data/lib/fog/core/wait_for.rb +15 -0
  28. data/lib/fog/core/wait_for_defaults.rb +21 -0
  29. data/lib/fog/dns.rb +40 -0
  30. data/lib/fog/identity.rb +28 -0
  31. data/lib/fog/image.rb +23 -0
  32. data/lib/fog/metering.rb +25 -0
  33. data/lib/fog/monitoring.rb +24 -0
  34. data/lib/fog/network.rb +28 -0
  35. data/lib/fog/orchestration.rb +25 -0
  36. data/lib/fog/schema/data_validator.rb +154 -0
  37. data/lib/fog/storage.rb +80 -0
  38. data/lib/fog/support.rb +26 -0
  39. data/lib/fog/test_helpers.rb +12 -0
  40. data/lib/fog/test_helpers/collection_helper.rb +102 -0
  41. data/lib/fog/test_helpers/compute/flavors_helper.rb +34 -0
  42. data/lib/fog/test_helpers/compute/server_helper.rb +27 -0
  43. data/lib/fog/test_helpers/compute/servers_helper.rb +12 -0
  44. data/lib/fog/test_helpers/formats_helper.rb +99 -0
  45. data/lib/fog/test_helpers/helper.rb +25 -0
  46. data/lib/fog/test_helpers/mock_helper.rb +107 -0
  47. data/lib/fog/test_helpers/model_helper.rb +35 -0
  48. data/lib/fog/test_helpers/responds_to_helper.rb +13 -0
  49. data/lib/fog/test_helpers/succeeds_helper.rb +11 -0
  50. data/lib/fog/version.rb +3 -0
  51. data/lib/fog/volume.rb +25 -0
  52. data/lib/fog/vpn.rb +25 -0
  53. data/lib/tasks/test_task.rb +46 -0
  54. metadata +267 -0
@@ -0,0 +1,35 @@
1
+ module Fog
2
+ class HMAC
3
+
4
+ def initialize(type, key)
5
+ @key = key
6
+ case type
7
+ when 'sha1'
8
+ setup_sha1
9
+ when 'sha256'
10
+ setup_sha256
11
+ end
12
+ end
13
+
14
+ def sign(data)
15
+ @signer.call(data)
16
+ end
17
+
18
+ private
19
+
20
+ def setup_sha1
21
+ @digest = OpenSSL::Digest.new('sha1')
22
+ @signer = lambda do |data|
23
+ OpenSSL::HMAC.digest(@digest, @key, data)
24
+ end
25
+ end
26
+
27
+ def setup_sha256
28
+ @digest = OpenSSL::Digest.new('sha256')
29
+ @signer = lambda do |data|
30
+ OpenSSL::HMAC.digest(@digest, @key, data)
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,44 @@
1
+ module Fog
2
+ class Logger
3
+
4
+ @channels = {
5
+ :deprecation => ::STDERR,
6
+ :warning => ::STDERR
7
+ }
8
+
9
+ @channels[:debug] = ::STDERR if ENV['DEBUG']
10
+
11
+ def self.[](channel)
12
+ @channels[channel]
13
+ end
14
+
15
+ def self.[]=(channel, value)
16
+ @channels[channel] = value
17
+ end
18
+
19
+ def self.debug(message)
20
+ self.write(:debug, "[light_black][fog][DEBUG] #{message}[/]\n")
21
+ end
22
+
23
+ def self.deprecation(message)
24
+ self.write(:deprecation, "[yellow][fog][DEPRECATION] #{message}[/]\n")
25
+ end
26
+
27
+ def self.warning(message)
28
+ self.write(:warning, "[yellow][fog][WARNING] #{message}[/]\n")
29
+ end
30
+
31
+ def self.write(key, value)
32
+ if channel = @channels[key]
33
+ message = if channel.tty?
34
+ value.gsub(Formatador::PARSE_REGEX) { "\e[#{Formatador::STYLES[$1.to_sym]}m" }.gsub(Formatador::INDENT_REGEX, '')
35
+ else
36
+ value.gsub(Formatador::PARSE_REGEX, '').gsub(Formatador::INDENT_REGEX, '')
37
+ end
38
+ channel.write(message)
39
+ end
40
+ nil
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,115 @@
1
+ module Fog
2
+
3
+ @mocking = false
4
+
5
+ def self.mock!
6
+ @mocking = true
7
+ end
8
+
9
+ def self.unmock!
10
+ @mocking = false
11
+ end
12
+
13
+ def self.mock?
14
+ @mocking
15
+ end
16
+
17
+ def self.mocking?
18
+ @mocking
19
+ end
20
+
21
+ module Mock
22
+
23
+ @delay = 1
24
+ def self.delay
25
+ @delay
26
+ end
27
+
28
+ def self.delay=(new_delay)
29
+ raise ArgumentError, "delay must be non-negative" unless new_delay >= 0
30
+ @delay = new_delay
31
+ end
32
+
33
+ def self.not_implemented(message = 'Contributions welcome!')
34
+ raise Fog::Errors::MockNotImplemented.new(message)
35
+ end
36
+
37
+ def self.random_ip(opts = {:version => :v4})
38
+ version = opts[:version]
39
+ if version == :v6
40
+ bit_length = 128
41
+ family = Socket::AF_INET6
42
+ elsif version == :v4
43
+ bit_length = 32
44
+ family = Socket::AF_INET
45
+ else
46
+ raise ArgumentError, "Unknown IP version: #{version}"
47
+ end
48
+
49
+ seed = 1 + rand((2**bit_length)-1)
50
+ IPAddr.new(seed, family).to_s
51
+ end
52
+
53
+ def self.random_base64(length)
54
+ random_selection(
55
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
56
+ length
57
+ )
58
+ end
59
+
60
+ def self.random_hex(length)
61
+ max = ('f' * length).to_i(16)
62
+ rand(max).to_s(16).rjust(length, '0')
63
+ end
64
+
65
+ def self.random_letters(length)
66
+ random_selection(
67
+ 'abcdefghijklmnopqrstuvwxyz',
68
+ length
69
+ )
70
+ end
71
+
72
+ def self.random_numbers(length)
73
+ max = ('9' * length).to_i
74
+ rand(max).to_s
75
+ end
76
+
77
+ def self.random_letters_and_numbers(length)
78
+ random_selection(
79
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
80
+ length
81
+ )
82
+ end
83
+
84
+ def self.random_selection(characters, length)
85
+ selection = ''
86
+ length.times do
87
+ position = rand(characters.length)
88
+ selection << characters[position..position]
89
+ end
90
+ selection
91
+ end
92
+
93
+ def self.reset
94
+ mocked_services = []
95
+ Fog.constants.map do |x|
96
+ x_const = Fog.const_get(x)
97
+ x_const.respond_to?(:constants) && x_const.constants.map do |y|
98
+ y_const = x_const.const_get(y)
99
+ y_const.respond_to?(:constants) && y_const.constants.map do |z|
100
+ if z.to_sym == :Mock
101
+ mocked_services << y_const.const_get(z)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ for mocked_service in mocked_services
108
+ next unless mocked_service.respond_to?(:reset)
109
+ mocked_service.reset
110
+ end
111
+ end
112
+
113
+ end
114
+
115
+ end
@@ -0,0 +1,80 @@
1
+ require "fog/core/deprecated_connection_accessors"
2
+
3
+ module Fog
4
+ class Model
5
+
6
+ extend Fog::Attributes::ClassMethods
7
+ include Fog::Attributes::InstanceMethods
8
+ include Fog::Core::DeprecatedConnectionAccessors
9
+
10
+ attr_accessor :collection
11
+ attr_reader :service
12
+
13
+ def initialize(new_attributes = {})
14
+ # TODO Remove compatibility with old connection option
15
+ @service = new_attributes.delete(:service)
16
+ if @service.nil? && new_attributes[:connection]
17
+ Fog::Logger.deprecation("Passing :connection option is deprecated, use :service instead [light_black](#{caller.first})[/]")
18
+ @service = new_attributes[:connection]
19
+ end
20
+ merge_attributes(new_attributes)
21
+ end
22
+
23
+ def inspect
24
+ Thread.current[:formatador] ||= Formatador.new
25
+ data = "#{Thread.current[:formatador].indentation}<#{self.class.name}"
26
+ Thread.current[:formatador].indent do
27
+ unless self.class.attributes.empty?
28
+ data << "\n#{Thread.current[:formatador].indentation}"
29
+ data << self.class.attributes.map {|attribute| "#{attribute}=#{send(attribute).inspect}"}.join(",\n#{Thread.current[:formatador].indentation}")
30
+ end
31
+ end
32
+ data << "\n#{Thread.current[:formatador].indentation}>"
33
+ data
34
+ end
35
+
36
+ def reload
37
+ requires :identity
38
+
39
+ return unless data = begin
40
+ collection.get(identity)
41
+ rescue Excon::Errors::SocketError
42
+ nil
43
+ end
44
+
45
+ new_attributes = data.attributes
46
+ merge_attributes(new_attributes)
47
+ self
48
+ end
49
+
50
+ def to_json(options = {})
51
+ Fog::JSON.encode(attributes)
52
+ end
53
+
54
+ def symbolize_keys(hash)
55
+ return nil if hash.nil?
56
+ hash.inject({}) do |options, (key, value)|
57
+ options[(key.to_sym rescue key) || key] = value
58
+ options
59
+ end
60
+ end
61
+
62
+ def wait_for(timeout=Fog.timeout, interval=1, &block)
63
+ reload_has_succeeded = false
64
+ duration = Fog.wait_for(timeout, interval) do # Note that duration = false if it times out
65
+ if reload
66
+ reload_has_succeeded = true
67
+ instance_eval(&block)
68
+ else
69
+ false
70
+ end
71
+ end
72
+ if reload_has_succeeded
73
+ return duration # false if timeout; otherwise {:duration => elapsed time }
74
+ else
75
+ raise Fog::Errors::Error.new("Reload failed, #{self.class} #{self.identity} not present.")
76
+ end
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,34 @@
1
+ module Fog
2
+
3
+ def self.providers
4
+ @providers ||= {}
5
+ end
6
+
7
+ def self.providers=(new_providers)
8
+ @providers = new_providers
9
+ end
10
+
11
+ module Provider
12
+
13
+ def self.extended(base)
14
+ provider = base.to_s.split('::').last
15
+ Fog.providers[provider.downcase.to_sym] = provider
16
+ end
17
+
18
+ def [](service_key)
19
+ eval(@services_registry[service_key]).new
20
+ end
21
+
22
+ def service(new_service, constant_string)
23
+ Fog.services[new_service] ||= []
24
+ Fog.services[new_service] |= [self.to_s.split('::').last.downcase.to_sym]
25
+ @services_registry ||= {}
26
+ @services_registry[new_service] = [self.to_s, constant_string].join('::')
27
+ end
28
+
29
+ def services
30
+ @services_registry.keys
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,96 @@
1
+ module Fog
2
+ module SCP
3
+
4
+ def self.new(address, username, options = {})
5
+ if Fog.mocking?
6
+ Fog::SCP::Mock.new(address, username, options)
7
+ else
8
+ Fog::SCP::Real.new(address, username, options)
9
+ end
10
+ end
11
+
12
+ class Mock
13
+
14
+ def self.data
15
+ @data ||= Hash.new do |hash, key|
16
+ hash[key] = []
17
+ end
18
+ end
19
+
20
+ def initialize(address, username, options)
21
+ @address = address
22
+ @username = username
23
+ @options = options
24
+ end
25
+
26
+ def upload(local_path, remote_path, upload_options = {})
27
+ self.class.data[@address] << { :username => @username,
28
+ :options => @options,
29
+ :local_path => local_path,
30
+ :remote_path => remote_path,
31
+ :upload_options => upload_options }
32
+ end
33
+
34
+ def download(remote_path, local_path, download_options = {})
35
+ self.class.data[@address] << { :username => @username,
36
+ :options => @options,
37
+ :remote_path => remote_path,
38
+ :local_path => local_path,
39
+ :download_options => download_options }
40
+ end
41
+
42
+ end
43
+
44
+ class Real
45
+
46
+ def initialize(address, username, options)
47
+ require 'net/scp'
48
+
49
+ key_manager = Net::SSH::Authentication::KeyManager.new(nil, options)
50
+
51
+ unless options[:key_data] || options[:keys] || options[:password] || key_manager.agent
52
+ raise ArgumentError.new(':key_data, :keys, :password or a loaded ssh-agent is required to initialize SSH')
53
+ end
54
+
55
+ options[:timeout] = 30
56
+ if options[:key_data] || options[:keys]
57
+ options[:keys_only] = true
58
+ #Explicitly set these so net-ssh doesn't add the default keys
59
+ #as seen at https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/authentication/session.rb#L131-146
60
+ options[:keys] = [] unless options[:keys]
61
+ options[:key_data] = [] unless options[:key_data]
62
+ end
63
+
64
+ @address = address
65
+ @username = username
66
+ @options = { :paranoid => false }.merge(options)
67
+ end
68
+
69
+ def upload(local_path, remote_path, upload_options = {}, &block)
70
+ begin
71
+ Net::SCP.start(@address, @username, @options) do |scp|
72
+ scp.upload!(local_path, remote_path, upload_options) do |ch, name, sent, total|
73
+ block.call(ch, name, sent, total) if block
74
+ end
75
+ end
76
+ rescue Exception => error
77
+ raise error
78
+ end
79
+ end
80
+
81
+ def download(remote_path, local_path, download_options = {}, &block)
82
+ begin
83
+ Net::SCP.start(@address, @username, @options) do |scp|
84
+ scp.download!(remote_path, local_path, download_options) do |ch, name, sent, total|
85
+ block.call(ch, name, sent, total) if block
86
+ end
87
+ end
88
+ rescue Exception => error
89
+ raise error
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+ end
@@ -0,0 +1,223 @@
1
+ module Fog
2
+
3
+ def self.services
4
+ @services ||= {}
5
+ end
6
+
7
+ class Service
8
+
9
+ class Error < Fog::Errors::Error; end
10
+ class NotFound < Fog::Errors::NotFound; end
11
+
12
+ module NoLeakInspector
13
+ def inspect
14
+ "#<#{self.class}:#{self.object_id} #{(self.instance_variables - service.secrets).map {|iv| [iv, self.instance_variable_get(iv).inspect].join('=')}.join(' ')}>"
15
+ end
16
+ end
17
+
18
+ module Collections
19
+
20
+ def collections
21
+ service.collections
22
+ end
23
+
24
+ def mocked_requests
25
+ service.mocked_requests
26
+ end
27
+
28
+ def requests
29
+ service.requests
30
+ end
31
+
32
+ end
33
+
34
+ class << self
35
+
36
+ def inherited(child)
37
+ child.class_eval <<-EOS, __FILE__, __LINE__
38
+ class Error < Fog::Service::Error; end
39
+ class NotFound < Fog::Service::NotFound; end
40
+
41
+ module Collections
42
+ include Fog::Service::Collections
43
+
44
+ def service
45
+ #{child}
46
+ end
47
+ end
48
+
49
+ def self.service
50
+ #{child}
51
+ end
52
+ EOS
53
+ end
54
+
55
+ def new(options={})
56
+ options = Fog.symbolize_credentials(options)
57
+ options = fetch_credentials(options).merge(options)
58
+ validate_options(options)
59
+ coerce_options(options)
60
+ setup_requirements
61
+
62
+ if Fog.mocking?
63
+ service::Mock.send(:include, service::Collections)
64
+ service::Mock.new(options)
65
+ else
66
+ service::Real.send(:include, service::Collections)
67
+ service::Real.send(:include, service::NoLeakInspector)
68
+ service::Real.new(options)
69
+ end
70
+ end
71
+
72
+ def fetch_credentials(options)
73
+ # attempt to load credentials from config file
74
+ begin
75
+ Fog.credentials.reject {|key, value| !(recognized | requirements).include?(key)}
76
+ rescue LoadError
77
+ # if there are no configured credentials, do nothing
78
+ {}
79
+ end
80
+ end
81
+
82
+ def setup_requirements
83
+ if superclass.respond_to?(:setup_requirements)
84
+ superclass.setup_requirements
85
+ end
86
+
87
+ @required ||= false
88
+ unless @required
89
+ for collection in collections
90
+ require [@model_path, collection].join('/')
91
+ constant = collection.to_s.split('_').map {|characters| characters[0...1].upcase << characters[1..-1]}.join('')
92
+ service::Collections.module_eval <<-EOS, __FILE__, __LINE__
93
+ def #{collection}(attributes = {})
94
+ #{service}::#{constant}.new({:service => self}.merge(attributes))
95
+ end
96
+ EOS
97
+ end
98
+ for model in models
99
+ require [@model_path, model].join('/')
100
+ end
101
+ for request in requests
102
+ require [@request_path, request].join('/')
103
+ if service::Mock.method_defined?(request)
104
+ mocked_requests << request
105
+ else
106
+ service::Mock.module_eval <<-EOS, __FILE__, __LINE__
107
+ def #{request}(*args)
108
+ Fog::Mock.not_implemented
109
+ end
110
+ EOS
111
+ end
112
+ end
113
+ @required = true
114
+ end
115
+ end
116
+
117
+ def model_path(new_path)
118
+ @model_path = new_path
119
+ end
120
+
121
+ def collection(new_collection)
122
+ collections << new_collection
123
+ end
124
+
125
+ def collections
126
+ @collections ||= []
127
+ end
128
+
129
+ def coerce_options(options)
130
+ options.each do |key, value|
131
+ value_string = value.to_s.downcase
132
+ if value.nil?
133
+ options.delete(key)
134
+ elsif value == value_string.to_i.to_s
135
+ options[key] = value.to_i
136
+ else
137
+ options[key] = case value_string
138
+ when 'false'
139
+ false
140
+ when 'true'
141
+ true
142
+ else
143
+ value
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ def mocked_requests
150
+ @mocked_requests ||= []
151
+ end
152
+
153
+ def model(new_model)
154
+ models << new_model
155
+ end
156
+
157
+ def models
158
+ @models ||= []
159
+ end
160
+
161
+ def request_path(new_path)
162
+ @request_path = new_path
163
+ end
164
+
165
+ def request(new_request)
166
+ requests << new_request
167
+ end
168
+
169
+ def requests
170
+ @requests ||= []
171
+ end
172
+
173
+ def secrets(*args)
174
+ if args.empty?
175
+ @secrets ||= []
176
+ else
177
+ args.inject(secrets) do |secrets, secret|
178
+ secrets << "@#{secret}".to_sym
179
+ end
180
+ end
181
+ end
182
+
183
+ def requires(*args)
184
+ requirements.concat(args)
185
+ end
186
+
187
+ def requirements
188
+ @requirements ||= []
189
+ end
190
+
191
+ def recognizes(*args)
192
+ recognized.concat(args)
193
+ end
194
+
195
+ def recognized
196
+ @recognized ||= [:connection_options]
197
+ end
198
+
199
+ def validate_options(options)
200
+ keys = []
201
+ for key, value in options
202
+ unless value.nil?
203
+ keys << key
204
+ end
205
+ end
206
+ missing = requirements - keys
207
+ unless missing.empty?
208
+ raise ArgumentError, "Missing required arguments: #{missing.join(', ')}"
209
+ end
210
+
211
+ unless recognizes.empty?
212
+ unrecognized = options.keys - requirements - recognized
213
+ unless unrecognized.empty?
214
+ Fog::Logger.warning("Unrecognized arguments: #{unrecognized.join(', ')}")
215
+ end
216
+ end
217
+ end
218
+
219
+ end
220
+
221
+ end
222
+ end
223
+