class TestPlumberyText(unittest.TestCase): def setUp(self): self.text = PlumberyText() def tearDown(self): pass def test_dictionary(self): template = 'little {{ test }} with multiple {{test}} and {{}} as well' context = PlumberyContext(dictionary={'test': 'toast'}) expected = 'little toast with multiple toast and {{}} as well' self.assertEqual( self.text.expand_string(template, context), expected) def test_engine(self): engine = PlumberyEngine() context = PlumberyContext(context=engine) template = "we are running plumbery {{ plumbery.version }}" expected = "we are running plumbery "+__version__ self.assertEqual( self.text.expand_string(template, context), expected) engine.set_user_name('fake_name') engine.set_user_password('fake_password') template = "{{ name.credentials }} {{ password.credentials }}" expected = engine.get_user_name()+" "+engine.get_user_password() self.assertEqual( self.text.expand_string(template, context), expected) def test_bad_parameters(self): with self.assertRaises(TypeError): self.text.expand_parameters(1234, {}) with self.assertRaises(TypeError): self.text.expand_parameters({}, {}) template = 'little {{ test with multiple {{test and {{ as well' context = PlumberyContext(dictionary={'test': 'toast'}) self.assertEqual( self.text.expand_parameters(template, context), template) template = 'little {{ }} test and {{ }} as well' context = PlumberyContext(dictionary={'test': 'toast'}) self.assertEqual( self.text.expand_parameters(template, context), template) template = "{{ parameter.is.unknown }}" context = PlumberyContext(dictionary={'test': 'toast'}) with self.assertRaises(KeyError): self.text.expand_parameters(template, context) def test_bad_string(self): self.text.expand_string(1234, {}) self.text.expand_string({}, {}) template = 'little {{ test with multiple {{test and {{ as well' context = PlumberyContext(dictionary={'test': 'toast'}) self.assertEqual( self.text.expand_string(template, context), template) template = 'little {{ }} test and {{ }} as well' context = PlumberyContext(dictionary={'test': 'toast'}) self.assertEqual( self.text.expand_string(template, context), template) template = "{{ secret.is.unknown }}" context = PlumberyContext(dictionary={'test': 'toast'}) self.assertEqual( self.text.expand_string(template, context), template) def test_could_expand(self): template = 'little {{ test with multiple {{test and {{ as well' self.assertTrue(self.text.could_expand(template)) # template = b'\x00\xFF\x00\xFF' # self.assertFalse(self.text.could_expand(template)) def test_input1(self): context = PlumberyContext(dictionary={'node.private': '12.34.56.78'}) self.assertEqual( self.text.expand_string(input1, context), expected1) def test_input2(self): context = PlumberyContext(dictionary={}) transformed = yaml.load(self.text.expand_string(input2, context)) unmatched = {o: (input2[o], transformed[o]) for o in input2.keys() if input2[o] != transformed[o]} if unmatched != {}: print(unmatched) self.assertEqual(len(unmatched), 0) context = PlumberyContext(dictionary={'node.private': '12.34.56.78'}) transformed = yaml.load(self.text.expand_string(input2, context)) unmatched = {o: (expected2[o], transformed[o]) for o in expected2.keys() if expected2[o] != transformed[o]} if unmatched != {}: print(unmatched) self.assertEqual(len(unmatched), 0) def test_input3(self): loaded = yaml.load(input3) context = PlumberyContext(dictionary={'node.public': '12.34.56.78'}) transformed = yaml.load(self.text.expand_string(loaded, context)) self.assertEqual(transformed, yaml.load(expected3)) def test_input4(self): loaded = yaml.load(input4) context = PlumberyContext(dictionary={}) transformed = yaml.load(self.text.expand_string(loaded, context)) self.assertEqual(transformed, loaded) def test_input5(self): loaded = yaml.load(input5) context = PlumberyContext(dictionary={}) transformed = yaml.load(self.text.expand_string(loaded, context)) self.assertEqual(transformed, loaded) def test_input6(self): loaded = yaml.load(input6) context = PlumberyContext(dict6) transformed = self.text.expand_string(loaded, context) self.assertEqual(transformed.strip(), expected6.strip()) def test_input7(self): loaded = yaml.load(input7) context = PlumberyContext(dict7) transformed = self.text.expand_string(loaded, context) self.assertEqual(transformed.strip(), expected7.strip()) def test_input8(self): loaded = yaml.load(input8) context = PlumberyContext(dictionary={}) expanded = self.text.expand_string(loaded, context) self.assertEqual(expanded.strip(), input8.strip()) def test_input9(self): loaded = yaml.load(input9) context = PlumberyContext(context=PlumberyEngine()) expanded = self.text.expand_string(loaded, context) self.assertEqual((' - |' in expanded), False) def test_input10(self): loaded = yaml.load(input10) context = PlumberyContext(dictionary={}) expanded = self.text.expand_string(loaded, context) self.assertEqual(expanded.strip(), input10.strip()) def test_node1(self): template = "{{ mongo_mongos01.public }}" context = PlumberyNodeContext(node=FakeNode1()) expected = '168.128.12.163' self.assertEqual( self.text.expand_string(template, context), expected) template = "{{mongo_mongos01.private }}" expected = '192.168.50.11' self.assertEqual( self.text.expand_string(template, context), expected) template = "{{ mongo_mongos01}}" expected = '192.168.50.11' self.assertEqual( self.text.expand_string(template, context), expected) template = "{{ mongo_mongos01.ipv6 }}" expected = '2a00:47c0:111:1136:47c9:5a6a:911d:6c7f' self.assertEqual( self.text.expand_string(template, context), expected) def test_node2(self): template = "{{ mongo_mongos02.public }}" context = PlumberyNodeContext(node=FakeNode1(), container=FakeContainer()) expected = '168.128.12.164' self.assertEqual( self.text.expand_string(template, context), expected) template = "{{ mongo_mongos02.private }}" expected = '192.168.50.12' self.assertEqual( self.text.expand_string(template, context), expected) template = "{{ mongo_mongos02 }}" expected = '192.168.50.12' self.assertEqual( self.text.expand_string(template, context), expected) template = "{{ mongo_mongos02.ipv6 }}" expected = '2a00:47c0:111:1136:47c9:5a6a:911d:6c7f' self.assertEqual( self.text.expand_string(template, context), expected)
def _get_prepares(self, node, settings, container): """ Defines the set of actions to be done on a node :param node: the node to be polished :type node: :class:`libcloud.compute.base.Node` :param settings: the fittings plan for this node :type settings: ``dict`` :param container: the container of this node :type container: :class:`plumbery.PlumberyInfrastructure` :return: a list of actions to be performed, and related descriptions :rtype: a ``list`` of `{ 'description': ..., 'genius': ... }`` """ if not isinstance(settings, dict): return [] environment = PlumberyNodeContext(node=node, container=container, context=self.facility) prepares = [] for key_file in self.key_files: try: path = os.path.expanduser(key_file) with open(path) as stream: key = stream.read() stream.close() prepares.append({ 'description': 'deploy SSH public key', 'genius': SSHKeyDeployment(key=key) }) except IOError: plogging.warning("no ssh key in {}".format(key_file)) if ('prepare' in settings and isinstance(settings['prepare'], list) and len(settings['prepare']) > 0): plogging.info('- using prepare commands') for script in settings['prepare']: tokens = script.split(' ') if len(tokens) == 1: tokens.insert(0, 'run') if tokens[0] in ['run', 'run_raw']: # send and run a script script = tokens[1] if len(tokens) > 2: args = tokens[2:] else: args = [] plogging.debug("- {} {} {}".format(tokens[0], script, ' '.join(args))) try: with open(script) as stream: text = stream.read() if (tokens[0] == 'run' and PlumberyText.could_expand(text)): plogging.debug( "- expanding script '{}'".format(script)) text = PlumberyText.expand_string( text, environment) if len(text) > 0: plogging.info("- running '{}'".format(script)) prepares.append({ 'description': ' '.join(tokens), 'genius': ScriptDeployment(script=text, args=args, name=script) }) else: plogging.error( "- script '{}' is empty".format(script)) except IOError: plogging.error( "- unable to read script '{}'".format(script)) elif tokens[0] in ['put', 'put_raw']: # send a file file = tokens[1] if len(tokens) > 2: destination = tokens[2] else: destination = './' + file plogging.debug("- {} {} {}".format(tokens[0], file, destination)) try: with open(file) as stream: content = stream.read() if (tokens[0] == 'put' and PlumberyText.could_expand(content)): plogging.debug( "- expanding file '{}'".format(file)) content = PlumberyText.expand_string( content, environment) plogging.info("- putting file '{}'".format(file)) prepares.append({ 'description': ' '.join(tokens), 'genius': FileContentDeployment(content=content, target=destination) }) except IOError: plogging.error( "- unable to read file '{}'".format(file)) else: # echo a sensible message eventually if tokens[0] == 'echo': tokens.pop(0) message = ' '.join(tokens) message = PlumberyText.expand_string(message, environment) plogging.info("- {}".format(message)) if ('cloud-config' in settings and isinstance(settings['cloud-config'], dict) and len(settings['cloud-config']) > 0): plogging.info('- using cloud-config') # mandatory, else cloud-init will not consider user-data plogging.debug('- preparing meta-data') meta_data = 'instance_id: dummy\n' destination = '/var/lib/cloud/seed/nocloud-net/meta-data' prepares.append({ 'description': 'put meta-data', 'genius': FileContentDeployment(content=meta_data, target=destination) }) plogging.debug('- preparing user-data') expanded = PlumberyText.expand_string(settings['cloud-config'], environment) user_data = '#cloud-config\n' + expanded plogging.debug(user_data) destination = '/var/lib/cloud/seed/nocloud-net/user-data' prepares.append({ 'description': 'put user-data', 'genius': FileContentDeployment(content=user_data, target=destination) }) plogging.debug('- preparing remote install of cloud-init') script = 'prepare.cloud-init.sh' try: path = os.path.dirname(__file__) + '/' + script with open(path) as stream: text = stream.read() if text: prepares.append({ 'description': 'run ' + script, 'genius': ScriptDeployment(script=text, name=script) }) except IOError: raise PlumberyException( "Error: cannot read '{}'".format(script)) plogging.debug('- preparing reboot to trigger cloud-init') prepares.append({ 'description': 'reboot node', 'genius': RebootDeployment(container=container) }) return prepares
def _get_prepares(self, node, settings, container): """ Defines the set of actions to be done on a node :param node: the node to be polished :type node: :class:`libcloud.compute.base.Node` :param settings: the fittings plan for this node :type settings: ``dict`` :param container: the container of this node :type container: :class:`plumbery.PlumberyInfrastructure` :return: a list of actions to be performed, and related descriptions :rtype: a ``list`` of `{ 'description': ..., 'genius': ... }`` """ if not isinstance(settings, dict): return [] environment = PlumberyNodeContext(node=node, container=container, context=self.facility) prepares = [] if self.key is not None: prepares.append({ 'description': 'deploy SSH public key', 'genius': SSHKeyDeployment(self.key)}) if ('prepare' in settings and isinstance(settings['prepare'], list) and len(settings['prepare']) > 0): plogging.info('- using prepare commands') for script in settings['prepare']: tokens = script.split(' ') if len(tokens) == 1: tokens.insert(0, 'run') if tokens[0] in ['run', 'run_raw']: # send and run a script script = tokens[1] if len(tokens) > 2: args = tokens[2:] else: args = [] plogging.debug("- {} {} {}".format( tokens[0], script, ' '.join(args))) try: with open(script) as stream: text = stream.read() if(tokens[0] == 'run' and PlumberyText.could_expand(text)): plogging.debug("- expanding script '{}'" .format(script)) text = PlumberyText.expand_string( text, environment) if len(text) > 0: plogging.info("- running '{}'" .format(script)) prepares.append({ 'description': ' '.join(tokens), 'genius': ScriptDeployment( script=text, args=args, name=script)}) else: plogging.error("- script '{}' is empty" .format(script)) except IOError: plogging.error("- unable to read script '{}'" .format(script)) elif tokens[0] in ['put', 'put_raw']: # send a file file = tokens[1] if len(tokens) > 2: destination = tokens[2] else: destination = './'+file plogging.debug("- {} {} {}".format( tokens[0], file, destination)) try: with open(file) as stream: content = stream.read() if(tokens[0] == 'put' and PlumberyText.could_expand(content)): plogging.debug("- expanding file '{}'" .format(file)) content = PlumberyText.expand_string( content, environment) plogging.info("- putting file '{}'" .format(file)) prepares.append({ 'description': ' '.join(tokens), 'genius': FileContentDeployment( content=content, target=destination)}) except IOError: plogging.error("- unable to read file '{}'" .format(file)) else: # echo a sensible message eventually if tokens[0] == 'echo': tokens.pop(0) message = ' '.join(tokens) message = PlumberyText.expand_string( message, environment) plogging.info("- {}".format(message)) if ('cloud-config' in settings and isinstance(settings['cloud-config'], dict) and len(settings['cloud-config']) > 0): plogging.info('- using cloud-config') # mandatory, else cloud-init will not consider user-data plogging.debug('- preparing meta-data') meta_data = 'instance_id: dummy\n' destination = '/var/lib/cloud/seed/nocloud-net/meta-data' prepares.append({ 'description': 'put meta-data', 'genius': FileContentDeployment( content=meta_data, target=destination)}) plogging.debug('- preparing user-data') expanded = PlumberyText.expand_string( settings['cloud-config'], environment) user_data = '#cloud-config\n'+expanded plogging.debug(user_data) destination = '/var/lib/cloud/seed/nocloud-net/user-data' prepares.append({ 'description': 'put user-data', 'genius': FileContentDeployment( content=user_data, target=destination)}) plogging.debug('- preparing remote install of cloud-init') script = 'prepare.cloud-init.sh' try: path = os.path.dirname(__file__)+'/'+script with open(path) as stream: text = stream.read() if text: prepares.append({ 'description': 'run '+script, 'genius': ScriptDeployment( script=text, name=script)}) except IOError: raise PlumberyException("Error: cannot read '{}'" .format(script)) plogging.debug('- preparing reboot to trigger cloud-init') prepares.append({ 'description': 'reboot node', 'genius': RebootDeployment( container=container)}) return prepares
class TestPlumberyText(unittest.TestCase): def setUp(self): self.text = PlumberyText() def tearDown(self): pass def test_dictionary(self): template = 'little {{ test }} with multiple {{test}} and {{}} as well' context = PlumberyContext(dictionary={'test': 'toast'}) expected = 'little toast with multiple toast and {{}} as well' self.assertEqual(self.text.expand_string(template, context), expected) def test_engine(self): engine = PlumberyEngine() context = PlumberyContext(context=engine) template = "we are running plumbery {{ plumbery.version }}" expected = "we are running plumbery " + __version__ self.assertEqual(self.text.expand_string(template, context), expected) engine.set_user_name('fake_name') engine.set_user_password('fake_password') template = "{{ name.credentials }} {{ password.credentials }}" expected = engine.get_user_name() + " " + engine.get_user_password() self.assertEqual(self.text.expand_string(template, context), expected) def test_bad_parameters(self): with self.assertRaises(TypeError): self.text.expand_parameters(1234, {}) with self.assertRaises(TypeError): self.text.expand_parameters({}, {}) template = 'little {{ test with multiple {{test and {{ as well' context = PlumberyContext(dictionary={'test': 'toast'}) self.assertEqual(self.text.expand_parameters(template, context), template) template = 'little {{ }} test and {{ }} as well' context = PlumberyContext(dictionary={'test': 'toast'}) self.assertEqual(self.text.expand_parameters(template, context), template) template = "{{ parameter.is.unknown }}" context = PlumberyContext(dictionary={'test': 'toast'}) with self.assertRaises(KeyError): self.text.expand_parameters(template, context) def test_bad_string(self): self.text.expand_string(1234, {}) self.text.expand_string({}, {}) template = 'little {{ test with multiple {{test and {{ as well' context = PlumberyContext(dictionary={'test': 'toast'}) self.assertEqual(self.text.expand_string(template, context), template) template = 'little {{ }} test and {{ }} as well' context = PlumberyContext(dictionary={'test': 'toast'}) self.assertEqual(self.text.expand_string(template, context), template) template = "{{ secret.is.unknown }}" context = PlumberyContext(dictionary={'test': 'toast'}) self.assertEqual(self.text.expand_string(template, context), template) def test_could_expand(self): template = 'little {{ test with multiple {{test and {{ as well' self.assertTrue(self.text.could_expand(template)) template = b'\x00\xFF\x00\xFF' self.assertFalse(self.text.could_expand(template)) def test_input1(self): context = PlumberyContext(dictionary={'node.private': '12.34.56.78'}) self.assertEqual(self.text.expand_string(input1, context), expected1) def test_input2(self): context = PlumberyContext(dictionary={}) transformed = yaml.load(self.text.expand_string(input2, context)) unmatched = { o: (input2[o], transformed[o]) for o in input2.keys() if input2[o] != transformed[o] } if unmatched != {}: print(unmatched) self.assertEqual(len(unmatched), 0) context = PlumberyContext(dictionary={'node.private': '12.34.56.78'}) transformed = yaml.load(self.text.expand_string(input2, context)) unmatched = { o: (expected2[o], transformed[o]) for o in expected2.keys() if expected2[o] != transformed[o] } if unmatched != {}: print(unmatched) self.assertEqual(len(unmatched), 0) def test_input3(self): loaded = yaml.load(input3) context = PlumberyContext(dictionary={'node.public': '12.34.56.78'}) transformed = yaml.load(self.text.expand_string(loaded, context)) self.assertEqual(transformed, yaml.load(expected3)) def test_input4(self): loaded = yaml.load(input4) context = PlumberyContext(dictionary={}) transformed = yaml.load(self.text.expand_string(loaded, context)) self.assertEqual(transformed, loaded) def test_input5(self): loaded = yaml.load(input5) context = PlumberyContext(dictionary={}) transformed = yaml.load(self.text.expand_string(loaded, context)) self.assertEqual(transformed, loaded) def test_input6(self): loaded = yaml.load(input6) context = PlumberyContext(dict6) transformed = self.text.expand_string(loaded, context) self.assertEqual(transformed.strip(), expected6.strip()) def test_input7(self): loaded = yaml.load(input7) context = PlumberyContext(dict7) transformed = self.text.expand_string(loaded, context) self.assertEqual(transformed.strip(), expected7.strip()) def test_input8(self): loaded = yaml.load(input8) context = PlumberyContext(dictionary={}) expanded = self.text.expand_string(loaded, context) self.assertEqual(expanded.strip(), input8.strip()) def test_input9(self): loaded = yaml.load(input9) context = PlumberyContext(context=PlumberyEngine()) expanded = self.text.expand_string(loaded, context) self.assertEqual((' - |' in expanded), False) def test_input10(self): loaded = yaml.load(input10) context = PlumberyContext(dictionary={}) expanded = self.text.expand_string(loaded, context) self.assertEqual(expanded.strip(), input10.strip()) def test_node1(self): template = "{{ mongo_mongos01.public }}" context = PlumberyNodeContext(node=FakeNode1()) expected = '168.128.12.163' self.assertEqual(self.text.expand_string(template, context), expected) template = "{{mongo_mongos01.private }}" expected = '192.168.50.11' self.assertEqual(self.text.expand_string(template, context), expected) template = "{{ mongo_mongos01}}" expected = '192.168.50.11' self.assertEqual(self.text.expand_string(template, context), expected) template = "{{ mongo_mongos01.ipv6 }}" expected = '2a00:47c0:111:1136:47c9:5a6a:911d:6c7f' self.assertEqual(self.text.expand_string(template, context), expected) def test_node2(self): template = "{{ mongo_mongos02.public }}" context = PlumberyNodeContext(node=FakeNode1(), container=FakeContainer()) expected = '168.128.12.164' self.assertEqual(self.text.expand_string(template, context), expected) template = "{{ mongo_mongos02.private }}" expected = '192.168.50.12' self.assertEqual(self.text.expand_string(template, context), expected) template = "{{ mongo_mongos02 }}" expected = '192.168.50.12' self.assertEqual(self.text.expand_string(template, context), expected) template = "{{ mongo_mongos02.ipv6 }}" expected = '2a00:47c0:111:1136:47c9:5a6a:911d:6c7f' self.assertEqual(self.text.expand_string(template, context), expected)