lib/logical-construct/ground-control/provision.rb in logical-construct-0.0.1.localtesting vs lib/logical-construct/ground-control/provision.rb in logical-construct-0.0.1

- old
+ new

@@ -1,70 +1,187 @@ require 'mattock' -require 'rake/packagetask' +require 'json' +require 'logical-construct/resolving-task' module LogicalConstruct module GroundControl class Provision < Mattock::Tasklib class WebConfigure < Mattock::Task - setting :target_ipaddr, nil - setting :web_port, 51076 + include ResolutionProtocol + + setting :target_protocol, "http" + setting :target_address, nil + setting :target_port, 51076 + runtime_setting :target_url setting :resolutions, {} + runtime_setting :web_resolutions - def action - connection = Excon.new(target_address) - index = connection.get - body = Nokogiri::HTML(index.body) - resolution_needed = body.xpath('//a[@href]') - resolution_needed.each do |link| - href = link['href'] - connection.post(href, {"data" => resolve(href)}) - end + def resolve_runtime_configuration + super + self.target_url ||= "#{target_protocol}://#{target_address}:#{target_port}/" + self.web_resolutions = Hash[resolutions.map do |name, value| + [web_path(name), value] + end] end def resolve(path) - resolved = resolutions.fetch(path) + resolved = web_resolutions.fetch(path) if resolved.respond_to? :call resolved = resolved.call end return resolved + rescue KeyError + puts "Can't find a resolution for #{path} in #{web_resolutions.keys.inspect} (ex #{resolutions.keys})" + raise end + + def uri(options) + uri_class = URI.scheme_list[target_protocol.upcase] + uri_hash = {:host => target_address, :port => target_port} + return uri_class.build(uri_hash.merge(options)).to_s + end + + def resolution_needed + index = RestClient.get(uri(:path => '/')) + body = Nokogiri::HTML(index.body) + return body.xpath("//a[@rel='requirement']") + end + + + #XXX I would like this to become an actual RESTful client at some + #point, but seems it would mean building it from scratch + def action + require 'uri' + require 'rest-client' + require 'nokogiri' + + until (link = resolution_needed.first).nil? + href = link['href'] + begin + response = RestClient.post(uri(:path => href), :data => resolve(href)) + rescue RestClient::InternalServerError => ex + require 'tempfile' + file = Tempfile.open('provision-error.html') + path = file.path + file.close! + + File::open(path, "w") do |file| + file.write ex.http_body + end + puts "Written error response to #{path}" + puts "Try: chromium #{path}" + fail "Unsuccessful response from server!" + end + end + end end default_namespace :provision setting :valise - setting :web_port, 51076 - setting :target_ipaddr, nil + setting :target_protocol, "http" + setting(:target_address, nil).isnt(:copiable) + setting :target_port, 51076 setting :resolutions, {} - setting :marshalling_path - setting :cookbooks_path + setting :marshalling_path, "marshall" + setting(:secret_data, nested { + setting :path + setting :tarball_path + setting :file_list + }) + + setting(:normal_data, nested { + setting :path + setting :tarball_path + setting :file_list + }) + + setting(:cookbooks, nested { + setting :path + setting :tarball_path + setting :file_list + }) + + setting :json_attribs_path + setting :roles, {} + setting :node_attribs, { "run_list" => [] } + setting :json_attribs, "" + def default_configuration(core) core.copy_settings_to(self) super + self.cookbooks.path = "cookbooks" + self.secret_data.path = "data-bags/secret" + self.normal_data.path = "data-bags" end + def resolve_configuration + super + self.json_attribs_path ||= File::join(marshalling_path, "node.json") + + self.cookbooks.file_list ||= Rake::FileList[cookbooks.path + "/**/*"].exclude(%r{/[.]git/}).exclude(%r{[.]sw[.]$}) + self.secret_data.file_list ||= Rake::FileList[secret_data.path + "/**/*"].exclude(%r{[.]sw[.]$}) + self.normal_data.file_list ||= + Rake::FileList[normal_data.path + "/**/*"].exclude(%r{^#{secret_data.path}}).exclude(%r{[.]sw[.]$}) + + self.cookbooks.tarball_path ||= File::join(marshalling_path, "cookbooks.tgz") + self.secret_data.tarball_path ||= File::join(marshalling_path, "secret_data_bags.tgz") + self.normal_data.tarball_path ||= File::join(marshalling_path, "normal_data_bags.tgz") + + resolutions["chef_config:cookbook_tarball"] ||= proc do + File::open(cookbooks.tarball_path, "rb") + end + + resolutions["chef_config:secret_data_tarball"] ||= proc do + File::open(secret_data.tarball_path, "rb") + end + + resolutions["chef_config:normal_data_tarball"] ||= proc do + File::open(normal_data.tarball_path, "rb") + end + end + + include Mattock::CommandLineDSL def define in_namespace do - task :collect, [:ipaddr] do |task, args| - self.target_ipaddr = args[:ipaddr] + directory marshalling_path + + task :collect, [:ipaddr, :role] do |task, args| + self.target_address = args[:ipaddr] + unless args[:role].nil? + self.node_attribs["run_list"] = roles[args[:role]] + end + self.json_attribs = JSON.pretty_generate(node_attribs) + resolutions["chef_config:json_attribs"] ||= json_attribs end - WebConfigure.new(:web_configure => :collect) do |task| - self.copy_settings_to(task) + file secret_data.tarball_path => [marshalling_path] + secret_data.file_list do + cmd("tar", "--exclude **/*.sw?", "-czf", secret_data.tarball_path, secret_data.path).must_succeed! end - namespace :cookbook do - Rake::PackageTask.new("cookbook", :noversion) do |task| - task.need_tar_gz = true - task.package_dir = marshalling_path - task.package_files.include(cookbooks_path + "/**/*") - end + file normal_data.tarball_path => [marshalling_path] + normal_data.file_list do + cmd("tar", + "--exclude **/*.sw?", + "--exclude #{secret_data.path}", + "-czf", normal_data.tarball_path, normal_data.path).must_succeed! end - end - task root_task, [:ipaddr] => :web_configure + file cookbooks.tarball_path => [marshalling_path] + cookbooks.file_list do + cmd("tar", "--exclude .git", "--exclude **/*.sw?", "-czf", cookbooks.tarball_path, cookbooks.path).must_succeed! + end + manifest = LogicalConstruct::GenerateManifest.new(self, :manifest => [cookbooks.tarball_path, :collect]) do |manifest| + manifest.receiving_name = "configuration:Manifest" + end + + WebConfigure.new(:web_configure => [:collect, :manifest, cookbooks.tarball_path]) do |task| + self.proxy_settings_to(task) + task.target_address = proxy_value.target_address + end + end + + task root_task, [:ipaddr] => self[:web_configure] end end end end