예제 #1
0
 def apply_default_apps(self, default_apps):
     # These annoying names are to prevent conflicts with fields in the default app definitions
     PLACEMENT_KEY = 'default_placement'
     CONDITION_KEY = 'default_condition'
     for section in default_apps:
         if section not in self._data:
             self._data[section] = []
         insert_idx = 0
         for app in default_apps[section]:
             placement = app.get(PLACEMENT_KEY, 'before')
             if PLACEMENT_KEY in app:
                 del app[PLACEMENT_KEY]
             if CONDITION_KEY in app:
                 condition = app[CONDITION_KEY]
                 del app[CONDITION_KEY]
                 template = Template()
                 tmp_vars = dict(VARS=dict(self._vars))
                 # Continue to next item if we have a condition and it evaluated to False
                 if not template.evaluate_condition(condition, tmp_vars):
                     continue
             if placement in ('before', 'pre'):
                 self._data[section].insert(insert_idx, app)
                 insert_idx += 1
             elif placement in ('after', 'post'):
                 self._data[section].append(app)
             else:
                 raise DeployConfigError(
                     'invalid default app placement: %s' % placement)
 def validate_check_type(self, value, expected_type=None):
     '''
     Determine the type of the passed value
     '''
     if isinstance(value, list):
         return 'list'
     if isinstance(value, dict):
         return 'dict'
     if isinstance(value, bool):
         return 'bool'
     if isinstance(value, int):
         return 'int'
     if isinstance(value, float):
         return 'float'
     if isinstance(value, str):
         # Values from variables always come in as a string, so we need special
         # logic to determine their actual type based on the field type
         try:
             if expected_type == 'float' and float(value) is not None:
                 return 'float'
             if expected_type == 'int' and int(value) is not None:
                 return 'int'
             if expected_type == 'bool' and self.convert_bool(
                     value) is not None:
                 return 'bool'
         except Exception:
             pass
         return 'str'
     raise DeployConfigError('unsupported type: %s' % type(value))
def app_validate_fields(app, app_index, output_plugins):
    try:
        unmatched = {}
        plugins_used = []
        for plugin in output_plugins:
            if plugin.is_needed(app):
                plugins_used.append(plugin.NAME)
                plugin_unmatched = plugin.validate_fields(app)
                unmatched[plugin.NAME] = plugin_unmatched
        # Compare unmatched from all plugins and compile final list
        # We are looking for fields that were unmatched by all active plugins,
        # with the added twist that we need to match what may be an unmatched
        # sub-field in one plugin and a top-level field in another.
        final_unmatched = []
        for plugin in unmatched:
            for entry in unmatched[plugin]:
                entry_keep = True
                # Check entry against unmatched entries from other plugins
                for plugin2 in unmatched:
                    if plugin2 == plugin:
                        continue
                    plugin2_matched = False
                    for entry2 in unmatched[plugin2]:
                        # Check for exact match or a parent/sub-field match
                        if entry2 == entry or entry.startswith('%s.' % entry2):
                            plugin2_matched = True
                            break
                    if not plugin2_matched:
                        entry_keep = False
                        break
                # Add entry to final list if it existed for all plugins
                if entry_keep and entry not in final_unmatched:
                    final_unmatched.append(entry)
        if final_unmatched:
            raise DeployConfigError('found the following unknown fields: %s' %
                                    ', '.join(sorted(final_unmatched)))
        if not plugins_used:
            raise DeployConfigError(
                'no output plugins were available for provided fields')
    except DeployConfigError as e:
        DISPLAY.display('Failed to validate fields in deploy config: %s' %
                        str(e))
        sys.exit(1)
예제 #4
0
 def load(self, path):
     self._path = path
     try:
         self._display.v('Loading deploy config file %s' % path)
         with open(path) as f:
             self._data = yaml_load(f.read())
         if not isinstance(self._data, dict):
             raise DeployConfigError(
                 'YAML file should contain a top-level dict', path=path)
         if 'version' in self._data:
             self._version = self._data['version']
             del self._data['version']
         for k, v in self._data.items():
             # Wrap the config in a list if it's not already a list
             # This makes it easier to process
             if not isinstance(v, list):
                 self._data[k] = [v]
     except DeployConfigError:
         raise
     except Exception as e:
         raise DeployConfigError('unexpected exception: %s' % str(e))
 def validate_fields(self, app):
     '''
     Validate the provided app config against plugin field definitions
     '''
     # Check that all required top-level fields are provided
     req_fields = self.get_required_fields()
     for field in req_fields:
         if field not in app:
             raise DeployConfigError("required field '%s' not defined" %
                                     field)
     # Check field/subfield types, required, and if field is locked
     unmatched = []
     for field, value in app.items():
         if self.has_field(field):
             if self.is_field_locked(field):
                 raise DeployConfigError(
                     "the field '%s' has been locked by the plugin config and cannot be overridden"
                     % field)
             field_unmatched = self._fields[self._section][field].validate(
                 value)
             unmatched.extend(field_unmatched)
         else:
             unmatched.append(field)
     return unmatched
예제 #6
0
 def validate_sections(self, valid_sections):
     for section in self._data:
         if section not in valid_sections:
             raise DeployConfigError(
                 "section name '%s' is not valid for available plugins" %
                 section)
    def validate(self, value, use_subtype=False):
        '''
        Validate passed value against field config
        '''
        unmatched = []
        if value is None:
            return unmatched
        field_type = self.type
        if use_subtype:
            # Use the field subtype
            field_type = self.subtype
        # Nothing to validate if no field type is specified
        if field_type is None:
            return unmatched
        value_type = self.validate_check_type(value, field_type)
        if value_type != field_type:
            # TODO: replace this with the ability to specify multiple types for a field
            # Hack to allow an int value to satisfy a float
            if field_type == 'float' and value_type == 'int':
                pass
            else:
                raise DeployConfigError(
                    "value for field '%s' is wrong type, expected '%s' and got: %s"
                    % (self.get_full_name(), field_type, value_type))
        if field_type == 'list' and self.subtype is not None:
            # Validate each list item separately if a field subtype is specified
            for value_item in value:
                # Use field's subtype for list items
                item_unmatched = self.validate(value_item, use_subtype=True)
                unmatched.extend(item_unmatched)
        elif field_type == 'dict':
            # Recursively validate sub-field values
            if self.fields is not None:
                for k, v in value.items():
                    if k not in self.fields or not self.fields[
                            k].is_valid_for_config_version():
                        unmatched.append('%s.%s' % (self.get_full_name(), k))
                        continue
                    field_unmatched = self.fields[k].validate(v)
                    unmatched.extend(field_unmatched)
                # Check for required and locked sub-fields
                for tmp_field_name, tmp_field in self.fields.items():
                    if tmp_field.required and value.get(
                            tmp_field_name,
                            None) is None and tmp_field.default is None:
                        raise DeployConfigError(
                            "field '%s' is required, but no value provided" %
                            tmp_field.get_full_name())
                    if tmp_field.locked and value.get(tmp_field_name,
                                                      None) is not None:
                        raise DeployConfigError(
                            "field '%s' is locked, but a value was provided" %
                            tmp_field.get_full_name())
            # Validate free-form value type
            elif self.subtype is not None and not use_subtype:
                for value_item in value.values():
                    item_unmatched = self.validate(value_item,
                                                   use_subtype=True)
                    unmatched.extend(item_unmatched)
        elif field_type == 'str':
            if self.validation_pattern is not None:
                if not re.match(self.validation_pattern, value):
                    raise DeployConfigError(
                        "value for field '%s' did not match validation pattern: %s"
                        % (self.get_full_name(), self.validation_pattern))

        return unmatched