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