def test_get_account_alias_invalid_env(self): """ Tests if get_account_alias raises exceptions when given invalid environments Returns: None Raises: AssertionError if any of the assert checks fail """ # Create junk environment values by attaching numbers to non-ephemeral environments and not attaching numbers # to ephemeral environments for env, account_alias in EFConfig.ENV_ACCOUNT_MAP.items(): if env not in EFConfig.EPHEMERAL_ENVS: env += '0' with self.assertRaises(ValueError) as exception: ef_conf_utils.get_account_alias(env) self.assertIn("unknown env", str(exception.exception)) # Hard coded junk values with self.assertRaises(ValueError) as exception: ef_conf_utils.get_account_alias("non-existent-env") self.assertIn("unknown env", str(exception.exception)) with patch('ef_conf_utils.env_valid') as mock_env_valid: with self.assertRaises(ValueError) as exception: mock_env_valid.return_value = True ef_conf_utils.get_account_alias("non-existent-env") self.assertIn("has no entry in ENV_ACCOUNT_MAP", str(exception.exception)) with self.assertRaises(ValueError) as exception: ef_conf_utils.get_account_alias("") self.assertIn("unknown env", str(exception.exception)) with self.assertRaises(ValueError) as exception: ef_conf_utils.get_account_alias(None) self.assertIn("unknown env", str(exception.exception))
def test_fully_qualified_env(self, mock_create_aws): """Does {{ENV_FULL}} resolve correctly""" mock_create_aws.return_value = self._clients # alpha0 test_string = "{{ENV_FULL}}" resolver = EFTemplateResolver(profile=get_account_alias("alpha0"), env="alpha0", region=TEST_REGION, service=TEST_SERVICE) resolver.load(test_string, PARAMS) self.assertEqual(resolver.render(), "alpha0") # prod resolver = EFTemplateResolver(profile=get_account_alias("test"), env="test", region=TEST_REGION, service=TEST_SERVICE) resolver.load(test_string, PARAMS) self.assertEqual(resolver.render(), "test") # mgmt.testaccount resolver = EFTemplateResolver( profile=get_account_alias("mgmt.testaccount"), env="mgmt.testaccount", region=TEST_REGION, service=TEST_SERVICE) resolver.load(test_string, PARAMS) self.assertEqual(resolver.render(), "mgmt.testaccount")
def test_get_account_alias(self): """ Checks if get_account_alias returns the correct account based on valid environments Returns: None Raises: AssertionError if any of the assert checks fail """ for env, account_alias in EFConfig.ENV_ACCOUNT_MAP.items(): # Attach a numeric value to environments that are ephemeral if env in EFConfig.EPHEMERAL_ENVS: env += '0' self.assertEquals(ef_conf_utils.get_account_alias(env), account_alias) # Do tests for global and mgmt envs, which have a special mapping, Example: global.account_alias if "global" in EFConfig.ENV_ACCOUNT_MAP: for account_alias in EFConfig.ENV_ACCOUNT_MAP.values(): self.assertEquals( ef_conf_utils.get_account_alias("global." + account_alias), account_alias) if "mgmt" in EFConfig.ENV_ACCOUNT_MAP: for account_alias in EFConfig.ENV_ACCOUNT_MAP.values(): self.assertEquals( ef_conf_utils.get_account_alias("mgmt." + account_alias), account_alias)
def handle_args_and_set_context(args): """ Args: args: the command line args, probably passed from main() as sys.argv[1:] Returns: a populated Context object based on CLI args """ parser = argparse.ArgumentParser() parser.add_argument("env", help="environment") parser.add_argument("path_to_template", help="path to the config template to process") parser.add_argument("--no_params", help="disable loading values from params file", action="store_true", default=False) parser.add_argument("--verbose", help="Output extra info", action="store_true", default=False) parser.add_argument("--lint", help="Test configs for valid JSON/YAML syntax", action="store_true", default=False) parser.add_argument("--silent", help="Suppress output of rendered template", action="store_true", default=False) parsed = vars(parser.parse_args(args)) path_to_template = abspath(parsed["path_to_template"]) service = path_to_template.split('/')[-3] return Context(get_account_alias(parsed["env"]), EFConfig.DEFAULT_REGION, parsed["env"], service, path_to_template, parsed["no_params"], parsed["verbose"], parsed["lint"], parsed["silent"])
def test_render_multiline_string_from_list(self, mock_create_aws): """Does {{multi}} resolve correctly as a multiline string from yaml parameters file""" mock_create_aws.return_value = self._clients test_string = "{{multi2}}" resolver = EFTemplateResolver(profile=get_account_alias("test"), env="test", region=TEST_REGION, service=TEST_SERVICE) with open(self.test_params_json) as json_file: resolver.load(test_string, json_file) self.assertEqual(resolver.render(), "one\ntwo\nthree")
def test_load_yaml_file(self, mock_create_aws): """Does {{one}} resolve correctly from yaml parameters file""" mock_create_aws.return_value = self._clients test_string = "{{one}}" resolver = EFTemplateResolver(profile=get_account_alias("alpha0"), env="alpha0", region=TEST_REGION, service=TEST_SERVICE) with open(self.test_params_yaml) as yaml_file: resolver.load(test_string, yaml_file) self.assertEqual(resolver.render(), "alpha one")
def test_render_multiline_string(self, mock_create_aws): """Does {{multi}} resolve correctly as a multiline string from yaml parameters file""" mock_create_aws.return_value = self._clients test_string = "{{multi}}" resolver = EFTemplateResolver(profile=get_account_alias("test"), env="test", region=TEST_REGION, service=TEST_SERVICE) with open(self.test_params_yaml) as yaml_file: resolver.load(test_string, yaml_file) self.assertEqual( resolver.render(), "thisisareallylongstringthatcoversmultiple\nlinesfortestingmultilinestrings" )
def env(self, value): """ Sets context.env, context.env_short, and context.account_alias if env is valid For envs of the form "global.<account>" and "mgmt.<account_alias>", env is captured as "global" or "mgmt" and account_alias is parsed out of the full env rather than looked up Args: value: the fully-qualified env value Raises: ValueError if env is not valid """ env_valid(value) self._env_full = value if value.find(".") == -1: # plain environment, e.g. prod, staging, proto<n> self._env = value self._account_alias = get_account_alias(value) else: # "<env>.<account_alias>" form, e.g. global.ellationeng or mgmt.ellationeng self._env, self._account_alias = value.split(".") # since we extracted an env, must reconfirm that it's legit global_env_valid(self._env) self._env_short = get_env_short(value)
def merge_files(service, skip_on_user_group_error=False): """ Given a prefix, find all templates below; merge with parameters; write to "dest" Args: service: "<service>", "all", or "ssh" skip_on_user_group_error: True or False For S3, full path becomes: s3://ellation-cx-global-configs/<service>/templates/<filename> s3://ellation-cx-global-configs/<service>/parameters/<filename>.parameters.<yaml|yml|json> For filesystem, full path becomes: /vagrant/configs/<service>/templates/<filename> /vagrant/configs/<service>/parameters/<filename>.parameters.<yaml|yml|json> """ if WHERE == "ec2": config_reader = EFInstanceinitConfigReader("s3", service, log_info, RESOURCES["s3"]) resolver = EFTemplateResolver() elif WHERE == "virtualbox-kvm": config_path = "{}/{}".format(VIRTUALBOX_CONFIG_ROOT, service) config_reader = EFInstanceinitConfigReader("file", config_path, log_info) environment = EFConfig.VAGRANT_ENV resolver = EFTemplateResolver(env=environment, profile=get_account_alias(environment), region=EFConfig.DEFAULT_REGION, service=service) while config_reader.next(): log_info("checking: {}".format(config_reader.current_key)) # if 'dest' for the current object contains an 'environments' list, check it dest = config_reader.dest if "environments" in dest: if not resolver.resolved["ENV_SHORT"] in dest["environments"]: log_info("Environment: {} not enabled for {}".format( resolver.resolved["ENV_SHORT"], config_reader.current_key)) continue # If 'dest' for the current object contains a user_group that hasn't been created in the environment yet and the # flag is set to True to skip, log the error and move onto the next config file without blowing up. if skip_on_user_group_error: user, group = get_user_group(dest) try: getpwnam(user).pw_uid except KeyError: log_info( "File specifies user {} that doesn't exist in environment. Skipping config file." .format(user)) continue try: getgrnam(group).gr_gid except KeyError: log_info( "File specifies group {} that doesn't exist in environment. Skipping config file." .format(group)) continue # Process the template_body - apply context + parameters log_info("Resolving template") resolver.load(config_reader.template, config_reader.parameters) rendered_body = resolver.render() if not resolver.resolved_ok(): critical( "Couldn't resolve all symbols; template has leftover {{ or }}: {}" .format(resolver.unresolved_symbols())) # Write the rendered file dir_path = normpath(dirname(dest["path"])) # Resolved OK. try to write the template log_info("make directories: {} {}".format(dir_path, dest["dir_perm"])) try: makedirs(dir_path, int(dest["dir_perm"], 8)) except OSError as error: if error.errno != 17: critical("Error making directories {}".format(repr(error))) log_info("open: " + dest["path"] + ",w+") try: outfile = open(dest["path"], 'w+') log_info("write") outfile.write(rendered_body) log_info("close") outfile.close() log_info("chmod file to: " + dest["file_perm"]) chmod(dest["path"], int(dest["file_perm"], 8)) user, group = get_user_group(dest) uid = getpwnam(user).pw_uid gid = getgrnam(group).gr_gid log_info("chown last directory in path to: " + dest["user_group"]) chown(dir_path, uid, gid) log_info("chown file to: " + dest["user_group"]) chown(dest["path"], uid, gid) except Exception as error: critical("Error writing file: " + dest["path"] + ": " + repr(error))
WAF Web ACL ID: {{aws:waf:web-acl-id,staging-StaticAcl}}\n\ SSL Certificate ARN us-west-2/cx-proto3.com: {{aws:acm:certificate-arn,us-west-2/cx-proto3.com}}\n\ Elastic network interface (ENI) eni-proto0-dnsproxy-1a: {{aws:ec2:eni/eni-id,eni-proto0-dnsproxy-1a}}\n\ Elastic IP Allocation ID: {{aws:ec2:elasticip/elasticip-id,ElasticIpMgmtCingest1}}\n\ Elastic IP IP Address: {{aws:ec2:elasticip/elasticip-ipaddress,ElasticIpMgmtCingest1}}\n\ EFConfig resolver, accountaliasofenv,prod: {{efconfig:accountaliasofenv,staging}}\n\ AMI lookup: {{version:ami-id,proto0/test-instance}}\n\ Latest AMI for test-instance: {{version:ami-id,proto0/test-instance}}\n\ Custom Data: \"{{efconfig:customdata,office_ips}}\"\ " GLOBAL_ENV_TEST_STRING = "fully-qualified environment:{{ENV_FULL}}\n" # Test with proto0 if LOCAL: resolver = EFTemplateResolver(profile=get_account_alias("proto0"), env="proto0", region="us-west-2", service="mine", verbose=True) else: resolver = EFTemplateResolver(verbose=True) resolver.load(TEST_STRING, PARAMS) resolver.render() print(resolver.template) print("unresolved symbol count: "+str(len(resolver.unresolved_symbols()))) print("unresolved symbols: "+repr(resolver.unresolved_symbols())) print("all template symbols: "+repr(resolver.symbols)) print("all EFTemplateResolver symbols: "+repr(resolver.resolved)) # Demo with the global env 'mgmt.ellationeng' (local only)
limitations under the License. """ import os import unittest from mock import call, Mock, patch # For local application imports, context_paths must be first despite lexicon ordering import context_paths from ef_config import EFConfig from ef_template_resolver import EFTemplateResolver from ef_conf_utils import get_account_alias TEST_PROFILE = get_account_alias("test") TEST_REGION = EFConfig.DEFAULT_REGION TEST_ENV = "test" TEST_SERVICE = "none" PARAMS = """{ "params":{ "default":{ "one": "default one", "two": "default two", "o": "o", "ne": "ne", "/_-.": "slashunderscoredashdot", ".": "dot", "my-thing": "my-hyphen-thing" },