arborist 0.0.1.pre20160106113421 → 0.0.1.pre20160128152542

Sign up to get free protection for your applications and to get access to all the features.
@@ -117,6 +117,22 @@ class Arborist::Client
117
117
  end
118
118
 
119
119
 
120
+ ### Remove a subscription
121
+ def unsubscribe( *args )
122
+ request = self.make_unsubscribe_request( *args )
123
+ response = self.send_tree_api_request( request )
124
+ return response
125
+ end
126
+
127
+
128
+ ### Remove the subscription with the specified +subid+.
129
+ def make_unsubscribe_request( subid )
130
+ self.log.debug "Making unsubscribe request for subid: %s" % [ subid ]
131
+
132
+ return self.pack_message( :unsubscribe, subscription_id: subid )
133
+ end
134
+
135
+
120
136
  ### Send the packed +request+ via the Tree API socket, raise an error on
121
137
  ### unsuccessful response, and return the response body.
122
138
  def send_tree_api_request( request )
@@ -0,0 +1,30 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ require 'arborist/cli' unless defined?( Arborist::CLI )
5
+ require 'arborist/client'
6
+
7
+ # Command to start an interactive client session.
8
+ module Arborist::CLI::Client
9
+ extend Arborist::CLI::Subcommand
10
+
11
+ desc 'Start an interactive client session'
12
+ long_desc <<-EOF
13
+ Starts a pry session in an Arborist::Client context.
14
+ EOF
15
+
16
+ command :client do |cmd|
17
+ cmd.action do |globals, options, args|
18
+ begin
19
+ require 'pry'
20
+ rescue LoadError
21
+ exit_now! "This command requires the 'pry' gem."
22
+ end
23
+
24
+ client = Arborist::Client.new
25
+ Pry.pry( client )
26
+ end
27
+ end
28
+
29
+ end # module Arborist::CLI::Client
30
+
@@ -0,0 +1,20 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ require 'arborist/cli' unless defined?( Arborist::CLI )
5
+
6
+
7
+ # Command to dump a basic Arborist config file
8
+ module Arborist::CLI::Config
9
+ extend Arborist::CLI::Subcommand
10
+
11
+ desc 'Dump a default Arborist config file'
12
+ command :config do |cmd|
13
+
14
+ cmd.action do |globals, options, args|
15
+ $stdout.puts Configurability.default_config.dump
16
+ end
17
+ end
18
+
19
+ end # module Arborist::CLI::Clone
20
+
@@ -0,0 +1,60 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ require 'arborist/cli' unless defined?( Arborist::CLI )
5
+
6
+
7
+ # Command to start a Arborist daemon
8
+ module Arborist::CLI::Start
9
+ extend Arborist::CLI::Subcommand
10
+
11
+ desc 'Start an Arborist daemon'
12
+ long_desc <<-EOF
13
+ Start the Arborist manager, observers, or monitors. The SOURCE is
14
+ passed to the loader to tell it where to load things from.
15
+ EOF
16
+
17
+ arg :DAEMON
18
+ arg :SOURCE
19
+
20
+ command :start do |cmd|
21
+
22
+ cmd.flag :loader, desc: "Specify a loader type to use.",
23
+ default_value: 'file'
24
+
25
+ cmd.action do |globals, options, args|
26
+ appname = args.shift
27
+ source = args.shift
28
+
29
+ loader = Arborist::Loader.create( options.loader, source )
30
+ runner = case appname
31
+ when 'manager'
32
+ Arborist.manager_for( loader )
33
+ when 'monitors'
34
+ Arborist.monitor_runner_for( loader )
35
+ when 'observers'
36
+ Arborist.observer_runner_for( loader )
37
+ else
38
+ raise "Don't know how to start %p" % [ appname ]
39
+ end
40
+
41
+ unless_dryrun( "starting #{appname}" ) do
42
+ start( runner )
43
+ end
44
+ end
45
+ end
46
+
47
+
48
+ ###############
49
+ module_function
50
+ ###############
51
+
52
+ ### Start the specified +runner+ instance after setting up the environment for
53
+ ### it.
54
+ def start( runner )
55
+ $0 = runner.class.name
56
+ runner.run
57
+ end
58
+
59
+ end # module Arborist::CLI::Start
60
+
@@ -0,0 +1,88 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ require 'msgpack'
5
+
6
+ require 'arborist/cli' unless defined?( Arborist::CLI )
7
+ require 'arborist/client'
8
+
9
+ # Command to watch events in an Arborist manager.
10
+ module Arborist::CLI::Watch
11
+ extend Arborist::CLI::Subcommand
12
+
13
+ desc 'Watch events in an Arborist manager'
14
+
15
+ command :watch do |cmd|
16
+ cmd.action do |globals, options, args|
17
+ client = Arborist::Client.new
18
+ subid = client.subscribe( identifier: '_' )
19
+
20
+ sock = client.event_api
21
+ sock.subscribe( subid )
22
+ prompt.say "Subscription %p" % [ subid ]
23
+
24
+ begin
25
+ prompt.say "Watching for events on manager at %s" % [ client.event_api_url ]
26
+ loop do
27
+ msgsubid = sock.recv
28
+ raise "Partial write?!" unless sock.rcvmore?
29
+ raw_event = sock.recv
30
+
31
+ event = MessagePack.unpack( raw_event )
32
+ prompt.say "[%s] %s\n" % [
33
+ hl(Time.now.strftime('%Y-%m-%d %H:%M:%S %Z')).color(:dark, :white),
34
+ hl(dump_event( event )).color( :dark, :white )
35
+ ]
36
+ end
37
+ ensure
38
+ client.unsubscribe( subid )
39
+ end
40
+ end
41
+ end
42
+
43
+
44
+ ###############
45
+ module_function
46
+ ###############
47
+
48
+ ### Return a String representation of the specified +event+.
49
+ def dump_event( event )
50
+ event_type = event['type']
51
+ id = event['identifier']
52
+
53
+ case event_type
54
+ when 'node.update'
55
+ type, status, error = event['data'].values_at( *%w'type status error' )
56
+ return "%s updated: %s is %s%s" % [
57
+ hl( id ).color( :cyan ),
58
+ type,
59
+ hl( status ).color( status.to_sym ),
60
+ error ? " (#{error})" : ''
61
+ ]
62
+ when 'node.delta'
63
+ pairs = diff_pairs( event['data'] )
64
+ return "%s delta, changes: %s" % [ hl( id ).color( :cyan ), pairs ]
65
+ else
66
+ return "%s event: %p" % [ hl(event_type).color(:dark, :white), event ]
67
+ end
68
+ end
69
+
70
+
71
+ ### Return a string showing the differences in a delta event's change +data+.
72
+ def diff_pairs( data )
73
+ return data.collect do |key, pairs|
74
+ if pairs.is_a?( Hash )
75
+ diff_pairs( pairs )
76
+ else
77
+ val1, val2 = *pairs
78
+ "%s: %s -> %s" % [
79
+ hl( key ).color( :dark, :white ),
80
+ hl( val1 ).color( :yellow ),
81
+ hl( val2 ).color( :bold, :yellow )
82
+ ]
83
+ end
84
+ end.join( hl(", ").color(:dark, :white) )
85
+ end
86
+
87
+ end # module Arborist::CLI::Watch
88
+
@@ -30,5 +30,10 @@ module Arborist::Event::NodeMatching
30
30
  end
31
31
 
32
32
 
33
+ ### Inject the node identifier into the generated hash.
34
+ def to_hash
35
+ return super.merge( identifier: self.node.identifier )
36
+ end
37
+
33
38
  end # module Arborist::Event::NodeMatching
34
39
 
@@ -0,0 +1,43 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ require 'pluggability'
5
+ require 'arborist' unless defined?( Arborist )
6
+
7
+
8
+ # Abstract base class for Arborist loader strategies
9
+ class Arborist::Loader
10
+ extend Loggability,
11
+ Pluggability
12
+
13
+
14
+ # Loggability API -- use the Arborist logger
15
+ log_to :arborist
16
+
17
+ # Pluggability API -- search for loaders under the specified path
18
+ plugin_prefixes 'arborist/loader'
19
+
20
+
21
+ ### Return an Enumerator that yields Arborist::Nodes loaded from the target
22
+ ### directory.
23
+ def nodes
24
+ raise NotImplementedError, "%p needs to implement #nodes"
25
+ end
26
+
27
+
28
+ ### Return an Enumerator that yields Arborist::Monitors loaded from the target
29
+ ### directory.
30
+ def monitors
31
+ raise NotImplementedError, "%p needs to implement #monitors"
32
+ end
33
+
34
+
35
+ ### Return an Enumerator that yields Arborist::Observers loaded from the target
36
+ ### directory.
37
+ def observers
38
+ raise NotImplementedError, "%p needs to implement #observers"
39
+ end
40
+
41
+
42
+ end # class Arborist::Loader
43
+
@@ -0,0 +1,72 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ require 'arborist/loader' unless defined?( Arborist::Loader )
5
+
6
+
7
+ # A loader for Arborist that knows how to load stuff from files on disk.
8
+ class Arborist::Loader::File < Arborist::Loader
9
+
10
+ ##
11
+ # The glob pattern to use for searching for files
12
+ FILE_PATTERN = '**/*.rb'
13
+
14
+
15
+ ### Create a new loader that will read nodes from the specified +directory+.
16
+ def initialize( directory )
17
+ Arborist.load_all
18
+ @directory = directory
19
+ end
20
+
21
+
22
+ # The directory to load files from
23
+ attr_reader :directory
24
+
25
+
26
+ ### Return an Enumerator
27
+ def paths
28
+ path = Pathname( self.directory )
29
+ if path.directory?
30
+ return Pathname.glob( directory + FILE_PATTERN ).each
31
+ else
32
+ return [ path ].each
33
+ end
34
+ end
35
+
36
+
37
+ ### Return an Enumerator that yields Arborist::Nodes loaded from the target
38
+ ### directory.
39
+ def nodes
40
+ return self.enumerator_for( Arborist::Node )
41
+ end
42
+
43
+
44
+ ### Return an Enumerator that yields Arborist::Monitors loaded from the target
45
+ ### directory.
46
+ def monitors
47
+ return self.enumerator_for( Arborist::Monitor )
48
+ end
49
+
50
+
51
+ ### Return an Enumerator that yields Arborist::Observers loaded from the target
52
+ ### directory.
53
+ def observers
54
+ return self.enumerator_for( Arborist::Observer )
55
+ end
56
+
57
+
58
+ ### Return an Enumerator that will instantiate and yield instances of the specified
59
+ ### +arborist_class+ for each file path in the loader's directory.
60
+ def enumerator_for( arborist_class )
61
+ return Enumerator.new do |yielder|
62
+ self.paths.each do |file|
63
+ objects = arborist_class.load( file )
64
+ objects.each do |object|
65
+ object.source = "file://%s" % [ file.expand_path ]
66
+ yielder.yield( object )
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ end # class Arborist::Loader::File
@@ -243,6 +243,7 @@ class Arborist::Manager
243
243
  end
244
244
  end
245
245
 
246
+
246
247
  ### Handle any queued signals.
247
248
  def process_signal_queue
248
249
  # Look for any signals that arrived and handle them
@@ -288,7 +289,7 @@ class Arborist::Manager
288
289
  end
289
290
 
290
291
 
291
- ### Handle a USR1 signal. Writes a message to the log by default.
292
+ ### Handle a USR1 signal. Writes a message to the log.
292
293
  def on_user1_signal( signo )
293
294
  self.log.info "Checkpoint: User signal."
294
295
  end
@@ -436,7 +437,7 @@ class Arborist::Manager
436
437
  ### an Enumerator if no block is given.
437
438
  def reachable_nodes( &block )
438
439
  iter = self.enumerator_for( self.root ) do |node|
439
- !node.down?
440
+ !(node.down? || node.disabled?)
440
441
  end
441
442
  return iter.each( &block ) if block
442
443
  return iter
@@ -491,7 +492,7 @@ class Arborist::Manager
491
492
  ### Propagate one or more +events+ to the specified +node+ and its ancestors in the tree,
492
493
  ### publishing them to matching subscriptions belonging to the nodes along the way.
493
494
  def propagate_events( node, *events )
494
- self.log.debug "Propagating %d events to node %s" % [ events.length, node.identifier ]
495
+ self.log.info "Propagating %d events to node %s" % [ events.length, node.identifier ]
495
496
  node.publish_events( *events )
496
497
 
497
498
  if node.parent
@@ -55,14 +55,14 @@ class Arborist::Monitor
55
55
  def exec_input( nodes, io )
56
56
  return if io.closed?
57
57
 
58
- nodes.each do |node|
59
- self.log.debug "Serializing node properties for %s" % [ node.identifier ]
60
- prop_map = node.properties.collect do |key, val|
58
+ nodes.each do |(identifier, data)|
59
+ self.log.debug "Serializing node properties for %s" % [ identifier ]
60
+ prop_map = data.collect do |key, val|
61
61
  "%s=%s" % [key, Shellwords.escape(val)]
62
62
  end
63
63
 
64
64
  self.log.debug " writing %d properties to %p" % [ prop_map.size, io ]
65
- io.puts "%s %s" % [ node.identifier, prop_map.join(' ') ]
65
+ io.puts "%s %s" % [ identifier, prop_map.join(' ') ]
66
66
  self.log.debug " wrote the node to FD %d" % [ io.fileno ]
67
67
  end
68
68
 
@@ -134,24 +134,9 @@ class Arborist::Monitor
134
134
  end
135
135
 
136
136
 
137
- ### Return an iterator for all the monitor files in the specified +directory+.
138
- def self::each_in( directory )
139
- path = Pathname( directory )
140
- paths = if path.directory?
141
- Pathname.glob( directory + MONITOR_FILE_PATTERN )
142
- else
143
- [ path ]
144
- end
145
-
146
- return paths.flat_map do |file|
147
- file_url = "file://%s" % [ file.expand_path ]
148
- monitors = self.load( file )
149
- self.log.debug "Loaded monitors %p..." % [ monitors ]
150
- monitors.each do |monitor|
151
- monitor.source = file_url
152
- end
153
- monitors
154
- end
137
+ ### Return an iterator for all the monitors supplied by the specified +loader+.
138
+ def self::each_in( loader )
139
+ return loader.monitors
155
140
  end
156
141
 
157
142
 
@@ -371,6 +356,7 @@ class Arborist::Monitor
371
356
  ### Set the module to use for the callbacks when interacting with the executed
372
357
  ### external command.
373
358
  def exec_callbacks( mod )
359
+ self.log.info "Setting exec callbacks handler to: %p" % [ mod.name ]
374
360
  self.exec_callbacks_mod = mod
375
361
  end
376
362