Skip to content

Commit

Permalink
Improve helm class reusing code (#1275)
Browse files Browse the repository at this point in the history
Fixes #

## Proposed Changes

* Improve helm class by removing redundant code
* restore correct import for jinja filters

## Docs and Tests

* [ ] Tests added
* [ ] Updated documentation
  • Loading branch information
ademariag authored Dec 3, 2024
1 parent fee0666 commit f8d877c
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 183 deletions.
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

0 comments on commit f8d877c

Please sign in to comment.