diff --git a/docs/content/reference/templating/inlining/postprocessor.md b/docs/content/reference/templating/inlining/postprocessor.md index 0e8c8997..71173264 100644 --- a/docs/content/reference/templating/inlining/postprocessor.md +++ b/docs/content/reference/templating/inlining/postprocessor.md @@ -151,6 +151,6 @@ With the Nyl `PostProcessor`, you can apply Kyverno policies to validate or muta template: *podSpec ``` -Running `nyl template forgejo.yaml` will use the `kyverno` CLI to apply the policy to the manifests generated by -the Helm chart. Note that the post processing happens at the very end after all other Kubernetes manifests have +Running `nyl template forgejo.yaml` will use the `kyverno` CLI to apply the policy to the resources generated by +the Helm chart. Note that the post processing happens at the very end after all other Kubernetes resources have been generated. diff --git a/docs/content/reference/templating/secrets.md b/docs/content/reference/templating/secrets.md index a2583783..3666c5aa 100644 --- a/docs/content/reference/templating/secrets.md +++ b/docs/content/reference/templating/secrets.md @@ -29,8 +29,8 @@ the desired configuration). !!! todo - ArgoCD caches generated manifests so there may be a time delay between the secret update and ArgoCD fully - re-materilizing the desired manifests with the updated secret being taken into account. What's the cache TTL, + ArgoCD caches generated resources so there may be a time delay between the secret update and ArgoCD fully + re-materilizing the desired resources with the updated secret being taken into account. What's the cache TTL, can it be changed/flushed? (A "hard refresh" usually works, but for automatic drift reconcilation when secrets update, having a lower TTL diff --git a/src/nyl/commands/template.py b/src/nyl/commands/template.py index 2a0ed0d7..90448976 100644 --- a/src/nyl/commands/template.py +++ b/src/nyl/commands/template.py @@ -95,13 +95,13 @@ def template( ), apply: bool = Option( False, - help="Run `kubectl apply` on the rendered manifests, once for each source file. " + help="Run `kubectl apply` on the rendered resources, once for each source file. " "Implies `--no-applyset-part-of`. When an ApplySet is defined in the source file, it will be applied " "separately. Note that this option implies `kubectl --prune`.", ), diff: bool = Option( False, - help="Run `kubectl diff` on the rendered manifests, once for each source file. Cannot be combined with " + help="Run `kubectl diff` on the rendered resources, once for each source file. Cannot be combined with " "`--apply`. Note that this does not generally ", ), generate_applysets: Optional[bool] = Option( @@ -111,7 +111,7 @@ def template( applyset_part_of: bool = Option( True, help="Add the 'applyset.kubernetes.io/part-of' label to all resources belonging to an ApplySet (if declared). " - "This option must be disabled when passing the generated manifests to `kubectl apply --applyset=...`, as it " + "This option must be disabled when passing the generated resources to `kubectl apply --applyset=...`, as it " "would otherwise cause an error due to the label being present on the input data.", ), default_namespace: Optional[str] = Option( @@ -175,7 +175,7 @@ def template( if apply: # When running with --apply, we must ensure that the --applyset-part-of option is disabled, as it would cause - # an error when passing the generated manifests to `kubectl apply --applyset=...`. + # an error when passing the generated resources to `kubectl apply --applyset=...`. applyset_part_of = False if apply and diff: @@ -373,14 +373,14 @@ def worker() -> ResourceList: if apply: logger.info("Kubectl-apply {} resource(s) from '{}'", len(source.resources), source.file) kubectl.apply( - manifests=source.resources, + resources=source.resources, applyset=applyset.reference if applyset else None, prune=True if applyset else False, force_conflicts=True, ) elif diff: logger.info("Kubectl-diff {} resource(s) from '{}'", len(source.resources), source.file) - kubectl.diff(manifests=source.resources, applyset=applyset) + kubectl.diff(resources=source.resources, applyset=applyset) else: # If we're not going to be applying the resources immediately via `kubectl`, we print them to stdout. for resource in source.resources: diff --git a/src/nyl/generator/__init__.py b/src/nyl/generator/__init__.py index 4726b1a6..66cdcc9d 100644 --- a/src/nyl/generator/__init__.py +++ b/src/nyl/generator/__init__.py @@ -1,5 +1,5 @@ """ -This package contains everything related to the generation of Kubernetes manifests via Nyl. +This package contains everything related to the generation of Kubernetes resources via Nyl. """ from abc import ABC, abstractmethod @@ -29,7 +29,7 @@ def __init_subclass__(cls, resource_type: type[T], **kwargs: Any) -> None: @abstractmethod def generate(self, /, resource: T) -> ResourceList: """ - Evaluate a Nyl resource and return a list of the generated Kubernetes manifests. + Evaluate a Nyl resource and return a list of the generated Kubernetes resources. """ raise NotImplementedError diff --git a/src/nyl/generator/dispatch.py b/src/nyl/generator/dispatch.py index 86df5f8e..14d73170 100644 --- a/src/nyl/generator/dispatch.py +++ b/src/nyl/generator/dispatch.py @@ -52,7 +52,7 @@ def default( components_path: Path to search for Nyl components. working_dir: The working directory to consider relative paths relative to. client: The Kubernetes API client to use for interacting with the Kubernetes API. - kube_version: The Kubernetes API version to generate manifests for. If not specified, the version will be + kube_version: The Kubernetes API version to generate resources for. If not specified, the version will be determined from the Kubernetes API server. kube_api_versions: The Kubernetes API versions supported by the cluster. If not specified, the versions will be determined from the Kubernetes API server. diff --git a/src/nyl/generator/helmchart.py b/src/nyl/generator/helmchart.py index 17bd03ad..2b9ca9c0 100644 --- a/src/nyl/generator/helmchart.py +++ b/src/nyl/generator/helmchart.py @@ -36,7 +36,7 @@ class HelmChartGenerator(Generator[HelmChart], resource_type=HelmChart): kube_version: str """ - The Kubernetes API version to generate manifests for. This must be known for Helm cluster feature detection (such + The Kubernetes API version to generate resources for. This must be known for Helm cluster feature detection (such as, for example, picking the right apiVersion for Ingress resources). """ @@ -198,7 +198,7 @@ def generate(self, /, res: HelmChart) -> ResourceList: # command.append(f"{key}={json.dumps(value)}") logger.opt(colors=True).debug( - "Generating manifests with Helm: $ {}", " ".join(map(shlex.quote, command)) + "Generating resources with Helm: $ {}", " ".join(map(shlex.quote, command)) ) try: result = subprocess.run(command, capture_output=True, check=True) diff --git a/src/nyl/profiles/config.py b/src/nyl/profiles/config.py index eacbdd2a..b7a94008 100644 --- a/src/nyl/profiles/config.py +++ b/src/nyl/profiles/config.py @@ -20,7 +20,7 @@ class Profile: values: dict[str, Any] = field(default_factory=dict) """ - Global values that are accessible during manifest rendering under the `values` object. + Global values that are accessible during manifest file rendering under the `values` object. """ kubeconfig: LocalKubeconfig | KubeconfigFromSsh = field(default_factory=lambda: LocalKubeconfig()) diff --git a/src/nyl/resources/__init__.py b/src/nyl/resources/__init__.py index e85fecdc..3dda1dba 100644 --- a/src/nyl/resources/__init__.py +++ b/src/nyl/resources/__init__.py @@ -40,18 +40,18 @@ def __init_subclass__(cls, api_version: str, kind: str | None = None) -> None: cls.KIND = kind or cls.__name__ @classmethod - def load(cls, manifest: Resource) -> "Self": + def load(cls, resource: Resource) -> "Self": """ Load a Nyl resource from a manifest. If called directly on `NylResource`, this will deserialize into the - appropriate subclass based on the `kind` field in the manifest. If the method is instead called on a subclass - directly, the subclass will be used to deserialize the manifest. + appropriate subclass based on the `kind` field in the resource. If the method is instead called on a subclass + directly, the subclass will be used to deserialize the resource. """ - if manifest.get("apiVersion") not in (API_VERSION_K8S, API_VERSION_INLINE): - raise ValueError(f"Unsupported apiVersion: {manifest.get('apiVersion')!r}") + if resource.get("apiVersion") not in (API_VERSION_K8S, API_VERSION_INLINE): + raise ValueError(f"Unsupported apiVersion: {resource.get('apiVersion')!r}") if cls is NylResource: - kind = manifest["kind"] + kind = resource["kind"] module_name = __name__ + "." + kind.lower() try: module = __import__(module_name, fromlist=[kind]) @@ -61,32 +61,32 @@ def load(cls, manifest: Resource) -> "Self": raise ValueError(f"Unsupported resource kind: {kind}") else: - if manifest["kind"] != cls.KIND: - raise ValueError(f"Expected kind {cls.KIND!r}, got {manifest['kind']!r}") + if resource["kind"] != cls.KIND: + raise ValueError(f"Expected kind {cls.KIND!r}, got {resource['kind']!r}") subcls = cls - manifest = Resource(manifest) - manifest.pop("apiVersion") - manifest.pop("kind") + resource = Resource(resource) + resource.pop("apiVersion") + resource.pop("kind") - return cast(Self, deser(manifest, subcls)) + return cast(Self, deser(resource, subcls)) @classmethod - def maybe_load(cls, manifest: Resource) -> "Self | None": + def maybe_load(cls, resource: Resource) -> "Self | None": """ - Maybe load the manifest into a NylResource if the `apiVersion` matches. If the resource kind is not supported, + Maybe load the resource into a NylResource if the `apiVersion` matches. If the resource kind is not supported, an error will be raised. If this is called on a subclass of `NylResource`, the subclass's kind will also be checked. """ - if cls.matches(manifest): - return cls.load(manifest) + if cls.matches(resource): + return cls.load(resource) return None @classmethod - def matches(cls, manifest: Resource, apiVersion: str | Collection[str] | None = None) -> bool: + def matches(cls, resource: Resource, apiVersion: str | Collection[str] | None = None) -> bool: """ - Check if the manifest is a NylResource of the correct `apiVersion` and possibly `kind` (if called on a + Check if the resource is a NylResource of the correct `apiVersion` and possibly `kind` (if called on a `NylResource` subclass). """ @@ -95,23 +95,23 @@ def matches(cls, manifest: Resource, apiVersion: str | Collection[str] | None = elif isinstance(apiVersion, str): apiVersion = {apiVersion} - if manifest.get("apiVersion") not in apiVersion: + if resource.get("apiVersion") not in apiVersion: return False - if cls is not NylResource and manifest["kind"] != cls.KIND: + if cls is not NylResource and resource["kind"] != cls.KIND: return False return True def dump(self) -> Resource: """ - Dump the resource to a manifest. + Dump the resource to a resource document. """ - manifest = cast(Resource, ser(self, type(self), settings=[SerializeDefaults(False)])) - manifest["apiVersion"] = self.API_VERSION - manifest["kind"] = self.KIND - return Resource(manifest) + resource = cast(Resource, ser(self, type(self), settings=[SerializeDefaults(False)])) + resource["apiVersion"] = self.API_VERSION + resource["kind"] = self.KIND + return Resource(resource) @dataclass diff --git a/src/nyl/resources/applyset.py b/src/nyl/resources/applyset.py index 5718c251..162eff4a 100644 --- a/src/nyl/resources/applyset.py +++ b/src/nyl/resources/applyset.py @@ -32,7 +32,7 @@ class ApplySet(NylResource, api_version=API_VERSION_K8S): Nyl's ApplySet resource is not namespaces. - When loading manifests from a file, Nyl looks for an ApplySet resource to determine if the manifests are to be + When loading manifests from a file, Nyl looks for an ApplySet resource to determine if the resources are to be associated with an ApplySet. """ @@ -152,15 +152,15 @@ def contains_group_kinds(self, value: list[str]) -> None: self.metadata.annotations = {} self.metadata.annotations[APPLYSET_ANNOTATION_CONTAINS_GROUP_KINDS] = ",".join(sorted(value)) - def set_group_kinds(self, manifests: ResourceList) -> None: + def set_group_kinds(self, resources: ResourceList) -> None: """ - Set the kinds of resources that are part of the ApplySet based on the specified manifests. + Set the kinds of resources that are part of the ApplySet based on the specified resources. """ kinds = set() - for manifest in manifests: - if "kind" in manifest: - kinds.add(get_canonical_resource_kind_name(manifest["apiVersion"], manifest["kind"])) + for resource in resources: + if "kind" in resource: + kinds.add(get_canonical_resource_kind_name(resource["apiVersion"], resource["kind"])) self.contains_group_kinds = list(kinds) def validate(self) -> None: diff --git a/src/nyl/resources/helmchart_test.py b/src/nyl/resources/helmchart_test.py index 6e369393..b990198c 100644 --- a/src/nyl/resources/helmchart_test.py +++ b/src/nyl/resources/helmchart_test.py @@ -71,11 +71,11 @@ def test__HelmChartGenerator__generate__populates_namespace( generator: HelmChartGenerator, ) -> None: helmchart.metadata.namespace = None - manifests = generator.generate(helmchart) - assert len(manifests) == 1 - assert manifests[0]["metadata"].get("namespace") is None + resources = generator.generate(helmchart) + assert len(resources) == 1 + assert resources[0]["metadata"].get("namespace") is None helmchart.metadata.namespace = "foo" - manifests = generator.generate(helmchart) - assert len(manifests) == 1 - assert manifests[0]["metadata"].get("namespace") == "foo" + resources = generator.generate(helmchart) + assert len(resources) == 1 + assert resources[0]["metadata"].get("namespace") == "foo" diff --git a/src/nyl/resources/postprocessor.py b/src/nyl/resources/postprocessor.py index 981180be..c669c48a 100644 --- a/src/nyl/resources/postprocessor.py +++ b/src/nyl/resources/postprocessor.py @@ -28,7 +28,7 @@ class KyvernoSpec: inlinePolicies: dict[str, KyvernoPolicyDocument] = field(default_factory=dict) """ A mapping of policy name to the Kyverno policy document. Allows specifying Kyverno policies to be applied - to the generated manifests inline. + to the generated resources inline. """ @@ -94,7 +94,7 @@ def get_policy_files(self, name: str, workdir: Path, tmpdir: Path) -> list[Path] @dataclass(kw_only=True) class PostProcessor(NylResource, api_version=API_VERSION_INLINE): """ - Configuration for post-processing Kubernetes manifests in a file. Note that the post-processing is always + Configuration for post-processing Kubernetes resources from a manifest file. Note that the post-processing is always scoped to the file that the processor is defined in. Post processors will be applied after all inline resources are reconciled. @@ -124,7 +124,7 @@ def process(self, resources: ResourceList, source_file: Path) -> ResourceList: if policy_files: logger.info( - "Applying {} Kyverno {} to manifests from '{}': {}", + "Applying {} Kyverno {} to resources from '{}': {}", len(policy_files), "policy" if len(policy_files) == 1 else "policies", source_file.name, @@ -168,7 +168,7 @@ def apply_kyverno_policies( manifest_file = tmp / "manifest.yaml" manifest_file.write_text(yaml.safe_dump_all(resources)) - # Create an output directory for Kyverno to write the mutated manifests to. + # Create an output directory for Kyverno to write the mutated resources to. output_dir = tmp / "output" output_dir.mkdir() @@ -186,7 +186,7 @@ def apply_kyverno_policies( if result.returncode != 0: logger.error("Kyverno stdout:\n{}", result.stdout.decode()) - raise RuntimeError("Kyverno failed to apply policies to manifests. See logs for more details") + raise RuntimeError("Kyverno failed to apply policies to resources. See logs for more details") else: logger.debug("Kyverno stdout:\n{}", result.stdout.decode()) @@ -195,7 +195,7 @@ def apply_kyverno_policies( list(chain(*(filter(None, yaml.safe_load_all(file.read_text())) for file in output_dir.iterdir()))) ) if len(new_resources) != len(resources): - # Showing identifies for manifests that have been added or removed is not very helpful because + # Showing identifiers for resources that have been added or removed is not very helpful because # Kyverno will add `namespace: default` to those without the field, which changes the identifier. raise RuntimeError( "Unexpected behaviour of `kyverno apply` command: The number of resources generated in the " diff --git a/src/nyl/templating.py b/src/nyl/templating.py index a3df3b09..f50f6735 100644 --- a/src/nyl/templating.py +++ b/src/nyl/templating.py @@ -102,7 +102,7 @@ class LookupResourceWrapper(Mapping[str, Any]): """ A wrapper for a Kubernetes resources returned by `lookup()` that permits looking up fields by `__getitem__()` and `__getattr__()`. This wraps a `ResourceInstance` or `ResourceField`, which can later be treated by the - `NylTemplateEngine` to serialize into a dictionary when embedded into a manifest. + `NylTemplateEngine` to serialize into a dictionary when embedded into a resource. This class is needed because the YAML serializer will not be able to serialize the `ResourceInstance` or `ResourceField` objects returned by `lookup()`. diff --git a/src/nyl/tools/kubectl.py b/src/nyl/tools/kubectl.py index 6147ed1e..43fe8227 100644 --- a/src/nyl/tools/kubectl.py +++ b/src/nyl/tools/kubectl.py @@ -87,14 +87,14 @@ def set_kubeconfig(self, kubeconfig: dict[str, Any] | str | Path) -> None: def apply( self, - manifests: ResourceList, + resources: ResourceList, force_conflicts: bool = False, server_side: bool = True, applyset: str | None = None, prune: bool = False, ) -> None: """ - Apply the given manifests to the cluster. + Apply the given resources to the cluster. """ env = self.env @@ -110,22 +110,22 @@ def apply( if force_conflicts: command.append("--force-conflicts") - logger.debug("Applying manifests with command: $ {command}", command=lazy_str(pretty_cmd, command)) - status = subprocess.run(command, input=yaml.safe_dump_all(manifests), text=True, env={**os.environ, **env}) + logger.debug("Applying resources with command: $ {command}", command=lazy_str(pretty_cmd, command)) + status = subprocess.run(command, input=yaml.safe_dump_all(resources), text=True, env={**os.environ, **env}) if status.returncode: raise KubectlError(status.returncode) def diff( self, - manifests: ResourceList, + resources: ResourceList, applyset: ApplySet | None = None, on_error: Literal["raise", "return"] = "raise", ) -> Literal["no-diff", "diff", "error"]: """ - Diff the given manifests against the cluster. + Diff the given resources against the cluster. Args: - manifests: The input manifests. + resources: The input resources. on_error: What to do if the diff command fails. If "raise", raise a KubectlError. If "return", return the status code. applyset: The applyset to use for the diff. This can only be combined with the `prune` option. @@ -143,8 +143,8 @@ def diff( if match_labels: command.extend(["-l", ",".join(f"{k}={v}" for k, v in match_labels.items())]) - logger.debug("Diffing manifests with command: $ {command}", command=lazy_str(pretty_cmd, command)) - status = subprocess.run(command, input=yaml.safe_dump_all(manifests), text=True) + logger.debug("Diffing resources with command: $ {command}", command=lazy_str(pretty_cmd, command)) + status = subprocess.run(command, input=yaml.safe_dump_all(resources), text=True) if status.returncode == 1: return "diff" elif status.returncode == 0: diff --git a/src/nyl/tools/kubernetes.py b/src/nyl/tools/kubernetes.py index 0dceecb2..499ea09f 100644 --- a/src/nyl/tools/kubernetes.py +++ b/src/nyl/tools/kubernetes.py @@ -19,7 +19,7 @@ def discover_kubernetes_api_versions(client: ApiClient) -> set[str]: return all_versions -def resource_locator(manifest: Resource) -> str: +def resource_locator(resource: Resource) -> str: """ Create a string that contains the apiVersion, kind, namespace and name of a Kubernetes resource formatted as @@ -29,8 +29,8 @@ def resource_locator(manifest: Resource) -> str: """ return ( - f"{manifest['apiVersion']}/{manifest['kind']}/" - f"{manifest['metadata'].get('namespace', '')}/{manifest['metadata']['name']}" + f"{resource['apiVersion']}/{resource['kind']}/" + f"{resource['metadata'].get('namespace', '')}/{resource['metadata']['name']}" ) @@ -65,13 +65,13 @@ def drop_empty_metadata_labels(resources: list[Resource]) -> None: del resource["metadata"]["labels"] -def is_cluster_scoped_resource(manifest: Resource) -> bool: +def is_cluster_scoped_resource(resource: Resource) -> bool: """ - Check if a manifest is a cluster scoped resource. + Check if a resource is a cluster scoped resource. """ # HACK: We should probably just list the resources via the Kubectl API? - fqn = manifest.get("kind", "") + "." + manifest.get("apiVersion", "").split("/")[0] + fqn = resource.get("kind", "") + "." + resource.get("apiVersion", "").split("/")[0] return fqn in { "ClusterRole.rbac.authorization.k8s.io", "ClusterRoleBinding.rbac.authorization.k8s.io",