Sceptre / sceptre

Build better AWS infrastructure
https://docs.sceptre-project.org
Other
1.49k stars 313 forks source link

Reimplement #1443 - parameter Schema Validation #1468

Closed alex-harvey-z3q closed 4 months ago

alex-harvey-z3q commented 6 months ago

Subject of the issue

In already-resolved and merged issue #1443 we implemented parameter validation. In Slack, we then discussed a preferred implementation of using instead a parameter Schema Validator.

This issue tracks possible reimplementation of this feature.

Your environment

More info

% git show f3b7e51fee1ff78a457f20b5dddac91254a5fdf2 
alex-harvey-z3q commented 6 months ago

After investigating this, I have concluded that @zaro0508 's proposal to use a schema validator is not straightforward and probably not worth it. The Schema Validator we are already using in the project is jsonschema and this is already in use in the template handler. However, it does not easily lend itself to our use case where we need to handle the edge case of a parameter being an instance of the Resolver class.

I tried some implementations similar to the following:

import jsonschema

# Define the schema
PARAMETERS_SCHEMA = {
    "type": "object",
    "additionalProperties": {
        "oneOf": [
            {"type": "string"},
            {"type": "object", "properties": {"type": {"enum": ["Resolver"]}}, "required": ["type"]},
            {
                "type": "array",
                "items": {
                    "oneOf": [
                        {"type": "string"},
                        {"type": "object", "properties": {"type": {"enum": ["Resolver"]}}, "required": ["type"]}
                    ]
                }
            }
        ]
    }
}

class Stack:
    def __init__(self, name: str, parameters: dict):
        self.name = name
        self.parameters = parameters

    def _validate_parameters(self, parameters):
        """Private method to validate parameters against the schema."""
        try:
            jsonschema.validate(instance=parameters, schema=PARAMETERS_SCHEMA)
        except jsonschema.ValidationError as e:
            raise ValueError(f"Invalid parameters: {e.message}")

    def set_parameters(self, config):
        """Method to set parameters with validation."""
        parameters = config.get("parameters", {})
        self._validate_parameters(parameters)
        self.parameters = parameters

# Example usage
config = {
    "parameters": {
        "param1": "value1",
        "param2": {"type": "Resolver"},
        "param3": ["value2", {"type": "Resolver"}]
    }
}

stack_name = "example_stack"
stack = Stack(name=stack_name, parameters={})
stack.set_parameters(config)
print(stack.parameters)

These however led to the unit tests failing with:

E           jsonschema.exceptions.ValidationError: !FakeResolver is not of type 'string'       

Further digging suggested I might have to somehow modify the Resolver class like

class Resolver:
    def __init__(self, name):
        self.type = "Resolver"
        self.name = name

However I could not get that to work either and it led to more problems.

The second point is use of the Schema Validator to cast parameters to strings does not seem to be something they can do.

All up and for these reasons, I gave up on this approach and simply looked at tweaking the _ensure_parameters method to also cast to string where applicable.