def test_simple_nodes_in_normal_node(self): class N(Node): class Hosts: role1 = {LocalHost1, LocalHost} role2 = LocalHost3 @map_roles('role1') class M(SimpleNode.Array): def func(self): return 'func-m' class X(SimpleNode): def func(self): return 'func-x' def func(self): return 'func-n' # `M` should behave as an array. env = Env(N()) self.assertEqual(env.func(), 'func-n') self.assertEqual(list(env.M.func()), ['func-m', 'func-m']) self.assertIsInstance(env.M.func(), ParallelActionResult) self.assertEqual(env.M[0].func(), 'func-m') self.assertEqual(list(env.M.X.func()), ['func-x', 'func-x']) self.assertIsInstance(env.M.X.func(), ParallelActionResult) self.assertEqual(env.M[0].X.func(), 'func-x') self.assertEqual(env.M.X[0].func(), 'func-x')
def test_get_childnodes_order(self): """ The inspector should return the childnodes in the order, as they were nested inside the node definition. """ # Automatic class Root(Node): class A(Node): pass class B(Node): pass self.assertEqual(repr(Inspector(Root()).get_childnodes()), '[<Node Root.A>, <Node Root.B>]') self.assertEqual(repr(Inspector(Env(Root())).get_childnodes()), '[Env(Root.A), Env(Root.B)]') # Manually set creation order class Root(Node): class A(Node): pass class B(Node): pass A._node_creation_counter = 44 B._node_creation_counter = 45 self.assertEqual(repr(Inspector(Root()).get_childnodes()), '[<Node Root.A>, <Node Root.B>]') self.assertEqual(repr(Inspector(Env(Root())).get_childnodes()), '[Env(Root.A), Env(Root.B)]') # Manually revert the creation order class Root(Node): class A(Node): pass class B(Node): pass A._node_creation_counter = 45 B._node_creation_counter = 44 self.assertEqual(repr(Inspector(Root()).get_childnodes()), '[<Node Root.B>, <Node Root.A>]') self.assertEqual(repr(Inspector(Env(Root())).get_childnodes()), '[Env(Root.B), Env(Root.A)]')
def __call__(self): from deployer.node import Node # Print info host_count = len(self.node.hosts) if host_count == 0: print 'No hosts found at this node. Nothing to execute.' return print 'Command will be executed on %i hosts:' % host_count for h in self.node.hosts: print ' - %s (%s)' % (h.slug, h.address) command = self.get_command() if not command: return # Run class RunNode(Node): class Hosts: host = self.node.hosts._all def run(self): self.hosts.run(command) env = Env(RunNode(), self.shell.pty, self.shell.logger_interface) # Run as any other action. (Nice exception handling, e.g. in case of NoInput on host selection.) try: env.run() except ActionException, e: pass
def test_simple_node(self): class N(SimpleNode): class Hosts: host = {LocalHost1, LocalHost2} def func(self): return 'result' # SimpleNode executes for each host separately. env = Env(N()) self.assertEqual(list(env.func()), ['result', 'result']) # Test return value of SimpleNode result = env.func() self.assertIsInstance(result, ParallelActionResult) # (The key is the Env, containing the isolation) self.assertIsInstance(result.keys()[0], Env) self.assertIsInstance(result.keys()[1], Env) self.assertIn(result.keys()[0]._node.Hosts.host, {LocalHost1, LocalHost2}) self.assertIn(result.keys()[1]._node.Hosts.host, {LocalHost1, LocalHost2}) self.assertEqual(result.values()[0], 'result') self.assertEqual(result.values()[1], 'result')
def test_simple_node_getitem(self): class N(SimpleNode): class Hosts: host = {LocalHost1, LocalHost2} def func(self): return 'result' n = N() self.assertIsInstance(n[0], SimpleNode) self.assertIsInstance(n[1], SimpleNode) self.assertIsInstance(n[LocalHost1], SimpleNode) self.assertIsInstance(n[LocalHost2], SimpleNode) self.assertEqual(n[0]._node_is_isolated, True) self.assertEqual(n[1]._node_is_isolated, True) self.assertEqual(n[LocalHost1]._node_is_isolated, True) self.assertEqual(n[LocalHost2]._node_is_isolated, True) self.assertRaises(KeyError, lambda: n[2]) self.assertRaises(KeyError, lambda: n[LocalHost3]) # Calling the isolated item should not return an array env = Env(N()) self.assertEqual(list(env.func()), ['result', 'result']) self.assertIsInstance(env.func(), ParallelActionResult) self.assertEqual(env[0].func(), 'result') self.assertEqual(env[1].func(), 'result') self.assertRaises(KeyError, lambda: env[2]) self.assertRaises(KeyError, lambda: env[LocalHost3])
def test_simple_node_getitem(self): class N(SimpleNode): class Hosts: host = LocalHost1, LocalHost2 def func(self): return 'result' n = N() self.assertIsInstance(n[0], SimpleNode) self.assertIsInstance(n[1], SimpleNode) self.assertIsInstance(n[LocalHost1], SimpleNode) self.assertIsInstance(n[LocalHost2], SimpleNode) self.assertEqual(n[0]._node_is_isolated, True) self.assertEqual(n[1]._node_is_isolated, True) self.assertEqual(n[LocalHost1]._node_is_isolated, True) self.assertEqual(n[LocalHost2]._node_is_isolated, True) self.assertRaises(KeyError, lambda: n[2]) self.assertRaises(KeyError, lambda: n[LocalHost3]) # Calling the isolated item should not return an array env = Env(N()) self.assertEqual(env.func(), ['result', 'result' ]) self.assertEqual(env[0].func(), 'result') self.assertEqual(env[1].func(), 'result') self.assertRaises(KeyError, lambda: env[2]) self.assertRaises(KeyError, lambda: env[LocalHost3])
def test_simple_nodes_in_normal_node(self): class N(Node): class Hosts: role1 = LocalHost1, LocalHost role2 = LocalHost3 @map_roles('role1') class M(SimpleNode.Array): def func(self): return 'func-m' class X(SimpleNode): def func(self): return 'func-x' def func(self): return 'func-n' # `M` should behave as an array. env = Env(N()) self.assertEqual(env.func(), 'func-n') self.assertEqual(env.M.func(), ['func-m', 'func-m' ]) self.assertEqual(env.M[0].func(), 'func-m') self.assertEqual(env.M.X.func(), ['func-x', 'func-x']) self.assertEqual(env.M[0].X.func(), 'func-x') self.assertEqual(env.M.X[0].func(), 'func-x')
def test_q_navigation(self): this = self class DummyException(Exception): pass class MyNode(Node): class Hosts: host = LocalHost # 1. Normal query from node. attr = 'value' query = Q.attr query2 = Q.attr + Q.attr def my_action(self): return self.query # 2. Exception in query from node. @property def this_raises(self): raise DummyException query3 = Q.this_raises + Q.attr def my_action2(self): # Calling query3 raises a QueryException # The inner exception that one is the DummyException try: return self.query3 except Exception as e: this.assertIsInstance(e, ActionException) this.assertIsInstance(e.inner_exception, QueryException) this.assertIsInstance(e.inner_exception.node, MyNode) this.assertIsInstance(e.inner_exception.query, Query) this.assertIsInstance(e.inner_exception.inner_exception, DummyException) # Raising the exception again at this point, will turn it # into an action exception. raise s = MyNode() env = Env(s) # 1. self.assertEqual(env.my_action(), 'value') self.assertEqual(env.query2, 'valuevalue') # 2. self.assertRaises(ActionException, env.my_action2) try: env.my_action2() except Exception as e: self.assertIsInstance(e, ActionException)
def test_simple_node(self): class N(SimpleNode): class Hosts: host = LocalHost1, LocalHost2 def func(self): return 'result' # SimpleNode executes for each host separately. env = Env(N()) self.assertEqual(env.func(), ['result', 'result' ])
def test_super_call(self): # Calling the superclass class A(Node): def action(self): return 'result' class B(A): def action(self): return 'The result was %s' % A.action(self) env = Env(B()) self.assertEqual(env.action(), 'The result was result')
def test_action_with_params(self): class MyNode(Node): class Hosts: host = LocalHost def my_action(self, param1, *a, **kw): return (param1, a, kw) s = MyNode() env = Env(s) result = env.my_action('param1', 1, 2, k=3, v=4) self.assertEqual(result, ('param1', (1, 2), { 'k': 3, 'v': 4 }) )
def test_action_with_params(self): class MyNode(Node): class Hosts: host = LocalHost def my_action(self, param1, *a, **kw): return (param1, a, kw) s = MyNode() env = Env(s) result = env.my_action('param1', 1, 2, k=3, v=4) self.assertEqual(result, ('param1', (1, 2), {'k': 3, 'v': 4}))
def test_wrapping_middle_node_in_env(self): class S(Node): class Hosts: role1 = LocalHost1, LocalHost2 class T(Node): def action(self): return len(self.hosts) s = S() env = Env(s.T) self.assertEqual(env.action(), 2)
def test_action_aliases(self): # We can define multiple aliases for an action. class A(Node): @alias('my_alias2') @alias('my_alias') def action(self): return 'result' env = Env(A()) self.assertEqual(env.action(), 'result') self.assertEqual(env.my_alias(), 'result') self.assertEqual(env.my_alias2(), 'result')
def test_wrapping_middle_node_in_env(self): class S(Node): class Hosts: role1 = {LocalHost1, LocalHost2} class T(Node): def action(self): return len(self.hosts) s = S() env = Env(s.T) self.assertEqual(env.action(), 2)
def test_auto_mapping_from_node_to_simplenode_array(self): # When no role_mapping is defined between Node and SimpleNode.Array, # we will map *all* roles to 'host' class A(Node): class Hosts: role1 = {LocalHost1, LocalHost2} role2 = {LocalHost3} role3 = {LocalHost4} class B(SimpleNode.Array): pass env = Env(A()) self.assertEqual(len(list(env.B)), 4) # When we have a Hosts class, and no explicit # mapping between A and B, this hosts will be used. class A(Node): class Hosts: role1 = {LocalHost1, LocalHost2} role2 = {LocalHost3, LocalHost4} class B(SimpleNode.Array): class Hosts: host = LocalHost1 env = Env(A()) self.assertEqual(len(list(env.B)), 1) # In case of JustOne, it should work as well. class A(Node): class Hosts: role1 = LocalHost1 class B(SimpleNode.JustOne): pass env = Env(A()) self.assertEqual(len(list(env.B)), 1) # Exception: Invalid initialisation of SimpleNode.JustOne. 2 hosts given to <Node A.B>. class A(Node): class Hosts: role1 = {LocalHost1, LocalHost2} class B(SimpleNode.JustOne): pass env = Env(A()) self.assertRaises(Exception, lambda: env.B)
def test_assignments_in_node(self): # It's not allowed to change attributes from a Node Environment. class MyNode(Node): def action(self): self.variable = 'value' # AttributeError wrapped in ActionException env = Env(MyNode()) self.assertRaises(ActionException, env.action) try: env.action() except ActionException as e: self.assertIsInstance(e.inner_exception, AttributeError)
def __call__(self): from deployer.contrib.nodes import connect class Connect(connect.Connect): class Hosts: host = self.node.hosts._all env = Env(Connect(), self.shell.pty, self.shell.logger_interface) # Run as any other action. (Nice exception handling, e.g. in case of NoInput on host selection.) try: env.with_host() except ActionException, e: pass
def test_action_alias(self): """ By using the @alias decorator around an action, the action should be available through multiple names. """ class Root(Node): @alias('b.c.d') @alias('b') def a(self): return 'result' env = Env(Root()) self.assertEqual(env.a(), 'result') self.assertEqual(env.b(), 'result') self.assertEqual(getattr(env, 'b.c.d')(), 'result')
def test_required_property(self): class A(Node): p = required_property() def action(self): self.p() env = Env(A()) # NotImplementedError wrapped in ActionException self.assertRaises(ActionException, env.action) try: env.action() except ActionException, e: self.assertIsInstance(e.inner_exception, NotImplementedError)
def test_assignments_in_node(self): # It's not allowed to change attributes from a Node Environment. class MyNode(Node): def action(self): self.variable = 'value' # AttributeError wrapped in ActionException env = Env(MyNode()) self.assertRaises(ActionException, env.action) try: env.action() except ActionException, e: self.assertIsInstance(e.inner_exception, AttributeError)
def test_property(self): class MyNode(Node): class Hosts: host = LocalHost @property def p(self): return 'value' def my_action(self): return self.p s = MyNode() env = Env(s) self.assertEqual(env.my_action(), 'value')
def test_required_property(self): class A(Node): p = required_property() def action(self): self.p() env = Env(A()) # NotImplementedError wrapped in ActionException self.assertRaises(ActionException, env.action) try: env.action() except ActionException as e: self.assertIsInstance(e.inner_exception, NotImplementedError)
def test_dont_isolate_yet(self): once = [0] for_each_host = [0] this = self class A(Node): class Hosts: my_role = {LocalHost1, LocalHost2} @map_roles('my_role') class B(SimpleNode.Array): def for_each_host(self): for_each_host[0] += 1 this.assertEqual(len(self.hosts), 1) @dont_isolate_yet def only_once(self): once[0] += 1 self.for_each_host() this.assertEqual(len(self.hosts), 2) return 'result' env = Env(A()) result = env.B.only_once() self.assertEqual(result, 'result') self.assertEqual(once, [1]) self.assertEqual(for_each_host, [2])
def __call__(self): from deployer.contrib.nodes import connect class Connect(connect.Connect): class Hosts: host = self.node.hosts.get_hosts() env = Env(Connect(), self.shell.pty, self.shell.logger_interface) # Run as any other action. (Nice exception handling, e.g. in case of NoInput on host selection.) try: env.with_host() except ActionException as e: pass except Exception as e: self.shell.logger_interface.log_exception(e)
def test_q_navigation(self): class MyNode(Node): class Hosts: host = LocalHost attr = 'value' query = Q.attr query2 = Q.attr + Q.attr def my_action(self): return self.query s = MyNode() env = Env(s) self.assertEqual(env.my_action(), 'value') self.assertEqual(env.query2, 'valuevalue')
def test_additional_roles_in_simple_node(self): # We should be able to pass additional roles to a SimpleNode, but # isolation happens at the 'host' role. this = self counter = [0] class A(Node): class Hosts: role1 = {LocalHost1, LocalHost2, LocalHost3} role2 = {LocalHost2, LocalHost4, LocalHost5} @map_roles('role1', extra='role2') class B(SimpleNode.Array): def action(self): this.assertEqual(len(self.hosts.filter('host')), 1) this.assertEqual(len(self.hosts.filter('extra')), 3) this.assertEqual(set(self.hosts.roles), set(['host', 'extra'])) self.C.action2() counter[0] += 1 class C(SimpleNode): def action2(self): this.assertEqual(len(self.hosts.filter('host')), 1) this.assertEqual(len(self.hosts.filter('extra')), 3) this.assertEqual(set(self.hosts.roles), set(['host', 'extra'])) counter[0] += 1 env = Env(A()) env.B.action() self.assertEqual(counter[0], 6)
def setUp(self): class Root(Node): attr = 'value' query = Q.attr + Q.attr def my_action(self): pass self.env = Env(Root()) self.env_insp = Inspector(self.env) self.node_insp = Inspector(Root())
def test_env_object(self): class S(Node): class Hosts: role1 = LocalHost role2 = LocalHost2 def my_action(self): return 'result' def return_name_of_self(self): return self.__class__.__name__ def echo_on_all(self): return self.hosts.run('/bin/echo echo', interactive=False) def echo_on_role1(self): return self.hosts.filter('role1').run('/bin/echo echo', interactive=False) def echo_on_role2(self): return self.hosts.filter('role2')[0].run('/bin/echo echo', interactive=False) s = S() env = Env(s) self.assertEqual(env.my_action(), 'result') self.assertEqual(env.return_name_of_self(), 'Env') self.assertEqual(env.echo_on_all(), ['echo\r\n', 'echo\r\n']) self.assertEqual(env.echo_on_role1(), ['echo\r\n']) self.assertEqual(env.echo_on_role2(), 'echo\r\n') # Isinstance hooks self.assertIsInstance(s, S) self.assertIsInstance(env, S)
def run_action(self, action_name, *a, **kw): """ Run a deployment command at the current shell state. """ env = Env(self.state._node, self.pty, self.logger_interface, is_sandbox=False) return getattr(env, action_name)(*a, **kw)
def test_hostcontainer_from_node(self): this = self class A(Node): class Hosts: role1 = LocalHost1 def action(self): with self.hosts.cd('/tmp'): this.assertEqual(self.hosts[0].run('pwd').strip(), '/tmp') with self.hosts.cd('/'): this.assertEqual(self.hosts[0].run('pwd').strip(), '/') this.assertEqual(self.hosts[0].run('pwd').strip(), '/tmp') env = Env(A()) env.action()
def test_bin_false(self): class S(Node): class Hosts: role1 = LocalHost def return_false(self): return self.hosts.run('/bin/false', interactive=False) s = S() env = Env(s) # This should raise an ExecCommandFailed, wrapped in an ActionException self.assertRaises(ActionException, env.return_false) try: env.return_false() except ActionException as e: self.assertIsInstance(e.inner_exception, ExecCommandFailed)
def test_bin_false(self): class S(Node): class Hosts: role1 = LocalHost def return_false(self): return self.hosts.run('/bin/false', interactive=False) s = S() env = Env(s) # This should raise an ExecCommandFailed, wrapped in an ActionException self.assertRaises(ActionException, env.return_false) try: env.return_false() except ActionException, e: self.assertIsInstance(e.inner_exception, ExecCommandFailed)
def test_nested_action(self): class MyNode(Node): class Node2(Node): class Node3(Node): def my_action(self): return 'result' env = Env(MyNode()) result = env.Node2.Node3.my_action() self.assertEqual(result, 'result')
def test_initialize_node(self): class A(Node): class Hosts: role1 = LocalHost2 role2 = LocalHost4, LocalHost5 def action(self): # Define a new Node-tree class B(Node): class Hosts: role2 = self.hosts.filter('role2') def action2(self): return len(self.hosts) # Initialize it in the current Env, and call an action of that one. return self.initialize_node(B).action2() env = Env(A()) self.assertEqual(env.action(), 2)
def test_initialize_node(self): class A(Node): class Hosts: role1 = {LocalHost2} role2 = {LocalHost4, LocalHost5} def action(self): # Define a new Node-tree class B(Node): class Hosts: role2 = self.hosts.filter('role2') def action2(self): return len(self.hosts) # Initialize it in the current Env, and call an action of that one. return self.initialize_node(B).action2() env = Env(A()) self.assertEqual(env.action(), 2)
def test_hostcontainer_from_node(self): this = self class A(Node): class Hosts: role1 = LocalHost1 def action(self): with self.hosts.cd('/tmp'): this.assertEqual(self.hosts.getcwd(), ['/tmp']) this.assertEqual(self.hosts[0].getcwd(), '/tmp') this.assertEqual(self.hosts[0].run('pwd').strip(), '/tmp') with self.hosts.cd('/'): this.assertEqual(self.hosts[0].run('pwd').strip(), '/') this.assertEqual(self.hosts[0].run('pwd').strip(), '/tmp') env = Env(A()) env.action()
def test_node_console(self): class Root(Node): def a(self): return 'result' # Test Console instance. p = Pty() env = Env(Root(), pty=p) self.assertIsInstance(env.console, Console) self.assertEqual(env.console.pty, p) self.assertEqual(env.console.is_interactive, p.interactive) #
def _get_env(self): """ Created a sandboxed environment for evaluation of attributes. (attributes shouldn't have side effects on servers, so sandboxing is fine.) Returns an ``Env`` object. """ env = Env(self.node, self.shell.pty, self.shell.logger_interface, is_sandbox=True) return Console(self.shell.pty).select_node_isolation(env)
def __call__(self): from deployer.node import Node # Print info host_count = len(self.node.hosts) if host_count == 0: print 'No hosts found at this node. Nothing to execute.' return print 'Command will be executed on %i hosts:' % host_count for h in self.node.hosts: print ' - %s (%s)' % (h.slug, h.address) command = self.get_command() if not command: return # Run use_sudo = self.use_sudo class RunNode(Node): class Hosts: host = self.node.hosts.get_hosts() def run(self): if use_sudo: self.hosts.sudo(command) else: self.hosts.run(command) env = Env(RunNode(), self.shell.pty, self.shell.logger_interface) # Run as any other action. (Nice exception handling, e.g. in case of # NoInput on host selection.) try: env.run() except ActionException as e: pass except Exception as e: self.shell.logger_interface.log_exception(e)
def test_nested_simple_nodes(self): class N(SimpleNode): class Hosts: host = {LocalHost1, LocalHost2} class M(SimpleNode): def func(self): return 'result' # `M` gets both hosts as well. env = Env(N()) self.assertEqual(list(env.M.func()), ['result', 'result'])
def setUp(self): class Root(Node): def a(self): pass @suppress_action_result def b(self): pass self.env = Env(Root()) self.env_insp = Inspector(self.env) self.node_insp = Inspector(Root())
def test_env_object(self): class S(Node): class Hosts: role1 = LocalHost role2 = LocalHost2 def my_action(self): return 'result' def return_name_of_self(self): return self.__class__.__name__ def echo_on_all(self): return self.hosts.run('/bin/echo echo', interactive=False) def echo_on_role1(self): return self.hosts.filter('role1').run('/bin/echo echo', interactive=False) def echo_on_role2(self): return self.hosts.get('role2').run('/bin/echo echo', interactive=False) s = S() env = Env(s) self.assertEqual(env.my_action(), 'result') self.assertEqual(env.return_name_of_self(), 'Env') self.assertEqual(env.echo_on_all(), [ 'echo\r\n', 'echo\r\n' ]) self.assertEqual(env.echo_on_role1(), [ 'echo\r\n' ]) self.assertEqual(env.echo_on_role2(), 'echo\r\n') # Isinstance hooks self.assertIsInstance(s, S) self.assertIsInstance(env, S)
def test_default_action(self): class A(Node): class Hosts: role = {LocalHost1, LocalHost2} def __call__(self): return 'A.call' class B(Node): def __call__(self): return 'B.call' @map_roles('role') class C(SimpleNode.Array): def __call__(self): return 'C.call' env = Env(A()) self.assertEqual(env(), 'A.call') self.assertEqual(env.B(), 'B.call') self.assertEqual(list(env.C()), ['C.call', 'C.call']) self.assertEqual(env.C[0](), 'C.call')
def test_calling_between_simple_and_normal_nodes(self): class N(Node): class Hosts: role1 = LocalHost1, LocalHost role2 = LocalHost3 def do_tests(this): self.assertEqual(this.M.func(), ['func-m', 'func-m']) self.assertEqual(this.M.X.func(), ['func-x', 'func-x']) self.assertEqual(this.M[0].func(), 'func-m') self.assertEqual(this.M.X[0].func(), 'func-x') def func(this): return 'func-n' @map_roles(host='role1') class M(SimpleNode.Array): def func(this): return 'func-m' class X(SimpleNode): def func(this): return 'func-x' def do_tests(this): self.assertIn(repr(this.parent), [ 'Env(N.M[0])', 'Env(N.M[1])']) self.assertEqual(repr(this.parent.parent), 'Env(N)') self.assertEqual(this.func(), 'func-x') self.assertEqual(this.parent.parent.func(), 'func-n') self.assertEqual(this.parent.parent.M.func(), ['func-m', 'func-m']) self.assertEqual(this.parent.parent.M[0].func(), 'func-m') env = Env(N()) env.do_tests() env.M.X.do_tests()
def test_simple_node(self): class N(SimpleNode): class Hosts: host = { LocalHost1, LocalHost2 } def func(self): return 'result' # SimpleNode executes for each host separately. env = Env(N()) self.assertEqual(list(env.func()), ['result', 'result' ]) # Test return value of SimpleNode result = env.func() self.assertIsInstance(result, ParallelActionResult) # (The key is the Env, containing the isolation) self.assertIsInstance(result.keys()[0], Env) self.assertIsInstance(result.keys()[1], Env) self.assertIn(result.keys()[0]._node.Hosts.host, { LocalHost1, LocalHost2 }) self.assertIn(result.keys()[1]._node.Hosts.host, { LocalHost1, LocalHost2 }) self.assertEqual(result.values()[0], 'result') self.assertEqual(result.values()[1], 'result')