Commit 6d9f1d0f authored by Xavier Thompson's avatar Xavier Thompson

slapconfiguration: Add .jsonschema recipe

[slapconfiguration]
<= slap-connection
recipe = slapos.cookbook:slapconfiguration.jsonschema
jsonschema = ${buildout:directory}/software.cfg.json
parent 5fb78e4c
...@@ -152,6 +152,7 @@ setup(name=name, ...@@ -152,6 +152,7 @@ setup(name=name,
'simplehttpserver = slapos.recipe.simplehttpserver:Recipe', 'simplehttpserver = slapos.recipe.simplehttpserver:Recipe',
'slapconfiguration = slapos.recipe.slapconfiguration:Recipe', 'slapconfiguration = slapos.recipe.slapconfiguration:Recipe',
'slapconfiguration.serialised = slapos.recipe.slapconfiguration:Serialised', 'slapconfiguration.serialised = slapos.recipe.slapconfiguration:Serialised',
'slapconfiguration.jsonschema = slapos.recipe.slapconfiguration:JsonSchema',
'slapconfiguration.jsondump = slapos.recipe.slapconfiguration:JsonDump', 'slapconfiguration.jsondump = slapos.recipe.slapconfiguration:JsonDump',
'squid = slapos.recipe.squid:Recipe', 'squid = slapos.recipe.squid:Recipe',
'sshkeys_authority = slapos.recipe.sshkeys_authority:Recipe', 'sshkeys_authority = slapos.recipe.sshkeys_authority:Recipe',
......
...@@ -29,13 +29,20 @@ import json ...@@ -29,13 +29,20 @@ import json
import logging import logging
import os import os
import jsonschema
import slapos.slap import slapos.slap
from slapos.recipe.librecipe import unwrap from slapos.recipe.librecipe import unwrap
import six import six
from six.moves.configparser import RawConfigParser from six.moves.configparser import RawConfigParser
from netaddr import valid_ipv4, valid_ipv6 from netaddr import valid_ipv4, valid_ipv6
from slapos.util import mkdir_p from slapos.util import (
mkdir_p,
SoftwareReleaseSchema,
SoftwareReleaseSerialisation,
SoftwareReleaseSchemaValidationError,
)
from slapos import format as slapformat from slapos import format as slapformat
from zc.buildout import UserError
logger = logging.getLogger("slapos") logger = logging.getLogger("slapos")
...@@ -263,6 +270,55 @@ class Serialised(Recipe): ...@@ -263,6 +270,55 @@ class Serialised(Recipe):
else: else:
return {} return {}
class JsonSchema(Recipe):
"""
Input:
jsonschema
JSON Schema for the SR.
All instance schemas must be available at the advertised relative paths.
Example:
${buildout:directory}/software.cfg.json
"""
def _schema(self, options):
path = options['jsonschema']
# because SoftwareReleaseSchema accepts only file:// paths
path = path if path.startswith('file://') else 'file://' + path
# because SoftwareReleaseSchema expects the SR url and adds .json
path = path[:-5] if path.endswith('.json') else path
return SoftwareReleaseSchema(path, options['slap-software-type'])
def _validator_for(self, schema):
# adapted from https://python-jsonschema.readthedocs.io/en/stable/faq
validator_cls = jsonschema.validators.validator_for(schema)
validate_original = validator_cls.VALIDATORS["properties"]
def validate_with_defaults(validator, properties, instance, schema):
# Set defaults
for key, subschema in properties.items():
if "default" in subschema:
instance.setdefault(key, subschema["default"])
# Call original properties validator
for error in validate_original(validator, properties, instance, schema):
yield error
# Extend validator class with extended properties validator
return jsonschema.validators.extend(
validator_cls, {"properties" : validate_with_defaults})
def _expandParameterDict(self, options, parameter_dict):
software_schema = self._schema(options)
serialisation = software_schema.getSerialisation(strict=True)
instance_schema = software_schema.getInstanceRequestParameterSchema()
if serialisation == SoftwareReleaseSerialisation.JsonInXml:
parameter_dict = unwrap(parameter_dict)
instance = parameter_dict if isinstance(parameter_dict, dict) else {}
validator = self._validator_for(instance_schema)(instance_schema)
errors = list(validator.iter_errors(instance))
if errors:
err = SoftwareReleaseSchemaValidationError(errors).format_error(indent=2)
msg = "Invalid parameters:\n" + err
raise UserError(msg)
options['configuration'] = instance
return instance
class JsonDump(Recipe): class JsonDump(Recipe):
def __init__(self, buildout, name, options): def __init__(self, buildout, name, options):
parameter_dict = self.fetch_parameter_dict(options) parameter_dict = self.fetch_parameter_dict(options)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment