Skip to content

Commit

Permalink
Update flux build to have parity with other flux commands (#798)
Browse files Browse the repository at this point in the history
This is a breaking change that updates the format of the `flux build`
command to have parity with `get` and `diff` commands.

Example to build a single kustomziation:
```bash
$ flux-local build ks apps --path tests/testdata/cluster/
```
Example to inflate a single helm release:
```bash
$ flux-local build hr podinfo -n podinfo --path tests/testdata/cluster
```

The old behavior `flux-local build tests/testdata/cluster/` can still be
achieved with `build all`:
```bash
$ flux-local build all tests/testdata/cluster/
```

Fixes #795
  • Loading branch information
allenporter authored Oct 8, 2024
1 parent e59d232 commit 95afdcf
Show file tree
Hide file tree
Showing 5 changed files with 2,215 additions and 51 deletions.
42 changes: 15 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,45 +52,33 @@ metallb 4.1.14 metallb-metallb bitnami

### flux-local build

You can use the `flux-local` cli to build all objects in a cluster, similar to how you
You can use the `flux-local` cli to build objects in a cluster, similar to how you
use `kustomize build`, which is used underneath. Here is an example to build all flux
`Kustomization` objects within a git repository, using `kustomize cfg count` to parse
the yaml output:

```bash
$ flux-local build tests/testdata/cluster/ | kustomize cfg count
$ flux-local build ks --path tests/testdata/cluster/ | kustomize cfg count
Certificate: 2
ClusterPolicy: 1
ConfigMap: 1
HelmRelease: 2
HelmRepository: 2
ConfigMap: 2
GitRepository: 1
HelmRelease: 3
HelmRepository: 3
Kustomization: 4
Namespace: 1
```

You can also specify the root to build all clusters.

Additionally, you can inflate `HelmRelease` objects inside each `Kustomization` by adding
the `--enable-helm` command line flag. This example again shows `kustomize cfg count`
to parse the yaml output which now includes the resources from `HelmRelease` objects
defined in the cluster:
Additionally, you can inflate `HelmRelease` objects inside a `Kustomization`. This example
again shows `kustomize cfg count` to parse the yaml output of an inflated `HelmRelease`
objects defined in the cluster:

```bash
$ flux-local build tests/testdata/cluster/ --enable-helm --skip-crds | kustomize cfg count
ClusterPolicy: 1
ClusterRole: 2
ClusterRoleBinding: 2
ConfigMap: 3
DaemonSet: 1
Deployment: 3
HelmRelease: 2
HelmRepository: 2
$ flux-local build hr podinfo -n podinfo --path tests/testdata/cluster/ | kustomize cfg count
ConfigMap: 1
Deployment: 2
Ingress: 1
Namespace: 1
Role: 3
RoleBinding: 3
Secret: 1
Service: 3
ServiceAccount: 2
ValidatingWebhookConfiguration: 1
Service: 2
```

### flux-local diff
Expand Down
160 changes: 154 additions & 6 deletions flux_local/tool/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
_SubParsersAction as SubParsersAction,
BooleanOptionalAction,
)
import logging
import pathlib
import tempfile
import pathlib
import logging
from typing import cast


Expand All @@ -20,8 +20,8 @@
_LOGGER = logging.getLogger(__name__)


class BuildAction:
"""Flux-local build action."""
class BuildAllAction:
"""Flux-local build all action."""

@classmethod
def register(
Expand All @@ -31,8 +31,8 @@ def register(
args = cast(
ArgumentParser,
subparsers.add_parser(
"build",
help="Build local flux Kustomization target from a local directory",
"all",
help="Build all local flux Kustomization target from a local directory",
description="""You can use the flux-local cli to build all
objects in a cluster, similar to how you use kustomize build.
This uses kustomize build internally.""",
Expand Down Expand Up @@ -117,3 +117,151 @@ async def run( # type: ignore[no-untyped-def]
for key in keys:
for line in helm_content.content[key]:
print(line, file=file)


class BuildKustomizationAction:
"""Flux-local build for Kustomizations."""

@classmethod
def register(
cls, subparsers: SubParsersAction # type: ignore[type-arg]
) -> ArgumentParser:
"""Register the subparser commands."""
args: ArgumentParser = cast(
ArgumentParser,
subparsers.add_parser(
"kustomizations",
aliases=["ks", "kustomization"],
help="Build Kustomization objects",
description=("The build command does a local kustomize build."),
),
)
args.add_argument(
"--output-file",
type=str,
default="/dev/stdout",
help="Output file for the results of the command",
)
selector.add_ks_selector_flags(args)
args.set_defaults(cls=cls)
return args

async def run( # type: ignore[no-untyped-def]
self,
output_file: str,
**kwargs, # pylint: disable=unused-argument
) -> None:
"""Async Action implementation."""
query = selector.build_ks_selector(**kwargs)
query.helm_release.enabled = False

content = ContentOutput()
query.kustomization.visitor = content.visitor()
await git_repo.build_manifest(
selector=query, options=selector.options(**kwargs)
)

with open(output_file, "w") as file:
keys = list(content.content)
keys.sort()
for key in keys:
for line in content.content[key]:
print(line, file=file)


class BuildHelmReleaseAction:
"""Flux-local diff for HelmRelease."""

@classmethod
def register(
cls, subparsers: SubParsersAction # type: ignore[type-arg]
) -> ArgumentParser:
"""Register the subparser commands."""
args: ArgumentParser = cast(
ArgumentParser,
subparsers.add_parser(
"helmreleases",
aliases=["hr", "helmrelease"],
help="Build HelmRelease objects",
description=(
"The build command does a local kustomize build, then inflates "
"the helm template."
),
),
)
args.add_argument(
"--output-file",
type=str,
default="/dev/stdout",
help="Output file for the results of the command",
)
selector.add_hr_selector_flags(args)
selector.add_helm_options_flags(args)
args.set_defaults(cls=cls)
return args

async def run( # type: ignore[no-untyped-def]
self,
output_file: str,
**kwargs, # pylint: disable=unused-argument
) -> None:
"""Async Action implementation."""
query = selector.build_hr_selector(**kwargs)
content = ContentOutput()
helm_visitor = HelmVisitor()
query.kustomization.visitor = content.visitor()
query.helm_repo.visitor = helm_visitor.repo_visitor()
query.oci_repo.visitor = helm_visitor.repo_visitor()
query.helm_release.visitor = helm_visitor.release_visitor()
helm_options = selector.build_helm_options(**kwargs)
await git_repo.build_manifest(
selector=query, options=selector.options(**kwargs)
)

helm_content = ContentOutput()
with tempfile.TemporaryDirectory() as helm_cache_dir:
await helm_visitor.inflate(
pathlib.Path(helm_cache_dir),
helm_content.visitor(),
helm_options,
)

with open(output_file, "w") as file:
keys = list(helm_content.content)
keys.sort()
for key in keys:
for line in helm_content.content[key]:
print(line, file=file)


class BuildAction:
"""Flux-local build action."""

@classmethod
def register(
cls, subparsers: SubParsersAction # type: ignore[type-arg]
) -> ArgumentParser:
"""Register the subparser commands."""
args: ArgumentParser = subparsers.add_parser(
"build",
help="Build a local flux resource",
description="""You can use the flux-local cli to build all
objects in a cluster, similar to how you use kustomize build.
This uses kustomize build internally.""",
)
subcmds = args.add_subparsers(
title="Available commands",
required=True,
)
BuildKustomizationAction.register(subcmds)
BuildHelmReleaseAction.register(subcmds)
BuildAllAction.register(subcmds)
args.set_defaults(cls=cls)
return args

async def run( # type: ignore[no-untyped-def]
self,
**kwargs, # pylint: disable=unused-argument
) -> None:
"""Async Action implementation."""
# No-op given subcommands are dispatched
2 changes: 1 addition & 1 deletion tests/testdata/cluster9/apps/podinfo/repository.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ spec:
operation: copy
url: oci://ghcr.io/stefanprodan/charts/podinfo
ref:
semver: ">= 6.0.0"
semver: "== 6.7.1"
Loading

0 comments on commit 95afdcf

Please sign in to comment.