def test_seek_regex_in_dict_value_nested_dict(self): haystack = dict(search_key=dict(something='value')) key_name = 'something' needle = 'val.*' expected = ['value'] self.assertEqual(seek_regex_key_in_dict_values(haystack, key_name, needle), expected)
def its_key_condition_be_value(_step_obj, key, condition, value, stash=Null, depth=0): if condition not in ('must', 'must not'): raise TerraformComplianceNotImplemented( 'This step only accepts "must" and "must not" as a condition.') if stash is Null: stash = _step_obj.context.stash if not stash or stash is Null: Error( _step_obj, 'No entities found for this step to process. Check your filtering steps in this scenario.' ) return False found_values = [] for entity in stash: if isinstance(entity, dict): found_values.extend( seek_regex_key_in_dict_values(entity, key, value)) elif isinstance(entity, list): for element in entity: found_values.extend( its_key_condition_be_value(_step_obj, key, condition, element, entity, depth + 1)) elif isinstance(entity, (str, int, bool)) and (str(entity).lower == key.lower or str(entity) == value.lower): found_values.append(entity) # Return the values to the parent call. if depth > 0: return found_values condition = condition == 'must' found_values = [values for values in found_values if values is not None] obj_address = _step_obj.context.name if hasattr(_step_obj.context, 'address'): obj_address = _step_obj.context.address elif hasattr(_step_obj.context, 'addresses'): obj_address = ', '.join(_step_obj.context.addresses) if found_values and not condition: Error( _step_obj, 'Found {}({}) in {} property of {}.'.format( value, ', '.join(found_values), key, obj_address)) elif not found_values and condition: Error( _step_obj, 'Can not find {} in {} property of {}.'.format( value, key, obj_address)) return True
def its_value_condition_match_the_search_regex_regex(_step_obj, condition, search_regex, _stash=None): def fail(condition): text = 'matches' if condition == 'must not' else 'does not match' raise Failure('{} property in {} {} {} with {} regex. ' 'It is set to {}.'.format( _step_obj.context.property_name, _step_obj.context.name, _step_obj.context.type, text, regex, values)) regex = r'{}'.format(search_regex) values = _step_obj.context.stash if _stash is None else _stash if type(values) is str or type(values) is int or type(values) is bool: matches = re.match(regex, str(values), flags=re.IGNORECASE) if (condition == 'must' and matches is None) or (condition == "must not" and matches is not None): fail(condition) elif type(values) is list: for value in values: its_value_condition_match_the_search_regex_regex( _step_obj, condition, search_regex, value) elif type(values) is dict: if values.get('values') is not None: values = its_value_condition_match_the_search_regex_regex( _step_obj, condition, search_regex, values['values']) else: values = seek_regex_key_in_dict_values( values, _step_obj.context.property_name, search_regex) if (condition == 'must' and values == []) or (condition == "must not" and values != []): fail(condition)
def it_condition_contain_something(_step_obj, something): prop_list = [] if _step_obj.context.type in ('resource', 'data'): for resource in _step_obj.context.stash: if type(resource) is not dict: resource = { 'values': resource, 'address': resource, 'type': _step_obj.context.name } values = resource.get('values', resource.get('expressions', {})) found_value = Null found_key = Null if type(values) is dict: found_key = values.get(something, seek_key_in_dict(values, something)) if type(found_key) is not list: found_key = [{something: found_key}] if len(found_key): found_key = found_key[0] if len( found_key) == 1 else found_key if type(found_key) is dict: found_value = jsonify( found_key.get(something, found_key)) else: found_value = found_key elif type(values) is list: found_value = [] for value in values: if type(value) is dict: # First search in the keys found_key = seek_key_in_dict(value, something) # Then search in the values with 'key' if not found_key: found_key = seek_regex_key_in_dict_values( value, 'key', something) if found_key: found_key = found_key[0] found_value = value.get('value') break if found_key is not Null and len(found_key): found_key = found_key[0] if len( found_key) == 1 else found_key if type(found_key) is dict: found_value.append( jsonify(found_key.get(something, found_key))) if type(found_value) is dict and 'constant_value' in found_value: found_value = found_value['constant_value'] if found_value is not Null and found_value != [] and found_value != '' and found_value != {}: prop_list.append({ 'address': resource['address'], 'values': found_value, 'type': _step_obj.context.name }) elif 'must' in _step_obj.context_sensitive_sentence: raise Failure('{} ({}) does not have {} property.'.format( resource['address'], resource.get('type', ''), something)) if prop_list: _step_obj.context.stash = prop_list _step_obj.context.property_name = something return True skip_step(_step_obj, resource=_step_obj.context.name, message='Can not find any {} property for {} resource in ' 'terraform plan.'.format(something, _step_obj.context.name)) elif _step_obj.context.type == 'provider': values = seek_key_in_dict(_step_obj.context.stash, something) if values: _step_obj.context.stash = values _step_obj.context.property_name = something return True skip_step( _step_obj, resource=_step_obj.context.name, message='Skipping the step since {} type does not have {} property.'. format(_step_obj.context.type, something))
def it_must_contain_something(_step_obj, something, inherited_values=Null): prop_list = [] _step_obj.context.stash = inherited_values if inherited_values is not Null else _step_obj.context.stash if _step_obj.context.type in ('resource', 'data'): for resource in _step_obj.context.stash: if not isinstance(resource, dict) \ or 'values' not in resource \ or 'address' not in resource \ or 'type' not in resource: resource = { 'values': resource, 'address': resource, 'type': _step_obj.context.name } values = resource.get('values', resource.get('expressions', {})) if not values: values = seek_key_in_dict(resource, something) found_value = Null found_key = Null if isinstance(values, dict): found_key = values.get(something, seek_key_in_dict(values, something)) if not isinstance(found_key, list): found_key = [{something: found_key}] if len(found_key): found_key = found_key[0] if len( found_key ) == 1 and something in found_key[0] else found_key if isinstance(found_key, dict): found_value = jsonify( found_key.get(something, found_key)) found_value = found_value if found_value not in ( [], '') else found_key else: found_value = found_key elif isinstance(values, list): found_value = [] for value in values: if isinstance(value, dict): # First search in the keys found_key = seek_key_in_dict(value, something) # Then search in the values with 'key' if not found_key: found_key = seek_regex_key_in_dict_values( value, 'key', something) if found_key: found_key = found_key[0] found_value = value.get('value') break elif isinstance(value, list): found_key, found_value = it_must_contain_something( _step_obj, something, value) if found_key is not Null and len(found_key): found_key = found_key[0] if len( found_key) == 1 else found_key if isinstance(found_key, dict): found_value.append( jsonify(found_key.get(something, found_key))) if isinstance(found_value, dict) and 'constant_value' in found_value: found_value = found_value['constant_value'] if found_value is not Null and found_value != [] and found_value != '' and found_value != {}: prop_list.append({ 'address': resource['address'], 'values': found_value, 'type': _step_obj.context.name }) else: Error( _step_obj, '{} ({}) does not have {} property.'.format( resource['address'], resource.get('type', ''), something)) if prop_list: _step_obj.context.stash = prop_list _step_obj.context.property_name = something return something, prop_list elif _step_obj.context.type == 'provider': for provider_data in _step_obj.context.stash: values = seek_key_in_dict(provider_data, something) if values: _step_obj.context.stash = values _step_obj.context.property_name = something _step_obj.context.address = '{}.{}'.format( provider_data.get('name', _step_obj.context.addresses), provider_data.get('alias', "\b")) return True else: Error( _step_obj, '{} {} does not have {} property.'.format( _step_obj.context.addresses, _step_obj.context.type, something)) Error( _step_obj, '{} {} does not have {} property.'.format( _step_obj.context.addresses, _step_obj.context.type, something))
def it_does_not_have_something(_step_obj, something, inherited_values=Null): prop_list = [] _step_obj.context.stash = inherited_values if inherited_values is not Null else _step_obj.context.stash if _step_obj.context.type in ('resource', 'data'): for resource in _step_obj.context.stash: if not isinstance(resource, dict) \ or 'values' not in resource \ or 'address' not in resource \ or 'type' not in resource: resource = {'values': resource, 'address': resource, 'type': _step_obj.context.name} values = resource.get('values', resource.get('expressions', {})) if not values: values = seek_key_in_dict(resource, something) found_value = Null found_key = Null if isinstance(values, dict): found_key = values.get(something, seek_key_in_dict(values, something)) if not isinstance(found_key, list): found_key = [{something: found_key}] if len(found_key): found_key = found_key[0] if len(found_key) == 1 and something in found_key[0] else found_key if isinstance(found_key, dict): found_value = jsonify(found_key.get(something, found_key)) else: found_value = found_key elif isinstance(values, list): found_value = [] for value in values: if isinstance(value, dict): # First search in the keys found_key = seek_key_in_dict(value, something) # Then search in the values with 'key' if not found_key: found_key = seek_regex_key_in_dict_values(value, 'key', something) if found_key: found_key = found_key[0] found_value = value.get('value') break elif isinstance(value, list): found_key, found_value = it_has_something(_step_obj, something, value) if found_key is not Null and len(found_key): found_key = found_key[0] if len(found_key) == 1 else found_key if isinstance(found_key, dict): found_value.append(jsonify(found_key.get(something, found_key))) if isinstance(found_value, dict) and 'constant_value' in found_value: found_value = found_value['constant_value'] if found_value is not Null and found_value != [] and found_value != '' and found_value != {}: prop_list.append(resource['address']) prop_list = [resource for resource in _step_obj.context.stash if resource['address'] not in prop_list] _step_obj.context.property_name = something if prop_list: _step_obj.context.stash = prop_list return something, prop_list if _step_obj.state != Step.State.FAILED: skip_step(_step_obj, resource=_step_obj.context.name, message='All objects ({}) coming from previous step has {} ' 'property.'.format(_step_obj.context.name, something)) elif _step_obj.context.type == 'provider': stash = [] for provider_data in _step_obj.context.stash: values = seek_key_in_dict(provider_data, something) if values: return False else: stash.append(provider_data) if stash: _step_obj.context.stash = stash _step_obj.context.property_name = something return True return True
def it_contains_something_old(_step_obj, something, inherited_values=Null): console_write("\t{} {}: {}".format(Defaults().warning_icon, Defaults().warning_colour('WARNING'), Defaults().info_colour('"When it contains {}" step functionality will be changed' ' on future versions and the functionality will be same ' 'as "When it has {}" step. Please use the ' 'latter.'.format(something, something)))) prop_list = [] _step_obj.context.stash = inherited_values if inherited_values is not Null else _step_obj.context.stash if _step_obj.context.type in ('resource', 'data'): for resource in _step_obj.context.stash: if not isinstance(resource, dict) \ or 'values' not in resource \ or 'address' not in resource \ or 'type' not in resource: resource = {'values': resource, 'address': resource, 'type': _step_obj.context.name} values = resource.get('values', resource.get('expressions', {})) if not values: values = seek_key_in_dict(resource, something) found_value = Null found_key = Null if isinstance(values, dict): found_key = values.get(something, seek_key_in_dict(values, something)) if not isinstance(found_key, list): found_key = [{something: found_key}] if len(found_key): found_key = found_key[0] if len(found_key) == 1 and something in found_key[0] else found_key if isinstance(found_key, dict): found_value = jsonify(found_key.get(something, found_key)) else: found_value = found_key elif isinstance(values, list): found_value = [] for value in values: if isinstance(value, dict): # First search in the keys found_key = seek_key_in_dict(value, something) # Then search in the values with 'key' if not found_key: found_key = seek_regex_key_in_dict_values(value, 'key', something) if found_key: found_key = found_key[0] found_value = value.get('value') break elif isinstance(value, list): found_key, found_value = it_contains_something_old(_step_obj, something, value) if found_key is not Null and len(found_key): found_key = found_key[0] if len(found_key) == 1 else found_key if isinstance(found_key, dict): found_value.append(jsonify(found_key.get(something, found_key))) if isinstance(found_value, dict) and 'constant_value' in found_value: found_value = found_value['constant_value'] if found_value is not Null and found_value != [] and found_value != '' and found_value != {}: prop_list.append({'address': resource['address'], 'values': found_value, 'type': _step_obj.context.name}) if prop_list: _step_obj.context.stash = prop_list _step_obj.context.property_name = something return something, prop_list if _step_obj.state != Step.State.FAILED: skip_step(_step_obj, resource=_step_obj.context.name, message='Can not find any {} property for {} resource in ' 'terraform plan.'.format(something, _step_obj.context.name)) elif _step_obj.context.type == 'provider': for provider_data in _step_obj.context.stash: values = seek_key_in_dict(provider_data, something) if values: _step_obj.context.stash = values _step_obj.context.property_name = something _step_obj.context.address = '{}.{}'.format(provider_data.get('name', _step_obj.context.addresses), provider_data.get('alias', "\b")) return True if _step_obj.state != Step.State.FAILED: skip_step(_step_obj, resource=_step_obj.context.name, message='Skipping the step since {} type does not have {} property.'.format(_step_obj.context.type, something))
def it_has_something(_step_obj, something, inherited_values=Null): prop_list = [] _step_obj.context.stash = inherited_values if inherited_values is not Null else _step_obj.context.stash if _step_obj.context.type in ('resource', 'data'): for resource in _step_obj.context.stash: if not isinstance(resource, dict) \ or 'values' not in resource \ or 'address' not in resource \ or 'type' not in resource: resource = {'values': resource, 'address': resource, 'type': _step_obj.context.name} values = resource.get('values', resource.get('expressions', {})) if not values: values = seek_key_in_dict(resource, something) found_value = Null found_key = Null if isinstance(values, dict): found_key = values.get(something, seek_key_in_dict(values, something)) if not isinstance(found_key, list): found_key = [{something: found_key}] if len(found_key): found_key = found_key[0] if len(found_key) == 1 and something in found_key[0] else found_key if isinstance(found_key, dict): found_value = jsonify(found_key.get(something, found_key)) else: found_value = found_key elif isinstance(values, list): found_value = [] for value in values: if isinstance(value, dict): # First search in the keys found_key = seek_key_in_dict(value, something) # Then search in the values with 'key' if not found_key: found_key = seek_regex_key_in_dict_values(value, 'key', something) if found_key: found_key = found_key[0] found_value = value.get('value') break elif isinstance(value, list): found_key, found_value = it_has_something(_step_obj, something, value) if found_key is not Null and len(found_key): found_key = found_key[0] if len(found_key) == 1 else found_key if isinstance(found_key, dict): found_value.append(jsonify(found_key.get(something, found_key))) if isinstance(found_value, dict) and 'constant_value' in found_value: found_value = found_value['constant_value'] if found_value not in (Null, [], '', {}): prop_list.append(resource) if prop_list: _step_obj.context.stash = prop_list _step_obj.context.property_name = something return something, prop_list if _step_obj.state != Step.State.FAILED: skip_step(_step_obj, resource=_step_obj.context.name, message='Can not find any {} property for {} resource in ' 'terraform plan.'.format(something, _step_obj.context.name)) elif _step_obj.context.type == 'provider': for provider_data in _step_obj.context.stash: values = seek_key_in_dict(provider_data, something) if values: _step_obj.context.stash = provider_data _step_obj.context.property_name = something _step_obj.context.address = '{}.{}'.format(provider_data.get('name', _step_obj.context.addresses), provider_data.get('alias', "\b")) return True if _step_obj.state != Step.State.FAILED: skip_step(_step_obj, resource=_step_obj.context.name, message='Skipping the step since {} type does not have {} property.'.format(_step_obj.context.type, something))
def it_condition_contain_something(_step_obj, something): prop_list = [] if _step_obj.context.type == 'resource': for resource in _step_obj.context.stash: if type(resource) is not dict: resource = { 'values': resource, 'address': resource, 'type': _step_obj.context.name } values = resource.get('values', {}) found_value = None found_key = None if type(values) is dict: found_key = seek_key_in_dict(values, something) if len(found_key): found_key = found_key[0] if type(found_key) is dict: found_value = jsonify(found_key[something]) elif type(values) is list: for value in values: if type(value) is dict: # First search in the keys found_key = seek_key_in_dict(value, something) # Then search in the values with 'key' if not found_key: found_key = seek_regex_key_in_dict_values( value, 'key', something) if found_key: found_key = found_key[0] found_value = value.get('value') break else: raise TerraformComplianceInternalFailure( 'Unexpected value type {}. {}'.format( type(value), value)) if found_key: prop_list.append({ 'address': resource['address'], 'values': found_value, 'type': _step_obj.context.name }) elif 'must' in _step_obj.context_sensitive_sentence: raise Failure('{} ({}) does not have {} property.'.format( resource['address'], resource.get('type', ''), something)) if prop_list: _step_obj.context.stash = prop_list _step_obj.context.property_name = something return True skip_step(_step_obj, resource=_step_obj.context.name, message='Can not find any {} property for {} resource in ' 'terraform plan.'.format(something, _step_obj.context.name)) elif _step_obj.context.type == 'provider': values = seek_key_in_dict(_step_obj.context.stash, something) if values: _step_obj.context.stash = values _step_obj.context.property_name = something return True skip_step( _step_obj, resource=_step_obj.context.name, message='Skipping the step since {} type does not have {} property.'. format(_step_obj.context.type, something))
def its_key_condition_be_value(_step_obj, key, condition, value, stash=Null, depth=0): if condition not in ('must', 'must not'): raise TerraformComplianceNotImplemented( 'This step only accepts "must" and "must not" as a condition.') condition = condition == 'must' if stash is Null: stash = _step_obj.context.stash if not stash or stash is Null: Error( _step_obj, 'No entities found for this step to process. Check your filtering steps in this scenario.' ) return False obj_address = _step_obj.context.name if hasattr(_step_obj.context, 'address'): obj_address = _step_obj.context.address elif hasattr(_step_obj.context, 'addresses'): obj_address = ', '.join(_step_obj.context.addresses) found_values = [] for entity in stash: if isinstance(entity, dict): found_value = seek_regex_key_in_dict_values(entity, key, value) if not found_value and condition: Error( _step_obj, 'Can not find {} in {} property of {}.'.format( value, key, entity.get('address', obj_address))) Error(_step_obj, 'Can not find {} in {} property of {}.'.format( value, key, obj_address)) # legacy error message elif found_value and not condition: Error( _step_obj, 'Found {}({}) in {} property of {}.'.format( value, ', '.join(found_values), key, obj_address)) found_values.extend(found_value) elif isinstance(entity, list): dict_entity = {f'not_{str(key)}': entity} found_value = seek_regex_key_in_dict_values( dict_entity, key, value) if not found_value and condition: Error( _step_obj, 'Can not find {} in {} property of {}.'.format( value, key, obj_address)) elif found_value and not condition: Error( _step_obj, 'Found {}({}) in {} property of {}.'.format( value, ', '.join(found_values), key, obj_address)) found_values.extend(found_value) elif isinstance(entity, (str, int, bool)): # raise error because you don't have a {key: value} if (str(entity).lower == key.lower or str(entity) == value.lower) and condition: Error( _step_obj, 'Value {} found in {} property of {}, but is not in {{key: value}} format.' .format(value, key, obj_address)) if condition: Error( _step_obj, 'Can not find {} in {} property of {}.'.format( value, key, obj_address)) _step_obj.context.stash = found_values return True