Skip to content

Commit

Permalink
Remove support for multiple clusters in a single path (#398)
Browse files Browse the repository at this point in the history
Remove support for multiple clusters in a single path
This is a breaking change that removes the `name` and `namespace` fields
from the `Cluster` object so they no longer act like fake
kustomizations.


Issue #390
  • Loading branch information
allenporter authored Nov 23, 2023
1 parent 7898e18 commit 3a1467c
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 157 deletions.
55 changes: 18 additions & 37 deletions flux_local/git_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,11 @@ class MetadataSelector:
@property
def predicate(
self,
) -> Callable[
[Kustomization | HelmRelease | Cluster | HelmRepository | ClusterPolicy], bool
]:
) -> Callable[[Kustomization | HelmRelease | HelmRepository | ClusterPolicy], bool]:
"""A predicate that selects Kustomization objects."""

def predicate(
obj: Kustomization | HelmRelease | Cluster | HelmRepository | ClusterPolicy,
obj: Kustomization | HelmRelease | HelmRepository | ClusterPolicy,
) -> bool:
if not self.enabled:
return False
Expand Down Expand Up @@ -472,41 +470,24 @@ async def get_clusters(
f"Try specifying another path within the git repo?"
)
_LOGGER.debug("roots=%s", roots)
clusters = [
Cluster(name=ks.name, namespace=ks.namespace or "", path=ks.path)
for ks in roots
if cluster_selector.predicate(ks)
]
build = True
if not clusters:
# There are no flux-system Kustomizations within this path. Fall back to
# assuming everything in the current directory is part of a cluster.
_LOGGER.debug(
"No clusters found; Processing as a Kustomization: %s",
path_selector.relative_path,
names = {ks.namespaced_name for ks in roots}
if len(names) != len(roots):
raise FluxException(
"Detected multiple Fluxtomizations with the same name indicating a multi-cluster setup. Please run with a more strict path"
)
clusters = [
Cluster(name="cluster", namespace="", path=str(path_selector.relative_path))
]
build = False

tasks = []
for cluster in clusters:
_LOGGER.debug("Building cluster %s %s", cluster.name, cluster.path)
tasks.append(
kustomization_traversal(
path_selector,
PathSelector(path=Path(cluster.path), sources=path_selector.sources),
build=build,
)
results = await kustomization_traversal(
path_selector,
PathSelector(path=path_selector.relative_path, sources=path_selector.sources),
build=False,
)
return [
Cluster(
path=str(path_selector.relative_path),
kustomizations=[
ks for ks in results if kustomization_selector.predicate(ks)
],
)
finished = await asyncio.gather(*tasks)
for cluster, results in zip(clusters, finished):
cluster.kustomizations = [
ks for ks in results if kustomization_selector.predicate(ks)
]
clusters.sort(key=lambda x: (x.path, x.namespace, x.name))
return clusters
]


async def build_kustomization(
Expand Down
27 changes: 0 additions & 27 deletions flux_local/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,39 +348,12 @@ class Cluster(BaseManifest):
a repo may also contain multiple (e.g. dev an prod).
"""

name: str
"""The name of the cluster."""

namespace: str
"""The namespace of the cluster."""

path: str
"""The local git repo path to the Kustomization objects for the cluster."""

kustomizations: list[Kustomization] = Field(default_factory=list)
"""A list of flux Kustomizations for the cluster."""

@classmethod
def parse_doc(cls, doc: dict[str, Any]) -> "Cluster":
"""Parse a partial Kustomization from a kubernetes resource."""
_check_version(doc, FLUXTOMIZE_DOMAIN)
if not (metadata := doc.get("metadata")):
raise InputException(f"Invalid {cls} missing metadata: {doc}")
if not (name := metadata.get("name")):
raise InputException(f"Invalid {cls} missing metadata.name: {doc}")
if not (namespace := metadata.get("namespace")):
raise InputException(f"Invalid {cls} missing metadata.namespace: {doc}")
if not (spec := doc.get("spec")):
raise InputException(f"Invalid {cls} missing spec: {doc}")
if not (path := spec.get("path")):
raise InputException(f"Invalid {cls} missing spec.path: {doc}")
return Cluster(name=name, namespace=namespace, path=path)

@property
def namespaced_name(self, sep: str = "/") -> str:
"""Return the namespace and name concatenated as an id."""
return f"{self.namespace}{sep}{self.name}"

@property
def id_name(self) -> str:
"""Identifier for the Cluster in tests."""
Expand Down
4 changes: 1 addition & 3 deletions flux_local/tool/get.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,7 @@ async def run( # type: ignore[no-untyped-def]
YamlFormatter().print([manifest.compact_dict()])
return

cols = ["name", "path", "kustomizations"]
if query.cluster.namespace is None:
cols.insert(0, "namespace")
cols = ["path", "kustomizations"]
results: list[dict[str, Any]] = []
for cluster in manifest.clusters:
value: dict[str, Any] = cluster.dict(include=set(cols))
Expand Down
2 changes: 1 addition & 1 deletion flux_local/tool/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def from_parent( # type: ignore[override]
"""The public constructor."""
item: ClusterCollector = super().from_parent(
parent=parent,
name=cluster.name,
name=cluster.path,
path=Path(cluster.path),
nodeid=str(Path(cluster.path)),
)
Expand Down
Loading

0 comments on commit 3a1467c

Please sign in to comment.