Kubernator™ (Ktor™) is an integrated solution for the Kubernetes state management. It operates on directories, processing their content via a collection of plugins, generating Kubernetes resources in the process, validating them, transforming them and then applying against the Kubernetes cluster.
While fully functional in the current state and used in production, this software is in BETA. A lot of things are expected to change rapidly, including main APIs, initialization procedures and some core features. Documentation at this stage is basically non-existent.
The product is licensed under the Apache License, Version 2.0. Please see LICENSE for further details.
Kubernator and its plugins are provided on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing Kubernator and assume any risks associated with doing so.
"Kubernator" and "Ktor" are trademarks or registered trademarks of Express Systems USA, Inc and Karellen, Inc. All other trademarks are property of their respective owners.
Real-world Kubernetes deployments are rarely a flat pile of YAML files. A single environment typically combines:
- plain Kubernetes manifests kept under version control,
- Helm charts from public and private repositories (including OCI),
- Istio meshes installed via operator manifests,
- Custom Resource Definitions shipped separately from the resources that consume them,
- infrastructure outputs from Terraform or Terragrunt (VPC IDs, cluster endpoints, ARNs),
- credentials and kubeconfig files that differ per environment (EKS, GKE, Minikube, bare clusters),
- Jinja-rendered templates with values that must be shared across many resources,
- chunks of configuration that live in other git repositories and must be pulled in at deploy time.
Tools that cover one piece of this well (kubectl, helm, kustomize, helmfile, terraform) each have their own
state model, their own templating semantics, and their own assumptions about where files live. Gluing them together
usually ends up as ad-hoc shell scripts that duplicate logic across environments, hide their dependencies, and are
difficult to test.
Kubernator is a CLI that walks a directory tree, executes an optional .kubernator.py script in each directory, and
runs a pipeline of plugins that generate, transform, validate, and apply Kubernetes resources. The directory tree
defines structure; the scripts define composition; the plugins define capability.
Key properties:
- Directory-driven. The unit of organisation is a directory. Sub-directories inherit context from their parents, making environment overlays and per-service customisation natural.
- Plugin pipeline. Plugins are explicitly registered by the user (not auto-loaded) and run through well-defined
stages:
init→start→ per-directory (before_dir→before_script→.kubernator.py→after_script→after_dir) →apply→summary. Registration order is execution order. - Hierarchical context. A context object (
ktor) follows directory traversal. Values set in/aare visible in/a/band/a/c/d; values set in/a/bare invisible in/a/c.ctx.globalsis always reachable. - Composability.
ktor.app.walk_local(...)andktor.app.walk_remote(...)let a script pull in further local or remote directories, including specific refs of git repositories. Remote content enters the context tree as a child of the directory that queued it. - Dry-run by default. Nothing is applied to a cluster unless
--yesis passed. The default mode (dump) writes the diff / final manifests to stdout. - Resource-aware. The Kubernetes plugin talks to the API server directly via the Python client, resolves CRDs, runs server-side field validation, and computes minimal patches instead of blindly re-applying.
$ docker run --mount type=bind,source="$(pwd)",target=/root,readonly -t ghcr.io/karellen/kubernator:latestThe image is tagged by version at ghcr.io/karellen/kubernator:<version> and :latest for the most recent non-dev
release. Kubernetes client libraries for API versions 19–29 are pre-cached inside the image.
$ brew install python@3.13
$ pip3.13 install kubernator
$ kubernator --version$ pip install kubernator
$ kubernator --versionPython 3.10 through 3.14 are supported. Some plugins (awscli, eks, gke) may require additional volume mounts or
environment variables for credentials and external tooling.
kubernator [OPTIONS] [dump|apply]
| Option | Description |
|---|---|
--version |
Print version and exit. |
--clear-cache |
Clear the application cache and exit. |
--clear-k8s-cache |
Clear the Kubernetes client library cache and exit. |
--pre-cache-k8s-client V [V ...] |
Download and cache the specified Kubernetes client library major versions. |
--pre-cache-k8s-client-no-patch |
Skip Kubernator's client patches while pre-caching (diagnostic). |
--log-format {human,json} |
Log output format. Default human. |
--log-file PATH |
Write logs to file instead of stderr. |
-v, --verbose LEVEL |
CRITICAL/ERROR/WARNING/INFO/DEBUG/TRACE. Default INFO. |
-f, --file PATH |
Output file for generated manifests. Default stdout. |
-o, --output-format {json,json-pretty,yaml} |
Output format. Default yaml. |
-p, --path PATH |
Starting directory. Default current directory. |
--yes |
Actually apply changes. Without this flag Kubernator runs as a dry-run. |
command |
dump (default) writes the computed plan; apply applies it to the cluster. |
On startup Kubernator parses command line arguments, initialises logging, discovers plugin modules (without activating
them), and registers the always-present app plugin. Every other plugin must be explicitly registered by a
.kubernator.py script via ktor.app.register_plugin("<name>", **kwargs). The order of register_plugin calls is
the order of handler execution for subsequent stages.
The pipeline stages, in order, are:
- Plugin Init — called once per plugin when it is registered.
- Plugin Start — called once per plugin after init.
- For each directory in the traversal queue:
- Before Directory — every registered plugin's
handle_before_dir. - If
.kubernator.pyis present:- Before Script — every plugin's
handle_before_script. - The
.kubernator.pyscript itself (executed withktorandloggerin globals). - After Script — every plugin's
handle_after_script(reverse order).
- Before Script — every plugin's
- After Directory — every plugin's
handle_after_dir(reverse order).
- Before Directory — every registered plugin's
- Apply — every plugin's
handle_apply(e.g. Kubernetes plugin pushes resources to the cluster when--yes). - Verify — every plugin's
handle_verify. - Shutdown — every plugin's
handle_shutdown(cleanups, even on failure). - Summary — every plugin's
handle_summary(on success only).
Within "for each directory", after after_dir fires, the app plugin scans the current directory for sub-directories,
filters them through context.app.excludes, re-orders the survivors according to context.app.includes patterns, and
appends them to the traversal queue. Scripts can also inject directories explicitly with walk_local / walk_remote.
A global state is carried through as the application runs. It is a hierarchy of PropertyDict objects that follows the
parent-child relationship of directory traversal. Given /a/b, /a/c, /a/c/d: a value set in /a's context is
visible in all of /a/b, /a/c, /a/c/d; a value set in /a/b is visible only there; a value set in /a/c is
visible in /a/c and /a/c/d but not in /a or /a/b.
context.globals is the top-most context, reachable from any stage (including those not associated with a directory).
When traversal enters a remote directory (materialised as a local temp directory), the remote tree enters the context
hierarchy as a child of the directory that queued it.
Context also carries references to essential functions — not only data. In pre-start and .kubernator.py scripts the
context is available as the global variable ktor, and a logger named after the script location is also injected.
Plugins are auto-discovered at startup but only the App plugin runs automatically. Every other plugin is opted in
by the user with ktor.app.register_plugin("<name>", **kwargs). A plugin may assert_plugin("other") during
registration to declare a hard dependency; some plugins register their prerequisites themselves (e.g. terragrunt
pulls in terraform, eks pulls in awscli).
The table below names the plugin (the string passed to register_plugin), its role, the external binary it needs (if
any), and whether Kubernator downloads that binary automatically given a version.
| Plugin | Role | Binary | Auto-download |
|---|---|---|---|
app |
Directory traversal, plugin lifecycle, core ktor API. |
— | — |
kubeconfig |
Sets/overrides the kubeconfig path used downstream. | — | — |
kubectl |
Thin wrapper around kubectl. |
kubectl |
Yes |
awscli |
Ensures an aws CLI is available; used by EKS. |
aws |
Yes |
eks |
Generates a kubeconfig for an AWS EKS cluster. | (via awscli) | — |
gke |
Generates a kubeconfig for a Google GKE cluster. | gcloud |
No (must be on PATH) |
minikube |
Provisions and manages a local Minikube cluster. | minikube |
Yes |
kind |
Provisions and manages a local kind (Kubernetes IN Docker) cluster. | kind |
Yes |
k3d |
Provisions and manages a local k3d (k3s in Docker) cluster. | k3d |
Yes |
terraform |
Initialises Terraform and exposes its outputs as ktor.tf. |
terraform |
Yes |
terragrunt |
Same as above, but via Terragrunt (wraps Terraform). | terragrunt |
Yes |
k8s |
Core Kubernetes plugin: loads, transforms, validates, applies. | (API server) | — |
helm |
Renders Helm charts (classic repo and OCI) into K8s resources. | helm |
Yes |
istio |
Installs and upgrades Istio via istioctl/IstioOperator. |
istioctl |
Yes |
templates |
Registers and renders Jinja2 templates that emit K8s resources. | — | — |
The App plugin traverses the directory structure, exposes essential functions through the context, and runs
.kubernator.py scripts. It is registered automatically and is the only plugin that is always active.
After each directory's after_dir stage the App plugin scans child directories, sorts them alphabetically, removes
those matching any pattern in context.app.excludes, and re-orders the remainder to match the order of patterns in
context.app.includes. For example, with children /a/foo, /a/bal, /a/bar, /a/baz, excludes ["f*"], and
includes ["baz", "*"], the resulting queue is /a/baz, /a/bal, /a/bar.
Scripts can override this queue explicitly via walk_local and walk_remote.
ktor.app.argsParsed command-line arguments.
ktor.app.cwdCurrent directory being processed (only defined inside per-directory stages).
ktor.app.includes,ktor.app.excludesMutable
Globssets of patterns for sub-directory filtering. Resettable per-directory.ktor.app.default_includes,ktor.app.default_excludesDefaults applied at the start of every directory.
ktor.app.walk_local(*paths, keep_context=False)Schedule local paths to be traversed after the current directory. Relative paths resolve against the current directory. With
keep_context=Truethe new paths inherit the current context instead of a fresh child context.ktor.app.walk_remote(repo, *path_prefixes, keep_context=False)Schedule paths inside a remote git repository.
repois a URL (optionally with?ref=<branch|tag|sha>). Absolutepath_prefixesare resolved relative to the repository root.ktor.app.repository_credentials_provider(func)Register a callable that adjusts credentials/scheme per URL.
func(parsed_url)returns(scheme, username, password)(any element may beNoneto leave it unchanged). Useful for flippinggit://tohttps://with a token in CI while leaving developer checkouts on SSH.ktor.app.register_plugin(name_or_class, **kwargs)Register and initialise a plugin.
nameis the plugin's registered identifier (e.g."k8s","helm").ktor.app.assert_plugin(name, requester)Assert that a prerequisite plugin is registered; raises if not.
ktor.app.register_cleanup(handler)Register an object with a
.cleanup()method to be invoked on shutdown.ktor.app.run(cmd, stdout, stderr, **kwargs),ktor.app.run_capturing_out(cmd, stderr, **kwargs),ktor.app.run_passthrough_capturing(cmd, stderr, **kwargs)Subprocess helpers with integrated logging.
ktor.app.download_remote_file(logger, url, category, sub_category),ktor.app.load_remote_file(logger, url, file_type, category, sub_category)HTTP download helpers with on-disk caching.
ktor.app.jp(path)JSONPath convenience helper bound to the core implementation.
Centralises the kubeconfig path so that other plugins see a consistent value. Defaults to $KUBECONFIG or
~/.kube/config. Plugins that generate their own kubeconfig (e.g. minikube, kind, k3d, eks, gke) call set(...) on this
plugin; consumers that need to react to changes register a notifier.
ktor.kubeconfig.kubeconfigCurrent kubeconfig path.
ktor.kubeconfig.set(path)Replace the kubeconfig path and notify all registered observers.
ktor.kubeconfig.register_change_notifier(callable)Callable is invoked whenever the path changes.
Thin wrapper around kubectl. If the k8s plugin has determined the cluster's API version, kubectl auto-selects a
matching binary; otherwise pass version="1.30.0" (or similar) to register_plugin. Kubernator downloads the binary
from the official Kubernetes release mirror and caches it.
ktor.kubectl.kubectl_file,ktor.kubectl.versionktor.kubectl.stanza()Base command list (kubectl binary +
--kubeconfigif set). Extend with additional arguments.ktor.kubectl.run(*args, **kwargs)Run
kubectl <args>, streaming stdout to logs.ktor.kubectl.run_capturing(*args, **kwargs)Run
kubectl <args>and return captured stdout.ktor.kubectl.get(resource_type, resource_name, namespace=None)Fetch a resource (or list of resources) as a Python dict/list.
Ensures aws is available; downloads and extracts the official AWS CLI v2 bundle if necessary. Primarily consumed by
the eks plugin but also usable directly from scripts that need AWS calls.
ktor.awscli.aws_file,ktor.awscli.versionktor.awscli.stanza(*args, output="json", region=None)Build a ready-to-exec AWS CLI command list with
--outputand (optionally)--region.
Uses awscli to generate a temporary kubeconfig for an EKS cluster and pushes it into the kubeconfig plugin. Register
with name=<cluster> and region=<aws-region>. AWS credentials must be available in the environment.
ktor.eks.kubeconfigPath to the generated temporary kubeconfig.
Equivalent for Google Kubernetes Engine. Relies on gcloud being installed on the host (not auto-downloaded). Register
with name, region, and project.
ktor.gke.kubeconfig,ktor.gke.name,ktor.gke.region,ktor.gke.project,ktor.gke.gcloud_file
Drives a local Minikube cluster for development or integration testing. Downloads the minikube binary if necessary,
starts a profile, and publishes the generated kubeconfig to the kubeconfig plugin. Typical registration:
ktor.app.register_plugin("minikube",
k8s_version="1.30.0",
profile="my-dev",
start_fresh=True,
keep_running=False,
driver="docker",
nodes=1)ktor.minikube.version,ktor.minikube.k8s_version,ktor.minikube.profile,ktor.minikube.kubeconfigktor.minikube.start_fresh,ktor.minikube.keep_running,ktor.minikube.nodes,ktor.minikube.driver,ktor.minikube.cpus,ktor.minikube.extra_args,ktor.minikube.extra_addonsktor.minikube.cmd(*args)/ktor.minikube.cmd_out(*args)Run a
minikubesubcommand, optionally capturing output.
Drives a local kind cluster (Kubernetes IN Docker — upstream kubeadm binaries running inside Docker containers, with
real etcd and separate control-plane components). Downloads the kind binary, creates a cluster, and publishes the
exported kubeconfig to the kubeconfig plugin. Unlike Minikube, kind does not ship bundled addons such as CSI hostpath
or LoadBalancer controllers — install those separately in your .kubernator.py if you need them, the same way you
would on any upstream Kubernetes cluster.
ktor.app.register_plugin("kind",
k8s_version="1.34.0",
profile="my-dev",
start_fresh=True,
keep_running=False,
nodes=5,
control_plane_nodes=3)Node images default to ghcr.io/karellen/kindest-node:v<k8s_version> (pre-built by the kindest-node-release workflow
in this repo for every K8s release ≥ 1.29.0, multi-arch amd64/arm64). Override with node_image_registry="kindest/node"
to pull from upstream Docker Hub, or pass node_image=... for a specific image. Lifecycle maps start/stop onto
docker start / docker stop on the node containers (kind has no native start/stop subcommands); keep_running=False
stops containers without deleting them, and start_fresh=True deletes and recreates the cluster.
Multi-node HA is supported directly: control_plane_nodes >= 2 causes kind to auto-spawn an haproxy load-balancer
container in front of the API servers. Additional knobs: extra_port_mappings, feature_gates, runtime_config, or
raw config YAML override.
ktor.kind.version,ktor.kind.k8s_version,ktor.kind.profile,ktor.kind.kubeconfigktor.kind.node_image,ktor.kind.node_image_registryktor.kind.nodes,ktor.kind.control_plane_nodes,ktor.kind.provider(dockerorpodman)ktor.kind.start_fresh,ktor.kind.keep_runningktor.kind.cmd(*args)/ktor.kind.cmd_out(*args)Run a
kindsubcommand, optionally capturing output.
Drives a local k3d cluster (k3s in Docker — Rancher's lightweight Kubernetes distribution running
inside Docker containers). Downloads the k3d binary, creates a cluster, and publishes the exported kubeconfig to the
kubeconfig plugin. Like kind, k3d does not ship the same addon set as Minikube — install CSI drivers, ingress
controllers, etc. yourself in your .kubernator.py if needed. K3s does ship with Traefik, ServiceLB, and a
local-path-provisioner enabled by default; pass k3s_server_args=["--disable=traefik", ...] to opt out.
ktor.app.register_plugin("k3d",
k8s_version="1.34.6",
profile="my-dev",
start_fresh=True,
keep_running=False,
nodes=5,
control_plane_nodes=3)Node images default to rancher/k3s:v<k8s_version>-k3s1 (multi-arch amd64/arm64, published per K8s patch by Rancher).
Override the suffix via node_image_suffix="-k3s2" when Rancher rebuilds for a given K8s version, or pass
node_image=... for a fully custom image. Lifecycle uses k3d's native cluster start / cluster stop subcommands
(no docker start/stop plumbing needed); keep_running=False stops the cluster without deleting it, and
start_fresh=True deletes and recreates the cluster.
Multi-server HA is supported directly: control_plane_nodes >= 2 causes k3d to auto-spawn a loadbalancer-role
container in front of the API servers. Additional knobs: extra_port_mappings (rendered onto the loadbalancer node),
feature_gates and runtime_config (translated into --kube-apiserver-arg=... server-side flags),
k3s_server_args / k3s_agent_args (raw k3s args targeted at server / agent nodes), or a raw config YAML override
(passed to --config verbatim — schema is k3d.io/v1alpha5 Simple).
Only the Docker provider is supported; k3d's experimental podman path is not offered.
ktor.k3d.version,ktor.k3d.k8s_version,ktor.k3d.profile,ktor.k3d.kubeconfigktor.k3d.node_image,ktor.k3d.node_image_registry,ktor.k3d.node_image_suffixktor.k3d.nodes,ktor.k3d.control_plane_nodes,ktor.k3d.provider(docker)ktor.k3d.start_fresh,ktor.k3d.keep_runningktor.k3d.cmd(*args)/ktor.k3d.cmd_out(*args)Run a
k3dsubcommand, optionally capturing output.
Runs terraform init + terraform output -json in the current directory and merges the outputs into ktor.tf, making
infrastructure values available to scripts. Pass version="1.5.7" to pin a Terraform version; Kubernator will
download the matching binary from HashiCorp.
ktor.terraform.version,ktor.terraform.tf_file,ktor.terraform.stanza()ktor.tfDictionary populated with Terraform outputs (merged across directories that invoked the plugin).
Same shape as the Terraform plugin but invokes terragrunt. Registers terraform implicitly. Pass version=... to
pin a Terragrunt version; the binary is downloaded from GitHub releases.
ktor.terragrunt.version,ktor.terragrunt.tg_file,ktor.terragrunt.stanza()ktor.tf(shared with the Terraform plugin)
The core of Kubernator. Connects to the cluster, resolves its API version, picks a compatible client library (from the
pre-cache or downloading on demand), and collects manifests from YAML files under each traversed directory. During the
apply stage it computes per-resource diffs, honours CRD schemas, retries on conflicts, and performs server-side or
client-side field validation according to configuration.
File discovery uses globs *.yaml / *.yml by default, configurable per-directory via ktor.k8s.includes and
ktor.k8s.excludes.
ktor.k8s.default_includes,ktor.k8s.default_excludes,ktor.k8s.includes,ktor.k8s.excludesktor.k8s.add_resources(manifests, source=None)— register a list/iterable of manifests directly.ktor.k8s.load_resources(path, file_type)— load manifests from a local file ("yaml"/"json").ktor.k8s.load_remote_resources(url, file_type, file_category=None)— load from a URL with caching.ktor.k8s.load_crds(path, file_type)/ktor.k8s.load_remote_crds(url, file_type, file_category=None)— register CRDs separately from consuming resources so their schemas are known during validation.ktor.k8s.import_cluster_crds()— pull CRDs that are already installed on the target cluster.ktor.k8s.add_transformer(func)/ktor.k8s.remove_transformer(func)— register a functionfunc(resources, resource)that may mutate manifests before apply.ktor.k8s.add_validator(func)— register a validator run after transformation.ktor.k8s.add_manifest_patcher(func)— register a low-level manifest patcher.ktor.k8s.get_api_versions()— set of(group, version)tuples in use.ktor.k8s.create_resource(manifest)— wrap a manifest as aK8SResourcewithout registering it.ktor.k8s.resource(manifest)— return a fully-wiredK8SResourcewith API bindings populated, for imperative CRUD (.get(),.create(dry_run=False),.patch(...),.delete(wait=True),.watch()) that bypasses the declarative apply lifecycle. Accepts a manifestdictor a single-document YAML string.ktor.k8s.resource_generator()— iterable of resources fed to the apply pipeline; override this on a subclass / wrap via context binding to filter or augment the set declaratively before apply.ktor.k8s.client,ktor.k8s.server_version,ktor.k8s.server_git_version— access the underlying Kubernetes client.ktor.k8s.field_validation("Ignore"/"Warn"/"Strict"),ktor.k8s.field_validation_warn_fatal,ktor.k8s.disable_client_patches,ktor.k8s.conflict_retry_delay— behavioural knobs, all settable atregister_plugintime.ktor.k8s.patch_field_excludes,ktor.k8s.immutable_changes— advanced patch/diff controls.
Renders Helm charts as Kubernetes resources and feeds them into the k8s pipeline. Files named *.helm.yaml /
*.helm.yml declare releases. Both classic HTTP chart repositories and OCI registries are supported. Register with
version=... to pin a Helm version (auto-downloaded) and optionally check_chart_versions=True to fail the build if a
pinned chart version is not the latest available in its repository.
Example foo.helm.yaml:
repository: https://kubernetes-sigs.github.io/metrics-server
chart: metrics-server
version: 3.12.2
name: metrics-server
namespace: kube-systemOCI form:
chart: oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller
name: arc
namespace: arc-systemsktor.helm.default_includes,ktor.helm.default_excludes,ktor.helm.includes,ktor.helm.excludesktor.helm.helm_file,ktor.helm.stanza()ktor.helm.namespace_transformer— if true, namespaced resources rendered without anamespace:receive the release namespace automatically.ktor.helm.check_chart_versionsktor.helm.add_helm(chart, name, namespace, repository=None, version=None, values=None, values_file=None, include_crds=False)— programmatic equivalent of a*.helm.yamlfile.ktor.helm.add_helm_template(template)— add a release declaration as a dict.
Installs and upgrades Istio using istioctl against an IstioOperator resource. Files matching *.istio.yaml /
*.istio.yml are treated as IstioOperator manifests. Register with version=... to pin istioctl.
ktor.istio.default_includes,ktor.istio.default_excludes,ktor.istio.includes,ktor.istio.excludesktor.istio.istioctl_file,ktor.istio.stanza()ktor.istio.test()— runistioctl version, returning(version_tuple, full_output_dict).
Defines Jinja2 templates and renders them into Kubernetes resources. The plugin uses custom delimiters {${ ... }$}
for expressions and {%$ ... %} for blocks so that templates remain valid-looking YAML.
Files ending *.tmpl.yaml / *.tmpl.yml are processed in two modes:
- a top-level
define:block registers named templates; - a top-level
apply:block renders named templates with supplied values and feeds the results intok8s.
Example define.tmpl.yaml:
define:
- name: test
path: .test.tmpl.yamlExample .test.tmpl.yaml (referenced above):
apiVersion: v1
kind: Namespace
metadata:
name: {${ values.name }$}Example apply.tmpl.yaml:
apply:
- name: test
values:
name: ns1Templates have access to the full context and to extra filters for JSON/YAML output (to_json, to_yaml,
to_yaml_str_block, to_json_yaml_str_block).
ktor.templates.default_includes,ktor.templates.default_excludes,ktor.templates.includes,ktor.templates.excludesktor.templates.render_template(name, source, values=())— render a registered template, returning the rendered string.ktor.templates.apply_template(name, values=(), source=None)— render and register the result as K8s manifests.
A .kubernator.py at the root of a directory containing YAML manifests:
import os
ktor.app.register_plugin("minikube",
k8s_version=os.environ["K8S_VERSION"],
profile="dev",
start_fresh=True,
keep_running=False)
ktor.app.register_plugin("k8s")Run a dry-run:
$ kubernator -p ./manifestsApply for real:
$ kubernator -p ./manifests --yes applyThe full_smoke integration test illustrates a realistic mix:
import os
ktor.app.register_plugin("minikube", k8s_version=os.environ["K8S_VERSION"],
start_fresh=bool(os.environ["START_FRESH"]),
keep_running=bool(os.environ["KEEP_RUNNING"]),
profile="full-smoke")
ktor.app.register_plugin("awscli")
ktor.app.register_plugin("terraform", version="1.5.7")
ktor.app.register_plugin("terragrunt", version="0.48.0")
ktor.app.register_plugin("k8s")
ktor.app.register_plugin("kubectl")
ktor.app.register_plugin("istio", version=os.environ["ISTIO_VERSION"])
ktor.app.register_plugin("helm", version="3.13.2")
ktor.app.register_plugin("templates")ktor.app.repository_credentials_provider(lambda r: ("ssh", "git", None))
ktor.app.walk_remote("git://repo.example.com/org/project?ref=dev", "/project")ktor.app.walk_local("/home/username/local-dir")def remove_replicas(resources, r: "K8SResource"):
if (r.group == "apps" and r.kind in ("StatefulSet", "Deployment")
and "replicas" in r.manifest["spec"]):
logger.warning("Resource %s in %s contains `replica` specification that will be removed. Use HPA!!!",
r, r.source)
del r.manifest["spec"]["replicas"]
ktor.k8s.add_transformer(remove_replicas)ktor.app.register_plugin("k8s")
ktor.k8s.import_cluster_crds()ktor.k8s.load_crds(ktor.app.cwd / ".." / "crd" / "manifests.yaml", "yaml")