Class: ChefMetal::Provisioner::VagrantProvisioner

Inherits:
ChefMetal::Provisioner show all
Includes:
Chef::Mixin::ShellOut
Defined in:
lib/chef_metal/provisioner/vagrant_provisioner.rb

Overview

Provisions machines in vagrant.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ChefMetal::Provisioner

#resource_created

Constructor Details

#initialize(cluster_path) ⇒ VagrantProvisioner

Create a new vagrant provisioner.

## Parameters cluster_path - path to the directory containing the vagrant files, which

should have been created with the vagrant_cluster resource.


16
17
18
# File 'lib/chef_metal/provisioner/vagrant_provisioner.rb', line 16

def initialize(cluster_path)
  @cluster_path = cluster_path
end

Instance Attribute Details

#cluster_pathObject (readonly)

Returns the value of attribute cluster_path.



20
21
22
# File 'lib/chef_metal/provisioner/vagrant_provisioner.rb', line 20

def cluster_path
  @cluster_path
end

Class Method Details

.inflate(node) ⇒ Object

Inflate a provisioner from node information; we don’t want to force the driver to figure out what the provisioner really needs, since it varies from provisioner to provisioner.

## Parameters node - node to inflate the provisioner for

returns a VagrantProvisioner



30
31
32
33
34
# File 'lib/chef_metal/provisioner/vagrant_provisioner.rb', line 30

def self.inflate(node)
  node_url = node['normal']['provisioner_output']['provisioner_url']
  cluster_path = node_url.split(':', 2)[1].sub(/^\/\//, "")
  self.new(cluster_path)
end

.vagrant_config_string(vagrant_config, variable, line_prefix) ⇒ Object

Used by vagrant_cluster and machine to get the string used to configure vagrant



196
197
198
199
200
201
202
203
204
# File 'lib/chef_metal/provisioner/vagrant_provisioner.rb', line 196

def self.vagrant_config_string(vagrant_config, variable, line_prefix)
  hostname = name.gsub(/[^A-Za-z0-9\-]/, '-')

  result = ''
  vagrant_config.each_pair do |key, value|
    result += "#{line_prefix}#{variable}.#{key} = #{value.inspect}\n"
  end
  result
end

Instance Method Details

#acquire_machine(action_handler, node) ⇒ Object

Acquire a machine, generally by provisioning it. Returns a Machine object pointing at the machine, allowing useful actions like setup, converge, execute, file and directory. The Machine object will have a “node” property which must be saved to the server (if it is any different from the original node object).

## Parameters action_handler - the action_handler object that is calling this method; this

is generally a action_handler, but could be anything that can support the
ChefMetal::ActionHandler interface (i.e., in the case of the test
kitchen metal driver for acquiring and destroying VMs; see the base
class for what needs providing).

node - node object (deserialized json) representing this machine. If

the node has a provisioner_options hash in it, these will be used
instead of options provided by the provisioner.  TODO compare and
fail if different?
node will have node['normal']['provisioner_options'] in it with any options.
It is a hash with this format:

   -- provisioner_url: vagrant:<cluster_path>
   -- vagrant_options: hash of properties of the "config"
      object, i.e. "vm.box" => "ubuntu12" and "vm.box_url"
   -- vagrant_config: string containing other vagrant config.
      Should assume the variable "config" represents machine config.
      Will be written verbatim into the vm's Vagrantfile.
   -- transport_options: hash of options specifying the transport.
        :type => :ssh
        :type => :winrm
        If not specified, ssh is used unless vm.guest is :windows.  If that is
        the case, the windows options are used and the port forward for 5985
        is detected.
   -- up_timeout: maximum time, in seconds, to wait for vagrant
      to bring up the machine.  Defaults to 10 minutes.

node['normal']['provisioner_output'] will be populated with information
about the created machine.  For vagrant, it is a hash with this
format:

   -- provisioner_url: vagrant_cluster://<current_node>/<cluster_path>
   -- vm_name: name of vagrant vm created
   -- vm_file_path: path to machine-specific vagrant config file
      on disk
   -- forwarded_ports: hash with key as guest_port => host_port


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/chef_metal/provisioner/vagrant_provisioner.rb', line 80

def acquire_machine(action_handler, node)
  # Set up the modified node data
  provisioner_options = node['normal']['provisioner_options']
  vm_name = node['name']
  old_provisioner_output = node['normal']['provisioner_output']
  node['normal']['provisioner_output'] = provisioner_output = {
    'provisioner_url' => provisioner_url(action_handler),
    'vm_name' => vm_name,
    'vm_file_path' => File.join(cluster_path, "#{vm_name}.vm")
  }
  # Preserve existing forwarded ports
  provisioner_output['forwarded_ports'] = old_provisioner_output['forwarded_ports'] if old_provisioner_output

  # TODO compare new options to existing and fail if we cannot change it
  # over (perhaps introduce a boolean that will force a delete and recreate
  # in such a case)

  # Determine contents of vm file
  vm_file_content = "Vagrant.configure('2') do |outer_config|\n"
  vm_file_content << "  outer_config.vm.define #{vm_name.inspect} do |config|\n"
  merged_vagrant_options = { 'vm.hostname' => node['name'] }
  merged_vagrant_options.merge!(provisioner_options['vagrant_options']) if provisioner_options['vagrant_options']
  merged_vagrant_options.each_pair do |key, value|
    vm_file_content << "    config.#{key} = #{value.inspect}\n"
  end
  vm_file_content << provisioner_options['vagrant_config'] if provisioner_options['vagrant_config']
  vm_file_content << "  end\nend\n"

  # Set up vagrant file
  vm_file = ChefMetal.inline_resource(action_handler) do
    file provisioner_output['vm_file_path'] do
      content vm_file_content
      action :create
    end
  end

  # Check current status of vm
  current_status = vagrant_status(vm_name)
  up_timeout = provisioner_options['up_timeout'] || 10*60

  if current_status != 'running'
    # Run vagrant up if vm is not running
    action_handler.perform_action "run vagrant up #{vm_name} (status was '#{current_status}')" do
      result = shell_out("vagrant up #{vm_name}", :cwd => cluster_path, :timeout => up_timeout)
      if result.exitstatus != 0
        raise "vagrant up #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
      end
      parse_vagrant_up(result.stdout, node)
    end
  elsif vm_file.updated_by_last_action?
    # Run vagrant reload if vm is running and vm file changed
    action_handler.perform_action "run vagrant reload #{vm_name}" do
      result = shell_out("vagrant reload #{vm_name}", :cwd => cluster_path, :timeout => up_timeout)
      if result.exitstatus != 0
        raise "vagrant reload #{vm_name} failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
      end
      parse_vagrant_up(result.stdout, node)
    end
  end

  # Create machine object for callers to use
  machine_for(node)
end

#connect_to_machine(node) ⇒ Object

Connect to machine without acquiring it



145
146
147
# File 'lib/chef_metal/provisioner/vagrant_provisioner.rb', line 145

def connect_to_machine(node)
  machine_for(node)
end

#delete_machine(action_handler, node) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/chef_metal/provisioner/vagrant_provisioner.rb', line 149

def delete_machine(action_handler, node)
  if node['normal'] && node['normal']['provisioner_output']
    provisioner_output = node['normal']['provisioner_output']
  else
    provisioner_output = {}
  end
  vm_name = provisioner_output['vm_name'] || node['name']
  current_status = vagrant_status(vm_name)
  if current_status != 'not created'
    action_handler.perform_action "run vagrant destroy -f #{vm_name} (status was '#{current_status}')" do
      result = shell_out("vagrant destroy -f #{vm_name}", :cwd => cluster_path)
      if result.exitstatus != 0
        raise "vagrant destroy failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
      end
    end
  end

  convergence_strategy_for(node).cleanup_convergence(action_handler, node)

  vm_file_path = provisioner_output['vm_file_path'] || File.join(cluster_path, "#{vm_name}.vm")
  ChefMetal.inline_resource(action_handler) do
    file vm_file_path do
      action :delete
    end
  end
end

#stop_machine(action_handler, node) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/chef_metal/provisioner/vagrant_provisioner.rb', line 176

def stop_machine(action_handler, node)
  if node['normal'] && node['normal']['provisioner_output']
    provisioner_output = node['normal']['provisioner_output']
  else
    provisioner_output = {}
  end
  vm_name = provisioner_output['vm_name'] || node['name']
  current_status = vagrant_status(vm_name)
  if current_status == 'running'
    action_handler.perform_action "run vagrant halt #{vm_name} (status was '#{current_status}')" do
      result = shell_out("vagrant halt #{vm_name}", :cwd => cluster_path)
      if result.exitstatus != 0
        raise "vagrant halt failed!\nSTDOUT:#{result.stdout}\nSTDERR:#{result.stderr}"
      end
    end
  end
end