Class: Vagabond::InternalConfiguration

Inherits:
Object
  • Object
show all
Includes:
Helpers
Defined in:
lib/vagabond/internal_configuration.rb

Constant Summary collapse

DEFAULT_ERCHEF_VERSION =
'11.0.8'

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers

included

Constructor Details

#initialize(vagabondfile, ui, options, args = {}) ⇒ InternalConfiguration

Returns a new instance of InternalConfiguration.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/vagabond/internal_configuration.rb', line 33

def initialize(vagabondfile, ui, options, args={})
  @vagabondfile = vagabondfile
  @checksums = Mash.new
  if(ui)
    @ui = ui
  else
    @ui = Logger.new('/dev/null')
    @ui.instance_eval do
      def color(*args)
      end
    end
  end
  @options = options
  create_store
  load_existing
  @config = storage_hash(
    Mash.new(
      :mappings => {},
      :template_mappings => {},
      :test_mappings => {},
      :spec_mappings => {},
      :spec_clusters => {}
    ).merge(config)
  )
  @force_bases = args[:force_bases] || []
  ensure_state
  make_knife_config_if_required
end

Class Attribute Details

.host_provisionedObject

Returns the value of attribute host_provisioned.



19
20
21
# File 'lib/vagabond/internal_configuration.rb', line 19

def host_provisioned
  @host_provisioned
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



28
29
30
# File 'lib/vagabond/internal_configuration.rb', line 28

def config
  @config
end

#force_basesObject

Returns the value of attribute force_bases.



31
32
33
# File 'lib/vagabond/internal_configuration.rb', line 31

def force_bases
  @force_bases
end

#optionsObject (readonly)

Returns the value of attribute options.



30
31
32
# File 'lib/vagabond/internal_configuration.rb', line 30

def options
  @options
end

#uiObject (readonly)

Returns the value of attribute ui.



29
30
31
# File 'lib/vagabond/internal_configuration.rb', line 29

def ui
  @ui
end

Class Method Details

.host_provisioned?Boolean

Returns:

  • (Boolean)


21
22
23
# File 'lib/vagabond/internal_configuration.rb', line 21

def host_provisioned?
  !!@host_provisioned
end

Instance Method Details

#[](k) ⇒ Object



109
110
111
# File 'lib/vagabond/internal_configuration.rb', line 109

def [](k)
  @config[k]
end

#[]=(k, v) ⇒ Object



113
114
115
116
117
118
119
# File 'lib/vagabond/internal_configuration.rb', line 113

def []=(k,v)
  if(v.is_a?(Hash))
    v = storage_hash(v)
  end
  @config[k] = v
  save
end

#cache_pathObject



233
234
235
236
237
238
# File 'lib/vagabond/internal_configuration.rb', line 233

def cache_path
  unless(@cache_path)
    FileUtils.mkdir_p(@cache_path = File.join(store_path, 'chef_cache'))
  end
  @cache_path
end

#check_bases_and_customs!Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/vagabond/internal_configuration.rb', line 86

def check_bases_and_customs!
  if(File.exists?(dna_path))
    dna = Mash.new(JSON.load(File.read(dna_path)))
    %w(bases customs).each do |key|
      if(dna[:vagabond][key])
        dna[:vagabond][key].each do |n, opts|
          options[:force_solo] = true unless Lxc.new(n).exists?
        end
      end
    end
    if(dna[:vagabond][:server])
      srv_name = [
        cookbook_attributes(:vagabond).server.prefix,
        dna[:vagabond][:server][:erchefs].first.to_s.gsub('.', '_')
      ].join
      options[:force_solo] = true unless Lxc.new(srv_name).exists?
    end
    unless(Lxc.new(cookbook_attributes(:vagabond).server.zero_lxc_name).exists?)
      options[:force_solo] = true
    end
  end
end

#cheffile_pathObject



247
248
249
250
251
252
253
254
# File 'lib/vagabond/internal_configuration.rb', line 247

def cheffile_path
  File.expand_path(
    File.join(
      File.dirname(__FILE__),
      running_development_vagabond? ? 'Cheffile.dev' : 'Cheffile'
    )
  )
end

#config_pathObject



324
325
326
# File 'lib/vagabond/internal_configuration.rb', line 324

def config_path
  File.join(store_path, 'vagabond.json')
end

#cookbook_attributes(cookbook, namespace = true) ⇒ Object



378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
# File 'lib/vagabond/internal_configuration.rb', line 378

def cookbook_attributes(cookbook, namespace=true)
  @_attr_cache ||= Mash.new
  unless(@_attr_cache[cookbook])
    node = Chef::Node.new
    %w(rb json js).each do |ext|
      Dir.glob(File.join(cookbook_path, cookbook.to_s, 'attributes', "*.#{ext}")).each do |attr_file|
        node.from_file(attr_file)
      end
    end
    if(namespace)
      key = namespace.is_a?(String) || namespace.is_a?(Symbol) ? namespace : cookbook
      @_attr_cache[cookbook] = node.attributes.send(key)
    else
      @_attr_cache[cookbook] = node.attributes
    end
  end
  @_attr_cache[cookbook]
end

#cookbook_pathObject



240
241
242
243
244
245
# File 'lib/vagabond/internal_configuration.rb', line 240

def cookbook_path
  unless(@cookbook_path)
    FileUtils.mkdir_p(@cookbook_path = File.join(store_path, 'cookbooks'))
  end
  @cookbook_path
end

#cookbook_vendor_required?Boolean

Returns:

  • (Boolean)


262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/vagabond/internal_configuration.rb', line 262

def cookbook_vendor_required?
  need_vendor = !File.exists?(vendor_cheffile_path)
  need_vendor ||= get_checksum(vendor_cheffile_path) != get_checksum(cheffile_path)
  spec = Gem::Specification.find_by_name('vagabond', ::Vagabond::VERSION.version)
  if(running_development_vagabond?)
    if(self[:dev_mode] && self[:dev_mode][:vendor_cookbook_check])
      elapsed_time = Time.now.to_i - self[:dev_mode][:vendor_cookbook_check].to_i
      need_vendor = elapsed_time > (ENV['VAGABOND_DEV_VENDOR_EVERY'] || 3600).to_i
    end
    self[:dev_mode] ||= Mash.new
    self[:dev_mode][:vendor_cookbook_check] = Time.now.to_i if need_vendor
  end
  unless(ENV['VAGABOND_FORCE_VENDOR'].to_s == 'false')
    ENV['VAGABOND_FORCE_VENDOR'] || need_vendor
  end
end

#create_storeObject



121
122
123
# File 'lib/vagabond/internal_configuration.rb', line 121

def create_store
  FileUtils.mkdir_p(store_path)
end

#dna_pathObject



154
155
156
# File 'lib/vagabond/internal_configuration.rb', line 154

def dna_path
  File.join(store_path, 'dna.json')
end

#ensure_stateObject



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/vagabond/internal_configuration.rb', line 62

def ensure_state
  unless(self.class.host_provisioned?)
    install_cookbooks
  end
  check_bases_and_customs!
  set_templates
  store_checksums
  write_dna_json
  write_solo_rb
  if(solo_needed?)
    run_solo
  else
    self.class.host_provisioned = true
  end
end

#file_changed?(path) ⇒ Boolean

Returns:

  • (Boolean)


357
358
359
360
# File 'lib/vagabond/internal_configuration.rb', line 357

def file_changed?(path)
  checksum = get_checksum(path)
  checksum unless @checksums[path] == checksum
end

#get_checksum(path) ⇒ Object



211
212
213
214
215
216
217
218
219
# File 'lib/vagabond/internal_configuration.rb', line 211

def get_checksum(path)
  if(File.exists?(path))
    s = Digest::SHA256.new
    s << File.read(path)
    s.hexdigest
  else
    ''
  end
end

#install_cookbooksObject



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/vagabond/internal_configuration.rb', line 284

def install_cookbooks
  begin
    if(cookbook_vendor_required?)
      FileUtils.copy(cheffile_path, vendor_cheffile_path)
      ui.info ui.color('Fetching required cookbooks...', :yellow)
      cmd = build_command('librarian-chef update', :shellout => {:cwd => File.dirname(cookbook_path)})
      cmd.run_command
      cmd.error!
      ui.info ui.color('  -> COMPLETE!', :yellow)
    else
      cmd = build_command('librarian-chef install', :shellout => {:cwd => File.dirname(cookbook_path)})
      cmd.run_command
      cmd.error!
    end
  rescue => e
    ui.info e.to_s
    ui.info ui.color('  -> FAILED!', :red, :bold)
    raise VagabondError::LibrarianHostInstallFailed.new(e)
  end
end

#knife_config_available?Boolean

Returns:

  • (Boolean)


343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/vagabond/internal_configuration.rb', line 343

def knife_config_available?
  if(File.exists?(File.join(store_path, 'knife.rb')))
    false
  else
    cwd = @vagabondfile.directory.split('/')
    found = false
    until(found || cwd.empty?)
      found = File.exists?(File.join(*(cwd + ['.chef/knife.rb'])))
      cwd.pop
    end
    found
  end
end

#load_existing(file = nil) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/vagabond/internal_configuration.rb', line 125

def load_existing(file=nil)
  if(File.exists?(path = File.join(store_path, 'vagabond.json')))
    if(file)
      file.rewind
      content = file.read
    else
      content = File.read(path)
    end
    if(content.strip.empty?)
      config = Mash.new
    else
      config = Mash.new(
        JSON.load(content)
      )
    end
    @config = storage_hash(Chef::Mixin::DeepMerge.merge(config, @config))
  else
    @config = storage_hash
  end
end

#make_knife_config_if_required(force = false) ⇒ Object



397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/vagabond/internal_configuration.rb', line 397

def make_knife_config_if_required(force=false)
  if(@vagabondfile.local_chef_server? || force)
    unless(knife_config_available?)
      store_dir = File.dirname(store_path)
      k_dir = File.join(store_dir, '.chef')
      FileUtils.mkdir_p(k_dir)
      unless(File.exists?(knife = File.join(k_dir, 'knife.rb')))
        File.open(knife, 'w') do |file|
          file.write <<-EOF
node_name 'dummy'
client_key File.join(File.dirname(__FILE__), 'client.pem')
validation_client_name 'dummy-validator'
validation_key File.join(File.dirname(__FILE__), 'validation.pem')
cookbook_path ['#{%w(cookbooks site-cookbooks).map{|dir|File.join(@vagabondfile.directory, dir)}.join(',')}']
EOF
        end
      end
      %w(client.pem validation.pem).each do |name|
        unless(File.exists?(pem = File.join(k_dir, name)))
          %x{openssl genrsa -out #{pem} 2048}
        end
      end
    end
  end
end

#run_soloObject



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/vagabond/internal_configuration.rb', line 305

def run_solo
  unless(self.class.host_provisioned?)
    begin
      ui.info ui.color('Ensuring expected system state (creating required base containers)', :yellow)
      ui.info ui.color('   - This can take a while on first run or new templates...', :yellow)
      cmd = build_command("chef-solo -j #{File.join(store_path, 'dna.json')} -c #{File.join(store_path, 'solo.rb')}", :sudo => true)
      cmd.run_command
      cmd.error!
      ui.info ui.color('  -> COMPLETE!', :yellow)
      self.class.host_provisioned = true
    rescue => e
      ui.info e.to_s
      FileUtils.rm(solo_path)
      ui.info ui.color('  -> FAILED!', :red, :bold)
      raise VagabondError::HostProvisionFailed.new(e)
    end
  end
end

#running_development_vagabond?Boolean

Returns:

  • (Boolean)


279
280
281
282
# File 'lib/vagabond/internal_configuration.rb', line 279

def running_development_vagabond?
  spec = Gem::Specification.find_by_name('vagabond', ::Vagabond::VERSION.version)
  ::Vagabond::VERSION.segments.last.odd?
end

#saveObject



328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/vagabond/internal_configuration.rb', line 328

def save
  mode = File.exists?(config_path) ? 'r+' : 'w+'
  File.open(config_path, mode) do |file|
    file.flock(File::LOCK_EX)
    file.rewind
    if(sha = file_changed?(file.path))
      @checksums[file.path] = sha
      load_existing(file)
    end
    file.rewind
    file.write(JSON.pretty_generate(@config))
    file.truncate(file.pos)
  end
end

#set_templatesObject



78
79
80
81
82
83
84
# File 'lib/vagabond/internal_configuration.rb', line 78

def set_templates
  unless(Vagabond.const_defined?(:BASE_TEMPLATES))
    Vagabond.const_set(
      :BASE_TEMPLATES, cookbook_attributes(:vagabond).bases.keys
    )
  end
end

#solo_needed?Boolean

Returns:

  • (Boolean)


221
222
223
224
225
226
227
228
229
230
231
# File 'lib/vagabond/internal_configuration.rb', line 221

def solo_needed?
  if(options[:force_solo])
    true
  elsif(options[:disable_solo])
    false
  else
    [dna_path, solo_path].detect do |path|
      @checksums[path] != get_checksum(path)
    end
  end
end

#solo_pathObject



158
159
160
# File 'lib/vagabond/internal_configuration.rb', line 158

def solo_path
  File.join(store_path, 'solo.rb')
end

#storage_hash(hsh = {}) ⇒ Object



362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/vagabond/internal_configuration.rb', line 362

def storage_hash(hsh={})
  new_hash = {}
  hsh.each_pair do |k,v|
    if(v.is_a?(Hash))
      new_hash[k] = storage_hash(v)
    else
      new_hash[k] = v
    end
  end
  n = NotifyMash.new(new_hash)
  n.add_notification do
    save
  end
  n
end

#store_checksumsObject



205
206
207
208
209
# File 'lib/vagabond/internal_configuration.rb', line 205

def store_checksums
  [dna_path, solo_path].each do |path|
    @checksums[path] = get_checksum(path)
  end
end

#store_pathObject



146
147
148
149
150
151
152
# File 'lib/vagabond/internal_configuration.rb', line 146

def store_path
  path = File.join(File.dirname(@vagabondfile.store_path), '.vagabond')
  unless(File.directory?(path))
    FileUtils.mkdir_p(path)
  end
  path
end

#vendor_cheffile_pathObject



256
257
258
259
260
# File 'lib/vagabond/internal_configuration.rb', line 256

def vendor_cheffile_path
  File.expand_path(
    File.join(File.dirname(cookbook_path), 'Cheffile')
  )
end

#write_dna_jsonObject



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/vagabond/internal_configuration.rb', line 162

def write_dna_json
  conf = Mash.new(:bases => Mash.new, :customs => Mash.new)
  (Array(@vagabondfile[:nodes]).map(&:last).map{|i| i[:template]}.compact + Array(force_bases)).uniq.each do |t|
    conf[:bases][t] = Mash.new(:enabled => true) if Vagabond::BASE_TEMPLATES.include?(t.to_s)
  end
  Array(@vagabondfile[:templates]).each do |t_name, opts|
    if(Vagabond::BASE_TEMPLATES.include?(opts[:base].to_s))
      conf[:bases][opts[:base]] = Mash.new(:enabled => true)
      if(opts.has_key?(:memory) && !opts[:memory].is_a?(Hash))
        opts[:memory][:ram] = opts[:memory].to_s
      end
      conf[:customs][generated_name(t_name)] = opts
      config[:template_mappings][t_name] = generated_name(t_name)
    else
      ui.fatal "Invalid base template encountered: #{t_name}"
      ui.info ui.color("  -> Valid base templates: #{BASE_TEMPLATES.sort.join(', ')}", :red)
      raise VagabondError::InvalidBaseTemplate.new(t_name)
    end
  end
  if(@vagabondfile.local_chef_server? && !@vagabondfile[:local_chef_server][:zero])
    version = cookbook_attributes(:vagabond).server.erchefs.dup || []
    version.push @vagabondfile[:local_chef_server][:version] || DEFAULT_ERCHEF_VERSION
    conf[:server] = Mash.new
    conf[:server][:erchefs] = [version].flatten.uniq
  end
  conf[:host_cookbook_store] = cookbook_path
  File.open(dna_path, 'w') do |file|
    file.write(
      JSON.dump(
        :vagabond => conf,
        :run_list => %w(recipe[vagabond])
      )
    )
  end
  save
end

#write_solo_rbObject



199
200
201
202
203
# File 'lib/vagabond/internal_configuration.rb', line 199

def write_solo_rb
  File.open(solo_path, 'w') do |file|
    file.write("\nfile_cache_path \"#{cache_path}\"\ncookbook_path \"#{cookbook_path}\"\n")
  end
end