circus-deployment 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|