Class: Chef::Knife::SoloCook

Inherits:
Chef::Knife show all
Includes:
KnifeSolo::NodeConfigCommand, KnifeSolo::SshCommand, KnifeSolo::Tools
Defined in:
lib/chef/knife/solo_cook.rb

Overview

Approach ported from spatula (github.com/trotter/spatula) Copyright 2009, Trotter Cashion

Direct Known Subclasses

Cook

Instance Method Summary collapse

Methods included from KnifeSolo::Tools

#config_value, config_value, cygwin_client?, #system!, #windows_client?

Methods included from KnifeSolo::NodeConfigCommand

#generate_node_config, included, load_deps, #node_config, #node_environment, #node_name, #nodes_path

Methods included from KnifeSolo::SshCommand

#ask_password, #config_file_options, #config_files, #connection_options, #custom_sudo_command, #detect_authentication_method, #first_cli_arg_is_a_hostname?, #host, #host_descriptor, #identity_file, included, load_deps, #password, #process_startup_file, #process_sudo, #processed_command, #run_command, #run_portable_mkdir_p, #run_with_fallbacks, #ssh_args, #ssh_connection, #ssh_control_path, #standard_sudo_command, #startup_script, #stream_command, #sudo_available?, #sudo_command, #try_connection, #user, #validate_ssh_options!, #windows_node?

Instance Method Details

#add_cookbook_path(path) ⇒ Object



185
186
187
188
# File 'lib/chef/knife/solo_cook.rb', line 185

def add_cookbook_path(path)
  path = expand_path path
  cookbook_paths.unshift(path) unless cookbook_paths.include?(path)
end

#adjust_rsync_path(path, path_prefix) ⇒ Object

path must be adjusted to work on windows



199
200
201
202
# File 'lib/chef/knife/solo_cook.rb', line 199

def adjust_rsync_path(path, path_prefix)
  path_s = path.to_s
  path_s.gsub(/^(\w):/) { path_prefix + "/#{$1}" }
end

#adjust_rsync_path_on_client(path) ⇒ Object



209
210
211
212
# File 'lib/chef/knife/solo_cook.rb', line 209

def adjust_rsync_path_on_client(path)
  return path unless windows_client?
  adjust_rsync_path(path, config_value(:cygdrive_prefix_local, '/cygdrive'))
end

#adjust_rsync_path_on_node(path) ⇒ Object



204
205
206
207
# File 'lib/chef/knife/solo_cook.rb', line 204

def adjust_rsync_path_on_node(path)
  return path unless windows_node?
  adjust_rsync_path(path, config_value(:cygdrive_prefix_remote, '/cygdrive'))
end

#berkshelf_installObject



240
241
242
243
# File 'lib/chef/knife/solo_cook.rb', line 240

def berkshelf_install
  path = KnifeSolo::Berkshelf.new(config, ui).install
  add_cookbook_path(path) if path
end

#check_chef_versionObject



306
307
308
309
310
311
312
313
314
# File 'lib/chef/knife/solo_cook.rb', line 306

def check_chef_version
  ui.msg "Checking Chef version..."
  unless chef_version_satisfies?(chef_version_constraint)
    raise "Couldn't find Chef #{chef_version_constraint} on #{host}. Please run `knife solo prepare #{ssh_args}` to ensure Chef is installed and up to date."
  end
  if node_environment != '_default' && chef_version_satisfies?('<11.6.0')
    ui.warn "Chef version #{chef_version} does not support environments. Environment '#{node_environment}' will be ignored."
  end
end

#chef_versionObject

Parses “Chef: x.y.z” from the chef-solo version output



321
322
323
324
325
326
327
# File 'lib/chef/knife/solo_cook.rb', line 321

def chef_version
  # Memoize the version to avoid multiple SSH calls
  @chef_version ||= lambda do
    cmd = %q{sudo chef-solo --version 2>/dev/null | awk '$1 == "Chef:" {print $2}'}
    run_command(cmd).stdout.strip
  end.call
end

#chef_version_constraintObject



150
151
152
# File 'lib/chef/knife/solo_cook.rb', line 150

def chef_version_constraint
  Chef::Config[:solo_chef_version] || ">=0.10.4"
end

#chef_version_satisfies?(requirement) ⇒ Boolean

Returns:

  • (Boolean)


316
317
318
# File 'lib/chef/knife/solo_cook.rb', line 316

def chef_version_satisfies?(requirement)
  Gem::Requirement.new(requirement).satisfied_by? Gem::Version.new(chef_version)
end

#chefignoreObject



194
195
196
# File 'lib/chef/knife/solo_cook.rb', line 194

def chefignore
  @chefignore ||= ::Chef::Cookbook::Chefignore.new("./")
end

#clean_upObject



345
346
347
348
349
350
351
# File 'lib/chef/knife/solo_cook.rb', line 345

def clean_up
  clean = SoloClean.new
  clean.ui = ui
  clean.name_args = @name_args
  clean.config.merge! config
  clean.run
end

#cookObject



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/chef/knife/solo_cook.rb', line 329

def cook
  cmd = "sudo chef-solo -c #{provisioning_path}/solo.rb -j #{provisioning_path}/dna.json"
  cmd << " -l debug" if debug?
  cmd << " -N #{config[:chef_node_name]}" if config[:chef_node_name]
  cmd << " -W" if config[:why_run]
  cmd << " -o #{config[:override_runlist]}" if config[:override_runlist]
  if Gem::Version.new(::Chef::VERSION) >= Gem::Version.new("12.10.54")
    cmd << " --legacy-mode" if config[:legacy_mode]
  end

  ui.msg "Running Chef: #{cmd}"

  result = stream_command cmd
  raise "chef-solo failed. See output above." unless result.success?
end

#cookbook_pathsObject



170
171
172
# File 'lib/chef/knife/solo_cook.rb', line 170

def cookbook_paths
  @cookbook_paths ||= expanded_config_paths(:cookbook_path)
end

#debug?Boolean

Returns:

  • (Boolean)


227
228
229
# File 'lib/chef/knife/solo_cook.rb', line 227

def debug?
  config[:verbosity] and config[:verbosity] > 0
end

#enable_reportingObject



158
159
160
# File 'lib/chef/knife/solo_cook.rb', line 158

def enable_reporting
  config_value(:enable_reporting, true)
end

#expand_path(path) ⇒ Object



162
163
164
# File 'lib/chef/knife/solo_cook.rb', line 162

def expand_path(path)
  Pathname.new(path).expand_path
end

#expanded_config_paths(key) ⇒ Object



166
167
168
# File 'lib/chef/knife/solo_cook.rb', line 166

def expanded_config_paths(key)
  Array(Chef::Config[key]).map { |path| expand_path path }
end

#generate_solorbObject



250
251
252
253
254
# File 'lib/chef/knife/solo_cook.rb', line 250

def generate_solorb
  ui.msg "Generating solo config..."
  template = Erubis::Eruby.new(KnifeSolo.resource('solo.rb.erb').read)
  write(template.result(binding), provisioning_path + '/solo.rb')
end

#librarian_installObject



245
246
247
248
# File 'lib/chef/knife/solo_cook.rb', line 245

def librarian_install
  path = KnifeSolo::Librarian.new(config, ui).install
  add_cookbook_path(path) if path
end

#log_levelObject



154
155
156
# File 'lib/chef/knife/solo_cook.rb', line 154

def log_level
  config_value(:log_level, Chef::Config[:log_level] || :warn).to_sym
end

#patch_cookbooks_pathObject



190
191
192
# File 'lib/chef/knife/solo_cook.rb', line 190

def patch_cookbooks_path
  KnifeSolo.resource('patch_cookbooks')
end

#provisioning_pathObject



122
123
124
125
# File 'lib/chef/knife/solo_cook.rb', line 122

def provisioning_path
  # TODO ~ will likely break on cmd.exe based windows sessions
  config_value(:provisioning_path, '~/chef-solo')
end

#proxy_setting_keysObject



174
175
176
# File 'lib/chef/knife/solo_cook.rb', line 174

def proxy_setting_keys
  [:http_proxy, :https_proxy, :http_proxy_user, :http_proxy_pass, :https_proxy_user, :https_proxy_pass, :no_proxy]
end

#proxy_settingsObject



178
179
180
181
182
183
# File 'lib/chef/knife/solo_cook.rb', line 178

def proxy_settings
  proxy_setting_keys.inject(Hash.new) do |ret, key|
    ret[key] = Chef::Config[key] if Chef::Config[key]
    ret
  end
end

#rsync(source_path, target_path, extra_opts = ['--delete-after', '-zt']) ⇒ Object



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/chef/knife/solo_cook.rb', line 287

def rsync(source_path, target_path, extra_opts = ['--delete-after', '-zt'])
  if config[:ssh_gateway]
    ssh_command = "ssh -TA #{config[:ssh_gateway]} ssh -T -o StrictHostKeyChecking=no #{ssh_args}"
  else
    ssh_command = "ssh #{ssh_args}"
  end

  cmd = ['rsync', '-rL', rsync_debug, rsync_permissions, %Q{--rsh=#{ssh_command}}]
  cmd += extra_opts
  cmd += rsync_excludes.map { |ignore| "--exclude=#{ignore}" }
  cmd += [ adjust_rsync_path_on_client(source_path),
           ':' + adjust_rsync_path_on_node(target_path) ]

  cmd = cmd.compact

  Chef::Log.debug cmd.inspect
  system!(*cmd)
end

#rsync_debugObject



214
215
216
# File 'lib/chef/knife/solo_cook.rb', line 214

def rsync_debug
  '-v' if debug?
end

#rsync_excludesObject



223
224
225
# File 'lib/chef/knife/solo_cook.rb', line 223

def rsync_excludes
  (%w{revision-deploys .git .hg .svn .bzr} + chefignore.ignores).uniq
end

#rsync_permissionsObject



219
220
221
# File 'lib/chef/knife/solo_cook.rb', line 219

def rsync_permissions
  '--chmod=ugo=rwX' if windows_client?
end

#runObject



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
# File 'lib/chef/knife/solo_cook.rb', line 86

def run
  time('Run') do

    if config[:skip_chef_check]
      ui.warn '`--skip-chef-check` is deprecated, please use `--no-chef-check`.'
      config[:chef_check] = false
    end

    validate!

    ui.msg "Running Chef on #{host}..."

    check_chef_version if config[:chef_check]
    if config_value(:sync, true)
      generate_node_config
      berkshelf_install if config_value(:berkshelf, true)
      librarian_install if config_value(:librarian, true)
      patch_cookbooks_install
      sync_kitchen
      generate_solorb
    end
    cook unless config[:sync_only]

    clean_up if config[:clean_up]
  end
end

#solo_legacy_modeObject



146
147
148
# File 'lib/chef/knife/solo_cook.rb', line 146

def solo_legacy_mode
  Chef::Config[:solo_legacy_mode] || false
end

#ssl_verify_modeObject



142
143
144
# File 'lib/chef/knife/solo_cook.rb', line 142

def ssl_verify_mode
  Chef::Config[:ssl_verify_mode] || :verify_peer
end

#sync_kitchenObject



127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/chef/knife/solo_cook.rb', line 127

def sync_kitchen
  ui.msg "Uploading the kitchen..."
  run_portable_mkdir_p(provisioning_path, '0700')

  cookbook_paths.each_with_index do |path, i|
    upload_to_provision_path(path.to_s, "/cookbooks-#{i + 1}", 'cookbook_path')
  end
  upload_to_provision_path(node_config.to_s, 'dna.json')
  upload_to_provision_path(nodes_path, 'nodes')
  upload_to_provision_path(:role_path, 'roles')
  upload_to_provision_path(:data_bag_path, 'data_bags')
  upload_to_provision_path(config[:secret_file] || :encrypted_data_bag_secret, 'data_bag_key')
  upload_to_provision_path(:environment_path, 'environments')
end

#time(msg) ⇒ Object

Time a command



232
233
234
235
236
237
238
# File 'lib/chef/knife/solo_cook.rb', line 232

def time(msg)
  return yield unless debug?
  ui.msg "Starting '#{msg}'"
  start = Time.now
  yield
  ui.msg "#{msg} finished in #{Time.now - start} seconds"
end

#upload(src, dest) ⇒ Object



256
257
258
# File 'lib/chef/knife/solo_cook.rb', line 256

def upload(src, dest)
  rsync(src, dest)
end

#upload_to_provision_path(src, dest, key_name = 'path') ⇒ Object



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/chef/knife/solo_cook.rb', line 260

def upload_to_provision_path(src, dest, key_name = 'path')
  if src.is_a? Symbol
    key_name = src.to_s
    src = Chef::Config[src]
  end

  if src.nil?
    Chef::Log.debug "'#{key_name}' not set"
  elsif !src.is_a?(String)
    ui.error "#{key_name} is not a String: #{src.inspect}"
  elsif !File.exist?(src)
    ui.warn "Local #{key_name} '#{src}' does not exist"
  else
    upload("#{src}#{'/' if File.directory?(src)}", File.join(provisioning_path, dest))
  end
end

#validate!Object



113
114
115
116
117
118
119
120
# File 'lib/chef/knife/solo_cook.rb', line 113

def validate!
  validate_ssh_options!

  if File.exist? 'solo.rb'
    ui.warn "solo.rb found, but since knife-solo v0.3.0 it is not used any more"
    ui.warn "Please read the upgrade instructions: https://github.com/matschaffer/knife-solo/wiki/Upgrading-to-0.3.0"
  end
end

#write(content, dest) ⇒ Object

TODO probably can get Net::SSH to do this directly



278
279
280
281
282
283
284
285
# File 'lib/chef/knife/solo_cook.rb', line 278

def write(content, dest)
  file = Tempfile.new(File.basename(dest))
  file.write(content)
  file.close
  upload(file.path, dest)
ensure
  file.unlink
end