Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve helm class reusing code #1275

Merged
merged 2 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 5 additions & 19 deletions kapitan/inputs/helm.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ def compile_file(self, config: KapitanInputTypeHelmConfig, input_path, compile_p
if config.kube_version:
helm_flags["--api-versions"] = config.kube_version

reveal = self.args.reveal
target_name = self.target_name
indent = self.args.indent

temp_dir = tempfile.mkdtemp()
# save the template output to temp dir first
_, error_message = render_chart(
Expand All @@ -82,21 +78,11 @@ def compile_file(self, config: KapitanInputTypeHelmConfig, input_path, compile_p
rel_file_name = os.path.join(rel_dir, file)
full_file_name = os.path.join(current_dir, file)
# Open each file and write its content to the compilation path
with open(full_file_name, "r") as f:
item_path = os.path.join(compile_path, rel_file_name)
os.makedirs(os.path.dirname(item_path), exist_ok=True)
with CompiledFile(
item_path,
self.ref_controller,
mode="w",
reveal=reveal,
target_name=target_name,
indent=indent,
) as fp:
yml_obj = list(yaml.safe_load_all(f))
# Write YAML objects to the compiled file
fp.write_yaml(yml_obj)
logger.debug("Wrote file %s to %s", full_file_name, item_path)
with open(full_file_name, "r", encoding="utf-8") as f:
file_path = os.path.join(compile_path, rel_file_name)
os.makedirs(os.path.dirname(file_path), exist_ok=True)
item_value = list(yaml.safe_load_all(f))
self.to_file(config, file_path, item_value)

def render_chart(self, *args, **kwargs):
return render_chart(*args, **kwargs)
Expand Down
2 changes: 1 addition & 1 deletion kapitan/inputs/jinja2.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from kapitan import cached
from kapitan.inputs.base import CompiledFile, InputType
from kapitan.inventory.model.input_types import KapitanInputTypeJinja2Config
from kapitan.jinja2_filters import render_jinja2
from kapitan.utils import render_jinja2

logger = logging.getLogger(__name__)

Expand Down
2 changes: 2 additions & 0 deletions kapitan/inventory/model/input_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class KapitanInputTypeJinja2Config(KapitanInputTypeBaseConfig):

class KapitanInputTypeHelmConfig(KapitanInputTypeBaseConfig):
input_type: Literal[InputTypes.HELM] = InputTypes.HELM
output_type: OutputType = OutputType.AUTO
prune: Optional[bool] = False
helm_params: dict = {}
helm_values: Optional[dict] = {}
helm_values_files: Optional[List[str]] = []
Expand Down
67 changes: 0 additions & 67 deletions kapitan/jinja2_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,73 +89,6 @@ def load_jinja2_filters_from_file(env, jinja2_filters):
load_module_from_path(env, jinja2_filters)


def render_jinja2_file(name, context, jinja2_filters=defaults.DEFAULT_JINJA2_FILTERS_PATH, search_paths=None):
"""Render jinja2 file name with context"""
path, filename = os.path.split(name)
search_paths = [path or "./"] + (search_paths or [])
env = jinja2.Environment(
undefined=jinja2.StrictUndefined,
loader=jinja2.FileSystemLoader(search_paths),
trim_blocks=True,
lstrip_blocks=True,
extensions=["jinja2.ext.do"],
)
load_jinja2_filters(env)
load_jinja2_filters_from_file(env, jinja2_filters)
try:
return env.get_template(filename).render(context)
except jinja2.TemplateError as e:
# Exception misses the line number info. Retreive it from traceback
err_info = _jinja_error_info(traceback.extract_tb(sys.exc_info()[2]))
raise CompileError(f"Jinja2 TemplateError: {e}, at {err_info[0]}:{err_info[1]}")


def render_jinja2(path, context, jinja2_filters=defaults.DEFAULT_JINJA2_FILTERS_PATH, search_paths=None):
"""
Render files in path with context
Returns a dict where the is key is the filename (with subpath)
and value is a dict with content and mode
Empty paths will not be rendered
Path can be a single file or directory
Ignores hidden files (.filename)
"""
rendered = {}
walk_root_files = []
if os.path.isfile(path):
dirname = os.path.dirname(path)
basename = os.path.basename(path)
walk_root_files = [(dirname, None, [basename])]
else:
walk_root_files = os.walk(path)

for root, _, files in walk_root_files:
for f in files:
if f.startswith("."):
logger.debug("render_jinja2: ignoring file %s", f)
continue
render_path = os.path.join(root, f)
logger.debug("render_jinja2 rendering %s", render_path)
# get subpath and filename, strip any leading/trailing /
name = render_path[len(os.path.commonprefix([root, path])) :].strip("/")
try:
rendered[name] = {
"content": render_jinja2_file(
render_path, context, jinja2_filters=jinja2_filters, search_paths=search_paths
),
"mode": file_mode(render_path),
}
except Exception as e:
raise CompileError(f"Jinja2 error: failed to render {render_path}: {e}")

return rendered


def file_mode(name):
"""Returns mode for file name"""
st = os.stat(name)
return stat.S_IMODE(st.st_mode)


# Custom filters
def reveal_maybe(ref_tag):
"Will reveal ref_tag if valid and --reveal flag is used"
Expand Down
10 changes: 8 additions & 2 deletions kapitan/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@
from kapitan import __file__ as kapitan_install_path
from kapitan.errors import CompileError, InventoryError, KapitanError
from kapitan.inventory import Inventory, get_inventory_backend
from kapitan.jinja2_filters import render_jinja2_file
from kapitan.utils import PrettyDumper, StrEnum, deep_get, flatten_dict, sha256_string
from kapitan.utils import (
PrettyDumper,
StrEnum,
deep_get,
flatten_dict,
render_jinja2_file,
sha256_string,
)

logger = logging.getLogger(__name__)

Expand Down
75 changes: 73 additions & 2 deletions kapitan/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import sys
import tarfile
from collections import Counter, defaultdict
from datetime import time
from functools import lru_cache
from hashlib import sha256
from zipfile import ZipFile
Expand All @@ -29,8 +28,13 @@
import requests
import yaml

from kapitan import cached
from kapitan import cached, defaults
from kapitan.errors import CompileError
from kapitan.jinja2_filters import (
_jinja_error_info,
load_jinja2_filters,
load_jinja2_filters_from_file,
)
from kapitan.version import VERSION

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -561,3 +565,70 @@ def copy_tree(src: str, dst: str, clobber_files=False) -> list:
shutil.copytree(src, dst, dirs_exist_ok=True, copy_function=copy_function)
after = set(glob.iglob(f"{dst}/*", recursive=True))
return list(after - before)


def render_jinja2_file(name, context, jinja2_filters=defaults.DEFAULT_JINJA2_FILTERS_PATH, search_paths=None):
"""Render jinja2 file name with context"""
path, filename = os.path.split(name)
search_paths = [path or "./"] + (search_paths or [])
env = jinja2.Environment(
undefined=jinja2.StrictUndefined,
loader=jinja2.FileSystemLoader(search_paths),
trim_blocks=True,
lstrip_blocks=True,
extensions=["jinja2.ext.do"],
)
load_jinja2_filters(env)
load_jinja2_filters_from_file(env, jinja2_filters)
try:
return env.get_template(filename).render(context)
except jinja2.TemplateError as e:
# Exception misses the line number info. Retreive it from traceback
err_info = _jinja_error_info(traceback.extract_tb(sys.exc_info()[2]))
raise CompileError(f"Jinja2 TemplateError: {e}, at {err_info[0]}:{err_info[1]}")


def render_jinja2(path, context, jinja2_filters=defaults.DEFAULT_JINJA2_FILTERS_PATH, search_paths=None):
"""
Render files in path with context
Returns a dict where the is key is the filename (with subpath)
and value is a dict with content and mode
Empty paths will not be rendered
Path can be a single file or directory
Ignores hidden files (.filename)
"""
rendered = {}
walk_root_files = []
if os.path.isfile(path):
dirname = os.path.dirname(path)
basename = os.path.basename(path)
walk_root_files = [(dirname, None, [basename])]
else:
walk_root_files = os.walk(path)

for root, _, files in walk_root_files:
for f in files:
if f.startswith("."):
logger.debug("render_jinja2: ignoring file %s", f)
continue
render_path = os.path.join(root, f)
logger.debug("render_jinja2 rendering %s", render_path)
# get subpath and filename, strip any leading/trailing /
name = render_path[len(os.path.commonprefix([root, path])) :].strip("/")
try:
rendered[name] = {
"content": render_jinja2_file(
render_path, context, jinja2_filters=jinja2_filters, search_paths=search_paths
),
"mode": file_mode(render_path),
}
except Exception as e:
raise CompileError(f"Jinja2 error: failed to render {render_path}: {e}")

return rendered


def file_mode(name):
"""Returns mode for file name"""
st = os.stat(name)
return stat.S_IMODE(st.st_mode)
Loading
Loading