Skip to content

Commit

Permalink
Allow building resources without a Kustomization (#20)
Browse files Browse the repository at this point in the history
Allow building with `kustomize cfg grep` without needing to first build
from a `Kustomization`. Includes a breaking change to move `build` out
of `Kustomization` into the module level.
  • Loading branch information
allenporter authored Feb 6, 2023
1 parent 1600ed2 commit 5c21426
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 34 deletions.
41 changes: 27 additions & 14 deletions flux_local/kustomize.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,27 @@
This example returns the objects inside a Kustomization using `kustomize build`:
```python
from flux_local.kustomize import Kustomize
from flux_local import kustomize
objects = await Kustomize.build('/path/to/objects').objects()
objects = await kustomize.build('/path/to/objects').objects()
for object in objects:
print(f"Found object {object['apiVersion']} {object['kind']}")
```
You can also filter documents to specific resource types or other fields:
```python
from flux_local.kustomize import Kustomize
from flux_local import kustomize
objects = await Kustomize.build('/path/to/objects').grep('kind=ConfigMap').objects()
objects = await kustomize.build('/path/to/objects').grep('kind=ConfigMap').objects()
for object in objects:
print(f"Found ConfigMap: {object['metadata']['name']}")
```
It is also possible to find bare objects without a Kustomization:
```python
from flux_local import kustomize
objects = await kustomize.grep('kind=ConfigMap', '/path/to/objects').objects()
for object in objects:
print(f"Found ConfigMap: {object['metadata']['name']}")
```
Expand All @@ -43,14 +52,7 @@ def __init__(self, cmds: list[list[str]]) -> None:
"""Initialize Kustomize."""
self._cmds = cmds

@classmethod
def build(cls, path: Path) -> "Kustomize":
"""Build cluster artifacts from the specified path."""
return Kustomize(cmds=[[KUSTOMIZE_BIN, "build", str(path)]])

def grep(
self, expr: str, path: Path | None = None, invert: bool = False
) -> "Kustomize":
def grep(self, expr: str, invert: bool = False) -> "Kustomize":
"""Filter resources based on an expression.
Example expressions:
Expand All @@ -60,8 +62,6 @@ def grep(
out = [KUSTOMIZE_BIN, "cfg", "grep", expr]
if invert:
out.append("--invert-match")
if path:
out.append(str(path))
return Kustomize(self._cmds + [out])

async def run(self) -> str:
Expand Down Expand Up @@ -95,3 +95,16 @@ async def validate(self, policy_path: Path) -> None:
],
]
await command.run_piped(cmds)


def build(path: Path) -> Kustomize:
"""Build cluster artifacts from the specified path."""
return Kustomize(cmds=[[KUSTOMIZE_BIN, "build", str(path)]])


def grep(expr: str, path: Path, invert: bool = False) -> Kustomize:
"""Filter resources in the specified path based on an expression."""
out = [KUSTOMIZE_BIN, "cfg", "grep", expr, str(path)]
if invert:
out.append("--invert-match")
return Kustomize([out])
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = flux-local
version = 0.0.1
version = 0.0.2
description = flux-local is a python library and set of tools for managing a flux gitops repository, with validation steps to help improve quality of commits, PRs, and general local testing.
long_description = file: README.md
long_description_content_type = text/markdown
Expand Down
14 changes: 7 additions & 7 deletions tests/test_helm.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import pytest
from aiofiles.os import mkdir

from flux_local import kustomize
from flux_local.helm import Helm
from flux_local.kustomize import Kustomize
from flux_local.manifest import HelmRelease, HelmRepository

TESTDATA_DIR = Path("tests/testdata/") / "helm-repo"
Expand All @@ -22,8 +22,8 @@ def tmp_config_path_fixture(tmp_path_factory: Any) -> Generator[Path, None, None
@pytest.fixture(name="helm_repos")
async def helm_repos_fixture() -> list[dict[str, Any]]:
"""Fixture for creating the HelmRepository objects"""
kustomize = Kustomize.build(TESTDATA_DIR).grep("kind=^HelmRepository$")
return await kustomize.objects()
cmd = kustomize.grep("kind=^HelmRepository$", TESTDATA_DIR)
return await cmd.objects()


@pytest.fixture(name="helm")
Expand All @@ -43,8 +43,8 @@ async def helm_fixture(tmp_config_path: Path, helm_repos: list[dict[str, Any]])
@pytest.fixture(name="helm_releases")
async def helm_releases_fixture() -> list[dict[str, Any]]:
"""Fixture for creating the HelmRelease objects."""
kustomize = Kustomize.build(TESTDATA_DIR).grep("kind=^HelmRelease$")
return await kustomize.objects()
cmd = kustomize.grep("kind=^HelmRelease$", TESTDATA_DIR)
return await cmd.objects()


async def test_update(helm: Helm) -> None:
Expand All @@ -58,9 +58,9 @@ async def test_template(helm: Helm, helm_releases: list[dict[str, Any]]) -> None

assert len(helm_releases) == 1
release = helm_releases[0]
kustomize = await helm.template(
obj = await helm.template(
HelmRelease.from_doc(release), release["spec"].get("values")
)
docs = await kustomize.grep("kind=ServiceAccount").objects()
docs = await obj.grep("kind=ServiceAccount").objects()
names = [doc.get("metadata", {}).get("name") for doc in docs]
assert names == ["metallb-controller", "metallb-speaker"]
31 changes: 19 additions & 12 deletions tests/test_kustomize.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,54 @@

import pytest

from flux_local import command
from flux_local.kustomize import Kustomize
from flux_local import command, kustomize

TESTDATA_DIR = Path("tests/testdata")


async def test_build() -> None:
"""Test a kustomize build command."""
result = await Kustomize.build(TESTDATA_DIR / "repo").run()
result = await kustomize.build(TESTDATA_DIR / "repo").run()
assert "Secret" in result
assert "ConfigMap" in result
assert result == (TESTDATA_DIR / "repo/all.golden").read_text()


async def test_build_grep() -> None:
"""Test a kustomize build and grep command chained."""
result = await kustomize.build(TESTDATA_DIR / "repo").grep("kind=ConfigMap").run()
assert "Secret" not in result
assert "ConfigMap" in result
assert result == (TESTDATA_DIR / "repo/configmap.build.golden").read_text()


async def test_grep() -> None:
"""Test a kustomize build command."""
result = await Kustomize.build(TESTDATA_DIR / "repo").grep("kind=ConfigMap").run()
"""Test a kustomize grep command."""
result = await kustomize.grep("kind=ConfigMap", TESTDATA_DIR / "repo").run()
assert "Secret" not in result
assert "ConfigMap" in result
assert result == (TESTDATA_DIR / "repo/configmap.golden").read_text()
assert result == (TESTDATA_DIR / "repo/configmap.grep.golden").read_text()


async def test_objects() -> None:
"""Test loading yaml documents."""
kustomize = Kustomize.build(TESTDATA_DIR / "repo").grep("kind=ConfigMap")
result = await kustomize.objects()
cmd = kustomize.build(TESTDATA_DIR / "repo").grep("kind=ConfigMap")
result = await cmd.objects()
assert len(result) == 1
assert result[0].get("kind") == "ConfigMap"
assert result[0].get("apiVersion") == "v1"


async def test_validate_pass() -> None:
"""Test applying policies to validate resources."""
kustomize = Kustomize.build(TESTDATA_DIR / "repo")
await kustomize.validate(TESTDATA_DIR / "policies/pass.yaml")
cmd = kustomize.build(TESTDATA_DIR / "repo")
await cmd.validate(TESTDATA_DIR / "policies/pass.yaml")


async def test_validate_fail() -> None:
"""Test applying policies to validate resources."""
kustomize = Kustomize.build(TESTDATA_DIR / "repo")
cmd = kustomize.build(TESTDATA_DIR / "repo")
with pytest.raises(
command.CommandException, match="require-test-annotation: validation error"
):
await kustomize.validate(TESTDATA_DIR / "policies/fail.yaml")
await cmd.validate(TESTDATA_DIR / "policies/fail.yaml")
File renamed without changes.
13 changes: 13 additions & 0 deletions tests/testdata/repo/configmap.grep.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: ConfigMap
metadata:
namespace: flux-system
name: cluster-settings
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'cluster-settings.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'cluster-settings.yaml'
data:
CLUSTER: dev
DOMAIN: example.org

0 comments on commit 5c21426

Please sign in to comment.