lib/logical-construct/ground-control/provision.rb in logical-construct-0.0.5 vs lib/logical-construct/ground-control/provision.rb in logical-construct-0.1.0

- old
+ new

@@ -1,205 +1,110 @@ require 'mattock' -require 'json' -require 'logical-construct/resolving-task' +require 'logical-construct/ground-control/generate-manifest' +require 'logical-construct/ground-control/build-plan' +require 'logical-construct/protocol/ssh-tunnel' + module LogicalConstruct module GroundControl class Provision < Mattock::Tasklib - class WebConfigure < Mattock::Task - 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 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 = 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 :target_protocol, "http" setting(:target_address, nil).isnt(:copiable) - setting :target_port, 51076 - setting :resolutions - setting :marshalling_path, "marshall" + setting :local_target_port, 51076 + setting :remote_target_port, 30712 - setting(:secret_data, nested { - setting :path - setting :tarball_path - setting :file_list - }) + setting :plan_archives, [] - setting(:normal_data, nested { - setting :path - setting :tarball_path - setting :file_list - }) + dir(:marshalling, "marshall") + dir(:plan_source, "plans") - setting(:cookbooks, nested { - setting :path - setting :tarball_path - setting :file_list - }) - - setting :json_attribs_path - setting :roles - setting :node_attribs - 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" - self.resolutions = {} - self.roles = {} - self.node_attribs = { "run_list" => [] } - end - def resolve_configuration + resolve_paths 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 + manifest = nil in_namespace do - directory marshalling_path + directory marshalling.absolute_path task :collect, [:ipaddr] do |task, args| self.target_address = args[:ipaddr] end - task :build_json_attribs, [:role] do |task, args| - 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 + tunnel = LogicalConstruct::SSHTunnel.new do |tunnel| + tunnel.target_address = proxy_value.target_address + copy_settings_to(tunnel) + self.local_target_port = tunnel.proxy_value.local_target_port end - desc "Print attribs (optionally: for :role)" - task :inspect_attribs, [:role] => :build_json_attribs do - puts json_attribs - end + manifest = LogicalConstruct::GenerateManifest.new(self) - 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! + start_flight = Mattock::Rake::RemoteCommandTask.define_task(:start_flight => :collect) do |start_flight| + start_flight.remote_server.address = proxy_value.target_address + start_flight.command = + Mattock::CommandLine.new("nohup", + "/opt/logical-construct/bin/flight-deck", + "-C start_server") + start_flight.command.redirect_stdin("/dev/null") + start_flight.command.redirect_stdout("/opt/logical-construct/flight_deck_server.log") + start_flight.command.copy_stream_to(2, 1) + start_flight.command.redirections << "&" 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! + start_resolution = Mattock::Rake::RemoteCommandTask.define_task(:start_resolution => :deliver_manifest) do |start_flight| + start_flight.remote_server.address = proxy_value.target_address + start_flight.verbose = 3 + start_flight.command = + Mattock::CommandLine.new("nohup", + "/opt/logical-construct/bin/flight-deck") + #TODO: Mattock CommandLine needs a "background" + #TODO: RemoteCommandTask should wrap the whole + #nohup-redirect-background thing + start_flight.command.redirect_stdin("/dev/null") + start_flight.command.redirect_stdout("/opt/logical-construct/flight_deck.log") + start_flight.command.copy_stream_to(2, 1) + start_flight.command.redirections << "&" end - 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 + task manifest.root_task => :collect - manifest = LogicalConstruct::GenerateManifest.new(self, :manifest => - [ - cookbooks.tarball_path, - secret_data.tarball_path, - normal_data.tarball_path, - :collect - ]) do |manifest| - manifest.receiving_name = "configuration:Manifest" - end + task_spine(:start_flight, :deliver_manifest, :fulfill_manifest, :start_resolution, :complete_provision) + task :deliver_manifest => manifest[:deliver] + task :fulfill_manifest => manifest[:fulfill] - WebConfigure.new(:web_configure => [:collect, :build_json_attribs, :manifest, cookbooks.tarball_path]) do |task| - self.proxy_settings_to(task) - task.target_address = proxy_value.target_address + tunnel.wrap(manifest[:deliver]) + tunnel.wrap(manifest[:fulfill]) + task :complete_provision => tunnel[:close_tunnel] + end + + desc "Provision :ipaddr with specified configs" + task root_task, [:ipaddr] => self[:complete_provision] + end + + def plan_task(name, &block) + plan = BuildPlan.new(self) do |build| + build.basename = name + yield build if block_given? + end + task self[:manifest] => plan.archive_path + + in_namespace do + namespace :package do + desc "Compile and archive plan #{name.inspect}" + task name => plan.archive_path end end - desc "Provision :ipaddr with specified configs (optionally: for :role)" - task root_task, [:ipaddr, :role] => self[:web_configure] + plan_archives << plan.archive_path + end + + def plans(*names) + names.each do |name| + plan_task(name) + end end end end end