nyl 0.2.2__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nyl-0.2.2 → nyl-0.3.0}/.github/workflows/python.yaml +5 -0
- {nyl-0.2.2 → nyl-0.3.0}/PKG-INFO +7 -1
- {nyl-0.2.2 → nyl-0.3.0}/README.md +6 -0
- {nyl-0.2.2 → nyl-0.3.0}/argocd-cmp/Dockerfile +15 -2
- nyl-0.3.0/docs/content/reference/glossary.md +27 -0
- nyl-0.2.2/docs/content/reference/templating/inlining.md → nyl-0.3.0/docs/content/reference/templating/inlining/helmcharts.md +5 -12
- nyl-0.3.0/docs/content/reference/templating/inlining/overview.md +10 -0
- nyl-0.3.0/docs/content/reference/templating/inlining/postprocessor.md +120 -0
- {nyl-0.2.2 → nyl-0.3.0}/pyproject.toml +1 -1
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/template.py +6 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/generator/__init__.py +9 -3
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/generator/dispatch_test.py +4 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/generator/helmchart.py +1 -1
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/resources/placeholder.py +0 -32
- nyl-0.3.0/src/nyl/resources/postprocessor.py +128 -0
- nyl-0.3.0/src/nyl/resources/postprocessor_test.py +133 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/kubernetes.py +16 -0
- {nyl-0.2.2 → nyl-0.3.0}/.envrc +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/.github/workflows/docker.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/.github/workflows/docs.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/.gitignore +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/.kraken.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/argocd-cmp/README.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/argocd-cmp/plugin.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/argocd-with-nyl/.envrc +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/argocd-with-nyl/.secrets.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/argocd-with-nyl/.sops.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/argocd-with-nyl/README.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/argocd-with-nyl/argocd.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/argocd-with-nyl/nyl-project.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/argocd-with-nyl/nyl-secrets.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/index.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/applysets.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/argocd-plugin.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/cluster-connectivity.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/configuration/environment.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/configuration/profiles.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/configuration/projects.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/configuration/secrets.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/configuration-api/profiles.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/configuration-api/secrets.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/templating/basics.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/templating/components.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/content/reference/templating/secrets.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/mkdocs.yml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/pyproject.toml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/docs/uv.lock +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/example/manifests/postgres.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/example/nyl-profiles.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/example/nyl-secrets.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/example/secrets.yaml +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/kubernetes/README.md +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/kubernetes/__init__.pyi +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/kubernetes/dynamic/__init__.pyi +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/kubernetes/dynamic/client.pyi +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/kubernetes/dynamic/discovery.pyi +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/kubernetes/dynamic/exceptions.pyi +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/kubernetes/dynamic/resource.pyi +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/kubernetes/watch/__init__.pyi +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/kubernetes/watch/watch.pyi +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/__init__.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/__init__.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/argocd.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/crds.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/new.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/profile.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/run.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/secrets.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/tools/__init__.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/tools/bcrypt.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/tools/sops.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/commands/tun.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/generator/components.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/generator/dispatch.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/generator/statefulsecret.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/profiles/__init__.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/profiles/config.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/profiles/config_test.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/profiles/kubeconfig.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/profiles/tunnel.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/project/config.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/resources/__init__.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/resources/applyset.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/resources/applyset_test.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/resources/helmchart.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/resources/statefulsecret.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/secrets/__init__.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/secrets/config.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/secrets/config_test.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/secrets/null.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/secrets/sops.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/secrets/sops_test.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/templating.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/templating_test.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/fs.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/fs_test.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/kubectl.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/kvstore.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/loads.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/logging.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/shell.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/testing.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/typer.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/types.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/src/nyl/tools/yaml.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/tests/__init__.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/tests/conftest.py +0 -0
- {nyl-0.2.2 → nyl-0.3.0}/uv.lock +0 -0
|
@@ -38,6 +38,11 @@ jobs:
|
|
|
38
38
|
- name: Run ${{ matrix.step }}
|
|
39
39
|
run: krakenw run fmt lint
|
|
40
40
|
|
|
41
|
+
- name: Install the Kyverno CLI (required for some Nyl features)
|
|
42
|
+
uses: kyverno/action-install-cli@v0.2.0
|
|
43
|
+
with:
|
|
44
|
+
release: 'v1.13.0'
|
|
45
|
+
|
|
41
46
|
# TODO: Until krakenw run test works (currently fails because of doctest modules)
|
|
42
47
|
- name: Run tests
|
|
43
48
|
run: uv run pytest -vv
|
{nyl-0.2.2 → nyl-0.3.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: nyl
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Advanced templating engine for Kubernetes and GitOps.
|
|
5
5
|
Author-email: Niklas Rosenstein <rosensteinniklas@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -31,6 +31,12 @@ Requires Python 3.11 or newer.
|
|
|
31
31
|
|
|
32
32
|
$ uvx nyl
|
|
33
33
|
|
|
34
|
+
For some features, additional programs must be available:
|
|
35
|
+
|
|
36
|
+
* [kubectl](https://kubernetes.io/de/docs/reference/kubectl/) for most operations
|
|
37
|
+
* [kyverno](https://kyverno.io/docs/kyverno-cli/) when using the Nyl `PostProcessor` resource
|
|
38
|
+
* [sops](https://github.com/getsops/sops) when using the SOPS secrets provider
|
|
39
|
+
|
|
34
40
|
## Local development
|
|
35
41
|
|
|
36
42
|
Install the project with [Uv](https://docs.astral.sh/uv/).
|
|
@@ -9,6 +9,12 @@ Requires Python 3.11 or newer.
|
|
|
9
9
|
|
|
10
10
|
$ uvx nyl
|
|
11
11
|
|
|
12
|
+
For some features, additional programs must be available:
|
|
13
|
+
|
|
14
|
+
* [kubectl](https://kubernetes.io/de/docs/reference/kubectl/) for most operations
|
|
15
|
+
* [kyverno](https://kyverno.io/docs/kyverno-cli/) when using the Nyl `PostProcessor` resource
|
|
16
|
+
* [sops](https://github.com/getsops/sops) when using the SOPS secrets provider
|
|
17
|
+
|
|
12
18
|
## Local development
|
|
13
19
|
|
|
14
20
|
Install the project with [Uv](https://docs.astral.sh/uv/).
|
|
@@ -29,7 +29,7 @@ RUN \
|
|
|
29
29
|
case "$(uname -m)" in \
|
|
30
30
|
x86_64) BIN_ARCH='amd64' ;; \
|
|
31
31
|
aarch64) BIN_ARCH='arm64' ;; \
|
|
32
|
-
*) echo "Unsupported architecture: $
|
|
32
|
+
*) echo "Unsupported architecture: $(uname -m)" && exit 1 ;; \
|
|
33
33
|
esac \
|
|
34
34
|
&& printf "%s" "$BIN_ARCH" > /BINARY_ARCH
|
|
35
35
|
|
|
@@ -57,12 +57,24 @@ RUN curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/s
|
|
|
57
57
|
FROM fetch-bin-base AS sops-bin
|
|
58
58
|
# Find SOPS releases at https://github.com/getsops/sops/releases
|
|
59
59
|
ARG SOPS_VERSION=v3.9.0
|
|
60
|
-
RUN curl -
|
|
60
|
+
RUN curl -fLO https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.$(cat /BINARY_ARCH) \
|
|
61
61
|
&& mv sops-${SOPS_VERSION}.linux.$(cat /BINARY_ARCH) /usr/local/bin/sops \
|
|
62
62
|
&& chmod +x /usr/local/bin/sops \
|
|
63
63
|
&& upx /usr/local/bin/sops \
|
|
64
64
|
&& sops --version
|
|
65
65
|
|
|
66
|
+
FROM fetch-bin-base AS kyverno-bin
|
|
67
|
+
ARG KYVERNO_VERSION=1.13.0
|
|
68
|
+
RUN \
|
|
69
|
+
case "$(uname -m)" in \
|
|
70
|
+
x86_64) KYVERNO_ARCH='x86_64' ;; \
|
|
71
|
+
aarch64) KYVERNO_ARCH='arm64' ;; \
|
|
72
|
+
*) echo "Unsupported architecture: $(uname -m)" && exit 1 ;; \
|
|
73
|
+
esac \
|
|
74
|
+
&& curl -fLO https://github.com/kyverno/kyverno/releases/download/v${KYVERNO_VERSION}/kyverno-cli_v${KYVERNO_VERSION}_linux_${KYVERNO_ARCH}.tar.gz \
|
|
75
|
+
&& tar -xvf kyverno-cli_v${KYVERNO_VERSION}_linux_${KYVERNO_ARCH}.tar.gz \
|
|
76
|
+
&& mv kyverno /usr/local/bin/ \
|
|
77
|
+
&& upx /usr/local/bin/kyverno
|
|
66
78
|
|
|
67
79
|
#: Build the ArgoCD CMP server image.
|
|
68
80
|
#: ----------------------------------
|
|
@@ -73,6 +85,7 @@ COPY --from=argocd-bin /usr/local/bin/argocd /usr/local/bin/argocd
|
|
|
73
85
|
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-cmp-server && chown 999:999 /usr/local/bin/argocd-cmp-server
|
|
74
86
|
COPY --from=helm-bin /usr/local/bin/helm /usr/local/bin/helm
|
|
75
87
|
COPY --from=sops-bin /usr/local/bin/sops /usr/local/bin/sops
|
|
88
|
+
COPY --from=kyverno-bin /usr/local/bin/kyverno /usr/local/bin/kyverno
|
|
76
89
|
COPY --from=build /opt/nyl /opt/nyl
|
|
77
90
|
RUN ln -s /opt/nyl/.venv/bin/nyl /usr/local/bin/nyl
|
|
78
91
|
COPY --chown=999 argocd-cmp/plugin.yaml /home/argocd/cmp-server/config/plugin.yaml
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Glossary
|
|
2
|
+
|
|
3
|
+
[Nyl-inline]: ./templating/inlining/overview.md
|
|
4
|
+
|
|
5
|
+
### Manifest
|
|
6
|
+
|
|
7
|
+
A manifest is a file that may define zero or more Kubernetes or [Nyl inline resources][Nyl-inline]. Nyl understands
|
|
8
|
+
individual manifests, i.e. YAML files, and certain behaviours operate on that level, such as Kubernetes Namespace
|
|
9
|
+
auto-filling and resource post-processing applying only to all resources defined in the same manifest as the
|
|
10
|
+
[`PostProcessor`](./templating/inlining/postprocessor.md).
|
|
11
|
+
|
|
12
|
+
### Profile
|
|
13
|
+
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
### Project
|
|
17
|
+
|
|
18
|
+
...
|
|
19
|
+
|
|
20
|
+
### Resource
|
|
21
|
+
|
|
22
|
+
A resource is a YAML document that follows the schema of a Kubernetes API resource or [Nyl inline resource][Nyl-inline].
|
|
23
|
+
An example would be a Kubernetes `ConfigMap`, `Deployment`, `Pod` or a Nyl `HelmChart` or `PostProcessor`.
|
|
24
|
+
|
|
25
|
+
### Secrets provider
|
|
26
|
+
|
|
27
|
+
...
|
|
@@ -2,19 +2,11 @@
|
|
|
2
2
|
weight: 5
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# HelmChart
|
|
6
6
|
|
|
7
|
-
Nyl
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
inlined resources.
|
|
11
|
-
|
|
12
|
-
## Helm charts
|
|
13
|
-
|
|
14
|
-
Nyl currently only supports inlining of Helm charts. Helm charts are inlined by specifying a `HelmChart` resource in
|
|
15
|
-
your configuration. Just like any other resource, the Helm chart resource values can be templated using Nyl's
|
|
16
|
-
[structured templating](../templating/basics.md) (for example to inject secrets), and are then rendered to Kubernetes
|
|
17
|
-
resources that are inlined in your configuration.
|
|
7
|
+
Nyl allows you to inline Helm charts by specifying a `HelmChart` resource in your manifest. Just like any other
|
|
8
|
+
resource, the Helm chart resource values can be templated using Nyl's [structured templating](../templating/basics.md)
|
|
9
|
+
(for example to inject secrets), and are then rendered to Kubernetes resources that are inlined in your configuration.
|
|
18
10
|
|
|
19
11
|
__Example__
|
|
20
12
|
|
|
@@ -28,6 +20,7 @@ spec:
|
|
|
28
20
|
chart:
|
|
29
21
|
repository: https://charts.bitnami.com/bitnami
|
|
30
22
|
name: nginx
|
|
23
|
+
# version: "..."
|
|
31
24
|
values:
|
|
32
25
|
controller:
|
|
33
26
|
service:
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Overview
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Inlining Overview
|
|
6
|
+
|
|
7
|
+
Nyl supports inlining of Kubernetes resources generated from templates in your configuration. Resources that are
|
|
8
|
+
inlined by Nyl are typically in the `inline.nyl.io/v1` API group, however Nyl also has the concept of
|
|
9
|
+
[components](./components.md) which effectively allow you to define your own API groups and resource kinds for
|
|
10
|
+
inlined resources.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# PostProcessor
|
|
2
|
+
|
|
3
|
+
The `inline.nyl.io/v1/PostProcessor` resource can be used to configure post-processing steps for all Kubernetes
|
|
4
|
+
resources defined in the same [Manifest](../../glossary.md#manifest) as the `PostProcessor`. This resource does
|
|
5
|
+
not accept a `metadata` field.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
* You need the [`kyverno` CLI](https://kyverno.io/docs/kyverno-cli/) installed
|
|
10
|
+
|
|
11
|
+
## API Spec
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
apiVersion: inline.nyl.io/v1
|
|
15
|
+
kind: PostProcessor
|
|
16
|
+
spec:
|
|
17
|
+
kyverno:
|
|
18
|
+
# A list of files that each contain a Kyverno policy resource, usually a `ClusterPolicy`. The paths are first
|
|
19
|
+
# considered relative to the manifest that this resource is defined in, and will then be searched in the project
|
|
20
|
+
# search path.
|
|
21
|
+
policyFiles:
|
|
22
|
+
- path/to/policy.yaml
|
|
23
|
+
|
|
24
|
+
# A mapping of policy names (what you would give as a filename) and a YAML document that represents the Kyverno
|
|
25
|
+
# policy resource (usually a `ClusterPolicy`).
|
|
26
|
+
inlinePolicies:
|
|
27
|
+
my-policy:
|
|
28
|
+
apiVersion: kyverno.io/v1
|
|
29
|
+
kind: ClusterPolicy
|
|
30
|
+
metadata:
|
|
31
|
+
name: enforce-pod-security-context
|
|
32
|
+
spec: {} # ...
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Example
|
|
36
|
+
|
|
37
|
+
If you're deploying to hardened RKE2, your pods must have a specific `securityContext` configuration in order to be
|
|
38
|
+
allowed by the PodSecurityPolicy. The application's Helm charts that you deploy may have options to inject the
|
|
39
|
+
required options, but if they are not you're usually out of luck unless you fork the Helm chart, or materialize
|
|
40
|
+
the resources and edit them in your project.
|
|
41
|
+
|
|
42
|
+
With the Nyl `PostProcessor`, you can apply Kyverno policies to validate or mutate the resources in a
|
|
43
|
+
[manifest](../../glossary.md#manifest).
|
|
44
|
+
|
|
45
|
+
=== "Manifest"
|
|
46
|
+
|
|
47
|
+
```yaml title="forgejo.yaml"
|
|
48
|
+
---
|
|
49
|
+
apiVersion: inline.nyl.io/v1
|
|
50
|
+
kind: PostProcessor
|
|
51
|
+
spec:
|
|
52
|
+
kyverno:
|
|
53
|
+
policyFiles:
|
|
54
|
+
- ./policies/security-profile.yaml
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
apiVersion: v1
|
|
58
|
+
kind: Namespace
|
|
59
|
+
metadata:
|
|
60
|
+
name: forgejo
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
apiVersion: inline.nyl.io/v1
|
|
64
|
+
kind: HelmChart
|
|
65
|
+
metadata:
|
|
66
|
+
name: forgejo
|
|
67
|
+
namespace: forgejo
|
|
68
|
+
spec:
|
|
69
|
+
chart:
|
|
70
|
+
repository: oci://code.forgejo.org/forgejo-helm # https://artifacthub.io/packages/helm/forgejo-helm/forgejo
|
|
71
|
+
name: forgejo
|
|
72
|
+
version: "10.0.1"
|
|
73
|
+
values: {}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
=== "Kyverno Policy"
|
|
77
|
+
|
|
78
|
+
```yaml title="policies/security-profile.yaml"
|
|
79
|
+
apiVersion: kyverno.io/v1
|
|
80
|
+
kind: ClusterPolicy
|
|
81
|
+
metadata:
|
|
82
|
+
name: enforce-security-context
|
|
83
|
+
spec:
|
|
84
|
+
validationFailureAction: enforce
|
|
85
|
+
rules:
|
|
86
|
+
- name: mutate-pod-security-context
|
|
87
|
+
match:
|
|
88
|
+
resources:
|
|
89
|
+
kinds:
|
|
90
|
+
- Pod
|
|
91
|
+
mutate:
|
|
92
|
+
patchStrategicMerge: &podSpec
|
|
93
|
+
spec:
|
|
94
|
+
securityContext:
|
|
95
|
+
runAsNonRoot: true
|
|
96
|
+
seccompProfile:
|
|
97
|
+
type: RuntimeDefault
|
|
98
|
+
containers: &containers
|
|
99
|
+
- (name): "*"
|
|
100
|
+
securityContext:
|
|
101
|
+
runAsNonRoot: true
|
|
102
|
+
allowPrivilegeEscalation: false
|
|
103
|
+
capabilities:
|
|
104
|
+
drop:
|
|
105
|
+
- "ALL"
|
|
106
|
+
initContainers: *containers
|
|
107
|
+
- name: mutate-deployment-security-context
|
|
108
|
+
match:
|
|
109
|
+
resources:
|
|
110
|
+
kinds:
|
|
111
|
+
- Deployment
|
|
112
|
+
mutate:
|
|
113
|
+
patchStrategicMerge:
|
|
114
|
+
spec:
|
|
115
|
+
template: *podSpec
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Running `nyl template forgejo.yaml` will use the `kyverno` CLI to apply the policy to the manifests generated by
|
|
119
|
+
the Helm chart. Note that the post processing happens at the very end after all other Kubernetes manifests have
|
|
120
|
+
been generated.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nyl"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0" # sed'ed on publish in CI
|
|
8
8
|
description = "Advanced templating engine for Kubernetes and GitOps."
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Niklas Rosenstein", email = "rosensteinniklas@gmail.com"},
|
|
@@ -5,6 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from textwrap import indent
|
|
6
6
|
from typing import Optional
|
|
7
7
|
from loguru import logger
|
|
8
|
+
from nyl.resources.postprocessor import PostProcessor
|
|
8
9
|
from typer import Argument, Option
|
|
9
10
|
from nyl.tools import yaml
|
|
10
11
|
from nyl.generator import reconcile_generator
|
|
@@ -177,8 +178,13 @@ def template(
|
|
|
177
178
|
generator,
|
|
178
179
|
source.manifests,
|
|
179
180
|
on_generated=lambda m: template_engine.evaluate(Manifests([m])),
|
|
181
|
+
skip_resources=[PostProcessor],
|
|
180
182
|
)
|
|
181
183
|
|
|
184
|
+
# Find any PostProcessor resources and apply them.
|
|
185
|
+
source.manifests, post_processors = PostProcessor.extract_from_list(source.manifests)
|
|
186
|
+
source.manifests = PostProcessor.apply_all(source.manifests, post_processors, source.file)
|
|
187
|
+
|
|
182
188
|
# Find the namespaces that are defined in the file. If we find any manifests without a namespace, we will
|
|
183
189
|
# inject that namespace name into them. Also find the applyset defined in the file.
|
|
184
190
|
namespaces: set[str] = set()
|
|
@@ -4,8 +4,9 @@ This package contains everything related to the generation of Kubernetes manifes
|
|
|
4
4
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from collections.abc import Callable
|
|
7
|
-
from typing import Any, ClassVar, Generic, TypeVar
|
|
7
|
+
from typing import Any, ClassVar, Generic, Sequence, TypeVar
|
|
8
8
|
|
|
9
|
+
from nyl.resources import NylResource
|
|
9
10
|
from stablehash import stablehash
|
|
10
11
|
|
|
11
12
|
from nyl.tools.types import Manifest, Manifests
|
|
@@ -37,6 +38,7 @@ def reconcile_generator(
|
|
|
37
38
|
generator: Generator[Manifest],
|
|
38
39
|
manifests: Manifests,
|
|
39
40
|
on_generated: Callable[[Manifest], Manifests],
|
|
41
|
+
skip_resources: Sequence[type[NylResource]] = (),
|
|
40
42
|
) -> Manifests:
|
|
41
43
|
"""
|
|
42
44
|
Recursively reconcile all Nyl resources in the manifests using the given generator.
|
|
@@ -46,6 +48,7 @@ def reconcile_generator(
|
|
|
46
48
|
manifests: The list of manifests to reconcile.
|
|
47
49
|
on_generated: A callback to call on each generated manifest, giving the opportunity to modify it.
|
|
48
50
|
This is used to pass the manifest through the `structured-templates` engine.
|
|
51
|
+
skip_resources: A list of Nyl resources to ignore.
|
|
49
52
|
"""
|
|
50
53
|
|
|
51
54
|
queue = Manifests(manifests)
|
|
@@ -63,8 +66,11 @@ def reconcile_generator(
|
|
|
63
66
|
result.append(resource)
|
|
64
67
|
else:
|
|
65
68
|
seen.add(resource_hash)
|
|
66
|
-
for
|
|
67
|
-
queue.
|
|
69
|
+
if any(t.matches(resource) for t in skip_resources):
|
|
70
|
+
queue.append(resource)
|
|
71
|
+
else:
|
|
72
|
+
for manifest in generator.generate(resource):
|
|
73
|
+
queue.extend(on_generated(manifest))
|
|
68
74
|
loops += 1
|
|
69
75
|
|
|
70
76
|
return result
|
|
@@ -46,6 +46,10 @@ def test__DispatchingGenerator__default__creates_generator_for_every_nyl_inline_
|
|
|
46
46
|
kube_api_versions=set(),
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
+
# We make an exception for the PostProcessor resource as it has no generator.
|
|
50
|
+
|
|
49
51
|
for kind in resource_kinds:
|
|
52
|
+
if kind == "PostProcessor":
|
|
53
|
+
continue
|
|
50
54
|
assert kind in generator.generators.keys()
|
|
51
55
|
assert isinstance(generator.generators[kind], Generator)
|
|
@@ -91,7 +91,7 @@ class HelmChartGenerator(Generator[HelmChart], resource_type=HelmChart):
|
|
|
91
91
|
else:
|
|
92
92
|
logger.opt(ansi=True).debug("Using cached Helm chart '{}' from repository '{}'", chart, repository)
|
|
93
93
|
|
|
94
|
-
chart = str(
|
|
94
|
+
chart = str(next(cache_dir.iterdir()))
|
|
95
95
|
repository = None
|
|
96
96
|
|
|
97
97
|
elif chart_ref.git:
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import base64
|
|
2
1
|
from dataclasses import dataclass
|
|
3
|
-
import hashlib
|
|
4
2
|
from typing import Annotated, ClassVar
|
|
5
3
|
from nyl.resources import API_VERSION_K8S, NylResource, ObjectMetadata
|
|
6
4
|
from databind.core import SerializeDefaults
|
|
@@ -78,33 +76,3 @@ class Placeholder(NylResource, api_version=API_VERSION_K8S):
|
|
|
78
76
|
namespace=namespace,
|
|
79
77
|
)
|
|
80
78
|
)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def calculate_applyset_id(*, name: str, namespace: str = "", group: str) -> str:
|
|
84
|
-
"""
|
|
85
|
-
Calculate the ID of a Kubernetes ApplySet with the specified name.
|
|
86
|
-
"""
|
|
87
|
-
|
|
88
|
-
# reference: https://kubernetes.io/docs/reference/labels-annotations-taints/#applyset-kubernetes-io-id
|
|
89
|
-
hash = hashlib.sha256(f"{name}.{namespace}.ApplySet.{group}".encode()).digest()
|
|
90
|
-
uid = base64.b64encode(hash).decode().rstrip("=").replace("/", "_").replace("+", "-")
|
|
91
|
-
return f"applyset-{uid}-v1"
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def get_canonical_resource_kind_name(api_version: str, kind: str) -> str:
|
|
95
|
-
"""
|
|
96
|
-
Given the apiVersion and kind of a Kubernetes resource, return the canonical name of the resource. This name can
|
|
97
|
-
be used to identify the resource in an ApplySet's `applyset.kubernetes.io/contains-group-kinds` annotation.
|
|
98
|
-
|
|
99
|
-
Note that according to the [reference][1], the resource name should use the plural form, but it appears that the
|
|
100
|
-
resource kind name is also accepted. Deriving the plural form will be difficult without querying the Kubernetes
|
|
101
|
-
API.
|
|
102
|
-
|
|
103
|
-
[1]: https://kubernetes.io/docs/reference/labels-annotations-taints/#applyset-kubernetes-io-contains-group-kinds
|
|
104
|
-
|
|
105
|
-
Args:
|
|
106
|
-
api_version: The apiVersion of the resource.
|
|
107
|
-
kind: The kind of the resource.
|
|
108
|
-
"""
|
|
109
|
-
|
|
110
|
-
return (f"{kind}." + (api_version.split("/")[0] if "/" in api_version else "")).rstrip(".")
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from tempfile import TemporaryDirectory
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from loguru import logger
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
from nyl.resources import API_VERSION_INLINE, NylResource
|
|
12
|
+
from nyl.tools.kubernetes import resource_locator
|
|
13
|
+
from nyl.tools.logging import lazy_str
|
|
14
|
+
from nyl.tools.shell import pretty_cmd
|
|
15
|
+
from nyl.tools.types import Manifests
|
|
16
|
+
|
|
17
|
+
KyvernoPolicyDocument = dict[str, Any]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(kw_only=True)
|
|
21
|
+
class KyvernoSpec:
|
|
22
|
+
policyFiles: list[str] = field(default_factory=list)
|
|
23
|
+
"""
|
|
24
|
+
A list of Kyverno policy filenames, either relative to the file that defined the #PostProcessor resource
|
|
25
|
+
or discoverable in the project search path.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
inlinePolicies: dict[str, KyvernoPolicyDocument] = field(default_factory=dict)
|
|
29
|
+
"""
|
|
30
|
+
A mapping of policy name to the Kyverno policy document. Allows specifying Kyverno policies to be applied
|
|
31
|
+
to the generated manifests inline.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(kw_only=True)
|
|
36
|
+
class PostProcessorSpec:
|
|
37
|
+
kyverno: KyvernoSpec
|
|
38
|
+
"""
|
|
39
|
+
Apply Kyverno policies.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(kw_only=True)
|
|
44
|
+
class PostProcessor(NylResource, api_version=API_VERSION_INLINE):
|
|
45
|
+
"""
|
|
46
|
+
Configuration for post-processing Kubernetes manifests in a file. Note that the post-processing is always
|
|
47
|
+
scoped to the file that the processor is defined in. Post processors will be applied after all inline resources
|
|
48
|
+
are reconciled.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# metadata: ObjectMetadata
|
|
52
|
+
|
|
53
|
+
spec: PostProcessorSpec
|
|
54
|
+
|
|
55
|
+
def process(self, manifests: Manifests, source_file: Path) -> Manifests:
|
|
56
|
+
"""
|
|
57
|
+
Post-process the given manifests.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
if self.spec.kyverno.policyFiles or self.spec.kyverno.inlinePolicies:
|
|
61
|
+
logger.info("Applying Kyverno policies to manifests from '{}': {}", source_file.name, self.spec.kyverno)
|
|
62
|
+
|
|
63
|
+
policy_paths = []
|
|
64
|
+
|
|
65
|
+
for policy in map(Path, self.spec.kyverno.policyFiles):
|
|
66
|
+
if (source_file.parent / policy).exists():
|
|
67
|
+
policy = (source_file.parent / policy).resolve()
|
|
68
|
+
|
|
69
|
+
assert policy.is_file() or policy.is_dir(), f"Path '{policy}' must be a directory or file"
|
|
70
|
+
# TODO: Resolve relative paths to full paths.
|
|
71
|
+
policy_paths.append(Path(policy))
|
|
72
|
+
|
|
73
|
+
with TemporaryDirectory() as _tmp:
|
|
74
|
+
tmp = Path(_tmp)
|
|
75
|
+
|
|
76
|
+
# Write inline policies to files.
|
|
77
|
+
inline_dir = tmp / "inline-policies"
|
|
78
|
+
inline_dir.mkdir()
|
|
79
|
+
for key, value in self.spec.kyverno.inlinePolicies.items():
|
|
80
|
+
# If the file name does not end with a YAML suffix, Kyverno will ignore the input file.
|
|
81
|
+
if not key.endswith(".yml") and not key.endswith(".yaml"):
|
|
82
|
+
key += ".yaml"
|
|
83
|
+
policy_paths.append(inline_dir.joinpath(key))
|
|
84
|
+
policy_paths[-1].write_text(yaml.safe_dump(value))
|
|
85
|
+
|
|
86
|
+
# Write manifests to a file.
|
|
87
|
+
manifests_file = tmp / "manifests.yaml"
|
|
88
|
+
manifests_file.write_text(yaml.safe_dump_all(manifests))
|
|
89
|
+
output_file = tmp / "output.yaml"
|
|
90
|
+
|
|
91
|
+
command = [
|
|
92
|
+
"kyverno",
|
|
93
|
+
"apply",
|
|
94
|
+
*map(str, policy_paths),
|
|
95
|
+
f"--resource={manifests_file}",
|
|
96
|
+
"-o",
|
|
97
|
+
str(output_file),
|
|
98
|
+
]
|
|
99
|
+
logger.debug("$ {}", lazy_str(pretty_cmd, command))
|
|
100
|
+
subprocess.check_call(command, stdout=sys.stderr) # TODO: Catch output?
|
|
101
|
+
|
|
102
|
+
# Kyverno only prints the mutated manifests. We assume their name/kind doesn't change and
|
|
103
|
+
# merge them back into the original list.
|
|
104
|
+
mutated_manifests = Manifests(list(filter(None, yaml.safe_load_all(output_file.read_text()))))
|
|
105
|
+
|
|
106
|
+
logger.info(" Mutated manifests: {}", ", ".join(map(resource_locator, mutated_manifests)))
|
|
107
|
+
keyed_manifests = {resource_locator(m): m for m in manifests}
|
|
108
|
+
keyed_manifests.update({resource_locator(m): m for m in mutated_manifests})
|
|
109
|
+
manifests = Manifests(list(keyed_manifests.values()))
|
|
110
|
+
|
|
111
|
+
return manifests
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def extract_from_list(manifests: Manifests) -> tuple[Manifests, list["PostProcessor"]]:
|
|
115
|
+
processors = []
|
|
116
|
+
new_manifests = Manifests([])
|
|
117
|
+
for manifest in list(manifests):
|
|
118
|
+
if processor := PostProcessor.maybe_load(manifest):
|
|
119
|
+
processors.append(processor)
|
|
120
|
+
else:
|
|
121
|
+
new_manifests.append(manifest)
|
|
122
|
+
return new_manifests, processors
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def apply_all(manifests: Manifests, processors: list["PostProcessor"], source_file: Path) -> Manifests:
|
|
126
|
+
for processor in processors:
|
|
127
|
+
manifests = processor.process(manifests, source_file)
|
|
128
|
+
return manifests
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from nyl.resources.postprocessor import KyvernoSpec, PostProcessor, PostProcessorSpec
|
|
3
|
+
from nyl.tools.types import Manifest, Manifests
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test__PostProcessor__extract_from_list() -> None:
|
|
7
|
+
manifest = Manifests(
|
|
8
|
+
[
|
|
9
|
+
Manifest(
|
|
10
|
+
{
|
|
11
|
+
"apiVersion": "v1",
|
|
12
|
+
"kind": "Pod",
|
|
13
|
+
"metadata": {
|
|
14
|
+
"name": "foo",
|
|
15
|
+
"namespace": "bar",
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
),
|
|
19
|
+
Manifest({"apiVersion": "inline.nyl.io/v1", "kind": "PostProcessor", "spec": {"kyverno": {}}}),
|
|
20
|
+
]
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
updated_manifest, processors = PostProcessor.extract_from_list(manifest)
|
|
24
|
+
assert updated_manifest == [manifest[0]]
|
|
25
|
+
assert len(processors) == 1
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test__PostProcessor__process__inlinePolicy() -> None:
|
|
29
|
+
manifest = Manifests(
|
|
30
|
+
[
|
|
31
|
+
Manifest(
|
|
32
|
+
{
|
|
33
|
+
"apiVersion": "v1",
|
|
34
|
+
"kind": "Pod",
|
|
35
|
+
"metadata": {"name": "foo", "namespace": "foo"},
|
|
36
|
+
"spec": {
|
|
37
|
+
"containers": [
|
|
38
|
+
{
|
|
39
|
+
"name": "main",
|
|
40
|
+
"image": "nginx:latest",
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
]
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
processor = PostProcessor(
|
|
50
|
+
spec=PostProcessorSpec(
|
|
51
|
+
kyverno=KyvernoSpec(
|
|
52
|
+
inlinePolicies={
|
|
53
|
+
"security-profile": {
|
|
54
|
+
"apiVersion": "kyverno.io/v1",
|
|
55
|
+
"kind": "ClusterPolicy",
|
|
56
|
+
"metadata": {"name": "enforce-pod-security-context"},
|
|
57
|
+
"spec": {
|
|
58
|
+
"validationFailureAction": "enforce",
|
|
59
|
+
"rules": [
|
|
60
|
+
{
|
|
61
|
+
"name": "mutate-pod-security-context",
|
|
62
|
+
"match": {"resources": {"kinds": ["Pod"]}},
|
|
63
|
+
"mutate": {
|
|
64
|
+
"patchStrategicMerge": {
|
|
65
|
+
"spec": {
|
|
66
|
+
"securityContext": {
|
|
67
|
+
"runAsNonRoot": True,
|
|
68
|
+
"seccompProfile": {"type": "RuntimeDefault"},
|
|
69
|
+
},
|
|
70
|
+
"containers": [
|
|
71
|
+
{
|
|
72
|
+
"(name)": "*",
|
|
73
|
+
"securityContext": {
|
|
74
|
+
"runAsNonRoot": True,
|
|
75
|
+
"allowPrivilegeEscalation": False,
|
|
76
|
+
"capabilities": {"drop": ["ALL"]},
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"initContainers": [
|
|
81
|
+
{
|
|
82
|
+
"(name)": "*",
|
|
83
|
+
"securityContext": {
|
|
84
|
+
"runAsNonRoot": True,
|
|
85
|
+
"allowPrivilegeEscalation": False,
|
|
86
|
+
"capabilities": {"drop": ["ALL"]},
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
updated_manifest = processor.process(manifest, Path("/"))
|
|
103
|
+
|
|
104
|
+
assert updated_manifest == [
|
|
105
|
+
{
|
|
106
|
+
"apiVersion": "v1",
|
|
107
|
+
"kind": "Pod",
|
|
108
|
+
"metadata": {"name": "foo", "namespace": "foo"},
|
|
109
|
+
"spec": {
|
|
110
|
+
"containers": [
|
|
111
|
+
{
|
|
112
|
+
"image": "nginx:latest",
|
|
113
|
+
"name": "main",
|
|
114
|
+
"securityContext": {
|
|
115
|
+
"allowPrivilegeEscalation": False,
|
|
116
|
+
"capabilities": {
|
|
117
|
+
"drop": [
|
|
118
|
+
"ALL",
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
"runAsNonRoot": True,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
"securityContext": {
|
|
126
|
+
"runAsNonRoot": True,
|
|
127
|
+
"seccompProfile": {
|
|
128
|
+
"type": "RuntimeDefault",
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from loguru import logger
|
|
2
2
|
from kubernetes.dynamic import DynamicClient
|
|
3
3
|
from kubernetes.client.api_client import ApiClient
|
|
4
|
+
from nyl.tools.types import Manifest
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
def discover_kubernetes_api_versions(client: ApiClient) -> set[str]:
|
|
@@ -15,3 +16,18 @@ def discover_kubernetes_api_versions(client: ApiClient) -> set[str]:
|
|
|
15
16
|
all_versions.add(f"{resource.group_version}/{resource.kind}")
|
|
16
17
|
logger.info("Discovered {} Kubernetes API version(s).", len(all_versions))
|
|
17
18
|
return all_versions
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def resource_locator(manifest: Manifest) -> str:
|
|
22
|
+
"""
|
|
23
|
+
Create a string that contains the apiVersion, kind, namespace and name of a Kubernetes resource formatted as
|
|
24
|
+
|
|
25
|
+
apiVersion/kind/namespace/name
|
|
26
|
+
|
|
27
|
+
This can be used to uniquely identify a resource.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
f"{manifest['apiVersion']}/{manifest['kind']}/"
|
|
32
|
+
f"{manifest['metadata'].get('namespace', '')}/{manifest['metadata']['name']}"
|
|
33
|
+
)
|
{nyl-0.2.2 → nyl-0.3.0}/.envrc
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nyl-0.2.2 → nyl-0.3.0}/uv.lock
RENAMED
|
File without changes
|