circus-deployment 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +23 -0
- data/README.md +0 -0
- data/bin/circus +24 -0
- data/lib/bundler/circus_bundler.rb +24 -0
- data/lib/bundler/circus_util.rb +43 -0
- data/lib/bundler/patches.rb +18 -0
- data/lib/circus/act.rb +74 -0
- data/lib/circus/actstore_client.rb +30 -0
- data/lib/circus/agents/agent.rb +59 -0
- data/lib/circus/agents/client.rb +77 -0
- data/lib/circus/agents/connection.rb +76 -0
- data/lib/circus/agents/conversation.rb +17 -0
- data/lib/circus/agents/dbus_connection.rb +85 -0
- data/lib/circus/agents/dbus_logger.rb +34 -0
- data/lib/circus/agents/encoding.rb +32 -0
- data/lib/circus/agents/logger.rb +52 -0
- data/lib/circus/agents/params.rb +47 -0
- data/lib/circus/agents/ssh_connection.rb +120 -0
- data/lib/circus/application.rb +99 -0
- data/lib/circus/booth_client.rb +25 -0
- data/lib/circus/booth_tool.rb +98 -0
- data/lib/circus/cli.rb +147 -0
- data/lib/circus/clown_client.rb +27 -0
- data/lib/circus/connection_builder.rb +32 -0
- data/lib/circus/external_util.rb +14 -0
- data/lib/circus/local_config.rb +65 -0
- data/lib/circus/profiles/base.rb +115 -0
- data/lib/circus/profiles/django.rb +90 -0
- data/lib/circus/profiles/jekyll.rb +90 -0
- data/lib/circus/profiles/make_base.rb +17 -0
- data/lib/circus/profiles/pure_py.rb +46 -0
- data/lib/circus/profiles/pure_rb.rb +48 -0
- data/lib/circus/profiles/python_base.rb +39 -0
- data/lib/circus/profiles/rack.rb +59 -0
- data/lib/circus/profiles/ruby_base.rb +52 -0
- data/lib/circus/profiles/shell.rb +46 -0
- data/lib/circus/profiles.rb +10 -0
- data/lib/circus/repos/git.rb +42 -0
- data/lib/circus/repos/mercurial.rb +42 -0
- data/lib/circus/repos.rb +16 -0
- data/lib/circus/resource_allocator_client.rb +19 -0
- data/lib/circus/stdout_logger.rb +11 -0
- data/lib/circus/version.rb +3 -0
- data/lib/circus.rb +9 -0
- data/vendor/ruby-dbus/COPYING +504 -0
- data/vendor/ruby-dbus/ChangeLog +782 -0
- data/vendor/ruby-dbus/HOWTO-RELEASE +14 -0
- data/vendor/ruby-dbus/NEWS +104 -0
- data/vendor/ruby-dbus/README +53 -0
- data/vendor/ruby-dbus/Rakefile +47 -0
- data/vendor/ruby-dbus/doc/tutorial/src/00.index.page +12 -0
- data/vendor/ruby-dbus/doc/tutorial/src/10.intro.page +127 -0
- data/vendor/ruby-dbus/doc/tutorial/src/20.basic_client.page +174 -0
- data/vendor/ruby-dbus/doc/tutorial/src/30.service.page +121 -0
- data/vendor/ruby-dbus/doc/tutorial/src/default.css +129 -0
- data/vendor/ruby-dbus/doc/tutorial/src/default.template +46 -0
- data/vendor/ruby-dbus/examples/gdbus/gdbus +255 -0
- data/vendor/ruby-dbus/examples/gdbus/gdbus.glade +184 -0
- data/vendor/ruby-dbus/examples/gdbus/launch.sh +4 -0
- data/vendor/ruby-dbus/examples/no-introspect/nm-test.rb +21 -0
- data/vendor/ruby-dbus/examples/no-introspect/tracker-test.rb +16 -0
- data/vendor/ruby-dbus/examples/rhythmbox/playpause.rb +25 -0
- data/vendor/ruby-dbus/examples/service/call_service.rb +25 -0
- data/vendor/ruby-dbus/examples/service/service_newapi.rb +51 -0
- data/vendor/ruby-dbus/examples/simple/call_introspect.rb +34 -0
- data/vendor/ruby-dbus/examples/utils/listnames.rb +11 -0
- data/vendor/ruby-dbus/examples/utils/notify.rb +19 -0
- data/vendor/ruby-dbus/lib/dbus/auth.rb +156 -0
- data/vendor/ruby-dbus/lib/dbus/bus.rb +750 -0
- data/vendor/ruby-dbus/lib/dbus/export.rb +133 -0
- data/vendor/ruby-dbus/lib/dbus/introspect.rb +544 -0
- data/vendor/ruby-dbus/lib/dbus/marshall.rb +443 -0
- data/vendor/ruby-dbus/lib/dbus/matchrule.rb +100 -0
- data/vendor/ruby-dbus/lib/dbus/message.rb +293 -0
- data/vendor/ruby-dbus/lib/dbus/type.rb +222 -0
- data/vendor/ruby-dbus/lib/dbus.rb +89 -0
- data/vendor/ruby-dbus/ruby-dbus.gemspec +28 -0
- data/vendor/ruby-dbus/setup.rb +1585 -0
- data/vendor/ruby-dbus/test/Makefile +4 -0
- data/vendor/ruby-dbus/test/bus_driver_test.rb +21 -0
- data/vendor/ruby-dbus/test/server_robustness_test.rb +41 -0
- data/vendor/ruby-dbus/test/server_test.rb +44 -0
- data/vendor/ruby-dbus/test/service_newapi.rb +99 -0
- data/vendor/ruby-dbus/test/session_bus_test_manual.rb +20 -0
- data/vendor/ruby-dbus/test/signal_test.rb +57 -0
- data/vendor/ruby-dbus/test/t1 +4 -0
- data/vendor/ruby-dbus/test/t2.rb +66 -0
- data/vendor/ruby-dbus/test/t3-ticket27.rb +18 -0
- data/vendor/ruby-dbus/test/t5-report-dbus-interface.rb +58 -0
- data/vendor/ruby-dbus/test/t6-loop.rb +85 -0
- data/vendor/ruby-dbus/test/test_all +26 -0
- data/vendor/ruby-dbus/test/test_server +74 -0
- data/vendor/ruby-dbus/test/variant_test.rb +66 -0
- metadata +225 -0
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
## Software License
|
2
|
+
> Copyright (c) 2009-2010 Paul Jones <pauljones23@gmail.com>
|
3
|
+
> Copyright (c) 2009-2010 LShift Ltd. <query@lshift.net>
|
4
|
+
>
|
5
|
+
> Permission is hereby granted, free of charge, to any person
|
6
|
+
> obtaining a copy of this software and associated documentation
|
7
|
+
> files (the "Software"), to deal in the Software without
|
8
|
+
> restriction, including without limitation the rights to use, copy,
|
9
|
+
> modify, merge, publish, distribute, sublicense, and/or sell copies
|
10
|
+
> of the Software, and to permit persons to whom the Software is
|
11
|
+
> furnished to do so, subject to the following conditions:
|
12
|
+
>
|
13
|
+
> The above copyright notice and this permission notice shall be
|
14
|
+
> included in all copies or substantial portions of the Software.
|
15
|
+
>
|
16
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
20
|
+
> HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
21
|
+
> WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
23
|
+
> DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
File without changes
|
data/bin/circus
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Circus command line interface script.
|
3
|
+
# Run <tt>circus -h</tt> to get more usage.
|
4
|
+
|
5
|
+
# If we can see a Gemfile, then we're in dev and should use it
|
6
|
+
if File.exists?(File.expand_path('../../Gemfile', __FILE__))
|
7
|
+
ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', __FILE__)
|
8
|
+
begin
|
9
|
+
# Try to require the preresolved locked set of gems.
|
10
|
+
require File.expand_path('../../.bundle/environment', __FILE__)
|
11
|
+
rescue LoadError
|
12
|
+
# Fall back on doing an unlocked resolve at runtime.
|
13
|
+
require "rubygems"
|
14
|
+
require "bundler"
|
15
|
+
Bundler.setup
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
$: << File.expand_path('../../lib', __FILE__)
|
20
|
+
$: << File.expand_path('../../vendor/ruby-dbus/lib', __FILE__)
|
21
|
+
require File.expand_path('../../lib/circus', __FILE__)
|
22
|
+
require File.expand_path('../../lib/circus/cli', __FILE__)
|
23
|
+
|
24
|
+
Circus::CLI.start
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Circus specific entry-point for bundler that handles the packaging of gems for an application.
|
4
|
+
require 'rubygems'
|
5
|
+
require 'bundler'
|
6
|
+
require 'bundler/cli'
|
7
|
+
require File.expand_path('../patches', __FILE__)
|
8
|
+
require File.expand_path('../circus_util', __FILE__)
|
9
|
+
|
10
|
+
class LoggingShell
|
11
|
+
def say(msg)
|
12
|
+
puts msg
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Run the bundler install
|
17
|
+
dir = File.expand_path('.')
|
18
|
+
ENV['BUNDLE_GEMFILE'] = File.join(dir, 'Gemfile')
|
19
|
+
Bundler.settings[:path] = 'vendor'
|
20
|
+
Bundler.ui = Bundler::UI::Shell.new(LoggingShell.new)
|
21
|
+
Gem::DefaultUserInteraction.ui = Bundler::UI::RGProxy.new(Bundler.ui)
|
22
|
+
Bundler::Installer.install(Bundler.root, Bundler.definition, {})
|
23
|
+
|
24
|
+
Bundler::CircusUtil.fix_external_paths(dir)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
class CircusUtil
|
5
|
+
def self.fix_external_paths(dir)
|
6
|
+
ENV['BUNDLE_GEMFILE'] = File.join(dir, 'Gemfile')
|
7
|
+
|
8
|
+
# Correct any path based components in the Gemfile
|
9
|
+
full_dir = File.expand_path(dir)
|
10
|
+
gem_cache_dir = File.join(dir, 'vendor', 'gems')
|
11
|
+
definition = Bundler.definition
|
12
|
+
required_updates = []
|
13
|
+
definition.sources.select { |s| s.is_a? Bundler::Source::Path }.each do |p|
|
14
|
+
unless p.path.to_s.start_with? full_dir
|
15
|
+
FileUtils.mkdir_p(gem_cache_dir)
|
16
|
+
FileUtils.cp_r(p.path, File.join(gem_cache_dir, p.path.basename.to_s))
|
17
|
+
|
18
|
+
if p.is_a? Bundler::Source::Git
|
19
|
+
required_updates << {:old => /git .*#{p.uri}.*/, :new => "path \"vendor/gems/#{p.path.basename.to_s}\""}
|
20
|
+
else
|
21
|
+
required_updates << {:old => p.options['path'], :new => "vendor/gems/#{p.path.basename.to_s}"}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
if required_updates.length > 0
|
26
|
+
FileUtils.cp "#{dir}/Gemfile", "#{dir}/Gemfile.circus_orig"
|
27
|
+
gf_content = File.read("#{dir}/Gemfile")
|
28
|
+
required_updates.each do |u|
|
29
|
+
gf_content.gsub!(u[:old], u[:new])
|
30
|
+
end
|
31
|
+
File.open("#{dir}/Gemfile", 'w') do |f|
|
32
|
+
f.write(gf_content)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.unfix_external_paths(dir)
|
38
|
+
if File.exists? "#{dir}/Gemfile.circus_orig"
|
39
|
+
FileUtils.mv "#{dir}/Gemfile.circus_orig", "#{dir}/Gemfile"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/circus/act.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Circus
|
5
|
+
class Act
|
6
|
+
attr_reader :name, :dir, :props, :profile
|
7
|
+
|
8
|
+
def initialize(name, dir, props = {})
|
9
|
+
@name = name
|
10
|
+
@dir = dir
|
11
|
+
@props = props
|
12
|
+
|
13
|
+
# Merge act specific properties
|
14
|
+
if File.exists? act_file
|
15
|
+
act_cfg = YAML.load(File.read(act_file))
|
16
|
+
@props.merge! act_cfg
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def should_package?
|
21
|
+
return false if @props['no-package']
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def detect!
|
26
|
+
# Run each profile against the directory to try to find one that matches
|
27
|
+
profile_clazz = Circus::Profiles::PROFILES.find { |p| p.accepts?(@name, @dir, @props) }
|
28
|
+
raise "No profile can run #{@dir}" unless profile_clazz
|
29
|
+
|
30
|
+
@profile = profile_clazz.new(@name, @dir, @props)
|
31
|
+
end
|
32
|
+
|
33
|
+
def package_for_dev(logger, run_root_dir)
|
34
|
+
detect! unless @profile
|
35
|
+
|
36
|
+
# Create our run directory, and tell the profile to package into it
|
37
|
+
run_dir = File.join(run_root_dir, @name)
|
38
|
+
FileUtils.mkdir_p run_dir
|
39
|
+
profile.package_for_dev(logger, run_dir)
|
40
|
+
end
|
41
|
+
|
42
|
+
def assemble(logger, output_root_dir, overlay_root)
|
43
|
+
detect! unless @profile
|
44
|
+
|
45
|
+
overlay_dir = File.join(overlay_root, name)
|
46
|
+
FileUtils.mkdir_p(overlay_dir)
|
47
|
+
return false unless profile.package_for_deploy(logger, overlay_dir)
|
48
|
+
|
49
|
+
begin
|
50
|
+
# Squash the output file
|
51
|
+
include_dirs = ["#{overlay_dir}/*"]
|
52
|
+
include_dirs << "#{@dir}/*" if profile.package_base_dir?
|
53
|
+
include_dirs.concat(profile.extra_dirs)
|
54
|
+
output_name = File.join(output_root_dir, "#{name}.act")
|
55
|
+
|
56
|
+
ExternalUtil.run_external(logger, 'Output packaging',
|
57
|
+
"mksquashfs #{include_dirs.join(' ')} #{output_name} -noappend -noI -noD -noF 2>&1")
|
58
|
+
ensure
|
59
|
+
profile.cleanup_after_deploy(logger, overlay_dir)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def upload(output_root_dir, act_store)
|
64
|
+
detect! unless @profile
|
65
|
+
|
66
|
+
output_name = File.join(output_root_dir, "#{name}.act")
|
67
|
+
act_store.upload_act(output_name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def act_file
|
71
|
+
File.join(@dir, 'act.yaml')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Circus
|
2
|
+
class ActStoreClient
|
3
|
+
def initialize(root, logger)
|
4
|
+
@root = root
|
5
|
+
@logger = logger
|
6
|
+
end
|
7
|
+
|
8
|
+
def upload_act(fn)
|
9
|
+
actname = File.basename(fn)
|
10
|
+
upload_url = "#{@root}/#{actname}"
|
11
|
+
|
12
|
+
@logger.info "Uploading to #{upload_url}"
|
13
|
+
uri = URI.parse(upload_url)
|
14
|
+
|
15
|
+
res = Net::HTTP.start(uri.host, uri.port) do |http|
|
16
|
+
req = Net::HTTP::Put.new(uri.request_uri)
|
17
|
+
req.body = File.read(fn)
|
18
|
+
req.content_type = 'application/binary'
|
19
|
+
|
20
|
+
http.request req
|
21
|
+
end
|
22
|
+
unless res.is_a? Net::HTTPSuccess
|
23
|
+
@logger.error "FAILED: Act Upload"
|
24
|
+
@logger.error " Status: #{res.code}"
|
25
|
+
@logger.error " Response: #{res.body}"
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'blather/client/dsl'
|
2
|
+
require 'circus/agents/params'
|
3
|
+
require 'circus/agents/logger'
|
4
|
+
require 'circus/agents/connection'
|
5
|
+
|
6
|
+
module Circus
|
7
|
+
module Agents
|
8
|
+
class Agent
|
9
|
+
include Blather::DSL
|
10
|
+
|
11
|
+
class <<self
|
12
|
+
def commands
|
13
|
+
@commands ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def command(name, &body)
|
17
|
+
commands << name
|
18
|
+
|
19
|
+
define_method("command_#{name}", &body)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(connection = nil)
|
24
|
+
@client = (connection || Connection.new)
|
25
|
+
|
26
|
+
# Approve all subscription requests
|
27
|
+
subscription :request? do |s|
|
28
|
+
write_to_stream s.approve!
|
29
|
+
end
|
30
|
+
|
31
|
+
# Add a handler for each chat command
|
32
|
+
self.class.commands.each do |name|
|
33
|
+
message :chat?, :body => /^#{name}( .*)?$/ do |m|
|
34
|
+
# begin
|
35
|
+
params = CommandParams.new(m.body[(name.length + 1)..-1])
|
36
|
+
logger = XMPPLogger.new(m.from, m.thread || m.id, lambda { |msg| write_to_stream(msg) })
|
37
|
+
|
38
|
+
send("command_#{name}", params, logger)
|
39
|
+
# rescue
|
40
|
+
# puts $!, $@
|
41
|
+
# end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
disconnected do
|
46
|
+
puts "Disconnected"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def configure!(config)
|
51
|
+
client.configure!(config)
|
52
|
+
end
|
53
|
+
|
54
|
+
def run
|
55
|
+
client.run
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'circus/agents/encoding'
|
2
|
+
require 'monitor'
|
3
|
+
|
4
|
+
module Circus
|
5
|
+
module Agents
|
6
|
+
# Client for XMPP based agents.
|
7
|
+
class Client
|
8
|
+
def initialize(connection)
|
9
|
+
@connection = connection
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(to, name, params = {}, &block)
|
13
|
+
body = if params.length > 0
|
14
|
+
"#{name} #{Encoding.encode(params)}"
|
15
|
+
else
|
16
|
+
name
|
17
|
+
end
|
18
|
+
msg = Blather::Stanza::Message.new to, body
|
19
|
+
msg.id = Blather::Stanza.next_id
|
20
|
+
msg.thread = msg.id
|
21
|
+
|
22
|
+
promise = Promise.new
|
23
|
+
last = nil
|
24
|
+
@connection.register_thread_handler(msg.thread) do |m|
|
25
|
+
block.call(m)
|
26
|
+
|
27
|
+
begin
|
28
|
+
if Conversation.ended? m
|
29
|
+
result = nil
|
30
|
+
if last and last.body and last.body.start_with? 'ok '
|
31
|
+
result = Encoding.decode(last.body[('ok '.length)..-1])
|
32
|
+
end
|
33
|
+
|
34
|
+
promise.completed!(result)
|
35
|
+
else
|
36
|
+
last = m
|
37
|
+
end
|
38
|
+
rescue
|
39
|
+
puts $!, $@
|
40
|
+
end
|
41
|
+
end
|
42
|
+
@connection.write(msg)
|
43
|
+
|
44
|
+
promise
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# A promise provides a reference to an expected future result. Client calls
|
49
|
+
# return immediately upon message dispatch - in order to retrieve the final
|
50
|
+
# result, the promise can be inspected.
|
51
|
+
class Promise
|
52
|
+
def initialize
|
53
|
+
@monitor = Monitor.new
|
54
|
+
@cond = @monitor.new_cond
|
55
|
+
@completed = false
|
56
|
+
@result = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def completed!(result)
|
60
|
+
@monitor.synchronize do
|
61
|
+
@completed = true
|
62
|
+
@result = result
|
63
|
+
@cond.signal
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def result
|
68
|
+
@monitor.synchronize do
|
69
|
+
return @result if @completed
|
70
|
+
|
71
|
+
@cond.wait
|
72
|
+
@result
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'blather/client/client'
|
2
|
+
require 'circus/agents/conversation'
|
3
|
+
require 'monitor'
|
4
|
+
|
5
|
+
module Circus
|
6
|
+
module Agents
|
7
|
+
class Connection < Blather::Client
|
8
|
+
attr_reader :thread_handlers
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super
|
12
|
+
|
13
|
+
@ready_mon = Monitor.new
|
14
|
+
@ready_cond = @ready_mon.new_cond
|
15
|
+
@thread_handlers = {}
|
16
|
+
register_handler :message do |m|
|
17
|
+
process_chat_thread(m)
|
18
|
+
end
|
19
|
+
register_handler :disconnected do
|
20
|
+
puts "Disconnected!"
|
21
|
+
end
|
22
|
+
register_handler :ready do
|
23
|
+
@ready_mon.synchronize {
|
24
|
+
@ready_cond.signal
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def configure!(config)
|
30
|
+
args = [config.jid, config.password]
|
31
|
+
if config.host
|
32
|
+
args << config.host
|
33
|
+
args << config.port if config.port
|
34
|
+
end
|
35
|
+
|
36
|
+
setup *args
|
37
|
+
end
|
38
|
+
|
39
|
+
def configure_bg!(config)
|
40
|
+
configure!(config)
|
41
|
+
|
42
|
+
Thread.new {
|
43
|
+
EM.run {
|
44
|
+
begin
|
45
|
+
run
|
46
|
+
rescue
|
47
|
+
puts $!, $@
|
48
|
+
end
|
49
|
+
}
|
50
|
+
}
|
51
|
+
@ready_mon.synchronize {
|
52
|
+
@ready_cond.wait(2)
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def register_thread_handler(thread_id, &block)
|
57
|
+
thread_handlers[thread_id] = block
|
58
|
+
end
|
59
|
+
|
60
|
+
def remove_thread_handler(thread_id)
|
61
|
+
thread_handlers.delete(thread_id)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def process_chat_thread(m)
|
66
|
+
return false unless m.thread and thread_handlers[m.thread]
|
67
|
+
|
68
|
+
handler = thread_handlers[m.thread]
|
69
|
+
remove_thread_handler(m.thread) if Conversation.ended? m
|
70
|
+
|
71
|
+
handler.call(m)
|
72
|
+
true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Circus
|
2
|
+
module Agents
|
3
|
+
class Conversation
|
4
|
+
CHAT_STATES = 'http://jabber.org/protocol/chatstates'
|
5
|
+
|
6
|
+
def self.end(m)
|
7
|
+
gone_el = Nokogiri::XML::Element.new('gone', m.document)
|
8
|
+
gone_el.default_namespace = CHAT_STATES
|
9
|
+
m << gone_el
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.ended?(m)
|
13
|
+
m.xpath('ns:gone', 'ns' => CHAT_STATES).length > 0
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'dbus'
|
2
|
+
require 'uuid'
|
3
|
+
|
4
|
+
module Circus
|
5
|
+
module Agents
|
6
|
+
class DBusConnection
|
7
|
+
def initialize(bus = nil)
|
8
|
+
@bus = bus || DBus::SystemBus.instance
|
9
|
+
end
|
10
|
+
|
11
|
+
def configure_bg!(options = {})
|
12
|
+
Thread.new do
|
13
|
+
l = DBus::Main.new
|
14
|
+
l << @bus
|
15
|
+
l.run
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(target, object, iface, method, args, logger)
|
20
|
+
action_id = UUID.generate
|
21
|
+
|
22
|
+
@service = @bus.service("com.deployacircus.#{object}")
|
23
|
+
obj = @service.object("/com/deployacircus/#{object}")
|
24
|
+
obj.introspect
|
25
|
+
obj_iface = obj["com.deployacircus.#{iface}"]
|
26
|
+
dbus_method = obj_iface.methods[method]
|
27
|
+
raise ArgumentError.new("Method #{method} on #{object} not found") unless dbus_method
|
28
|
+
|
29
|
+
msg = DBus::Message.new(DBus::Message::METHOD_CALL)
|
30
|
+
msg.path = obj.path
|
31
|
+
msg.interface = obj_iface.name
|
32
|
+
msg.destination = obj.destination
|
33
|
+
msg.member = dbus_method.name
|
34
|
+
msg.sender = @bus.unique_name
|
35
|
+
dbus_method.params.each do |p|
|
36
|
+
if p.name == 'id'
|
37
|
+
msg.add_param p.type, action_id
|
38
|
+
else
|
39
|
+
msg.add_param(p.type, args[p.name])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
promise = DBusPromise.new(@bus)
|
43
|
+
@bus.on_return(msg) do |rmsg|
|
44
|
+
promise.completed!(rmsg)
|
45
|
+
end
|
46
|
+
obj_iface.on_signal(@bus, 'LogEntry') do |log_action_id, msg|
|
47
|
+
logger.info(msg) if action_id == log_action_id
|
48
|
+
end
|
49
|
+
@bus.send(msg.marshall)
|
50
|
+
|
51
|
+
promise
|
52
|
+
end
|
53
|
+
|
54
|
+
def send_file(fn)
|
55
|
+
# Normal DBus connections can see the file locally just fine, so we don't need to
|
56
|
+
# do anything with it.
|
57
|
+
fn
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class DBusPromise
|
62
|
+
def initialize(bus)
|
63
|
+
@bus = bus
|
64
|
+
@l = DBus::Main.new
|
65
|
+
@l << @bus
|
66
|
+
@result = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def result
|
70
|
+
@l.run
|
71
|
+
|
72
|
+
if @result.is_a? DBus::Error
|
73
|
+
raise @result
|
74
|
+
else
|
75
|
+
@result.params[0]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def completed!(result)
|
80
|
+
@result = result
|
81
|
+
@l.quit
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Circus
|
2
|
+
module Agents
|
3
|
+
class DBusLogger
|
4
|
+
def initialize(adapter, action_id)
|
5
|
+
@adapter = adapter
|
6
|
+
@action_id = action_id
|
7
|
+
end
|
8
|
+
|
9
|
+
def info(content)
|
10
|
+
write(content)
|
11
|
+
end
|
12
|
+
|
13
|
+
def error(content)
|
14
|
+
write("ERROR: #{content}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def failed(msg)
|
18
|
+
write("failed #{msg}")
|
19
|
+
end
|
20
|
+
|
21
|
+
def complete(res = nil)
|
22
|
+
if res
|
23
|
+
write("ok #{Encoding.encode(res)}")
|
24
|
+
else
|
25
|
+
write('ok')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def write(msg)
|
30
|
+
@adapter.LogEntry(@action_id, msg)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Circus
|
4
|
+
module Agents
|
5
|
+
class Encoding
|
6
|
+
def self.encode(params, sep = '&')
|
7
|
+
params.map do |k,v|
|
8
|
+
if v.is_a? Array
|
9
|
+
v.map do |iv|
|
10
|
+
"#{CGI.escape(k.to_s)}=#{CGI.escape(iv.to_s)}"
|
11
|
+
end.join(sep)
|
12
|
+
else
|
13
|
+
"#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
|
14
|
+
end
|
15
|
+
end.join(sep)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.decode(str)
|
19
|
+
parsed = CGI::parse(str)
|
20
|
+
result = {}
|
21
|
+
parsed.each do |k, v|
|
22
|
+
if v.length == 1
|
23
|
+
result[k] = v.first
|
24
|
+
else
|
25
|
+
result[k] = v
|
26
|
+
end
|
27
|
+
end
|
28
|
+
result
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'circus/agents/encoding'
|
2
|
+
require 'circus/agents/conversation'
|
3
|
+
|
4
|
+
module Circus
|
5
|
+
module Agents
|
6
|
+
class XMPPLogger
|
7
|
+
def initialize(to, thread, writer)
|
8
|
+
@to = to
|
9
|
+
@thread = thread || "thread#{Blather::Stanza.next_id}"
|
10
|
+
@writer = writer
|
11
|
+
end
|
12
|
+
|
13
|
+
def info(content)
|
14
|
+
write("#{content}")
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def error(content)
|
19
|
+
write("ERROR: #{content}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def failed(msg)
|
23
|
+
write("failed #{msg}")
|
24
|
+
gone
|
25
|
+
end
|
26
|
+
|
27
|
+
def complete(res = nil)
|
28
|
+
if res
|
29
|
+
write("ok #{Encoding.encode(res)}")
|
30
|
+
else
|
31
|
+
write('ok')
|
32
|
+
end
|
33
|
+
gone
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def write(content)
|
38
|
+
msg = Blather::Stanza::Message.new @to, content
|
39
|
+
msg.thread = @thread
|
40
|
+
@writer.call(msg)
|
41
|
+
end
|
42
|
+
|
43
|
+
def gone
|
44
|
+
msg = Blather::Stanza::Message.new @to, nil
|
45
|
+
msg.thread = @thread
|
46
|
+
|
47
|
+
Conversation.end(msg)
|
48
|
+
@writer.call(msg)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|