chef-metal 0.7 → 0.8

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: f78844f0b35265a0587a59130f2cc18f094c91ee
4
- data.tar.gz: 4456458de1b2708269d784c6dd1c42559353b861
3
+ metadata.gz: 9811bbe55b66c09c1a01be9e87b80b918f2c5428
4
+ data.tar.gz: 1e5f30552fb823c7e11c9df9e1e08d5682604556
5
5
  SHA512:
6
- metadata.gz: dbec23e421ace3000e3f09dca9142120683fbd5201d4187adf73f8381b36a1d4863a803a716e8bc77e69b0ce69b1d6c17980ea8fbdc355d37534d7a452a6de62
7
- data.tar.gz: 3652afb4356159e48a3103c6fe1e9f6477219074453cd9fd11f164dce9c44f717f80c75e23d2d78f7202d7151c9f110e731d7e9aea109b816f64da67a0e38b0a
6
+ metadata.gz: e94c8a56ae93cdb19752de8ce82c33c5954def45c751b472d941c1c68242c03086fc50daa3f94bc4a9d87763049eea99e4b70b10312f738fa6e78fc768ec53f5
7
+ data.tar.gz: 91d84206a6fdcb0120f090255e4137e64b98a954c1af11e25bd8db127073744a9bdaad9ebde088d9ec3f96925ef07f87b6f7522ce7652b54f137b04c2b5ba779
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Chef Metal Changelog
2
2
 
3
+ ## 0.8 (4/8/2014)
4
+
5
+ - New machine_execute resource! (irving@getchef.com)
6
+ - Experimental "metal" command line: metal execute NODENAME COMMAND ARGS
7
+ - Transport: Add ability to stream execute() for better nested chef-client debugging
8
+
3
9
  ## 0.7 (4/5/2014)
4
10
 
5
11
  - Change transport interface: add ability to rewrite URL instead of forwarding ports
data/bin/metal ADDED
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
5
+ require 'chef'
6
+ require 'chef_metal'
7
+ require 'chef/rest'
8
+ require 'chef/application'
9
+ require 'chef/knife'
10
+ require 'chef/server_api'
11
+
12
+ class ChefMetal::Application < Chef::Application
13
+
14
+ # Mimic self_pipe sleep from Unicorn to capture signals safely
15
+ SELF_PIPE = []
16
+
17
+ option :config_file,
18
+ :short => "-c CONFIG",
19
+ :long => "--config CONFIG",
20
+ :description => "The configuration file to use"
21
+
22
+ option :log_level,
23
+ :short => "-l LEVEL",
24
+ :long => "--log_level LEVEL",
25
+ :description => "Set the log level (debug, info, warn, error, fatal)",
26
+ :proc => lambda { |l| l.to_sym }
27
+
28
+ option :log_location,
29
+ :short => "-L LOGLOCATION",
30
+ :long => "--logfile LOGLOCATION",
31
+ :description => "Set the log file location, defaults to STDOUT - recommended for daemonizing",
32
+ :proc => nil
33
+
34
+ option :node_name,
35
+ :short => "-N NODE_NAME",
36
+ :long => "--node-name NODE_NAME",
37
+ :description => "The node name for this client",
38
+ :proc => nil
39
+
40
+ option :chef_server_url,
41
+ :short => "-S CHEFSERVERURL",
42
+ :long => "--server CHEFSERVERURL",
43
+ :description => "The chef server URL",
44
+ :proc => nil
45
+
46
+ option :client_key,
47
+ :short => "-k KEY_FILE",
48
+ :long => "--client_key KEY_FILE",
49
+ :description => "Set the client key file location",
50
+ :proc => nil
51
+
52
+ option :local_mode,
53
+ :short => "-z",
54
+ :long => "--local-mode",
55
+ :description => "Point chef-client at local repository",
56
+ :boolean => true
57
+
58
+ option :chef_zero_port,
59
+ :long => "--chef-zero-port PORT",
60
+ :description => "Port to start chef-zero on"
61
+
62
+ def reconfigure
63
+ super
64
+
65
+ Chef::Config[:chef_server_url] = config[:chef_server_url] if config.has_key? :chef_server_url
66
+
67
+ Chef::Config.local_mode = config[:local_mode] if config.has_key?(:local_mode)
68
+ if Chef::Config.local_mode && !Chef::Config.has_key?(:cookbook_path) && !Chef::Config.has_key?(:chef_repo_path)
69
+ Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Dir.pwd)
70
+ end
71
+ Chef::Config.chef_zero.port = config[:chef_zero_port] if config[:chef_zero_port]
72
+
73
+ if Chef::Config[:daemonize]
74
+ Chef::Config[:interval] ||= 1800
75
+ end
76
+
77
+ if Chef::Config[:once]
78
+ Chef::Config[:interval] = nil
79
+ Chef::Config[:splay] = nil
80
+ end
81
+
82
+ if Chef::Config[:json_attribs]
83
+ config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs])
84
+ @chef_client_json = config_fetcher.fetch_json
85
+ end
86
+ end
87
+
88
+ def setup_application
89
+ end
90
+
91
+ def load_config_file
92
+ if !config.has_key?(:config_file)
93
+ require 'chef/knife'
94
+ config[:config_file] = Chef::Knife.locate_config_file
95
+ end
96
+ super
97
+ end
98
+
99
+ def run_application
100
+ Chef::Application.setup_server_connectivity
101
+
102
+ command = cli_arguments.shift
103
+ case command
104
+ when 'execute'
105
+ exit_code = 0
106
+ each_machine(cli_arguments.shift) do |machine|
107
+ puts "#{machine.node['name']}: running '#{cli_arguments.join(' ')}'"
108
+ result = machine.execute_always(cli_arguments.join(' '))
109
+ puts result.stdout if result.stdout != ''
110
+ STDERR.puts result.stderr if result.stderr != ''
111
+ exit_code = result.exitstatus if result.exitstatus != 0
112
+ end
113
+ exit(exit_code) if exit_code != 0
114
+ else
115
+ Chef::Log.error("Command '#{command}' unrecognized")
116
+ end
117
+
118
+ Chef::Application.destroy_server_connectivity
119
+ end
120
+
121
+ def rest
122
+ @rest ||= Chef::ServerAPI.new()
123
+ end
124
+
125
+ def each_machine(spec)
126
+ spec.split(',').each do |name|
127
+ node = rest.get("/nodes/#{name}")
128
+ provisioner_output = node['normal']['provisioner_output']
129
+ if !provisioner_output
130
+ Chef::Log.error("Node #{name} was not provisioned with Metal.")
131
+ next
132
+ end
133
+
134
+ provisioner = ChefMetal.provisioner_for_node(node)
135
+ machine = provisioner.connect_to_machine(node)
136
+
137
+ yield machine
138
+ end
139
+ end
140
+ end
141
+
142
+ ChefMetal::Application.new.run
@@ -0,0 +1,27 @@
1
+ require 'chef/provider/lwrp_base'
2
+ require 'cheffish/cheffish_server_api'
3
+
4
+ class Chef::Provider::MachineExecute < Chef::Provider::LWRPBase
5
+
6
+ use_inline_resources
7
+
8
+ def whyrun_supported?
9
+ true
10
+ end
11
+
12
+ def machine
13
+ @machine ||= begin
14
+ if new_resource.machine.kind_of?(ChefMetal::Machine)
15
+ new_resource.machine
16
+ else
17
+ # TODO this is inefficient, can we cache or something?
18
+ node = Cheffish::CheffishServerAPI.new(new_resource.chef_server).get("/nodes/#{new_resource.machine}")
19
+ new_resource.provisioner.connect_to_machine(node)
20
+ end
21
+ end
22
+ end
23
+
24
+ action :run do
25
+ machine.execute(self, new_resource.command)
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ require 'chef/resource/lwrp_base'
2
+ require 'chef_metal'
3
+ require 'chef_metal/machine'
4
+ require 'chef_metal/provisioner'
5
+
6
+ class Chef::Resource::MachineExecute < Chef::Resource::LWRPBase
7
+ self.resource_name = 'machine_execute'
8
+
9
+ def initialize(*args)
10
+ super
11
+ @chef_server = Cheffish.enclosing_chef_server
12
+ @provisioner = ChefMetal.enclosing_provisioner
13
+ end
14
+
15
+ actions :run
16
+ default_action :run
17
+
18
+ attribute :command, :kind_of => String, :name_attribute => true
19
+ attribute :machine, :kind_of => [String, ChefMetal::Machine]
20
+
21
+ attribute :chef_server, :kind_of => Hash
22
+ attribute :provisioner, :kind_of => ChefMetal::Provisioner
23
+ end
data/lib/chef_metal.rb CHANGED
@@ -4,6 +4,8 @@ require 'chef/resource/machine'
4
4
  require 'chef/provider/machine'
5
5
  require 'chef/resource/machine_file'
6
6
  require 'chef/provider/machine_file'
7
+ require 'chef/resource/machine_execute'
8
+ require 'chef/provider/machine_execute'
7
9
 
8
10
  require 'chef_metal/inline_resource'
9
11
 
@@ -33,7 +33,7 @@ module ChefMetal
33
33
  end
34
34
 
35
35
  def converge(action_handler, machine)
36
- machine.execute(action_handler, 'chef-client')
36
+ machine.execute(action_handler, "chef-client -l #{Chef::Config.log_level.to_s}", :stream => true)
37
37
  end
38
38
 
39
39
  private
@@ -29,7 +29,7 @@ module ChefMetal
29
29
  end
30
30
 
31
31
  def converge(action_handler, machine)
32
- machine.execute(action_handler, 'chef-client')
32
+ machine.execute(action_handler, "chef-client -l #{Chef::Config.log_level.to_s}", :stream => true)
33
33
  end
34
34
  end
35
35
  end
@@ -0,0 +1,86 @@
1
+ module ChefMetal
2
+ class Provisioner
3
+ # Inflate a provisioner from node information; we don't want to force the
4
+ # driver to figure out what the provisioner really needs, since it varies
5
+ # from provisioner to provisioner.
6
+ #
7
+ # ## Parameters
8
+ # node - node to inflate the provisioner for
9
+ #
10
+ # returns a should return a Privisoner from the information provided
11
+ def self.inflate(node)
12
+ raise "#{self.class} does not override self.inflate"
13
+ end
14
+
15
+ # Acquire a machine, generally by provisioning it. Returns a Machine
16
+ # object pointing at the machine, allowing useful actions like setup,
17
+ # converge, execute, file and directory. The Machine object will have a
18
+ # "node" property which must be saved to the server (if it is any
19
+ # different from the original node object).
20
+ #
21
+ # ## Parameters
22
+ # action_handler - the action_handler object that is calling this method; this
23
+ # is generally a provider, but could be anything that can support the
24
+ # interface (i.e., in the case of the test kitchen metal driver for
25
+ # acquiring and destroying VMs).
26
+ # node - node object (deserialized json) representing this machine. If
27
+ # the node has a provisioner_options hash in it, these will be used
28
+ # instead of options provided by the provisioner. TODO compare and
29
+ # fail if different?
30
+ # node will have node['normal']['provisioner_options'] in it with any
31
+ # options. It is a hash with at least these options:
32
+ #
33
+ # -- provisioner_url: <provisioner url>
34
+ #
35
+ # node['normal']['provisioner_output'] will be populated with
36
+ # information about the created machine. For vagrant, it is a hash
37
+ # with at least these options:
38
+ #
39
+ # -- provisioner_url: <provisioner url>
40
+ #
41
+ def acquire_machine(action_handler, node)
42
+ raise "#{self.class} does not override acquire_machine"
43
+ end
44
+
45
+ # Connect to a machine without acquiring it. This method will NOT make any
46
+ # changes to anything.
47
+ #
48
+ # ## Parameters
49
+ # node - node object (deserialized json) representing this machine. The
50
+ # node may have normal attributes "provisioner_options" and
51
+ # "provisioner_output" in it, representing the input and output of
52
+ # any prior "acquire_machine" process (if any).
53
+ #
54
+ def connect_to_machine(node)
55
+ raise "#{self.class} does not override connect_to_machine"
56
+ end
57
+
58
+ # Delete the given machine (idempotent). Should destroy the machine,
59
+ # returning things to the state before acquire_machine was called.
60
+ def delete_machine(action_handler, node)
61
+ raise "#{self.class} does not override delete_machine"
62
+ end
63
+
64
+ # Stop the given machine.
65
+ def stop_machine(action_handler, node)
66
+ raise "#{self.class} does not override stop_machine"
67
+ end
68
+
69
+ # Provider notification that happens at the point a resource is declared
70
+ # (after all properties have been set on it)
71
+ def resource_created(machine)
72
+ end
73
+
74
+ protected
75
+
76
+ def save_node(provider, node, chef_server)
77
+ # Save the node and create the client. TODO strip automatic attributes first so we don't race with "current state"
78
+ ChefMetal.inline_resource(provider) do
79
+ chef_node node['name'] do
80
+ chef_server chef_server
81
+ raw_json node
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,54 @@
1
+ module ChefMetal
2
+ class Provisioner
3
+ def create_machine(instance)
4
+ end
5
+ def delete_machine(instance)
6
+ end
7
+ def stop_machine(instance)
8
+ end
9
+ end
10
+ end
11
+
12
+ module ChefMetal
13
+ class Instance
14
+ def create
15
+ end
16
+ def delete
17
+ end
18
+ def stop
19
+ end
20
+ def converge
21
+ end
22
+ def
23
+
24
+ end
25
+ end
26
+
27
+
28
+ - MachineList (physical)
29
+ - Driver (lists, creates, deletes associated machines)
30
+ EC2
31
+ LXC
32
+ Docker
33
+ BareMetal
34
+ - Machine (physical)
35
+ - Transport
36
+ SSH (+Rsync)
37
+ WinRM
38
+ LXC IPC
39
+ Docker API
40
+ - Provisioner
41
+ Chef Client
42
+ Puppet
43
+
44
+ - Cluster (logical)
45
+ Chef server
46
+ Puppet master
47
+ - Instance (logical)
48
+ create
49
+ delete
50
+ exists?
51
+ provision
52
+ verify
53
+ execute
54
+ upload/download
@@ -17,10 +17,14 @@ module ChefMetal
17
17
  raise "converge not overridden on #{self.class}"
18
18
  end
19
19
 
20
- def execute(action_handler, command)
20
+ def execute(action_handler, command, options = {})
21
21
  raise "execute not overridden on #{self.class}"
22
22
  end
23
23
 
24
+ def execute_always(command, options = {})
25
+ raise "execute_always not overridden on #{self.class}"
26
+ end
27
+
24
28
  def read_file(path)
25
29
  raise "read_file not overridden on #{self.class}"
26
30
  end
@@ -23,14 +23,14 @@ module ChefMetal
23
23
  convergence_strategy.converge(action_handler, self)
24
24
  end
25
25
 
26
- def execute(action_handler, command)
26
+ def execute(action_handler, command, options = {})
27
27
  action_handler.converge_by "run '#{command}' on #{node['name']}" do
28
- transport.execute(command).error!
28
+ transport.execute(command, options).error!
29
29
  end
30
30
  end
31
31
 
32
- def execute_always(command)
33
- transport.execute(command)
32
+ def execute_always(command, options = {})
33
+ transport.execute(command, options)
34
34
  end
35
35
 
36
36
  def read_file(path)
@@ -1,6 +1,6 @@
1
1
  module ChefMetal
2
2
  class Transport
3
- def execute(command)
3
+ def execute(command, options = {})
4
4
  raise "execute not overridden on #{self.class}"
5
5
  end
6
6
 
@@ -31,5 +31,29 @@ module ChefMetal
31
31
  def available?
32
32
  raise "available? not overridden on #{self.class}"
33
33
  end
34
+
35
+ protected
36
+
37
+ # Helper to implement stdout/stderr streaming in execute
38
+ def stream_chunk(options, stdout_chunk, stderr_chunk)
39
+ if options[:stream].is_a?(Proc)
40
+ options[:stream].call(stdout_chunk, stderr_chunk)
41
+ else
42
+ if stdout_chunk
43
+ if options[:stream_stdout]
44
+ options[:stream_stdout].print stdout_chunk
45
+ elsif options[:stream]
46
+ STDOUT.print stdout_chunk
47
+ end
48
+ end
49
+ if stderr_chunk
50
+ if options[:stream_stderr]
51
+ options[:stream_stderr].print stderr_chunk
52
+ elsif options[:stream]
53
+ STDERR.print stderr_chunk
54
+ end
55
+ end
56
+ end
57
+ end
34
58
  end
35
59
  end
@@ -4,7 +4,7 @@ require 'socket'
4
4
 
5
5
  module ChefMetal
6
6
  class Transport
7
- class SSH < Transport
7
+ class SSH < ChefMetal::Transport
8
8
  def initialize(host, username, ssh_options, options)
9
9
  require 'net/ssh'
10
10
  require 'net/scp'
@@ -19,8 +19,8 @@ module ChefMetal
19
19
  attr_reader :ssh_options
20
20
  attr_reader :options
21
21
 
22
- def execute(command)
23
- Chef::Log.info("Executing #{command} on #{username}@#{host}")
22
+ def execute(command, execute_options = {})
23
+ Chef::Log.info("Executing #{options[:prefix]}#{command} on #{username}@#{host}")
24
24
  stdout = ''
25
25
  stderr = ''
26
26
  exitstatus = nil
@@ -38,10 +38,12 @@ module ChefMetal
38
38
 
39
39
  channel.on_data do |ch2, data|
40
40
  stdout << data
41
+ stream_chunk(execute_options, data, nil)
41
42
  end
42
43
 
43
44
  channel.on_extended_data do |ch2, type, data|
44
45
  stderr << data
46
+ stream_chunk(execute_options, nil, data)
45
47
  end
46
48
 
47
49
  channel.on_request "exit-status" do |ch, data|
@@ -53,9 +55,9 @@ module ChefMetal
53
55
  channel.wait
54
56
 
55
57
  Chef::Log.info("Completed #{command} on #{username}@#{host}: exit status #{exitstatus}")
56
- Chef::Log.debug("Stdout was:\n#{stdout}") if stdout != ''
57
- Chef::Log.info("Stderr was:\n#{stderr}") if stderr != ''
58
- SSHResult.new(stdout, stderr, exitstatus)
58
+ Chef::Log.debug("Stdout was:\n#{stdout}") if stdout != '' && !options[:stream] && !options[:stream_stdout]
59
+ Chef::Log.info("Stderr was:\n#{stderr}") if stderr != '' && !options[:stream] && !options[:stream_stderr]
60
+ SSHResult.new(command, execute_options, stdout, stderr, exitstatus)
59
61
  end
60
62
 
61
63
  def read_file(path)
@@ -153,18 +155,25 @@ module ChefMetal
153
155
  end
154
156
 
155
157
  class SSHResult
156
- def initialize(stdout, stderr, exitstatus)
158
+ def initialize(command, options, stdout, stderr, exitstatus)
159
+ @command = command
160
+ @options = options
157
161
  @stdout = stdout
158
162
  @stderr = stderr
159
163
  @exitstatus = exitstatus
160
164
  end
161
165
 
166
+ attr_reader :command
167
+ attr_reader :options
162
168
  attr_reader :stdout
163
169
  attr_reader :stderr
164
170
  attr_reader :exitstatus
165
171
 
166
172
  def error!
167
- raise "Error: code #{exitstatus}.\nSTDOUT:#{stdout}\nSTDERR:#{stderr}" if exitstatus != 0
173
+ if exitstatus != 0
174
+ # TODO stdout/stderr is already printed at info/debug level. Let's not print it twice, it's a lot.
175
+ msg = "Error: command '#{command}' exited with code #{exitstatus}.\n"
176
+ end
168
177
  end
169
178
  end
170
179
  end
@@ -1,8 +1,9 @@
1
+ require 'chef_metal/transport'
1
2
  require 'base64'
2
3
 
3
4
  module ChefMetal
4
5
  class Transport
5
- class WinRM
6
+ class WinRM < ChefMetal::Transport
6
7
  def initialize(endpoint, type, options = {})
7
8
  @endpoint = endpoint
8
9
  @type = type
@@ -13,9 +14,11 @@ module ChefMetal
13
14
  attr_reader :type
14
15
  attr_reader :options
15
16
 
16
- def execute(command)
17
- output = session.run_powershell_script(command)
18
- WinRMResult.new(output)
17
+ def execute(command, execute_options = {})
18
+ output = session.run_powershell_script(command) do |stdout, stderr|
19
+ stream_chunk(execute_options, stdout, stderr)
20
+ end
21
+ WinRMResult.new(command, execute_options, output)
19
22
  end
20
23
 
21
24
  def read_file(path)
@@ -50,7 +53,7 @@ $file.Close
50
53
  end
51
54
 
52
55
  def disconnect
53
- #
56
+ #
54
57
  end
55
58
 
56
59
  def escape(string)
@@ -68,7 +71,9 @@ $file.Close
68
71
  end
69
72
 
70
73
  class WinRMResult
71
- def initialize(output)
74
+ def initialize(command, options, output)
75
+ @command = command
76
+ @options = options
72
77
  @exitstatus = output[:exitcode]
73
78
  @stdout = ''
74
79
  @stderr = ''
@@ -81,9 +86,15 @@ $file.Close
81
86
  attr_reader :stdout
82
87
  attr_reader :stderr
83
88
  attr_reader :exitstatus
89
+ attr_reader :command
90
+ attr_reader :options
84
91
 
85
92
  def error!
86
- raise "Error: code #{exitstatus}.\nSTDOUT:#{stdout}\nSTDERR:#{stderr}" if exitstatus != 0
93
+ if exitstatus != 0
94
+ msg = "Error: command '#{command}' exited with code #{exitstatus}.\n"
95
+ msg << "STDOUT: #{stdout}" if !options[:stream] && !options[:stream_stdout]
96
+ msg << "STDERR: #{stderr}" if !options[:stream] && !options[:stream_stderr]
97
+ end
87
98
  end
88
99
  end
89
100
  end
@@ -1,3 +1,3 @@
1
1
  module ChefMetal
2
- VERSION = '0.7'
2
+ VERSION = '0.8'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-metal
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.7'
4
+ version: '0.8'
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Keiser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-06 00:00:00.000000000 Z
11
+ date: 2014-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef
@@ -138,7 +138,8 @@ dependencies:
138
138
  version: '0'
139
139
  description: A library for creating machines and infrastructures idempotently in Chef.
140
140
  email: jkeiser@opscode.com
141
- executables: []
141
+ executables:
142
+ - metal
142
143
  extensions: []
143
144
  extra_rdoc_files:
144
145
  - README.md
@@ -150,8 +151,10 @@ files:
150
151
  - README.md
151
152
  - CHANGELOG.md
152
153
  - lib/chef/provider/machine.rb
154
+ - lib/chef/provider/machine_execute.rb
153
155
  - lib/chef/provider/machine_file.rb
154
156
  - lib/chef/resource/machine.rb
157
+ - lib/chef/resource/machine_execute.rb
155
158
  - lib/chef/resource/machine_file.rb
156
159
  - lib/chef_metal/action_handler.rb
157
160
  - lib/chef_metal/aws_credentials.rb
@@ -161,7 +164,9 @@ files:
161
164
  - lib/chef_metal/convergence_strategy/no_converge.rb
162
165
  - lib/chef_metal/convergence_strategy/precreate_chef_objects.rb
163
166
  - lib/chef_metal/convergence_strategy.rb
167
+ - lib/chef_metal/driver.rb
164
168
  - lib/chef_metal/inline_resource.rb
169
+ - lib/chef_metal/instance.rb
165
170
  - lib/chef_metal/machine/basic_machine.rb
166
171
  - lib/chef_metal/machine/unix_machine.rb
167
172
  - lib/chef_metal/machine/windows_machine.rb
@@ -175,6 +180,7 @@ files:
175
180
  - lib/chef_metal/transport.rb
176
181
  - lib/chef_metal/version.rb
177
182
  - lib/chef_metal.rb
183
+ - bin/metal
178
184
  homepage: http://wiki.opscode.com/display/chef
179
185
  licenses: []
180
186
  metadata: {}