knife-cloud 1.0.0.rc.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +33 -0
  3. data/.travis.yml +7 -0
  4. data/CHANGELOG.md +11 -0
  5. data/CONTRIBUTING.md +5 -0
  6. data/Gemfile +9 -0
  7. data/LICENSE +201 -0
  8. data/README.md +420 -0
  9. data/Rakefile +35 -0
  10. data/knife-cloud.gemspec +27 -0
  11. data/lib/chef/knife/cloud/chefbootstrap/bootstrap_distribution.rb +31 -0
  12. data/lib/chef/knife/cloud/chefbootstrap/bootstrap_options.rb +191 -0
  13. data/lib/chef/knife/cloud/chefbootstrap/bootstrap_protocol.rb +69 -0
  14. data/lib/chef/knife/cloud/chefbootstrap/bootstrapper.rb +78 -0
  15. data/lib/chef/knife/cloud/chefbootstrap/ssh_bootstrap_protocol.rb +179 -0
  16. data/lib/chef/knife/cloud/chefbootstrap/unix_distribution.rb +31 -0
  17. data/lib/chef/knife/cloud/chefbootstrap/windows_distribution.rb +32 -0
  18. data/lib/chef/knife/cloud/chefbootstrap/winrm_bootstrap_protocol.rb +85 -0
  19. data/lib/chef/knife/cloud/command.rb +101 -0
  20. data/lib/chef/knife/cloud/exceptions.rb +38 -0
  21. data/lib/chef/knife/cloud/fog/options.rb +29 -0
  22. data/lib/chef/knife/cloud/fog/service.rb +200 -0
  23. data/lib/chef/knife/cloud/helpers.rb +39 -0
  24. data/lib/chef/knife/cloud/list_resource_command.rb +97 -0
  25. data/lib/chef/knife/cloud/list_resource_options.rb +21 -0
  26. data/lib/chef/knife/cloud/server/create_command.rb +165 -0
  27. data/lib/chef/knife/cloud/server/create_options.rb +80 -0
  28. data/lib/chef/knife/cloud/server/delete_command.rb +68 -0
  29. data/lib/chef/knife/cloud/server/delete_options.rb +42 -0
  30. data/lib/chef/knife/cloud/server/list_command.rb +84 -0
  31. data/lib/chef/knife/cloud/server/list_options.rb +43 -0
  32. data/lib/chef/knife/cloud/server/options.rb +39 -0
  33. data/lib/chef/knife/cloud/server/show_command.rb +55 -0
  34. data/lib/chef/knife/cloud/server/show_options.rb +36 -0
  35. data/lib/chef/knife/cloud/service.rb +91 -0
  36. data/lib/knife-cloud/version.rb +6 -0
  37. data/lib/test/fixtures/knife.rb +9 -0
  38. data/lib/test/fixtures/validation.pem +27 -0
  39. data/lib/test/knife-utils/helper.rb +39 -0
  40. data/lib/test/knife-utils/knife_test_utils.rb +40 -0
  41. data/lib/test/knife-utils/matchers.rb +29 -0
  42. data/lib/test/knife-utils/test_bed.rb +56 -0
  43. data/lib/test/templates/chef-full-chef-zero.erb +67 -0
  44. data/lib/test/templates/windows-chef-client-msi.erb +231 -0
  45. data/lib/test/templates/windows-shell.erb +77 -0
  46. data/spec/resource_spec_helper.rb +49 -0
  47. data/spec/server_command_common_spec_helper.rb +48 -0
  48. data/spec/spec_helper.rb +25 -0
  49. data/spec/support/shared_examples_for_command.rb +35 -0
  50. data/spec/support/shared_examples_for_servercreatecommand.rb +144 -0
  51. data/spec/support/shared_examples_for_serverdeletecommand.rb +77 -0
  52. data/spec/support/shared_examples_for_service.rb +85 -0
  53. data/spec/unit/bootstrap_protocol_spec.rb +70 -0
  54. data/spec/unit/bootstrapper_spec.rb +171 -0
  55. data/spec/unit/cloud_command_spec.rb +35 -0
  56. data/spec/unit/command_spec.rb +49 -0
  57. data/spec/unit/fog_service_spec.rb +138 -0
  58. data/spec/unit/list_resource_command_spec.rb +136 -0
  59. data/spec/unit/server_create_command_spec.rb +198 -0
  60. data/spec/unit/server_delete_command_spec.rb +25 -0
  61. data/spec/unit/server_list_command_spec.rb +119 -0
  62. data/spec/unit/server_show_command_spec.rb +64 -0
  63. data/spec/unit/service_spec.rb +46 -0
  64. data/spec/unit/ssh_bootstrap_protocol_spec.rb +116 -0
  65. data/spec/unit/unix_distribution_spec.rb +37 -0
  66. data/spec/unit/windows_distribution_spec.rb +37 -0
  67. data/spec/unit/winrm_bootstrap_protocol_spec.rb +106 -0
  68. metadata +248 -0
data/README.md ADDED
@@ -0,0 +1,420 @@
1
+ Knife Cloud
2
+ ===============
3
+
4
+ [![Build Status](http://img.shields.io/travis/opscode/knife-cloud.svg)][travis]
5
+ [![Code Climate](http://img.shields.io/codeclimate/github/opscode/knife-cloud.svg)][codeclimate]
6
+
7
+ [travis]: https://travis-ci.org/opscode/knife-cloud
8
+ [codeclimate]: https://codeclimate.com/github/opscode/knife-cloud
9
+
10
+ ## Description
11
+
12
+ Knife-cloud is a library for implementing knife plugins that integrate cloud
13
+ infrastructure with Chef. For more information about knife and Chef visit https://getchef.com/chef.
14
+
15
+ ## Purpose
16
+
17
+ The knife-cloud library has been designed to integrate the common tasks of all knife plugins. As a developer of a knife plugin, you will not have to worry about writing generic code in your plugin, eg: the Chef bootstrap code or SSH / WinRM connection code.
18
+
19
+ ## Installation
20
+
21
+ This library is distributed as a Ruby Gem. To install it, run:
22
+
23
+ $ gem install knife-cloud
24
+
25
+ Depending on your system's configuration, you may need to run this command with root privileges.
26
+ Alternatively, you can build the gem from the knife-cloud source code.
27
+
28
+ $ git clone https://github.com/opscode/knife-cloud
29
+ $ cd knife-cloud
30
+ $ rake gem
31
+ $ gem install knife-cloud-x.y.z.gem
32
+
33
+ ## Writing your custom plugin
34
+
35
+ General documentation of how to develop a knife plugin can be found in
36
+ [Chef documentation](http://docs.getchef.com/plugin_knife_custom.html). Use of
37
+ the `knife-cloud` gem to implement the plugin automates many aspects of the
38
+ process.
39
+
40
+ Here is an example of how `knife-cloud` can be used. First, create a new ruby project, say knife-myplugin. Add the knife-cloud gem to its gemspec.
41
+
42
+ Sample gemspec:
43
+
44
+ # -*- encoding: utf-8 -*-
45
+ $:.push File.expand_path("../lib", __FILE__)
46
+ require "knife-myplugin/version"
47
+
48
+ Gem::Specification.new do |s|
49
+ s.name = "knife-myplugin"
50
+ s.version = Knife::Myplugin::VERSION
51
+ s.platform = Gem::Platform::RUBY
52
+ s.has_rdoc = true
53
+ s.extra_rdoc_files = ["README.md", "LICENSE" ]
54
+ s.authors = ["RockingSoul"]
55
+ s.email = ["rocking.soul@dreamworld.com"]
56
+ s.homepage = "https://github.com/dreamworld/knife-myplugin"
57
+ s.summary = %q{TODO}
58
+ s.description = %q{TODO}
59
+
60
+ s.files = `git ls-files`.split("\n")
61
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
62
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
63
+ s.require_paths = ["lib"]
64
+
65
+ s.add_dependency "knife-cloud"
66
+
67
+ %w(rspec-core rspec-expectations rspec-mocks rspec_junit_formatter).each { |gem| s.add_development_dependency gem }
68
+ end
69
+
70
+ Sample Gemfile:
71
+
72
+ source "https://rubygems.org"
73
+
74
+ # Specify your gem's dependencies in knife-<yourcloud>.gemspec
75
+ gemspec
76
+
77
+ group :development do
78
+ gem 'rspec', '>= 2.7.0'
79
+ gem 'guard-rspec'
80
+ gem 'rspec_junit_formatter'
81
+ gem 'rake'
82
+ gem 'mixlib-shellout'
83
+ end
84
+
85
+ ### Code structure
86
+
87
+ Create a new ruby project, say knife-myplugin. Its code structure will look like:
88
+
89
+ lib
90
+ chef
91
+ knife
92
+ cloud
93
+ myplugin_service.rb
94
+ myplugin_service_options.rb
95
+ myplugin_server_create_options.rb
96
+ myplugin_server_create.rb
97
+ myplugin_server_delete.rb
98
+ myplugin_server_list.rb
99
+ myplugin_flavor_list.rb
100
+
101
+ ### Service
102
+
103
+ - myplugin_service.rb
104
+
105
+ Knife-cloud has a wrapper written for the Fog service. It can be used in your plugin as mentioned in the steps below. If ruby Fog does not have support for your cloud provider, you will have to write your own Service class analogous to the Chef::Knife::Cloud::FogService class which is defined in knife-cloud.
106
+ Implement your Service class which should inherit from the FogService class.
107
+
108
+ Example Code:
109
+
110
+ require 'chef/knife/cloud/fog/service'
111
+ class Chef
112
+ class Knife
113
+ class Cloud
114
+ class MypluginService < FogService
115
+
116
+ def initialize(options = {})
117
+ # TODO - Add cloud specific auth params to be passed to fog connection. See knife-openstack for real life example.
118
+ super(options.merge({
119
+ :auth_params => {
120
+ :my_provider => 'MyPlugin',
121
+ :my_name => 'demo_name',
122
+ :my_password => 'demo_password'
123
+ }}))
124
+ end
125
+
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ #### Service Options
132
+
133
+ - myplugin_service_options.rb
134
+
135
+ This inherits all the options provided by Fog. You will have to add any cloud specific auth options.
136
+
137
+ Example Code:
138
+
139
+ require 'chef/knife/cloud/fog/options'
140
+ class Chef
141
+ class Knife
142
+ class Cloud
143
+ module MypluginServiceOptions
144
+
145
+ def self.included(includer)
146
+ includer.class_eval do
147
+ include FogOptions
148
+
149
+ # TODO - define your cloud specific auth options.
150
+ # Example:
151
+ # Myplugin Connection params.
152
+ # option :azure_username,
153
+ # :short => "-A USERNAME",
154
+ # :long => "--myplugin-username KEY",
155
+ # :description => "Your Myplugin Username",
156
+ # :proc => Proc.new { |key| Chef::Config[:knife][:myplugin_username] = key }
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ ### Server Create Command
165
+
166
+ - myplugin_server_create.rb
167
+
168
+ This class will inherit from the Chef::Knife::Cloud::ServerCreateCommand class.
169
+
170
+ require 'chef/knife/cloud/server/create_command'
171
+ require 'chef/knife/myplugin_helpers'
172
+ require 'chef/knife/cloud/myplugin_server_create_options'
173
+ require 'chef/knife/cloud/myplugin_service'
174
+ require 'chef/knife/cloud/myplugin_service_options'
175
+ require 'chef/knife/cloud/exceptions'
176
+
177
+ class Chef
178
+ class Knife
179
+ class Cloud
180
+ class MypluginServerCreate < ServerCreateCommand
181
+ include MypluginHelpers
182
+ include MypluginServerCreateOptions
183
+ include MypluginServiceOptions
184
+
185
+ banner "knife myplugin server create (options)"
186
+
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ ##### Override the methods below for plugin specific execution
193
+ - before_exec_command
194
+
195
+ - after_exec_command
196
+
197
+ - before_bootstrap
198
+
199
+ - validate_params!
200
+
201
+ ##### Code Example
202
+
203
+ Following is the code template for the above methods
204
+
205
+ def before_exec_command
206
+ # setup the create options
207
+ # TODO - update this section to define the server_def that should be passed to fog for creating VM. This will be specific to your cloud.
208
+ # Example:
209
+ @create_options = {
210
+ :server_def => {
211
+ # servers require a name, knife-cloud generates the chef_node_name
212
+ :name => config[:chef_node_name],
213
+ :image_ref => locate_config_value(:image),
214
+ :flavor_ref => locate_config_value(:flavor),
215
+ #...
216
+ },
217
+ :server_create_timeout => locate_config_value(:server_create_timeout)
218
+ }
219
+
220
+ @create_options[:server_def].merge!({:user_data => locate_config_value(:user_data)}) if locate_config_value(:user_data)
221
+
222
+ Chef::Log.debug("Create server params - server_def = #{@create_options[:server_def]}")
223
+
224
+ # TODO - Update the columns info with the keys and callbacks required as per fog object returned for your cloud. Framework looks for 'key' on your image object hash returned by fog. If you need the values to be formatted or if your value is another object that needs to be looked up use value_callback.
225
+ # Example:
226
+ @columns_with_info = [
227
+ {:label => 'Instance ID', :key => 'id'},
228
+ {:label => 'Name', :key => 'name'},
229
+ {:label => 'Public IP', :key => 'addresses', :value_callback => method(:primary_public_ip_address)},
230
+ {:label => 'Private IP', :key => 'addresses', :value_callback => method(:primary_private_ip_address)},
231
+ {:label => 'Flavor', :key => 'flavor', :value_callback => method(:get_id)},
232
+ {:label => 'Image', :key => 'image', :value_callback => method(:get_id)},
233
+ {:label => 'Keypair', :key => 'key_name'},
234
+ {:label => 'State', :key => 'state'}
235
+ ]
236
+ super
237
+ end
238
+
239
+ def get_id(value)
240
+ value['id']
241
+ end
242
+
243
+ # Setup the floating ip after server creation.
244
+ def after_exec_command
245
+ # Any action you want to perform post VM creation in your cloud.
246
+ # Example say assigning floating IP to the newly created VM.
247
+ # Make calls to "service" object if you need any information for cloud, example service.connection.addresses
248
+ # Make call to "server" object if you want set properties on newly created VM, example server.associate_address(floating_address)
249
+
250
+ super
251
+ end
252
+
253
+ def before_bootstrap
254
+ super
255
+ # TODO - Set the IP address that should be used for connection with the newly created VM. This IP address is used for bootstrapping the VM and should be accessible from knife workstation.
256
+
257
+ # your logic goes here to set bootstrap_ip_address...
258
+
259
+ Chef::Log.debug("Bootstrap IP Address: #{bootstrap_ip_address}")
260
+ if bootstrap_ip_address.nil?
261
+ error_message = "No IP address available for bootstrapping."
262
+ ui.error(error_message)
263
+ raise CloudExceptions::BootstrapError, error_message
264
+ end
265
+ config[:bootstrap_ip_address] = bootstrap_ip_address
266
+ end
267
+
268
+ def validate_params!
269
+ super
270
+ errors = []
271
+
272
+ # TODO - Add your validation here for any create server parameters and populate errors [] with error message strings.
273
+
274
+ # errors << "your error message" if some_param_undefined
275
+
276
+ error_message = ""
277
+ raise CloudExceptions::ValidationError, error_message if errors.each{|e| ui.error(e); error_message = "#{error_message} #{e}."}.any?
278
+ end
279
+
280
+ ### Server Delete Command
281
+
282
+ - myplugin_server_delete.rb
283
+
284
+ You may just need to create your MypluginServerDelete class here. Unless your plugin has some very specific delete server case, you can leave the code just like shown in the example below:
285
+
286
+ require 'chef/knife/cloud/server/delete_options'
287
+ require 'chef/knife/cloud/server/delete_command'
288
+ require 'chef/knife/cloud/myplugin_service'
289
+ require 'chef/knife/cloud/myplugin_service_options'
290
+ require 'chef/knife/myplugin_helpers'
291
+
292
+ class Chef
293
+ class Knife
294
+ class Cloud
295
+ class MypluginServerDelete < ServerDeleteCommand
296
+ include ServerDeleteOptions
297
+ include MypluginServiceOptions
298
+ include MypluginHelpers
299
+
300
+ banner "knife myplugin server delete INSTANCEID [INSTANCEID] (options)"
301
+
302
+ end
303
+ end
304
+ end
305
+ end
306
+
307
+ ### Server List Command
308
+
309
+ - myplugin_server_list.rb
310
+
311
+ Your class MypluginServerList should inherit from Chef::Knife::Cloud::ServerListCommand class.
312
+
313
+ Example -
314
+ require 'chef/knife/cloud/server/list_command'
315
+ require 'chef/knife/myplugin_helpers'
316
+ require 'chef/knife/cloud/myplugin_service_options'
317
+ require 'chef/knife/cloud/server/list_options'
318
+
319
+ class Chef
320
+ class Knife
321
+ class Cloud
322
+ class MypluginServerList < ServerListCommand
323
+ include MypluginHelpers
324
+ include MypluginServiceOptions
325
+ include ServerListOptions
326
+
327
+ banner "knife myplugin server list (options)"
328
+
329
+ def before_exec_command
330
+ # TODO - Update the columns info with the keys and callbacks required as per fog object returned for your cloud. Framework looks for 'key' on your server object hash returned by fog. If you need the values to be formatted or if your value is another object that needs to be looked up use value_callback.
331
+
332
+ @columns_with_info = [
333
+ {:label => 'Instance ID', :key => 'id'},
334
+ {:label => 'Name', :key => 'name'},
335
+ {:label => 'Public IP', :key => 'addresses', :value_callback => method(:your_callback)},
336
+ #...
337
+ ]
338
+ super
339
+ end
340
+
341
+ # TODO - callback example, this gets a object/value returned by server.addresses
342
+ def your_callback (addresses)
343
+ #...
344
+ end
345
+
346
+ end
347
+ end
348
+ end
349
+ end
350
+
351
+ ### Flavor List Command
352
+
353
+ - myplugin_flavor_list.rb
354
+
355
+ For all other list commands other than the server-list, they must inherit from the Chef::Knife::Cloud::ResourceListCommand class.
356
+
357
+ Example -
358
+
359
+ require 'chef/knife/cloud/list_resource_command'
360
+ require 'chef/knife/myplugin_helpers'
361
+ require 'chef/knife/cloud/myplugin_service_options'
362
+
363
+ class Chef
364
+ class Knife
365
+ class Cloud
366
+ class MypluginFlavorList < ResourceListCommand
367
+ include MypluginHelpers
368
+ include MypluginServiceOptions
369
+
370
+ banner "knife myplugin flavor list (options)"
371
+
372
+ def before_exec_command
373
+ # Set columns_with_info map
374
+ # TODO - Update the columns info with the keys and callbacks required as per fog object returned for your cloud. Framework looks for 'key' on your flavor object hash returned by fog. If you need the values to be formatted or if your value is another object that needs to be looked up use value_callback.
375
+ # Example:
376
+ @columns_with_info = [
377
+ {:label => 'ID', :key => 'id'},
378
+ {:label => 'Name', :key => 'name'},
379
+ {:label => 'Virtual CPUs', :key => 'vcpus'},
380
+ {:label => 'RAM', :key => 'ram', :value_callback => method(:ram_in_mb)},
381
+ {:label => 'Disk', :key => 'disk', :value_callback => method(:disk_in_gb)}
382
+ ]
383
+ end
384
+
385
+ def query_resource
386
+ @service.list_resource_configurations
387
+ end
388
+
389
+ # TODO - This is just for example
390
+ def ram_in_mb(ram)
391
+ "#{ram} MB"
392
+ end
393
+
394
+ # TODO - This is just for example
395
+ def disk_in_gb(disk)
396
+ "#{disk} GB"
397
+ end
398
+
399
+ end
400
+ end
401
+ end
402
+ end
403
+
404
+ ## License
405
+
406
+ Copyright:: Copyright (c) 2014 Chef Software, Inc.
407
+
408
+ License:: Apache License, Version 2.0
409
+
410
+ Licensed under the Apache License, Version 2.0 (the "License");
411
+ you may not use this file except in compliance with the License.
412
+ You may obtain a copy of the License at
413
+
414
+ http://www.apache.org/licenses/LICENSE-2.0
415
+
416
+ Unless required by applicable law or agreed to in writing, software
417
+ distributed under the License is distributed on an "AS IS" BASIS,
418
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
419
+ See the License for the specific language governing permissions and
420
+ limitations under the License.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ # Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
2
+ # Copyright:: Copyright (c) 2013 Opscode, Inc.
3
+
4
+ require 'bundler'
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ require 'rubygems'
8
+ require 'rubygems/package_task'
9
+
10
+ task :default => :all
11
+ task :all => [:spec, :uninstall, :install]
12
+
13
+ # Packaging
14
+ GEM_NAME = "knife-cloud"
15
+ require File.dirname(__FILE__) + '/lib/knife-cloud/version'
16
+ spec = eval(File.read("knife-cloud.gemspec"))
17
+ Gem::PackageTask.new(spec) do |pkg|
18
+ pkg.gem_spec = spec
19
+ end
20
+
21
+ desc "uninstall #{GEM_NAME}-#{Knife::Cloud::VERSION}.gem from system..."
22
+ task :uninstall do
23
+ sh %{gem uninstall #{GEM_NAME} -x -v #{Knife::Cloud::VERSION} }
24
+ end
25
+
26
+ # rspec
27
+ begin
28
+ require 'rspec/core/rake_task'
29
+ desc "Run all specs in spec directory"
30
+ RSpec::Core::RakeTask.new(:spec) do |t|
31
+ t.pattern = 'spec/unit/**/*_spec.rb'
32
+ end
33
+ rescue LoadError
34
+ STDERR.puts "\n*** RSpec not available. (sudo) gem install rspec to run unit tests. ***\n\n"
35
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "knife-cloud/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "knife-cloud"
7
+ s.version = Knife::Cloud::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.extra_rdoc_files = ["README.md", "LICENSE" ]
10
+ s.authors = ["Kaustubh Deorukhkar", "Ameya Varade"]
11
+ s.email = ["dev@getchef.com"]
12
+ s.homepage = "https://github.com/opscode/knife-cloud"
13
+ s.summary = %q{knife-cloud plugin}
14
+ s.description = s.summary
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib", "spec"]
20
+
21
+ s.add_dependency "fog", ">= 1.10.0"
22
+ s.add_dependency 'knife-windows', '>= 0.5.14'
23
+ s.add_dependency "chef", ">= 0.10.10"
24
+ s.add_dependency 'mixlib-shellout'
25
+
26
+ %w(rspec-core rspec-expectations rspec-mocks rspec_junit_formatter).each { |gem| s.add_development_dependency gem }
27
+ end
@@ -0,0 +1,31 @@
1
+ #
2
+ # Author:: Prabhu Das (<prabhu.das@clogeny.com>)
3
+ # Copyright:: Copyright (c) 2013 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'chef/knife/cloud/helpers'
19
+
20
+ class Chef
21
+ class Knife
22
+ class Cloud
23
+ class BootstrapDistribution
24
+
25
+ def initialize(config)
26
+ @config = config
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end