diff --git a/HELM_CHART_GENERATION.md b/docs/helm-chart-generation.md similarity index 73% rename from HELM_CHART_GENERATION.md rename to docs/helm-chart-generation.md index f9fd07c4..b1d083bd 100644 --- a/HELM_CHART_GENERATION.md +++ b/docs/helm-chart-generation.md @@ -24,8 +24,7 @@ kompose version ### 1. Create spec file ```bash -laconic-so --stack deploy init \ - --deploy-to k8s \ +laconic-so --stack deploy --deploy-to k8s init \ --kube-config ~/.kube/config \ --output spec.yml ``` @@ -42,8 +41,7 @@ laconic-so --stack deploy create \ ### 3. Deploy to Kubernetes ```bash -cd my-deployment/chart -helm install my-release ./ --namespace zenith --create-namespace +helm install my-release my-deployment/chart kubectl get pods -n zenith ``` @@ -54,18 +52,17 @@ my-deployment/ ├── spec.yml # Reference ├── stack.yml # Reference └── chart/ # Helm chart - ├── Chart.yaml - ├── README.md - └── templates/ - └── *.yaml + ├── Chart.yaml + ├── README.md + └── templates/ + └── *.yaml ``` ## Example ```bash # Generate chart for stage1-zenithd -laconic-so --stack stage1-zenithd deploy init \ - --deploy-to k8s \ +laconic-so --stack stage1-zenithd deploy --deploy-to k8s init \ --kube-config ~/.kube/config \ --output stage1-spec.yml @@ -75,6 +72,5 @@ laconic-so --stack stage1-zenithd deploy create \ --helm-chart # Deploy -cd stage1-deployment/chart -helm install stage1-zenithd ./ --namespace zenith --create-namespace +helm install stage1-zenithd stage1-deployment/chart ``` diff --git a/stack_orchestrator/deploy/k8s/helm/chart_generator.py b/stack_orchestrator/deploy/k8s/helm/chart_generator.py index eacb5120..b366f942 100644 --- a/stack_orchestrator/deploy/k8s/helm/chart_generator.py +++ b/stack_orchestrator/deploy/k8s/helm/chart_generator.py @@ -30,16 +30,46 @@ from stack_orchestrator.deploy.k8s.helm.kompose_wrapper import ( get_kompose_version, convert_to_helm_chart ) +from stack_orchestrator.util import get_yaml -def generate_helm_chart(stack_name: str, spec_file: str, deployment_dir: str = None) -> None: +def _post_process_chart(chart_dir: Path, chart_name: str) -> None: + """ + Post-process Kompose-generated chart to fix common issues. + + Fixes: + 1. Chart.yaml name, description and keywords + + TODO: + - Add defaultMode: 0755 to ConfigMap volumes containing scripts (.sh files) + """ + yaml = get_yaml() + + # Fix Chart.yaml + chart_yaml_path = chart_dir / "Chart.yaml" + if chart_yaml_path.exists(): + chart_yaml = yaml.load(open(chart_yaml_path, "r")) + + # Fix name + chart_yaml["name"] = chart_name + + # Fix description + chart_yaml["description"] = f"Generated Helm chart for {chart_name} stack" + + # Fix keywords + if "keywords" in chart_yaml and isinstance(chart_yaml["keywords"], list): + chart_yaml["keywords"] = [chart_name] + + with open(chart_yaml_path, "w") as f: + yaml.dump(chart_yaml, f) + + +def generate_helm_chart(stack_path: str, spec_file: str, deployment_dir: str = None) -> None: """ Generate a self-sufficient Helm chart from stack compose files using Kompose. - This is completely separate from the existing k8s deployment flow. - Args: - stack_name: Name of the stack + stack_path: Path to the stack directory spec_file: Path to the deployment spec file deployment_dir: Optional directory for deployment output @@ -49,7 +79,6 @@ def generate_helm_chart(stack_name: str, spec_file: str, deployment_dir: str = N ├── stack.yml # Reference └── chart/ # Self-sufficient Helm chart ├── Chart.yaml - ├── values.yaml (generated by Kompose) ├── README.md └── templates/ └── *.yaml @@ -69,11 +98,18 @@ def generate_helm_chart(stack_name: str, spec_file: str, deployment_dir: str = N - Generate comprehensive values.yaml documentation """ - # 1. Setup deployment directory + parsed_stack = get_parsed_stack_config(stack_path) + stack_name = parsed_stack.get("name", stack_path) + + # 1. Check Kompose availability + if not check_kompose_available(): + error_exit("kompose not found in PATH.\n") + + # 2. Setup deployment directory if deployment_dir: deployment_dir_path = Path(deployment_dir) else: - deployment_dir_path = Path("deployment-001") + deployment_dir_path = Path(f"{stack_name}-deployment") if deployment_dir_path.exists(): error_exit(f"Deployment directory already exists: {deployment_dir_path}") @@ -83,12 +119,12 @@ def generate_helm_chart(stack_name: str, spec_file: str, deployment_dir: str = N deployment_dir_path.mkdir(parents=True) - # 2. Copy spec and stack files to deployment directory (for reference) + # 3. Copy spec and stack files to deployment directory (for reference) spec_path = Path(spec_file).resolve() if not spec_path.exists(): error_exit(f"Spec file not found: {spec_file}") - stack_file_path = get_stack_path(stack_name).joinpath(constants.stack_file_name) + stack_file_path = get_stack_path(stack_path).joinpath(constants.stack_file_name) if not stack_file_path.exists(): error_exit(f"Stack file not found: {stack_file_path}") @@ -99,34 +135,26 @@ def generate_helm_chart(stack_name: str, spec_file: str, deployment_dir: str = N print(f"Copied spec file: {spec_path}") print(f"Copied stack file: {stack_file_path}") - # 3. Get compose files from stack - parsed_stack = get_parsed_stack_config(stack_name) + # 4. Get compose files from stack pods = get_pod_list(parsed_stack) - if not pods: - error_exit(f"No pods found in stack: {stack_name}") + error_exit(f"No pods found in stack: {stack_path}") + + # Get clean stack name from stack.yml + chart_name = stack_name.replace("_", "-").replace(" ", "-") if opts.o.debug: print(f"Found {len(pods)} pod(s) in stack: {pods}") compose_files = [] for pod in pods: - pod_file = get_pod_file_path(stack_name, parsed_stack, pod) + pod_file = get_pod_file_path(stack_path, parsed_stack, pod) if not pod_file.exists(): error_exit(f"Pod file not found: {pod_file}") compose_files.append(pod_file) if opts.o.debug: print(f"Found compose file: {pod_file.name}") - # 4. Check Kompose availability - if not check_kompose_available(): - error_exit( - "kompose not found in PATH.\n" - "Install from: https://kompose.io/installation/\n" - " - Linux: curl -L https://github.com/kubernetes/kompose/releases/download/v1.34.0/kompose-linux-amd64 -o kompose\n" - " - macOS: brew install kompose" - ) - try: version = get_kompose_version() print(f"Using kompose version: {version}") @@ -135,7 +163,6 @@ def generate_helm_chart(stack_name: str, spec_file: str, deployment_dir: str = N # 5. Create chart directory and invoke Kompose chart_dir = deployment_dir_path / "chart" - chart_name = stack_name.replace("_", "-").replace(" ", "-") # Helm chart names prefer dashes print(f"Converting {len(compose_files)} compose file(s) to Helm chart using Kompose...") @@ -150,10 +177,13 @@ def generate_helm_chart(stack_name: str, spec_file: str, deployment_dir: str = N except Exception as e: error_exit(f"Helm chart generation failed: {e}") - # 6. Generate README.md with basic installation instructions + # 6. Post-process generated chart + _post_process_chart(chart_dir, chart_name) + + # 7. Generate README.md with basic installation instructions readme_content = f"""# {chart_name} Helm Chart -Generated by laconic-so from stack: {stack_name} +Generated by laconic-so from stack: `{stack_path} ## Prerequisites @@ -164,20 +194,25 @@ Generated by laconic-so from stack: {stack_name} ## Installation ```bash -# Create namespace (optional) -kubectl create namespace zenith - # Install the chart -helm install {chart_name} ./ --namespace zenith +helm install {chart_name} {chart_dir} # Check deployment status -kubectl get pods -n zenith +kubectl get pods +``` + +## Upgrade + +To apply changes made to chart, perform upgrade: + +```bash +helm upgrade {chart_name} {chart_dir} ``` ## Uninstallation ```bash -helm uninstall {chart_name} --namespace zenith +helm uninstall {chart_name} ``` ## Configuration @@ -191,38 +226,6 @@ Edit the generated template files in `templates/` to customize: - Resource limits (CPU, memory) - Persistent volume sizes - Replica counts - -TODO: values.yaml will be enhanced in future versions to provide a cleaner configuration interface. - -## Generated Files - -- `Chart.yaml` - Chart metadata -- `templates/` - Kubernetes resource manifests - - Deployments - - Services - - PersistentVolumeClaims - - ConfigMaps - -## Post-Processing TODO - -The following enhancements are planned: -- Parameterized values.yaml with common configuration options -- StatefulSet conversion for stateful services (databases, validators) -- Embedded deployment artifacts (genesis files, configs) as ConfigMaps -- Secret templates for sensitive data (validator keys, passwords) -- Init containers for setup and configuration -- Enhanced Chart.yaml metadata - -## Notes - -This chart is generated from compose files and may require manual adjustments for production use: -1. Review resource limits in deployment manifests -2. Configure persistent volume storage classes for your cluster -3. Set up ingress for external access if needed -4. Configure secrets for sensitive data (passwords, keys) -5. Adjust service types (ClusterIP, NodePort, LoadBalancer) based on your needs - -For more information, see the laconic-so documentation. """ readme_path = chart_dir / "README.md" @@ -238,7 +241,7 @@ For more information, see the laconic-so documentation. print(f"\nChart details:") print(f" Name: {chart_name}") print(f" Location: {chart_dir.absolute()}") - print(f" Stack: {stack_name}") + print(f" Stack: {stack_path}") # Count generated files template_files = list((chart_dir / "templates").glob("*.yaml")) if (chart_dir / "templates").exists() else [] @@ -259,8 +262,8 @@ For more information, see the laconic-so documentation. print(f" ls templates/") print(f"") print(f" 3. Install to Kubernetes:") - print(f" helm install {chart_name} ./ --namespace zenith --create-namespace") + print(f" helm install {chart_name} {chart_dir}") print(f"") print(f" 4. Check deployment:") - print(f" kubectl get pods -n zenith") + print(f" kubectl get pods") print(f"")