kmt 0.1.1__tar.gz
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-0.1.1/LICENSE +21 -0
- kmt-0.1.1/PKG-INFO +14 -0
- kmt-0.1.1/README.md +0 -0
- kmt-0.1.1/setup.cfg +4 -0
- kmt-0.1.1/setup.py +26 -0
- kmt-0.1.1/src/kmt/__init__.py +3 -0
- kmt-0.1.1/src/kmt/__main__.py +4 -0
- kmt-0.1.1/src/kmt/exception.py +8 -0
- kmt-0.1.1/src/kmt/handlers.py +124 -0
- kmt-0.1.1/src/kmt/kmt.py +113 -0
- kmt-0.1.1/src/kmt/util.py +67 -0
- kmt-0.1.1/src/kmt.egg-info/PKG-INFO +14 -0
- kmt-0.1.1/src/kmt.egg-info/SOURCES.txt +15 -0
- kmt-0.1.1/src/kmt.egg-info/dependency_links.txt +1 -0
- kmt-0.1.1/src/kmt.egg-info/entry_points.txt +2 -0
- kmt-0.1.1/src/kmt.egg-info/requires.txt +3 -0
- kmt-0.1.1/src/kmt.egg-info/top_level.txt +1 -0
kmt-0.1.1/LICENSE
ADDED
|
@@ -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.
|
kmt-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
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
|
kmt-0.1.1/README.md
ADDED
|
File without changes
|
kmt-0.1.1/setup.cfg
ADDED
kmt-0.1.1/setup.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from setuptools import find_packages, setup
|
|
4
|
+
|
|
5
|
+
with open("README.md") as readme_file:
|
|
6
|
+
README = readme_file.read()
|
|
7
|
+
|
|
8
|
+
setup_args = {
|
|
9
|
+
"name": "kmt",
|
|
10
|
+
"version": os.environ["BUILD_VERSION"],
|
|
11
|
+
"description": "Kubernetes Manifest Transform",
|
|
12
|
+
"long_description_content_type": "text/markdown",
|
|
13
|
+
"long_description": README,
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"packages": find_packages(where="src", include=["kmt", "kmt.*"]),
|
|
16
|
+
"author": "Jesse Reichman",
|
|
17
|
+
"keywords": ["Kubernetes", "Manifest", "Transform"],
|
|
18
|
+
"url": "https://github.com/archmachina/kmt",
|
|
19
|
+
"download_url": "https://pypi.org/project/kmt/",
|
|
20
|
+
"entry_points": {"console_scripts": ["kmt = kmt:main"]},
|
|
21
|
+
"package_dir": {"": "src"},
|
|
22
|
+
"install_requires": ["PyYAML>=6.0.0", "Jinja2>=3.1.0", "ttast>=0.2.0"],
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
setup(**setup_args, include_package_data=True)
|
|
@@ -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-0.1.1/src/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()
|
|
@@ -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,14 @@
|
|
|
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
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
src/kmt/__init__.py
|
|
5
|
+
src/kmt/__main__.py
|
|
6
|
+
src/kmt/exception.py
|
|
7
|
+
src/kmt/handlers.py
|
|
8
|
+
src/kmt/kmt.py
|
|
9
|
+
src/kmt/util.py
|
|
10
|
+
src/kmt.egg-info/PKG-INFO
|
|
11
|
+
src/kmt.egg-info/SOURCES.txt
|
|
12
|
+
src/kmt.egg-info/dependency_links.txt
|
|
13
|
+
src/kmt.egg-info/entry_points.txt
|
|
14
|
+
src/kmt.egg-info/requires.txt
|
|
15
|
+
src/kmt.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
kmt
|