arborist 0.0.1.pre20160106113421 → 0.0.1.pre20160128152542

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.
@@ -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