def index_resources( config, policies, date=None, concurrency=5, accounts=None, tag=None, verbose=False): """index policy resources""" logging.basicConfig(level=(verbose and logging.DEBUG or logging.INFO)) logging.getLogger('botocore').setLevel(logging.WARNING) logging.getLogger('elasticsearch').setLevel(logging.WARNING) logging.getLogger('urllib3').setLevel(logging.WARNING) logging.getLogger('requests').setLevel(logging.WARNING) logging.getLogger('c7n.worker').setLevel(logging.INFO) # validating the config and policy files. with open(config) as fh: config = yaml.safe_load(fh.read()) jsonschema.validate(config, CONFIG_SCHEMA) with open(policies) as fh: policies = yaml.safe_load(fh.read()) load_resources() schema.validate(policies) date = valid_date(date, delta=1) with ProcessPoolExecutor(max_workers=concurrency) as w: futures = {} jobs = [] for account in config.get('accounts'): if accounts and account['name'] not in accounts: continue if tag: found = False for t in account['tags'].values(): if tag == t: found = True break if not found: continue for region in account.get('regions'): for policy in policies.get('policies'): p = (config, account, region, policy, date) jobs.append(p) for j in jobs: log.debug("submit account:{} region:{} policy:{} date:{}".format( j[1]['name'], j[2], j[3]['name'], j[4])) futures[w.submit(index_account_resources, *j)] = j # Process completed for f in as_completed(futures): config, account, region, policy, date = futures[f] if f.exception(): log.warning( "error account:{} region:{} policy:{} error:{}".format( account['name'], region, policy['name'], f.exception())) continue log.info("complete account:{} region:{} policy:{}".format( account['name'], region, policy['name']))
def load(options, path, format='yaml', validate=True): # should we do os.path.expanduser here? if not os.path.exists(path): raise IOError("Invalid path for config %r" % path) load_resources() with open(path) as fh: if format == 'yaml': data = utils.yaml_load(fh.read()) elif format == 'json': data = utils.loads(fh.read()) validate = False # Test for empty policy file if not data or data.get('policies') is None: return None if validate: from c7n.schema import validate errors = validate(data) if errors: raise Exception("Failed to validate on policy %s \n %s" % (errors[1], errors[0])) collection = PolicyCollection.from_data(data, options) return collection
def load(options, path, format='yaml', validate=True, vars=None): # should we do os.path.expanduser here? if not os.path.exists(path): raise IOError("Invalid path for config %r" % path) load_resources() data = utils.load_file(path, format=format, vars=vars) if format == 'json': validate = False if isinstance(data, list): log.warning('yaml in invalid format. The "policies:" line is probably missing.') return None # Test for empty policy file if not data or data.get('policies') is None: return None if validate: from c7n.schema import validate errors = validate(data) if errors: raise Exception( "Failed to validate on policy %s \n %s" % ( errors[1], errors[0])) collection = PolicyCollection.from_data(data, options) if validate: # non schema validation of policies [p.validate() for p in collection] return collection
def validate(options): if not os.path.exists(options.config): raise ValueError("Invalid path for config %r" % options.config) options.dryrun = True format = options.config.rsplit('.', 1)[-1] with open(options.config) as fh: if format in ('yml', 'yaml'): data = yaml.safe_load(fh.read()) if format in ('json',): data = json.load(fh) errors = schema.validate(data) if not errors: null_config = Bag(dryrun=True, log_group=None, cache=None, assume_role="na") for p in data.get('policies', ()): try: Policy(p, null_config, Bag()) except Exception as e: log.error("Policy: %s is invalid: %s" % ( p.get('name', 'unknown'), e)) sys.exit(1) return log.info("Config valid") return log.error("Invalid configuration") for e in errors: log.error(" %s" % e) sys.exit(1)
def validate(options): load_resources() if len(options.configs) < 1: log.error('no config files specified') sys.exit(1) used_policy_names = set() schm = schema.generate() errors = [] for config_file in options.configs: config_file = os.path.expanduser(config_file) if not os.path.exists(config_file): raise ValueError("Invalid path for config %r" % config_file) options.dryrun = True fmt = config_file.rsplit('.', 1)[-1] with open(config_file) as fh: if fmt in ('yml', 'yaml'): data = yaml.safe_load(fh.read()) elif fmt in ('json', ): data = json.load(fh) else: log.error("The config file must end in .json, .yml or .yaml.") raise ValueError( "The config file must end in .json, .yml or .yaml.") errors += schema.validate(data, schm) conf_policy_names = { p.get('name', 'unknown') for p in data.get('policies', ()) } dupes = conf_policy_names.intersection(used_policy_names) if len(dupes) >= 1: errors.append( ValueError( "Only one policy with a given name allowed, duplicates: %s" % (", ".join(dupes)))) used_policy_names = used_policy_names.union(conf_policy_names) if not errors: null_config = Bag(dryrun=True, log_group=None, cache=None, assume_role="na") for p in data.get('policies', ()): try: policy = Policy(p, null_config, Bag()) policy.validate() except Exception as e: msg = "Policy: %s is invalid: %s" % (p.get( 'name', 'unknown'), e) errors.append(msg) if not errors: log.info("Configuration valid: {}".format(config_file)) continue log.error("Configuration invalid: {}".format(config_file)) for e in errors: log.error("%s" % e) if errors: sys.exit(1)
def load(options, path, format=None, validate=True, vars=None): # should we do os.path.expanduser here? if not os.path.exists(path): raise IOError("Invalid path for config %r" % path) from c7n.schema import validate, StructureParser data = utils.load_file(path, format=format, vars=vars) structure = StructureParser() structure.validate(data) load_resources(structure.get_resource_types(data)) if isinstance(data, list): log.warning( 'yaml in invalid format. The "policies:" line is probably missing.' ) return None if validate: errors = validate(data) if errors: raise PolicyValidationError("Failed to validate policy %s \n %s" % (errors[1], errors[0])) # Test for empty policy file if not data or data.get('policies') is None: return None collection = PolicyCollection.from_data(data, options) if validate: # non schema validation of policies [p.validate() for p in collection] return collection
def validate(options): load_resources() if len(options.configs) < 1: log.error('no config files specified') sys.exit(1) used_policy_names = set() schm = schema.generate() errors = [] for config_file in options.configs: config_file = os.path.expanduser(config_file) if not os.path.exists(config_file): raise ValueError("Invalid path for config %r" % config_file) options.dryrun = True fmt = config_file.rsplit('.', 1)[-1] with open(config_file) as fh: if fmt in ('yml', 'yaml'): data = yaml.safe_load(fh.read()) elif fmt in ('json',): data = json.load(fh) else: log.error("The config file must end in .json, .yml or .yaml.") raise ValueError("The config file must end in .json, .yml or .yaml.") errors += schema.validate(data, schm) conf_policy_names = { p.get('name', 'unknown') for p in data.get('policies', ())} dupes = conf_policy_names.intersection(used_policy_names) if len(dupes) >= 1: errors.append(ValueError( "Only one policy with a given name allowed, duplicates: %s" % ( ", ".join(dupes) ) )) used_policy_names = used_policy_names.union(conf_policy_names) if not errors: null_config = Config.empty(dryrun=True, account_id='na', region='na') for p in data.get('policies', ()): try: policy = Policy(p, null_config, Bag()) policy.validate() except Exception as e: msg = "Policy: %s is invalid: %s" % ( p.get('name', 'unknown'), e) errors.append(msg) if not errors: log.info("Configuration valid: {}".format(config_file)) continue log.error("Configuration invalid: {}".format(config_file)) for e in errors: log.error("%s" % e) if errors: sys.exit(1)
def validate(options): load_resources() if options.config is not None: # support the old -c option options.configs.append(options.config) if len(options.configs) < 1: # no configs to test # We don't have the parser object, so fake ArgumentParser.error eprint('Error: no config files specified') sys.exit(1) used_policy_names = set() schm = schema.generate() errors = [] for config_file in options.configs: config_file = os.path.expanduser(config_file) if not os.path.exists(config_file): raise ValueError("Invalid path for config %r" % config_file) options.dryrun = True format = config_file.rsplit('.', 1)[-1] with open(config_file) as fh: if format in ('yml', 'yaml'): data = yaml.safe_load(fh.read()) if format in ('json', ): data = json.load(fh) errors = schema.validate(data, schm) conf_policy_names = {p['name'] for p in data.get('policies', ())} dupes = conf_policy_names.intersection(used_policy_names) if len(dupes) >= 1: errors.append( ValueError( "Only one policy with a given name allowed, duplicates: %s" % (", ".join(dupes)))) used_policy_names = used_policy_names.union(conf_policy_names) if not errors: null_config = Bag(dryrun=True, log_group=None, cache=None, assume_role="na") for p in data.get('policies', ()): try: Policy(p, null_config, Bag()) except Exception as e: msg = "Policy: %s is invalid: %s" % (p.get( 'name', 'unknown'), e) errors.append(msg) if not errors: log.info("Configuration valid: {}".format(config_file)) continue log.error("Configuration invalid: {}".format(config_file)) for e in errors: log.error(" %s" % e) if errors: sys.exit(1)
def validate(options, policy_collection): errors = schema.validate(policy_collection.data) if not errors: log.info("Config valid") return log.error("Invalid configuration") for e in errors: log.error(" %s" % e) sys.exit(1)
def validate(options): load_resources() if options.config is not None: # support the old -c option options.configs.append(options.config) if len(options.configs) < 1: # no configs to test # We don't have the parser object, so fake ArgumentParser.error print('custodian validate: error: no config files specified', file=sys.stderr) sys.exit(2) used_policy_names = set() schm = schema.generate() errors = [] for config_file in options.configs: config_file = os.path.expanduser(config_file) if not os.path.exists(config_file): raise ValueError("Invalid path for config %r" % config_file) options.dryrun = True format = config_file.rsplit('.', 1)[-1] with open(config_file) as fh: if format in ('yml', 'yaml'): data = yaml.safe_load(fh.read()) if format in ('json',): data = json.load(fh) errors = schema.validate(data, schm) conf_policy_names = {p['name'] for p in data.get('policies', ())} dupes = conf_policy_names.intersection(used_policy_names) if len(dupes) >= 1: errors.append(ValueError( "Only one policy with a given name allowed, duplicates: %s" % ( ", ".join(dupes) ) )) used_policy_names = used_policy_names.union(conf_policy_names) if not errors: null_config = Bag(dryrun=True, log_group=None, cache=None, assume_role="na") for p in data.get('policies', ()): try: Policy(p, null_config, Bag()) except Exception as e: msg = "Policy: %s is invalid: %s" % ( p.get('name', 'unknown'), e) errors.append(msg) if not errors: log.info("Configuration valid: {}".format(config_file)) continue log.error("Configuration invalid: {}".format(config_file)) for e in errors: log.error(" %s" % e) if errors: sys.exit(1)
def test_duplicate_policies(self): data = { "policies": [ {"name": "monday-morning", "resource": "ec2"}, {"name": "monday-morning", "resource": "ec2"}, ] } result = validate(data) self.assertEqual(len(result), 2) self.assertTrue(isinstance(result[0], ValueError)) self.assertTrue("monday-morning" in str(result[0]))
def test_duplicate_policies(self): data = { 'policies': [ {'name': 'monday-morning', 'resource': 'ec2'}, {'name': 'monday-morning', 'resource': 'ec2'}, ]} result = validate(data) self.assertEqual(len(result), 1) self.assertTrue(isinstance(result[0], ValueError)) self.assertTrue('monday-morning' in str(result[0]))
def test_py3_policy_error(self): data = { 'policies': [{ 'name': 'policy-ec2', 'resource': 'ec2', 'actions': [ {'type': 'terminate', 'force': 'asdf'}]}]} result = validate(data) self.assertEqual(len(result), 2) err, policy = result self.assertTrue("'asdf' is not of type 'boolean'" in str(err).replace("u'", "'")) self.assertEqual(policy, 'policy-ec2')
def test_handle_specific_error_fail(self, mock_specific_error): from jsonschema.exceptions import ValidationError data = { 'policies': [{'name': 'test', 'resource': 'ec2', 'filters': { 'type': 'ebs', 'invalid': []} }] } mock_specific_error.side_effect = ValueError('The specific error crapped out hard') resp = validate(data) # if it is 2, then we know we got the exception from specific_error self.assertEqual(len(resp), 2) self.assertIsInstance(resp[0], ValidationError) self.assertIsInstance(resp[1], ValidationError)
def load(options, path, format='yaml', validate=True): if not os.path.exists(path): raise ValueError("Invalid path for config %r" % path) with open(path) as fh: if format == 'yaml': data = utils.yaml_load(fh.read()) elif format == 'json': data = utils.loads(fh.read()) validate = False if validate: from c7n.schema import validate errors = validate(data) if errors: raise errors[0] return PolicyCollection(data, options)
def test_handle_specific_error_fail(self, mock_specific_error): from jsonschema.exceptions import ValidationError data = { "policies": [ { "name": "test", "resource": "aws.ec2", "filters": {"type": "ebs", "invalid": []}, } ] } mock_specific_error.side_effect = ValueError( "The specific error crapped out hard" ) resp = validate(data) # if it is 2, then we know we got the exception from specific_error self.assertEqual(len(resp), 2) self.assertIsInstance(resp[0], ValidationError) self.assertIsInstance(resp[1], ValidationError)
def validate(options): if not os.path.exists(options.config): raise ValueError("Invalid path for config %r" % options.config) format = options.config.rsplit('.', 1)[-1] with open(options.config) as fh: if format in ('yml', 'yaml'): data = yaml.safe_load(fh.read()) if format in ('json', ): data = json.load(fh) errors = schema.validate(data) if not errors: log.info("Config valid") return log.error("Invalid configuration") for e in errors: log.error(" %s" % e) sys.exit(1)
def validate(options): if not os.path.exists(options.config): raise ValueError("Invalid path for config %r" % options.config) format = options.config.rsplit('.', 1)[-1] with open(options.config) as fh: if format in ('yml', 'yaml'): data = yaml.safe_load(fh.read()) if format in ('json',): data = json.load(fh) errors = schema.validate(data) if not errors: log.info("Config valid") return log.error("Invalid configuration") for e in errors: log.error(" %s" % e) sys.exit(1)
def load(options, path, format='yaml', validate=True): # should we do os.path.expanduser here? if not os.path.exists(path): raise IOError("Invalid path for config %r" % path) load_resources() with open(path) as fh: if format == 'yaml': data = utils.yaml_load(fh.read()) elif format == 'json': data = utils.loads(fh.read()) validate = False # Test for empty policy file if not data or data.get('policies') is None: return None if validate: from c7n.schema import validate errors = validate(data) if errors: raise Exception("Failed to validate on policy %s \n %s" % (errors[1], errors[0])) return PolicyCollection(data, options)
def validate(options): from c7n import schema if len(options.configs) < 1: log.error('no config files specified') sys.exit(1) used_policy_names = set() structure = StructureParser() errors = [] for config_file in options.configs: config_file = os.path.expanduser(config_file) if not os.path.exists(config_file): raise ValueError("Invalid path for config %r" % config_file) options.dryrun = True fmt = config_file.rsplit('.', 1)[-1] with open(config_file) as fh: if fmt in ('yml', 'yaml', 'json'): data = yaml.load(fh.read(), Loader=DuplicateKeyCheckLoader) else: log.error("The config file must end in .json, .yml or .yaml.") raise ValueError("The config file must end in .json, .yml or .yaml.") try: structure.validate(data) except PolicyValidationError as e: log.error("Configuration invalid: {}".format(config_file)) log.error("%s" % e) errors.append(e) continue load_resources(structure.get_resource_types(data)) schm = schema.generate() errors += schema.validate(data, schm) conf_policy_names = { p.get('name', 'unknown') for p in data.get('policies', ())} dupes = conf_policy_names.intersection(used_policy_names) if len(dupes) >= 1: errors.append(ValueError( "Only one policy with a given name allowed, duplicates: %s" % ( ", ".join(dupes) ) )) used_policy_names = used_policy_names.union(conf_policy_names) if not errors: null_config = Config.empty(dryrun=True, account_id='na', region='na') for p in data.get('policies', ()): try: policy = Policy(p, null_config, Bag()) policy.validate() except Exception as e: msg = "Policy: %s is invalid: %s" % ( p.get('name', 'unknown'), e) errors.append(msg) if not errors: log.info("Configuration valid: {}".format(config_file)) continue log.error("Configuration invalid: {}".format(config_file)) for e in errors: log.error("%s" % e) if errors: sys.exit(1)
def test_empty_skeleton(self): self.assertEqual(validate({'policies': []}), [])
def validate(options): from c7n import schema if len(options.configs) < 1: log.error('no config files specified') sys.exit(1) used_policy_names = set() structure = StructureParser() errors = [] found_deprecations = False footnotes = deprecated.Footnotes() for config_file in options.configs: config_file = os.path.expanduser(config_file) if not os.path.exists(config_file): raise ValueError("Invalid path for config %r" % config_file) options.dryrun = True fmt = config_file.rsplit('.', 1)[-1] with open(config_file) as fh: if fmt in ('yml', 'yaml', 'json'): # our loader is safe loader derived. data = yaml.load( fh.read(), Loader=DuplicateKeyCheckLoader) # nosec nosemgrep else: log.error("The config file must end in .json, .yml or .yaml.") raise ValueError( "The config file must end in .json, .yml or .yaml.") try: structure.validate(data) except PolicyValidationError as e: log.error("Configuration invalid: {}".format(config_file)) log.error("%s" % e) errors.append(e) continue load_resources(structure.get_resource_types(data)) schm = schema.generate() errors += schema.validate(data, schm) conf_policy_names = { p.get('name', 'unknown') for p in data.get('policies', ()) } dupes = conf_policy_names.intersection(used_policy_names) if len(dupes) >= 1: errors.append( ValueError( "Only one policy with a given name allowed, duplicates: %s" % (", ".join(dupes)))) used_policy_names = used_policy_names.union(conf_policy_names) source_locator = None if fmt in ('yml', 'yaml'): # For yaml files there is at least the expectation that the policy # name is on a line by itself. With JSON, the file could be one big # line. At this stage we are only attempting to find line number for # policies in yaml files. source_locator = SourceLocator(config_file) if not errors: null_config = Config.empty(dryrun=True, account_id='na', region='na') for p in data.get('policies', ()): try: policy = Policy(p, null_config, Bag()) policy.validate() # If the policy is invalid, there isn't much point checking # for deprecated usage as there is no guarantee as to the # state of the policy. if options.check_deprecations != deprecated.SKIP: report = deprecated.report(policy) if report: found_deprecations = True log.warning( "deprecated usage found in policy\n" + report.format(source_locator=source_locator, footnotes=footnotes)) except Exception as e: msg = "Policy: %s is invalid: %s" % (p.get( 'name', 'unknown'), e) errors.append(msg) if not errors: log.info("Configuration valid: {}".format(config_file)) continue log.error("Configuration invalid: {}".format(config_file)) for e in errors: log.error("%s" % e) if found_deprecations: notes = footnotes() if notes: log.warning("deprecation footnotes:\n" + notes) if options.check_deprecations == deprecated.STRICT: sys.exit(1) if errors: sys.exit(1)