kmt 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
kmt/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .kmt import main
2
+
3
+ __all__ = ["main"]
kmt/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .kmt import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
kmt/exception.py ADDED
@@ -0,0 +1,8 @@
1
+ """
2
+ Contains common exceptions used by kmt
3
+ """
4
+
5
+ class ValidationException(Exception):
6
+ """
7
+ Validation of some condition failed
8
+ """
kmt/handlers.py ADDED
@@ -0,0 +1,124 @@
1
+ import ttast
2
+ import logging
3
+ import copy
4
+ import yaml
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ class HandlerMetadata(ttast.Handler):
9
+ def parse(self):
10
+ pass
11
+
12
+ def is_per_block(self):
13
+ return True
14
+
15
+ def run(self, block):
16
+ pass
17
+
18
+ class SupportHandlerExtractGVKN(ttast.SupportHandler):
19
+ def parse(self):
20
+ pass
21
+
22
+ def pre(self, block):
23
+ if block is None:
24
+ return
25
+
26
+ # Best effort extract of Group, Version, Kind, Name from the object, if
27
+ # it is yaml
28
+
29
+ try:
30
+ manifest = yaml.safe_load(block.text)
31
+ except yaml.YAMLError as exc:
32
+ logger.debug(f"ExtractGVKN: Could not parse input object: {exc}")
33
+ return
34
+
35
+ # Make sure we're working with a dictionary
36
+ if not isinstance(manifest, dict):
37
+ logger.debug(f"ExtractGVKN: Parsed yaml is not a dictionary")
38
+ return
39
+
40
+ api_version = ""
41
+ group = ""
42
+ version = ""
43
+ name = ""
44
+ kind = ""
45
+
46
+ # api version
47
+ api_version = manifest.get("apiVersion", "")
48
+
49
+ # group and version
50
+ if api_version != "":
51
+ split = api_version.split("/")
52
+
53
+ if len(split) == 1:
54
+ version = split[0]
55
+ elif len(split) == 2:
56
+ group = split[0]
57
+ version = split[1]
58
+
59
+ # Kind
60
+ kind = manifest.get("kind")
61
+
62
+ # Name
63
+ metadata = manifest.get("metadata")
64
+ if isinstance(metadata, dict):
65
+ name = metadata.get("name", "")
66
+
67
+ block.meta["k8s_name"] = name
68
+ block.meta["k8s_kind"] = kind
69
+ block.meta["k8s_group"] = group
70
+ block.meta["k8s_version"] = version
71
+ block.meta["k8s_api_version"] = api_version
72
+
73
+
74
+ def post(self, block):
75
+ pass
76
+
77
+ class SupportHandlerSplitYaml(ttast.SupportHandler):
78
+ def parse(self):
79
+ pass
80
+
81
+ def pre(self, block):
82
+ pass
83
+
84
+ def post(self, block):
85
+ if block is None:
86
+ return
87
+
88
+ lines = block.text.splitlines()
89
+ documents = []
90
+ current = []
91
+
92
+ for line in lines:
93
+
94
+ # Determine if we have the beginning of a yaml document
95
+ if line == "---" and len(current) > 0:
96
+ documents.append("\n".join(current))
97
+ current = []
98
+
99
+ current.append(line)
100
+
101
+ documents.append("\n".join(current))
102
+
103
+ # Strip each document
104
+ documents = [x.strip() for x in documents]
105
+
106
+ # If we have a single document and it's the same as the
107
+ # original block, just exit
108
+ if len(documents) == 1 and documents[0] == block.text:
109
+ return
110
+
111
+ # Add all documents to the pipeline text block list
112
+ new_blocks = [ttast.TextBlock(item) for item in documents]
113
+ for new_block in new_blocks:
114
+ new_block.meta = copy.deepcopy(block.meta)
115
+ new_block.tags = copy.deepcopy(block.tags)
116
+
117
+ self.state.pipeline.add_block(new_block)
118
+
119
+ # Remove the original source block from the list
120
+ self.state.pipeline.remove_block(block)
121
+
122
+ logger.debug(f"split_yaml: output 1 document -> {len(documents)} documents")
123
+
124
+ return new_blocks
kmt/kmt.py ADDED
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ """
4
+
5
+ import argparse
6
+ import logging
7
+ import sys
8
+
9
+ from .exception import *
10
+ from .util import *
11
+ import ttast
12
+ from . import handlers
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ def process_args() -> int:
17
+ """
18
+ Processes kmt command line arguments, initialises and runs the pipeline to perform text processing
19
+ """
20
+
21
+ # Create parser for command line arguments
22
+ parser = argparse.ArgumentParser(
23
+ prog="kmt", description="Kubernetes Manifest Transform", exit_on_error=False
24
+ )
25
+
26
+ # Parser configuration
27
+ parser.add_argument(
28
+ "-c", action="append", dest="configs", help="Configuration files"
29
+ )
30
+
31
+ parser.add_argument(
32
+ "-d", action="store_true", dest="debug", help="Enable debug output"
33
+ )
34
+
35
+ args = parser.parse_args()
36
+
37
+ # Capture argument options
38
+ debug = args.debug
39
+ configs = args.configs
40
+
41
+ # Logging configuration
42
+ level = logging.WARNING
43
+ if debug:
44
+ level = logging.DEBUG
45
+
46
+ logging.basicConfig(level=level, format="%(levelname)s: %(message)s")
47
+
48
+ try:
49
+ # Add each config as a pipeline step, which will read and merge the config
50
+ steps = list()
51
+ if configs is not None:
52
+ for config_item in configs:
53
+ # If '-' is specified, read the configuration from stdin
54
+ if config_item == "-":
55
+ step_def = {
56
+ "type": "config",
57
+ "stdin": True
58
+ }
59
+ else:
60
+ step_def = {
61
+ "type": "config",
62
+ "file": config_item
63
+ }
64
+
65
+ steps.append(step_def)
66
+
67
+ pipeline = ttast.Pipeline()
68
+
69
+ # Include the default handlers and support handlers
70
+ pipeline.add_handlers(ttast.builtin_handlers())
71
+ pipeline.add_support_handlers(ttast.builtin_support_handlers())
72
+
73
+ # Add our custom handlers and support handlers
74
+ pipeline.add_support_handlers([
75
+ handlers.SupportHandlerSplitYaml,
76
+ handlers.SupportHandlerExtractGVKN
77
+ ])
78
+
79
+ # Add the steps we've defined to the pipeline
80
+ for step in steps:
81
+ pipeline.add_step(step)
82
+
83
+ # Start executing the pipeline
84
+ pipeline.run()
85
+
86
+ except Exception as e: # pylint: disable=broad-exception-caught
87
+ if debug:
88
+ logger.error(e, exc_info=True, stack_info=True)
89
+ else:
90
+ logger.error(e)
91
+ return 1
92
+
93
+ return 0
94
+
95
+
96
+ def main():
97
+ """
98
+ Entrypoint for the module.
99
+ Minor exception handling is performed, along with return code processing and
100
+ flushing of stdout on program exit.
101
+ """
102
+ try:
103
+ ret = process_args()
104
+ sys.stdout.flush()
105
+ sys.exit(ret)
106
+ except Exception as e: # pylint: disable=broad-exception-caught
107
+ logging.getLogger(__name__).exception(e)
108
+ sys.stdout.flush()
109
+ sys.exit(1)
110
+
111
+
112
+ if __name__ == "__main__":
113
+ main()
kmt/util.py ADDED
@@ -0,0 +1,67 @@
1
+
2
+ import jinja2
3
+ import logging
4
+
5
+ from .exception import *
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ def validate(val, message):
10
+ if not val:
11
+ raise ValidationException(message)
12
+
13
+ def template_if_string(val, mapping):
14
+ if val is not None and isinstance(val, str):
15
+ try:
16
+ environment = jinja2.Environment()
17
+
18
+ template = environment.from_string(val)
19
+ return template.render(mapping)
20
+ except KeyError as e:
21
+ raise PipelineRunException(f"Missing key in template substitution: {e}") from e
22
+
23
+ return val
24
+
25
+ def parse_bool(obj) -> bool:
26
+ if obj is None:
27
+ raise PipelineRunException("None value passed to parse_bool")
28
+
29
+ if isinstance(obj, bool):
30
+ return obj
31
+
32
+ obj = str(obj)
33
+
34
+ if obj.lower() in ["true", "1"]:
35
+ return True
36
+
37
+ if obj.lower() in ["false", "0"]:
38
+ return False
39
+
40
+ raise PipelineRunException(f"Unparseable value ({obj}) passed to parse_bool")
41
+
42
+ def pop_property(spec, key, *, template_map=None, default=None, required=False):
43
+ validate(isinstance(spec, dict), f"Invalid spec passed to pop_property. Must be dict")
44
+ validate(isinstance(template_map, dict) or template_map is None, "Invalid type passed as template_map")
45
+
46
+ if key not in spec:
47
+ # Raise exception is the key isn't present, but required
48
+ if required:
49
+ raise KeyError(f'Missing key "{key}" in spec or value is null')
50
+
51
+ # If the key is not present, return the default
52
+ return default
53
+
54
+ # Retrieve value
55
+ val = spec.pop(key)
56
+
57
+ # Template the value, depending on the type
58
+ if val is not None and template_map is not None:
59
+ if isinstance(val, str):
60
+ val = template_if_string(val, template_map)
61
+ elif isinstance(val, list):
62
+ val = [template_if_string(x, template_map) for x in val]
63
+ elif isinstance(val, dict):
64
+ for val_key in val:
65
+ val[val_key] = template_if_string(val[val_key], template_map)
66
+
67
+ return val
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 ArchMachina
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.1
2
+ Name: kmt
3
+ Version: 0.1.1
4
+ Summary: Kubernetes Manifest Transform
5
+ Home-page: https://github.com/archmachina/kmt
6
+ Download-URL: https://pypi.org/project/kmt/
7
+ Author: Jesse Reichman
8
+ License: MIT
9
+ Keywords: Kubernetes,Manifest,Transform
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: PyYAML >=6.0.0
13
+ Requires-Dist: Jinja2 >=3.1.0
14
+ Requires-Dist: ttast >=0.2.0
15
+
@@ -0,0 +1,12 @@
1
+ kmt/__init__.py,sha256=qA0O5DS6W_R8GUO4lSsSopEJRH8QeDzYj7MLN-ZfY_I,42
2
+ kmt/__main__.py,sha256=1oCvV66p7vdjcVUT7c3wdiPkM0Vq-B1DpISmo_hHZPw,61
3
+ kmt/exception.py,sha256=PAQR7Di0Itn7SQnzVQKLoVSZE0n6bEW2lS8V2YksfHo,142
4
+ kmt/handlers.py,sha256=IsVuWc2tL--Q_-wnYW86V3-m5nXZS1-8N1KsbNK_fGA,3221
5
+ kmt/kmt.py,sha256=UOz7KISVEA25LlSl-EoBJZc3Xu9_kWGhY-LisEzFPWU,2989
6
+ kmt/util.py,sha256=sM4-DXCAMD_KCc_XSzrycFOGqco2jCdJKJ52b8N-MNc,2008
7
+ kmt-0.1.1.dist-info/LICENSE,sha256=tv8dENThTg6WliLKSLo5JT5xgoqRWJ7NTgoatu9HogE,1068
8
+ kmt-0.1.1.dist-info/METADATA,sha256=9VcSLzTcFAbDNqBwDTh2JH0b_L95QcVF60avf1lLjOo,404
9
+ kmt-0.1.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
10
+ kmt-0.1.1.dist-info/entry_points.txt,sha256=0W2nnP5Xle1_CDr9NMZSWz_CsXvD8RWl3FWAT3-cqT8,33
11
+ kmt-0.1.1.dist-info/top_level.txt,sha256=w1tBmLKWmKZYlp-gga6CmkT69DIJpruXkPTwb2iKBEI,4
12
+ kmt-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.43.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ kmt = kmt:main
@@ -0,0 +1 @@
1
+ kmt