From 12e8a0adebebd6fb7a9231aa810f77e1366a12eb Mon Sep 17 00:00:00 2001 From: Sylvain Laperche Date: Tue, 26 Nov 2019 10:10:28 +0100 Subject: [PATCH] buildchain/targets: add a SLS Renderer For now we define a SLS as a YAML file with optional shebang and list of imports. Since both YAML and SLS renderer uses YAML dump, we extract it into an helper. Refs: #2070 Signed-off-by: Sylvain Laperche --- buildchain/buildchain/targets/__init__.py | 6 ++- buildchain/buildchain/targets/serialize.py | 52 ++++++++++++++++------ 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/buildchain/buildchain/targets/__init__.py b/buildchain/buildchain/targets/__init__.py index 5b89475c1d..f1bdc81471 100644 --- a/buildchain/buildchain/targets/__init__.py +++ b/buildchain/buildchain/targets/__init__.py @@ -15,7 +15,9 @@ from buildchain.targets.repository import ( Repository, RPMRepository, DEBRepository ) -from buildchain.targets.serialize import Renderer, SerializedData, YAMLDocument +from buildchain.targets.serialize import ( + Renderer, SerializedData, SaltState, YAMLDocument +) from buildchain.targets.template import TemplateFile # For mypy, see `--no-implicit-reexport` documentation. @@ -29,6 +31,6 @@ 'Package', 'RPMPackage', 'DEBPackage', 'RemoteImage', 'Repository', 'RPMRepository', 'DEBRepository', - 'Renderer', 'SerializedData', 'YAMLDocument', + 'Renderer', 'SerializedData', 'SaltState', 'YAMLDocument', 'TemplateFile', ] diff --git a/buildchain/buildchain/targets/serialize.py b/buildchain/buildchain/targets/serialize.py index 8ab1ee89ee..6760e3f8ae 100644 --- a/buildchain/buildchain/targets/serialize.py +++ b/buildchain/buildchain/targets/serialize.py @@ -3,10 +3,11 @@ """Targets to write files from Python objects.""" import base64 +import collections import enum import json from pathlib import Path -from typing import Any, Callable, Dict, Mapping +from typing import Any, Callable, Dict, IO, Mapping import yaml @@ -35,25 +36,26 @@ def render_envfile(variables: Mapping[str, str], filepath: Path) -> None: def render_yaml(data: Any, filepath: Path) -> None: """Serialize an object as YAML to a given file path.""" with filepath.open('w', encoding='utf-8') as fp: - dumper = yaml.SafeDumper(fp, sort_keys=False) # type: ignore - dumper.add_representer( # type: ignore - YAMLDocument.Literal, _literal_representer - ) - dumper.add_representer( # type: ignore - YAMLDocument.ByteString, _bytestring_representer - ) - try: - dumper.open() # type: ignore - dumper.represent(data) # type: ignore - dumper.close() # type: ignore - finally: - dumper.dispose() # type: ignore + _yaml_dump(data, fp) + + +def render_sls(sls: 'SaltState', filepath: Path) -> None: + """Serialize a Salt state to a given file path.""" + with filepath.open('w', encoding='utf-8') as fp: + if sls.shebang: + fp.write(sls.shebang) + fp.write('\n'*2) + if sls.imports: + fp.write('\n'.join(sls.imports)) + fp.write('\n'*2) + _yaml_dump(sls.content, fp) class Renderer(enum.Enum): """Supported rendering methods for `SerializedData` targets.""" JSON = 'JSON' ENV = 'ENV' + SLS = 'SLS' YAML = 'YAML' @@ -64,6 +66,7 @@ class SerializedData(base.AtomicTarget): Renderer.JSON: render_json, Renderer.ENV: render_envfile, Renderer.YAML: render_yaml, + Renderer.SLS: render_sls, } def __init__( @@ -142,6 +145,11 @@ def bytestring(cls, value: bytes) -> 'YAMLDocument.ByteString': return cls.ByteString(value) +SaltState = collections.namedtuple( + 'SaltState', ['content', 'shebang', 'imports'] +) + + def _literal_representer(dumper: yaml.BaseDumper, data: Any) -> Any: scalar = yaml.representer.SafeRepresenter.represent_str( # type: ignore dumper, data @@ -156,4 +164,20 @@ def _bytestring_representer(dumper: yaml.BaseDumper, data: Any) -> Any: ) +def _yaml_dump(data: Any, fp: IO[Any]) -> None: + dumper = yaml.SafeDumper(fp, sort_keys=False) # type: ignore + dumper.add_representer( # type: ignore + YAMLDocument.Literal, _literal_representer + ) + dumper.add_representer( # type: ignore + YAMLDocument.ByteString, _bytestring_representer + ) + try: + dumper.open() # type: ignore + dumper.represent(data) # type: ignore + dumper.close() # type: ignore + finally: + dumper.dispose() # type: ignore + + # }}}