spec/arborist/client_spec.rb in arborist-0.2.0.pre20170519125456 vs spec/arborist/client_spec.rb in arborist-0.2.0
- old
+ new
@@ -7,10 +7,17 @@
describe Arborist::Client do
let( :client ) { described_class.new }
+ it "can return a singleton instance" do
+ one = described_class.instance
+ two = described_class.instance
+ expect( one ).to be_a( described_class )
+ expect( one ).to equal( two )
+ end
+
describe "synchronous API", :testing_manager do
before( :each ) do
@manager_thread = Thread.new do
@manager = make_testing_manager()
@@ -45,30 +52,56 @@
let( :manager ) { @manager }
- describe "high-level methods" do
+ describe "convenience API" do
- it "provides a convenience method for acknowledging" do
- manager.nodes['sidonie'].update( error: "Clown apocalypse" )
+ it "can fetch a single node" do
+ res = client.fetch_node( 'duir' )
+ expect( res ).to be_a( Hash )
+ expect( res['identifier'] ).to eq( 'duir' )
+ end
- res = client.acknowledge( :sidonie, "I'm on it.", "ged" )
- expect( manager.nodes['sidonie'] ).to be_acked
+ it "has a convenience method for fetching dependencies" do
+ res = client.dependencies_of( 'sidonie' )
+ expect( res ).to be_a( Hash ).and include( 'sidonie-postgresql', 'sidonie-ssh' )
end
- it "provides a convenience method for clearing acknowledgments" do
- manager.nodes['sidonie'].update( error: "Clown apocalypse" )
+ it "can pivot dependencies on node attributes" do
+ res = client.dependencies_of( 'sidonie', partition: 'type' )
+ expect( res ).to be_a( Hash )
+ expect( res['service'] ).to include(
+ a_hash_including( { 'identifier' => 'sidonie-ssh' } ),
+ a_hash_including( { 'identifier' => 'sidonie-postgresql' } )
+ )
+ end
- res = client.acknowledge( :sidonie, "I'm on it.", "ged" )
- res = client.clear_acknowledgement( :sidonie )
- expect( manager.nodes['sidonie'] ).to_not be_acked
+ it "can fetch a subset of node dependency attributes" do
+ res = client.dependencies_of( 'sidonie', properties: %w[ description type ] )
+ expect( res ).to be_a( Hash ).and include(
+ 'sandbox01',
+ 'sandbox01-canary',
+ 'sidonie-couchpotato',
+ 'sidonie-demon-http',
+ 'sidonie-http',
+ 'sidonie-iscsi',
+ 'sidonie-pms',
+ 'sidonie-postgresql',
+ 'sidonie-sabnzbd',
+ 'sidonie-sickbeard',
+ 'sidonie-smtp',
+ 'sidonie-ssh',
+ 'vhost01',
+ 'yevaud-cozy_frontend'
+ )
+ expect( res.values ).to all( have_attributes(length: a_value_between(1, 2)) )
+ expect( res.values.map(&:keys) ).to all( contain_exactly 'type', 'description' )
end
-
end
describe "protocol-level API" do
@@ -76,92 +109,105 @@
res = client.status
expect( res ).to include( 'server_version', 'state', 'uptime', 'nodecount' )
end
- it "can list the nodes of the manager it's connected to" do
- res = client.list
+ it "can fetch the nodes of the manager it's connected to" do
+ res = client.fetch
expect( res ).to be_an( Array )
expect( res.length ).to eq( manager.nodes.length )
end
- it "can list a subtree of the nodes of the manager it's connected to" do
- res = client.list( from: 'duir' )
+ it "can fetch a subtree of the nodes of the manager it's connected to" do
+ res = client.fetch( from: 'duir' )
expect( res ).to be_an( Array )
expect( res.length ).to be < manager.nodes.length
end
- it "can list a depth-limited subtree of the node of the managed it's connected to" do
- res = client.list( depth: 2 )
+ it "can fetch a depth-limited subtree of the node of the managed it's connected to" do
+ res = client.fetch( depth: 2 )
expect( res ).to be_an( Array )
- expect( res.length ).to eq( 8 )
+ expect( res.length ).to eq( 9 )
end
- it "can list a depth-limited subtree of the nodes of the manager it's connected to" do
- res = client.list( from: 'duir', depth: 1 )
+ it "can fetch a depth-limited subtree of the nodes of the manager it's connected to" do
+ res = client.fetch( from: 'duir', depth: 1 )
expect( res ).to be_an( Array )
- expect( res.length ).to eq( 5 )
+ expect( res.length ).to eq( 6 )
end
- it "can fetch all node properties for all 'up' nodes" do
- res = client.fetch
+ it "can get a Hash of all nodes keyed by identifier" do
+ res = client.search
expect( res ).to be_a( Hash )
expect( res.length ).to be == manager.nodes.length
expect( res.values ).to all( be_a(Hash) )
end
- it "can fetch identifiers for all 'up' nodes" do
- res = client.fetch( {}, properties: nil )
+ it "includes downed nodes by default in the results of a search" do
+ manager.nodes['sidonie'].update( error: 'something happened' )
+ res = client.search
expect( res ).to be_a( Hash )
expect( res.length ).to be == manager.nodes.length
+ expect( res.values ).to all( be_a(Hash) )
+ end
+
+
+ it "can get a Hash of all nodes without user properties" do
+ res = client.search( options: { properties: nil } )
+ expect( res ).to be_a( Hash )
+ expect( res.length ).to be == manager.nodes.length
expect( res.values ).to all( be_empty )
end
- it "can fetch a subset of properties for all 'up' nodes" do
- res = client.fetch( {}, properties: [:addresses, :status] )
+ it "can get a Hash of all nodes with a subset of properties" do
+ res = client.search( options: { properties: [:addresses, :status] })
expect( res ).to be_a( Hash )
expect( res.length ).to be == manager.nodes.length
expect( res.values ).to all( be_a(Hash) )
expect( res.values.map(&:length) ).to all( be <= 2 )
end
- it "can fetch a subset of properties for all 'up' nodes matching specified criteria" do
- res = client.fetch( {type: 'host'}, properties: [:addresses, :status] )
+ it "can get a Hash of all nodes with a subset of properties that match specified criteria" do
+ res = client.search( criteria: {type: 'host'}, options: {properties: [:addresses, :status]} )
expect( res ).to be_a( Hash )
expect( res.length ).to be == manager.nodes.values.count {|n| n.type == 'host' }
expect( res.values ).to all( include('addresses', 'status') )
end
- it "can fetch all node properties for 'up' nodes that don't match specified criteria" do
- res = client.fetch( {}, properties: [:addresses, :status], exclude: {tag: 'testing'} )
+ it "can get a Hash of all nodes with a subset of properties that don't match specified criteria" do
+ res = client.search(
+ options: {
+ properties: [:addresses, :status],
+ exclude: {tag: 'testing'}
+ }
+ )
testing_nodes = manager.nodes.values.select {|n| n.tags.include?('testing') }
expect( res ).to be_a( Hash )
expect( res ).to_not be_empty()
expect( res.length ).to eq( manager.nodes.length - testing_nodes.length )
expect( res.values ).to all( be_a(Hash) )
end
- it "can fetch all properties for all nodes regardless of their status" do
+ it "can get a Hash of nodes that exclude nodes that are down" do
# Down a node
manager.nodes['duir'].update( error: 'something happened' )
- res = client.fetch( {type: 'host'}, include_down: true )
+ res = client.search( criteria: {type: 'host'}, options: {exclude_down: true} )
expect( res ).to be_a( Hash )
- expect( res ).to include( 'duir' )
- expect( res['duir']['status'] ).to eq( 'down' )
+ expect( res ).to_not include( 'duir' )
end
it "can update the properties of managed nodes", :no_ci do
res = client.update( duir: { ping: {rtt: 24} } )
@@ -171,10 +217,21 @@
expect( manager.nodes['duir'].properties['ping'] ).to include( 'rtt' )
expect( manager.nodes['duir'].properties['ping']['rtt'] ).to eq( 24 )
end
+ it "can fetch a list of all nodes which have a dependency on a target node" do
+ res = client.deps( identifier: 'sidonie-postgresql' )
+
+ expected_ids = manager.nodes['sidonie-postgresql'].node_subscribers.to_a
+
+ expect( res ).to be_a( Hash ).and( include('deps') )
+ expect( res['deps'] ).to be_an( Array )
+ expect( res['deps'] ).to contain_exactly( *expected_ids )
+ end
+
+
it "can subscribe to all events" do
sub_id = client.subscribe
expect( sub_id ).to be_a( String )
expect( sub_id ).to match( /^[\w\-]{16,}/ )
@@ -259,39 +316,42 @@
expect( res ).to be_nil
end
it "can prune nodes from the tree" do
- res = client.prune( 'sidonie-ssh' )
+ res = client.prune( identifier: 'sidonie-ssh' )
expect( res ).to be_a( Hash )
expect( res ).to include( 'identifier' => 'sidonie-ssh' )
expect( manager.nodes ).to_not include( 'sidonie-ssh' )
end
it "returns nil without error when pruning a node that doesn't exist" do
- res = client.prune( 'carrigor' )
+ res = client.prune( identifier: 'carrigor' )
expect( res ).to be_nil
end
it "can graft new nodes onto the tree" do
- res = client.graft( 'breakfast-burrito', type: 'host' )
+ res = client.graft( identifier: 'breakfast-burrito', type: 'host' )
expect( res ).to eq({ 'identifier' => 'breakfast-burrito' })
expect( manager.nodes ).to include( 'breakfast-burrito' )
expect( manager.nodes['breakfast-burrito'] ).to be_a( Arborist::Node::Host )
expect( manager.nodes['breakfast-burrito'].parent ).to eq( '_' )
end
it "can graft nodes with attributes onto the tree" do
- res = client.graft( 'breakfast-burrito',
+ res = client.graft(
+ identifier: 'breakfast-burrito',
type: 'service',
parent: 'duir',
- port: 9999,
- tags: ['yusss']
+ attributes: {
+ port: 9999,
+ tags: ['yusss']
+ }
)
expect( res ).to eq({ 'identifier' => 'duir-breakfast-burrito' })
expect( manager.nodes ).to include( 'duir-breakfast-burrito' )
expect( manager.nodes['duir-breakfast-burrito'] ).to be_a( Arborist::Node::Service )
expect( manager.nodes['duir-breakfast-burrito'].parent ).to eq( 'duir' )
@@ -299,15 +359,40 @@
expect( manager.nodes['duir-breakfast-burrito'].tags ).to include( 'yusss' )
end
it "can modify operational attributes of a node" do
- res = client.modify( "duir", tags: 'girlrobot' )
+ res = client.modify( identifier: "duir", attributes: { tags: 'girlrobot' })
expect( res ).to be_truthy
expect( manager.nodes['duir'].tags ).to eq( ['girlrobot'] )
end
+
+ it "can acknowledge a node" do
+ manager.nodes['sidonie'].update( error: "Clown apocalypse" )
+
+ res = client.acknowledge( identifier: 'sidonie', message: "I'm on it.", sender: "ged" )
+
+ expect( manager.nodes['sidonie'] ).to be_acked
+ end
+
+
+ it "can clear a node's acknowledgment" do
+ manager.nodes['sidonie'].update( error: "Clown apocalypse" )
+
+ res = client.acknowledge( identifier: 'sidonie', message: "I'm on it.", sender: "ged" )
+ res = client.clear_acknowledgement( identifier: 'sidonie' )
+
+ expect( manager.nodes['sidonie'] ).to_not be_acked
+ end
+
+
+ it "acking raises an appropriate error when it's missing arguments" do
+ expect {
+ client.acknowledge( identifier: 'sidonie', message: "I'm on it." )
+ }.to raise_error( ArgumentError, /missing keyword: sender/ )
+ end
end
end
@@ -324,49 +409,49 @@
expect( header['version'] ).to eq( Arborist::Client::API_VERSION )
expect( header['action'] ).to eq( 'status' )
end
- it "can make a raw list request" do
- req = client.make_list_request
+ it "can make a raw fetch request" do
+ req = client.make_fetch_request
expect( req ).to be_a( CZTop::Message )
header, body = Arborist::TreeAPI.decode( req )
expect( header ).to be_a( Hash )
expect( header ).to include( 'version', 'action' )
expect( header ).to_not include( 'from' )
expect( header['version'] ).to eq( Arborist::Client::API_VERSION )
- expect( header['action'] ).to eq( 'list' )
+ expect( header['action'] ).to eq( 'fetch' )
end
- it "can make a raw fetch request" do
- req = client.make_fetch_request( {} )
+ it "can make a raw search request" do
+ req = client.make_search_request( {} )
expect( req ).to be_a( CZTop::Message )
header, body = Arborist::TreeAPI.decode( req )
expect( header ).to be_a( Hash )
expect( header ).to include( 'version', 'action' )
expect( header['version'] ).to eq( Arborist::Client::API_VERSION )
- expect( header['action'] ).to eq( 'fetch' )
+ expect( header['action'] ).to eq( 'search' )
expect( body ).to eq([ {}, {} ])
end
- it "can make a raw fetch request with criteria" do
- req = client.make_fetch_request( {type: 'host'} )
+ it "can make a raw search request with criteria" do
+ req = client.make_search_request( {type: 'host'} )
expect( req ).to be_a( CZTop::Message )
header, body = Arborist::TreeAPI.decode( req )
expect( header ).to be_a( Hash )
expect( header ).to include( 'version', 'action' )
expect( header['version'] ).to eq( Arborist::Client::API_VERSION )
- expect( header['action'] ).to eq( 'fetch' )
+ expect( header['action'] ).to eq( 'search' )
body = body
expect( body.first ).to be_a( Hash )
expect( body.first ).to include( 'type' )
expect( body.first['type'] ).to eq( 'host' )
@@ -381,9 +466,30 @@
expect( header ).to be_a( Hash )
expect( header ).to include( 'version', 'action' )
expect( header['version'] ).to eq( Arborist::Client::API_VERSION )
expect( header['action'] ).to eq( 'update' )
+
+ expect( body ).to be_a( Hash )
+ expect( body ).to include( 'duir' )
+ expect( body['duir'] ).to eq( 'error' => 'Something happened.' )
+ end
+
+
+ it "can make a raw update request with headers" do
+ req = client.make_update_request(
+ {duir: {error: "Something happened."}},
+ {monitor_key: 'foom'}
+ )
+ expect( req ).to be_a( CZTop::Message )
+
+ header, body = Arborist::TreeAPI.decode( req )
+
+ expect( header ).to be_a( Hash )
+ expect( header ).to include( 'version', 'action', 'monitor_key' )
+ expect( header['version'] ).to eq( Arborist::Client::API_VERSION )
+ expect( header['action'] ).to eq( 'update' )
+ expect( header['monitor_key'] ).to eq( 'foom' )
expect( body ).to be_a( Hash )
expect( body ).to include( 'duir' )
expect( body['duir'] ).to eq( 'error' => 'Something happened.' )
end