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

Constant Summary collapse

CHEF_VERSION_CONSTRAINT =
">=0.10.4"

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



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

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



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

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



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

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



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

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



216
217
218
219
# File 'lib/chef/knife/solo_cook.rb', line 216

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

#check_chef_versionObject



282
283
284
285
286
287
288
289
290
# File 'lib/chef/knife/solo_cook.rb', line 282

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



297
298
299
300
301
302
303
# File 'lib/chef/knife/solo_cook.rb', line 297

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_satisfies?(requirement) ⇒ Boolean

Returns:

  • (Boolean)


292
293
294
# File 'lib/chef/knife/solo_cook.rb', line 292

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

#chefignoreObject



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

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

#clean_upObject



318
319
320
321
322
323
324
# File 'lib/chef/knife/solo_cook.rb', line 318

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

#cookObject



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

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]

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

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

#cookbook_pathsObject



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

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

#debug?Boolean

Returns:

  • (Boolean)


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

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

#expand_path(path) ⇒ Object



138
139
140
# File 'lib/chef/knife/solo_cook.rb', line 138

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

#expanded_config_paths(key) ⇒ Object



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

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

#generate_solorbObject



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

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



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

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

#patch_cookbooks_pathObject



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

def patch_cookbooks_path
  KnifeSolo.resource('patch_cookbooks')
end

#provisioning_pathObject



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

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

#proxy_setting_keysObject



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

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



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

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



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/chef/knife/solo_cook.rb', line 263

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



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

def rsync_debug
  '-v' if debug?
end

#rsync_excludesObject



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

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

#rsync_permissionsObject



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

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

#runObject



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/chef/knife/solo_cook.rb', line 78

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

#ssl_verify_modeObject



134
135
136
# File 'lib/chef/knife/solo_cook.rb', line 134

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

#sync_kitchenObject



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/chef/knife/solo_cook.rb', line 119

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



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

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



232
233
234
# File 'lib/chef/knife/solo_cook.rb', line 232

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

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



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/chef/knife/solo_cook.rb', line 236

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



105
106
107
108
109
110
111
112
# File 'lib/chef/knife/solo_cook.rb', line 105

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



254
255
256
257
258
259
260
261
# File 'lib/chef/knife/solo_cook.rb', line 254

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