From a750b645b90259f3148c44f92c80f615dc18ce3d Mon Sep 17 00:00:00 2001 From: David Boreham Date: Tue, 30 Jan 2024 11:18:08 -0700 Subject: [PATCH 1/9] Merge Ci test branch fixes (#717) --- .gitea/workflows/test-k8s-deploy.yml | 3 +++ tests/k8s-deploy/run-deploy-test.sh | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/test-k8s-deploy.yml b/.gitea/workflows/test-k8s-deploy.yml index 208b8b0c..92c76a7d 100644 --- a/.gitea/workflows/test-k8s-deploy.yml +++ b/.gitea/workflows/test-k8s-deploy.yml @@ -36,6 +36,8 @@ jobs: run: ./scripts/create_build_tag_file.sh - name: "Build local shiv package" run: ./scripts/build_shiv_package.sh + - name: "Check cgroups version" + run: mount | grep cgroup - name: "Install kind" run: ./tests/scripts/install-kind.sh - name: "Install Kubectl" @@ -45,3 +47,4 @@ jobs: source /opt/bash-utils/cgroup-helper.sh join_cgroup ./tests/k8s-deploy/run-deploy-test.sh + diff --git a/tests/k8s-deploy/run-deploy-test.sh b/tests/k8s-deploy/run-deploy-test.sh index 0c7246e0..de1a5f10 100755 --- a/tests/k8s-deploy/run-deploy-test.sh +++ b/tests/k8s-deploy/run-deploy-test.sh @@ -120,7 +120,8 @@ fi # Stop then start again and check the volume was preserved $TEST_TARGET_SO deployment --dir $test_deployment_dir stop # Sleep a bit just in case -sleep 2 +# sleep for longer to check if that's why the subsequent create cluster fails +sleep 20 $TEST_TARGET_SO deployment --dir $test_deployment_dir start wait_for_pods_started wait_for_log_output From 428b05158ed44affe1668e5f420972269dd53f6f Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Tue, 30 Jan 2024 13:31:59 -0600 Subject: [PATCH 2/9] Fix DnsRecord ownership check. (#718) * Fix DnsRecord ownership check. * Var names --- .../deploy/webapp/deploy_webapp_from_registry.py | 4 ++-- .../deploy/webapp/undeploy_webapp_from_registry.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py b/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py index 2e4544eb..baaa31db 100644 --- a/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py +++ b/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py @@ -59,8 +59,8 @@ def process_app_deployment_request( dns_record = laconic.get_record(dns_crn) if dns_record: matched_owner = match_owner(app_deployment_request, dns_record) - if not matched_owner and dns_record.request: - matched_owner = match_owner(app_deployment_request, laconic.get_record(dns_record.request, require=True)) + if not matched_owner and dns_record.attributes.request: + matched_owner = match_owner(app_deployment_request, laconic.get_record(dns_record.attributes.request, require=True)) if matched_owner: print("Matched DnsRecord ownership:", matched_owner) diff --git a/stack_orchestrator/deploy/webapp/undeploy_webapp_from_registry.py b/stack_orchestrator/deploy/webapp/undeploy_webapp_from_registry.py index 6c5cac85..74cf0d60 100644 --- a/stack_orchestrator/deploy/webapp/undeploy_webapp_from_registry.py +++ b/stack_orchestrator/deploy/webapp/undeploy_webapp_from_registry.py @@ -40,8 +40,8 @@ def process_app_removal_request(ctx, matched_owner = match_owner(app_removal_request, deployment_record, dns_record) # Or of the original deployment request. - if not matched_owner and deployment_record.request: - matched_owner = match_owner(app_removal_request, laconic.get_record(deployment_record.request, require=True)) + if not matched_owner and deployment_record.attributes.request: + matched_owner = match_owner(app_removal_request, laconic.get_record(deployment_record.attributes.request, require=True)) if matched_owner: print("Matched deployment ownership:", matched_owner) From 62af03077f44c0f9b853fe91f77228e9b1990d41 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Tue, 30 Jan 2024 22:13:45 -0600 Subject: [PATCH 3/9] Add deployed/error status output to the state file. (#719) * More status info * Up default resource limits. * Need ps --- .../container-build/cerc-nextjs-base/Dockerfile | 2 +- stack_orchestrator/deploy/k8s/cluster_info.py | 2 +- .../deploy/webapp/deploy_webapp_from_registry.py | 16 ++++++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/stack_orchestrator/data/container-build/cerc-nextjs-base/Dockerfile b/stack_orchestrator/data/container-build/cerc-nextjs-base/Dockerfile index 8949c4e9..c3c12cc6 100644 --- a/stack_orchestrator/data/container-build/cerc-nextjs-base/Dockerfile +++ b/stack_orchestrator/data/container-build/cerc-nextjs-base/Dockerfile @@ -30,7 +30,7 @@ RUN \ # [Optional] Uncomment this section to install additional OS packages. RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends jq gettext-base + && apt-get -y install --no-install-recommends jq gettext-base procps # [Optional] Uncomment if you want to install more global node modules # RUN su node -c "npm install -g " diff --git a/stack_orchestrator/deploy/k8s/cluster_info.py b/stack_orchestrator/deploy/k8s/cluster_info.py index 85fd63a8..9ba8041f 100644 --- a/stack_orchestrator/deploy/k8s/cluster_info.py +++ b/stack_orchestrator/deploy/k8s/cluster_info.py @@ -195,7 +195,7 @@ class ClusterInfo: volume_mounts=volume_mounts, resources=client.V1ResourceRequirements( requests={"cpu": "100m", "memory": "200Mi"}, - limits={"cpu": "500m", "memory": "500Mi"}, + limits={"cpu": "1000m", "memory": "2000Mi"}, ), ) containers.append(container) diff --git a/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py b/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py index baaa31db..45f50377 100644 --- a/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py +++ b/stack_orchestrator/deploy/webapp/deploy_webapp_from_registry.py @@ -136,13 +136,17 @@ def load_known_requests(filename): return {} -def dump_known_requests(filename, requests): +def dump_known_requests(filename, requests, status="SEEN"): if not filename: return known_requests = load_known_requests(filename) for r in requests: - known_requests[r.id] = r.createTime - json.dump(known_requests, open(filename, "w")) + known_requests[r.id] = { + "createTime": r.createTime, + "status": status + } + with open(filename, "w") as f: + json.dump(known_requests, f) @click.command() @@ -201,6 +205,7 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_ requests.reverse() requests_by_name = {} for r in requests: + # TODO: Do this _after_ filtering deployments and cancellations to minimize round trips. app = laconic.get_record(r.attributes.application) if not app: print("Skipping request %s, cannot locate app." % r.id) @@ -256,6 +261,8 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_ if not dry_run: for r in requests_to_execute: + dump_known_requests(state_file, [r], "DEPLOYING") + status = "ERROR" try: process_app_deployment_request( ctx, @@ -268,5 +275,6 @@ def command(ctx, kube_config, laconic_config, image_registry, deployment_parent_ kube_config, image_registry ) + status = "DEPLOYED" finally: - dump_known_requests(state_file, [r]) + dump_known_requests(state_file, [r], status) From 12ec1bec43163fc01ab59b66a8c896deb3806c49 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Tue, 30 Jan 2024 23:09:48 -0600 Subject: [PATCH 4/9] Add ConfigMap support for k8s. (#714) * Minor fixes for deploying with k8s and podman. * ConfigMap support --- stack_orchestrator/build/build_containers.py | 5 ++ .../deploy/deployment_create.py | 62 ++++++++++++-- stack_orchestrator/deploy/images.py | 3 +- stack_orchestrator/deploy/k8s/cluster_info.py | 80 ++++++++++++++----- stack_orchestrator/deploy/k8s/deploy_k8s.py | 75 ++++++++++++----- stack_orchestrator/deploy/k8s/helpers.py | 25 ++++-- stack_orchestrator/deploy/spec.py | 12 +++ 7 files changed, 208 insertions(+), 54 deletions(-) diff --git a/stack_orchestrator/build/build_containers.py b/stack_orchestrator/build/build_containers.py index e987c504..7b0957b0 100644 --- a/stack_orchestrator/build/build_containers.py +++ b/stack_orchestrator/build/build_containers.py @@ -33,6 +33,7 @@ from stack_orchestrator.base import get_npm_registry_url # TODO: find a place for this # epilog="Config provided either in .env or settings.ini or env vars: CERC_REPO_BASE_DIR (defaults to ~/cerc)" + def make_container_build_env(dev_root_path: str, container_build_dir: str, debug: bool, @@ -104,6 +105,9 @@ def process_container(stack: str, build_command = os.path.join(container_build_dir, "default-build.sh") + f" {default_container_tag} {repo_dir_or_build_dir}" if not dry_run: + # No PATH at all causes failures with podman. + if "PATH" not in container_build_env: + container_build_env["PATH"] = os.environ["PATH"] if verbose: print(f"Executing: {build_command} with environment: {container_build_env}") build_result = subprocess.run(build_command, shell=True, env=container_build_env) @@ -119,6 +123,7 @@ def process_container(stack: str, else: print("Skipped") + @click.command() @click.option('--include', help="only build these containers") @click.option('--exclude', help="don\'t build these containers") diff --git a/stack_orchestrator/deploy/deployment_create.py b/stack_orchestrator/deploy/deployment_create.py index 138fc1bb..b95386b1 100644 --- a/stack_orchestrator/deploy/deployment_create.py +++ b/stack_orchestrator/deploy/deployment_create.py @@ -54,19 +54,44 @@ def _get_ports(stack): def _get_named_volumes(stack): # Parse the compose files looking for named volumes - named_volumes = [] + named_volumes = { + "rw": [], + "ro": [] + } parsed_stack = get_parsed_stack_config(stack) pods = get_pod_list(parsed_stack) yaml = get_yaml() + + def find_vol_usage(parsed_pod_file, vol): + ret = {} + if "services" in parsed_pod_file: + for svc_name, svc in parsed_pod_file["services"].items(): + if "volumes" in svc: + for svc_volume in svc["volumes"]: + parts = svc_volume.split(":") + if parts[0] == vol: + ret[svc_name] = { + "volume": parts[0], + "mount": parts[1], + "options": parts[2] if len(parts) == 3 else None + } + return ret + for pod in pods: pod_file_path = get_pod_file_path(parsed_stack, pod) parsed_pod_file = yaml.load(open(pod_file_path, "r")) if "volumes" in parsed_pod_file: volumes = parsed_pod_file["volumes"] for volume in volumes.keys(): - # Volume definition looks like: - # 'laconicd-data': None - named_volumes.append(volume) + for vu in find_vol_usage(parsed_pod_file, volume).values(): + read_only = vu["options"] == "ro" + if read_only: + if vu["volume"] not in named_volumes["rw"] and vu["volume"] not in named_volumes["ro"]: + named_volumes["ro"].append(vu["volume"]) + else: + if vu["volume"] not in named_volumes["rw"]: + named_volumes["rw"].append(vu["volume"]) + return named_volumes @@ -98,12 +123,24 @@ def _fixup_pod_file(pod, spec, compose_dir): _create_bind_dir_if_relative(volume, volume_spec, compose_dir) new_volume_spec = {"driver": "local", "driver_opts": { - "type": "none", - "device": volume_spec_fixedup, - "o": "bind" + "type": "none", + "device": volume_spec_fixedup, + "o": "bind" } } pod["volumes"][volume] = new_volume_spec + + # Fix up configmaps + if "configmaps" in spec: + spec_cfgmaps = spec["configmaps"] + if "volumes" in pod: + pod_volumes = pod["volumes"] + for volume in pod_volumes.keys(): + if volume in spec_cfgmaps: + volume_cfg = spec_cfgmaps[volume] + # Just make the dir (if necessary) + _create_bind_dir_if_relative(volume, volume_cfg, compose_dir) + # Fix up ports if "network" in spec and "ports" in spec["network"]: spec_ports = spec["network"]["ports"] @@ -319,9 +356,18 @@ def init_operation(deploy_command_context, stack, deployer_type, config, named_volumes = _get_named_volumes(stack) if named_volumes: volume_descriptors = {} - for named_volume in named_volumes: + configmap_descriptors = {} + for named_volume in named_volumes["rw"]: volume_descriptors[named_volume] = f"./data/{named_volume}" + for named_volume in named_volumes["ro"]: + if "k8s" in deployer_type: + if "config" in named_volume: + configmap_descriptors[named_volume] = f"./data/{named_volume}" + else: + volume_descriptors[named_volume] = f"./data/{named_volume}" spec_file_content["volumes"] = volume_descriptors + if configmap_descriptors: + spec_file_content["configmaps"] = configmap_descriptors if opts.o.debug: print(f"Creating spec file for stack: {stack} with content: {spec_file_content}") diff --git a/stack_orchestrator/deploy/images.py b/stack_orchestrator/deploy/images.py index ddbb33f7..28d3bdb8 100644 --- a/stack_orchestrator/deploy/images.py +++ b/stack_orchestrator/deploy/images.py @@ -31,7 +31,8 @@ def _image_needs_pushed(image: str): def remote_tag_for_image(image: str, remote_repo_url: str): # Turns image tags of the form: foo/bar:local into remote.repo/org/bar:deploy - (org, image_name_with_version) = image.split("/") + major_parts = image.split("/", 2) + image_name_with_version = major_parts[1] if 2 == len(major_parts) else major_parts[0] (image_name, image_version) = image_name_with_version.split(":") if image_version == "local": return f"{remote_repo_url}/{image_name}:deploy" diff --git a/stack_orchestrator/deploy/k8s/cluster_info.py b/stack_orchestrator/deploy/k8s/cluster_info.py index 9ba8041f..f0e7c87b 100644 --- a/stack_orchestrator/deploy/k8s/cluster_info.py +++ b/stack_orchestrator/deploy/k8s/cluster_info.py @@ -13,6 +13,8 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import os + from kubernetes import client from typing import Any, List, Set @@ -112,9 +114,10 @@ class ClusterInfo: services = pod["services"] for service_name in services: service_info = services[service_name] - port = int(service_info["ports"][0]) - if opts.o.debug: - print(f"service port: {port}") + if "ports" in service_info: + port = int(service_info["ports"][0]) + if opts.o.debug: + print(f"service port: {port}") service = client.V1Service( metadata=client.V1ObjectMeta(name=f"{self.app_name}-service"), spec=client.V1ServiceSpec( @@ -130,30 +133,70 @@ class ClusterInfo: def get_pvcs(self): result = [] - volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map) + spec_volumes = self.spec.get_volumes() + named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map) if opts.o.debug: - print(f"Volumes: {volumes}") - for volume_name in volumes: + print(f"Spec Volumes: {spec_volumes}") + print(f"Named Volumes: {named_volumes}") + for volume_name in spec_volumes: + if volume_name not in named_volumes: + if opts.o.debug: + print(f"{volume_name} not in pod files") + continue spec = client.V1PersistentVolumeClaimSpec( access_modes=["ReadWriteOnce"], storage_class_name="manual", resources=client.V1ResourceRequirements( requests={"storage": "2Gi"} ), - volume_name=volume_name + volume_name=f"{self.app_name}-{volume_name}" ) pvc = client.V1PersistentVolumeClaim( - metadata=client.V1ObjectMeta(name=volume_name, - labels={"volume-label": volume_name}), + metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}", + labels={"volume-label": f"{self.app_name}-{volume_name}"}), spec=spec, ) result.append(pvc) return result + def get_configmaps(self): + result = [] + spec_configmaps = self.spec.get_configmaps() + named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map) + for cfg_map_name, cfg_map_path in spec_configmaps.items(): + if cfg_map_name not in named_volumes: + if opts.o.debug: + print(f"{cfg_map_name} not in pod files") + continue + + if not cfg_map_path.startswith("/"): + cfg_map_path = os.path.join(os.path.dirname(self.spec.file_path), cfg_map_path) + + # Read in all the files at a single-level of the directory. This mimics the behavior + # of `kubectl create configmap foo --from-file=/path/to/dir` + data = {} + 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] = open(full_path, 'rt').read() + + spec = client.V1ConfigMap( + metadata=client.V1ObjectMeta(name=f"{self.app_name}-{cfg_map_name}", + labels={"configmap-label": cfg_map_name}), + data=data + ) + result.append(spec) + return result + def get_pvs(self): result = [] - volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map) - for volume_name in volumes: + spec_volumes = self.spec.get_volumes() + named_volumes = named_volumes_from_pod_files(self.parsed_pod_yaml_map) + for volume_name in spec_volumes: + if volume_name not in named_volumes: + if opts.o.debug: + print(f"{volume_name} not in pod files") + continue spec = client.V1PersistentVolumeSpec( storage_class_name="manual", access_modes=["ReadWriteOnce"], @@ -161,8 +204,8 @@ class ClusterInfo: host_path=client.V1HostPathVolumeSource(path=get_node_pv_mount_path(volume_name)) ) pv = client.V1PersistentVolume( - metadata=client.V1ObjectMeta(name=volume_name, - labels={"volume-label": volume_name}), + metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}", + labels={"volume-label": f"{self.app_name}-{volume_name}"}), spec=spec, ) result.append(pv) @@ -178,10 +221,11 @@ class ClusterInfo: container_name = service_name service_info = services[service_name] image = service_info["image"] - port = int(service_info["ports"][0]) - if opts.o.debug: - print(f"image: {image}") - print(f"service port: {port}") + if "ports" in service_info: + port = int(service_info["ports"][0]) + if opts.o.debug: + print(f"image: {image}") + print(f"service port: {port}") # Re-write the image tag for remote deployment image_to_use = remote_tag_for_image( image, self.spec.get_image_registry()) if self.spec.get_image_registry() is not None else image @@ -199,7 +243,7 @@ class ClusterInfo: ), ) containers.append(container) - volumes = volumes_for_pod_files(self.parsed_pod_yaml_map) + volumes = volumes_for_pod_files(self.parsed_pod_yaml_map, self.spec) image_pull_secrets = [client.V1LocalObjectReference(name="laconic-registry")] template = client.V1PodTemplateSpec( metadata=client.V1ObjectMeta(labels={"app": self.app_name}), diff --git a/stack_orchestrator/deploy/k8s/deploy_k8s.py b/stack_orchestrator/deploy/k8s/deploy_k8s.py index 0a339fe9..045d1893 100644 --- a/stack_orchestrator/deploy/k8s/deploy_k8s.py +++ b/stack_orchestrator/deploy/k8s/deploy_k8s.py @@ -1,5 +1,4 @@ # Copyright © 2023 Vulcanize - # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or @@ -110,6 +109,20 @@ class K8sDeployer(Deployer): if opts.o.debug: print("PVCs created:") print(f"{pvc_resp}") + + # Figure out the ConfigMaps for this deployment + config_maps = self.cluster_info.get_configmaps() + for cfg_map in config_maps: + if opts.o.debug: + print(f"Sending this ConfigMap: {cfg_map}") + cfg_rsp = self.core_api.create_namespaced_config_map( + body=cfg_map, + namespace=self.k8s_namespace + ) + if opts.o.debug: + print("ConfigMap created:") + print(f"{cfg_rsp}") + # Process compose files into a Deployment deployment = self.cluster_info.get_deployment(image_pull_policy=None if self.is_kind() else "Always") # Create the k8s objects @@ -135,17 +148,21 @@ class K8sDeployer(Deployer): if not self.is_kind(): ingress: client.V1Ingress = self.cluster_info.get_ingress() - if opts.o.debug: - print(f"Sending this ingress: {ingress}") - ingress_resp = self.networking_api.create_namespaced_ingress( - namespace=self.k8s_namespace, - body=ingress - ) - if opts.o.debug: - print("Ingress created:") - print(f"{ingress_resp}") + if ingress: + if opts.o.debug: + print(f"Sending this ingress: {ingress}") + ingress_resp = self.networking_api.create_namespaced_ingress( + namespace=self.k8s_namespace, + body=ingress + ) + if opts.o.debug: + print("Ingress created:") + print(f"{ingress_resp}") + else: + if opts.o.debug: + print("No ingress configured") - def down(self, timeout, volumes): + def down(self, timeout, volumes): # noqa: C901 self.connect_api() # Delete the k8s objects # Create the host-path-mounted PVs for this deployment @@ -175,6 +192,22 @@ class K8sDeployer(Deployer): print(f"{pvc_resp}") except client.exceptions.ApiException as e: _check_delete_exception(e) + + # Figure out the ConfigMaps for this deployment + cfg_maps = self.cluster_info.get_configmaps() + for cfg_map in cfg_maps: + if opts.o.debug: + print(f"Deleting this ConfigMap: {cfg_map}") + try: + cfg_map_resp = self.core_api.delete_namespaced_config_map( + name=cfg_map.metadata.name, namespace=self.k8s_namespace + ) + if opts.o.debug: + print("ConfigMap deleted:") + print(f"{cfg_map_resp}") + except client.exceptions.ApiException as e: + _check_delete_exception(e) + deployment = self.cluster_info.get_deployment() if opts.o.debug: print(f"Deleting this deployment: {deployment}") @@ -198,14 +231,18 @@ class K8sDeployer(Deployer): if not self.is_kind(): ingress: client.V1Ingress = self.cluster_info.get_ingress() - if opts.o.debug: - print(f"Deleting this ingress: {ingress}") - try: - self.networking_api.delete_namespaced_ingress( - name=ingress.metadata.name, namespace=self.k8s_namespace - ) - except client.exceptions.ApiException as e: - _check_delete_exception(e) + if ingress: + if opts.o.debug: + print(f"Deleting this ingress: {ingress}") + try: + self.networking_api.delete_namespaced_ingress( + name=ingress.metadata.name, namespace=self.k8s_namespace + ) + except client.exceptions.ApiException as e: + _check_delete_exception(e) + else: + if opts.o.debug: + print("No ingress to delete") if self.is_kind(): # Destroy the kind cluster diff --git a/stack_orchestrator/deploy/k8s/helpers.py b/stack_orchestrator/deploy/k8s/helpers.py index 62545dfd..b094acd5 100644 --- a/stack_orchestrator/deploy/k8s/helpers.py +++ b/stack_orchestrator/deploy/k8s/helpers.py @@ -73,7 +73,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 in volumes.keys(): + for volume, value in volumes.items(): # Volume definition looks like: # 'laconicd-data': None named_volumes.append(volume) @@ -98,22 +98,31 @@ def volume_mounts_for_service(parsed_pod_files, service): volumes = service_obj["volumes"] for mount_string in volumes: # Looks like: test-data:/data - (volume_name, mount_path) = mount_string.split(":") - volume_device = client.V1VolumeMount(mount_path=mount_path, name=volume_name) + parts = mount_string.split(":") + volume_name = parts[0] + mount_path = parts[1] + mount_options = parts[2] if len(parts) == 3 else None + volume_device = client.V1VolumeMount( + mount_path=mount_path, name=volume_name, read_only="ro" == mount_options) result.append(volume_device) return result -def volumes_for_pod_files(parsed_pod_files): +def volumes_for_pod_files(parsed_pod_files, spec): result = [] for pod in parsed_pod_files: parsed_pod_file = parsed_pod_files[pod] if "volumes" in parsed_pod_file: volumes = parsed_pod_file["volumes"] for volume_name in volumes.keys(): - claim = client.V1PersistentVolumeClaimVolumeSource(claim_name=volume_name) - volume = client.V1Volume(name=volume_name, persistent_volume_claim=claim) - result.append(volume) + if volume_name in spec.get_configmaps(): + config_map = client.V1ConfigMapVolumeSource(name=volume_name) + volume = client.V1Volume(name=volume_name, config_map=config_map) + result.append(volume) + else: + claim = client.V1PersistentVolumeClaimVolumeSource(claim_name=volume_name) + volume = client.V1Volume(name=volume_name, persistent_volume_claim=claim) + result.append(volume) return result @@ -158,7 +167,7 @@ def _generate_kind_mounts(parsed_pod_files, deployment_dir): volume_definitions.append( f" - hostPath: {_make_absolute_host_path(volume_host_path_map[volume_name], deployment_dir)}\n" f" containerPath: {get_node_pv_mount_path(volume_name)}" - ) + ) return ( "" if len(volume_definitions) == 0 else ( " extraMounts:\n" diff --git a/stack_orchestrator/deploy/spec.py b/stack_orchestrator/deploy/spec.py index c4f791bf..dd6cd107 100644 --- a/stack_orchestrator/deploy/spec.py +++ b/stack_orchestrator/deploy/spec.py @@ -22,6 +22,7 @@ from stack_orchestrator import constants class Spec: obj: typing.Any + file_path: Path def __init__(self) -> None: pass @@ -29,12 +30,23 @@ class Spec: def init_from_file(self, file_path: Path): with file_path: self.obj = get_yaml().load(open(file_path, "r")) + self.file_path = file_path def get_image_registry(self): return (self.obj[constants.image_resigtry_key] if self.obj and constants.image_resigtry_key in self.obj else None) + def get_volumes(self): + return (self.obj["volumes"] + if self.obj and "volumes" in self.obj + else {}) + + def get_configmaps(self): + return (self.obj["configmaps"] + if self.obj and "configmaps" in self.obj + else {}) + def get_http_proxy(self): return (self.obj[constants.network_key][constants.http_proxy_key] if self.obj and constants.network_key in self.obj From b39805078798abfae636ec4b7f277bf62a1bb5ed Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Wed, 31 Jan 2024 15:11:32 -0600 Subject: [PATCH 5/9] Don't include volumes in spec if we don't have any. (#720) --- stack_orchestrator/deploy/deployment_create.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stack_orchestrator/deploy/deployment_create.py b/stack_orchestrator/deploy/deployment_create.py index b95386b1..b72f262c 100644 --- a/stack_orchestrator/deploy/deployment_create.py +++ b/stack_orchestrator/deploy/deployment_create.py @@ -365,7 +365,8 @@ def init_operation(deploy_command_context, stack, deployer_type, config, configmap_descriptors[named_volume] = f"./data/{named_volume}" else: volume_descriptors[named_volume] = f"./data/{named_volume}" - spec_file_content["volumes"] = volume_descriptors + if volume_descriptors: + spec_file_content["volumes"] = volume_descriptors if configmap_descriptors: spec_file_content["configmaps"] = configmap_descriptors From 1c30441000a461a00dc2b3e1e160a054b0d60c07 Mon Sep 17 00:00:00 2001 From: David Boreham Date: Thu, 1 Feb 2024 07:40:20 -0700 Subject: [PATCH 6/9] Add schedule for k8s deploy test --- .gitea/workflows/test-k8s-deploy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitea/workflows/test-k8s-deploy.yml b/.gitea/workflows/test-k8s-deploy.yml index 92c76a7d..50315363 100644 --- a/.gitea/workflows/test-k8s-deploy.yml +++ b/.gitea/workflows/test-k8s-deploy.yml @@ -8,6 +8,8 @@ on: - '.gitea/workflows/triggers/test-k8s-deploy' - '.gitea/workflows/test-k8s-deploy.yml' - 'tests/k8s-deploy/run-deploy-test.sh' + schedule: # Note: coordinate with other tests to not overload runners at the same time of day + - cron: '3 15 * * *' jobs: test: From 6629017d6a987f77fede8580843947b6715976dc Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Fri, 2 Feb 2024 18:04:06 -0600 Subject: [PATCH 7/9] Support other webapp types (react, static). (#721) * Support other webapp types (react, static). --- stack_orchestrator/build/build_webapp.py | 11 +++--- .../cerc-nextjs-base/Dockerfile | 2 +- .../scripts/start-serving-app.sh | 2 +- .../cerc-webapp-base/Dockerfile | 8 ++--- .../cerc-webapp-base/Dockerfile.webapp | 11 ++++++ .../container-build/cerc-webapp-base/build.sh | 24 +++++++++++-- .../scripts/apply-runtime-env.sh | 33 +++++++++++++++++ .../{ => scripts}/apply-webapp-config.sh | 0 .../cerc-webapp-base/scripts/build-app.sh | 36 +++++++++++++++++++ .../scripts/start-serving-app.sh | 15 ++++++++ .../cerc-webapp-base/start-serving-app.sh | 9 ----- .../deploy/webapp/run_webapp.py | 2 +- stack_orchestrator/deploy/webapp/util.py | 26 +++++++++++++- tests/webapp-test/run-webapp-test.sh | 4 +-- 14 files changed, 157 insertions(+), 26 deletions(-) create mode 100644 stack_orchestrator/data/container-build/cerc-webapp-base/Dockerfile.webapp create mode 100755 stack_orchestrator/data/container-build/cerc-webapp-base/scripts/apply-runtime-env.sh rename stack_orchestrator/data/container-build/cerc-webapp-base/{ => scripts}/apply-webapp-config.sh (100%) create mode 100755 stack_orchestrator/data/container-build/cerc-webapp-base/scripts/build-app.sh create mode 100755 stack_orchestrator/data/container-build/cerc-webapp-base/scripts/start-serving-app.sh delete mode 100755 stack_orchestrator/data/container-build/cerc-webapp-base/start-serving-app.sh diff --git a/stack_orchestrator/build/build_webapp.py b/stack_orchestrator/build/build_webapp.py index 287347eb..a9124590 100644 --- a/stack_orchestrator/build/build_webapp.py +++ b/stack_orchestrator/build/build_webapp.py @@ -25,10 +25,11 @@ from decouple import config import click from pathlib import Path from stack_orchestrator.build import build_containers +from stack_orchestrator.deploy.webapp.util import determine_base_container @click.command() -@click.option('--base-container', default="cerc/nextjs-base") +@click.option('--base-container') @click.option('--source-repo', help="directory containing the webapp to build", required=True) @click.option("--force-rebuild", is_flag=True, default=False, help="Override dependency checking -- always rebuild") @click.option("--extra-build-args", help="Supply extra arguments to build") @@ -57,6 +58,9 @@ def command(ctx, base_container, source_repo, force_rebuild, extra_build_args, t if not quiet: print(f'Dev Root is: {dev_root_path}') + if not base_container: + base_container = determine_base_container(source_repo) + # First build the base container. container_build_env = build_containers.make_container_build_env(dev_root_path, container_build_dir, debug, force_rebuild, extra_build_args) @@ -64,13 +68,12 @@ def command(ctx, base_container, source_repo, force_rebuild, extra_build_args, t build_containers.process_container(None, base_container, container_build_dir, container_build_env, dev_root_path, quiet, verbose, dry_run, continue_on_error) - # Now build the target webapp. We use the same build script, but with a different Dockerfile and work dir. container_build_env["CERC_WEBAPP_BUILD_RUNNING"] = "true" container_build_env["CERC_CONTAINER_BUILD_WORK_DIR"] = os.path.abspath(source_repo) container_build_env["CERC_CONTAINER_BUILD_DOCKERFILE"] = os.path.join(container_build_dir, - base_container.replace("/", "-"), - "Dockerfile.webapp") + base_container.replace("/", "-"), + "Dockerfile.webapp") if not tag: webapp_name = os.path.abspath(source_repo).split(os.path.sep)[-1] container_build_env["CERC_CONTAINER_BUILD_TAG"] = f"cerc/{webapp_name}:local" diff --git a/stack_orchestrator/data/container-build/cerc-nextjs-base/Dockerfile b/stack_orchestrator/data/container-build/cerc-nextjs-base/Dockerfile index c3c12cc6..5f9548ee 100644 --- a/stack_orchestrator/data/container-build/cerc-nextjs-base/Dockerfile +++ b/stack_orchestrator/data/container-build/cerc-nextjs-base/Dockerfile @@ -36,7 +36,7 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ # RUN su node -c "npm install -g " # Expose port for http -EXPOSE 3000 +EXPOSE 80 COPY /scripts /scripts diff --git a/stack_orchestrator/data/container-build/cerc-nextjs-base/scripts/start-serving-app.sh b/stack_orchestrator/data/container-build/cerc-nextjs-base/scripts/start-serving-app.sh index 3692f2f6..bd254572 100755 --- a/stack_orchestrator/data/container-build/cerc-nextjs-base/scripts/start-serving-app.sh +++ b/stack_orchestrator/data/container-build/cerc-nextjs-base/scripts/start-serving-app.sh @@ -58,4 +58,4 @@ if [ "$CERC_NEXTJS_SKIP_GENERATE" != "true" ]; then fi fi -$CERC_BUILD_TOOL start . -p ${CERC_LISTEN_PORT:-3000} +$CERC_BUILD_TOOL start . -- -p ${CERC_LISTEN_PORT:-80} diff --git a/stack_orchestrator/data/container-build/cerc-webapp-base/Dockerfile b/stack_orchestrator/data/container-build/cerc-webapp-base/Dockerfile index 275a5c3c..331e04a1 100644 --- a/stack_orchestrator/data/container-build/cerc-webapp-base/Dockerfile +++ b/stack_orchestrator/data/container-build/cerc-webapp-base/Dockerfile @@ -1,6 +1,6 @@ # Originally from: https://github.com/devcontainers/images/blob/main/src/javascript-node/.devcontainer/Dockerfile # [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster -ARG VARIANT=18-bullseye +ARG VARIANT=20-bullseye FROM node:${VARIANT} ARG USERNAME=node @@ -28,7 +28,7 @@ RUN \ # [Optional] Uncomment this section to install additional OS packages. RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends jq + && apt-get -y install --no-install-recommends jq gettext-base # [Optional] Uncomment if you want to install an additional version of node using nvm # ARG EXTRA_NODE_VERSION=10 @@ -37,9 +37,7 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ # We do this to get a yq binary from the published container, for the correct architecture we're building here COPY --from=docker.io/mikefarah/yq:latest /usr/bin/yq /usr/local/bin/yq -RUN mkdir -p /scripts -COPY ./apply-webapp-config.sh /scripts -COPY ./start-serving-app.sh /scripts +COPY scripts /scripts # [Optional] Uncomment if you want to install more global node modules # RUN su node -c "npm install -g " diff --git a/stack_orchestrator/data/container-build/cerc-webapp-base/Dockerfile.webapp b/stack_orchestrator/data/container-build/cerc-webapp-base/Dockerfile.webapp new file mode 100644 index 00000000..711eff25 --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-webapp-base/Dockerfile.webapp @@ -0,0 +1,11 @@ +FROM cerc/webapp-base:local as builder + +ARG CERC_BUILD_TOOL + +WORKDIR /app +COPY . . +RUN rm -rf node_modules build .next* +RUN /scripts/build-app.sh /app build /data + +FROM cerc/webapp-base:local +COPY --from=builder /data /data diff --git a/stack_orchestrator/data/container-build/cerc-webapp-base/build.sh b/stack_orchestrator/data/container-build/cerc-webapp-base/build.sh index 51712dc4..12b25e5e 100755 --- a/stack_orchestrator/data/container-build/cerc-webapp-base/build.sh +++ b/stack_orchestrator/data/container-build/cerc-webapp-base/build.sh @@ -1,9 +1,29 @@ #!/usr/bin/env bash -# Build cerc/laconic-registry-cli +# Build cerc/webapp-base source ${CERC_CONTAINER_BASE_DIR}/build-base.sh # See: https://stackoverflow.com/a/246128/1701505 SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -docker build -t cerc/webapp-base:local ${build_command_args} -f ${SCRIPT_DIR}/Dockerfile ${SCRIPT_DIR} +CERC_CONTAINER_BUILD_WORK_DIR=${CERC_CONTAINER_BUILD_WORK_DIR:-$SCRIPT_DIR} +CERC_CONTAINER_BUILD_DOCKERFILE=${CERC_CONTAINER_BUILD_DOCKERFILE:-$SCRIPT_DIR/Dockerfile} +CERC_CONTAINER_BUILD_TAG=${CERC_CONTAINER_BUILD_TAG:-cerc/webapp-base:local} + +docker build -t $CERC_CONTAINER_BUILD_TAG ${build_command_args} -f $CERC_CONTAINER_BUILD_DOCKERFILE $CERC_CONTAINER_BUILD_WORK_DIR + +if [ $? -eq 0 ] && [ "$CERC_CONTAINER_BUILD_TAG" != "cerc/webapp-base:local" ]; then + cat < $TMP_ENV + set -a + source .env + source $TMP_ENV + set +a + rm -f $TMP_ENV +fi + +for f in $(find . -regex ".*.[tj]sx?$" -type f | grep -v 'node_modules'); do + for e in $(cat "${f}" | tr -s '[:blank:]' '\n' | tr -s '[{},();]' '\n' | egrep -o -e '^"CERC_RUNTIME_ENV_[^\"]+"' -e '^"LACONIC_HOSTED_CONFIG_[^\"]+"'); do + orig_name=$(echo -n "${e}" | sed 's/"//g') + cur_name=$(echo -n "${orig_name}" | sed 's/CERC_RUNTIME_ENV_//g') + cur_val=$(echo -n "\$${cur_name}" | envsubst) + if [ "$CERC_RETAIN_ENV_QUOTES" != "true" ]; then + cur_val=$(sed "s/^[\"']//" <<< "$cur_val" | sed "s/[\"']//") + fi + esc_val=$(sed 's/[&/\]/\\&/g' <<< "$cur_val") + echo "$f: $cur_name=$cur_val" + sed -i "s/$orig_name/$esc_val/g" $f + done +done diff --git a/stack_orchestrator/data/container-build/cerc-webapp-base/apply-webapp-config.sh b/stack_orchestrator/data/container-build/cerc-webapp-base/scripts/apply-webapp-config.sh similarity index 100% rename from stack_orchestrator/data/container-build/cerc-webapp-base/apply-webapp-config.sh rename to stack_orchestrator/data/container-build/cerc-webapp-base/scripts/apply-webapp-config.sh diff --git a/stack_orchestrator/data/container-build/cerc-webapp-base/scripts/build-app.sh b/stack_orchestrator/data/container-build/cerc-webapp-base/scripts/build-app.sh new file mode 100755 index 00000000..c102a054 --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-webapp-base/scripts/build-app.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +if [ -n "$CERC_SCRIPT_DEBUG" ]; then + set -x +fi + +CERC_BUILD_TOOL="${CERC_BUILD_TOOL}" +WORK_DIR="${1:-/app}" +OUTPUT_DIR="${2:-build}" +DEST_DIR="${3:-/data}" + +if [ -f "${WORK_DIR}/package.json" ]; then + echo "Building node-based webapp ..." + cd "${WORK_DIR}" || exit 1 + + if [ -z "$CERC_BUILD_TOOL" ]; then + if [ -f "yarn.lock" ]; then + CERC_BUILD_TOOL=yarn + else + CERC_BUILD_TOOL=npm + fi + fi + + $CERC_BUILD_TOOL install || exit 1 + $CERC_BUILD_TOOL build || exit 1 + + rm -rf "${DEST_DIR}" + mv "${WORK_DIR}/${OUTPUT_DIR}" "${DEST_DIR}" +else + echo "Copying static app ..." + mv "${WORK_DIR}" "${DEST_DIR}" +fi + +exit 0 diff --git a/stack_orchestrator/data/container-build/cerc-webapp-base/scripts/start-serving-app.sh b/stack_orchestrator/data/container-build/cerc-webapp-base/scripts/start-serving-app.sh new file mode 100755 index 00000000..365d05fb --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-webapp-base/scripts/start-serving-app.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +if [ -n "$CERC_SCRIPT_DEBUG" ]; then + set -x +fi + +CERC_WEBAPP_FILES_DIR="${CERC_WEBAPP_FILES_DIR:-/data}" +CERC_ENABLE_CORS="${CERC_ENABLE_CORS:-false}" + +if [ "true" == "$CERC_ENABLE_CORS" ]; then + CERC_HTTP_EXTRA_ARGS="$CERC_HTTP_EXTRA_ARGS --cors" +fi + +/scripts/apply-webapp-config.sh /config/config.yml ${CERC_WEBAPP_FILES_DIR} +/scripts/apply-runtime-env.sh ${CERC_WEBAPP_FILES_DIR} +http-server $CERC_HTTP_EXTRA_ARGS -p ${CERC_LISTEN_PORT:-80} ${CERC_WEBAPP_FILES_DIR} diff --git a/stack_orchestrator/data/container-build/cerc-webapp-base/start-serving-app.sh b/stack_orchestrator/data/container-build/cerc-webapp-base/start-serving-app.sh deleted file mode 100755 index 69fa6c22..00000000 --- a/stack_orchestrator/data/container-build/cerc-webapp-base/start-serving-app.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$CERC_SCRIPT_DEBUG" ]; then - set -x -fi - -CERC_WEBAPP_FILES_DIR="${CERC_WEBAPP_FILES_DIR:-/data}" - -/scripts/apply-webapp-config.sh /config/config.yml ${CERC_WEBAPP_FILES_DIR} -http-server -p 80 ${CERC_WEBAPP_FILES_DIR} diff --git a/stack_orchestrator/deploy/webapp/run_webapp.py b/stack_orchestrator/deploy/webapp/run_webapp.py index 4dbf234a..f780c6f8 100644 --- a/stack_orchestrator/deploy/webapp/run_webapp.py +++ b/stack_orchestrator/deploy/webapp/run_webapp.py @@ -27,7 +27,7 @@ from dotenv import dotenv_values from stack_orchestrator import constants from stack_orchestrator.deploy.deployer_factory import getDeployer -WEBAPP_PORT = 3000 +WEBAPP_PORT = 80 @click.command() diff --git a/stack_orchestrator/deploy/webapp/util.py b/stack_orchestrator/deploy/webapp/util.py index ef98117b..50485110 100644 --- a/stack_orchestrator/deploy/webapp/util.py +++ b/stack_orchestrator/deploy/webapp/util.py @@ -195,6 +195,23 @@ def file_hash(filename): return hashlib.sha1(open(filename).read().encode()).hexdigest() +def determine_base_container(clone_dir, app_type="webapp"): + if not app_type or not app_type.startswith("webapp"): + raise Exception(f"Unsupported app_type {app_type}") + + base_container = "cerc/webapp-base" + if app_type == "webapp/next": + base_container = "cerc/nextjs-base" + elif app_type == "webapp": + pkg_json_path = os.path.join(clone_dir, "package.json") + if os.path.exists(pkg_json_path): + pkg_json = json.load(open(pkg_json_path)) + if "next" in pkg_json.get("dependencies", {}): + base_container = "cerc/nextjs-base" + + return base_container + + def build_container_image(app_record, tag, extra_build_args=[]): tmpdir = tempfile.mkdtemp() @@ -216,8 +233,15 @@ def build_container_image(app_record, tag, extra_build_args=[]): result = subprocess.run(["git", "clone", "--depth", "1", repo, clone_dir]) result.check_returncode() + base_container = determine_base_container(clone_dir, app_record.attributes.app_type) + print("Building webapp ...") - build_command = [sys.argv[0], "build-webapp", "--source-repo", clone_dir, "--tag", tag] + build_command = [ + sys.argv[0], "build-webapp", + "--source-repo", clone_dir, + "--tag", tag, + "--base-container", base_container + ] if extra_build_args: build_command.append("--extra-build-args") build_command.append(" ".join(extra_build_args)) diff --git a/tests/webapp-test/run-webapp-test.sh b/tests/webapp-test/run-webapp-test.sh index 5db382f8..8cae4828 100755 --- a/tests/webapp-test/run-webapp-test.sh +++ b/tests/webapp-test/run-webapp-test.sh @@ -30,14 +30,14 @@ CHECK="SPECIAL_01234567890_TEST_STRING" set +e -CONTAINER_ID=$(docker run -p 3000:3000 -d -e CERC_SCRIPT_DEBUG=$CERC_SCRIPT_DEBUG cerc/test-progressive-web-app:local) +CONTAINER_ID=$(docker run -p 3000:80 -d -e CERC_SCRIPT_DEBUG=$CERC_SCRIPT_DEBUG cerc/test-progressive-web-app:local) sleep 3 wget -t 7 -O test.before -m http://localhost:3000 docker logs $CONTAINER_ID docker remove -f $CONTAINER_ID -CONTAINER_ID=$(docker run -p 3000:3000 -e CERC_WEBAPP_DEBUG=$CHECK -e CERC_SCRIPT_DEBUG=$CERC_SCRIPT_DEBUG -d cerc/test-progressive-web-app:local) +CONTAINER_ID=$(docker run -p 3000:80 -e CERC_WEBAPP_DEBUG=$CHECK -e CERC_SCRIPT_DEBUG=$CERC_SCRIPT_DEBUG -d cerc/test-progressive-web-app:local) sleep 3 wget -t 7 -O test.after -m http://localhost:3000 From 2fcd416e29517e682f00e5d663b939b3ee2e1d86 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Fri, 2 Feb 2024 20:05:15 -0600 Subject: [PATCH 8/9] Basic webapp deployer stack. (#722) --- .../cerc-webapp-deployer-backend/build.sh | 9 +++++++++ .../data/stacks/webapp-deployer-backend/stack.yml | 11 +++++++++++ stack_orchestrator/deploy/deploy.py | 8 ++++---- stack_orchestrator/deploy/deployment_create.py | 9 ++++----- stack_orchestrator/deploy/k8s/cluster_info.py | 2 +- stack_orchestrator/deploy/k8s/helpers.py | 6 +++--- 6 files changed, 32 insertions(+), 13 deletions(-) create mode 100755 stack_orchestrator/data/container-build/cerc-webapp-deployer-backend/build.sh create mode 100644 stack_orchestrator/data/stacks/webapp-deployer-backend/stack.yml diff --git a/stack_orchestrator/data/container-build/cerc-webapp-deployer-backend/build.sh b/stack_orchestrator/data/container-build/cerc-webapp-deployer-backend/build.sh new file mode 100755 index 00000000..948701d6 --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-webapp-deployer-backend/build.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Build cerc/webapp-deployer-backend + +source ${CERC_CONTAINER_BASE_DIR}/build-base.sh + +# See: https://stackoverflow.com/a/246128/1701505 +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +docker build -t cerc/webapp-deployer-backend:local ${build_command_args} ${CERC_REPO_BASE_DIR}/webapp-deployment-status-api diff --git a/stack_orchestrator/data/stacks/webapp-deployer-backend/stack.yml b/stack_orchestrator/data/stacks/webapp-deployer-backend/stack.yml new file mode 100644 index 00000000..ca61049c --- /dev/null +++ b/stack_orchestrator/data/stacks/webapp-deployer-backend/stack.yml @@ -0,0 +1,11 @@ +version: "1.0" +name: webapp-deployer-backend +description: "Deployer for webapps" +repos: + - git.vdb.to:telackey/webapp-deployment-status-api +containers: + - cerc/webapp-deployer-backend +pods: + - name: webapp-deployer-backend + repository: git.vdb.to:telackey/webapp-deployment-status-api + path: ./ diff --git a/stack_orchestrator/deploy/deploy.py b/stack_orchestrator/deploy/deploy.py index 18d27a21..de68154b 100644 --- a/stack_orchestrator/deploy/deploy.py +++ b/stack_orchestrator/deploy/deploy.py @@ -347,8 +347,8 @@ def _make_cluster_context(ctx, stack, include, exclude, cluster, env_file): else: if deployment: compose_file_name = os.path.join(compose_dir, f"docker-compose-{pod_name}.yml") - pod_pre_start_command = pod["pre_start_command"] - pod_post_start_command = pod["post_start_command"] + pod_pre_start_command = pod.get("pre_start_command") + pod_post_start_command = pod.get("post_start_command") script_dir = compose_dir.parent.joinpath("pods", pod_name, "scripts") if pod_pre_start_command is not None: pre_start_commands.append(os.path.join(script_dir, pod_pre_start_command)) @@ -357,8 +357,8 @@ def _make_cluster_context(ctx, stack, include, exclude, cluster, env_file): else: pod_root_dir = os.path.join(dev_root_path, pod_repository.split("/")[-1], pod["path"]) compose_file_name = os.path.join(pod_root_dir, f"docker-compose-{pod_name}.yml") - pod_pre_start_command = pod["pre_start_command"] - pod_post_start_command = pod["post_start_command"] + pod_pre_start_command = pod.get("pre_start_command") + pod_post_start_command = pod.get("post_start_command") if pod_pre_start_command is not None: pre_start_commands.append(os.path.join(pod_root_dir, pod_pre_start_command)) if pod_post_start_command is not None: diff --git a/stack_orchestrator/deploy/deployment_create.py b/stack_orchestrator/deploy/deployment_create.py index b72f262c..44824766 100644 --- a/stack_orchestrator/deploy/deployment_create.py +++ b/stack_orchestrator/deploy/deployment_create.py @@ -360,11 +360,10 @@ def init_operation(deploy_command_context, stack, deployer_type, config, for named_volume in named_volumes["rw"]: volume_descriptors[named_volume] = f"./data/{named_volume}" for named_volume in named_volumes["ro"]: - if "k8s" in deployer_type: - if "config" in named_volume: - configmap_descriptors[named_volume] = f"./data/{named_volume}" - else: - volume_descriptors[named_volume] = f"./data/{named_volume}" + if "k8s" in deployer_type and "config" in named_volume: + configmap_descriptors[named_volume] = f"./data/{named_volume}" + else: + volume_descriptors[named_volume] = f"./data/{named_volume}" if volume_descriptors: spec_file_content["volumes"] = volume_descriptors if configmap_descriptors: diff --git a/stack_orchestrator/deploy/k8s/cluster_info.py b/stack_orchestrator/deploy/k8s/cluster_info.py index f0e7c87b..35b2b9da 100644 --- a/stack_orchestrator/deploy/k8s/cluster_info.py +++ b/stack_orchestrator/deploy/k8s/cluster_info.py @@ -243,7 +243,7 @@ class ClusterInfo: ), ) containers.append(container) - volumes = volumes_for_pod_files(self.parsed_pod_yaml_map, self.spec) + volumes = volumes_for_pod_files(self.parsed_pod_yaml_map, self.spec, self.app_name) image_pull_secrets = [client.V1LocalObjectReference(name="laconic-registry")] template = client.V1PodTemplateSpec( metadata=client.V1ObjectMeta(labels={"app": self.app_name}), diff --git a/stack_orchestrator/deploy/k8s/helpers.py b/stack_orchestrator/deploy/k8s/helpers.py index b094acd5..081c3e62 100644 --- a/stack_orchestrator/deploy/k8s/helpers.py +++ b/stack_orchestrator/deploy/k8s/helpers.py @@ -108,7 +108,7 @@ def volume_mounts_for_service(parsed_pod_files, service): return result -def volumes_for_pod_files(parsed_pod_files, spec): +def volumes_for_pod_files(parsed_pod_files, spec, app_name): result = [] for pod in parsed_pod_files: parsed_pod_file = parsed_pod_files[pod] @@ -116,11 +116,11 @@ def volumes_for_pod_files(parsed_pod_files, spec): volumes = parsed_pod_file["volumes"] for volume_name in volumes.keys(): if volume_name in spec.get_configmaps(): - config_map = client.V1ConfigMapVolumeSource(name=volume_name) + config_map = client.V1ConfigMapVolumeSource(name=f"{app_name}-{volume_name}") volume = client.V1Volume(name=volume_name, config_map=config_map) result.append(volume) else: - claim = client.V1PersistentVolumeClaimVolumeSource(claim_name=volume_name) + claim = client.V1PersistentVolumeClaimVolumeSource(claim_name=f"{app_name}-{volume_name}") volume = client.V1Volume(name=volume_name, persistent_volume_claim=claim) result.append(volume) return result From 25a2b70f2c8c0f4f5ef5c1044f7941d6fb8eeb5f Mon Sep 17 00:00:00 2001 From: David Boreham Date: Sat, 3 Feb 2024 18:25:02 -0700 Subject: [PATCH 9/9] Fix command in mainnet-eth docs --- stack_orchestrator/data/stacks/mainnet-eth/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack_orchestrator/data/stacks/mainnet-eth/README.md b/stack_orchestrator/data/stacks/mainnet-eth/README.md index 8f0dc1c4..2656b75b 100644 --- a/stack_orchestrator/data/stacks/mainnet-eth/README.md +++ b/stack_orchestrator/data/stacks/mainnet-eth/README.md @@ -18,7 +18,7 @@ $ laconic-so --stack mainnet-eth build-containers ``` $ laconic-so --stack mainnet-eth deploy init --map-ports-to-host any-same --output mainnet-eth-spec.yml -$ laconic-so deploy create --spec-file mainnet-eth-spec.yml --deployment-dir mainnet-eth-deployment +$ laconic-so deploy --stack mainnet-eth create --spec-file mainnet-eth-spec.yml --deployment-dir mainnet-eth-deployment ``` ## Start the stack ```