From 26ebbd86886a1124aed10c63a7e4387ca067af9a Mon Sep 17 00:00:00 2001 From: Tabulo Date: Sat, 15 Apr 2023 01:45:43 +0200 Subject: [PATCH] Add tests related to the transparent support of an eventual shebang in generated code [POLYGLOT] --- cdist/test/code/__init__.py | 291 +++++++++++------- .../gencode-local | 23 ++ .../gencode-remote | 1 + .../gencode-local | 19 ++ .../gencode-remote | 1 + .../gencode-local | 20 ++ .../gencode-remote | 1 + 7 files changed, 245 insertions(+), 111 deletions(-) create mode 100755 cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_perl/gencode-local create mode 120000 cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_perl/gencode-remote create mode 100755 cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_shell/gencode-local create mode 120000 cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_shell/gencode-remote create mode 100755 cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_shell_generates_perl/gencode-local create mode 120000 cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_shell_generates_perl/gencode-remote diff --git a/cdist/test/code/__init__.py b/cdist/test/code/__init__.py index bf80110d..62f1409a 100644 --- a/cdist/test/code/__init__.py +++ b/cdist/test/code/__init__.py @@ -24,6 +24,7 @@ import getpass import os import shutil import logging +import re import cdist from cdist import core @@ -37,123 +38,191 @@ my_dir = op.abspath(op.dirname(__file__)) fixtures = op.join(my_dir, 'fixtures') conf_dir = op.join(fixtures, 'conf') +class Burried: + '''This a bogus -alas needed- wrapper class whose sole + purpose is to hide the inner classes from `unittest` + that would otherwise try to run them, even though + they have now become abstractish base classes. + ''' -class CodeTestCase(test.CdistTestCase): + class CodeTestCase(test.CdistTestCase): + ''' + This is now a base class (which should not be invoked by unittest). + A couple tests had to be refactored, without any behavioral changes, + so as to suit POLYGLOT testing. + ''' + + def setUp(self): + self.setup_for_type('__dump_environment') + + def setup_for_type(self, tested_type_name): + self.local_dir = self.mkdtemp() + self.hostdir = cdist.str_hash(self.target_host[0]) + self.host_base_path = os.path.join(self.local_dir, self.hostdir) + + self.local = local.Local( + target_host=self.target_host, + target_host_tags=self.target_host_tags, + base_root_path=self.host_base_path, + host_dir_name=self.hostdir, + exec_path=cdist.test.cdist_exec_path, + add_conf_dirs=[conf_dir]) + self.local.create_files_dirs() + + self.remote_dir = self.mkdtemp() + remote_exec = self.remote_exec + remote_copy = self.remote_copy + self.remote = remote.Remote( + target_host=self.target_host, + remote_exec=remote_exec, + remote_copy=remote_copy, + base_path=self.remote_dir, + stdout_base_path=self.local.stdout_base_path, + stderr_base_path=self.local.stderr_base_path) + self.remote.create_files_dirs() + + self.code = code.Code(self.target_host, self.local, self.remote) + + self.cdist_type = core.CdistType(self.local.type_path, + tested_type_name) + self.cdist_object = core.CdistObject( + self.cdist_type, self.local.object_path, 'whatever', + self.local.object_marker_name) + self.cdist_object.create() + + def tearDown(self): + shutil.rmtree(self.local_dir) + shutil.rmtree(self.remote_dir) + + def _expected_environment(self): + expected = { + '__target_host' : self.local.target_host[0], + '__target_hostname' : self.local.target_host[1], + '__target_fqdn' : self.local.target_host[2], + '__global' : self.local.base_path, + '__type' : self.cdist_type.absolute_path, + '__object' : self.cdist_object.absolute_path, + '__object_id' : self.cdist_object.object_id, + '__object_name' : self.cdist_object.name, + '__files' : self.local.files_path, + '__target_host_tags' : self.local.target_host_tags, + '__cdist_log_level': str(logging.WARNING), + '__cdist_log_level_name' : 'WARNING' + } + return expected + + # Keeping around older simplex parsing logic for a while + def _parse_env_from_generated_code(self, script_text): + output_dict = {} + for line in script_text.split('\n'): + if line: + junk, value = line.split(': ') + key = junk.split(' ')[1] + output_dict[key] = value + return output_dict + + def test_run_gencode_local_environment(self): + script_text = self.code.run_gencode_local(self.cdist_object) + + self.assertDictContainsSubset( + self._expected_environment(), + self._parse_env_from_generated_code(script_text) + ) + + def test_run_gencode_remote_environment(self): + script_text = self.code.run_gencode_remote(self.cdist_object) + + self.assertDictContainsSubset( + self._expected_environment(), + self._parse_env_from_generated_code(script_text) + ) + + def test_transfer_code_remote(self): + self.cdist_object.code_remote = self.code.run_gencode_remote( + self.cdist_object) + self.code.transfer_code_remote(self.cdist_object) + destination = os.path.join(self.remote.object_path, + self.cdist_object.code_remote_path) + self.assertTrue(os.path.isfile(destination)) + + def test_run_code_local(self): + self.cdist_object.code_local = self.code.run_gencode_local( + self.cdist_object) + self.code.run_code_local(self.cdist_object) + + def test_run_code_remote_environment(self): + self.cdist_object.code_remote = self.code.run_gencode_remote( + self.cdist_object) + self.code.transfer_code_remote(self.cdist_object) + self.code.run_code_remote(self.cdist_object) + + class CodeTestCasePolyglot(CodeTestCase): + '''Base class for testing generetaed polyglot code + + The assumption here is that, if cdist works for Perl code, + it should work for any script language, as long as the + corresponding interpretor (given on the shebang line) + is available. + ''' + def _parse_env_from_generated_code(self, script_text): + ''' + New parsing logic allows for some simple generated perlish code + (as well as basic shell code). + + This implementation can actually be moved up the parent class + as a drop in replacement of the older parser. + + NOTE that this kind of parsing is necesarily fragile and sloppy. + It has been factored out and can easily be overriden, if needed. + ''' + output_dict = {} + for line in script_text.split('\n'): + line = line.strip() + + if not line: + continue # Ignore blank lines + + if bool(re.match('^#', line)): + continue # Ignore lines consisting of only comments + + if not bool(re.search(': ', line)): + continue # Ignore lines that do not contain our splitter pattern. + + junk, value = line.split(': ') + key = junk.split(' ')[1] + + # Remove leading quotes (single or double) + key = re.sub('^\s*["\']+\s*', '', key) + + # Remove trailing quotes + # and any eventual whitespace escape sequences just before + # XXX: Why do we need to double escape the backslash (4 in all) + value = re.sub('\s*([\\\\]+[trn])*\s*["\']*\s*[;]*\s*$', '', value) + + output_dict[key] = value + + return output_dict + +# Actual Test Case classes (visible to unittest) +class CodeTestCase(Burried.CodeTestCase): + ''' Older tests that cover the monoglot scenario (shell generates shell) + gencode-* is typically written for /bin/sh (which, in turn, must generate shell code). + ''' def setUp(self): - self.local_dir = self.mkdtemp() - self.hostdir = cdist.str_hash(self.target_host[0]) - self.host_base_path = os.path.join(self.local_dir, self.hostdir) + self.setup_for_type('__dump_environment') - self.local = local.Local( - target_host=self.target_host, - target_host_tags=self.target_host_tags, - base_root_path=self.host_base_path, - host_dir_name=self.hostdir, - exec_path=cdist.test.cdist_exec_path, - add_conf_dirs=[conf_dir]) - self.local.create_files_dirs() +class CodeTestCase_polyglot_perl_generates_perl(Burried.CodeTestCasePolyglot): + def setUp(self): + self.setup_for_type('__dump_environment_polyglot_perl_generates_perl') - self.remote_dir = self.mkdtemp() - remote_exec = self.remote_exec - remote_copy = self.remote_copy - self.remote = remote.Remote( - target_host=self.target_host, - remote_exec=remote_exec, - remote_copy=remote_copy, - base_path=self.remote_dir, - stdout_base_path=self.local.stdout_base_path, - stderr_base_path=self.local.stderr_base_path) - self.remote.create_files_dirs() - - self.code = code.Code(self.target_host, self.local, self.remote) - - self.cdist_type = core.CdistType(self.local.type_path, - '__dump_environment') - self.cdist_object = core.CdistObject( - self.cdist_type, self.local.object_path, 'whatever', - self.local.object_marker_name) - self.cdist_object.create() - - def tearDown(self): - shutil.rmtree(self.local_dir) - shutil.rmtree(self.remote_dir) - - def test_run_gencode_local_environment(self): - output_string = self.code.run_gencode_local(self.cdist_object) - output_dict = {} - for line in output_string.split('\n'): - if line: - junk, value = line.split(': ') - key = junk.split(' ')[1] - output_dict[key] = value - self.assertEqual(output_dict['__target_host'], - self.local.target_host[0]) - self.assertEqual(output_dict['__target_hostname'], - self.local.target_host[1]) - self.assertEqual(output_dict['__target_fqdn'], - self.local.target_host[2]) - self.assertEqual(output_dict['__global'], self.local.base_path) - self.assertEqual(output_dict['__type'], self.cdist_type.absolute_path) - self.assertEqual(output_dict['__object'], - self.cdist_object.absolute_path) - self.assertEqual(output_dict['__object_id'], - self.cdist_object.object_id) - self.assertEqual(output_dict['__object_name'], self.cdist_object.name) - self.assertEqual(output_dict['__files'], self.local.files_path) - self.assertEqual(output_dict['__target_host_tags'], - self.local.target_host_tags) - self.assertEqual(output_dict['__cdist_log_level'], - str(logging.WARNING)) - self.assertEqual(output_dict['__cdist_log_level_name'], 'WARNING') - - def test_run_gencode_remote_environment(self): - output_string = self.code.run_gencode_remote(self.cdist_object) - output_dict = {} - for line in output_string.split('\n'): - if line: - junk, value = line.split(': ') - key = junk.split(' ')[1] - output_dict[key] = value - self.assertEqual(output_dict['__target_host'], - self.local.target_host[0]) - self.assertEqual(output_dict['__target_hostname'], - self.local.target_host[1]) - self.assertEqual(output_dict['__target_fqdn'], - self.local.target_host[2]) - self.assertEqual(output_dict['__global'], self.local.base_path) - self.assertEqual(output_dict['__type'], self.cdist_type.absolute_path) - self.assertEqual(output_dict['__object'], - self.cdist_object.absolute_path) - self.assertEqual(output_dict['__object_id'], - self.cdist_object.object_id) - self.assertEqual(output_dict['__object_name'], self.cdist_object.name) - self.assertEqual(output_dict['__files'], self.local.files_path) - self.assertEqual(output_dict['__target_host_tags'], - self.local.target_host_tags) - self.assertEqual(output_dict['__cdist_log_level'], - str(logging.WARNING)) - self.assertEqual(output_dict['__cdist_log_level_name'], 'WARNING') - - def test_transfer_code_remote(self): - self.cdist_object.code_remote = self.code.run_gencode_remote( - self.cdist_object) - self.code.transfer_code_remote(self.cdist_object) - destination = os.path.join(self.remote.object_path, - self.cdist_object.code_remote_path) - self.assertTrue(os.path.isfile(destination)) - - def test_run_code_local(self): - self.cdist_object.code_local = self.code.run_gencode_local( - self.cdist_object) - self.code.run_code_local(self.cdist_object) - - def test_run_code_remote_environment(self): - self.cdist_object.code_remote = self.code.run_gencode_remote( - self.cdist_object) - self.code.transfer_code_remote(self.cdist_object) - self.code.run_code_remote(self.cdist_object) +class CodeTestCase_polyglot_perl_generates_shell(Burried.CodeTestCasePolyglot): + def setUp(self): + self.setup_for_type('__dump_environment_polyglot_perl_generates_shell') +class CodeTestCase_polyglot_shell_generates_perl(Burried.CodeTestCasePolyglot): + def setUp(self): + self.setup_for_type('__dump_environment_polyglot_shell_generates_perl') if __name__ == '__main__': import unittest diff --git a/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_perl/gencode-local b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_perl/gencode-local new file mode 100755 index 00000000..1b5aa02a --- /dev/null +++ b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_perl/gencode-local @@ -0,0 +1,23 @@ +#!/usr/bin/env perl +# [POLYGLOT]: A perl script that generates perl code + +use strict; + +print <<"EOT"; +#!/usr/bin/env perl +use strict; + +print "__target_host: $ENV{__target_host}\n"; +print "__target_hostname: $ENV{__target_hostname}\n"; +print "__target_fqdn: $ENV{__target_fqdn}\n"; +print "__global: $ENV{__global}\n"; +print "__type: $ENV{__type}\n"; +print "__object: $ENV{__object}\n"; +print "__object_id: $ENV{__object_id}\n"; +print "__object_name: $ENV{__object_name}\n"; +print "__files: $ENV{__files}\n"; +print "__target_host_tags: $ENV{__target_host_tags}\n"; +print "__cdist_log_level: $ENV{__cdist_log_level}\n"; +print "__cdist_log_level_name: $ENV{__cdist_log_level_name}\n"; + +EOT diff --git a/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_perl/gencode-remote b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_perl/gencode-remote new file mode 120000 index 00000000..7b427cac --- /dev/null +++ b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_perl/gencode-remote @@ -0,0 +1 @@ +gencode-local \ No newline at end of file diff --git a/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_shell/gencode-local b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_shell/gencode-local new file mode 100755 index 00000000..77fe4365 --- /dev/null +++ b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_shell/gencode-local @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +# [POLYGLOT]: A perl script that generates shell code + +use strict; + +print <<"EOT"; +echo __target_host: $ENV{__target_host} +echo __target_hostname: $ENV{__target_hostname} +echo __target_fqdn: $ENV{__target_fqdn} +echo __global: $ENV{__global} +echo __type: $ENV{__type} +echo __object: $ENV{__object} +echo __object_id: $ENV{__object_id} +echo __object_name: $ENV{__object_name} +echo __files: $ENV{__files} +echo __target_host_tags: $ENV{__target_host_tags} +echo __cdist_log_level: $ENV{__cdist_log_level} +echo __cdist_log_level_name: $ENV{__cdist_log_level_name} +EOT \ No newline at end of file diff --git a/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_shell/gencode-remote b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_shell/gencode-remote new file mode 120000 index 00000000..7b427cac --- /dev/null +++ b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_perl_generates_shell/gencode-remote @@ -0,0 +1 @@ +gencode-local \ No newline at end of file diff --git a/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_shell_generates_perl/gencode-local b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_shell_generates_perl/gencode-local new file mode 100755 index 00000000..0cc597eb --- /dev/null +++ b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_shell_generates_perl/gencode-local @@ -0,0 +1,20 @@ +#!/bin/sh +# [POLYGLOT]: A shell script that generates perl code + +cat <<-EOT +#!/usr/bin/env perl +use strict; + +print "__target_host: ${__target_host}\n"; +print "__target_hostname: ${__target_hostname}\n"; +print "__target_fqdn: ${__target_fqdn}\n"; +print "__global: ${__global}\n"; +print "__type: ${__type}\n"; +print "__object: ${__object}\n"; +print "__object_id: ${__object_id}\n"; +print "__object_name: ${__object_name}\n"; +print "__files: ${__files}\n"; +print "__target_host_tags: ${__target_host_tags}\n"; +print "__cdist_log_level: ${__cdist_log_level}\n"; +print "__cdist_log_level_name: ${__cdist_log_level_name}\n"; +EOT diff --git a/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_shell_generates_perl/gencode-remote b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_shell_generates_perl/gencode-remote new file mode 120000 index 00000000..7b427cac --- /dev/null +++ b/cdist/test/code/fixtures/conf/type/__dump_environment_polyglot_shell_generates_perl/gencode-remote @@ -0,0 +1 @@ +gencode-local \ No newline at end of file