chef-provisioning-fog 0.20.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d393fe60b94489dca31ede225f1d5729510eb8e5
4
- data.tar.gz: 2c0ca50215ed95b55918657d64c67baecc6c2c1c
3
+ metadata.gz: db6ee7ec27d271fb3880b648dcaab0aa3dc6c3e3
4
+ data.tar.gz: 3562d4b915fae443ef15cbe7f48e9a1bf5d0af40
5
5
  SHA512:
6
- metadata.gz: 24a68b07d7ae4492f9b31898f2850f4abf824d6deb3ffac909f21e4e38b163b1cb3d4cc40d90a73f367b35df53cf27dc5fa53f64366d371bd72767f1eb9843a1
7
- data.tar.gz: 373dc190d606cb7e931bf662ed31ee1ca2df1a7ba09546dad836c8d559672303f241ae7f308b42920b2e284f8ce9d4cf66d1c70c8ef732e8c844c909b3e4f376
6
+ metadata.gz: 510f83a7ecc7ef0ab683e767fc0490039b2f812fe6827fbbf46cf85034d05663d974676ec6f8e4891413decb4409d12fc5d08649ba45c6dd8e659fe0c979cbea
7
+ data.tar.gz: 318681b378186a7219bae547ad29b96eaac2b2053a97f93a0a15fd38150388790eee279bfb91549c8542e4b455b024db5222346dd339cf096c3243ca9f373ed9
data/Gemfile CHANGED
@@ -3,7 +3,7 @@ gemfile
3
3
  gemspec
4
4
 
5
5
  group :development do
6
- gem "chef", git: "https://github.com/chef/chef" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.2.2") # until stable 12.14 is released (won't load new cheffish and such otherwise)
6
+ gem "chef"
7
7
  gem 'guard'
8
8
  gem 'guard-rspec'
9
9
  gem 'rb-readline'
data/README.md CHANGED
@@ -210,6 +210,24 @@ with_driver "fog:XenServer:<XEN-SERVER-IP/NAME>", compute_options: {
210
210
 
211
211
  For a full example see [examples/xenserver/simple.rb](examples/xenserver/simple.rb).
212
212
 
213
+ ### Scaleway
214
+
215
+ You should configure the driver with your credentials or :
216
+
217
+ ``` ruby
218
+ with_driver "fog:Scaleway:Your-Organisation-UUID:region", compute_options: {
219
+ scalewat_token: 'your-api-token',
220
+ }
221
+ ```
222
+
223
+ or just use the fog configuration (~/.fog):
224
+
225
+ ``` ruby
226
+ with_driver 'fog:Scaleway'
227
+ ```
228
+
229
+ For full examples, see [examples/scaleway](examples/scaleway).
230
+
213
231
  ### Cleaning up
214
232
 
215
233
  ```ruby
@@ -17,7 +17,9 @@ Gem::Specification.new do |s|
17
17
  s.add_dependency 'google-api-client', "~> 0.8.0"
18
18
  s.add_dependency 'fog-softlayer' , '~> 1.1.0'
19
19
  s.add_dependency 'fog-digitalocean'
20
+ s.add_dependency 'fog-scaleway'
20
21
  s.add_dependency 'retryable'
22
+ s.add_dependency 'winrm-elevated'
21
23
 
22
24
  s.add_development_dependency 'rspec'
23
25
  s.add_development_dependency 'rake'
@@ -0,0 +1,53 @@
1
+ require 'chef/provider/lwrp_base'
2
+ require 'openssl'
3
+ require 'chef/provisioning/chef_provider_action_handler'
4
+ require 'chef/provisioning/chef_managed_entry_store'
5
+
6
+ class Chef
7
+ class Provider
8
+ class ScalewayVolume < Chef::Provider::LWRPBase
9
+ provides :scaleway_volume
10
+
11
+ def action_handler
12
+ @action_handler ||= Chef::Provisioning::ChefProviderActionHandler.new(self)
13
+ end
14
+
15
+ def load_current_resource
16
+ end
17
+
18
+ def whyrun_supported?
19
+ true
20
+ end
21
+
22
+ def volume_spec
23
+ @volume_spec ||= chef_managed_entry_store.get_or_new(:volume, new_resource.name)
24
+ end
25
+
26
+ # Get the driver specified in the resource
27
+ def new_driver
28
+ @new_driver ||= run_context.chef_provisioning.driver_for(new_resource.driver)
29
+ end
30
+
31
+ def chef_managed_entry_store
32
+ @chef_managed_entry_store ||= Provisioning.chef_managed_entry_store(new_resource.chef_server)
33
+ end
34
+
35
+
36
+ action :create do
37
+ unless volume_spec.reference && new_driver.volume_for(volume_spec)
38
+ new_driver.create_volume(action_handler, volume_spec,
39
+ new_resource.volume_options)
40
+ end
41
+ new_resource.id = volume_spec.reference['id']
42
+ end
43
+
44
+ action :destroy do
45
+ if volume_spec.reference && volume_spec.reference['id']
46
+ new_driver.destroy_volume(action_handler, volume_spec,
47
+ new_resource.volume_options)
48
+ volume_spec.delete(action_handler)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -277,6 +277,14 @@ module FogDriver
277
277
  end
278
278
  end
279
279
 
280
+ def create_volume(action_handler, volume_spec, volume_options)
281
+ raise "##create_volume not implemented in in #{self.class}"
282
+ end
283
+
284
+ def destroy_volume(action_handler, volume_spec, volume_options)
285
+ raise "##destroy_volume not implemented in in #{self.class}"
286
+ end
287
+
280
288
  protected
281
289
 
282
290
  def option_for(machine_options, key)
@@ -308,13 +316,13 @@ module FogDriver
308
316
  Chef::Log.warn "Machine #{machine_spec.name} (#{machine_spec.reference['server_id']} on #{driver_url}) no longer exists. Recreating ..."
309
317
  end
310
318
 
311
- machine_spec.reference ||= {}
312
- machine_spec.reference.update(
313
- 'driver_url' => driver_url,
314
- 'driver_version' => FogDriver::VERSION,
315
- 'creator' => creator,
316
- 'allocated_at' => Time.now.to_i
317
- )
319
+ machine_spec.reference ||= {}
320
+ machine_spec.reference.update(
321
+ 'driver_url' => driver_url,
322
+ 'driver_version' => FogDriver::VERSION,
323
+ 'creator' => creator,
324
+ 'allocated_at' => Time.now.to_i
325
+ )
318
326
 
319
327
  bootstrap_options = bootstrap_options_for(action_handler, machine_spec, machine_options)
320
328
  machine_spec.reference['key_name'] = bootstrap_options[:key_name] if bootstrap_options[:key_name]
@@ -368,6 +376,7 @@ module FogDriver
368
376
  def create_many_servers(num_servers, bootstrap_options, parallelizer)
369
377
  parallelizer.parallelize(1.upto(num_servers)) do |i|
370
378
  clean_bootstrap_options = Marshal.load(Marshal.dump(bootstrap_options)) # Prevent destructive operations on bootstrap_options.
379
+
371
380
  server = compute.servers.create(clean_bootstrap_options)
372
381
  yield server if block_given?
373
382
  server
@@ -483,7 +492,6 @@ module FogDriver
483
492
  end
484
493
  server.reload
485
494
  end
486
-
487
495
  end
488
496
  end
489
497
 
@@ -616,6 +624,10 @@ module FogDriver
616
624
  end
617
625
  end
618
626
 
627
+ def volume_for(volume_spec)
628
+ raise "Immplement me in #{self.class.name}"
629
+ end
630
+
619
631
  def convergence_strategy_for(machine_spec, machine_options)
620
632
  # Defaults
621
633
  if !machine_spec.reference
@@ -48,7 +48,7 @@ module FogDriver
48
48
  result = Cheffish::MergedConfig.new(new_config, config, new_defaults)
49
49
 
50
50
  new_compute_options[:openstack_auth_url] = id if (id && id != '')
51
-
51
+
52
52
  credential = Fog.credentials.find_all{ |k,v|
53
53
  k.to_s.start_with?('openstack') }
54
54
  credential.each { |k,v|
@@ -0,0 +1,248 @@
1
+ # fog:OpenStack:https://identifyhost:portNumber/v2.0
2
+
3
+ require 'fog/scaleway'
4
+ require 'fog/scaleway/models/compute/server'
5
+
6
+ #
7
+ # We're monkeypatching here to avoid overriding too much of the base
8
+ # class. This monkey patch will be removed if we can merge those changes
9
+ # upstream
10
+ #
11
+ class Fog::Scaleway::Compute::Server
12
+ alias :start :poweron
13
+ alias :stop :poweroff
14
+
15
+ def disassociate_address(ip)
16
+ ip = ip.address if ip.respond_to? :address
17
+ if public_ip && public_ip.address == ip
18
+ public_ip.server = nil
19
+ public_ip.save
20
+ end
21
+ reload
22
+ end
23
+ end
24
+
25
+ class Chef
26
+ module Provisioning
27
+ module FogDriver
28
+ module Providers
29
+ class Scaleway < FogDriver::Driver
30
+ Driver.register_provider_class('Scaleway', FogDriver::Providers::Scaleway)
31
+
32
+ def creator
33
+ compute_options[:scaleway_organization]
34
+ end
35
+
36
+ def convergence_strategy_for(machine_spec, machine_options)
37
+ machine_options = Cheffish::MergedConfig.new(machine_options, {
38
+ :convergence_options => {:ohai_hints => {'scaleway' => {}}}
39
+ })
40
+ super(machine_spec, machine_options)
41
+ end
42
+
43
+ def bootstrap_options_for(action_handler, machine_spec, machine_options)
44
+ opts = super
45
+ opts[:tags] = opts[:tags].map { |key, value| [key, value].join('=') }
46
+
47
+ # Let's fetch the id of the volumes if the user didn't provide it
48
+ # Which probably means they were created in chef
49
+ if opts[:volumes]
50
+ managed_entry_store = machine_spec.managed_entry_store
51
+ volumes = Marshal.load(Marshal.dump(opts[:volumes]))
52
+
53
+ volumes.each do |index, volume|
54
+ unless volume[:id]
55
+ volume_spec = managed_entry_store.get(:volume, volume[:name])
56
+ unless volume_spec
57
+ raise "Volume #{volume[:name]} unknown, create it or provide its id"
58
+ end
59
+ volume[:id] = volume_spec.reference['id']
60
+ end
61
+ end
62
+ opts[:volumes] = volumes
63
+ end
64
+ opts
65
+ end
66
+
67
+ def destroy_machine(action_handler, machine_spec, machine_options)
68
+ server = server_for(machine_spec)
69
+ if server
70
+ action_handler.perform_action "destroy machine #{machine_spec.name} (#{machine_spec.reference['server_id']} at #{driver_url})" do
71
+
72
+ # Scaleway's API fail if we try to stop/terminate an instance with
73
+ # certains states
74
+ if server.state == 'running'
75
+ server.stop
76
+ server.wait_for { server.state != 'running' }
77
+ end
78
+ ['stopping', 'starting'].each do |state|
79
+ server.wait_for { server.state != state } if server.state == state
80
+ end
81
+
82
+ if server.state == 'stopped'
83
+ server.destroy
84
+ else
85
+ Chef::log.fatal "Server is in an unknown state (#{server.state})"
86
+ end
87
+ machine_spec.reference = nil
88
+ end
89
+ end
90
+ strategy = ConvergenceStrategy::NoConverge.new(machine_options[:convergence_options], config)
91
+ strategy.cleanup_convergence(action_handler, machine_spec)
92
+ end
93
+
94
+ def stop_machine(action_handler, machine_spec, machine_options)
95
+ server = server_for(machine_spec)
96
+ if server and server.state == 'running'
97
+ action_handler.perform_action "stop machine #{machine_spec.name} (#{server.id} at #{driver_url})" do
98
+ server.poweroff(true)
99
+ server.wait_for { server.state == 'stopped' }
100
+ end
101
+ end
102
+ end
103
+
104
+ def self.compute_options_for(provider, id, config)
105
+ new_compute_options = {}
106
+ new_compute_options[:provider] = provider
107
+ if (id && id != '')
108
+ org, region = id.split(':')
109
+ new_compute_options[:scaleway_organization] = org
110
+ new_compute_options[:scaleway_region] = region || 'par1'
111
+ end
112
+ new_config = { :driver_options => { :compute_options => new_compute_options }}
113
+
114
+ new_default_compute_options = {}
115
+ new_defaults = {
116
+ :driver_options => { :compute_options => new_default_compute_options },
117
+ :machine_options => { :bootstrap_options => {} }
118
+ }
119
+
120
+ result = Cheffish::MergedConfig.new(new_config, config, new_defaults)
121
+
122
+ credential = Fog.credentials
123
+ new_default_compute_options[:scaleway_organization] ||= credential[:scaleway_organization]
124
+ new_default_compute_options[:scaleway_token] ||= credential[:scaleway_token]
125
+
126
+ id = [result[:driver_options][:compute_options][:scaleway_organization],
127
+ result[:driver_options][:compute_options][:scaleway_region]].join(':')
128
+
129
+ [result, id]
130
+ end
131
+
132
+ def converge_floating_ips(action_handler, machine_spec, machine_options, server)
133
+ if server.dynamic_ip_required
134
+ Chef::Log.info "Dynamic IP allocation has been enabled, not converging IPs"
135
+ else
136
+ super
137
+ end
138
+ end
139
+
140
+ # Scaleway only has one global pool.
141
+ def attach_ip_from_pool(server, pool)
142
+ return server if server.public_ip
143
+
144
+ Chef::Log.info "Scaleway has only one IP pool, ignoring pool argument"
145
+ ip = server.service.ips.all.select { |ip| ip.address.nil? }.first
146
+ if ip
147
+ ip.server = server
148
+ ip.save
149
+ server.reload
150
+ else
151
+ # Allocate a new IP
152
+ ip = server.service.ips.create
153
+ ip.server = server
154
+ ip.save
155
+ server.reload
156
+ end
157
+ end
158
+
159
+ def attach_ip(server, floating_ip)
160
+ ip = server.service.ips.get(floating_ip)
161
+ if ip.nil?
162
+ raise RuntimeError, "Requested IP (#{floating_ip}) not found"
163
+ end
164
+ if ip.server and ip.server.identity != server.identity
165
+ raise RuntimeError, "Requested IP (#{floating_ip}) already attached"
166
+ end
167
+
168
+ if server.public_ip
169
+ old_ip = server.public_ip
170
+ Chef::Log.info "Server #{server.identity} already has IP #{old_ip.address}, removing it"
171
+ old_ip.server = nil
172
+ old_ip.save
173
+ end
174
+
175
+ ip.server = server
176
+ ip.save
177
+ server.reload
178
+ end
179
+
180
+ # Get the public IP if any
181
+ def find_floating_ips(server, action_handler)
182
+ public_ips = []
183
+ Retryable.retryable(RETRYABLE_OPTIONS) do |retries, _exception|
184
+ action_handler.report_progress "Querying for public IP attached to server #{server.id}, API attempt #{retries+1}/#{RETRYABLE_OPTIONS[:tries]} ..."
185
+ public_ips << server.public_ip.address if server.public_ip
186
+ end
187
+ public_ips
188
+ end
189
+
190
+
191
+ def create_volume(action_handler, volume_spec, volume_options)
192
+ # Prevent destructive operations on volume_options.
193
+ clean_volume_options = Marshal.load(Marshal.dump(volume_options))
194
+
195
+ volume_spec.reference ||= {}
196
+ volume_spec.reference.update(
197
+ 'driver_url' => driver_url,
198
+ 'driver_version' => FogDriver::VERSION,
199
+ 'creator' => creator,
200
+ 'allocated_at' => Time.now.to_i,
201
+ )
202
+
203
+ description = ["Creating volume #{volume_spec.name}"]
204
+ volume_options.each { |k, v| description << " #{k}: #{v.inspect}"}
205
+
206
+ action_handler.report_progress description
207
+ if action_handler.should_perform_actions
208
+ clean_volume_options['name'] = volume_spec.name
209
+
210
+ volume = compute.volumes.create(clean_volume_options)
211
+
212
+ volume_spec.reference.update(
213
+ 'id' => volume.id,
214
+ 'volume_type' => volume.volume_type,
215
+ 'size' => volume.size
216
+ )
217
+ volume_spec.save(action_handler)
218
+ action_handler.performed_action "volume #{volume_spec.name} created as #{volume.id} on #{driver_url}"
219
+ end
220
+ end
221
+
222
+ def destroy_volume(action_handler, volume_spec, volume_options)
223
+ volume = volume_for(volume_spec)
224
+
225
+ if volume && action_handler.should_perform_actions
226
+ begin
227
+ msg = "destroyed volume #{volume_spec.name} at #{driver_url}"
228
+ action_handler.perform_action msg do
229
+ volume.destroy
230
+ volume_spec.reference = nil
231
+ volume_spec.save(action_handler)
232
+ end
233
+ rescue Fog::Scaleway::Compute::InvalidRequestError => e
234
+ Chef::Log.error "Unable to destroy volume #{volume_spec.name} : #{e.message}"
235
+ end
236
+ end
237
+ end
238
+
239
+ def volume_for(volume_spec)
240
+ if volume_spec.reference
241
+ compute.volumes.get(volume_spec.reference['id'])
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
@@ -1,6 +1,8 @@
1
1
  require 'chef/provisioning/fog_driver/driver'
2
2
  require 'chef/resource/fog_key_pair'
3
3
  require 'chef/provider/fog_key_pair'
4
+ require 'chef/resource/scaleway_volume'
5
+ require 'chef/provider/scaleway_volume'
4
6
 
5
7
  class Chef
6
8
  module DSL
@@ -27,6 +29,10 @@ class Chef
27
29
  with_fog_driver('Vcair', driver_options, &block)
28
30
  end
29
31
 
32
+ def with_fog_scaleway_driver(driver_options = nil, &block)
33
+ with_fog_driver('Scaleway', driver_options, &block)
34
+ end
35
+
30
36
  end
31
37
  end
32
38
  end
@@ -1,7 +1,7 @@
1
1
  class Chef
2
2
  module Provisioning
3
3
  module FogDriver
4
- VERSION = '0.20.0'
4
+ VERSION = '0.21.0'
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,32 @@
1
+ require 'chef/provisioning'
2
+
3
+ class Chef::Resource::ScalewayVolume < Chef::Resource::LWRPBase
4
+ self.resource_name = 'scaleway_volume'
5
+
6
+ def initialize(*args)
7
+ super
8
+ @driver = run_context.chef_provisioning.current_driver
9
+ @chef_server = run_context.cheffish.current_chef_server
10
+ end
11
+
12
+ actions :create, :destroy, :nothing
13
+ default_action :create
14
+
15
+ attribute :id
16
+ attribute :chef_server
17
+ attribute :driver
18
+ attribute :volume_options
19
+
20
+ def add_volume_options(options)
21
+ if @volume_options
22
+ @volume_options = Cheffish::MergedConfig.new(options, @volume_options)
23
+ else
24
+ @volume_options = options
25
+ end
26
+ end
27
+
28
+ # We are not interested in Chef's cloning behavior here.
29
+ def load_prior_resource(*args)
30
+ Chef::Log.debug("Overloading #{resource_name}.load_prior_resource with NOOP")
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+ require 'chef/provisioning/fog_driver/providers/scaleway'
3
+
4
+ describe Chef::Provisioning::FogDriver::Providers::Scaleway do
5
+ subject do
6
+ Chef::Provisioning::FogDriver::Driver.from_provider(
7
+ 'Scaleway', driver_options: { compute_options: {
8
+ scaleway_organization: 'org',
9
+ scaleway_token: 'key'}
10
+ }
11
+ )
12
+ end
13
+
14
+ it "returns the correct driver" do
15
+ expect(subject).to be_an_instance_of Chef::Provisioning::FogDriver::Providers::Scaleway
16
+ end
17
+
18
+ it "has a Fog backend" do
19
+ pending unless Fog.mock?
20
+ expect(subject.compute).to be_an_instance_of Fog::Scaleway::Compute::Mock
21
+ end
22
+
23
+ describe '#creator' do
24
+ it 'returns the organization' do
25
+ expect(subject.creator).to eq 'org'
26
+ end
27
+ end
28
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-provisioning-fog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.0
4
+ version: 0.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Keiser
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2016-08-30 00:00:00.000000000 Z
15
+ date: 2017-03-03 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: chef-provisioning
@@ -90,6 +90,20 @@ dependencies:
90
90
  - - ">="
91
91
  - !ruby/object:Gem::Version
92
92
  version: '0'
93
+ - !ruby/object:Gem::Dependency
94
+ name: fog-scaleway
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ type: :runtime
101
+ prerelease: false
102
+ version_requirements: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
93
107
  - !ruby/object:Gem::Dependency
94
108
  name: retryable
95
109
  requirement: !ruby/object:Gem::Requirement
@@ -104,6 +118,20 @@ dependencies:
104
118
  - - ">="
105
119
  - !ruby/object:Gem::Version
106
120
  version: '0'
121
+ - !ruby/object:Gem::Dependency
122
+ name: winrm-elevated
123
+ requirement: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ type: :runtime
129
+ prerelease: false
130
+ version_requirements: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
107
135
  - !ruby/object:Gem::Dependency
108
136
  name: rspec
109
137
  requirement: !ruby/object:Gem::Requirement
@@ -165,6 +193,7 @@ files:
165
193
  - Rakefile
166
194
  - chef-provisioning-fog.gemspec
167
195
  - lib/chef/provider/fog_key_pair.rb
196
+ - lib/chef/provider/scaleway_volume.rb
168
197
  - lib/chef/provisioning/driver_init/fog.rb
169
198
  - lib/chef/provisioning/fog_driver/driver.rb
170
199
  - lib/chef/provisioning/fog_driver/providers/aws.rb
@@ -175,12 +204,14 @@ files:
175
204
  - lib/chef/provisioning/fog_driver/providers/joyent.rb
176
205
  - lib/chef/provisioning/fog_driver/providers/openstack.rb
177
206
  - lib/chef/provisioning/fog_driver/providers/rackspace.rb
207
+ - lib/chef/provisioning/fog_driver/providers/scaleway.rb
178
208
  - lib/chef/provisioning/fog_driver/providers/softlayer.rb
179
209
  - lib/chef/provisioning/fog_driver/providers/vcair.rb
180
210
  - lib/chef/provisioning/fog_driver/providers/xenserver.rb
181
211
  - lib/chef/provisioning/fog_driver/recipe_dsl.rb
182
212
  - lib/chef/provisioning/fog_driver/version.rb
183
213
  - lib/chef/resource/fog_key_pair.rb
214
+ - lib/chef/resource/scaleway_volume.rb
184
215
  - spec/spec_helper.rb
185
216
  - spec/support/aws/config-file.csv
186
217
  - spec/support/aws/ini-file.ini
@@ -189,6 +220,7 @@ files:
189
220
  - spec/unit/fog_driver_spec.rb
190
221
  - spec/unit/providers/aws/credentials_spec.rb
191
222
  - spec/unit/providers/rackspace_spec.rb
223
+ - spec/unit/providers/scaleway_spec.rb
192
224
  - spec/unit/providers/softlayer.rb
193
225
  homepage: https://github.com/opscode/chef-provisioning-fog
194
226
  licenses: []