make deployments self-sufficient by copying hooks into deployment dir
parent
4977e3ff43
commit
befb548a6e
|
|
@ -54,3 +54,5 @@
|
||||||
{"type":"status_update","timestamp":"2026-04-21T06:08:14.457815115Z","issue_id":"so-ad7","payload":{"status":"closed"}}
|
{"type":"status_update","timestamp":"2026-04-21T06:08:14.457815115Z","issue_id":"so-ad7","payload":{"status":"closed"}}
|
||||||
{"type":"update","timestamp":"2026-04-21T09:00:47.364859946Z","issue_id":"so-p3p","payload":{"description":"## Problem\n\nThe Caddy ingress controller image is hardcoded in `ingress-caddy-kind-deploy.yaml`, with no mechanism to update it short of cluster recreation or manual `kubectl patch`. laconic-so should: (1) allow spec.yml to specify a Caddy image, (2) support updating the Caddy image as part of `deployment start`, (3) set `strategy: Recreate` on the Caddy Deployment since hostPort pods can't rolling-update.\n\n## Resolution\n\n- New spec key `caddy-ingress-image`. Fresh install uses it (fallback: manifest default). On subsequent `deployment start`, if the spec key is set and the running Caddy image differs, SO patches the Deployment and waits for rollout.\n- Spec key absent =\u003e SO does **not** touch a running Caddy, to avoid silently reverting images set out-of-band (ansible playbook, another deployment's spec).\n- `strategy: Recreate` added to the Caddy Deployment manifest.\n- Reconcile runs under both `--perform-cluster-management` and the default `--skip-cluster-management` (it's a plain k8s-API patch, not a cluster lifecycle op).\n- Image substitution locates the container by name instead of string-matching the shipped default, so the spec override wins regardless of what the manifest hardcodes.\n- Cluster-scoped caveat: `caddy-system` is shared across deployments; last `deployment start` that sets the key wins for everyone. Documented in `deployment_patterns.md`."}}
|
{"type":"update","timestamp":"2026-04-21T09:00:47.364859946Z","issue_id":"so-p3p","payload":{"description":"## Problem\n\nThe Caddy ingress controller image is hardcoded in `ingress-caddy-kind-deploy.yaml`, with no mechanism to update it short of cluster recreation or manual `kubectl patch`. laconic-so should: (1) allow spec.yml to specify a Caddy image, (2) support updating the Caddy image as part of `deployment start`, (3) set `strategy: Recreate` on the Caddy Deployment since hostPort pods can't rolling-update.\n\n## Resolution\n\n- New spec key `caddy-ingress-image`. Fresh install uses it (fallback: manifest default). On subsequent `deployment start`, if the spec key is set and the running Caddy image differs, SO patches the Deployment and waits for rollout.\n- Spec key absent =\u003e SO does **not** touch a running Caddy, to avoid silently reverting images set out-of-band (ansible playbook, another deployment's spec).\n- `strategy: Recreate` added to the Caddy Deployment manifest.\n- Reconcile runs under both `--perform-cluster-management` and the default `--skip-cluster-management` (it's a plain k8s-API patch, not a cluster lifecycle op).\n- Image substitution locates the container by name instead of string-matching the shipped default, so the spec override wins regardless of what the manifest hardcodes.\n- Cluster-scoped caveat: `caddy-system` is shared across deployments; last `deployment start` that sets the key wins for everyone. Documented in `deployment_patterns.md`."}}
|
||||||
{"type":"status_update","timestamp":"2026-04-21T09:00:47.745675131Z","issue_id":"so-p3p","payload":{"status":"closed"}}
|
{"type":"status_update","timestamp":"2026-04-21T09:00:47.745675131Z","issue_id":"so-p3p","payload":{"status":"closed"}}
|
||||||
|
{"type":"comment","timestamp":"2026-04-27T13:41:16.962883653Z","issue_id":"so-078","payload":{"body":"Fixed. deploy create now copies commands.py into deployment_dir/hooks/. call_stack_deploy_start loads hooks from the deployment dir instead of resolving via get_stack_path, so deployment start no longer requires the stack repo to be present or cwd to be correct."}}
|
||||||
|
{"type":"close","timestamp":"2026-04-27T13:41:17.073012545Z","issue_id":"so-078","payload":{}}
|
||||||
|
|
|
||||||
|
|
@ -276,9 +276,10 @@ def call_stack_deploy_start(deployment_context):
|
||||||
create additional k8s resources (Services, etc.) in the deployment namespace.
|
create additional k8s resources (Services, etc.) in the deployment namespace.
|
||||||
The namespace can be derived as f"laconic-{deployment_context.id}".
|
The namespace can be derived as f"laconic-{deployment_context.id}".
|
||||||
"""
|
"""
|
||||||
python_file_paths = _commands_plugin_paths(deployment_context.stack.name)
|
hooks_dir = deployment_context.deployment_dir / "hooks"
|
||||||
for python_file_path in python_file_paths:
|
if not hooks_dir.exists():
|
||||||
if python_file_path.exists():
|
return
|
||||||
|
for python_file_path in sorted(hooks_dir.glob("commands*.py")):
|
||||||
spec = util.spec_from_file_location("commands", python_file_path)
|
spec = util.spec_from_file_location("commands", python_file_path)
|
||||||
if spec is None or spec.loader is None:
|
if spec is None or spec.loader is None:
|
||||||
continue
|
continue
|
||||||
|
|
@ -1111,6 +1112,25 @@ def _safe_copy_tree(src: Path, dst: Path, exclude_patterns: Optional[List[str]]
|
||||||
safe_copy_file(src_path, dst_path)
|
safe_copy_file(src_path, dst_path)
|
||||||
|
|
||||||
|
|
||||||
|
def _copy_hooks(stack_name: str, target_dir: Path):
|
||||||
|
"""Copy commands.py hooks into deployment_dir/hooks/ so the deployment is self-sufficient.
|
||||||
|
|
||||||
|
Single repo: hooks/commands.py
|
||||||
|
Multi-repo: hooks/commands_0.py, hooks/commands_1.py, ... (not tested)
|
||||||
|
"""
|
||||||
|
plugin_paths = get_plugin_code_paths(stack_name)
|
||||||
|
sources = [p.joinpath("deploy", "commands.py") for p in plugin_paths if p.joinpath("deploy", "commands.py").exists()]
|
||||||
|
if not sources:
|
||||||
|
return
|
||||||
|
hooks_dir = target_dir / "hooks"
|
||||||
|
hooks_dir.mkdir(exist_ok=True)
|
||||||
|
if len(sources) == 1:
|
||||||
|
copyfile(sources[0], hooks_dir / "commands.py")
|
||||||
|
else:
|
||||||
|
for i, src in enumerate(sources):
|
||||||
|
copyfile(src, hooks_dir / f"commands_{i}.py")
|
||||||
|
|
||||||
|
|
||||||
def _write_deployment_files(
|
def _write_deployment_files(
|
||||||
target_dir: Path,
|
target_dir: Path,
|
||||||
spec_file: Path,
|
spec_file: Path,
|
||||||
|
|
@ -1138,6 +1158,8 @@ def _write_deployment_files(
|
||||||
copyfile(spec_file, target_dir.joinpath(constants.spec_file_name))
|
copyfile(spec_file, target_dir.joinpath(constants.spec_file_name))
|
||||||
copyfile(stack_file, target_dir.joinpath(constants.stack_file_name))
|
copyfile(stack_file, target_dir.joinpath(constants.stack_file_name))
|
||||||
|
|
||||||
|
_copy_hooks(stack_name, target_dir)
|
||||||
|
|
||||||
# Create deployment file if requested
|
# Create deployment file if requested
|
||||||
if include_deployment_file:
|
if include_deployment_file:
|
||||||
_create_deployment_file(target_dir, stack_source=stack_source)
|
_create_deployment_file(target_dir, stack_source=stack_source)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue