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

Fix build in postbuild substitution #734

Merged
merged 3 commits into from
Jun 29, 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
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ repos:
exclude: '^tests/.*/__snapshots__/.*.ambr$'
- id: end-of-file-fixer
- id: check-yaml
exclude: '^tests/testdata/cluster8/apps/.*\.yaml$'
exclude: '^tests/testdata/cluster8/apps/.*\.yaml|tests/testdata/cluster/apps/prod/.*\.yaml$'
- id: check-added-large-files
- repo: https:/psf/black
rev: 24.4.2
Expand Down
89 changes: 63 additions & 26 deletions flux_local/git_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,27 +403,54 @@ def remove(self, kustomization: Kustomization) -> None:
del self._cache[key]


@dataclass
class VisitResult:
"""Result of visiting a kustomization."""

kustomizations: list[Kustomization]
config_maps: list[ConfigMap]
secrets: list[Secret]

def __post_init__(self) -> None:
"""Validate the object"""
unique = {ks.namespaced_name for ks in self.kustomizations}
if len(unique) != len(self.kustomizations):
ks_names = [ks.namespaced_name for ks in self.kustomizations]
dupes = list(filter(lambda x: ks_names.count(x) > 1, ks_names))
raise FluxException(
f"Detected multiple Fluxtomizations with the same name: {dupes}. "
"This indicates either (1) an incorrect Kustomization which needs to be fixed "
"or (2) a multi-cluster setup which requires flux-local to run with a more strict --path."
)


async def visit_kustomization(
selector: PathSelector,
builder: CachableBuilder,
path: Path,
visit_ks: Kustomization | None,
) -> list[Kustomization]:
) -> VisitResult:
"""Visit a path and return a list of Kustomizations."""

_LOGGER.debug("Visiting path (%s) %s", selector.path, path)
label = visit_ks.namespaced_name if visit_ks else str(path)

kinds = [CLUSTER_KUSTOMIZE_KIND, CONFIG_MAP_KIND, SECRET_KIND]

with trace_context(f"Kustomization '{label}'"):
cmd: kustomize.Kustomize
if visit_ks is None:
cmd = kustomize.grep(f"kind={CLUSTER_KUSTOMIZE_KIND}", selector.root / path)
cmd = kustomize.filter_resources(kinds, selector.root / path)
else:
cmd = await builder.build(visit_ks, selector.root / path)
cmd = cmd.grep(f"kind={CLUSTER_KUSTOMIZE_KIND}")
cmd = cmd.grep(GREP_SOURCE_REF_KIND)
cmd = cmd.filter_resources(kinds)
cmd = await cmd.stash()
ks_cmd = cmd.grep(GREP_SOURCE_REF_KIND)
cfg_cmd = cmd.filter_resources([CONFIG_MAP_KIND, SECRET_KIND])

try:
docs = await cmd.objects()
ks_docs = await ks_cmd.objects()
cfg_docs = await cfg_cmd.objects()
except KustomizePathException as err:
raise FluxException(err) from err
except FluxException as err:
Expand All @@ -436,25 +463,26 @@ async def visit_kustomization(
f"Error building Fluxtomization '{visit_ks.namespaced_name}' "
f"path '{path}': {ERROR_DETAIL_BAD_KS} {err}"
) from err
kustomizations = list(
filter(
is_allowed_source(selector.sources or []),
[
Kustomization.parse_doc(doc)
for doc in filter(FLUXTOMIZE_DOMAIN_FILTER, docs)
],
)

return VisitResult(
kustomizations=list(
filter(
is_allowed_source(selector.sources or []),
[
Kustomization.parse_doc(doc)
for doc in filter(FLUXTOMIZE_DOMAIN_FILTER, ks_docs)
],
)
),
config_maps=[
ConfigMap.parse_doc(doc)
for doc in cfg_docs
if doc.get("kind") == CONFIG_MAP_KIND
],
secrets=[
Secret.parse_doc(doc) for doc in cfg_docs if doc.get("kind") == SECRET_KIND
],
)
unique = {ks.namespaced_name for ks in kustomizations}
if len(unique) != len(kustomizations):
ks_names = [ks.namespaced_name for ks in kustomizations]
dupes = list(filter(lambda x: ks_names.count(x) > 1, ks_names))
raise FluxException(
f"Detected multiple Fluxtomizations with the same name: {dupes}. "
"This indicates either (1) an incorrect Kustomization which needs to be fixed "
"or (2) a multi-cluster setup which requires flux-local to run with a more strict --path."
)
return kustomizations


async def kustomization_traversal(
Expand All @@ -468,6 +496,7 @@ async def kustomization_traversal(

path_queue: deque[tuple[Path, Kustomization | None]] = deque()
path_queue.append((selector.relative_path, None))
cluster_config = values.cluster_config([], [])
while path_queue:
# Fully empty the queue, running all tasks in parallel
tasks = []
Expand All @@ -484,7 +513,10 @@ async def kustomization_traversal(
# Find new kustomizations
kustomizations = []
for result in await asyncio.gather(*tasks):
for ks in result:
cluster_config = values.merge_cluster_config(
cluster_config, result.secrets, result.config_maps
)
for ks in result.kustomizations:
if ks.namespaced_name in visited_ks:
continue
kustomizations.append(ks)
Expand All @@ -502,6 +534,12 @@ async def kustomization_traversal(
if not (ks_path := adjust_ks_path(ks, selector)):
continue
ks.path = str(ks_path)
if ks.postbuild_substitute_from:
values.expand_postbuild_substitute_reference(
ks,
cluster_config,
)

path_queue.append((ks_path, ks))
response_kustomizations.append(ks)

Expand Down Expand Up @@ -581,8 +619,7 @@ async def build_kustomization(
if not kinds:
return

regexp = f"kind=^({'|'.join(kinds)})$"
docs = await cmd.grep(regexp).objects(
docs = await cmd.filter_resources(kinds).objects(
target_namespace=kustomization.target_namespace
)

Expand Down
13 changes: 13 additions & 0 deletions flux_local/kustomize.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ def skip_resources(self, kinds: list[str]) -> "Kustomize":
skip_re = "|".join(kinds)
return self.grep(f"kind=^({skip_re})$", invert=True)

def filter_resources(self, kinds: list[str]) -> "Kustomize":
"""Skip resources kinds of the specified types."""
if not kinds:
return self
skip_re = "|".join(kinds)
return self.grep(f"kind=^({skip_re})$", invert=False)

async def validate_policies(self, policies: list[manifest.ClusterPolicy]) -> None:
"""Apply kyverno policies to objects built so far."""
if not policies:
Expand Down Expand Up @@ -283,6 +290,12 @@ def grep(expr: str, path: Path, invert: bool = False) -> Kustomize:
return Kustomize([Command(args, cwd=cwd, exc=KustomizeException)])


def filter_resources(kinds: list[str], path: Path) -> Kustomize:
"""Filter resources in the specified path based of a specific kind."""
regexp = f"kind=^({'|'.join(kinds)})$"
return grep(regexp, path)


def update_namespace(doc: dict[str, Any], namespace: str) -> dict[str, Any]:
"""Update the namespace of the specified document.

Expand Down
15 changes: 14 additions & 1 deletion flux_local/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ def cluster_config(
)


def merge_cluster_config(
config: ClusterConfig, secrets: list[Secret], config_maps: list[ConfigMap]
) -> ClusterConfig:
"""Create a ClusterConfig from a list of secrets and configmaps."""
return ClusterConfig(
lambda: list(config.secrets) + secrets,
lambda: list(config.config_maps) + config_maps,
)


def ks_cluster_config(kustomizations: list[Kustomization]) -> ClusterConfig:
"""Create a ClusterConfig from a list of Kustomizations."""

Expand Down Expand Up @@ -262,7 +272,9 @@ def expand_postbuild_substitute_reference(
continue

if found_data is None:
if not ref.optional and not ref.kind == SECRET_KIND: # Secrets are commonly filtered
if (
not ref.optional and not ref.kind == SECRET_KIND
): # Secrets are commonly filtered
_LOGGER.warning(
"Unable to find SubstituteReference for %s: %s",
ks.namespaced_name,
Expand All @@ -271,5 +283,6 @@ def expand_postbuild_substitute_reference(
continue

values.update(found_data)
_LOGGER.debug("update_postbuild_substitutions=%s", values)
ks.update_postbuild_substitutions(values)
return ks
Loading
Loading