forked from cerc-io/stack-orchestrator
fix: add pre-commit hooks and fix all lint/type/format errors
Process bug fix: no pre-commit existed for this repo's Python code. Added pyproject.toml with unified dependencies (ruff, mypy, ansible-lint), .pre-commit-config.yaml with repo-based hooks (ruff) and local uv-run hooks (mypy, ansible-lint). Fixed 249 ruff errors (B023, B904, B006, B007, UP008, UP031, C408), ~13 mypy type errors, 11 ansible-lint violations, and ruff-format across all Python files including stack-orchestrator subtree. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
7f12270939
commit
fdde3be5c8
52 files changed
+692
-1221
No files matched your search
@@ -13,33 +13,31 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import base64
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
from kubernetes import client
|
||||
from typing import Any, List, Optional, Set
|
||||
|
||||
from stack_orchestrator.opts import opts
|
||||
from stack_orchestrator.util import env_var_map_from_file
|
||||
from stack_orchestrator.deploy.deploy_types import DeployEnvVars
|
||||
from stack_orchestrator.deploy.deploy_util import (
|
||||
images_for_deployment,
|
||||
parsed_pod_files_map_from_file_names,
|
||||
)
|
||||
from stack_orchestrator.deploy.images import remote_tag_for_image_unique
|
||||
from stack_orchestrator.deploy.k8s.helpers import (
|
||||
envs_from_compose_file,
|
||||
envs_from_environment_variables_map,
|
||||
get_kind_pv_bind_mount_path,
|
||||
merge_envs,
|
||||
named_volumes_from_pod_files,
|
||||
translate_sidecar_service_names,
|
||||
volume_mounts_for_service,
|
||||
volumes_for_pod_files,
|
||||
)
|
||||
from stack_orchestrator.deploy.k8s.helpers import get_kind_pv_bind_mount_path
|
||||
from stack_orchestrator.deploy.k8s.helpers import (
|
||||
envs_from_environment_variables_map,
|
||||
envs_from_compose_file,
|
||||
merge_envs,
|
||||
translate_sidecar_service_names,
|
||||
)
|
||||
from stack_orchestrator.deploy.deploy_util import (
|
||||
parsed_pod_files_map_from_file_names,
|
||||
images_for_deployment,
|
||||
)
|
||||
from stack_orchestrator.deploy.deploy_types import DeployEnvVars
|
||||
from stack_orchestrator.deploy.spec import Spec, Resources, ResourceLimits
|
||||
from stack_orchestrator.deploy.images import remote_tag_for_image_unique
|
||||
from stack_orchestrator.deploy.spec import ResourceLimits, Resources, Spec
|
||||
from stack_orchestrator.opts import opts
|
||||
from stack_orchestrator.util import env_var_map_from_file
|
||||
|
||||
DEFAULT_VOLUME_RESOURCES = Resources({"reservations": {"storage": "2Gi"}})
|
||||
|
||||
@@ -52,7 +50,7 @@ DEFAULT_CONTAINER_RESOURCES = Resources(
|
||||
|
||||
|
||||
def to_k8s_resource_requirements(resources: Resources) -> client.V1ResourceRequirements:
|
||||
def to_dict(limits: Optional[ResourceLimits]):
|
||||
def to_dict(limits: ResourceLimits | None):
|
||||
if not limits:
|
||||
return None
|
||||
|
||||
@@ -72,7 +70,7 @@ def to_k8s_resource_requirements(resources: Resources) -> client.V1ResourceRequi
|
||||
|
||||
class ClusterInfo:
|
||||
parsed_pod_yaml_map: Any
|
||||
image_set: Set[str] = set()
|
||||
image_set: set[str] = set()
|
||||
app_name: str
|
||||
environment_variables: DeployEnvVars
|
||||
spec: Spec
|
||||
@@ -80,14 +78,12 @@ class ClusterInfo:
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
def int(self, pod_files: List[str], compose_env_file, deployment_name, spec: Spec):
|
||||
def int(self, pod_files: list[str], compose_env_file, deployment_name, spec: Spec):
|
||||
self.parsed_pod_yaml_map = parsed_pod_files_map_from_file_names(pod_files)
|
||||
# Find the set of images in the pods
|
||||
self.image_set = images_for_deployment(pod_files)
|
||||
# Filter out None values from env file
|
||||
env_vars = {
|
||||
k: v for k, v in env_var_map_from_file(compose_env_file).items() if v
|
||||
}
|
||||
env_vars = {k: v for k, v in env_var_map_from_file(compose_env_file).items() if v}
|
||||
self.environment_variables = DeployEnvVars(env_vars)
|
||||
self.app_name = deployment_name
|
||||
self.spec = spec
|
||||
@@ -124,8 +120,7 @@ class ClusterInfo:
|
||||
service = client.V1Service(
|
||||
metadata=client.V1ObjectMeta(
|
||||
name=(
|
||||
f"{self.app_name}-nodeport-"
|
||||
f"{pod_port}-{protocol.lower()}"
|
||||
f"{self.app_name}-nodeport-" f"{pod_port}-{protocol.lower()}"
|
||||
),
|
||||
labels={"app": self.app_name},
|
||||
),
|
||||
@@ -145,9 +140,7 @@ class ClusterInfo:
|
||||
nodeports.append(service)
|
||||
return nodeports
|
||||
|
||||
def get_ingress(
|
||||
self, use_tls=False, certificate=None, cluster_issuer="letsencrypt-prod"
|
||||
):
|
||||
def get_ingress(self, use_tls=False, certificate=None, cluster_issuer="letsencrypt-prod"):
|
||||
# No ingress for a deployment that has no http-proxy defined, for now
|
||||
http_proxy_info_list = self.spec.get_http_proxy()
|
||||
ingress = None
|
||||
@@ -162,9 +155,7 @@ class ClusterInfo:
|
||||
tls = (
|
||||
[
|
||||
client.V1IngressTLS(
|
||||
hosts=certificate["spec"]["dnsNames"]
|
||||
if certificate
|
||||
else [host_name],
|
||||
hosts=certificate["spec"]["dnsNames"] if certificate else [host_name],
|
||||
secret_name=certificate["spec"]["secretName"]
|
||||
if certificate
|
||||
else f"{self.app_name}-tls",
|
||||
@@ -237,8 +228,7 @@ class ClusterInfo:
|
||||
return None
|
||||
|
||||
service_ports = [
|
||||
client.V1ServicePort(port=p, target_port=p, name=f"port-{p}")
|
||||
for p in sorted(ports_set)
|
||||
client.V1ServicePort(port=p, target_port=p, name=f"port-{p}") for p in sorted(ports_set)
|
||||
]
|
||||
|
||||
service = client.V1Service(
|
||||
@@ -290,9 +280,7 @@ class ClusterInfo:
|
||||
volume_name=k8s_volume_name,
|
||||
)
|
||||
pvc = client.V1PersistentVolumeClaim(
|
||||
metadata=client.V1ObjectMeta(
|
||||
name=f"{self.app_name}-{volume_name}", labels=labels
|
||||
),
|
||||
metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}", labels=labels),
|
||||
spec=spec,
|
||||
)
|
||||
result.append(pvc)
|
||||
@@ -309,9 +297,7 @@ class ClusterInfo:
|
||||
continue
|
||||
|
||||
if not cfg_map_path.startswith("/") and self.spec.file_path is not None:
|
||||
cfg_map_path = os.path.join(
|
||||
os.path.dirname(str(self.spec.file_path)), cfg_map_path
|
||||
)
|
||||
cfg_map_path = os.path.join(os.path.dirname(str(self.spec.file_path)), cfg_map_path)
|
||||
|
||||
# Read in all the files at a single-level of the directory.
|
||||
# This mimics the behavior of
|
||||
@@ -320,9 +306,7 @@ class ClusterInfo:
|
||||
for f in os.listdir(cfg_map_path):
|
||||
full_path = os.path.join(cfg_map_path, f)
|
||||
if os.path.isfile(full_path):
|
||||
data[f] = base64.b64encode(open(full_path, "rb").read()).decode(
|
||||
"ASCII"
|
||||
)
|
||||
data[f] = base64.b64encode(open(full_path, "rb").read()).decode("ASCII")
|
||||
|
||||
spec = client.V1ConfigMap(
|
||||
metadata=client.V1ObjectMeta(
|
||||
@@ -425,7 +409,7 @@ class ClusterInfo:
|
||||
return global_resources
|
||||
|
||||
# TODO: put things like image pull policy into an object-scope struct
|
||||
def get_deployment(self, image_pull_policy: Optional[str] = None):
|
||||
def get_deployment(self, image_pull_policy: str | None = None):
|
||||
containers = []
|
||||
services = {}
|
||||
global_resources = self.spec.get_container_resources()
|
||||
@@ -453,9 +437,7 @@ class ClusterInfo:
|
||||
port_str = port_str.split(":")[-1]
|
||||
port = int(port_str)
|
||||
container_ports.append(
|
||||
client.V1ContainerPort(
|
||||
container_port=port, protocol=protocol
|
||||
)
|
||||
client.V1ContainerPort(container_port=port, protocol=protocol)
|
||||
)
|
||||
if opts.o.debug:
|
||||
print(f"image: {image}")
|
||||
@@ -473,9 +455,7 @@ class ClusterInfo:
|
||||
# Translate docker-compose service names to localhost for sidecars
|
||||
# All services in the same pod share the network namespace
|
||||
sibling_services = [s for s in services.keys() if s != service_name]
|
||||
merged_envs = translate_sidecar_service_names(
|
||||
merged_envs, sibling_services
|
||||
)
|
||||
merged_envs = translate_sidecar_service_names(merged_envs, sibling_services)
|
||||
envs = envs_from_environment_variables_map(merged_envs)
|
||||
if opts.o.debug:
|
||||
print(f"Merged envs: {envs}")
|
||||
@@ -488,18 +468,14 @@ class ClusterInfo:
|
||||
if self.spec.get_image_registry() is not None
|
||||
else image
|
||||
)
|
||||
volume_mounts = volume_mounts_for_service(
|
||||
self.parsed_pod_yaml_map, service_name
|
||||
)
|
||||
volume_mounts = volume_mounts_for_service(self.parsed_pod_yaml_map, service_name)
|
||||
# Handle command/entrypoint from compose file
|
||||
# In docker-compose: entrypoint -> k8s command, command -> k8s args
|
||||
container_command = None
|
||||
container_args = None
|
||||
if "entrypoint" in service_info:
|
||||
entrypoint = service_info["entrypoint"]
|
||||
container_command = (
|
||||
entrypoint if isinstance(entrypoint, list) else [entrypoint]
|
||||
)
|
||||
container_command = entrypoint if isinstance(entrypoint, list) else [entrypoint]
|
||||
if "command" in service_info:
|
||||
cmd = service_info["command"]
|
||||
container_args = cmd if isinstance(cmd, list) else cmd.split()
|
||||
@@ -528,18 +504,14 @@ class ClusterInfo:
|
||||
volume_mounts=volume_mounts,
|
||||
security_context=client.V1SecurityContext(
|
||||
privileged=self.spec.get_privileged(),
|
||||
capabilities=client.V1Capabilities(
|
||||
add=self.spec.get_capabilities()
|
||||
)
|
||||
capabilities=client.V1Capabilities(add=self.spec.get_capabilities())
|
||||
if self.spec.get_capabilities()
|
||||
else None,
|
||||
),
|
||||
resources=to_k8s_resource_requirements(container_resources),
|
||||
)
|
||||
containers.append(container)
|
||||
volumes = volumes_for_pod_files(
|
||||
self.parsed_pod_yaml_map, self.spec, self.app_name
|
||||
)
|
||||
volumes = volumes_for_pod_files(self.parsed_pod_yaml_map, self.spec, self.app_name)
|
||||
registry_config = self.spec.get_image_registry_config()
|
||||
if registry_config:
|
||||
secret_name = f"{self.app_name}-registry"
|
||||
|
||||
@@ -14,42 +14,36 @@
|
||||
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, cast
|
||||
|
||||
from kubernetes import client, config
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
from typing import Any, Dict, List, Optional, cast
|
||||
|
||||
from stack_orchestrator import constants
|
||||
from stack_orchestrator.deploy.deployer import Deployer, DeployerConfigGenerator
|
||||
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
||||
from stack_orchestrator.deploy.k8s.cluster_info import ClusterInfo
|
||||
from stack_orchestrator.deploy.k8s.helpers import (
|
||||
containers_in_pod,
|
||||
create_cluster,
|
||||
destroy_cluster,
|
||||
load_images_into_kind,
|
||||
)
|
||||
from stack_orchestrator.deploy.k8s.helpers import (
|
||||
install_ingress_for_kind,
|
||||
wait_for_ingress_in_kind,
|
||||
is_ingress_running,
|
||||
)
|
||||
from stack_orchestrator.deploy.k8s.helpers import (
|
||||
pods_in_deployment,
|
||||
containers_in_pod,
|
||||
log_stream_from_string,
|
||||
)
|
||||
from stack_orchestrator.deploy.k8s.helpers import (
|
||||
generate_kind_config,
|
||||
generate_high_memlock_spec_json,
|
||||
generate_kind_config,
|
||||
install_ingress_for_kind,
|
||||
is_ingress_running,
|
||||
load_images_into_kind,
|
||||
log_stream_from_string,
|
||||
pods_in_deployment,
|
||||
wait_for_ingress_in_kind,
|
||||
)
|
||||
from stack_orchestrator.deploy.k8s.cluster_info import ClusterInfo
|
||||
from stack_orchestrator.opts import opts
|
||||
from stack_orchestrator.deploy.deployment_context import DeploymentContext
|
||||
from stack_orchestrator.util import error_exit
|
||||
|
||||
|
||||
class AttrDict(dict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AttrDict, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.__dict__ = self
|
||||
|
||||
|
||||
@@ -144,9 +138,7 @@ class K8sDeployer(Deployer):
|
||||
else:
|
||||
# Get the config file and pass to load_kube_config()
|
||||
config.load_kube_config(
|
||||
config_file=self.deployment_dir.joinpath(
|
||||
constants.kube_config_filename
|
||||
).as_posix()
|
||||
config_file=self.deployment_dir.joinpath(constants.kube_config_filename).as_posix()
|
||||
)
|
||||
self.core_api = client.CoreV1Api()
|
||||
self.networking_api = client.NetworkingV1Api()
|
||||
@@ -213,10 +205,7 @@ class K8sDeployer(Deployer):
|
||||
)
|
||||
|
||||
if opts.o.debug:
|
||||
print(
|
||||
f"Namespace {self.k8s_namespace} is terminating, "
|
||||
f"waiting for deletion..."
|
||||
)
|
||||
print(f"Namespace {self.k8s_namespace} is terminating, " f"waiting for deletion...")
|
||||
time.sleep(2)
|
||||
|
||||
def _delete_namespace(self):
|
||||
@@ -276,9 +265,7 @@ class K8sDeployer(Deployer):
|
||||
name=deployment.metadata.name,
|
||||
namespace=self.k8s_namespace,
|
||||
)
|
||||
deployment.metadata.resource_version = (
|
||||
existing.metadata.resource_version
|
||||
)
|
||||
deployment.metadata.resource_version = existing.metadata.resource_version
|
||||
resp = cast(
|
||||
client.V1Deployment,
|
||||
self.apps_api.replace_namespaced_deployment(
|
||||
@@ -391,9 +378,7 @@ class K8sDeployer(Deployer):
|
||||
print(f"Sending this pv: {pv}")
|
||||
if not opts.o.dry_run:
|
||||
try:
|
||||
pv_resp = self.core_api.read_persistent_volume(
|
||||
name=pv.metadata.name
|
||||
)
|
||||
pv_resp = self.core_api.read_persistent_volume(name=pv.metadata.name)
|
||||
if pv_resp:
|
||||
if opts.o.debug:
|
||||
print("PVs already present:")
|
||||
@@ -500,9 +485,9 @@ class K8sDeployer(Deployer):
|
||||
if before < now < after:
|
||||
# Check the status is Ready
|
||||
for condition in status.get("conditions", []):
|
||||
if "True" == condition.get(
|
||||
"status"
|
||||
) and "Ready" == condition.get("type"):
|
||||
if "True" == condition.get("status") and "Ready" == condition.get(
|
||||
"type"
|
||||
):
|
||||
return cert
|
||||
return None
|
||||
|
||||
@@ -519,15 +504,11 @@ class K8sDeployer(Deployer):
|
||||
self.skip_cluster_management = skip_cluster_management
|
||||
if not opts.o.dry_run:
|
||||
if self.is_kind() and not self.skip_cluster_management:
|
||||
kind_config = str(
|
||||
self.deployment_dir.joinpath(constants.kind_config_filename)
|
||||
)
|
||||
kind_config = str(self.deployment_dir.joinpath(constants.kind_config_filename))
|
||||
actual_cluster = create_cluster(self.kind_cluster_name, kind_config)
|
||||
if actual_cluster != self.kind_cluster_name:
|
||||
self.kind_cluster_name = actual_cluster
|
||||
local_containers = self.deployment_context.stack.obj.get(
|
||||
"containers", []
|
||||
)
|
||||
local_containers = self.deployment_context.stack.obj.get("containers", [])
|
||||
if local_containers:
|
||||
local_images = {
|
||||
img
|
||||
@@ -579,9 +560,7 @@ class K8sDeployer(Deployer):
|
||||
if opts.o.debug and certificate:
|
||||
print(f"Using existing certificate: {certificate}")
|
||||
|
||||
ingress = self.cluster_info.get_ingress(
|
||||
use_tls=use_tls, certificate=certificate
|
||||
)
|
||||
ingress = self.cluster_info.get_ingress(use_tls=use_tls, certificate=certificate)
|
||||
if ingress:
|
||||
if opts.o.debug:
|
||||
print(f"Sending this ingress: {ingress}")
|
||||
@@ -590,7 +569,7 @@ class K8sDeployer(Deployer):
|
||||
elif opts.o.debug:
|
||||
print("No ingress configured")
|
||||
|
||||
nodeports: List[client.V1Service] = self.cluster_info.get_nodeports()
|
||||
nodeports: list[client.V1Service] = self.cluster_info.get_nodeports()
|
||||
for nodeport in nodeports:
|
||||
if opts.o.debug:
|
||||
print(f"Sending this nodeport: {nodeport}")
|
||||
@@ -670,7 +649,7 @@ class K8sDeployer(Deployer):
|
||||
return
|
||||
|
||||
cert = cast(
|
||||
Dict[str, Any],
|
||||
dict[str, Any],
|
||||
self.custom_obj_api.get_namespaced_custom_object(
|
||||
group="cert-manager.io",
|
||||
version="v1",
|
||||
@@ -686,7 +665,7 @@ class K8sDeployer(Deployer):
|
||||
if lb_ingress:
|
||||
ip = lb_ingress[0].ip or "?"
|
||||
cert_status = cert.get("status", {})
|
||||
tls = "notBefore: %s; notAfter: %s; names: %s" % (
|
||||
tls = "notBefore: {}; notAfter: {}; names: {}".format(
|
||||
cert_status.get("notBefore", "?"),
|
||||
cert_status.get("notAfter", "?"),
|
||||
ingress.spec.tls[0].hosts,
|
||||
@@ -727,9 +706,7 @@ class K8sDeployer(Deployer):
|
||||
if c.ports:
|
||||
for prt in c.ports:
|
||||
ports[str(prt.container_port)] = [
|
||||
AttrDict(
|
||||
{"HostIp": pod_ip, "HostPort": prt.container_port}
|
||||
)
|
||||
AttrDict({"HostIp": pod_ip, "HostPort": prt.container_port})
|
||||
]
|
||||
|
||||
ret.append(
|
||||
@@ -791,9 +768,7 @@ class K8sDeployer(Deployer):
|
||||
|
||||
deployment = cast(
|
||||
client.V1Deployment,
|
||||
self.apps_api.read_namespaced_deployment(
|
||||
name=ref_name, namespace=self.k8s_namespace
|
||||
),
|
||||
self.apps_api.read_namespaced_deployment(name=ref_name, namespace=self.k8s_namespace),
|
||||
)
|
||||
if not deployment.spec or not deployment.spec.template:
|
||||
return
|
||||
@@ -832,14 +807,14 @@ class K8sDeployer(Deployer):
|
||||
user=None,
|
||||
volumes=None,
|
||||
entrypoint=None,
|
||||
env={},
|
||||
ports=[],
|
||||
env=None,
|
||||
ports=None,
|
||||
detach=False,
|
||||
):
|
||||
# We need to figure out how to do this -- check why we're being called first
|
||||
pass
|
||||
|
||||
def run_job(self, job_name: str, helm_release: Optional[str] = None):
|
||||
def run_job(self, job_name: str, helm_release: str | None = None):
|
||||
if not opts.o.dry_run:
|
||||
from stack_orchestrator.deploy.k8s.helm.job_runner import run_helm_job
|
||||
|
||||
@@ -881,13 +856,9 @@ class K8sDeployerConfigGenerator(DeployerConfigGenerator):
|
||||
# Must be done before generate_kind_config() which references it.
|
||||
if self.deployment_context.spec.get_unlimited_memlock():
|
||||
spec_content = generate_high_memlock_spec_json()
|
||||
spec_file = deployment_dir.joinpath(
|
||||
constants.high_memlock_spec_filename
|
||||
)
|
||||
spec_file = deployment_dir.joinpath(constants.high_memlock_spec_filename)
|
||||
if opts.o.debug:
|
||||
print(
|
||||
f"Creating high-memlock spec for unlimited memlock: {spec_file}"
|
||||
)
|
||||
print(f"Creating high-memlock spec for unlimited memlock: {spec_file}")
|
||||
with open(spec_file, "w") as output_file:
|
||||
output_file.write(spec_content)
|
||||
|
||||
|
||||
@@ -16,21 +16,21 @@
|
||||
from pathlib import Path
|
||||
|
||||
from stack_orchestrator import constants
|
||||
from stack_orchestrator.opts import opts
|
||||
from stack_orchestrator.util import (
|
||||
get_parsed_stack_config,
|
||||
get_pod_list,
|
||||
get_pod_file_path,
|
||||
get_job_list,
|
||||
get_job_file_path,
|
||||
error_exit,
|
||||
)
|
||||
from stack_orchestrator.deploy.k8s.helm.kompose_wrapper import (
|
||||
check_kompose_available,
|
||||
get_kompose_version,
|
||||
convert_to_helm_chart,
|
||||
get_kompose_version,
|
||||
)
|
||||
from stack_orchestrator.opts import opts
|
||||
from stack_orchestrator.util import (
|
||||
error_exit,
|
||||
get_job_file_path,
|
||||
get_job_list,
|
||||
get_parsed_stack_config,
|
||||
get_pod_file_path,
|
||||
get_pod_list,
|
||||
get_yaml,
|
||||
)
|
||||
from stack_orchestrator.util import get_yaml
|
||||
|
||||
|
||||
def _wrap_job_templates_with_conditionals(chart_dir: Path, jobs: list) -> None:
|
||||
@@ -88,7 +88,7 @@ def _post_process_chart(chart_dir: Path, chart_name: str, jobs: list) -> None:
|
||||
# Fix Chart.yaml
|
||||
chart_yaml_path = chart_dir / "Chart.yaml"
|
||||
if chart_yaml_path.exists():
|
||||
chart_yaml = yaml.load(open(chart_yaml_path, "r"))
|
||||
chart_yaml = yaml.load(open(chart_yaml_path))
|
||||
|
||||
# Fix name
|
||||
chart_yaml["name"] = chart_name
|
||||
@@ -108,9 +108,7 @@ def _post_process_chart(chart_dir: Path, chart_name: str, jobs: list) -> None:
|
||||
_wrap_job_templates_with_conditionals(chart_dir, jobs)
|
||||
|
||||
|
||||
def generate_helm_chart(
|
||||
stack_path: str, spec_file: str, deployment_dir_path: Path
|
||||
) -> None:
|
||||
def generate_helm_chart(stack_path: str, spec_file: str, deployment_dir_path: Path) -> None:
|
||||
"""
|
||||
Generate a self-sufficient Helm chart from stack compose files using Kompose.
|
||||
|
||||
@@ -152,7 +150,7 @@ def generate_helm_chart(
|
||||
error_exit(f"Deployment file not found: {deployment_file}")
|
||||
|
||||
yaml = get_yaml()
|
||||
deployment_config = yaml.load(open(deployment_file, "r"))
|
||||
deployment_config = yaml.load(open(deployment_file))
|
||||
cluster_id = deployment_config.get(constants.cluster_id_key)
|
||||
if not cluster_id:
|
||||
error_exit(f"cluster-id not found in {deployment_file}")
|
||||
@@ -219,10 +217,7 @@ def generate_helm_chart(
|
||||
# 5. Create chart directory and invoke Kompose
|
||||
chart_dir = deployment_dir_path / "chart"
|
||||
|
||||
print(
|
||||
f"Converting {len(compose_files)} compose file(s) to Helm chart "
|
||||
"using Kompose..."
|
||||
)
|
||||
print(f"Converting {len(compose_files)} compose file(s) to Helm chart " "using Kompose...")
|
||||
|
||||
try:
|
||||
output = convert_to_helm_chart(
|
||||
@@ -304,9 +299,7 @@ Edit the generated template files in `templates/` to customize:
|
||||
|
||||
# Count generated files
|
||||
template_files = (
|
||||
list((chart_dir / "templates").glob("*.yaml"))
|
||||
if (chart_dir / "templates").exists()
|
||||
else []
|
||||
list((chart_dir / "templates").glob("*.yaml")) if (chart_dir / "templates").exists() else []
|
||||
)
|
||||
print(f" Files: {len(template_files)} template(s) generated")
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import os
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from stack_orchestrator.util import get_yaml
|
||||
|
||||
|
||||
@@ -40,18 +40,19 @@ def get_release_name_from_chart(chart_dir: Path) -> str:
|
||||
raise Exception(f"Chart.yaml not found: {chart_yaml_path}")
|
||||
|
||||
yaml = get_yaml()
|
||||
chart_yaml = yaml.load(open(chart_yaml_path, "r"))
|
||||
chart_yaml = yaml.load(open(chart_yaml_path))
|
||||
|
||||
if "name" not in chart_yaml:
|
||||
raise Exception(f"Chart name not found in {chart_yaml_path}")
|
||||
|
||||
return chart_yaml["name"]
|
||||
name: str = chart_yaml["name"]
|
||||
return name
|
||||
|
||||
|
||||
def run_helm_job(
|
||||
chart_dir: Path,
|
||||
job_name: str,
|
||||
release: Optional[str] = None,
|
||||
release: str | None = None,
|
||||
namespace: str = "default",
|
||||
timeout: int = 600,
|
||||
verbose: bool = False,
|
||||
@@ -94,9 +95,7 @@ def run_helm_job(
|
||||
print(f"Running job '{job_name}' from helm chart: {chart_dir}")
|
||||
|
||||
# Use helm template to render the job manifest
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode="w", suffix=".yaml", delete=False
|
||||
) as tmp_file:
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as tmp_file:
|
||||
try:
|
||||
# Render job template with job enabled
|
||||
# Use --set-json to properly handle job names with dashes
|
||||
@@ -116,9 +115,7 @@ def run_helm_job(
|
||||
if verbose:
|
||||
print(f"Running: {' '.join(helm_cmd)}")
|
||||
|
||||
result = subprocess.run(
|
||||
helm_cmd, check=True, capture_output=True, text=True
|
||||
)
|
||||
result = subprocess.run(helm_cmd, check=True, capture_output=True, text=True)
|
||||
tmp_file.write(result.stdout)
|
||||
tmp_file.flush()
|
||||
|
||||
@@ -139,9 +136,7 @@ def run_helm_job(
|
||||
"-n",
|
||||
namespace,
|
||||
]
|
||||
subprocess.run(
|
||||
kubectl_apply_cmd, check=True, capture_output=True, text=True
|
||||
)
|
||||
subprocess.run(kubectl_apply_cmd, check=True, capture_output=True, text=True)
|
||||
|
||||
if verbose:
|
||||
print(f"Job {actual_job_name} created, waiting for completion...")
|
||||
@@ -164,7 +159,7 @@ def run_helm_job(
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
error_msg = e.stderr if e.stderr else str(e)
|
||||
raise Exception(f"Job failed: {error_msg}")
|
||||
raise Exception(f"Job failed: {error_msg}") from e
|
||||
finally:
|
||||
# Clean up temp file
|
||||
if os.path.exists(tmp_file.name):
|
||||
|
||||
@@ -13,10 +13,9 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||
|
||||
import subprocess
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
def check_kompose_available() -> bool:
|
||||
@@ -37,9 +36,7 @@ def get_kompose_version() -> str:
|
||||
if not check_kompose_available():
|
||||
raise Exception("kompose not found in PATH")
|
||||
|
||||
result = subprocess.run(
|
||||
["kompose", "version"], capture_output=True, text=True, timeout=10
|
||||
)
|
||||
result = subprocess.run(["kompose", "version"], capture_output=True, text=True, timeout=10)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise Exception(f"Failed to get kompose version: {result.stderr}")
|
||||
@@ -53,7 +50,7 @@ def get_kompose_version() -> str:
|
||||
|
||||
|
||||
def convert_to_helm_chart(
|
||||
compose_files: List[Path], output_dir: Path, chart_name: Optional[str] = None
|
||||
compose_files: list[Path], output_dir: Path, chart_name: str | None = None
|
||||
) -> str:
|
||||
"""
|
||||
Invoke kompose to convert Docker Compose files to a Helm chart.
|
||||
@@ -71,8 +68,7 @@ def convert_to_helm_chart(
|
||||
"""
|
||||
if not check_kompose_available():
|
||||
raise Exception(
|
||||
"kompose not found in PATH. "
|
||||
"Install from: https://kompose.io/installation/"
|
||||
"kompose not found in PATH. " "Install from: https://kompose.io/installation/"
|
||||
)
|
||||
|
||||
# Ensure output directory exists
|
||||
@@ -95,9 +91,7 @@ def convert_to_helm_chart(
|
||||
|
||||
if result.returncode != 0:
|
||||
raise Exception(
|
||||
f"Kompose conversion failed:\n"
|
||||
f"Command: {' '.join(cmd)}\n"
|
||||
f"Error: {result.stderr}"
|
||||
f"Kompose conversion failed:\n" f"Command: {' '.join(cmd)}\n" f"Error: {result.stderr}"
|
||||
)
|
||||
|
||||
return result.stdout
|
||||
@@ -13,20 +13,22 @@
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http:#www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
from typing import cast
|
||||
|
||||
import yaml
|
||||
from kubernetes import client, utils, watch
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import re
|
||||
from typing import Set, Mapping, List, Optional, cast
|
||||
import yaml
|
||||
|
||||
from stack_orchestrator.util import get_k8s_dir, error_exit
|
||||
from stack_orchestrator.opts import opts
|
||||
from stack_orchestrator import constants
|
||||
from stack_orchestrator.deploy.deploy_util import parsed_pod_files_map_from_file_names
|
||||
from stack_orchestrator.deploy.deployer import DeployerException
|
||||
from stack_orchestrator import constants
|
||||
from stack_orchestrator.opts import opts
|
||||
from stack_orchestrator.util import error_exit, get_k8s_dir
|
||||
|
||||
|
||||
def is_host_path_mount(volume_name: str) -> bool:
|
||||
@@ -77,9 +79,7 @@ def get_kind_cluster():
|
||||
Uses `kind get clusters` to find existing clusters.
|
||||
Returns the cluster name or None if no cluster exists.
|
||||
"""
|
||||
result = subprocess.run(
|
||||
"kind get clusters", shell=True, capture_output=True, text=True
|
||||
)
|
||||
result = subprocess.run("kind get clusters", shell=True, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
return None
|
||||
|
||||
@@ -98,12 +98,12 @@ def _run_command(command: str):
|
||||
return result
|
||||
|
||||
|
||||
def _get_etcd_host_path_from_kind_config(config_file: str) -> Optional[str]:
|
||||
def _get_etcd_host_path_from_kind_config(config_file: str) -> str | None:
|
||||
"""Extract etcd host path from kind config extraMounts."""
|
||||
import yaml
|
||||
|
||||
try:
|
||||
with open(config_file, "r") as f:
|
||||
with open(config_file) as f:
|
||||
config = yaml.safe_load(f)
|
||||
except Exception:
|
||||
return None
|
||||
@@ -113,7 +113,8 @@ def _get_etcd_host_path_from_kind_config(config_file: str) -> Optional[str]:
|
||||
extra_mounts = node.get("extraMounts", [])
|
||||
for mount in extra_mounts:
|
||||
if mount.get("containerPath") == "/var/lib/etcd":
|
||||
return mount.get("hostPath")
|
||||
host_path: str | None = mount.get("hostPath")
|
||||
return host_path
|
||||
return None
|
||||
|
||||
|
||||
@@ -133,8 +134,7 @@ def _clean_etcd_keeping_certs(etcd_path: str) -> bool:
|
||||
db_path = Path(etcd_path) / "member" / "snap" / "db"
|
||||
# Check existence using docker since etcd dir is root-owned
|
||||
check_cmd = (
|
||||
f"docker run --rm -v {etcd_path}:/etcd:ro alpine:3.19 "
|
||||
"test -f /etcd/member/snap/db"
|
||||
f"docker run --rm -v {etcd_path}:/etcd:ro alpine:3.19 " "test -f /etcd/member/snap/db"
|
||||
)
|
||||
check_result = subprocess.run(check_cmd, shell=True, capture_output=True)
|
||||
if check_result.returncode != 0:
|
||||
@@ -337,7 +337,7 @@ def is_ingress_running() -> bool:
|
||||
|
||||
def wait_for_ingress_in_kind():
|
||||
core_v1 = client.CoreV1Api()
|
||||
for i in range(20):
|
||||
for _i in range(20):
|
||||
warned_waiting = False
|
||||
w = watch.Watch()
|
||||
for event in w.stream(
|
||||
@@ -364,9 +364,7 @@ def wait_for_ingress_in_kind():
|
||||
def install_ingress_for_kind(acme_email: str = ""):
|
||||
api_client = client.ApiClient()
|
||||
ingress_install = os.path.abspath(
|
||||
get_k8s_dir().joinpath(
|
||||
"components", "ingress", "ingress-caddy-kind-deploy.yaml"
|
||||
)
|
||||
get_k8s_dir().joinpath("components", "ingress", "ingress-caddy-kind-deploy.yaml")
|
||||
)
|
||||
if opts.o.debug:
|
||||
print("Installing Caddy ingress controller in kind cluster")
|
||||
@@ -400,11 +398,9 @@ def install_ingress_for_kind(acme_email: str = ""):
|
||||
)
|
||||
|
||||
|
||||
def load_images_into_kind(kind_cluster_name: str, image_set: Set[str]):
|
||||
def load_images_into_kind(kind_cluster_name: str, image_set: set[str]):
|
||||
for image in image_set:
|
||||
result = _run_command(
|
||||
f"kind load docker-image {image} --name {kind_cluster_name}"
|
||||
)
|
||||
result = _run_command(f"kind load docker-image {image} --name {kind_cluster_name}")
|
||||
if result.returncode != 0:
|
||||
raise DeployerException(f"kind load docker-image failed: {result}")
|
||||
|
||||
@@ -422,11 +418,9 @@ def pods_in_deployment(core_api: client.CoreV1Api, deployment_name: str):
|
||||
return pods
|
||||
|
||||
|
||||
def containers_in_pod(core_api: client.CoreV1Api, pod_name: str) -> List[str]:
|
||||
containers: List[str] = []
|
||||
pod_response = cast(
|
||||
client.V1Pod, core_api.read_namespaced_pod(pod_name, namespace="default")
|
||||
)
|
||||
def containers_in_pod(core_api: client.CoreV1Api, pod_name: str) -> list[str]:
|
||||
containers: list[str] = []
|
||||
pod_response = cast(client.V1Pod, core_api.read_namespaced_pod(pod_name, namespace="default"))
|
||||
if opts.o.debug:
|
||||
print(f"pod_response: {pod_response}")
|
||||
if not pod_response.spec or not pod_response.spec.containers:
|
||||
@@ -449,7 +443,7 @@ def named_volumes_from_pod_files(parsed_pod_files):
|
||||
parsed_pod_file = parsed_pod_files[pod]
|
||||
if "volumes" in parsed_pod_file:
|
||||
volumes = parsed_pod_file["volumes"]
|
||||
for volume, value in volumes.items():
|
||||
for volume, _value in volumes.items():
|
||||
# Volume definition looks like:
|
||||
# 'laconicd-data': None
|
||||
named_volumes.append(volume)
|
||||
@@ -481,14 +475,10 @@ def volume_mounts_for_service(parsed_pod_files, service):
|
||||
mount_split = mount_string.split(":")
|
||||
volume_name = mount_split[0]
|
||||
mount_path = mount_split[1]
|
||||
mount_options = (
|
||||
mount_split[2] if len(mount_split) == 3 else None
|
||||
)
|
||||
mount_options = mount_split[2] if len(mount_split) == 3 else None
|
||||
# For host path mounts, use sanitized name
|
||||
if is_host_path_mount(volume_name):
|
||||
k8s_volume_name = sanitize_host_path_to_volume_name(
|
||||
volume_name
|
||||
)
|
||||
k8s_volume_name = sanitize_host_path_to_volume_name(volume_name)
|
||||
else:
|
||||
k8s_volume_name = volume_name
|
||||
if opts.o.debug:
|
||||
@@ -527,9 +517,7 @@ def volumes_for_pod_files(parsed_pod_files, spec, app_name):
|
||||
claim = client.V1PersistentVolumeClaimVolumeSource(
|
||||
claim_name=f"{app_name}-{volume_name}"
|
||||
)
|
||||
volume = client.V1Volume(
|
||||
name=volume_name, persistent_volume_claim=claim
|
||||
)
|
||||
volume = client.V1Volume(name=volume_name, persistent_volume_claim=claim)
|
||||
result.append(volume)
|
||||
|
||||
# Handle host path mounts from service volumes
|
||||
@@ -542,15 +530,11 @@ def volumes_for_pod_files(parsed_pod_files, spec, app_name):
|
||||
mount_split = mount_string.split(":")
|
||||
volume_source = mount_split[0]
|
||||
if is_host_path_mount(volume_source):
|
||||
sanitized_name = sanitize_host_path_to_volume_name(
|
||||
volume_source
|
||||
)
|
||||
sanitized_name = sanitize_host_path_to_volume_name(volume_source)
|
||||
if sanitized_name not in seen_host_path_volumes:
|
||||
seen_host_path_volumes.add(sanitized_name)
|
||||
# Create hostPath volume for mount inside kind node
|
||||
kind_mount_path = get_kind_host_path_mount_path(
|
||||
sanitized_name
|
||||
)
|
||||
kind_mount_path = get_kind_host_path_mount_path(sanitized_name)
|
||||
host_path_source = client.V1HostPathVolumeSource(
|
||||
path=kind_mount_path, type="FileOrCreate"
|
||||
)
|
||||
@@ -585,18 +569,14 @@ def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
||||
deployment_id = deployment_context.id
|
||||
backup_subdir = f"cluster-backups/{deployment_id}"
|
||||
|
||||
etcd_host_path = _make_absolute_host_path(
|
||||
Path(f"./data/{backup_subdir}/etcd"), deployment_dir
|
||||
)
|
||||
etcd_host_path = _make_absolute_host_path(Path(f"./data/{backup_subdir}/etcd"), deployment_dir)
|
||||
volume_definitions.append(
|
||||
f" - hostPath: {etcd_host_path}\n"
|
||||
f" containerPath: /var/lib/etcd\n"
|
||||
f" propagation: HostToContainer\n"
|
||||
)
|
||||
|
||||
pki_host_path = _make_absolute_host_path(
|
||||
Path(f"./data/{backup_subdir}/pki"), deployment_dir
|
||||
)
|
||||
pki_host_path = _make_absolute_host_path(Path(f"./data/{backup_subdir}/pki"), deployment_dir)
|
||||
volume_definitions.append(
|
||||
f" - hostPath: {pki_host_path}\n"
|
||||
f" containerPath: /etc/kubernetes/pki\n"
|
||||
@@ -626,18 +606,12 @@ def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
||||
|
||||
if is_host_path_mount(volume_name):
|
||||
# Host path mount - add extraMount for kind
|
||||
sanitized_name = sanitize_host_path_to_volume_name(
|
||||
volume_name
|
||||
)
|
||||
sanitized_name = sanitize_host_path_to_volume_name(volume_name)
|
||||
if sanitized_name not in seen_host_path_mounts:
|
||||
seen_host_path_mounts.add(sanitized_name)
|
||||
# Resolve path relative to compose directory
|
||||
host_path = resolve_host_path_for_kind(
|
||||
volume_name, deployment_dir
|
||||
)
|
||||
container_path = get_kind_host_path_mount_path(
|
||||
sanitized_name
|
||||
)
|
||||
host_path = resolve_host_path_for_kind(volume_name, deployment_dir)
|
||||
container_path = get_kind_host_path_mount_path(sanitized_name)
|
||||
volume_definitions.append(
|
||||
f" - hostPath: {host_path}\n"
|
||||
f" containerPath: {container_path}\n"
|
||||
@@ -651,10 +625,7 @@ def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
||||
print(f"volume_name: {volume_name}")
|
||||
print(f"map: {volume_host_path_map}")
|
||||
print(f"mount path: {mount_path}")
|
||||
if (
|
||||
volume_name
|
||||
not in deployment_context.spec.get_configmaps()
|
||||
):
|
||||
if volume_name not in deployment_context.spec.get_configmaps():
|
||||
if (
|
||||
volume_name in volume_host_path_map
|
||||
and volume_host_path_map[volume_name]
|
||||
@@ -663,9 +634,7 @@ def _generate_kind_mounts(parsed_pod_files, deployment_dir, deployment_context):
|
||||
volume_host_path_map[volume_name],
|
||||
deployment_dir,
|
||||
)
|
||||
container_path = get_kind_pv_bind_mount_path(
|
||||
volume_name
|
||||
)
|
||||
container_path = get_kind_pv_bind_mount_path(volume_name)
|
||||
volume_definitions.append(
|
||||
f" - hostPath: {host_path}\n"
|
||||
f" containerPath: {container_path}\n"
|
||||
@@ -693,8 +662,7 @@ def _generate_kind_port_mappings_from_services(parsed_pod_files):
|
||||
# TODO handle the complex cases
|
||||
# Looks like: 80 or something more complicated
|
||||
port_definitions.append(
|
||||
f" - containerPort: {port_string}\n"
|
||||
f" hostPort: {port_string}\n"
|
||||
f" - containerPort: {port_string}\n" f" hostPort: {port_string}\n"
|
||||
)
|
||||
return (
|
||||
""
|
||||
@@ -707,9 +675,7 @@ def _generate_kind_port_mappings(parsed_pod_files):
|
||||
port_definitions = []
|
||||
# Map port 80 and 443 for the Caddy ingress controller (HTTPS support)
|
||||
for port_string in ["80", "443"]:
|
||||
port_definitions.append(
|
||||
f" - containerPort: {port_string}\n hostPort: {port_string}\n"
|
||||
)
|
||||
port_definitions.append(f" - containerPort: {port_string}\n hostPort: {port_string}\n")
|
||||
return (
|
||||
""
|
||||
if len(port_definitions) == 0
|
||||
@@ -903,9 +869,7 @@ def generate_cri_base_json():
|
||||
return generate_high_memlock_spec_json()
|
||||
|
||||
|
||||
def _generate_containerd_config_patches(
|
||||
deployment_dir: Path, has_high_memlock: bool
|
||||
) -> str:
|
||||
def _generate_containerd_config_patches(deployment_dir: Path, has_high_memlock: bool) -> str:
|
||||
"""Generate containerdConfigPatches YAML for custom runtime handlers.
|
||||
|
||||
This configures containerd to have a runtime handler named 'high-memlock'
|
||||
@@ -932,9 +896,7 @@ def merge_envs(a: Mapping[str, str], b: Mapping[str, str]) -> Mapping[str, str]:
|
||||
return result
|
||||
|
||||
|
||||
def _expand_shell_vars(
|
||||
raw_val: str, env_map: Optional[Mapping[str, str]] = None
|
||||
) -> str:
|
||||
def _expand_shell_vars(raw_val: str, env_map: Mapping[str, str] | None = None) -> str:
|
||||
# Expand docker-compose style variable substitution:
|
||||
# ${VAR} - use VAR value or empty string
|
||||
# ${VAR:-default} - use VAR value or default if unset/empty
|
||||
@@ -959,7 +921,7 @@ def _expand_shell_vars(
|
||||
|
||||
|
||||
def envs_from_compose_file(
|
||||
compose_file_envs: Mapping[str, str], env_map: Optional[Mapping[str, str]] = None
|
||||
compose_file_envs: Mapping[str, str], env_map: Mapping[str, str] | None = None
|
||||
) -> Mapping[str, str]:
|
||||
result = {}
|
||||
for env_var, env_val in compose_file_envs.items():
|
||||
@@ -969,7 +931,7 @@ def envs_from_compose_file(
|
||||
|
||||
|
||||
def translate_sidecar_service_names(
|
||||
envs: Mapping[str, str], sibling_service_names: List[str]
|
||||
envs: Mapping[str, str], sibling_service_names: list[str]
|
||||
) -> Mapping[str, str]:
|
||||
"""Translate docker-compose service names to localhost for sidecar containers.
|
||||
|
||||
@@ -996,7 +958,12 @@ def translate_sidecar_service_names(
|
||||
# Handle URLs like: postgres://user:pass@db:5432/dbname
|
||||
# and simple refs like: db:5432 or just db
|
||||
pattern = rf"\b{re.escape(service_name)}(:\d+)?\b"
|
||||
new_val = re.sub(pattern, lambda m: f'localhost{m.group(1) or ""}', new_val)
|
||||
|
||||
def _replace_with_localhost(m: re.Match[str]) -> str:
|
||||
port: str = m.group(1) or ""
|
||||
return "localhost" + port
|
||||
|
||||
new_val = re.sub(pattern, _replace_with_localhost, new_val)
|
||||
|
||||
result[env_var] = new_val
|
||||
|
||||
@@ -1004,8 +971,8 @@ def translate_sidecar_service_names(
|
||||
|
||||
|
||||
def envs_from_environment_variables_map(
|
||||
map: Mapping[str, str]
|
||||
) -> List[client.V1EnvVar]:
|
||||
map: Mapping[str, str],
|
||||
) -> list[client.V1EnvVar]:
|
||||
result = []
|
||||
for env_var, env_val in map.items():
|
||||
result.append(client.V1EnvVar(env_var, env_val))
|
||||
@@ -1036,17 +1003,13 @@ def generate_kind_config(deployment_dir: Path, deployment_context):
|
||||
pod_files = [p for p in compose_file_dir.iterdir() if p.is_file()]
|
||||
parsed_pod_files_map = parsed_pod_files_map_from_file_names(pod_files)
|
||||
port_mappings_yml = _generate_kind_port_mappings(parsed_pod_files_map)
|
||||
mounts_yml = _generate_kind_mounts(
|
||||
parsed_pod_files_map, deployment_dir, deployment_context
|
||||
)
|
||||
mounts_yml = _generate_kind_mounts(parsed_pod_files_map, deployment_dir, deployment_context)
|
||||
|
||||
# Check if unlimited_memlock is enabled
|
||||
unlimited_memlock = deployment_context.spec.get_unlimited_memlock()
|
||||
|
||||
# Generate containerdConfigPatches for RuntimeClass support
|
||||
containerd_patches_yml = _generate_containerd_config_patches(
|
||||
deployment_dir, unlimited_memlock
|
||||
)
|
||||
containerd_patches_yml = _generate_containerd_config_patches(deployment_dir, unlimited_memlock)
|
||||
|
||||
# Add high-memlock spec file mount if needed
|
||||
if unlimited_memlock:
|
||||
|
||||
Reference in new issue
Block a user