def _value_to_sort(self, config, r): expr = config.get('key') vtype = config.get('value_type', 'string') vregex = config.get('value_regex') v = None try: # extract value based on jmespath if expr: v = self.get_resource_value(expr, r, vregex) if v is not None: # now convert to expected type if vtype == 'number': v = float(v) elif vtype == 'date': v = parse_date(v) else: v = str(v) except (AttributeError, ValueError): v = None if v is None: v = config.get('null_sort_value') return v
def validate(self): if len(self.data) == 1: return self # `resource_count` requires a slightly different schema than the rest of # the value filters because it operates on the full resource list if self.data.get('value_type') == 'resource_count': return self._validate_resource_count() elif self.data.get('value_type') == 'date': if not parse_date(self.data.get('value')): raise PolicyValidationError( "value_type: date with invalid date value:%s", self.data.get('value', '')) if 'key' not in self.data and 'key' in self.required_keys: raise PolicyValidationError("Missing 'key' in value filter %s" % self.data) if ('value' not in self.data and 'value_from' not in self.data and 'value' in self.required_keys): raise PolicyValidationError("Missing 'value' in value filter %s" % self.data) if 'op' in self.data: if not self.data['op'] in OPERATORS: raise PolicyValidationError( "Invalid operator in value filter %s" % self.data) if self.data['op'] in {'regex', 'regex-case'}: # Sanity check that we can compile try: re.compile(self.data['value']) except re.error as e: raise PolicyValidationError("Invalid regex: %s %s" % (e, self.data)) if 'value_regex' in self.data: return self._validate_value_regex(self.data['value_regex']) return self
def process_lambda(self, client, r): exclude_aliases = self.data.get('exclude-aliases', True) retain_latest = self.data.get('retain-latest', False) date_threshold = self.data.get('older-than') date_threshold = (date_threshold and parse_date(datetime.utcnow()) - timedelta(days=date_threshold) or None) aliased_versions = () if exclude_aliases: aliased_versions = self.get_aliased_versions(client, r) versions_pager = client.get_paginator('list_versions_by_function') versions_pager.PAGE_ITERATOR_CLASS = query.RetryPageIterator pager = versions_pager.paginate(FunctionName=r['FunctionName']) matched = total = 0 latest_sha = None for page in pager: versions = page.get('Versions') for v in versions: if v['Version'] == '$LATEST': latest_sha = v['CodeSha256'] continue total += v['CodeSize'] if v['FunctionArn'] in aliased_versions: continue if date_threshold and parse_date( v['LastModified']) > date_threshold: continue # Retain numbered version, not required, but it feels like a good thing # to do. else the latest alias will still point. if retain_latest and latest_sha and v[ 'CodeSha256'] == latest_sha: continue matched += v['CodeSize'] self.manager.retry(client.delete_function, FunctionName=v['FunctionArn']) return (matched, total)
def process_value_type(self, sentinel, value, resource): if self.vtype == 'normalize' and isinstance(value, str): return sentinel, value.strip().lower() elif self.vtype == 'expr': sentinel = self.get_resource_value(sentinel, resource) return sentinel, value elif self.vtype == 'integer': try: value = int(str(value).strip()) except ValueError: value = 0 elif self.vtype == 'size': try: return sentinel, len(value) except TypeError: return sentinel, 0 elif self.vtype == 'unique_size': try: return sentinel, len(set(value)) except TypeError: return sentinel, 0 elif self.vtype == 'swap': return value, sentinel elif self.vtype == 'date': return parse_date(sentinel), parse_date(value) elif self.vtype == 'age': if not isinstance(sentinel, datetime.datetime): sentinel = datetime.datetime.now(tz=tzutc()) - timedelta(sentinel) value = parse_date(value) if value is None: # compatiblity value = 0 # Reverse the age comparison, we want to compare the value being # greater than the sentinel typically. Else the syntax for age # comparisons is intuitively wrong. return value, sentinel elif self.vtype == 'cidr': s = parse_cidr(sentinel) v = parse_cidr(value) if (isinstance(s, ipaddress._BaseAddress) and isinstance(v, ipaddress._BaseNetwork)): return v, s return s, v elif self.vtype == 'cidr_size': cidr = parse_cidr(value) if cidr: return sentinel, cidr.prefixlen return sentinel, 0 # Allows for expiration filtering, for events in the future as opposed # to events in the past which age filtering allows for. elif self.vtype == 'expiration': if not isinstance(sentinel, datetime.datetime): sentinel = datetime.datetime.now(tz=tzutc()) + timedelta(sentinel) value = parse_date(value) if value is None: value = 0 return sentinel, value # Allows for comparing version numbers, for things that you expect a minimum version number. elif self.vtype == 'version': s = ComparableVersion(sentinel) v = ComparableVersion(value) return s, v return sentinel, value
def test_parse_date_floor(): # bulk of parse date tests are actually in test_filters assert utils.parse_date(30) is None assert utils.parse_date(1) is None assert utils.parse_date('3000') is None assert utils.parse_date('30') is None