From 96b68c493f93626730846b392aa2409312458674 Mon Sep 17 00:00:00 2001 From: chrisfu Date: Sun, 18 Jan 2026 16:14:09 -0800 Subject: [PATCH] remove background files --- .gitignore | 1 + .../4411745888/env.sh | 29 - .../4411745888/hosts.txt | 15 - .../4411745888/init_cloudnative_pg.sh | 326 -------- .../4411745888/init_k8s.sh | 203 ----- .../4411745888/init_openbao.sh | 490 ----------- .../4411745888/init_port_forwards.sh | 537 ------------ .../4411745888/init_prole-db.sh | 215 ----- .../4413171632/env.sh | 29 - .../4413171632/hosts.txt | 15 - .../4413171632/init_cloudnative_pg.sh | 326 -------- .../4413171632/init_k8s.sh | 203 ----- .../4413171632/init_openbao.sh | 490 ----------- .../4413171632/init_port_forwards.sh | 537 ------------ .../4413171632/init_prole-db.sh | 215 ----- .../4452427696/env.sh | 29 - .../4452427696/hosts.txt | 15 - .../4452427696/init_cloudnative_pg.sh | 326 -------- .../4452427696/init_k8s.sh | 203 ----- .../4452427696/init_openbao.sh | 490 ----------- .../4452427696/init_port_forwards.sh | 537 ------------ .../4452427696/init_prole-db.sh | 215 ----- .../4491602512/env.sh | 29 - .../4491602512/hosts.txt | 15 - .../4491602512/init_cloudnative_pg.sh | 326 -------- .../4491602512/init_k8s.sh | 203 ----- .../4491602512/init_openbao.sh | 490 ----------- .../4491602512/init_port_forwards.sh | 537 ------------ .../4491602512/init_prole-db.sh | 215 ----- .../4494452656/env.sh | 29 - .../4494452656/hosts.txt | 15 - .../4494452656/init_cloudnative_pg.sh | 326 -------- .../4494452656/init_k8s.sh | 203 ----- .../4494452656/init_openbao.sh | 490 ----------- .../4494452656/init_port_forwards.sh | 537 ------------ .../4494452656/init_prole-db.sh | 330 -------- .../localpycs/pyimod01_archive.pyc | Bin 5054 -> 0 bytes .../localpycs/pyimod02_importers.pyc | Bin 32335 -> 0 bytes .../localpycs/pyimod03_ctypes.pyc | Bin 6635 -> 0 bytes .../localpycs/struct.pyc | Bin 307 -> 0 bytes build/setup/localpycs/pyimod01_archive.pyc | Bin 5054 -> 0 bytes build/setup/localpycs/pyimod02_importers.pyc | Bin 32335 -> 0 bytes build/setup/localpycs/pyimod03_ctypes.pyc | Bin 6635 -> 0 bytes build/setup/localpycs/struct.pyc | Bin 307 -> 0 bytes img/installWindow.png | Bin 2003909 -> 0 bytes img/ringPerspective.jpg | Bin 347669 -> 0 bytes img/test.png | Bin 4040 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 852 -> 0 bytes installer/__pycache__/build.cpython-314.pyc | Bin 3406 -> 0 bytes installer/__pycache__/config.cpython-314.pyc | Bin 9376 -> 0 bytes installer/__pycache__/deploy.cpython-314.pyc | Bin 27214 -> 0 bytes installer/__pycache__/main.cpython-314.pyc | Bin 2027 -> 0 bytes installer/__pycache__/screen.cpython-314.pyc | Bin 15233 -> 0 bytes .../__pycache__/workstation.cpython-314.pyc | Bin 3856 -> 0 bytes prole-db.iml | 6 + prole-mssql-db/.system/instance_id | 3 - .../Content/3ACF660917F73E764D4410BF1EAA48F5 | Bin 1456 -> 0 bytes .../Content/4B56126FA52F7DB9080F75930F649BFA | Bin 1456 -> 0 bytes .../Content/55C4822D0F35FE8F4F67713B4F628992 | Bin 1756 -> 0 bytes .../Content/750F9863DC96151354F5941FD665FA84 | Bin 1456 -> 0 bytes .../Content/FEE33CE020C970EA56929081C2D05808 | Bin 1456 -> 0 bytes .../MetaData/3ACF660917F73E764D4410BF1EAA48F5 | Bin 324 -> 0 bytes .../MetaData/4B56126FA52F7DB9080F75930F649BFA | Bin 324 -> 0 bytes .../MetaData/55C4822D0F35FE8F4F67713B4F628992 | Bin 254 -> 0 bytes .../MetaData/750F9863DC96151354F5941FD665FA84 | Bin 324 -> 0 bytes .../MetaData/FEE33CE020C970EA56929081C2D05808 | Bin 324 -> 0 bytes .../My/AppContainerUserCertRead | 0 ...e=neutral, PublicKeyToken=b03f5f7f11d50a3a | 0 ...e=neutral, PublicKeyToken=b03f5f7f11d50a3a | 0 .../.system/system/debug/PASSWD.LOG | 0 prole-mssql-db/.system/system/licensing.hiv | Bin 8192 -> 0 bytes prole-mssql-db/.system/system/lsa.hiv | Bin 16384 -> 0 bytes prole-mssql-db/.system/system/lsasetup.log | Bin 1378 -> 0 bytes prole-mssql-db/.system/system/security.hiv | Bin 53248 -> 0 bytes .../User/113a9b59-ae3c-4253-89c1-0078e8eb0d0d | Bin 468 -> 0 bytes .../Microsoft/Protect/S-1-5-18/User/Preferred | Bin 24 -> 0 bytes {prole-dns => prole-net}/init-prole-dns.sh | 0 ...install_logic.cpython-314-pytest-9.0.2.pyc | Bin 10113 -> 0 bytes ...st_navigation.cpython-314-pytest-9.0.2.pyc | Bin 17933 -> 0 bytes .../test_build.cpython-314-pytest-9.0.2.pyc | Bin 3365 -> 0 bytes .../test_config.cpython-314-pytest-9.0.2.pyc | Bin 8909 -> 0 bytes .../test_screen.cpython-314-pytest-9.0.2.pyc | Bin 8551 -> 0 bytes web/WEB-INF/web.xml | 6 - www/README.md | 131 --- www/images/prole-type.gif | Bin 599 -> 0 bytes www/index.html | 78 -- www/installer.html | 762 ------------------ www/pom.xml | 86 -- www/soundBlock/index.html | 104 --- .../java/org/prole/shop/ShopApplication.java | 13 - .../prole/shop/batch/AvroMapItemWriter.java | 111 --- .../shop/batch/BatchExportConfiguration.java | 159 ---- .../shop/batch/HourlyArchiveScheduler.java | 39 - .../prole/shop/controller/CartController.java | 87 -- .../shop/controller/CheckoutController.java | 93 --- .../prole/shop/controller/HomeController.java | 41 - .../shop/controller/ProductController.java | 35 - .../shop/controller/ServiceController.java | 51 -- .../java/org/prole/shop/model/CartItem.java | 146 ---- .../main/java/org/prole/shop/model/Order.java | 290 ------- .../java/org/prole/shop/model/OrderItem.java | 114 --- .../java/org/prole/shop/model/Product.java | 169 ---- .../org/prole/shop/model/ScheduleSlot.java | 100 --- .../java/org/prole/shop/model/Service.java | 151 ---- .../shop/repository/CartItemRepository.java | 13 - .../shop/repository/OrderRepository.java | 12 - .../shop/repository/ProductRepository.java | 13 - .../repository/ScheduleSlotRepository.java | 25 - .../shop/repository/ServiceRepository.java | 13 - .../prole/shop/service/CalendarService.java | 42 - .../org/prole/shop/service/CartService.java | 114 --- .../org/prole/shop/service/OrderService.java | 128 --- .../prole/shop/service/ProductService.java | 34 - .../prole/shop/service/ServiceService.java | 34 - .../prole/shop/service/ShippingService.java | 44 - www/src/main/resources/application.properties | 38 - www/src/main/resources/data.sql | 40 - www/src/main/resources/schema.sql | 119 --- .../resources/static/images/placeholder.jpg | 1 - .../main/resources/templates/cart/view.html | 91 --- .../resources/templates/checkout/form.html | 114 --- .../resources/templates/checkout/success.html | 21 - www/src/main/resources/templates/index.html | 52 -- www/src/main/resources/templates/layout.html | 79 -- .../resources/templates/products/detail.html | 38 - .../resources/templates/products/list.html | 26 - .../resources/templates/services/detail.html | 61 -- .../resources/templates/services/list.html | 25 - 128 files changed, 7 insertions(+), 13136 deletions(-) delete mode 100755 MagicMock/mock.Entry().get().strip()/4411745888/env.sh delete mode 100644 MagicMock/mock.Entry().get().strip()/4411745888/hosts.txt delete mode 100755 MagicMock/mock.Entry().get().strip()/4411745888/init_cloudnative_pg.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4411745888/init_k8s.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4411745888/init_openbao.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4411745888/init_port_forwards.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4411745888/init_prole-db.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4413171632/env.sh delete mode 100644 MagicMock/mock.Entry().get().strip()/4413171632/hosts.txt delete mode 100755 MagicMock/mock.Entry().get().strip()/4413171632/init_cloudnative_pg.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4413171632/init_k8s.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4413171632/init_openbao.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4413171632/init_port_forwards.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4413171632/init_prole-db.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4452427696/env.sh delete mode 100644 MagicMock/mock.Entry().get().strip()/4452427696/hosts.txt delete mode 100755 MagicMock/mock.Entry().get().strip()/4452427696/init_cloudnative_pg.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4452427696/init_k8s.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4452427696/init_openbao.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4452427696/init_port_forwards.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4452427696/init_prole-db.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4491602512/env.sh delete mode 100644 MagicMock/mock.Entry().get().strip()/4491602512/hosts.txt delete mode 100755 MagicMock/mock.Entry().get().strip()/4491602512/init_cloudnative_pg.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4491602512/init_k8s.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4491602512/init_openbao.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4491602512/init_port_forwards.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4491602512/init_prole-db.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4494452656/env.sh delete mode 100644 MagicMock/mock.Entry().get().strip()/4494452656/hosts.txt delete mode 100755 MagicMock/mock.Entry().get().strip()/4494452656/init_cloudnative_pg.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4494452656/init_k8s.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4494452656/init_openbao.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4494452656/init_port_forwards.sh delete mode 100755 MagicMock/mock.Entry().get().strip()/4494452656/init_prole-db.sh delete mode 100644 build/Install Prole Infrastructure/localpycs/pyimod01_archive.pyc delete mode 100644 build/Install Prole Infrastructure/localpycs/pyimod02_importers.pyc delete mode 100644 build/Install Prole Infrastructure/localpycs/pyimod03_ctypes.pyc delete mode 100644 build/Install Prole Infrastructure/localpycs/struct.pyc delete mode 100644 build/setup/localpycs/pyimod01_archive.pyc delete mode 100644 build/setup/localpycs/pyimod02_importers.pyc delete mode 100644 build/setup/localpycs/pyimod03_ctypes.pyc delete mode 100644 build/setup/localpycs/struct.pyc delete mode 100644 img/installWindow.png delete mode 100644 img/ringPerspective.jpg delete mode 100644 img/test.png delete mode 100644 installer/__pycache__/__init__.cpython-314.pyc delete mode 100644 installer/__pycache__/build.cpython-314.pyc delete mode 100644 installer/__pycache__/config.cpython-314.pyc delete mode 100644 installer/__pycache__/deploy.cpython-314.pyc delete mode 100644 installer/__pycache__/main.cpython-314.pyc delete mode 100644 installer/__pycache__/screen.cpython-314.pyc delete mode 100644 installer/__pycache__/workstation.cpython-314.pyc delete mode 100644 prole-mssql-db/.system/instance_id delete mode 100644 prole-mssql-db/.system/profiles/Client/AppData/LocalLow/Microsoft/CryptnetUrlCache/Content/3ACF660917F73E764D4410BF1EAA48F5 delete mode 100644 prole-mssql-db/.system/profiles/Client/AppData/LocalLow/Microsoft/CryptnetUrlCache/Content/4B56126FA52F7DB9080F75930F649BFA delete mode 100644 prole-mssql-db/.system/profiles/Client/AppData/LocalLow/Microsoft/CryptnetUrlCache/Content/55C4822D0F35FE8F4F67713B4F628992 delete mode 100644 prole-mssql-db/.system/profiles/Client/AppData/LocalLow/Microsoft/CryptnetUrlCache/Content/750F9863DC96151354F5941FD665FA84 delete mode 100644 prole-mssql-db/.system/profiles/Client/AppData/LocalLow/Microsoft/CryptnetUrlCache/Content/FEE33CE020C970EA56929081C2D05808 delete mode 100644 prole-mssql-db/.system/profiles/Client/AppData/LocalLow/Microsoft/CryptnetUrlCache/MetaData/3ACF660917F73E764D4410BF1EAA48F5 delete mode 100644 prole-mssql-db/.system/profiles/Client/AppData/LocalLow/Microsoft/CryptnetUrlCache/MetaData/4B56126FA52F7DB9080F75930F649BFA delete mode 100644 prole-mssql-db/.system/profiles/Client/AppData/LocalLow/Microsoft/CryptnetUrlCache/MetaData/55C4822D0F35FE8F4F67713B4F628992 delete mode 100644 prole-mssql-db/.system/profiles/Client/AppData/LocalLow/Microsoft/CryptnetUrlCache/MetaData/750F9863DC96151354F5941FD665FA84 delete mode 100644 prole-mssql-db/.system/profiles/Client/AppData/LocalLow/Microsoft/CryptnetUrlCache/MetaData/FEE33CE020C970EA56929081C2D05808 delete mode 100644 prole-mssql-db/.system/profiles/Client/AppData/Roaming/Microsoft/SystemCertificates/My/AppContainerUserCertRead delete mode 100644 prole-mssql-db/.system/programdata/Microsoft/NetFramework/BreadcrumbStore/Microsoft.Diagnostics.Tracing.EventSource, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a delete mode 100644 prole-mssql-db/.system/programdata/Microsoft/NetFramework/BreadcrumbStore/Microsoft.Diagnostics.Tracing.EventSource, Version=1.1.26.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a delete mode 100644 prole-mssql-db/.system/system/debug/PASSWD.LOG delete mode 100644 prole-mssql-db/.system/system/licensing.hiv delete mode 100644 prole-mssql-db/.system/system/lsa.hiv delete mode 100644 prole-mssql-db/.system/system/lsasetup.log delete mode 100644 prole-mssql-db/.system/system/security.hiv delete mode 100644 prole-mssql-db/.system/system/system32/Microsoft/Protect/S-1-5-18/User/113a9b59-ae3c-4253-89c1-0078e8eb0d0d delete mode 100644 prole-mssql-db/.system/system/system32/Microsoft/Protect/S-1-5-18/User/Preferred rename {prole-dns => prole-net}/init-prole-dns.sh (100%) delete mode 100644 tests/__pycache__/test_install_logic.cpython-314-pytest-9.0.2.pyc delete mode 100644 tests/__pycache__/test_navigation.cpython-314-pytest-9.0.2.pyc delete mode 100644 tests/installer/__pycache__/test_build.cpython-314-pytest-9.0.2.pyc delete mode 100644 tests/installer/__pycache__/test_config.cpython-314-pytest-9.0.2.pyc delete mode 100644 tests/installer/__pycache__/test_screen.cpython-314-pytest-9.0.2.pyc delete mode 100644 web/WEB-INF/web.xml delete mode 100644 www/README.md delete mode 100644 www/images/prole-type.gif delete mode 100644 www/index.html delete mode 100644 www/installer.html delete mode 100644 www/pom.xml delete mode 100644 www/soundBlock/index.html delete mode 100644 www/src/main/java/org/prole/shop/ShopApplication.java delete mode 100644 www/src/main/java/org/prole/shop/batch/AvroMapItemWriter.java delete mode 100644 www/src/main/java/org/prole/shop/batch/BatchExportConfiguration.java delete mode 100644 www/src/main/java/org/prole/shop/batch/HourlyArchiveScheduler.java delete mode 100644 www/src/main/java/org/prole/shop/controller/CartController.java delete mode 100644 www/src/main/java/org/prole/shop/controller/CheckoutController.java delete mode 100644 www/src/main/java/org/prole/shop/controller/HomeController.java delete mode 100644 www/src/main/java/org/prole/shop/controller/ProductController.java delete mode 100644 www/src/main/java/org/prole/shop/controller/ServiceController.java delete mode 100644 www/src/main/java/org/prole/shop/model/CartItem.java delete mode 100644 www/src/main/java/org/prole/shop/model/Order.java delete mode 100644 www/src/main/java/org/prole/shop/model/OrderItem.java delete mode 100644 www/src/main/java/org/prole/shop/model/Product.java delete mode 100644 www/src/main/java/org/prole/shop/model/ScheduleSlot.java delete mode 100644 www/src/main/java/org/prole/shop/model/Service.java delete mode 100644 www/src/main/java/org/prole/shop/repository/CartItemRepository.java delete mode 100644 www/src/main/java/org/prole/shop/repository/OrderRepository.java delete mode 100644 www/src/main/java/org/prole/shop/repository/ProductRepository.java delete mode 100644 www/src/main/java/org/prole/shop/repository/ScheduleSlotRepository.java delete mode 100644 www/src/main/java/org/prole/shop/repository/ServiceRepository.java delete mode 100644 www/src/main/java/org/prole/shop/service/CalendarService.java delete mode 100644 www/src/main/java/org/prole/shop/service/CartService.java delete mode 100644 www/src/main/java/org/prole/shop/service/OrderService.java delete mode 100644 www/src/main/java/org/prole/shop/service/ProductService.java delete mode 100644 www/src/main/java/org/prole/shop/service/ServiceService.java delete mode 100644 www/src/main/java/org/prole/shop/service/ShippingService.java delete mode 100644 www/src/main/resources/application.properties delete mode 100644 www/src/main/resources/data.sql delete mode 100644 www/src/main/resources/schema.sql delete mode 100644 www/src/main/resources/static/images/placeholder.jpg delete mode 100644 www/src/main/resources/templates/cart/view.html delete mode 100644 www/src/main/resources/templates/checkout/form.html delete mode 100644 www/src/main/resources/templates/checkout/success.html delete mode 100644 www/src/main/resources/templates/index.html delete mode 100644 www/src/main/resources/templates/layout.html delete mode 100644 www/src/main/resources/templates/products/detail.html delete mode 100644 www/src/main/resources/templates/products/list.html delete mode 100644 www/src/main/resources/templates/services/detail.html delete mode 100644 www/src/main/resources/templates/services/list.html diff --git a/.gitignore b/.gitignore index 925ac58..15b14c4 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ htmlcov/ /workstation/Prole/Saved/ /workstation/Prole/DerivedDataCache/ /workstation/Prole/Build/ +/ssh-keys/ diff --git a/MagicMock/mock.Entry().get().strip()/4411745888/env.sh b/MagicMock/mock.Entry().get().strip()/4411745888/env.sh deleted file mode 100755 index 430df05..0000000 --- a/MagicMock/mock.Entry().get().strip()/4411745888/env.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -# Prole environment configuration -# This file is generated by the installer. Source it in new shells, or execute as a wrapper: -# "$PROLE_HOME/env.sh" [args…] -# shellcheck shell=bash -export PROLE_HOME="" -export PROLE_CONF="" -export PROLE_DATA="" -export PROLE_LOGS="" -export PROLE_SERVICE="" - -# Ensure PATH works for GUI-launched shells (Docker, Ollama, etc.) -_prole_add_path() { case ":${PATH}:" in *":$1:"*) ;; *) PATH="$1:${PATH:-}" ;; esac; } -_prole_add_path "$PROLE_HOME/bin" -_prole_add_path "/opt/homebrew/bin" -_prole_add_path "/usr/local/bin" -_prole_add_path "/usr/bin" -_prole_add_path "/bin" -_prole_add_path "/usr/sbin" -_prole_add_path "/sbin" -export PATH - -# Add custom paths below if needed (examples): -# _prole_add_path "/Applications/Ollama.app/Contents/MacOS" - -# If executed with arguments, run them under this environment -if [ "$#" -gt 0 ]; then - exec "$@" -fi diff --git a/MagicMock/mock.Entry().get().strip()/4411745888/hosts.txt b/MagicMock/mock.Entry().get().strip()/4411745888/hosts.txt deleted file mode 100644 index 4e73115..0000000 --- a/MagicMock/mock.Entry().get().strip()/4411745888/hosts.txt +++ /dev/null @@ -1,15 +0,0 @@ -myrddin.prole.org 10.0.0.3 -raspberry.prole.org 10.0.0.4 -pi.prole.org 10.0.0.5 -synology.prole.org 10.0.0.203 -morgoth.prole.org 10.0.0.204 -zinfandel.prole.org 10.0.0.205 -aventage.prole.org 10.0.0.206 -retropie.prole.org 10.0.0.207 -fairyland.prole.org 10.0.0.208 -k8s.prole.org zinfandel.prole.org -mc.prole.org 73.15.20.166 -morana.prole.org 10.0.0.66 -ollama.prole.org 73.15.20.166 -svc.prole.org 73.15.20.166 -www.prole.org ghs.googlehosted.com \ No newline at end of file diff --git a/MagicMock/mock.Entry().get().strip()/4411745888/init_cloudnative_pg.sh b/MagicMock/mock.Entry().get().strip()/4411745888/init_cloudnative_pg.sh deleted file mode 100755 index 6708415..0000000 --- a/MagicMock/mock.Entry().get().strip()/4411745888/init_cloudnative_pg.sh +++ /dev/null @@ -1,326 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_cloudnative_pg.sh -# Purpose: -# - Distribute administrator ed25519 key pair to CloudNative‑PG as a Kubernetes Secret for cert auth -# - Configure Kerberos (GSSAPI) using external Kerberos KDC -# - Patch CNPG cluster to enable TLS and GSSAPI where possible -# -# Usage: -# ./init_cloudnative_pg.sh start|stop|status|restart -# ./init_cloudnative_pg.sh initialize # create/update k8s secrets/configs and patch CNPG -# ./init_cloudnative_pg.sh update|reload # re-apply/patch -# -# Requirements: -# - init_openbao.sh has been run (OpenBao running in k8s) -# - $PROLE_HOME/env.sh or $HOME/.prole/env.sh defining PROLE_SERVICE - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Load env without leaking our positional args to the env script (some env.sh may `exec "$@"`). -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -else - echo "ERROR: Missing env. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2 - exit 1 -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -if [[ -z "${PROLE_SERVICE:-}" ]]; then - echo "ERROR: PROLE_SERVICE is not defined in env." >&2 - exit 1 -fi - -ACTION=${1:-} -CNPG_CLUSTER_NAME=${2:-${CNPG_CLUSTER_NAME:-prole-db}} -NAMESPACE=${NAMESPACE:-prole} -OPENBAO_NAME=${OPENBAO_NAME:-openbao} -REALM=${REALM:-PROLE.ORG} -DOMAIN=${DOMAIN:-prole.org} -KRB5_KDC=${KRB5_KDC:-} -KRB5_ADMIN=${KRB5_ADMIN:-} - -CNPG_MANIFEST="$SCRIPT_DIR/../k8s/prole/prole-db.yaml" - -SECRETS_DIR="$PROLE_SERVICE/secrets" -ADMIN_PRIV="$SECRETS_DIR/admin_ed25519.key" -ADMIN_PUB="$SECRETS_DIR/admin_ed25519.pub" -OPENBAO_TOKEN_FILE="$SECRETS_DIR/openbao-root-token" - -ensure_tools() { - for t in kubectl curl openssl base64 jq; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -# Resolve OpenBao URL: prefer explicit env, then localhost port-forward, then cluster DNS -bao_service_url() { - if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then - echo "$PROLE_OPENBAO_URL" - return 0 - fi - # Prefer standard local port-forward managed by etc/init_port_forwards.sh - if curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then - echo "http://127.0.0.1:18200" - return 0 - fi - echo "http://$OPENBAO_NAME.$NAMESPACE.svc.cluster.local:8200" -} - -fetch_admin_keys_and_db_pass_from_bao_or_local() { - local token url - if [[ -f "$OPENBAO_TOKEN_FILE" ]]; then - token=$(cat "$OPENBAO_TOKEN_FILE") - else - token="" - fi - url=$(bao_service_url) - if [[ -n "$token" ]]; then - echo "Attempting to read admin key pair from OpenBao kv/prole/admin ..." - if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -e '.data.data' >/dev/null 2>&1; then - local priv_b64 pub_b64 - priv_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -r '.data.data.admin_private_key_b64') - pub_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -r '.data.data.admin_public_key_b64') - printf "%s" "$priv_b64" | base64 -d >"$ADMIN_PRIV" - printf "%s" "$pub_b64" | base64 -d >"$ADMIN_PUB" - chmod 0600 "$ADMIN_PRIV" - fi - - echo "Attempting to read database password from OpenBao kv/prole/db ..." - if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/db" | jq -e '.data.data' >/dev/null 2>&1; then - local db_pass - db_pass=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/db" | jq -r '.data.data.password') - if [[ -n "$db_pass" ]]; then - echo "Updating database user secret 'prole-db-user' from OpenBao ..." - kubectl create secret generic prole-db-user -n "$NAMESPACE" \ - --from-literal=username=prole \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -f - - - echo "Updating database superuser secret 'prole-db-superuser' from OpenBao ..." - kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \ - --from-literal=username=postgres \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -f - - fi - fi - fi - - if [[ -f "$ADMIN_PRIV" && -f "$ADMIN_PUB" ]]; then - echo "Using local admin key pair at $SECRETS_DIR" - return 0 - fi - echo "ERROR: Could not obtain admin key pair from OpenBao and no local files found." >&2 - exit 1 -} - -apply_cnpg_admin_secret() { - echo "Creating/updating Secret cnpg-admin-key ..." - kubectl create secret generic cnpg-admin-key -n "$NAMESPACE" \ - --from-file=admin.key="$ADMIN_PRIV" \ - --from-file=admin.pub="$ADMIN_PUB" \ - --dry-run=client -o yaml | kubectl apply -f - -} - -ensure_krb5_conf_configmap() { - echo "Creating/updating ConfigMap prole-krb5-conf ..." - - local kdc_val admin_val - if [[ -n "$KRB5_KDC" ]]; then - kdc_val="$KRB5_KDC" - admin_val=${KRB5_ADMIN:-$(echo "$KRB5_KDC" | cut -d, -f1)} - else - kdc_val="kdc.$DOMAIN" - admin_val="kdc.$DOMAIN" - fi - - local TMP - TMP=$(mktemp) - cat >"$TMP" </dev/null 2>&1; then - echo "CA secret $ca_secret_name already exists; skipping generation." - return 0 - fi - echo "Generating self-signed CA (RSA 4096) for CNPG ..." - local TMPD - TMPD=$(mktemp -d) - openssl genrsa -out "$TMPD/ca.key" 4096 - openssl req -x509 -new -key "$TMPD/ca.key" -out "$TMPD/ca.crt" -days 3650 -subj "/CN=Prole CNPG CA" - kubectl -n "$NAMESPACE" create secret generic "$ca_secret_name" \ - --from-file=ca.crt="$TMPD/ca.crt" \ - --from-file=ca.key="$TMPD/ca.key" \ - --dry-run=client -o yaml | kubectl apply -f - - rm -rf "$TMPD" -} - -patch_cnpg_cluster_for_auth() { - echo "Patching CNPG Cluster $CNPG_CLUSTER_NAME to enable GSSAPI and fix config (best-effort) ..." - # Best-effort patch; schema may vary with CNPG version. - # We remove krb_srvname as it is unrecognized in some PG 17 builds. - # We revert certificates to default to avoid operator TLS issues. - kubectl -n "$NAMESPACE" patch cluster "$CNPG_CLUSTER_NAME" --type merge -p "{ - \"spec\": { - \"postgresql\": { - \"parameters\": {\"krb_srvname\": null}, - \"pg_hba\": [ - \"local all postgres trust\", - \"host all postgres all scram-sha-256\", - \"host all all all gss include_realm=1 krb_realm=$REALM\", - \"host all all all scram-sha-256\" - ] - }, - \"certificates\": { - \"serverCASecret\": null, - \"clientCASecret\": null - } - } - }" >/dev/null || echo "Note: patch may need adjustment for your CNPG version." -} - -initialize() { - ensure_tools - - # Ensure port-forward is running for OpenBao (dependency) - echo "Ensuring port-forward for OpenBao is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart openbao - - fetch_admin_keys_and_db_pass_from_bao_or_local - apply_cnpg_admin_secret - ensure_krb5_conf_configmap - # generate_tls_if_missing - patch_cnpg_cluster_for_auth - - # Ensure port-forward is running for Postgres (local access) - echo "Ensuring port-forward for Postgres is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart postgres - - echo "Initialization complete for CNPG + Kerberos + cert artifacts." -} - -update_reload() { - initialize -} - -case "$ACTION" in - recreate) - ensure_tools - "$0" delete "$CNPG_CLUSTER_NAME" - "$0" create "$CNPG_CLUSTER_NAME" - ;; - create) - ensure_tools - echo "Installing CloudNative-PG operator ..." - kubectl apply --server-side -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.27/releases/cnpg-1.27.0.yaml - - echo "Creating CloudNative-PG cluster and resources for '$CNPG_CLUSTER_NAME' ..." - - kubectl apply -k "$SCRIPT_DIR/../k8s/prole" - - # Ensure prole-index-html exists for prole deployment readiness probe - if ! kubectl get configmap prole-index-html -n prole >/dev/null 2>&1; then - echo "Creating prole-index-html configmap..." - printf "

Prole

" > /tmp/index.html - kubectl create configmap prole-index-html --from-file=/tmp/index.html -n prole - rm /tmp/index.html - fi - - # Ensure prole-nginx-tls exists (self-signed for dev) - if ! kubectl get secret prole-nginx-tls -n prole >/dev/null 2>&1; then - echo "Generating self-signed prole-nginx-tls for development..." - openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout /tmp/nginx-tls.key -out /tmp/nginx-tls.crt \ - -subj "/CN=prole.org" >/dev/null 2>&1 - kubectl create secret tls prole-nginx-tls --key /tmp/nginx-tls.key --cert /tmp/nginx-tls.crt -n prole - rm /tmp/nginx-tls.key /tmp/nginx-tls.crt - fi - - echo "Initializing and patching cluster ..." - initialize - ;; - delete) - ensure_tools - echo "Deleting all resources for '$CNPG_CLUSTER_NAME' ..." - kubectl delete -k "$SCRIPT_DIR/../k8s/prole" --ignore-not-found - ;; - start) - ensure_tools - if [[ ! -f "$CNPG_MANIFEST" ]]; then - echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2 - exit 1 - fi - echo "Starting CloudNative-PG cluster from $CNPG_MANIFEST in namespace $NAMESPACE..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" - ;; - stop) - ensure_tools - if [[ ! -f "$CNPG_MANIFEST" ]]; then - echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2 - exit 1 - fi - echo "Stopping CloudNative-PG cluster using $CNPG_MANIFEST ..." - kubectl delete -f "$CNPG_MANIFEST" --ignore-not-found - ;; - status) - ensure_tools - echo "--- CloudNative-PG Cluster Status ($CNPG_CLUSTER_NAME) ---" - if kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then - kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" - echo "" - echo "CNPG Plugin Status:" - if kubectl cnpg version >/dev/null 2>&1; then - kubectl cnpg status "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" - else - echo "Note: 'kubectl cnpg' plugin not found; skipping detailed status." - fi - else - echo "Cluster '$CNPG_CLUSTER_NAME' not found in namespace '$NAMESPACE'." - fi - ;; - restart) - ensure_tools - "$0" stop - "$0" start - ;; - initialize) - initialize - ;; - update|reload) - update_reload - ;; - *) - echo "Usage: $0 {create|delete|recreate|start|stop|status|restart|initialize|update|reload} [dbname]" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4411745888/init_k8s.sh b/MagicMock/mock.Entry().get().strip()/4411745888/init_k8s.sh deleted file mode 100755 index 35f14aa..0000000 --- a/MagicMock/mock.Entry().get().strip()/4411745888/init_k8s.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# etc/init_k8s.sh -# Purpose: Manage k3d/k8s environment - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Preserve positional args while sourcing env -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -# Default values -VERBOSE=false -ENVIRONMENT="prod" -HOST="" -CLUSTER_NAME_DEFAULT="prole-dev-cluster" -CLUSTER_PORT_DEFAULT="6443" - -usage() { - cat </dev/null || { echo "ERROR: Missing required tool: $t" >&2; exit 1; } - done -} - -ensure_docker() { - if ! docker info >/dev/null 2>&1; then - echo "ERROR: Docker is not running." >&2 - exit 1 - fi -} - -initialize() { - ensure_tools - ensure_docker - - if [[ "$ENVIRONMENT" == "dev" ]]; then - if [[ -z "$CLUSTER_NAME" ]]; then - if [[ -t 0 ]]; then - read -p "Enter k3d cluster name [$CLUSTER_NAME_DEFAULT]: " CLUSTER_NAME - CLUSTER_NAME=${CLUSTER_NAME:-$CLUSTER_NAME_DEFAULT} - else - CLUSTER_NAME=$CLUSTER_NAME_DEFAULT - fi - fi - - if k3d cluster list "$CLUSTER_NAME" >/dev/null 2>&1; then - log "Cluster '$CLUSTER_NAME' already exists." - else - log "Creating k3d cluster '$CLUSTER_NAME'..." - k3d cluster create "$CLUSTER_NAME" -p "0.0.0.0:$CLUSTER_PORT_DEFAULT:6443@server:0" - fi - else - if [[ -n "$HOST" ]]; then - log "Environment is $ENVIRONMENT. Host is $HOST." - log "TODO: Fetch kubeconfig from $HOST (placeholder)." - else - log "Environment is $ENVIRONMENT. Use -h|--host to specify the remote cluster host." - fi - fi -} - -status() { - ensure_tools - log "Checking Docker status..." - if docker info >/dev/null 2>&1; then - echo "Docker is running." - else - echo "Docker is NOT running." - fi - - log "Listing k3d clusters..." - k3d cluster list -} - -get_cluster_name() { - # If a name was provided or exists in env, use it. - # Otherwise, if there is only one cluster, use it. - # Finally, use default. - if [[ -n "${CLUSTER_NAME:-}" ]]; then - echo "$CLUSTER_NAME" - return - fi - - local clusters - clusters=$(k3d cluster list --no-headers | awk '{print $1}') - local count - count=$(echo "$clusters" | grep -c . || true) - - if [[ "$count" -eq 1 ]]; then - echo "$clusters" - else - echo "$CLUSTER_NAME_DEFAULT" - fi -} - -case "$ACTION" in - initialize) - initialize - ;; - status) - status - ;; - start) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Starting k3d cluster '$CLUSTER_NAME'..." - k3d cluster start "$CLUSTER_NAME" - ;; - stop) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Stopping k3d cluster '$CLUSTER_NAME'..." - k3d cluster stop "$CLUSTER_NAME" - ;; - restart) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Restarting k3d cluster '$CLUSTER_NAME'..." - k3d cluster stop "$CLUSTER_NAME" - k3d cluster start "$CLUSTER_NAME" - ;; - *) - usage - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4411745888/init_openbao.sh b/MagicMock/mock.Entry().get().strip()/4411745888/init_openbao.sh deleted file mode 100755 index 3e59151..0000000 --- a/MagicMock/mock.Entry().get().strip()/4411745888/init_openbao.sh +++ /dev/null @@ -1,490 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_openbao.sh -# Purpose: -# - Deploy OpenBao to Kubernetes (dev mode) and store admin ed25519 key pair -# Generate and apply a Kerberos krb5.conf ConfigMap for an external realm -# - Local Docker helpers for OpenBao (optional) - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Preserve positional args while sourcing env -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -if [[ -z "${PROLE_SERVICE:-}" ]]; then - echo "ERROR: PROLE_SERVICE is not defined in env. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2 - exit 1 -fi - -ACTION=${1:-} - -# Defaults -NAMESPACE=${NAMESPACE:-prole} -OPENBAO_NAME=${OPENBAO_NAME:-openbao} -OPENBAO_MANIFEST_DIR="$SCRIPT_DIR/../k8s/openbao" - -# Kerberos realm defaults -KRB5_REALM=${KRB5_REALM:-PROLE.ORG} -DOMAIN=${DOMAIN:-$(echo "$KRB5_REALM" | tr 'A-Z' 'a-z')} -KRB5_KDC=${KRB5_KDC:-kdc.$DOMAIN} -KRB5_ADMIN=${KRB5_ADMIN:-} - -# Secrets and token locations -SECRETS_DIR="$PROLE_SERVICE/secrets" -mkdir -p "$SECRETS_DIR" -admin_key_priv="$SECRETS_DIR/admin_ed25519.key" -admin_key_pub="$SECRETS_DIR/admin_ed25519.pub" -root_token_file="$SECRETS_DIR/openbao-root-token" - -ensure_tools() { - for t in kubectl curl openssl base64; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -ensure_docker() { - command -v docker >/dev/null || { echo "Missing required tool: docker" >&2; exit 1; } -} - -ensure_admin_keypair() { - # Ensure admin ed25519 keypair exists and a root token is available - mkdir -p "$SECRETS_DIR" - if [[ ! -f "$admin_key_priv" || ! -f "$admin_key_pub" ]]; then - echo "Generating admin ed25519 keypair in $SECRETS_DIR ..." - openssl genpkey -algorithm ED25519 -out "$admin_key_priv" - openssl pkey -in "$admin_key_priv" -pubout -out "$admin_key_pub" - chmod 0600 "$admin_key_priv" - fi - # Ensure a root token exists for OpenBao dev server interactions - if [[ ! -f "$root_token_file" || ! -s "$root_token_file" ]]; then - openssl rand -hex 24 >"$root_token_file" - chmod 0600 "$root_token_file" - fi -} - -generate_openbao_manifests() { - mkdir -p "$OPENBAO_MANIFEST_DIR" - # Only create a simple deployment if none exists; otherwise respect current file - local deploy="$OPENBAO_MANIFEST_DIR/deployment.yaml" - if [[ ! -f "$deploy" ]]; then - cat >"$deploy" <"$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" </dev/null 2>&1; then - kubectl rollout status statefulset/$OPENBAO_NAME -n "$NAMESPACE" --timeout=120s - else - kubectl rollout status deploy/$OPENBAO_NAME -n "$NAMESPACE" --timeout=120s - fi -} - -init_openbao_kv_and_store_admin_key() { - local token - token=$(cat "$root_token_file") - # Enable kv (if not already) and write keys - local svc="http://127.0.0.1:18200" - echo "Configuring KV at $svc ..." - - # Check if kv/ is already mounted - if curl -sS -H "X-Vault-Token: $token" "$svc/v1/sys/mounts" | grep -q '"kv/":'; then - echo "KV path 'kv/' is already enabled." - else - curl -sS -H "X-Vault-Token: $token" -X POST "$svc/v1/sys/mounts/kv" \ - -d '{"type":"kv","options":{"version":"2"}}' >/dev/null 2>&1 || true - fi - - local priv pub - priv=$(base64 <"$admin_key_priv" | tr -d '\n') - pub=$(base64 <"$admin_key_pub" | tr -d '\n') - - # Check if admin key already exists and matches - local existing_data - existing_data=$(curl -sS -H "X-Vault-Token: $token" "$svc/v1/kv/data/prole/admin" 2>/dev/null || true) - if [[ -n "$existing_data" ]]; then - local ex_priv ex_pub - ex_priv=$(echo "$existing_data" | grep -o '"admin_private_key_b64":"[^"]*' | cut -d'"' -f4 || true) - ex_pub=$(echo "$existing_data" | grep -o '"admin_public_key_b64":"[^"]*' | cut -d'"' -f4 || true) - - if [[ "$ex_priv" == "$priv" && "$ex_pub" == "$pub" ]]; then - echo "Admin key pair in OpenBao kv/prole/admin is already up to date." - else - echo "Writing admin key pair to kv/prole/admin ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/admin" \ - -d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null - echo "Stored admin key pair in OpenBao kv/prole/admin." - fi - else - echo "Writing admin key pair to kv/prole/admin ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/admin" \ - -d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null - echo "Stored admin key pair in OpenBao kv/prole/admin." - fi - - # Store User SSH keys if they exist - local user_key_priv="$HOME/.ssh/id_prole_ed25519" - local user_key_pub="$HOME/.ssh/id_prole_ed25519.pub" - if [[ -f "$user_key_priv" && -f "$user_key_pub" ]]; then - local u_priv u_pub - u_priv=$(base64 <"$user_key_priv" | tr -d '\n') - u_pub=$(base64 <"$user_key_pub" | tr -d '\n') - local username=${PROLE_DB_USER:-"prole"} - - echo "Writing user SSH keys for '$username' to kv/prole/user ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/user" \ - -d "{\"data\":{\"username\":\"$username\",\"private_key_b64\":\"$u_priv\",\"public_key_b64\":\"$u_pub\"}}" >/dev/null - echo "Stored user SSH keys in OpenBao kv/prole/user." - fi - - if [[ -n "$db_pass" ]]; then - local username=${PROLE_DB_USER:-"prole"} - echo "Writing database user password for '$username' to kv/prole/db ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/db" \ - -d "{\"data\":{\"username\":\"$username\",\"password\":\"$db_pass\"}}" >/dev/null - echo "Stored database password in OpenBao kv/prole/db." - fi -} - -# Removed: prompt_admin_password_and_apply_secret (no AD admin secret required) - -docker_start() { - echo "Starting local Docker container for OpenBao ..." - ensure_docker - docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true - # OpenBao dev - local token - token=$(cat "$root_token_file" 2>/dev/null || true) - if [[ -z "$token" ]]; then - token=$(openssl rand -hex 24) - printf "%s" "$token" >"$root_token_file" - chmod 0600 "$root_token_file" - fi - docker run -d --name "$OPENBAO_NAME" -p 18200:8200 \ - "$OPENBAO_IMAGE" server -dev -dev-listen-address=0.0.0.0:8200 -dev-root-token-id="$token" - echo "Docker container started: $OPENBAO_NAME" -} - -docker_stop() { - docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true - echo "Stopped OpenBao container if it was running." -} - -docker_restart() { - docker_stop - docker_start -} - -# status command implementation wrapped in a function to avoid top-level 'local' -cmd_status() { - echo "--- init_primary_domain status ---" - echo "Namespace: $NAMESPACE" - echo "OpenBao name: $OPENBAO_NAME" - echo "Manifest dir: $OPENBAO_MANIFEST_DIR" - - # Files/manifests present - local ok=0 - if [[ -f "$OPENBAO_MANIFEST_DIR/deployment.yaml" ]]; then - echo "[OK] OpenBao deployment manifest exists" - else - echo "[MISSING] $OPENBAO_MANIFEST_DIR/deployment.yaml" - ok=1 - fi - if [[ -f "$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" ]]; then - echo "[OK] Kerberos ConfigMap manifest exists" - else - echo "[MISSING] $OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" - ok=1 - fi - - # K8s resources - if kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" >/dev/null 2>&1; then - local ready desired - ready=$(kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") - desired=$(kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0") - ready=${ready:-0} - desired=${desired:-0} - if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then - echo "[OK] OpenBao StatefulSet running ($ready/$desired ready)" - else - echo "[WARN] OpenBao StatefulSet not fully ready ($ready/$desired)" - ok=1 - fi - elif kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" >/dev/null 2>&1; then - # Get readiness - local ready desired - ready=$(kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") - desired=$(kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0") - ready=${ready:-0} - desired=${desired:-0} - if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then - echo "[OK] OpenBao Deployment running ($ready/$desired ready)" - else - echo "[WARN] OpenBao Deployment not fully ready ($ready/$desired)" - ok=1 - fi - else - echo "[MISSING] OpenBao Deployment or StatefulSet '$OPENBAO_NAME' in namespace '$NAMESPACE'" - ok=1 - fi - - if kubectl -n "$NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then - echo "[OK] Kerberos ConfigMap 'prole-krb5-conf' present" - else - echo "[MISSING] Kerberos ConfigMap 'prole-krb5-conf' in namespace '$NAMESPACE'" - ok=1 - fi - - # Docker (optional local mode) - if command -v docker >/dev/null 2>&1; then - if docker ps --format '{{.Names}}' | grep -Fxq "$OPENBAO_NAME"; then - echo "[OK] Docker container '$OPENBAO_NAME' is running" - else - # Not necessarily an error; we primarily use k8s - echo "[INFO] Docker container '$OPENBAO_NAME' not running (k8s mode may be in use)" - fi - fi - - # Secrets and tokens - if [[ -f "$admin_key_priv" && -f "$admin_key_pub" ]]; then - echo "[OK] Admin ed25519 keypair present in $SECRETS_DIR" - else - echo "[MISSING] Admin keypair files in $SECRETS_DIR" - ok=1 - fi - if [[ -s "$root_token_file" ]]; then - echo "[OK] OpenBao root token file present" - else - echo "[MISSING] OpenBao root token file ($root_token_file)" - ok=1 - fi - - # OpenBao reachability and token validity (best-effort) - # Prefer explicit override, then localhost port-forward, then cluster DNS - local svc_url - if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then - svc_url="$PROLE_OPENBAO_URL" - elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then - svc_url="http://127.0.0.1:18200" - else - svc_url="http://$OPENBAO_NAME.$NAMESPACE.svc.cluster.local:8200" - fi - if curl -sS "$svc_url/v1/sys/health" >/dev/null 2>&1; then - echo "[OK] OpenBao service reachable" - if [[ -s "$root_token_file" ]]; then - local code - code=$(curl -s -o /dev/null -w "%{http_code}" -H "X-Vault-Token: $(cat "$root_token_file")" "$svc_url/v1/sys/mounts" || echo "000") - if [[ "$code" == "200" ]]; then - echo "[OK] OpenBao root token authenticates" - else - echo "[WARN] OpenBao root token did not authenticate (HTTP $code)" - ok=1 - fi - fi - else - echo "[WARN] OpenBao service not reachable at $svc_url" - ok=1 - fi - - # Service certificate existence (CNPG TLS) - local CNPG_CLUSTER_NAME - CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db} - if kubectl -n "$NAMESPACE" get secret "${CNPG_CLUSTER_NAME}-tls" >/dev/null 2>&1; then - echo "[OK] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' exists" - else - echo "[INFO] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' not found" - fi - - echo "-------------------------------------" - if [[ $ok -eq 0 ]]; then - echo "Status: OK" - return 0 - else - echo "Status: Issues detected" - return 1 - fi -} - -case "$ACTION" in - start) - ensure_tools - ensure_docker - ensure_admin_keypair - docker_start - ;; - status) - ensure_tools - cmd_status - ;; - stop) - ensure_docker - docker_stop - ;; - restart) - ensure_tools - ensure_docker - ensure_admin_keypair - docker_restart - ;; - initialize) - ensure_tools - # Read password from stdin if provided - db_pass="" - if [[ ! -t 0 ]]; then - read -r db_pass - fi - - ensure_admin_keypair - generate_openbao_manifests - generate_kerberos_configmap - apply_k8s - - if [[ -n "$db_pass" ]]; then - local username=${PROLE_DB_USER:-"prole"} - echo "Creating database user secret 'prole-db-user' for '$username' ..." - kubectl create secret generic prole-db-user -n "$NAMESPACE" \ - --from-literal=username="$username" \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f - - - echo "Creating database superuser secret 'prole-db-superuser' ..." - kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \ - --from-literal=username=postgres \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f - - fi - - wait_for_openbao - - # Ensure port-forward is running for OpenBao - echo "Ensuring port-forward for OpenBao is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart openbao - - if ! curl -sS http://127.0.0.1:18200/v1/sys/health >/dev/null 2>&1; then - echo "ERROR: OpenBao not reachable at http://127.0.0.1:18200." >&2 - echo "Please start port-forwards: $SCRIPT_DIR/init_port_forwards.sh start openbao" >&2 - exit 1 - fi - init_openbao_kv_and_store_admin_key - echo "Initialization complete. Manifests in: $OPENBAO_MANIFEST_DIR" - ;; - update|reload) - generate_openbao_manifests - generate_kerberos_configmap - apply_k8s - echo "Re-applied manifests." - ;; - *) - echo "Usage: $0 {start|stop|status|restart|initialize|update|reload}" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4411745888/init_port_forwards.sh b/MagicMock/mock.Entry().get().strip()/4411745888/init_port_forwards.sh deleted file mode 100755 index 9ebea5c..0000000 --- a/MagicMock/mock.Entry().get().strip()/4411745888/init_port_forwards.sh +++ /dev/null @@ -1,537 +0,0 @@ -#!/usr/bin/env bash -set -u - -# init_port_forwards.sh -# Portable-ish (macOS, Ubuntu, Raspberry Pi OS, Alpine) bash init-style script -# Manages kubectl port-forward daemons defined in an XML file. - -PROG="init_port_forwards" -PROLE_HOME="${PROLE_HOME:-/Users/chrisfu/dev/prole}" -VERBOSE=0 -CONFIG_FILE="$PROLE_HOME/conf/port-mappings.properties" - -usage() { - cat < [component] - -Options: - -c, --config-file=FILE Path to local-ports.properties (XML) - -v, --verbose Verbose output - -Examples: - $PROG -c ./port-mappings.properties start - $PROG stop openbao - $PROG --verbose status -EOF -} - -TARGET_ID="" - -log() { printf '%s\n' "$*"; } -vlog() { [ "$VERBOSE" -eq 1 ] && printf '[verbose] %s\n' "$*" || true; } -err() { printf '[error] %s\n' "$*" >&2; } - -have() { command -v "$1" >/dev/null 2>&1; } - -# Choose a writable state dir for pid/log files: -# - Prefer XDG_RUNTIME_DIR if set and writable -# - Else /var/run if writable (rare without root) -# - Else ~/.local/state -# - Else /tmp -state_dir() { - if [ -n "${XDG_RUNTIME_DIR:-}" ] && [ -w "${XDG_RUNTIME_DIR:-}" ]; then - printf '%s/%s' "$XDG_RUNTIME_DIR" "$PROG" - return - fi - if [ -d "/var/run" ] && [ -w "/var/run" ]; then - printf '/var/run/%s' "$PROG" - return - fi - if [ -n "${HOME:-}" ]; then - mkdir -p "$HOME/.local/state" >/dev/null 2>&1 || true - if [ -d "$HOME/.local/state" ] && [ -w "$HOME/.local/state" ]; then - printf '%s/.local/state/%s' "$HOME" "$PROG" - return - fi - fi - printf '/tmp/%s' "$PROG" -} - -STATE_DIR="$(state_dir)" -PID_DIR="$STATE_DIR/pids" -LOG_DIR="$STATE_DIR/logs" - -ensure_dirs() { - mkdir -p "$PID_DIR" "$LOG_DIR" 2>/dev/null || true - if [ ! -d "$PID_DIR" ] || [ ! -d "$LOG_DIR" ]; then - err "Unable to create state directories under: $STATE_DIR" - exit 1 - fi -} - -# Use kubectl for actions; kubecolor is used only for prettier status output. -KUBECTL="kubectl" -detect_kubectl() { - if have kubectl; then - KUBECTL="kubectl" - else - err "kubectl not found in PATH" - exit 1 - fi -} - -# Basic environment validation -validate_env() { - detect_kubectl - ensure_dirs - - if ! "$KUBECTL" version --client >/dev/null 2>&1; then - err "kubectl seems broken or not executable" - exit 1 - fi - - # Can we reach the cluster? - if ! "$KUBECTL" cluster-info >/dev/null 2>&1; then - err "kubectl cannot reach a cluster (check KUBECONFIG/context)" - err "Try: kubectl config get-contexts && kubectl config use-context " - exit 1 - fi -} - -# ---- Preflight: Docker + k3d awareness ---- -# Return 0 when Docker CLI can talk to a running daemon. -docker_is_running() { - if ! have docker; then - return 1 - fi - docker info >/dev/null 2>&1 -} - -# Hard-fail with clean guidance when Docker is not up (used for start/restart). -ensure_docker_running() { - if docker_is_running; then - return 0 - fi - if ! have docker; then - err "Docker CLI not found. Please install Docker Desktop or docker CLI." - else - err "Docker is not running. Start Docker Desktop and wait until it is ready." - fi - err "Tip (macOS): open -a Docker" - err "Then re-run: $PROG start" - exit 3 -} - -# Detect k3d cluster name from env or current kubectl context. -# - If K3D_CLUSTER is set, use it. -# - Else, if current-context starts with 'k3d-', strip prefix to get name. -detect_k3d_context() { - if [ -n "${K3D_CLUSTER:-}" ]; then - printf '%s' "$K3D_CLUSTER" - return 0 - fi - local ctx - ctx="$($KUBECTL config current-context 2>/dev/null || echo)" - case "$ctx" in - k3d-*) printf '%s' "${ctx#k3d-}"; return 0 ;; - *) return 1 ;; - esac -} - -# Return 0 if the given k3d cluster exists and is running. -k3d_cluster_is_running() { - local name="$1" - have k3d || return 1 - # Use json output when available; fall back to grep otherwise - if k3d cluster list -o json >/dev/null 2>&1; then - k3d cluster list -o json 2>/dev/null | grep -q '"name"\s*:\s*"'"$name"'"' && \ - k3d cluster list -o json 2>/dev/null | sed -n 's/.*"name"\s*:\s*"\([^"]\+\)".*"serversRunning"\s*:\s*\([0-9]\+\).*/\1 \2/p' | awk -v n="$name" '$1==n {exit ($2>0)?0:1}' - return $? - else - k3d cluster list 2>/dev/null | grep -E "^$name\s" | grep -q running - return $? - fi -} - -# If current context indicates k3d, ensure the cluster is up; otherwise no-op. -ensure_k3d_ready_if_applicable() { - local k3d_name - if ! k3d_name="$(detect_k3d_context)"; then - return 0 - fi - if ! have k3d; then - err "k3d is not installed but kubectl context suggests k3d (context=$("$KUBECTL" config current-context))." - err "Install k3d: brew install k3d (macOS)" - err "Or switch context: kubectl config use-context " - exit 4 - fi - if ! k3d_cluster_is_running "$k3d_name"; then - err "k3d cluster '$k3d_name' is not running." - err "Start it: k3d cluster start $k3d_name" - err "Then re-run: $PROG start" - exit 4 - fi -} - -pid_file_for() { printf '%s/%s.pid' "$PID_DIR" "$1"; } -log_file_for() { printf '%s/%s.log' "$LOG_DIR" "$1"; } - -is_pid_running() { - # Return 0 if pid exists and running, else 1 - # kill -0 is portable. - local pid="$1" - [ -n "$pid" ] && kill -0 "$pid" >/dev/null 2>&1 -} - -read_pid() { - local pf="$1" - [ -f "$pf" ] || return 1 - # shellcheck disable=SC2162 - read pid <"$pf" || return 1 - printf '%s' "$pid" -} - -write_pid() { - local pf="$1" pid="$2" - printf '%s\n' "$pid" >"$pf" -} - -remove_pidfile() { - local pf="$1" - rm -f "$pf" >/dev/null 2>&1 || true -} - -# ---- XML parsing (simple attribute extraction) ---- -# We parse lines containing: -# Attributes must use double quotes in the XML (as in the sample). -get_attr() { - # $1 = line, $2 = attribute name - # outputs value or empty - printf '%s\n' "$1" | sed -n "s/.*$2=\"\([^\"]*\)\".*/\1/p" -} - -foreach_mapping() { - # Calls a provided function with mapping fields: - # callback id ns target address hostPort servicePort protocol description - local callback="$1" - [ -f "$CONFIG_FILE" ] || { err "Config file not found: $CONFIG_FILE"; exit 1; } - - # Support both single-line and multi-line self-closing mapping tags, e.g.: - # OR lines spanning multiple lines until "/>". - local in_mapping=0 in_comment=0 buffer="" line - while IFS= read -r line; do - # Handle XML comments: skip anything between - if [ $in_comment -eq 1 ]; then - case "$line" in - *"-->"*) in_comment=0; continue ;; - *) continue ;; - esac - fi - case "$line" in - *""*) - # single-line comment; skip line - continue - ;; - *) - in_comment=1 - continue - ;; - esac - ;; - esac - if [ $in_mapping -eq 0 ]; then - case "$line" in - *"' - buffer="$buffer $line" - fi - - if [ $in_mapping -eq 1 ] && printf '%s' "$line" | grep -q "/>"; then - # Normalize whitespace to make attribute extraction robust - local merged - merged=$(printf '%s\n' "$buffer" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g') - - local id ns target address hostPort servicePort protocol description - id="$(get_attr "$merged" "id")" - ns="$(get_attr "$merged" "namespace")" - target="$(get_attr "$merged" "target")" - address="$(get_attr "$merged" "address")" - hostPort="$(get_attr "$merged" "hostPort")" - servicePort="$(get_attr "$merged" "servicePort")" - protocol="$(get_attr "$merged" "protocol")" - description="$(get_attr "$merged" "description")" - - # Basic validation - if [ -z "$id" ] || [ -z "$ns" ] || [ -z "$target" ] || [ -z "$hostPort" ] || [ -z "$servicePort" ]; then - err "Invalid mapping (missing required attributes): $merged" - exit 1 - fi - - # Filter by TARGET_ID if set - if [ -n "$TARGET_ID" ] && [ "$id" != "$TARGET_ID" ]; then - in_mapping=0 - buffer="" - continue - fi - - if [ -z "$address" ]; then address="127.0.0.1"; fi - if [ -z "$protocol" ]; then protocol="TCP"; fi - - vlog "mapping: id=$id ns=$ns target=$target address=$address hostPort=$hostPort servicePort=$servicePort protocol=$protocol" - "$callback" "$id" "$ns" "$target" "$address" "$hostPort" "$servicePort" "$protocol" "$description" - - # Reset accumulator - in_mapping=0 - buffer="" - fi - done <"$CONFIG_FILE" -} - -build_port_forward_cmd() { - # echo a command string - local ns="$1" target="$2" address="$3" hostPort="$4" servicePort="$5" - # kubectl port-forward -n --address : - printf '%s port-forward -n %s --address %s %s %s:%s' \ - "$KUBECTL" "$ns" "$address" "$target" "$hostPort" "$servicePort" -} - -start_port_forward() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - local pidfile logfile cmd pid - - # Aggressively stop existing processes before starting to avoid port conflicts - stop_port_forward "$id" "$ns" "$target" "$address" "$hostPort" "$servicePort" "$protocol" "$description" - - pidfile="$(pid_file_for "$id")" - logfile="$(log_file_for "$id")" - cmd="$(build_port_forward_cmd "$ns" "$target" "$address" "$hostPort" "$servicePort")" - - log "Starting: $id $address:$hostPort -> $target:$servicePort ($ns) ${description:-}" - vlog "Command: $cmd" - - # Start in background, keep output in log. - # nohup is available on macOS/Linux; redirect stdin from /dev/null to detach. - nohup sh -c "$cmd" >>"$logfile" 2>&1 /dev/null 2>&1 || true - - # wait a moment, then SIGKILL if needed - local i - for i in 1 2 3 4 5; do - if ! is_pid_running "$pid"; then break; fi - sleep 0.2 - done - if is_pid_running "$pid"; then - err "$id did not stop gracefully; sending SIGKILL" - kill -9 "$pid" >/dev/null 2>&1 || true - fi - else - if [ -f "$pidfile" ]; then - vlog "$id stale pidfile (pid=$pid not running)" - fi - fi - remove_pidfile "$pidfile" - - # Aggressively remove any other matching kubectl port-forward processes - # Search for processes that match: kubectl port-forward -n ... : - local extra_pids - extra_pids=$(ps -ef | grep "port-forward" | grep "\-n" | grep "$ns" | grep "$target" | grep "$hostPort:$servicePort" | grep -v grep | awk '{print $2}') - - for epid in $extra_pids; do - if [ "$epid" != "$pid" ]; then - log "Cleaning up orphan process for $id (pid=$epid)" - kill -9 "$epid" >/dev/null 2>&1 || true - fi - done -} - -status_one() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - local pidfile pid - - pidfile="$(pid_file_for "$id")" - pid="" - if [ -f "$pidfile" ]; then - pid="$(read_pid "$pidfile" || true)" - fi - - if [ -n "${pid:-}" ] && is_pid_running "$pid"; then - printf 'RUNNING %-12s pid=%-7s %s:%s -> %s:%s ns=%s %s\n' \ - "$id" "$pid" "$address" "$hostPort" "$target" "$servicePort" "$ns" "${description:-}" - # ps details (portable flags vary; use a conservative format) - ps -p "$pid" -o pid=,ppid=,etime=,command= 2>/dev/null | sed 's/^/ /' || true - else - printf 'STOPPED %-12s (no live pid) %s:%s -> %s:%s ns=%s %s\n' \ - "$id" "$address" "$hostPort" "$target" "$servicePort" "$ns" "${description:-}" - fi -} - -do_start() { - validate_env - # Preflight: require Docker daemon and k3d (if applicable) - ensure_docker_running - ensure_k3d_ready_if_applicable - foreach_mapping start_port_forward -} - -do_stop() { - # stop doesn't require cluster access, but it does need state dirs - ensure_dirs - foreach_mapping stop_port_forward -} - -do_restart() { - validate_env - # Preflight: require Docker daemon and k3d (if applicable) - ensure_docker_running - ensure_k3d_ready_if_applicable - foreach_mapping stop_port_forward - foreach_mapping start_port_forward -} - -do_status() { - validate_env - # Non-fatal awareness messages - if docker_is_running; then - log "[OK] Docker daemon is running" - else - if have docker; then - log "[WARN] Docker is not running" - else - log "[WARN] Docker CLI not found" - fi - fi - - local _k3d_name - if _k3d_name="$(detect_k3d_context)"; then - if have k3d; then - if k3d_cluster_is_running "$_k3d_name"; then - log "[OK] k3d cluster '$_k3d_name' is running" - else - log "[WARN] k3d cluster '$_k3d_name' is not running" - fi - else - log "[WARN] k3d not installed but context suggests k3d (cluster='$_k3d_name')" - fi - fi - - log "== Context / Cluster ==" - "$KUBECTL" config current-context 2>/dev/null | sed 's/^/ context: /' || true - "$KUBECTL" cluster-info 2>/dev/null | sed 's/^/ /' || true - log "" - - log "== Port-forward processes ==" - foreach_mapping status_one - log "" - - log "== Quick k3d awareness checks (best-effort) ==" - # If user is on k3d, current-context often includes k3d-... but not guaranteed. - # Show nodes + a few namespaces/services related to mappings (best effort). - "$KUBECTL" get nodes -o wide 2>/dev/null | sed 's/^/ /' || true - log "" - "$KUBECTL" get ns 2>/dev/null | sed 's/^/ /' || true - log "" - - # For each mapping, try to show target existence - log "== Target existence (best-effort) ==" - _target_check() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - # kubectl get -n - # If target is like "svc/name", "deploy/name", etc. - if "$KUBECTL" get -n "$ns" "$target" >/dev/null 2>&1; then - printf 'OK %-12s %s (ns=%s)\n' "$id" "$target" "$ns" - else - printf 'MISSING %-12s %s (ns=%s)\n' "$id" "$target" "$ns" - fi - } - foreach_mapping _target_check -} - -# ---- arg parsing ---- -ACTION="" - -while [ $# -gt 0 ]; do - case "$1" in - start|stop|restart|status) - if [ -z "$ACTION" ]; then - ACTION="$1" - else - TARGET_ID="$1" - fi - shift - ;; - -v|--verbose) - VERBOSE=1 - shift - ;; - -c) - shift - [ $# -gt 0 ] || { err "-c requires a file path"; usage; exit 2; } - CONFIG_FILE="$1" - shift - ;; - --config-file=*) - CONFIG_FILE="${1#*=}" - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - if [ -z "$ACTION" ]; then - err "Unknown arg: $1" - usage - exit 2 - fi - TARGET_ID="$1" - shift - ;; - esac -done - -[ -n "$ACTION" ] || { usage; exit 2; } - -case "$ACTION" in - start) do_start ;; - stop) do_stop ;; - restart) do_restart ;; - status) do_status ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4411745888/init_prole-db.sh b/MagicMock/mock.Entry().get().strip()/4411745888/init_prole-db.sh deleted file mode 100755 index b87de05..0000000 --- a/MagicMock/mock.Entry().get().strip()/4411745888/init_prole-db.sh +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_prole-db.sh -# Purpose: -# - Manage prole-db CloudNative-PG cluster operations (deploy, start, stop, etc.) - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -ACTION=${1:-} -VERSION=${2:-latest} - -# Load env -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - source "$HOME/.prole/env.sh" -fi - -CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db} -NAMESPACE=${NAMESPACE:-prole} - -CNPG_MANIFEST="$SCRIPT_DIR/../k8s/prole/prole-db.yaml" - -ensure_tools() { - for t in kubectl jq; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -get_latest_image() { - local version_file="$SCRIPT_DIR/../conf/postgresql/.version" - if [[ -f "$version_file" ]]; then - echo "prole-db:$(cat "$version_file" | tr -d '[:space:]')" - else - echo "prole-db:17.7-033" - fi -} - -start() { - ensure_tools - echo "Checking dependencies..." - - # 1. k3d is running - if ! command -v k3d >/dev/null || ! k3d cluster list >/dev/null 2>&1; then - echo "ERROR: k3d is not running or not installed." >&2 - exit 1 - fi - - # 2. cnpg operator is loaded - if ! kubectl get deployment -n cnpg-system cnpg-controller-manager >/dev/null 2>&1; then - echo "ERROR: CloudNative-PG operator is not loaded." >&2 - exit 1 - fi - - # 3. openbao is configured - if ! kubectl get statefulset openbao -n "$NAMESPACE" >/dev/null 2>&1 && ! kubectl get deployment openbao -n "$NAMESPACE" >/dev/null 2>&1; then - echo "ERROR: OpenBao is not deployed." >&2 - exit 1 - fi - - # Compare latest image with deployed - local latest_image - latest_image=$(get_latest_image) - - local current_image - current_image=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.imageName}' 2>/dev/null || echo "") - - if [[ "$current_image" != "$latest_image" ]]; then - echo "Updating cluster image from '$current_image' to '$latest_image'..." - kubectl patch cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --type merge -p "{\"spec\": {\"imageName\": \"$latest_image\"}}" - # Force a rollout to ensure the new image is pulled even if it was just a tag update (though we use unique tags) - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true - else - echo "Cluster is already using the latest image: $latest_image" - fi - - echo "Starting prole-db cluster (ensuring manifest is applied)..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" -} - -stop() { - ensure_tools - echo "Stopping prole-db cluster $CNPG_CLUSTER_NAME..." - - # Identify instances - local instances - instances=$(kubectl get pods -n "$NAMESPACE" -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" -o jsonpath='{.items[*].metadata.name}') - - if [[ -z "$instances" ]]; then - echo "No instances found for cluster $CNPG_CLUSTER_NAME." - return - fi - - # Determine replicas and primary - local primary - primary=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.status.currentPrimary}') - - echo "Primary: $primary" - - # Shutdown replicas first - for pod in $instances; do - if [[ "$pod" != "$primary" ]]; then - echo "Shutting down replica $pod..." - kubectl exec -n "$NAMESPACE" "$pod" -c postgres -- psql -U postgres -c "CHECKPOINT;" || true - fi - done - - # Shutdown primary last - if [[ -n "$primary" ]]; then - echo "Shutting down primary $primary..." - kubectl exec -n "$NAMESPACE" "$primary" -c postgres -- psql -U postgres -c "CHECKPOINT;" || true - fi - - echo "Deleting cluster resource..." - kubectl delete -f "$CNPG_MANIFEST" --ignore-not-found -} - -restart() { - ensure_tools - echo "Restarting prole-db cluster..." - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -} - -deploy() { - ensure_tools - local image - if [[ "$VERSION" == "latest" ]]; then - image=$(get_latest_image) - else - image="prole-db:$VERSION" - fi - - echo "Deploying $image to cluster $CNPG_CLUSTER_NAME..." - - # If cluster doesn't exist, use init_cloudnative_pg.sh create first - if ! kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then - echo "Cluster not found. Running init_cloudnative_pg.sh create..." - bash "$SCRIPT_DIR/init_cloudnative_pg.sh" create - fi - - # Ensure image is updated if manifest has an older version - # First, apply the manifest to ensure the cluster exists/is updated - echo "Applying manifest $CNPG_MANIFEST..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" - - # Then force the specific image version via patch if different - local current_image - current_image=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.imageName}' 2>/dev/null || echo "") - if [[ "$current_image" != "$image" ]]; then - echo "Patching cluster to use image '$image' (was '$current_image')..." - kubectl patch cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --type merge -p "{\"spec\": {\"imageName\": \"$image\"}}" - # Force a rollout - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true - fi -} - -rollback() { - ensure_tools - echo "Rollback: Updating to previous image (manually specified version or fallback)..." - if [[ "$VERSION" == "latest" ]]; then - echo "Please specify a version to rollback to. Usage: $0 rollback " - exit 1 - fi - deploy -} - -backup() { - echo "Backup - tbd, when we configure s3 or other block store" -} - -reset() { - ensure_tools - echo "Resetting prole-db cluster..." - echo "Removing cnpg instances and pods..." - kubectl delete cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --ignore-not-found - - # Wait for deletion - kubectl wait --for=delete cluster/"$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --timeout=60s || true - - echo "Reinstantiating..." - deploy -} - -case "$ACTION" in - start) - start - ;; - stop) - stop - ;; - restart) - restart - ;; - deploy) - deploy - ;; - rollback) - rollback - ;; - backup) - backup - ;; - reset) - reset - ;; - *) - echo "Usage: $0 {start|stop|restart|deploy|rollback|backup|reset} [version]" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4413171632/env.sh b/MagicMock/mock.Entry().get().strip()/4413171632/env.sh deleted file mode 100755 index 5b7b968..0000000 --- a/MagicMock/mock.Entry().get().strip()/4413171632/env.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -# Prole environment configuration -# This file is generated by the installer. Source it in new shells, or execute as a wrapper: -# "$PROLE_HOME/env.sh" [args…] -# shellcheck shell=bash -export PROLE_HOME="" -export PROLE_CONF="" -export PROLE_DATA="" -export PROLE_LOGS="" -export PROLE_SERVICE="" - -# Ensure PATH works for GUI-launched shells (Docker, Ollama, etc.) -_prole_add_path() { case ":${PATH}:" in *":$1:"*) ;; *) PATH="$1:${PATH:-}" ;; esac; } -_prole_add_path "$PROLE_HOME/bin" -_prole_add_path "/opt/homebrew/bin" -_prole_add_path "/usr/local/bin" -_prole_add_path "/usr/bin" -_prole_add_path "/bin" -_prole_add_path "/usr/sbin" -_prole_add_path "/sbin" -export PATH - -# Add custom paths below if needed (examples): -# _prole_add_path "/Applications/Ollama.app/Contents/MacOS" - -# If executed with arguments, run them under this environment -if [ "$#" -gt 0 ]; then - exec "$@" -fi diff --git a/MagicMock/mock.Entry().get().strip()/4413171632/hosts.txt b/MagicMock/mock.Entry().get().strip()/4413171632/hosts.txt deleted file mode 100644 index 4e73115..0000000 --- a/MagicMock/mock.Entry().get().strip()/4413171632/hosts.txt +++ /dev/null @@ -1,15 +0,0 @@ -myrddin.prole.org 10.0.0.3 -raspberry.prole.org 10.0.0.4 -pi.prole.org 10.0.0.5 -synology.prole.org 10.0.0.203 -morgoth.prole.org 10.0.0.204 -zinfandel.prole.org 10.0.0.205 -aventage.prole.org 10.0.0.206 -retropie.prole.org 10.0.0.207 -fairyland.prole.org 10.0.0.208 -k8s.prole.org zinfandel.prole.org -mc.prole.org 73.15.20.166 -morana.prole.org 10.0.0.66 -ollama.prole.org 73.15.20.166 -svc.prole.org 73.15.20.166 -www.prole.org ghs.googlehosted.com \ No newline at end of file diff --git a/MagicMock/mock.Entry().get().strip()/4413171632/init_cloudnative_pg.sh b/MagicMock/mock.Entry().get().strip()/4413171632/init_cloudnative_pg.sh deleted file mode 100755 index 6708415..0000000 --- a/MagicMock/mock.Entry().get().strip()/4413171632/init_cloudnative_pg.sh +++ /dev/null @@ -1,326 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_cloudnative_pg.sh -# Purpose: -# - Distribute administrator ed25519 key pair to CloudNative‑PG as a Kubernetes Secret for cert auth -# - Configure Kerberos (GSSAPI) using external Kerberos KDC -# - Patch CNPG cluster to enable TLS and GSSAPI where possible -# -# Usage: -# ./init_cloudnative_pg.sh start|stop|status|restart -# ./init_cloudnative_pg.sh initialize # create/update k8s secrets/configs and patch CNPG -# ./init_cloudnative_pg.sh update|reload # re-apply/patch -# -# Requirements: -# - init_openbao.sh has been run (OpenBao running in k8s) -# - $PROLE_HOME/env.sh or $HOME/.prole/env.sh defining PROLE_SERVICE - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Load env without leaking our positional args to the env script (some env.sh may `exec "$@"`). -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -else - echo "ERROR: Missing env. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2 - exit 1 -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -if [[ -z "${PROLE_SERVICE:-}" ]]; then - echo "ERROR: PROLE_SERVICE is not defined in env." >&2 - exit 1 -fi - -ACTION=${1:-} -CNPG_CLUSTER_NAME=${2:-${CNPG_CLUSTER_NAME:-prole-db}} -NAMESPACE=${NAMESPACE:-prole} -OPENBAO_NAME=${OPENBAO_NAME:-openbao} -REALM=${REALM:-PROLE.ORG} -DOMAIN=${DOMAIN:-prole.org} -KRB5_KDC=${KRB5_KDC:-} -KRB5_ADMIN=${KRB5_ADMIN:-} - -CNPG_MANIFEST="$SCRIPT_DIR/../k8s/prole/prole-db.yaml" - -SECRETS_DIR="$PROLE_SERVICE/secrets" -ADMIN_PRIV="$SECRETS_DIR/admin_ed25519.key" -ADMIN_PUB="$SECRETS_DIR/admin_ed25519.pub" -OPENBAO_TOKEN_FILE="$SECRETS_DIR/openbao-root-token" - -ensure_tools() { - for t in kubectl curl openssl base64 jq; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -# Resolve OpenBao URL: prefer explicit env, then localhost port-forward, then cluster DNS -bao_service_url() { - if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then - echo "$PROLE_OPENBAO_URL" - return 0 - fi - # Prefer standard local port-forward managed by etc/init_port_forwards.sh - if curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then - echo "http://127.0.0.1:18200" - return 0 - fi - echo "http://$OPENBAO_NAME.$NAMESPACE.svc.cluster.local:8200" -} - -fetch_admin_keys_and_db_pass_from_bao_or_local() { - local token url - if [[ -f "$OPENBAO_TOKEN_FILE" ]]; then - token=$(cat "$OPENBAO_TOKEN_FILE") - else - token="" - fi - url=$(bao_service_url) - if [[ -n "$token" ]]; then - echo "Attempting to read admin key pair from OpenBao kv/prole/admin ..." - if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -e '.data.data' >/dev/null 2>&1; then - local priv_b64 pub_b64 - priv_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -r '.data.data.admin_private_key_b64') - pub_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -r '.data.data.admin_public_key_b64') - printf "%s" "$priv_b64" | base64 -d >"$ADMIN_PRIV" - printf "%s" "$pub_b64" | base64 -d >"$ADMIN_PUB" - chmod 0600 "$ADMIN_PRIV" - fi - - echo "Attempting to read database password from OpenBao kv/prole/db ..." - if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/db" | jq -e '.data.data' >/dev/null 2>&1; then - local db_pass - db_pass=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/db" | jq -r '.data.data.password') - if [[ -n "$db_pass" ]]; then - echo "Updating database user secret 'prole-db-user' from OpenBao ..." - kubectl create secret generic prole-db-user -n "$NAMESPACE" \ - --from-literal=username=prole \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -f - - - echo "Updating database superuser secret 'prole-db-superuser' from OpenBao ..." - kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \ - --from-literal=username=postgres \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -f - - fi - fi - fi - - if [[ -f "$ADMIN_PRIV" && -f "$ADMIN_PUB" ]]; then - echo "Using local admin key pair at $SECRETS_DIR" - return 0 - fi - echo "ERROR: Could not obtain admin key pair from OpenBao and no local files found." >&2 - exit 1 -} - -apply_cnpg_admin_secret() { - echo "Creating/updating Secret cnpg-admin-key ..." - kubectl create secret generic cnpg-admin-key -n "$NAMESPACE" \ - --from-file=admin.key="$ADMIN_PRIV" \ - --from-file=admin.pub="$ADMIN_PUB" \ - --dry-run=client -o yaml | kubectl apply -f - -} - -ensure_krb5_conf_configmap() { - echo "Creating/updating ConfigMap prole-krb5-conf ..." - - local kdc_val admin_val - if [[ -n "$KRB5_KDC" ]]; then - kdc_val="$KRB5_KDC" - admin_val=${KRB5_ADMIN:-$(echo "$KRB5_KDC" | cut -d, -f1)} - else - kdc_val="kdc.$DOMAIN" - admin_val="kdc.$DOMAIN" - fi - - local TMP - TMP=$(mktemp) - cat >"$TMP" </dev/null 2>&1; then - echo "CA secret $ca_secret_name already exists; skipping generation." - return 0 - fi - echo "Generating self-signed CA (RSA 4096) for CNPG ..." - local TMPD - TMPD=$(mktemp -d) - openssl genrsa -out "$TMPD/ca.key" 4096 - openssl req -x509 -new -key "$TMPD/ca.key" -out "$TMPD/ca.crt" -days 3650 -subj "/CN=Prole CNPG CA" - kubectl -n "$NAMESPACE" create secret generic "$ca_secret_name" \ - --from-file=ca.crt="$TMPD/ca.crt" \ - --from-file=ca.key="$TMPD/ca.key" \ - --dry-run=client -o yaml | kubectl apply -f - - rm -rf "$TMPD" -} - -patch_cnpg_cluster_for_auth() { - echo "Patching CNPG Cluster $CNPG_CLUSTER_NAME to enable GSSAPI and fix config (best-effort) ..." - # Best-effort patch; schema may vary with CNPG version. - # We remove krb_srvname as it is unrecognized in some PG 17 builds. - # We revert certificates to default to avoid operator TLS issues. - kubectl -n "$NAMESPACE" patch cluster "$CNPG_CLUSTER_NAME" --type merge -p "{ - \"spec\": { - \"postgresql\": { - \"parameters\": {\"krb_srvname\": null}, - \"pg_hba\": [ - \"local all postgres trust\", - \"host all postgres all scram-sha-256\", - \"host all all all gss include_realm=1 krb_realm=$REALM\", - \"host all all all scram-sha-256\" - ] - }, - \"certificates\": { - \"serverCASecret\": null, - \"clientCASecret\": null - } - } - }" >/dev/null || echo "Note: patch may need adjustment for your CNPG version." -} - -initialize() { - ensure_tools - - # Ensure port-forward is running for OpenBao (dependency) - echo "Ensuring port-forward for OpenBao is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart openbao - - fetch_admin_keys_and_db_pass_from_bao_or_local - apply_cnpg_admin_secret - ensure_krb5_conf_configmap - # generate_tls_if_missing - patch_cnpg_cluster_for_auth - - # Ensure port-forward is running for Postgres (local access) - echo "Ensuring port-forward for Postgres is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart postgres - - echo "Initialization complete for CNPG + Kerberos + cert artifacts." -} - -update_reload() { - initialize -} - -case "$ACTION" in - recreate) - ensure_tools - "$0" delete "$CNPG_CLUSTER_NAME" - "$0" create "$CNPG_CLUSTER_NAME" - ;; - create) - ensure_tools - echo "Installing CloudNative-PG operator ..." - kubectl apply --server-side -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.27/releases/cnpg-1.27.0.yaml - - echo "Creating CloudNative-PG cluster and resources for '$CNPG_CLUSTER_NAME' ..." - - kubectl apply -k "$SCRIPT_DIR/../k8s/prole" - - # Ensure prole-index-html exists for prole deployment readiness probe - if ! kubectl get configmap prole-index-html -n prole >/dev/null 2>&1; then - echo "Creating prole-index-html configmap..." - printf "

Prole

" > /tmp/index.html - kubectl create configmap prole-index-html --from-file=/tmp/index.html -n prole - rm /tmp/index.html - fi - - # Ensure prole-nginx-tls exists (self-signed for dev) - if ! kubectl get secret prole-nginx-tls -n prole >/dev/null 2>&1; then - echo "Generating self-signed prole-nginx-tls for development..." - openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout /tmp/nginx-tls.key -out /tmp/nginx-tls.crt \ - -subj "/CN=prole.org" >/dev/null 2>&1 - kubectl create secret tls prole-nginx-tls --key /tmp/nginx-tls.key --cert /tmp/nginx-tls.crt -n prole - rm /tmp/nginx-tls.key /tmp/nginx-tls.crt - fi - - echo "Initializing and patching cluster ..." - initialize - ;; - delete) - ensure_tools - echo "Deleting all resources for '$CNPG_CLUSTER_NAME' ..." - kubectl delete -k "$SCRIPT_DIR/../k8s/prole" --ignore-not-found - ;; - start) - ensure_tools - if [[ ! -f "$CNPG_MANIFEST" ]]; then - echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2 - exit 1 - fi - echo "Starting CloudNative-PG cluster from $CNPG_MANIFEST in namespace $NAMESPACE..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" - ;; - stop) - ensure_tools - if [[ ! -f "$CNPG_MANIFEST" ]]; then - echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2 - exit 1 - fi - echo "Stopping CloudNative-PG cluster using $CNPG_MANIFEST ..." - kubectl delete -f "$CNPG_MANIFEST" --ignore-not-found - ;; - status) - ensure_tools - echo "--- CloudNative-PG Cluster Status ($CNPG_CLUSTER_NAME) ---" - if kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then - kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" - echo "" - echo "CNPG Plugin Status:" - if kubectl cnpg version >/dev/null 2>&1; then - kubectl cnpg status "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" - else - echo "Note: 'kubectl cnpg' plugin not found; skipping detailed status." - fi - else - echo "Cluster '$CNPG_CLUSTER_NAME' not found in namespace '$NAMESPACE'." - fi - ;; - restart) - ensure_tools - "$0" stop - "$0" start - ;; - initialize) - initialize - ;; - update|reload) - update_reload - ;; - *) - echo "Usage: $0 {create|delete|recreate|start|stop|status|restart|initialize|update|reload} [dbname]" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4413171632/init_k8s.sh b/MagicMock/mock.Entry().get().strip()/4413171632/init_k8s.sh deleted file mode 100755 index 35f14aa..0000000 --- a/MagicMock/mock.Entry().get().strip()/4413171632/init_k8s.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# etc/init_k8s.sh -# Purpose: Manage k3d/k8s environment - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Preserve positional args while sourcing env -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -# Default values -VERBOSE=false -ENVIRONMENT="prod" -HOST="" -CLUSTER_NAME_DEFAULT="prole-dev-cluster" -CLUSTER_PORT_DEFAULT="6443" - -usage() { - cat </dev/null || { echo "ERROR: Missing required tool: $t" >&2; exit 1; } - done -} - -ensure_docker() { - if ! docker info >/dev/null 2>&1; then - echo "ERROR: Docker is not running." >&2 - exit 1 - fi -} - -initialize() { - ensure_tools - ensure_docker - - if [[ "$ENVIRONMENT" == "dev" ]]; then - if [[ -z "$CLUSTER_NAME" ]]; then - if [[ -t 0 ]]; then - read -p "Enter k3d cluster name [$CLUSTER_NAME_DEFAULT]: " CLUSTER_NAME - CLUSTER_NAME=${CLUSTER_NAME:-$CLUSTER_NAME_DEFAULT} - else - CLUSTER_NAME=$CLUSTER_NAME_DEFAULT - fi - fi - - if k3d cluster list "$CLUSTER_NAME" >/dev/null 2>&1; then - log "Cluster '$CLUSTER_NAME' already exists." - else - log "Creating k3d cluster '$CLUSTER_NAME'..." - k3d cluster create "$CLUSTER_NAME" -p "0.0.0.0:$CLUSTER_PORT_DEFAULT:6443@server:0" - fi - else - if [[ -n "$HOST" ]]; then - log "Environment is $ENVIRONMENT. Host is $HOST." - log "TODO: Fetch kubeconfig from $HOST (placeholder)." - else - log "Environment is $ENVIRONMENT. Use -h|--host to specify the remote cluster host." - fi - fi -} - -status() { - ensure_tools - log "Checking Docker status..." - if docker info >/dev/null 2>&1; then - echo "Docker is running." - else - echo "Docker is NOT running." - fi - - log "Listing k3d clusters..." - k3d cluster list -} - -get_cluster_name() { - # If a name was provided or exists in env, use it. - # Otherwise, if there is only one cluster, use it. - # Finally, use default. - if [[ -n "${CLUSTER_NAME:-}" ]]; then - echo "$CLUSTER_NAME" - return - fi - - local clusters - clusters=$(k3d cluster list --no-headers | awk '{print $1}') - local count - count=$(echo "$clusters" | grep -c . || true) - - if [[ "$count" -eq 1 ]]; then - echo "$clusters" - else - echo "$CLUSTER_NAME_DEFAULT" - fi -} - -case "$ACTION" in - initialize) - initialize - ;; - status) - status - ;; - start) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Starting k3d cluster '$CLUSTER_NAME'..." - k3d cluster start "$CLUSTER_NAME" - ;; - stop) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Stopping k3d cluster '$CLUSTER_NAME'..." - k3d cluster stop "$CLUSTER_NAME" - ;; - restart) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Restarting k3d cluster '$CLUSTER_NAME'..." - k3d cluster stop "$CLUSTER_NAME" - k3d cluster start "$CLUSTER_NAME" - ;; - *) - usage - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4413171632/init_openbao.sh b/MagicMock/mock.Entry().get().strip()/4413171632/init_openbao.sh deleted file mode 100755 index 3e59151..0000000 --- a/MagicMock/mock.Entry().get().strip()/4413171632/init_openbao.sh +++ /dev/null @@ -1,490 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_openbao.sh -# Purpose: -# - Deploy OpenBao to Kubernetes (dev mode) and store admin ed25519 key pair -# Generate and apply a Kerberos krb5.conf ConfigMap for an external realm -# - Local Docker helpers for OpenBao (optional) - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Preserve positional args while sourcing env -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -if [[ -z "${PROLE_SERVICE:-}" ]]; then - echo "ERROR: PROLE_SERVICE is not defined in env. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2 - exit 1 -fi - -ACTION=${1:-} - -# Defaults -NAMESPACE=${NAMESPACE:-prole} -OPENBAO_NAME=${OPENBAO_NAME:-openbao} -OPENBAO_MANIFEST_DIR="$SCRIPT_DIR/../k8s/openbao" - -# Kerberos realm defaults -KRB5_REALM=${KRB5_REALM:-PROLE.ORG} -DOMAIN=${DOMAIN:-$(echo "$KRB5_REALM" | tr 'A-Z' 'a-z')} -KRB5_KDC=${KRB5_KDC:-kdc.$DOMAIN} -KRB5_ADMIN=${KRB5_ADMIN:-} - -# Secrets and token locations -SECRETS_DIR="$PROLE_SERVICE/secrets" -mkdir -p "$SECRETS_DIR" -admin_key_priv="$SECRETS_DIR/admin_ed25519.key" -admin_key_pub="$SECRETS_DIR/admin_ed25519.pub" -root_token_file="$SECRETS_DIR/openbao-root-token" - -ensure_tools() { - for t in kubectl curl openssl base64; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -ensure_docker() { - command -v docker >/dev/null || { echo "Missing required tool: docker" >&2; exit 1; } -} - -ensure_admin_keypair() { - # Ensure admin ed25519 keypair exists and a root token is available - mkdir -p "$SECRETS_DIR" - if [[ ! -f "$admin_key_priv" || ! -f "$admin_key_pub" ]]; then - echo "Generating admin ed25519 keypair in $SECRETS_DIR ..." - openssl genpkey -algorithm ED25519 -out "$admin_key_priv" - openssl pkey -in "$admin_key_priv" -pubout -out "$admin_key_pub" - chmod 0600 "$admin_key_priv" - fi - # Ensure a root token exists for OpenBao dev server interactions - if [[ ! -f "$root_token_file" || ! -s "$root_token_file" ]]; then - openssl rand -hex 24 >"$root_token_file" - chmod 0600 "$root_token_file" - fi -} - -generate_openbao_manifests() { - mkdir -p "$OPENBAO_MANIFEST_DIR" - # Only create a simple deployment if none exists; otherwise respect current file - local deploy="$OPENBAO_MANIFEST_DIR/deployment.yaml" - if [[ ! -f "$deploy" ]]; then - cat >"$deploy" <"$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" </dev/null 2>&1; then - kubectl rollout status statefulset/$OPENBAO_NAME -n "$NAMESPACE" --timeout=120s - else - kubectl rollout status deploy/$OPENBAO_NAME -n "$NAMESPACE" --timeout=120s - fi -} - -init_openbao_kv_and_store_admin_key() { - local token - token=$(cat "$root_token_file") - # Enable kv (if not already) and write keys - local svc="http://127.0.0.1:18200" - echo "Configuring KV at $svc ..." - - # Check if kv/ is already mounted - if curl -sS -H "X-Vault-Token: $token" "$svc/v1/sys/mounts" | grep -q '"kv/":'; then - echo "KV path 'kv/' is already enabled." - else - curl -sS -H "X-Vault-Token: $token" -X POST "$svc/v1/sys/mounts/kv" \ - -d '{"type":"kv","options":{"version":"2"}}' >/dev/null 2>&1 || true - fi - - local priv pub - priv=$(base64 <"$admin_key_priv" | tr -d '\n') - pub=$(base64 <"$admin_key_pub" | tr -d '\n') - - # Check if admin key already exists and matches - local existing_data - existing_data=$(curl -sS -H "X-Vault-Token: $token" "$svc/v1/kv/data/prole/admin" 2>/dev/null || true) - if [[ -n "$existing_data" ]]; then - local ex_priv ex_pub - ex_priv=$(echo "$existing_data" | grep -o '"admin_private_key_b64":"[^"]*' | cut -d'"' -f4 || true) - ex_pub=$(echo "$existing_data" | grep -o '"admin_public_key_b64":"[^"]*' | cut -d'"' -f4 || true) - - if [[ "$ex_priv" == "$priv" && "$ex_pub" == "$pub" ]]; then - echo "Admin key pair in OpenBao kv/prole/admin is already up to date." - else - echo "Writing admin key pair to kv/prole/admin ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/admin" \ - -d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null - echo "Stored admin key pair in OpenBao kv/prole/admin." - fi - else - echo "Writing admin key pair to kv/prole/admin ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/admin" \ - -d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null - echo "Stored admin key pair in OpenBao kv/prole/admin." - fi - - # Store User SSH keys if they exist - local user_key_priv="$HOME/.ssh/id_prole_ed25519" - local user_key_pub="$HOME/.ssh/id_prole_ed25519.pub" - if [[ -f "$user_key_priv" && -f "$user_key_pub" ]]; then - local u_priv u_pub - u_priv=$(base64 <"$user_key_priv" | tr -d '\n') - u_pub=$(base64 <"$user_key_pub" | tr -d '\n') - local username=${PROLE_DB_USER:-"prole"} - - echo "Writing user SSH keys for '$username' to kv/prole/user ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/user" \ - -d "{\"data\":{\"username\":\"$username\",\"private_key_b64\":\"$u_priv\",\"public_key_b64\":\"$u_pub\"}}" >/dev/null - echo "Stored user SSH keys in OpenBao kv/prole/user." - fi - - if [[ -n "$db_pass" ]]; then - local username=${PROLE_DB_USER:-"prole"} - echo "Writing database user password for '$username' to kv/prole/db ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/db" \ - -d "{\"data\":{\"username\":\"$username\",\"password\":\"$db_pass\"}}" >/dev/null - echo "Stored database password in OpenBao kv/prole/db." - fi -} - -# Removed: prompt_admin_password_and_apply_secret (no AD admin secret required) - -docker_start() { - echo "Starting local Docker container for OpenBao ..." - ensure_docker - docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true - # OpenBao dev - local token - token=$(cat "$root_token_file" 2>/dev/null || true) - if [[ -z "$token" ]]; then - token=$(openssl rand -hex 24) - printf "%s" "$token" >"$root_token_file" - chmod 0600 "$root_token_file" - fi - docker run -d --name "$OPENBAO_NAME" -p 18200:8200 \ - "$OPENBAO_IMAGE" server -dev -dev-listen-address=0.0.0.0:8200 -dev-root-token-id="$token" - echo "Docker container started: $OPENBAO_NAME" -} - -docker_stop() { - docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true - echo "Stopped OpenBao container if it was running." -} - -docker_restart() { - docker_stop - docker_start -} - -# status command implementation wrapped in a function to avoid top-level 'local' -cmd_status() { - echo "--- init_primary_domain status ---" - echo "Namespace: $NAMESPACE" - echo "OpenBao name: $OPENBAO_NAME" - echo "Manifest dir: $OPENBAO_MANIFEST_DIR" - - # Files/manifests present - local ok=0 - if [[ -f "$OPENBAO_MANIFEST_DIR/deployment.yaml" ]]; then - echo "[OK] OpenBao deployment manifest exists" - else - echo "[MISSING] $OPENBAO_MANIFEST_DIR/deployment.yaml" - ok=1 - fi - if [[ -f "$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" ]]; then - echo "[OK] Kerberos ConfigMap manifest exists" - else - echo "[MISSING] $OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" - ok=1 - fi - - # K8s resources - if kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" >/dev/null 2>&1; then - local ready desired - ready=$(kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") - desired=$(kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0") - ready=${ready:-0} - desired=${desired:-0} - if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then - echo "[OK] OpenBao StatefulSet running ($ready/$desired ready)" - else - echo "[WARN] OpenBao StatefulSet not fully ready ($ready/$desired)" - ok=1 - fi - elif kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" >/dev/null 2>&1; then - # Get readiness - local ready desired - ready=$(kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") - desired=$(kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0") - ready=${ready:-0} - desired=${desired:-0} - if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then - echo "[OK] OpenBao Deployment running ($ready/$desired ready)" - else - echo "[WARN] OpenBao Deployment not fully ready ($ready/$desired)" - ok=1 - fi - else - echo "[MISSING] OpenBao Deployment or StatefulSet '$OPENBAO_NAME' in namespace '$NAMESPACE'" - ok=1 - fi - - if kubectl -n "$NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then - echo "[OK] Kerberos ConfigMap 'prole-krb5-conf' present" - else - echo "[MISSING] Kerberos ConfigMap 'prole-krb5-conf' in namespace '$NAMESPACE'" - ok=1 - fi - - # Docker (optional local mode) - if command -v docker >/dev/null 2>&1; then - if docker ps --format '{{.Names}}' | grep -Fxq "$OPENBAO_NAME"; then - echo "[OK] Docker container '$OPENBAO_NAME' is running" - else - # Not necessarily an error; we primarily use k8s - echo "[INFO] Docker container '$OPENBAO_NAME' not running (k8s mode may be in use)" - fi - fi - - # Secrets and tokens - if [[ -f "$admin_key_priv" && -f "$admin_key_pub" ]]; then - echo "[OK] Admin ed25519 keypair present in $SECRETS_DIR" - else - echo "[MISSING] Admin keypair files in $SECRETS_DIR" - ok=1 - fi - if [[ -s "$root_token_file" ]]; then - echo "[OK] OpenBao root token file present" - else - echo "[MISSING] OpenBao root token file ($root_token_file)" - ok=1 - fi - - # OpenBao reachability and token validity (best-effort) - # Prefer explicit override, then localhost port-forward, then cluster DNS - local svc_url - if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then - svc_url="$PROLE_OPENBAO_URL" - elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then - svc_url="http://127.0.0.1:18200" - else - svc_url="http://$OPENBAO_NAME.$NAMESPACE.svc.cluster.local:8200" - fi - if curl -sS "$svc_url/v1/sys/health" >/dev/null 2>&1; then - echo "[OK] OpenBao service reachable" - if [[ -s "$root_token_file" ]]; then - local code - code=$(curl -s -o /dev/null -w "%{http_code}" -H "X-Vault-Token: $(cat "$root_token_file")" "$svc_url/v1/sys/mounts" || echo "000") - if [[ "$code" == "200" ]]; then - echo "[OK] OpenBao root token authenticates" - else - echo "[WARN] OpenBao root token did not authenticate (HTTP $code)" - ok=1 - fi - fi - else - echo "[WARN] OpenBao service not reachable at $svc_url" - ok=1 - fi - - # Service certificate existence (CNPG TLS) - local CNPG_CLUSTER_NAME - CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db} - if kubectl -n "$NAMESPACE" get secret "${CNPG_CLUSTER_NAME}-tls" >/dev/null 2>&1; then - echo "[OK] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' exists" - else - echo "[INFO] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' not found" - fi - - echo "-------------------------------------" - if [[ $ok -eq 0 ]]; then - echo "Status: OK" - return 0 - else - echo "Status: Issues detected" - return 1 - fi -} - -case "$ACTION" in - start) - ensure_tools - ensure_docker - ensure_admin_keypair - docker_start - ;; - status) - ensure_tools - cmd_status - ;; - stop) - ensure_docker - docker_stop - ;; - restart) - ensure_tools - ensure_docker - ensure_admin_keypair - docker_restart - ;; - initialize) - ensure_tools - # Read password from stdin if provided - db_pass="" - if [[ ! -t 0 ]]; then - read -r db_pass - fi - - ensure_admin_keypair - generate_openbao_manifests - generate_kerberos_configmap - apply_k8s - - if [[ -n "$db_pass" ]]; then - local username=${PROLE_DB_USER:-"prole"} - echo "Creating database user secret 'prole-db-user' for '$username' ..." - kubectl create secret generic prole-db-user -n "$NAMESPACE" \ - --from-literal=username="$username" \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f - - - echo "Creating database superuser secret 'prole-db-superuser' ..." - kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \ - --from-literal=username=postgres \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f - - fi - - wait_for_openbao - - # Ensure port-forward is running for OpenBao - echo "Ensuring port-forward for OpenBao is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart openbao - - if ! curl -sS http://127.0.0.1:18200/v1/sys/health >/dev/null 2>&1; then - echo "ERROR: OpenBao not reachable at http://127.0.0.1:18200." >&2 - echo "Please start port-forwards: $SCRIPT_DIR/init_port_forwards.sh start openbao" >&2 - exit 1 - fi - init_openbao_kv_and_store_admin_key - echo "Initialization complete. Manifests in: $OPENBAO_MANIFEST_DIR" - ;; - update|reload) - generate_openbao_manifests - generate_kerberos_configmap - apply_k8s - echo "Re-applied manifests." - ;; - *) - echo "Usage: $0 {start|stop|status|restart|initialize|update|reload}" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4413171632/init_port_forwards.sh b/MagicMock/mock.Entry().get().strip()/4413171632/init_port_forwards.sh deleted file mode 100755 index 9ebea5c..0000000 --- a/MagicMock/mock.Entry().get().strip()/4413171632/init_port_forwards.sh +++ /dev/null @@ -1,537 +0,0 @@ -#!/usr/bin/env bash -set -u - -# init_port_forwards.sh -# Portable-ish (macOS, Ubuntu, Raspberry Pi OS, Alpine) bash init-style script -# Manages kubectl port-forward daemons defined in an XML file. - -PROG="init_port_forwards" -PROLE_HOME="${PROLE_HOME:-/Users/chrisfu/dev/prole}" -VERBOSE=0 -CONFIG_FILE="$PROLE_HOME/conf/port-mappings.properties" - -usage() { - cat < [component] - -Options: - -c, --config-file=FILE Path to local-ports.properties (XML) - -v, --verbose Verbose output - -Examples: - $PROG -c ./port-mappings.properties start - $PROG stop openbao - $PROG --verbose status -EOF -} - -TARGET_ID="" - -log() { printf '%s\n' "$*"; } -vlog() { [ "$VERBOSE" -eq 1 ] && printf '[verbose] %s\n' "$*" || true; } -err() { printf '[error] %s\n' "$*" >&2; } - -have() { command -v "$1" >/dev/null 2>&1; } - -# Choose a writable state dir for pid/log files: -# - Prefer XDG_RUNTIME_DIR if set and writable -# - Else /var/run if writable (rare without root) -# - Else ~/.local/state -# - Else /tmp -state_dir() { - if [ -n "${XDG_RUNTIME_DIR:-}" ] && [ -w "${XDG_RUNTIME_DIR:-}" ]; then - printf '%s/%s' "$XDG_RUNTIME_DIR" "$PROG" - return - fi - if [ -d "/var/run" ] && [ -w "/var/run" ]; then - printf '/var/run/%s' "$PROG" - return - fi - if [ -n "${HOME:-}" ]; then - mkdir -p "$HOME/.local/state" >/dev/null 2>&1 || true - if [ -d "$HOME/.local/state" ] && [ -w "$HOME/.local/state" ]; then - printf '%s/.local/state/%s' "$HOME" "$PROG" - return - fi - fi - printf '/tmp/%s' "$PROG" -} - -STATE_DIR="$(state_dir)" -PID_DIR="$STATE_DIR/pids" -LOG_DIR="$STATE_DIR/logs" - -ensure_dirs() { - mkdir -p "$PID_DIR" "$LOG_DIR" 2>/dev/null || true - if [ ! -d "$PID_DIR" ] || [ ! -d "$LOG_DIR" ]; then - err "Unable to create state directories under: $STATE_DIR" - exit 1 - fi -} - -# Use kubectl for actions; kubecolor is used only for prettier status output. -KUBECTL="kubectl" -detect_kubectl() { - if have kubectl; then - KUBECTL="kubectl" - else - err "kubectl not found in PATH" - exit 1 - fi -} - -# Basic environment validation -validate_env() { - detect_kubectl - ensure_dirs - - if ! "$KUBECTL" version --client >/dev/null 2>&1; then - err "kubectl seems broken or not executable" - exit 1 - fi - - # Can we reach the cluster? - if ! "$KUBECTL" cluster-info >/dev/null 2>&1; then - err "kubectl cannot reach a cluster (check KUBECONFIG/context)" - err "Try: kubectl config get-contexts && kubectl config use-context " - exit 1 - fi -} - -# ---- Preflight: Docker + k3d awareness ---- -# Return 0 when Docker CLI can talk to a running daemon. -docker_is_running() { - if ! have docker; then - return 1 - fi - docker info >/dev/null 2>&1 -} - -# Hard-fail with clean guidance when Docker is not up (used for start/restart). -ensure_docker_running() { - if docker_is_running; then - return 0 - fi - if ! have docker; then - err "Docker CLI not found. Please install Docker Desktop or docker CLI." - else - err "Docker is not running. Start Docker Desktop and wait until it is ready." - fi - err "Tip (macOS): open -a Docker" - err "Then re-run: $PROG start" - exit 3 -} - -# Detect k3d cluster name from env or current kubectl context. -# - If K3D_CLUSTER is set, use it. -# - Else, if current-context starts with 'k3d-', strip prefix to get name. -detect_k3d_context() { - if [ -n "${K3D_CLUSTER:-}" ]; then - printf '%s' "$K3D_CLUSTER" - return 0 - fi - local ctx - ctx="$($KUBECTL config current-context 2>/dev/null || echo)" - case "$ctx" in - k3d-*) printf '%s' "${ctx#k3d-}"; return 0 ;; - *) return 1 ;; - esac -} - -# Return 0 if the given k3d cluster exists and is running. -k3d_cluster_is_running() { - local name="$1" - have k3d || return 1 - # Use json output when available; fall back to grep otherwise - if k3d cluster list -o json >/dev/null 2>&1; then - k3d cluster list -o json 2>/dev/null | grep -q '"name"\s*:\s*"'"$name"'"' && \ - k3d cluster list -o json 2>/dev/null | sed -n 's/.*"name"\s*:\s*"\([^"]\+\)".*"serversRunning"\s*:\s*\([0-9]\+\).*/\1 \2/p' | awk -v n="$name" '$1==n {exit ($2>0)?0:1}' - return $? - else - k3d cluster list 2>/dev/null | grep -E "^$name\s" | grep -q running - return $? - fi -} - -# If current context indicates k3d, ensure the cluster is up; otherwise no-op. -ensure_k3d_ready_if_applicable() { - local k3d_name - if ! k3d_name="$(detect_k3d_context)"; then - return 0 - fi - if ! have k3d; then - err "k3d is not installed but kubectl context suggests k3d (context=$("$KUBECTL" config current-context))." - err "Install k3d: brew install k3d (macOS)" - err "Or switch context: kubectl config use-context " - exit 4 - fi - if ! k3d_cluster_is_running "$k3d_name"; then - err "k3d cluster '$k3d_name' is not running." - err "Start it: k3d cluster start $k3d_name" - err "Then re-run: $PROG start" - exit 4 - fi -} - -pid_file_for() { printf '%s/%s.pid' "$PID_DIR" "$1"; } -log_file_for() { printf '%s/%s.log' "$LOG_DIR" "$1"; } - -is_pid_running() { - # Return 0 if pid exists and running, else 1 - # kill -0 is portable. - local pid="$1" - [ -n "$pid" ] && kill -0 "$pid" >/dev/null 2>&1 -} - -read_pid() { - local pf="$1" - [ -f "$pf" ] || return 1 - # shellcheck disable=SC2162 - read pid <"$pf" || return 1 - printf '%s' "$pid" -} - -write_pid() { - local pf="$1" pid="$2" - printf '%s\n' "$pid" >"$pf" -} - -remove_pidfile() { - local pf="$1" - rm -f "$pf" >/dev/null 2>&1 || true -} - -# ---- XML parsing (simple attribute extraction) ---- -# We parse lines containing: -# Attributes must use double quotes in the XML (as in the sample). -get_attr() { - # $1 = line, $2 = attribute name - # outputs value or empty - printf '%s\n' "$1" | sed -n "s/.*$2=\"\([^\"]*\)\".*/\1/p" -} - -foreach_mapping() { - # Calls a provided function with mapping fields: - # callback id ns target address hostPort servicePort protocol description - local callback="$1" - [ -f "$CONFIG_FILE" ] || { err "Config file not found: $CONFIG_FILE"; exit 1; } - - # Support both single-line and multi-line self-closing mapping tags, e.g.: - # OR lines spanning multiple lines until "/>". - local in_mapping=0 in_comment=0 buffer="" line - while IFS= read -r line; do - # Handle XML comments: skip anything between - if [ $in_comment -eq 1 ]; then - case "$line" in - *"-->"*) in_comment=0; continue ;; - *) continue ;; - esac - fi - case "$line" in - *""*) - # single-line comment; skip line - continue - ;; - *) - in_comment=1 - continue - ;; - esac - ;; - esac - if [ $in_mapping -eq 0 ]; then - case "$line" in - *"' - buffer="$buffer $line" - fi - - if [ $in_mapping -eq 1 ] && printf '%s' "$line" | grep -q "/>"; then - # Normalize whitespace to make attribute extraction robust - local merged - merged=$(printf '%s\n' "$buffer" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g') - - local id ns target address hostPort servicePort protocol description - id="$(get_attr "$merged" "id")" - ns="$(get_attr "$merged" "namespace")" - target="$(get_attr "$merged" "target")" - address="$(get_attr "$merged" "address")" - hostPort="$(get_attr "$merged" "hostPort")" - servicePort="$(get_attr "$merged" "servicePort")" - protocol="$(get_attr "$merged" "protocol")" - description="$(get_attr "$merged" "description")" - - # Basic validation - if [ -z "$id" ] || [ -z "$ns" ] || [ -z "$target" ] || [ -z "$hostPort" ] || [ -z "$servicePort" ]; then - err "Invalid mapping (missing required attributes): $merged" - exit 1 - fi - - # Filter by TARGET_ID if set - if [ -n "$TARGET_ID" ] && [ "$id" != "$TARGET_ID" ]; then - in_mapping=0 - buffer="" - continue - fi - - if [ -z "$address" ]; then address="127.0.0.1"; fi - if [ -z "$protocol" ]; then protocol="TCP"; fi - - vlog "mapping: id=$id ns=$ns target=$target address=$address hostPort=$hostPort servicePort=$servicePort protocol=$protocol" - "$callback" "$id" "$ns" "$target" "$address" "$hostPort" "$servicePort" "$protocol" "$description" - - # Reset accumulator - in_mapping=0 - buffer="" - fi - done <"$CONFIG_FILE" -} - -build_port_forward_cmd() { - # echo a command string - local ns="$1" target="$2" address="$3" hostPort="$4" servicePort="$5" - # kubectl port-forward -n --address : - printf '%s port-forward -n %s --address %s %s %s:%s' \ - "$KUBECTL" "$ns" "$address" "$target" "$hostPort" "$servicePort" -} - -start_port_forward() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - local pidfile logfile cmd pid - - # Aggressively stop existing processes before starting to avoid port conflicts - stop_port_forward "$id" "$ns" "$target" "$address" "$hostPort" "$servicePort" "$protocol" "$description" - - pidfile="$(pid_file_for "$id")" - logfile="$(log_file_for "$id")" - cmd="$(build_port_forward_cmd "$ns" "$target" "$address" "$hostPort" "$servicePort")" - - log "Starting: $id $address:$hostPort -> $target:$servicePort ($ns) ${description:-}" - vlog "Command: $cmd" - - # Start in background, keep output in log. - # nohup is available on macOS/Linux; redirect stdin from /dev/null to detach. - nohup sh -c "$cmd" >>"$logfile" 2>&1 /dev/null 2>&1 || true - - # wait a moment, then SIGKILL if needed - local i - for i in 1 2 3 4 5; do - if ! is_pid_running "$pid"; then break; fi - sleep 0.2 - done - if is_pid_running "$pid"; then - err "$id did not stop gracefully; sending SIGKILL" - kill -9 "$pid" >/dev/null 2>&1 || true - fi - else - if [ -f "$pidfile" ]; then - vlog "$id stale pidfile (pid=$pid not running)" - fi - fi - remove_pidfile "$pidfile" - - # Aggressively remove any other matching kubectl port-forward processes - # Search for processes that match: kubectl port-forward -n ... : - local extra_pids - extra_pids=$(ps -ef | grep "port-forward" | grep "\-n" | grep "$ns" | grep "$target" | grep "$hostPort:$servicePort" | grep -v grep | awk '{print $2}') - - for epid in $extra_pids; do - if [ "$epid" != "$pid" ]; then - log "Cleaning up orphan process for $id (pid=$epid)" - kill -9 "$epid" >/dev/null 2>&1 || true - fi - done -} - -status_one() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - local pidfile pid - - pidfile="$(pid_file_for "$id")" - pid="" - if [ -f "$pidfile" ]; then - pid="$(read_pid "$pidfile" || true)" - fi - - if [ -n "${pid:-}" ] && is_pid_running "$pid"; then - printf 'RUNNING %-12s pid=%-7s %s:%s -> %s:%s ns=%s %s\n' \ - "$id" "$pid" "$address" "$hostPort" "$target" "$servicePort" "$ns" "${description:-}" - # ps details (portable flags vary; use a conservative format) - ps -p "$pid" -o pid=,ppid=,etime=,command= 2>/dev/null | sed 's/^/ /' || true - else - printf 'STOPPED %-12s (no live pid) %s:%s -> %s:%s ns=%s %s\n' \ - "$id" "$address" "$hostPort" "$target" "$servicePort" "$ns" "${description:-}" - fi -} - -do_start() { - validate_env - # Preflight: require Docker daemon and k3d (if applicable) - ensure_docker_running - ensure_k3d_ready_if_applicable - foreach_mapping start_port_forward -} - -do_stop() { - # stop doesn't require cluster access, but it does need state dirs - ensure_dirs - foreach_mapping stop_port_forward -} - -do_restart() { - validate_env - # Preflight: require Docker daemon and k3d (if applicable) - ensure_docker_running - ensure_k3d_ready_if_applicable - foreach_mapping stop_port_forward - foreach_mapping start_port_forward -} - -do_status() { - validate_env - # Non-fatal awareness messages - if docker_is_running; then - log "[OK] Docker daemon is running" - else - if have docker; then - log "[WARN] Docker is not running" - else - log "[WARN] Docker CLI not found" - fi - fi - - local _k3d_name - if _k3d_name="$(detect_k3d_context)"; then - if have k3d; then - if k3d_cluster_is_running "$_k3d_name"; then - log "[OK] k3d cluster '$_k3d_name' is running" - else - log "[WARN] k3d cluster '$_k3d_name' is not running" - fi - else - log "[WARN] k3d not installed but context suggests k3d (cluster='$_k3d_name')" - fi - fi - - log "== Context / Cluster ==" - "$KUBECTL" config current-context 2>/dev/null | sed 's/^/ context: /' || true - "$KUBECTL" cluster-info 2>/dev/null | sed 's/^/ /' || true - log "" - - log "== Port-forward processes ==" - foreach_mapping status_one - log "" - - log "== Quick k3d awareness checks (best-effort) ==" - # If user is on k3d, current-context often includes k3d-... but not guaranteed. - # Show nodes + a few namespaces/services related to mappings (best effort). - "$KUBECTL" get nodes -o wide 2>/dev/null | sed 's/^/ /' || true - log "" - "$KUBECTL" get ns 2>/dev/null | sed 's/^/ /' || true - log "" - - # For each mapping, try to show target existence - log "== Target existence (best-effort) ==" - _target_check() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - # kubectl get -n - # If target is like "svc/name", "deploy/name", etc. - if "$KUBECTL" get -n "$ns" "$target" >/dev/null 2>&1; then - printf 'OK %-12s %s (ns=%s)\n' "$id" "$target" "$ns" - else - printf 'MISSING %-12s %s (ns=%s)\n' "$id" "$target" "$ns" - fi - } - foreach_mapping _target_check -} - -# ---- arg parsing ---- -ACTION="" - -while [ $# -gt 0 ]; do - case "$1" in - start|stop|restart|status) - if [ -z "$ACTION" ]; then - ACTION="$1" - else - TARGET_ID="$1" - fi - shift - ;; - -v|--verbose) - VERBOSE=1 - shift - ;; - -c) - shift - [ $# -gt 0 ] || { err "-c requires a file path"; usage; exit 2; } - CONFIG_FILE="$1" - shift - ;; - --config-file=*) - CONFIG_FILE="${1#*=}" - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - if [ -z "$ACTION" ]; then - err "Unknown arg: $1" - usage - exit 2 - fi - TARGET_ID="$1" - shift - ;; - esac -done - -[ -n "$ACTION" ] || { usage; exit 2; } - -case "$ACTION" in - start) do_start ;; - stop) do_stop ;; - restart) do_restart ;; - status) do_status ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4413171632/init_prole-db.sh b/MagicMock/mock.Entry().get().strip()/4413171632/init_prole-db.sh deleted file mode 100755 index b87de05..0000000 --- a/MagicMock/mock.Entry().get().strip()/4413171632/init_prole-db.sh +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_prole-db.sh -# Purpose: -# - Manage prole-db CloudNative-PG cluster operations (deploy, start, stop, etc.) - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -ACTION=${1:-} -VERSION=${2:-latest} - -# Load env -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - source "$HOME/.prole/env.sh" -fi - -CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db} -NAMESPACE=${NAMESPACE:-prole} - -CNPG_MANIFEST="$SCRIPT_DIR/../k8s/prole/prole-db.yaml" - -ensure_tools() { - for t in kubectl jq; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -get_latest_image() { - local version_file="$SCRIPT_DIR/../conf/postgresql/.version" - if [[ -f "$version_file" ]]; then - echo "prole-db:$(cat "$version_file" | tr -d '[:space:]')" - else - echo "prole-db:17.7-033" - fi -} - -start() { - ensure_tools - echo "Checking dependencies..." - - # 1. k3d is running - if ! command -v k3d >/dev/null || ! k3d cluster list >/dev/null 2>&1; then - echo "ERROR: k3d is not running or not installed." >&2 - exit 1 - fi - - # 2. cnpg operator is loaded - if ! kubectl get deployment -n cnpg-system cnpg-controller-manager >/dev/null 2>&1; then - echo "ERROR: CloudNative-PG operator is not loaded." >&2 - exit 1 - fi - - # 3. openbao is configured - if ! kubectl get statefulset openbao -n "$NAMESPACE" >/dev/null 2>&1 && ! kubectl get deployment openbao -n "$NAMESPACE" >/dev/null 2>&1; then - echo "ERROR: OpenBao is not deployed." >&2 - exit 1 - fi - - # Compare latest image with deployed - local latest_image - latest_image=$(get_latest_image) - - local current_image - current_image=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.imageName}' 2>/dev/null || echo "") - - if [[ "$current_image" != "$latest_image" ]]; then - echo "Updating cluster image from '$current_image' to '$latest_image'..." - kubectl patch cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --type merge -p "{\"spec\": {\"imageName\": \"$latest_image\"}}" - # Force a rollout to ensure the new image is pulled even if it was just a tag update (though we use unique tags) - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true - else - echo "Cluster is already using the latest image: $latest_image" - fi - - echo "Starting prole-db cluster (ensuring manifest is applied)..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" -} - -stop() { - ensure_tools - echo "Stopping prole-db cluster $CNPG_CLUSTER_NAME..." - - # Identify instances - local instances - instances=$(kubectl get pods -n "$NAMESPACE" -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" -o jsonpath='{.items[*].metadata.name}') - - if [[ -z "$instances" ]]; then - echo "No instances found for cluster $CNPG_CLUSTER_NAME." - return - fi - - # Determine replicas and primary - local primary - primary=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.status.currentPrimary}') - - echo "Primary: $primary" - - # Shutdown replicas first - for pod in $instances; do - if [[ "$pod" != "$primary" ]]; then - echo "Shutting down replica $pod..." - kubectl exec -n "$NAMESPACE" "$pod" -c postgres -- psql -U postgres -c "CHECKPOINT;" || true - fi - done - - # Shutdown primary last - if [[ -n "$primary" ]]; then - echo "Shutting down primary $primary..." - kubectl exec -n "$NAMESPACE" "$primary" -c postgres -- psql -U postgres -c "CHECKPOINT;" || true - fi - - echo "Deleting cluster resource..." - kubectl delete -f "$CNPG_MANIFEST" --ignore-not-found -} - -restart() { - ensure_tools - echo "Restarting prole-db cluster..." - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -} - -deploy() { - ensure_tools - local image - if [[ "$VERSION" == "latest" ]]; then - image=$(get_latest_image) - else - image="prole-db:$VERSION" - fi - - echo "Deploying $image to cluster $CNPG_CLUSTER_NAME..." - - # If cluster doesn't exist, use init_cloudnative_pg.sh create first - if ! kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then - echo "Cluster not found. Running init_cloudnative_pg.sh create..." - bash "$SCRIPT_DIR/init_cloudnative_pg.sh" create - fi - - # Ensure image is updated if manifest has an older version - # First, apply the manifest to ensure the cluster exists/is updated - echo "Applying manifest $CNPG_MANIFEST..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" - - # Then force the specific image version via patch if different - local current_image - current_image=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.imageName}' 2>/dev/null || echo "") - if [[ "$current_image" != "$image" ]]; then - echo "Patching cluster to use image '$image' (was '$current_image')..." - kubectl patch cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --type merge -p "{\"spec\": {\"imageName\": \"$image\"}}" - # Force a rollout - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true - fi -} - -rollback() { - ensure_tools - echo "Rollback: Updating to previous image (manually specified version or fallback)..." - if [[ "$VERSION" == "latest" ]]; then - echo "Please specify a version to rollback to. Usage: $0 rollback " - exit 1 - fi - deploy -} - -backup() { - echo "Backup - tbd, when we configure s3 or other block store" -} - -reset() { - ensure_tools - echo "Resetting prole-db cluster..." - echo "Removing cnpg instances and pods..." - kubectl delete cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --ignore-not-found - - # Wait for deletion - kubectl wait --for=delete cluster/"$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --timeout=60s || true - - echo "Reinstantiating..." - deploy -} - -case "$ACTION" in - start) - start - ;; - stop) - stop - ;; - restart) - restart - ;; - deploy) - deploy - ;; - rollback) - rollback - ;; - backup) - backup - ;; - reset) - reset - ;; - *) - echo "Usage: $0 {start|stop|restart|deploy|rollback|backup|reset} [version]" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4452427696/env.sh b/MagicMock/mock.Entry().get().strip()/4452427696/env.sh deleted file mode 100755 index a652599..0000000 --- a/MagicMock/mock.Entry().get().strip()/4452427696/env.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -# Prole environment configuration -# This file is generated by the installer. Source it in new shells, or execute as a wrapper: -# "$PROLE_HOME/env.sh" [args…] -# shellcheck shell=bash -export PROLE_HOME="" -export PROLE_CONF="" -export PROLE_DATA="" -export PROLE_LOGS="" -export PROLE_SERVICE="" - -# Ensure PATH works for GUI-launched shells (Docker, Ollama, etc.) -_prole_add_path() { case ":${PATH}:" in *":$1:"*) ;; *) PATH="$1:${PATH:-}" ;; esac; } -_prole_add_path "$PROLE_HOME/bin" -_prole_add_path "/opt/homebrew/bin" -_prole_add_path "/usr/local/bin" -_prole_add_path "/usr/bin" -_prole_add_path "/bin" -_prole_add_path "/usr/sbin" -_prole_add_path "/sbin" -export PATH - -# Add custom paths below if needed (examples): -# _prole_add_path "/Applications/Ollama.app/Contents/MacOS" - -# If executed with arguments, run them under this environment -if [ "$#" -gt 0 ]; then - exec "$@" -fi diff --git a/MagicMock/mock.Entry().get().strip()/4452427696/hosts.txt b/MagicMock/mock.Entry().get().strip()/4452427696/hosts.txt deleted file mode 100644 index 4e73115..0000000 --- a/MagicMock/mock.Entry().get().strip()/4452427696/hosts.txt +++ /dev/null @@ -1,15 +0,0 @@ -myrddin.prole.org 10.0.0.3 -raspberry.prole.org 10.0.0.4 -pi.prole.org 10.0.0.5 -synology.prole.org 10.0.0.203 -morgoth.prole.org 10.0.0.204 -zinfandel.prole.org 10.0.0.205 -aventage.prole.org 10.0.0.206 -retropie.prole.org 10.0.0.207 -fairyland.prole.org 10.0.0.208 -k8s.prole.org zinfandel.prole.org -mc.prole.org 73.15.20.166 -morana.prole.org 10.0.0.66 -ollama.prole.org 73.15.20.166 -svc.prole.org 73.15.20.166 -www.prole.org ghs.googlehosted.com \ No newline at end of file diff --git a/MagicMock/mock.Entry().get().strip()/4452427696/init_cloudnative_pg.sh b/MagicMock/mock.Entry().get().strip()/4452427696/init_cloudnative_pg.sh deleted file mode 100755 index 6708415..0000000 --- a/MagicMock/mock.Entry().get().strip()/4452427696/init_cloudnative_pg.sh +++ /dev/null @@ -1,326 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_cloudnative_pg.sh -# Purpose: -# - Distribute administrator ed25519 key pair to CloudNative‑PG as a Kubernetes Secret for cert auth -# - Configure Kerberos (GSSAPI) using external Kerberos KDC -# - Patch CNPG cluster to enable TLS and GSSAPI where possible -# -# Usage: -# ./init_cloudnative_pg.sh start|stop|status|restart -# ./init_cloudnative_pg.sh initialize # create/update k8s secrets/configs and patch CNPG -# ./init_cloudnative_pg.sh update|reload # re-apply/patch -# -# Requirements: -# - init_openbao.sh has been run (OpenBao running in k8s) -# - $PROLE_HOME/env.sh or $HOME/.prole/env.sh defining PROLE_SERVICE - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Load env without leaking our positional args to the env script (some env.sh may `exec "$@"`). -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -else - echo "ERROR: Missing env. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2 - exit 1 -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -if [[ -z "${PROLE_SERVICE:-}" ]]; then - echo "ERROR: PROLE_SERVICE is not defined in env." >&2 - exit 1 -fi - -ACTION=${1:-} -CNPG_CLUSTER_NAME=${2:-${CNPG_CLUSTER_NAME:-prole-db}} -NAMESPACE=${NAMESPACE:-prole} -OPENBAO_NAME=${OPENBAO_NAME:-openbao} -REALM=${REALM:-PROLE.ORG} -DOMAIN=${DOMAIN:-prole.org} -KRB5_KDC=${KRB5_KDC:-} -KRB5_ADMIN=${KRB5_ADMIN:-} - -CNPG_MANIFEST="$SCRIPT_DIR/../k8s/prole/prole-db.yaml" - -SECRETS_DIR="$PROLE_SERVICE/secrets" -ADMIN_PRIV="$SECRETS_DIR/admin_ed25519.key" -ADMIN_PUB="$SECRETS_DIR/admin_ed25519.pub" -OPENBAO_TOKEN_FILE="$SECRETS_DIR/openbao-root-token" - -ensure_tools() { - for t in kubectl curl openssl base64 jq; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -# Resolve OpenBao URL: prefer explicit env, then localhost port-forward, then cluster DNS -bao_service_url() { - if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then - echo "$PROLE_OPENBAO_URL" - return 0 - fi - # Prefer standard local port-forward managed by etc/init_port_forwards.sh - if curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then - echo "http://127.0.0.1:18200" - return 0 - fi - echo "http://$OPENBAO_NAME.$NAMESPACE.svc.cluster.local:8200" -} - -fetch_admin_keys_and_db_pass_from_bao_or_local() { - local token url - if [[ -f "$OPENBAO_TOKEN_FILE" ]]; then - token=$(cat "$OPENBAO_TOKEN_FILE") - else - token="" - fi - url=$(bao_service_url) - if [[ -n "$token" ]]; then - echo "Attempting to read admin key pair from OpenBao kv/prole/admin ..." - if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -e '.data.data' >/dev/null 2>&1; then - local priv_b64 pub_b64 - priv_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -r '.data.data.admin_private_key_b64') - pub_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -r '.data.data.admin_public_key_b64') - printf "%s" "$priv_b64" | base64 -d >"$ADMIN_PRIV" - printf "%s" "$pub_b64" | base64 -d >"$ADMIN_PUB" - chmod 0600 "$ADMIN_PRIV" - fi - - echo "Attempting to read database password from OpenBao kv/prole/db ..." - if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/db" | jq -e '.data.data' >/dev/null 2>&1; then - local db_pass - db_pass=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/db" | jq -r '.data.data.password') - if [[ -n "$db_pass" ]]; then - echo "Updating database user secret 'prole-db-user' from OpenBao ..." - kubectl create secret generic prole-db-user -n "$NAMESPACE" \ - --from-literal=username=prole \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -f - - - echo "Updating database superuser secret 'prole-db-superuser' from OpenBao ..." - kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \ - --from-literal=username=postgres \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -f - - fi - fi - fi - - if [[ -f "$ADMIN_PRIV" && -f "$ADMIN_PUB" ]]; then - echo "Using local admin key pair at $SECRETS_DIR" - return 0 - fi - echo "ERROR: Could not obtain admin key pair from OpenBao and no local files found." >&2 - exit 1 -} - -apply_cnpg_admin_secret() { - echo "Creating/updating Secret cnpg-admin-key ..." - kubectl create secret generic cnpg-admin-key -n "$NAMESPACE" \ - --from-file=admin.key="$ADMIN_PRIV" \ - --from-file=admin.pub="$ADMIN_PUB" \ - --dry-run=client -o yaml | kubectl apply -f - -} - -ensure_krb5_conf_configmap() { - echo "Creating/updating ConfigMap prole-krb5-conf ..." - - local kdc_val admin_val - if [[ -n "$KRB5_KDC" ]]; then - kdc_val="$KRB5_KDC" - admin_val=${KRB5_ADMIN:-$(echo "$KRB5_KDC" | cut -d, -f1)} - else - kdc_val="kdc.$DOMAIN" - admin_val="kdc.$DOMAIN" - fi - - local TMP - TMP=$(mktemp) - cat >"$TMP" </dev/null 2>&1; then - echo "CA secret $ca_secret_name already exists; skipping generation." - return 0 - fi - echo "Generating self-signed CA (RSA 4096) for CNPG ..." - local TMPD - TMPD=$(mktemp -d) - openssl genrsa -out "$TMPD/ca.key" 4096 - openssl req -x509 -new -key "$TMPD/ca.key" -out "$TMPD/ca.crt" -days 3650 -subj "/CN=Prole CNPG CA" - kubectl -n "$NAMESPACE" create secret generic "$ca_secret_name" \ - --from-file=ca.crt="$TMPD/ca.crt" \ - --from-file=ca.key="$TMPD/ca.key" \ - --dry-run=client -o yaml | kubectl apply -f - - rm -rf "$TMPD" -} - -patch_cnpg_cluster_for_auth() { - echo "Patching CNPG Cluster $CNPG_CLUSTER_NAME to enable GSSAPI and fix config (best-effort) ..." - # Best-effort patch; schema may vary with CNPG version. - # We remove krb_srvname as it is unrecognized in some PG 17 builds. - # We revert certificates to default to avoid operator TLS issues. - kubectl -n "$NAMESPACE" patch cluster "$CNPG_CLUSTER_NAME" --type merge -p "{ - \"spec\": { - \"postgresql\": { - \"parameters\": {\"krb_srvname\": null}, - \"pg_hba\": [ - \"local all postgres trust\", - \"host all postgres all scram-sha-256\", - \"host all all all gss include_realm=1 krb_realm=$REALM\", - \"host all all all scram-sha-256\" - ] - }, - \"certificates\": { - \"serverCASecret\": null, - \"clientCASecret\": null - } - } - }" >/dev/null || echo "Note: patch may need adjustment for your CNPG version." -} - -initialize() { - ensure_tools - - # Ensure port-forward is running for OpenBao (dependency) - echo "Ensuring port-forward for OpenBao is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart openbao - - fetch_admin_keys_and_db_pass_from_bao_or_local - apply_cnpg_admin_secret - ensure_krb5_conf_configmap - # generate_tls_if_missing - patch_cnpg_cluster_for_auth - - # Ensure port-forward is running for Postgres (local access) - echo "Ensuring port-forward for Postgres is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart postgres - - echo "Initialization complete for CNPG + Kerberos + cert artifacts." -} - -update_reload() { - initialize -} - -case "$ACTION" in - recreate) - ensure_tools - "$0" delete "$CNPG_CLUSTER_NAME" - "$0" create "$CNPG_CLUSTER_NAME" - ;; - create) - ensure_tools - echo "Installing CloudNative-PG operator ..." - kubectl apply --server-side -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.27/releases/cnpg-1.27.0.yaml - - echo "Creating CloudNative-PG cluster and resources for '$CNPG_CLUSTER_NAME' ..." - - kubectl apply -k "$SCRIPT_DIR/../k8s/prole" - - # Ensure prole-index-html exists for prole deployment readiness probe - if ! kubectl get configmap prole-index-html -n prole >/dev/null 2>&1; then - echo "Creating prole-index-html configmap..." - printf "

Prole

" > /tmp/index.html - kubectl create configmap prole-index-html --from-file=/tmp/index.html -n prole - rm /tmp/index.html - fi - - # Ensure prole-nginx-tls exists (self-signed for dev) - if ! kubectl get secret prole-nginx-tls -n prole >/dev/null 2>&1; then - echo "Generating self-signed prole-nginx-tls for development..." - openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout /tmp/nginx-tls.key -out /tmp/nginx-tls.crt \ - -subj "/CN=prole.org" >/dev/null 2>&1 - kubectl create secret tls prole-nginx-tls --key /tmp/nginx-tls.key --cert /tmp/nginx-tls.crt -n prole - rm /tmp/nginx-tls.key /tmp/nginx-tls.crt - fi - - echo "Initializing and patching cluster ..." - initialize - ;; - delete) - ensure_tools - echo "Deleting all resources for '$CNPG_CLUSTER_NAME' ..." - kubectl delete -k "$SCRIPT_DIR/../k8s/prole" --ignore-not-found - ;; - start) - ensure_tools - if [[ ! -f "$CNPG_MANIFEST" ]]; then - echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2 - exit 1 - fi - echo "Starting CloudNative-PG cluster from $CNPG_MANIFEST in namespace $NAMESPACE..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" - ;; - stop) - ensure_tools - if [[ ! -f "$CNPG_MANIFEST" ]]; then - echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2 - exit 1 - fi - echo "Stopping CloudNative-PG cluster using $CNPG_MANIFEST ..." - kubectl delete -f "$CNPG_MANIFEST" --ignore-not-found - ;; - status) - ensure_tools - echo "--- CloudNative-PG Cluster Status ($CNPG_CLUSTER_NAME) ---" - if kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then - kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" - echo "" - echo "CNPG Plugin Status:" - if kubectl cnpg version >/dev/null 2>&1; then - kubectl cnpg status "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" - else - echo "Note: 'kubectl cnpg' plugin not found; skipping detailed status." - fi - else - echo "Cluster '$CNPG_CLUSTER_NAME' not found in namespace '$NAMESPACE'." - fi - ;; - restart) - ensure_tools - "$0" stop - "$0" start - ;; - initialize) - initialize - ;; - update|reload) - update_reload - ;; - *) - echo "Usage: $0 {create|delete|recreate|start|stop|status|restart|initialize|update|reload} [dbname]" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4452427696/init_k8s.sh b/MagicMock/mock.Entry().get().strip()/4452427696/init_k8s.sh deleted file mode 100755 index 35f14aa..0000000 --- a/MagicMock/mock.Entry().get().strip()/4452427696/init_k8s.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# etc/init_k8s.sh -# Purpose: Manage k3d/k8s environment - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Preserve positional args while sourcing env -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -# Default values -VERBOSE=false -ENVIRONMENT="prod" -HOST="" -CLUSTER_NAME_DEFAULT="prole-dev-cluster" -CLUSTER_PORT_DEFAULT="6443" - -usage() { - cat </dev/null || { echo "ERROR: Missing required tool: $t" >&2; exit 1; } - done -} - -ensure_docker() { - if ! docker info >/dev/null 2>&1; then - echo "ERROR: Docker is not running." >&2 - exit 1 - fi -} - -initialize() { - ensure_tools - ensure_docker - - if [[ "$ENVIRONMENT" == "dev" ]]; then - if [[ -z "$CLUSTER_NAME" ]]; then - if [[ -t 0 ]]; then - read -p "Enter k3d cluster name [$CLUSTER_NAME_DEFAULT]: " CLUSTER_NAME - CLUSTER_NAME=${CLUSTER_NAME:-$CLUSTER_NAME_DEFAULT} - else - CLUSTER_NAME=$CLUSTER_NAME_DEFAULT - fi - fi - - if k3d cluster list "$CLUSTER_NAME" >/dev/null 2>&1; then - log "Cluster '$CLUSTER_NAME' already exists." - else - log "Creating k3d cluster '$CLUSTER_NAME'..." - k3d cluster create "$CLUSTER_NAME" -p "0.0.0.0:$CLUSTER_PORT_DEFAULT:6443@server:0" - fi - else - if [[ -n "$HOST" ]]; then - log "Environment is $ENVIRONMENT. Host is $HOST." - log "TODO: Fetch kubeconfig from $HOST (placeholder)." - else - log "Environment is $ENVIRONMENT. Use -h|--host to specify the remote cluster host." - fi - fi -} - -status() { - ensure_tools - log "Checking Docker status..." - if docker info >/dev/null 2>&1; then - echo "Docker is running." - else - echo "Docker is NOT running." - fi - - log "Listing k3d clusters..." - k3d cluster list -} - -get_cluster_name() { - # If a name was provided or exists in env, use it. - # Otherwise, if there is only one cluster, use it. - # Finally, use default. - if [[ -n "${CLUSTER_NAME:-}" ]]; then - echo "$CLUSTER_NAME" - return - fi - - local clusters - clusters=$(k3d cluster list --no-headers | awk '{print $1}') - local count - count=$(echo "$clusters" | grep -c . || true) - - if [[ "$count" -eq 1 ]]; then - echo "$clusters" - else - echo "$CLUSTER_NAME_DEFAULT" - fi -} - -case "$ACTION" in - initialize) - initialize - ;; - status) - status - ;; - start) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Starting k3d cluster '$CLUSTER_NAME'..." - k3d cluster start "$CLUSTER_NAME" - ;; - stop) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Stopping k3d cluster '$CLUSTER_NAME'..." - k3d cluster stop "$CLUSTER_NAME" - ;; - restart) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Restarting k3d cluster '$CLUSTER_NAME'..." - k3d cluster stop "$CLUSTER_NAME" - k3d cluster start "$CLUSTER_NAME" - ;; - *) - usage - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4452427696/init_openbao.sh b/MagicMock/mock.Entry().get().strip()/4452427696/init_openbao.sh deleted file mode 100755 index 3e59151..0000000 --- a/MagicMock/mock.Entry().get().strip()/4452427696/init_openbao.sh +++ /dev/null @@ -1,490 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_openbao.sh -# Purpose: -# - Deploy OpenBao to Kubernetes (dev mode) and store admin ed25519 key pair -# Generate and apply a Kerberos krb5.conf ConfigMap for an external realm -# - Local Docker helpers for OpenBao (optional) - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Preserve positional args while sourcing env -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -if [[ -z "${PROLE_SERVICE:-}" ]]; then - echo "ERROR: PROLE_SERVICE is not defined in env. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2 - exit 1 -fi - -ACTION=${1:-} - -# Defaults -NAMESPACE=${NAMESPACE:-prole} -OPENBAO_NAME=${OPENBAO_NAME:-openbao} -OPENBAO_MANIFEST_DIR="$SCRIPT_DIR/../k8s/openbao" - -# Kerberos realm defaults -KRB5_REALM=${KRB5_REALM:-PROLE.ORG} -DOMAIN=${DOMAIN:-$(echo "$KRB5_REALM" | tr 'A-Z' 'a-z')} -KRB5_KDC=${KRB5_KDC:-kdc.$DOMAIN} -KRB5_ADMIN=${KRB5_ADMIN:-} - -# Secrets and token locations -SECRETS_DIR="$PROLE_SERVICE/secrets" -mkdir -p "$SECRETS_DIR" -admin_key_priv="$SECRETS_DIR/admin_ed25519.key" -admin_key_pub="$SECRETS_DIR/admin_ed25519.pub" -root_token_file="$SECRETS_DIR/openbao-root-token" - -ensure_tools() { - for t in kubectl curl openssl base64; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -ensure_docker() { - command -v docker >/dev/null || { echo "Missing required tool: docker" >&2; exit 1; } -} - -ensure_admin_keypair() { - # Ensure admin ed25519 keypair exists and a root token is available - mkdir -p "$SECRETS_DIR" - if [[ ! -f "$admin_key_priv" || ! -f "$admin_key_pub" ]]; then - echo "Generating admin ed25519 keypair in $SECRETS_DIR ..." - openssl genpkey -algorithm ED25519 -out "$admin_key_priv" - openssl pkey -in "$admin_key_priv" -pubout -out "$admin_key_pub" - chmod 0600 "$admin_key_priv" - fi - # Ensure a root token exists for OpenBao dev server interactions - if [[ ! -f "$root_token_file" || ! -s "$root_token_file" ]]; then - openssl rand -hex 24 >"$root_token_file" - chmod 0600 "$root_token_file" - fi -} - -generate_openbao_manifests() { - mkdir -p "$OPENBAO_MANIFEST_DIR" - # Only create a simple deployment if none exists; otherwise respect current file - local deploy="$OPENBAO_MANIFEST_DIR/deployment.yaml" - if [[ ! -f "$deploy" ]]; then - cat >"$deploy" <"$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" </dev/null 2>&1; then - kubectl rollout status statefulset/$OPENBAO_NAME -n "$NAMESPACE" --timeout=120s - else - kubectl rollout status deploy/$OPENBAO_NAME -n "$NAMESPACE" --timeout=120s - fi -} - -init_openbao_kv_and_store_admin_key() { - local token - token=$(cat "$root_token_file") - # Enable kv (if not already) and write keys - local svc="http://127.0.0.1:18200" - echo "Configuring KV at $svc ..." - - # Check if kv/ is already mounted - if curl -sS -H "X-Vault-Token: $token" "$svc/v1/sys/mounts" | grep -q '"kv/":'; then - echo "KV path 'kv/' is already enabled." - else - curl -sS -H "X-Vault-Token: $token" -X POST "$svc/v1/sys/mounts/kv" \ - -d '{"type":"kv","options":{"version":"2"}}' >/dev/null 2>&1 || true - fi - - local priv pub - priv=$(base64 <"$admin_key_priv" | tr -d '\n') - pub=$(base64 <"$admin_key_pub" | tr -d '\n') - - # Check if admin key already exists and matches - local existing_data - existing_data=$(curl -sS -H "X-Vault-Token: $token" "$svc/v1/kv/data/prole/admin" 2>/dev/null || true) - if [[ -n "$existing_data" ]]; then - local ex_priv ex_pub - ex_priv=$(echo "$existing_data" | grep -o '"admin_private_key_b64":"[^"]*' | cut -d'"' -f4 || true) - ex_pub=$(echo "$existing_data" | grep -o '"admin_public_key_b64":"[^"]*' | cut -d'"' -f4 || true) - - if [[ "$ex_priv" == "$priv" && "$ex_pub" == "$pub" ]]; then - echo "Admin key pair in OpenBao kv/prole/admin is already up to date." - else - echo "Writing admin key pair to kv/prole/admin ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/admin" \ - -d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null - echo "Stored admin key pair in OpenBao kv/prole/admin." - fi - else - echo "Writing admin key pair to kv/prole/admin ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/admin" \ - -d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null - echo "Stored admin key pair in OpenBao kv/prole/admin." - fi - - # Store User SSH keys if they exist - local user_key_priv="$HOME/.ssh/id_prole_ed25519" - local user_key_pub="$HOME/.ssh/id_prole_ed25519.pub" - if [[ -f "$user_key_priv" && -f "$user_key_pub" ]]; then - local u_priv u_pub - u_priv=$(base64 <"$user_key_priv" | tr -d '\n') - u_pub=$(base64 <"$user_key_pub" | tr -d '\n') - local username=${PROLE_DB_USER:-"prole"} - - echo "Writing user SSH keys for '$username' to kv/prole/user ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/user" \ - -d "{\"data\":{\"username\":\"$username\",\"private_key_b64\":\"$u_priv\",\"public_key_b64\":\"$u_pub\"}}" >/dev/null - echo "Stored user SSH keys in OpenBao kv/prole/user." - fi - - if [[ -n "$db_pass" ]]; then - local username=${PROLE_DB_USER:-"prole"} - echo "Writing database user password for '$username' to kv/prole/db ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/db" \ - -d "{\"data\":{\"username\":\"$username\",\"password\":\"$db_pass\"}}" >/dev/null - echo "Stored database password in OpenBao kv/prole/db." - fi -} - -# Removed: prompt_admin_password_and_apply_secret (no AD admin secret required) - -docker_start() { - echo "Starting local Docker container for OpenBao ..." - ensure_docker - docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true - # OpenBao dev - local token - token=$(cat "$root_token_file" 2>/dev/null || true) - if [[ -z "$token" ]]; then - token=$(openssl rand -hex 24) - printf "%s" "$token" >"$root_token_file" - chmod 0600 "$root_token_file" - fi - docker run -d --name "$OPENBAO_NAME" -p 18200:8200 \ - "$OPENBAO_IMAGE" server -dev -dev-listen-address=0.0.0.0:8200 -dev-root-token-id="$token" - echo "Docker container started: $OPENBAO_NAME" -} - -docker_stop() { - docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true - echo "Stopped OpenBao container if it was running." -} - -docker_restart() { - docker_stop - docker_start -} - -# status command implementation wrapped in a function to avoid top-level 'local' -cmd_status() { - echo "--- init_primary_domain status ---" - echo "Namespace: $NAMESPACE" - echo "OpenBao name: $OPENBAO_NAME" - echo "Manifest dir: $OPENBAO_MANIFEST_DIR" - - # Files/manifests present - local ok=0 - if [[ -f "$OPENBAO_MANIFEST_DIR/deployment.yaml" ]]; then - echo "[OK] OpenBao deployment manifest exists" - else - echo "[MISSING] $OPENBAO_MANIFEST_DIR/deployment.yaml" - ok=1 - fi - if [[ -f "$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" ]]; then - echo "[OK] Kerberos ConfigMap manifest exists" - else - echo "[MISSING] $OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" - ok=1 - fi - - # K8s resources - if kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" >/dev/null 2>&1; then - local ready desired - ready=$(kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") - desired=$(kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0") - ready=${ready:-0} - desired=${desired:-0} - if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then - echo "[OK] OpenBao StatefulSet running ($ready/$desired ready)" - else - echo "[WARN] OpenBao StatefulSet not fully ready ($ready/$desired)" - ok=1 - fi - elif kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" >/dev/null 2>&1; then - # Get readiness - local ready desired - ready=$(kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") - desired=$(kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0") - ready=${ready:-0} - desired=${desired:-0} - if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then - echo "[OK] OpenBao Deployment running ($ready/$desired ready)" - else - echo "[WARN] OpenBao Deployment not fully ready ($ready/$desired)" - ok=1 - fi - else - echo "[MISSING] OpenBao Deployment or StatefulSet '$OPENBAO_NAME' in namespace '$NAMESPACE'" - ok=1 - fi - - if kubectl -n "$NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then - echo "[OK] Kerberos ConfigMap 'prole-krb5-conf' present" - else - echo "[MISSING] Kerberos ConfigMap 'prole-krb5-conf' in namespace '$NAMESPACE'" - ok=1 - fi - - # Docker (optional local mode) - if command -v docker >/dev/null 2>&1; then - if docker ps --format '{{.Names}}' | grep -Fxq "$OPENBAO_NAME"; then - echo "[OK] Docker container '$OPENBAO_NAME' is running" - else - # Not necessarily an error; we primarily use k8s - echo "[INFO] Docker container '$OPENBAO_NAME' not running (k8s mode may be in use)" - fi - fi - - # Secrets and tokens - if [[ -f "$admin_key_priv" && -f "$admin_key_pub" ]]; then - echo "[OK] Admin ed25519 keypair present in $SECRETS_DIR" - else - echo "[MISSING] Admin keypair files in $SECRETS_DIR" - ok=1 - fi - if [[ -s "$root_token_file" ]]; then - echo "[OK] OpenBao root token file present" - else - echo "[MISSING] OpenBao root token file ($root_token_file)" - ok=1 - fi - - # OpenBao reachability and token validity (best-effort) - # Prefer explicit override, then localhost port-forward, then cluster DNS - local svc_url - if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then - svc_url="$PROLE_OPENBAO_URL" - elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then - svc_url="http://127.0.0.1:18200" - else - svc_url="http://$OPENBAO_NAME.$NAMESPACE.svc.cluster.local:8200" - fi - if curl -sS "$svc_url/v1/sys/health" >/dev/null 2>&1; then - echo "[OK] OpenBao service reachable" - if [[ -s "$root_token_file" ]]; then - local code - code=$(curl -s -o /dev/null -w "%{http_code}" -H "X-Vault-Token: $(cat "$root_token_file")" "$svc_url/v1/sys/mounts" || echo "000") - if [[ "$code" == "200" ]]; then - echo "[OK] OpenBao root token authenticates" - else - echo "[WARN] OpenBao root token did not authenticate (HTTP $code)" - ok=1 - fi - fi - else - echo "[WARN] OpenBao service not reachable at $svc_url" - ok=1 - fi - - # Service certificate existence (CNPG TLS) - local CNPG_CLUSTER_NAME - CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db} - if kubectl -n "$NAMESPACE" get secret "${CNPG_CLUSTER_NAME}-tls" >/dev/null 2>&1; then - echo "[OK] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' exists" - else - echo "[INFO] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' not found" - fi - - echo "-------------------------------------" - if [[ $ok -eq 0 ]]; then - echo "Status: OK" - return 0 - else - echo "Status: Issues detected" - return 1 - fi -} - -case "$ACTION" in - start) - ensure_tools - ensure_docker - ensure_admin_keypair - docker_start - ;; - status) - ensure_tools - cmd_status - ;; - stop) - ensure_docker - docker_stop - ;; - restart) - ensure_tools - ensure_docker - ensure_admin_keypair - docker_restart - ;; - initialize) - ensure_tools - # Read password from stdin if provided - db_pass="" - if [[ ! -t 0 ]]; then - read -r db_pass - fi - - ensure_admin_keypair - generate_openbao_manifests - generate_kerberos_configmap - apply_k8s - - if [[ -n "$db_pass" ]]; then - local username=${PROLE_DB_USER:-"prole"} - echo "Creating database user secret 'prole-db-user' for '$username' ..." - kubectl create secret generic prole-db-user -n "$NAMESPACE" \ - --from-literal=username="$username" \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f - - - echo "Creating database superuser secret 'prole-db-superuser' ..." - kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \ - --from-literal=username=postgres \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f - - fi - - wait_for_openbao - - # Ensure port-forward is running for OpenBao - echo "Ensuring port-forward for OpenBao is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart openbao - - if ! curl -sS http://127.0.0.1:18200/v1/sys/health >/dev/null 2>&1; then - echo "ERROR: OpenBao not reachable at http://127.0.0.1:18200." >&2 - echo "Please start port-forwards: $SCRIPT_DIR/init_port_forwards.sh start openbao" >&2 - exit 1 - fi - init_openbao_kv_and_store_admin_key - echo "Initialization complete. Manifests in: $OPENBAO_MANIFEST_DIR" - ;; - update|reload) - generate_openbao_manifests - generate_kerberos_configmap - apply_k8s - echo "Re-applied manifests." - ;; - *) - echo "Usage: $0 {start|stop|status|restart|initialize|update|reload}" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4452427696/init_port_forwards.sh b/MagicMock/mock.Entry().get().strip()/4452427696/init_port_forwards.sh deleted file mode 100755 index 9ebea5c..0000000 --- a/MagicMock/mock.Entry().get().strip()/4452427696/init_port_forwards.sh +++ /dev/null @@ -1,537 +0,0 @@ -#!/usr/bin/env bash -set -u - -# init_port_forwards.sh -# Portable-ish (macOS, Ubuntu, Raspberry Pi OS, Alpine) bash init-style script -# Manages kubectl port-forward daemons defined in an XML file. - -PROG="init_port_forwards" -PROLE_HOME="${PROLE_HOME:-/Users/chrisfu/dev/prole}" -VERBOSE=0 -CONFIG_FILE="$PROLE_HOME/conf/port-mappings.properties" - -usage() { - cat < [component] - -Options: - -c, --config-file=FILE Path to local-ports.properties (XML) - -v, --verbose Verbose output - -Examples: - $PROG -c ./port-mappings.properties start - $PROG stop openbao - $PROG --verbose status -EOF -} - -TARGET_ID="" - -log() { printf '%s\n' "$*"; } -vlog() { [ "$VERBOSE" -eq 1 ] && printf '[verbose] %s\n' "$*" || true; } -err() { printf '[error] %s\n' "$*" >&2; } - -have() { command -v "$1" >/dev/null 2>&1; } - -# Choose a writable state dir for pid/log files: -# - Prefer XDG_RUNTIME_DIR if set and writable -# - Else /var/run if writable (rare without root) -# - Else ~/.local/state -# - Else /tmp -state_dir() { - if [ -n "${XDG_RUNTIME_DIR:-}" ] && [ -w "${XDG_RUNTIME_DIR:-}" ]; then - printf '%s/%s' "$XDG_RUNTIME_DIR" "$PROG" - return - fi - if [ -d "/var/run" ] && [ -w "/var/run" ]; then - printf '/var/run/%s' "$PROG" - return - fi - if [ -n "${HOME:-}" ]; then - mkdir -p "$HOME/.local/state" >/dev/null 2>&1 || true - if [ -d "$HOME/.local/state" ] && [ -w "$HOME/.local/state" ]; then - printf '%s/.local/state/%s' "$HOME" "$PROG" - return - fi - fi - printf '/tmp/%s' "$PROG" -} - -STATE_DIR="$(state_dir)" -PID_DIR="$STATE_DIR/pids" -LOG_DIR="$STATE_DIR/logs" - -ensure_dirs() { - mkdir -p "$PID_DIR" "$LOG_DIR" 2>/dev/null || true - if [ ! -d "$PID_DIR" ] || [ ! -d "$LOG_DIR" ]; then - err "Unable to create state directories under: $STATE_DIR" - exit 1 - fi -} - -# Use kubectl for actions; kubecolor is used only for prettier status output. -KUBECTL="kubectl" -detect_kubectl() { - if have kubectl; then - KUBECTL="kubectl" - else - err "kubectl not found in PATH" - exit 1 - fi -} - -# Basic environment validation -validate_env() { - detect_kubectl - ensure_dirs - - if ! "$KUBECTL" version --client >/dev/null 2>&1; then - err "kubectl seems broken or not executable" - exit 1 - fi - - # Can we reach the cluster? - if ! "$KUBECTL" cluster-info >/dev/null 2>&1; then - err "kubectl cannot reach a cluster (check KUBECONFIG/context)" - err "Try: kubectl config get-contexts && kubectl config use-context " - exit 1 - fi -} - -# ---- Preflight: Docker + k3d awareness ---- -# Return 0 when Docker CLI can talk to a running daemon. -docker_is_running() { - if ! have docker; then - return 1 - fi - docker info >/dev/null 2>&1 -} - -# Hard-fail with clean guidance when Docker is not up (used for start/restart). -ensure_docker_running() { - if docker_is_running; then - return 0 - fi - if ! have docker; then - err "Docker CLI not found. Please install Docker Desktop or docker CLI." - else - err "Docker is not running. Start Docker Desktop and wait until it is ready." - fi - err "Tip (macOS): open -a Docker" - err "Then re-run: $PROG start" - exit 3 -} - -# Detect k3d cluster name from env or current kubectl context. -# - If K3D_CLUSTER is set, use it. -# - Else, if current-context starts with 'k3d-', strip prefix to get name. -detect_k3d_context() { - if [ -n "${K3D_CLUSTER:-}" ]; then - printf '%s' "$K3D_CLUSTER" - return 0 - fi - local ctx - ctx="$($KUBECTL config current-context 2>/dev/null || echo)" - case "$ctx" in - k3d-*) printf '%s' "${ctx#k3d-}"; return 0 ;; - *) return 1 ;; - esac -} - -# Return 0 if the given k3d cluster exists and is running. -k3d_cluster_is_running() { - local name="$1" - have k3d || return 1 - # Use json output when available; fall back to grep otherwise - if k3d cluster list -o json >/dev/null 2>&1; then - k3d cluster list -o json 2>/dev/null | grep -q '"name"\s*:\s*"'"$name"'"' && \ - k3d cluster list -o json 2>/dev/null | sed -n 's/.*"name"\s*:\s*"\([^"]\+\)".*"serversRunning"\s*:\s*\([0-9]\+\).*/\1 \2/p' | awk -v n="$name" '$1==n {exit ($2>0)?0:1}' - return $? - else - k3d cluster list 2>/dev/null | grep -E "^$name\s" | grep -q running - return $? - fi -} - -# If current context indicates k3d, ensure the cluster is up; otherwise no-op. -ensure_k3d_ready_if_applicable() { - local k3d_name - if ! k3d_name="$(detect_k3d_context)"; then - return 0 - fi - if ! have k3d; then - err "k3d is not installed but kubectl context suggests k3d (context=$("$KUBECTL" config current-context))." - err "Install k3d: brew install k3d (macOS)" - err "Or switch context: kubectl config use-context " - exit 4 - fi - if ! k3d_cluster_is_running "$k3d_name"; then - err "k3d cluster '$k3d_name' is not running." - err "Start it: k3d cluster start $k3d_name" - err "Then re-run: $PROG start" - exit 4 - fi -} - -pid_file_for() { printf '%s/%s.pid' "$PID_DIR" "$1"; } -log_file_for() { printf '%s/%s.log' "$LOG_DIR" "$1"; } - -is_pid_running() { - # Return 0 if pid exists and running, else 1 - # kill -0 is portable. - local pid="$1" - [ -n "$pid" ] && kill -0 "$pid" >/dev/null 2>&1 -} - -read_pid() { - local pf="$1" - [ -f "$pf" ] || return 1 - # shellcheck disable=SC2162 - read pid <"$pf" || return 1 - printf '%s' "$pid" -} - -write_pid() { - local pf="$1" pid="$2" - printf '%s\n' "$pid" >"$pf" -} - -remove_pidfile() { - local pf="$1" - rm -f "$pf" >/dev/null 2>&1 || true -} - -# ---- XML parsing (simple attribute extraction) ---- -# We parse lines containing: -# Attributes must use double quotes in the XML (as in the sample). -get_attr() { - # $1 = line, $2 = attribute name - # outputs value or empty - printf '%s\n' "$1" | sed -n "s/.*$2=\"\([^\"]*\)\".*/\1/p" -} - -foreach_mapping() { - # Calls a provided function with mapping fields: - # callback id ns target address hostPort servicePort protocol description - local callback="$1" - [ -f "$CONFIG_FILE" ] || { err "Config file not found: $CONFIG_FILE"; exit 1; } - - # Support both single-line and multi-line self-closing mapping tags, e.g.: - # OR lines spanning multiple lines until "/>". - local in_mapping=0 in_comment=0 buffer="" line - while IFS= read -r line; do - # Handle XML comments: skip anything between - if [ $in_comment -eq 1 ]; then - case "$line" in - *"-->"*) in_comment=0; continue ;; - *) continue ;; - esac - fi - case "$line" in - *""*) - # single-line comment; skip line - continue - ;; - *) - in_comment=1 - continue - ;; - esac - ;; - esac - if [ $in_mapping -eq 0 ]; then - case "$line" in - *"' - buffer="$buffer $line" - fi - - if [ $in_mapping -eq 1 ] && printf '%s' "$line" | grep -q "/>"; then - # Normalize whitespace to make attribute extraction robust - local merged - merged=$(printf '%s\n' "$buffer" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g') - - local id ns target address hostPort servicePort protocol description - id="$(get_attr "$merged" "id")" - ns="$(get_attr "$merged" "namespace")" - target="$(get_attr "$merged" "target")" - address="$(get_attr "$merged" "address")" - hostPort="$(get_attr "$merged" "hostPort")" - servicePort="$(get_attr "$merged" "servicePort")" - protocol="$(get_attr "$merged" "protocol")" - description="$(get_attr "$merged" "description")" - - # Basic validation - if [ -z "$id" ] || [ -z "$ns" ] || [ -z "$target" ] || [ -z "$hostPort" ] || [ -z "$servicePort" ]; then - err "Invalid mapping (missing required attributes): $merged" - exit 1 - fi - - # Filter by TARGET_ID if set - if [ -n "$TARGET_ID" ] && [ "$id" != "$TARGET_ID" ]; then - in_mapping=0 - buffer="" - continue - fi - - if [ -z "$address" ]; then address="127.0.0.1"; fi - if [ -z "$protocol" ]; then protocol="TCP"; fi - - vlog "mapping: id=$id ns=$ns target=$target address=$address hostPort=$hostPort servicePort=$servicePort protocol=$protocol" - "$callback" "$id" "$ns" "$target" "$address" "$hostPort" "$servicePort" "$protocol" "$description" - - # Reset accumulator - in_mapping=0 - buffer="" - fi - done <"$CONFIG_FILE" -} - -build_port_forward_cmd() { - # echo a command string - local ns="$1" target="$2" address="$3" hostPort="$4" servicePort="$5" - # kubectl port-forward -n --address : - printf '%s port-forward -n %s --address %s %s %s:%s' \ - "$KUBECTL" "$ns" "$address" "$target" "$hostPort" "$servicePort" -} - -start_port_forward() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - local pidfile logfile cmd pid - - # Aggressively stop existing processes before starting to avoid port conflicts - stop_port_forward "$id" "$ns" "$target" "$address" "$hostPort" "$servicePort" "$protocol" "$description" - - pidfile="$(pid_file_for "$id")" - logfile="$(log_file_for "$id")" - cmd="$(build_port_forward_cmd "$ns" "$target" "$address" "$hostPort" "$servicePort")" - - log "Starting: $id $address:$hostPort -> $target:$servicePort ($ns) ${description:-}" - vlog "Command: $cmd" - - # Start in background, keep output in log. - # nohup is available on macOS/Linux; redirect stdin from /dev/null to detach. - nohup sh -c "$cmd" >>"$logfile" 2>&1 /dev/null 2>&1 || true - - # wait a moment, then SIGKILL if needed - local i - for i in 1 2 3 4 5; do - if ! is_pid_running "$pid"; then break; fi - sleep 0.2 - done - if is_pid_running "$pid"; then - err "$id did not stop gracefully; sending SIGKILL" - kill -9 "$pid" >/dev/null 2>&1 || true - fi - else - if [ -f "$pidfile" ]; then - vlog "$id stale pidfile (pid=$pid not running)" - fi - fi - remove_pidfile "$pidfile" - - # Aggressively remove any other matching kubectl port-forward processes - # Search for processes that match: kubectl port-forward -n ... : - local extra_pids - extra_pids=$(ps -ef | grep "port-forward" | grep "\-n" | grep "$ns" | grep "$target" | grep "$hostPort:$servicePort" | grep -v grep | awk '{print $2}') - - for epid in $extra_pids; do - if [ "$epid" != "$pid" ]; then - log "Cleaning up orphan process for $id (pid=$epid)" - kill -9 "$epid" >/dev/null 2>&1 || true - fi - done -} - -status_one() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - local pidfile pid - - pidfile="$(pid_file_for "$id")" - pid="" - if [ -f "$pidfile" ]; then - pid="$(read_pid "$pidfile" || true)" - fi - - if [ -n "${pid:-}" ] && is_pid_running "$pid"; then - printf 'RUNNING %-12s pid=%-7s %s:%s -> %s:%s ns=%s %s\n' \ - "$id" "$pid" "$address" "$hostPort" "$target" "$servicePort" "$ns" "${description:-}" - # ps details (portable flags vary; use a conservative format) - ps -p "$pid" -o pid=,ppid=,etime=,command= 2>/dev/null | sed 's/^/ /' || true - else - printf 'STOPPED %-12s (no live pid) %s:%s -> %s:%s ns=%s %s\n' \ - "$id" "$address" "$hostPort" "$target" "$servicePort" "$ns" "${description:-}" - fi -} - -do_start() { - validate_env - # Preflight: require Docker daemon and k3d (if applicable) - ensure_docker_running - ensure_k3d_ready_if_applicable - foreach_mapping start_port_forward -} - -do_stop() { - # stop doesn't require cluster access, but it does need state dirs - ensure_dirs - foreach_mapping stop_port_forward -} - -do_restart() { - validate_env - # Preflight: require Docker daemon and k3d (if applicable) - ensure_docker_running - ensure_k3d_ready_if_applicable - foreach_mapping stop_port_forward - foreach_mapping start_port_forward -} - -do_status() { - validate_env - # Non-fatal awareness messages - if docker_is_running; then - log "[OK] Docker daemon is running" - else - if have docker; then - log "[WARN] Docker is not running" - else - log "[WARN] Docker CLI not found" - fi - fi - - local _k3d_name - if _k3d_name="$(detect_k3d_context)"; then - if have k3d; then - if k3d_cluster_is_running "$_k3d_name"; then - log "[OK] k3d cluster '$_k3d_name' is running" - else - log "[WARN] k3d cluster '$_k3d_name' is not running" - fi - else - log "[WARN] k3d not installed but context suggests k3d (cluster='$_k3d_name')" - fi - fi - - log "== Context / Cluster ==" - "$KUBECTL" config current-context 2>/dev/null | sed 's/^/ context: /' || true - "$KUBECTL" cluster-info 2>/dev/null | sed 's/^/ /' || true - log "" - - log "== Port-forward processes ==" - foreach_mapping status_one - log "" - - log "== Quick k3d awareness checks (best-effort) ==" - # If user is on k3d, current-context often includes k3d-... but not guaranteed. - # Show nodes + a few namespaces/services related to mappings (best effort). - "$KUBECTL" get nodes -o wide 2>/dev/null | sed 's/^/ /' || true - log "" - "$KUBECTL" get ns 2>/dev/null | sed 's/^/ /' || true - log "" - - # For each mapping, try to show target existence - log "== Target existence (best-effort) ==" - _target_check() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - # kubectl get -n - # If target is like "svc/name", "deploy/name", etc. - if "$KUBECTL" get -n "$ns" "$target" >/dev/null 2>&1; then - printf 'OK %-12s %s (ns=%s)\n' "$id" "$target" "$ns" - else - printf 'MISSING %-12s %s (ns=%s)\n' "$id" "$target" "$ns" - fi - } - foreach_mapping _target_check -} - -# ---- arg parsing ---- -ACTION="" - -while [ $# -gt 0 ]; do - case "$1" in - start|stop|restart|status) - if [ -z "$ACTION" ]; then - ACTION="$1" - else - TARGET_ID="$1" - fi - shift - ;; - -v|--verbose) - VERBOSE=1 - shift - ;; - -c) - shift - [ $# -gt 0 ] || { err "-c requires a file path"; usage; exit 2; } - CONFIG_FILE="$1" - shift - ;; - --config-file=*) - CONFIG_FILE="${1#*=}" - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - if [ -z "$ACTION" ]; then - err "Unknown arg: $1" - usage - exit 2 - fi - TARGET_ID="$1" - shift - ;; - esac -done - -[ -n "$ACTION" ] || { usage; exit 2; } - -case "$ACTION" in - start) do_start ;; - stop) do_stop ;; - restart) do_restart ;; - status) do_status ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4452427696/init_prole-db.sh b/MagicMock/mock.Entry().get().strip()/4452427696/init_prole-db.sh deleted file mode 100755 index b87de05..0000000 --- a/MagicMock/mock.Entry().get().strip()/4452427696/init_prole-db.sh +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_prole-db.sh -# Purpose: -# - Manage prole-db CloudNative-PG cluster operations (deploy, start, stop, etc.) - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -ACTION=${1:-} -VERSION=${2:-latest} - -# Load env -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - source "$HOME/.prole/env.sh" -fi - -CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db} -NAMESPACE=${NAMESPACE:-prole} - -CNPG_MANIFEST="$SCRIPT_DIR/../k8s/prole/prole-db.yaml" - -ensure_tools() { - for t in kubectl jq; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -get_latest_image() { - local version_file="$SCRIPT_DIR/../conf/postgresql/.version" - if [[ -f "$version_file" ]]; then - echo "prole-db:$(cat "$version_file" | tr -d '[:space:]')" - else - echo "prole-db:17.7-033" - fi -} - -start() { - ensure_tools - echo "Checking dependencies..." - - # 1. k3d is running - if ! command -v k3d >/dev/null || ! k3d cluster list >/dev/null 2>&1; then - echo "ERROR: k3d is not running or not installed." >&2 - exit 1 - fi - - # 2. cnpg operator is loaded - if ! kubectl get deployment -n cnpg-system cnpg-controller-manager >/dev/null 2>&1; then - echo "ERROR: CloudNative-PG operator is not loaded." >&2 - exit 1 - fi - - # 3. openbao is configured - if ! kubectl get statefulset openbao -n "$NAMESPACE" >/dev/null 2>&1 && ! kubectl get deployment openbao -n "$NAMESPACE" >/dev/null 2>&1; then - echo "ERROR: OpenBao is not deployed." >&2 - exit 1 - fi - - # Compare latest image with deployed - local latest_image - latest_image=$(get_latest_image) - - local current_image - current_image=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.imageName}' 2>/dev/null || echo "") - - if [[ "$current_image" != "$latest_image" ]]; then - echo "Updating cluster image from '$current_image' to '$latest_image'..." - kubectl patch cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --type merge -p "{\"spec\": {\"imageName\": \"$latest_image\"}}" - # Force a rollout to ensure the new image is pulled even if it was just a tag update (though we use unique tags) - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true - else - echo "Cluster is already using the latest image: $latest_image" - fi - - echo "Starting prole-db cluster (ensuring manifest is applied)..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" -} - -stop() { - ensure_tools - echo "Stopping prole-db cluster $CNPG_CLUSTER_NAME..." - - # Identify instances - local instances - instances=$(kubectl get pods -n "$NAMESPACE" -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" -o jsonpath='{.items[*].metadata.name}') - - if [[ -z "$instances" ]]; then - echo "No instances found for cluster $CNPG_CLUSTER_NAME." - return - fi - - # Determine replicas and primary - local primary - primary=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.status.currentPrimary}') - - echo "Primary: $primary" - - # Shutdown replicas first - for pod in $instances; do - if [[ "$pod" != "$primary" ]]; then - echo "Shutting down replica $pod..." - kubectl exec -n "$NAMESPACE" "$pod" -c postgres -- psql -U postgres -c "CHECKPOINT;" || true - fi - done - - # Shutdown primary last - if [[ -n "$primary" ]]; then - echo "Shutting down primary $primary..." - kubectl exec -n "$NAMESPACE" "$primary" -c postgres -- psql -U postgres -c "CHECKPOINT;" || true - fi - - echo "Deleting cluster resource..." - kubectl delete -f "$CNPG_MANIFEST" --ignore-not-found -} - -restart() { - ensure_tools - echo "Restarting prole-db cluster..." - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -} - -deploy() { - ensure_tools - local image - if [[ "$VERSION" == "latest" ]]; then - image=$(get_latest_image) - else - image="prole-db:$VERSION" - fi - - echo "Deploying $image to cluster $CNPG_CLUSTER_NAME..." - - # If cluster doesn't exist, use init_cloudnative_pg.sh create first - if ! kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then - echo "Cluster not found. Running init_cloudnative_pg.sh create..." - bash "$SCRIPT_DIR/init_cloudnative_pg.sh" create - fi - - # Ensure image is updated if manifest has an older version - # First, apply the manifest to ensure the cluster exists/is updated - echo "Applying manifest $CNPG_MANIFEST..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" - - # Then force the specific image version via patch if different - local current_image - current_image=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.imageName}' 2>/dev/null || echo "") - if [[ "$current_image" != "$image" ]]; then - echo "Patching cluster to use image '$image' (was '$current_image')..." - kubectl patch cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --type merge -p "{\"spec\": {\"imageName\": \"$image\"}}" - # Force a rollout - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true - fi -} - -rollback() { - ensure_tools - echo "Rollback: Updating to previous image (manually specified version or fallback)..." - if [[ "$VERSION" == "latest" ]]; then - echo "Please specify a version to rollback to. Usage: $0 rollback " - exit 1 - fi - deploy -} - -backup() { - echo "Backup - tbd, when we configure s3 or other block store" -} - -reset() { - ensure_tools - echo "Resetting prole-db cluster..." - echo "Removing cnpg instances and pods..." - kubectl delete cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --ignore-not-found - - # Wait for deletion - kubectl wait --for=delete cluster/"$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --timeout=60s || true - - echo "Reinstantiating..." - deploy -} - -case "$ACTION" in - start) - start - ;; - stop) - stop - ;; - restart) - restart - ;; - deploy) - deploy - ;; - rollback) - rollback - ;; - backup) - backup - ;; - reset) - reset - ;; - *) - echo "Usage: $0 {start|stop|restart|deploy|rollback|backup|reset} [version]" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4491602512/env.sh b/MagicMock/mock.Entry().get().strip()/4491602512/env.sh deleted file mode 100755 index 3b9f538..0000000 --- a/MagicMock/mock.Entry().get().strip()/4491602512/env.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -# Prole environment configuration -# This file is generated by the installer. Source it in new shells, or execute as a wrapper: -# "$PROLE_HOME/env.sh" [args…] -# shellcheck shell=bash -export PROLE_HOME="" -export PROLE_CONF="" -export PROLE_DATA="" -export PROLE_LOGS="" -export PROLE_SERVICE="" - -# Ensure PATH works for GUI-launched shells (Docker, Ollama, etc.) -_prole_add_path() { case ":${PATH}:" in *":$1:"*) ;; *) PATH="$1:${PATH:-}" ;; esac; } -_prole_add_path "$PROLE_HOME/bin" -_prole_add_path "/opt/homebrew/bin" -_prole_add_path "/usr/local/bin" -_prole_add_path "/usr/bin" -_prole_add_path "/bin" -_prole_add_path "/usr/sbin" -_prole_add_path "/sbin" -export PATH - -# Add custom paths below if needed (examples): -# _prole_add_path "/Applications/Ollama.app/Contents/MacOS" - -# If executed with arguments, run them under this environment -if [ "$#" -gt 0 ]; then - exec "$@" -fi diff --git a/MagicMock/mock.Entry().get().strip()/4491602512/hosts.txt b/MagicMock/mock.Entry().get().strip()/4491602512/hosts.txt deleted file mode 100644 index 4e73115..0000000 --- a/MagicMock/mock.Entry().get().strip()/4491602512/hosts.txt +++ /dev/null @@ -1,15 +0,0 @@ -myrddin.prole.org 10.0.0.3 -raspberry.prole.org 10.0.0.4 -pi.prole.org 10.0.0.5 -synology.prole.org 10.0.0.203 -morgoth.prole.org 10.0.0.204 -zinfandel.prole.org 10.0.0.205 -aventage.prole.org 10.0.0.206 -retropie.prole.org 10.0.0.207 -fairyland.prole.org 10.0.0.208 -k8s.prole.org zinfandel.prole.org -mc.prole.org 73.15.20.166 -morana.prole.org 10.0.0.66 -ollama.prole.org 73.15.20.166 -svc.prole.org 73.15.20.166 -www.prole.org ghs.googlehosted.com \ No newline at end of file diff --git a/MagicMock/mock.Entry().get().strip()/4491602512/init_cloudnative_pg.sh b/MagicMock/mock.Entry().get().strip()/4491602512/init_cloudnative_pg.sh deleted file mode 100755 index 6708415..0000000 --- a/MagicMock/mock.Entry().get().strip()/4491602512/init_cloudnative_pg.sh +++ /dev/null @@ -1,326 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_cloudnative_pg.sh -# Purpose: -# - Distribute administrator ed25519 key pair to CloudNative‑PG as a Kubernetes Secret for cert auth -# - Configure Kerberos (GSSAPI) using external Kerberos KDC -# - Patch CNPG cluster to enable TLS and GSSAPI where possible -# -# Usage: -# ./init_cloudnative_pg.sh start|stop|status|restart -# ./init_cloudnative_pg.sh initialize # create/update k8s secrets/configs and patch CNPG -# ./init_cloudnative_pg.sh update|reload # re-apply/patch -# -# Requirements: -# - init_openbao.sh has been run (OpenBao running in k8s) -# - $PROLE_HOME/env.sh or $HOME/.prole/env.sh defining PROLE_SERVICE - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Load env without leaking our positional args to the env script (some env.sh may `exec "$@"`). -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -else - echo "ERROR: Missing env. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2 - exit 1 -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -if [[ -z "${PROLE_SERVICE:-}" ]]; then - echo "ERROR: PROLE_SERVICE is not defined in env." >&2 - exit 1 -fi - -ACTION=${1:-} -CNPG_CLUSTER_NAME=${2:-${CNPG_CLUSTER_NAME:-prole-db}} -NAMESPACE=${NAMESPACE:-prole} -OPENBAO_NAME=${OPENBAO_NAME:-openbao} -REALM=${REALM:-PROLE.ORG} -DOMAIN=${DOMAIN:-prole.org} -KRB5_KDC=${KRB5_KDC:-} -KRB5_ADMIN=${KRB5_ADMIN:-} - -CNPG_MANIFEST="$SCRIPT_DIR/../k8s/prole/prole-db.yaml" - -SECRETS_DIR="$PROLE_SERVICE/secrets" -ADMIN_PRIV="$SECRETS_DIR/admin_ed25519.key" -ADMIN_PUB="$SECRETS_DIR/admin_ed25519.pub" -OPENBAO_TOKEN_FILE="$SECRETS_DIR/openbao-root-token" - -ensure_tools() { - for t in kubectl curl openssl base64 jq; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -# Resolve OpenBao URL: prefer explicit env, then localhost port-forward, then cluster DNS -bao_service_url() { - if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then - echo "$PROLE_OPENBAO_URL" - return 0 - fi - # Prefer standard local port-forward managed by etc/init_port_forwards.sh - if curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then - echo "http://127.0.0.1:18200" - return 0 - fi - echo "http://$OPENBAO_NAME.$NAMESPACE.svc.cluster.local:8200" -} - -fetch_admin_keys_and_db_pass_from_bao_or_local() { - local token url - if [[ -f "$OPENBAO_TOKEN_FILE" ]]; then - token=$(cat "$OPENBAO_TOKEN_FILE") - else - token="" - fi - url=$(bao_service_url) - if [[ -n "$token" ]]; then - echo "Attempting to read admin key pair from OpenBao kv/prole/admin ..." - if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -e '.data.data' >/dev/null 2>&1; then - local priv_b64 pub_b64 - priv_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -r '.data.data.admin_private_key_b64') - pub_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -r '.data.data.admin_public_key_b64') - printf "%s" "$priv_b64" | base64 -d >"$ADMIN_PRIV" - printf "%s" "$pub_b64" | base64 -d >"$ADMIN_PUB" - chmod 0600 "$ADMIN_PRIV" - fi - - echo "Attempting to read database password from OpenBao kv/prole/db ..." - if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/db" | jq -e '.data.data' >/dev/null 2>&1; then - local db_pass - db_pass=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/db" | jq -r '.data.data.password') - if [[ -n "$db_pass" ]]; then - echo "Updating database user secret 'prole-db-user' from OpenBao ..." - kubectl create secret generic prole-db-user -n "$NAMESPACE" \ - --from-literal=username=prole \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -f - - - echo "Updating database superuser secret 'prole-db-superuser' from OpenBao ..." - kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \ - --from-literal=username=postgres \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -f - - fi - fi - fi - - if [[ -f "$ADMIN_PRIV" && -f "$ADMIN_PUB" ]]; then - echo "Using local admin key pair at $SECRETS_DIR" - return 0 - fi - echo "ERROR: Could not obtain admin key pair from OpenBao and no local files found." >&2 - exit 1 -} - -apply_cnpg_admin_secret() { - echo "Creating/updating Secret cnpg-admin-key ..." - kubectl create secret generic cnpg-admin-key -n "$NAMESPACE" \ - --from-file=admin.key="$ADMIN_PRIV" \ - --from-file=admin.pub="$ADMIN_PUB" \ - --dry-run=client -o yaml | kubectl apply -f - -} - -ensure_krb5_conf_configmap() { - echo "Creating/updating ConfigMap prole-krb5-conf ..." - - local kdc_val admin_val - if [[ -n "$KRB5_KDC" ]]; then - kdc_val="$KRB5_KDC" - admin_val=${KRB5_ADMIN:-$(echo "$KRB5_KDC" | cut -d, -f1)} - else - kdc_val="kdc.$DOMAIN" - admin_val="kdc.$DOMAIN" - fi - - local TMP - TMP=$(mktemp) - cat >"$TMP" </dev/null 2>&1; then - echo "CA secret $ca_secret_name already exists; skipping generation." - return 0 - fi - echo "Generating self-signed CA (RSA 4096) for CNPG ..." - local TMPD - TMPD=$(mktemp -d) - openssl genrsa -out "$TMPD/ca.key" 4096 - openssl req -x509 -new -key "$TMPD/ca.key" -out "$TMPD/ca.crt" -days 3650 -subj "/CN=Prole CNPG CA" - kubectl -n "$NAMESPACE" create secret generic "$ca_secret_name" \ - --from-file=ca.crt="$TMPD/ca.crt" \ - --from-file=ca.key="$TMPD/ca.key" \ - --dry-run=client -o yaml | kubectl apply -f - - rm -rf "$TMPD" -} - -patch_cnpg_cluster_for_auth() { - echo "Patching CNPG Cluster $CNPG_CLUSTER_NAME to enable GSSAPI and fix config (best-effort) ..." - # Best-effort patch; schema may vary with CNPG version. - # We remove krb_srvname as it is unrecognized in some PG 17 builds. - # We revert certificates to default to avoid operator TLS issues. - kubectl -n "$NAMESPACE" patch cluster "$CNPG_CLUSTER_NAME" --type merge -p "{ - \"spec\": { - \"postgresql\": { - \"parameters\": {\"krb_srvname\": null}, - \"pg_hba\": [ - \"local all postgres trust\", - \"host all postgres all scram-sha-256\", - \"host all all all gss include_realm=1 krb_realm=$REALM\", - \"host all all all scram-sha-256\" - ] - }, - \"certificates\": { - \"serverCASecret\": null, - \"clientCASecret\": null - } - } - }" >/dev/null || echo "Note: patch may need adjustment for your CNPG version." -} - -initialize() { - ensure_tools - - # Ensure port-forward is running for OpenBao (dependency) - echo "Ensuring port-forward for OpenBao is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart openbao - - fetch_admin_keys_and_db_pass_from_bao_or_local - apply_cnpg_admin_secret - ensure_krb5_conf_configmap - # generate_tls_if_missing - patch_cnpg_cluster_for_auth - - # Ensure port-forward is running for Postgres (local access) - echo "Ensuring port-forward for Postgres is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart postgres - - echo "Initialization complete for CNPG + Kerberos + cert artifacts." -} - -update_reload() { - initialize -} - -case "$ACTION" in - recreate) - ensure_tools - "$0" delete "$CNPG_CLUSTER_NAME" - "$0" create "$CNPG_CLUSTER_NAME" - ;; - create) - ensure_tools - echo "Installing CloudNative-PG operator ..." - kubectl apply --server-side -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.27/releases/cnpg-1.27.0.yaml - - echo "Creating CloudNative-PG cluster and resources for '$CNPG_CLUSTER_NAME' ..." - - kubectl apply -k "$SCRIPT_DIR/../k8s/prole" - - # Ensure prole-index-html exists for prole deployment readiness probe - if ! kubectl get configmap prole-index-html -n prole >/dev/null 2>&1; then - echo "Creating prole-index-html configmap..." - printf "

Prole

" > /tmp/index.html - kubectl create configmap prole-index-html --from-file=/tmp/index.html -n prole - rm /tmp/index.html - fi - - # Ensure prole-nginx-tls exists (self-signed for dev) - if ! kubectl get secret prole-nginx-tls -n prole >/dev/null 2>&1; then - echo "Generating self-signed prole-nginx-tls for development..." - openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout /tmp/nginx-tls.key -out /tmp/nginx-tls.crt \ - -subj "/CN=prole.org" >/dev/null 2>&1 - kubectl create secret tls prole-nginx-tls --key /tmp/nginx-tls.key --cert /tmp/nginx-tls.crt -n prole - rm /tmp/nginx-tls.key /tmp/nginx-tls.crt - fi - - echo "Initializing and patching cluster ..." - initialize - ;; - delete) - ensure_tools - echo "Deleting all resources for '$CNPG_CLUSTER_NAME' ..." - kubectl delete -k "$SCRIPT_DIR/../k8s/prole" --ignore-not-found - ;; - start) - ensure_tools - if [[ ! -f "$CNPG_MANIFEST" ]]; then - echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2 - exit 1 - fi - echo "Starting CloudNative-PG cluster from $CNPG_MANIFEST in namespace $NAMESPACE..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" - ;; - stop) - ensure_tools - if [[ ! -f "$CNPG_MANIFEST" ]]; then - echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2 - exit 1 - fi - echo "Stopping CloudNative-PG cluster using $CNPG_MANIFEST ..." - kubectl delete -f "$CNPG_MANIFEST" --ignore-not-found - ;; - status) - ensure_tools - echo "--- CloudNative-PG Cluster Status ($CNPG_CLUSTER_NAME) ---" - if kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then - kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" - echo "" - echo "CNPG Plugin Status:" - if kubectl cnpg version >/dev/null 2>&1; then - kubectl cnpg status "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" - else - echo "Note: 'kubectl cnpg' plugin not found; skipping detailed status." - fi - else - echo "Cluster '$CNPG_CLUSTER_NAME' not found in namespace '$NAMESPACE'." - fi - ;; - restart) - ensure_tools - "$0" stop - "$0" start - ;; - initialize) - initialize - ;; - update|reload) - update_reload - ;; - *) - echo "Usage: $0 {create|delete|recreate|start|stop|status|restart|initialize|update|reload} [dbname]" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4491602512/init_k8s.sh b/MagicMock/mock.Entry().get().strip()/4491602512/init_k8s.sh deleted file mode 100755 index 35f14aa..0000000 --- a/MagicMock/mock.Entry().get().strip()/4491602512/init_k8s.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# etc/init_k8s.sh -# Purpose: Manage k3d/k8s environment - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Preserve positional args while sourcing env -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -# Default values -VERBOSE=false -ENVIRONMENT="prod" -HOST="" -CLUSTER_NAME_DEFAULT="prole-dev-cluster" -CLUSTER_PORT_DEFAULT="6443" - -usage() { - cat </dev/null || { echo "ERROR: Missing required tool: $t" >&2; exit 1; } - done -} - -ensure_docker() { - if ! docker info >/dev/null 2>&1; then - echo "ERROR: Docker is not running." >&2 - exit 1 - fi -} - -initialize() { - ensure_tools - ensure_docker - - if [[ "$ENVIRONMENT" == "dev" ]]; then - if [[ -z "$CLUSTER_NAME" ]]; then - if [[ -t 0 ]]; then - read -p "Enter k3d cluster name [$CLUSTER_NAME_DEFAULT]: " CLUSTER_NAME - CLUSTER_NAME=${CLUSTER_NAME:-$CLUSTER_NAME_DEFAULT} - else - CLUSTER_NAME=$CLUSTER_NAME_DEFAULT - fi - fi - - if k3d cluster list "$CLUSTER_NAME" >/dev/null 2>&1; then - log "Cluster '$CLUSTER_NAME' already exists." - else - log "Creating k3d cluster '$CLUSTER_NAME'..." - k3d cluster create "$CLUSTER_NAME" -p "0.0.0.0:$CLUSTER_PORT_DEFAULT:6443@server:0" - fi - else - if [[ -n "$HOST" ]]; then - log "Environment is $ENVIRONMENT. Host is $HOST." - log "TODO: Fetch kubeconfig from $HOST (placeholder)." - else - log "Environment is $ENVIRONMENT. Use -h|--host to specify the remote cluster host." - fi - fi -} - -status() { - ensure_tools - log "Checking Docker status..." - if docker info >/dev/null 2>&1; then - echo "Docker is running." - else - echo "Docker is NOT running." - fi - - log "Listing k3d clusters..." - k3d cluster list -} - -get_cluster_name() { - # If a name was provided or exists in env, use it. - # Otherwise, if there is only one cluster, use it. - # Finally, use default. - if [[ -n "${CLUSTER_NAME:-}" ]]; then - echo "$CLUSTER_NAME" - return - fi - - local clusters - clusters=$(k3d cluster list --no-headers | awk '{print $1}') - local count - count=$(echo "$clusters" | grep -c . || true) - - if [[ "$count" -eq 1 ]]; then - echo "$clusters" - else - echo "$CLUSTER_NAME_DEFAULT" - fi -} - -case "$ACTION" in - initialize) - initialize - ;; - status) - status - ;; - start) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Starting k3d cluster '$CLUSTER_NAME'..." - k3d cluster start "$CLUSTER_NAME" - ;; - stop) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Stopping k3d cluster '$CLUSTER_NAME'..." - k3d cluster stop "$CLUSTER_NAME" - ;; - restart) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Restarting k3d cluster '$CLUSTER_NAME'..." - k3d cluster stop "$CLUSTER_NAME" - k3d cluster start "$CLUSTER_NAME" - ;; - *) - usage - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4491602512/init_openbao.sh b/MagicMock/mock.Entry().get().strip()/4491602512/init_openbao.sh deleted file mode 100755 index 3e59151..0000000 --- a/MagicMock/mock.Entry().get().strip()/4491602512/init_openbao.sh +++ /dev/null @@ -1,490 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_openbao.sh -# Purpose: -# - Deploy OpenBao to Kubernetes (dev mode) and store admin ed25519 key pair -# Generate and apply a Kerberos krb5.conf ConfigMap for an external realm -# - Local Docker helpers for OpenBao (optional) - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Preserve positional args while sourcing env -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -if [[ -z "${PROLE_SERVICE:-}" ]]; then - echo "ERROR: PROLE_SERVICE is not defined in env. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2 - exit 1 -fi - -ACTION=${1:-} - -# Defaults -NAMESPACE=${NAMESPACE:-prole} -OPENBAO_NAME=${OPENBAO_NAME:-openbao} -OPENBAO_MANIFEST_DIR="$SCRIPT_DIR/../k8s/openbao" - -# Kerberos realm defaults -KRB5_REALM=${KRB5_REALM:-PROLE.ORG} -DOMAIN=${DOMAIN:-$(echo "$KRB5_REALM" | tr 'A-Z' 'a-z')} -KRB5_KDC=${KRB5_KDC:-kdc.$DOMAIN} -KRB5_ADMIN=${KRB5_ADMIN:-} - -# Secrets and token locations -SECRETS_DIR="$PROLE_SERVICE/secrets" -mkdir -p "$SECRETS_DIR" -admin_key_priv="$SECRETS_DIR/admin_ed25519.key" -admin_key_pub="$SECRETS_DIR/admin_ed25519.pub" -root_token_file="$SECRETS_DIR/openbao-root-token" - -ensure_tools() { - for t in kubectl curl openssl base64; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -ensure_docker() { - command -v docker >/dev/null || { echo "Missing required tool: docker" >&2; exit 1; } -} - -ensure_admin_keypair() { - # Ensure admin ed25519 keypair exists and a root token is available - mkdir -p "$SECRETS_DIR" - if [[ ! -f "$admin_key_priv" || ! -f "$admin_key_pub" ]]; then - echo "Generating admin ed25519 keypair in $SECRETS_DIR ..." - openssl genpkey -algorithm ED25519 -out "$admin_key_priv" - openssl pkey -in "$admin_key_priv" -pubout -out "$admin_key_pub" - chmod 0600 "$admin_key_priv" - fi - # Ensure a root token exists for OpenBao dev server interactions - if [[ ! -f "$root_token_file" || ! -s "$root_token_file" ]]; then - openssl rand -hex 24 >"$root_token_file" - chmod 0600 "$root_token_file" - fi -} - -generate_openbao_manifests() { - mkdir -p "$OPENBAO_MANIFEST_DIR" - # Only create a simple deployment if none exists; otherwise respect current file - local deploy="$OPENBAO_MANIFEST_DIR/deployment.yaml" - if [[ ! -f "$deploy" ]]; then - cat >"$deploy" <"$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" </dev/null 2>&1; then - kubectl rollout status statefulset/$OPENBAO_NAME -n "$NAMESPACE" --timeout=120s - else - kubectl rollout status deploy/$OPENBAO_NAME -n "$NAMESPACE" --timeout=120s - fi -} - -init_openbao_kv_and_store_admin_key() { - local token - token=$(cat "$root_token_file") - # Enable kv (if not already) and write keys - local svc="http://127.0.0.1:18200" - echo "Configuring KV at $svc ..." - - # Check if kv/ is already mounted - if curl -sS -H "X-Vault-Token: $token" "$svc/v1/sys/mounts" | grep -q '"kv/":'; then - echo "KV path 'kv/' is already enabled." - else - curl -sS -H "X-Vault-Token: $token" -X POST "$svc/v1/sys/mounts/kv" \ - -d '{"type":"kv","options":{"version":"2"}}' >/dev/null 2>&1 || true - fi - - local priv pub - priv=$(base64 <"$admin_key_priv" | tr -d '\n') - pub=$(base64 <"$admin_key_pub" | tr -d '\n') - - # Check if admin key already exists and matches - local existing_data - existing_data=$(curl -sS -H "X-Vault-Token: $token" "$svc/v1/kv/data/prole/admin" 2>/dev/null || true) - if [[ -n "$existing_data" ]]; then - local ex_priv ex_pub - ex_priv=$(echo "$existing_data" | grep -o '"admin_private_key_b64":"[^"]*' | cut -d'"' -f4 || true) - ex_pub=$(echo "$existing_data" | grep -o '"admin_public_key_b64":"[^"]*' | cut -d'"' -f4 || true) - - if [[ "$ex_priv" == "$priv" && "$ex_pub" == "$pub" ]]; then - echo "Admin key pair in OpenBao kv/prole/admin is already up to date." - else - echo "Writing admin key pair to kv/prole/admin ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/admin" \ - -d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null - echo "Stored admin key pair in OpenBao kv/prole/admin." - fi - else - echo "Writing admin key pair to kv/prole/admin ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/admin" \ - -d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null - echo "Stored admin key pair in OpenBao kv/prole/admin." - fi - - # Store User SSH keys if they exist - local user_key_priv="$HOME/.ssh/id_prole_ed25519" - local user_key_pub="$HOME/.ssh/id_prole_ed25519.pub" - if [[ -f "$user_key_priv" && -f "$user_key_pub" ]]; then - local u_priv u_pub - u_priv=$(base64 <"$user_key_priv" | tr -d '\n') - u_pub=$(base64 <"$user_key_pub" | tr -d '\n') - local username=${PROLE_DB_USER:-"prole"} - - echo "Writing user SSH keys for '$username' to kv/prole/user ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/user" \ - -d "{\"data\":{\"username\":\"$username\",\"private_key_b64\":\"$u_priv\",\"public_key_b64\":\"$u_pub\"}}" >/dev/null - echo "Stored user SSH keys in OpenBao kv/prole/user." - fi - - if [[ -n "$db_pass" ]]; then - local username=${PROLE_DB_USER:-"prole"} - echo "Writing database user password for '$username' to kv/prole/db ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/db" \ - -d "{\"data\":{\"username\":\"$username\",\"password\":\"$db_pass\"}}" >/dev/null - echo "Stored database password in OpenBao kv/prole/db." - fi -} - -# Removed: prompt_admin_password_and_apply_secret (no AD admin secret required) - -docker_start() { - echo "Starting local Docker container for OpenBao ..." - ensure_docker - docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true - # OpenBao dev - local token - token=$(cat "$root_token_file" 2>/dev/null || true) - if [[ -z "$token" ]]; then - token=$(openssl rand -hex 24) - printf "%s" "$token" >"$root_token_file" - chmod 0600 "$root_token_file" - fi - docker run -d --name "$OPENBAO_NAME" -p 18200:8200 \ - "$OPENBAO_IMAGE" server -dev -dev-listen-address=0.0.0.0:8200 -dev-root-token-id="$token" - echo "Docker container started: $OPENBAO_NAME" -} - -docker_stop() { - docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true - echo "Stopped OpenBao container if it was running." -} - -docker_restart() { - docker_stop - docker_start -} - -# status command implementation wrapped in a function to avoid top-level 'local' -cmd_status() { - echo "--- init_primary_domain status ---" - echo "Namespace: $NAMESPACE" - echo "OpenBao name: $OPENBAO_NAME" - echo "Manifest dir: $OPENBAO_MANIFEST_DIR" - - # Files/manifests present - local ok=0 - if [[ -f "$OPENBAO_MANIFEST_DIR/deployment.yaml" ]]; then - echo "[OK] OpenBao deployment manifest exists" - else - echo "[MISSING] $OPENBAO_MANIFEST_DIR/deployment.yaml" - ok=1 - fi - if [[ -f "$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" ]]; then - echo "[OK] Kerberos ConfigMap manifest exists" - else - echo "[MISSING] $OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" - ok=1 - fi - - # K8s resources - if kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" >/dev/null 2>&1; then - local ready desired - ready=$(kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") - desired=$(kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0") - ready=${ready:-0} - desired=${desired:-0} - if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then - echo "[OK] OpenBao StatefulSet running ($ready/$desired ready)" - else - echo "[WARN] OpenBao StatefulSet not fully ready ($ready/$desired)" - ok=1 - fi - elif kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" >/dev/null 2>&1; then - # Get readiness - local ready desired - ready=$(kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") - desired=$(kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0") - ready=${ready:-0} - desired=${desired:-0} - if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then - echo "[OK] OpenBao Deployment running ($ready/$desired ready)" - else - echo "[WARN] OpenBao Deployment not fully ready ($ready/$desired)" - ok=1 - fi - else - echo "[MISSING] OpenBao Deployment or StatefulSet '$OPENBAO_NAME' in namespace '$NAMESPACE'" - ok=1 - fi - - if kubectl -n "$NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then - echo "[OK] Kerberos ConfigMap 'prole-krb5-conf' present" - else - echo "[MISSING] Kerberos ConfigMap 'prole-krb5-conf' in namespace '$NAMESPACE'" - ok=1 - fi - - # Docker (optional local mode) - if command -v docker >/dev/null 2>&1; then - if docker ps --format '{{.Names}}' | grep -Fxq "$OPENBAO_NAME"; then - echo "[OK] Docker container '$OPENBAO_NAME' is running" - else - # Not necessarily an error; we primarily use k8s - echo "[INFO] Docker container '$OPENBAO_NAME' not running (k8s mode may be in use)" - fi - fi - - # Secrets and tokens - if [[ -f "$admin_key_priv" && -f "$admin_key_pub" ]]; then - echo "[OK] Admin ed25519 keypair present in $SECRETS_DIR" - else - echo "[MISSING] Admin keypair files in $SECRETS_DIR" - ok=1 - fi - if [[ -s "$root_token_file" ]]; then - echo "[OK] OpenBao root token file present" - else - echo "[MISSING] OpenBao root token file ($root_token_file)" - ok=1 - fi - - # OpenBao reachability and token validity (best-effort) - # Prefer explicit override, then localhost port-forward, then cluster DNS - local svc_url - if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then - svc_url="$PROLE_OPENBAO_URL" - elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then - svc_url="http://127.0.0.1:18200" - else - svc_url="http://$OPENBAO_NAME.$NAMESPACE.svc.cluster.local:8200" - fi - if curl -sS "$svc_url/v1/sys/health" >/dev/null 2>&1; then - echo "[OK] OpenBao service reachable" - if [[ -s "$root_token_file" ]]; then - local code - code=$(curl -s -o /dev/null -w "%{http_code}" -H "X-Vault-Token: $(cat "$root_token_file")" "$svc_url/v1/sys/mounts" || echo "000") - if [[ "$code" == "200" ]]; then - echo "[OK] OpenBao root token authenticates" - else - echo "[WARN] OpenBao root token did not authenticate (HTTP $code)" - ok=1 - fi - fi - else - echo "[WARN] OpenBao service not reachable at $svc_url" - ok=1 - fi - - # Service certificate existence (CNPG TLS) - local CNPG_CLUSTER_NAME - CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db} - if kubectl -n "$NAMESPACE" get secret "${CNPG_CLUSTER_NAME}-tls" >/dev/null 2>&1; then - echo "[OK] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' exists" - else - echo "[INFO] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' not found" - fi - - echo "-------------------------------------" - if [[ $ok -eq 0 ]]; then - echo "Status: OK" - return 0 - else - echo "Status: Issues detected" - return 1 - fi -} - -case "$ACTION" in - start) - ensure_tools - ensure_docker - ensure_admin_keypair - docker_start - ;; - status) - ensure_tools - cmd_status - ;; - stop) - ensure_docker - docker_stop - ;; - restart) - ensure_tools - ensure_docker - ensure_admin_keypair - docker_restart - ;; - initialize) - ensure_tools - # Read password from stdin if provided - db_pass="" - if [[ ! -t 0 ]]; then - read -r db_pass - fi - - ensure_admin_keypair - generate_openbao_manifests - generate_kerberos_configmap - apply_k8s - - if [[ -n "$db_pass" ]]; then - local username=${PROLE_DB_USER:-"prole"} - echo "Creating database user secret 'prole-db-user' for '$username' ..." - kubectl create secret generic prole-db-user -n "$NAMESPACE" \ - --from-literal=username="$username" \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f - - - echo "Creating database superuser secret 'prole-db-superuser' ..." - kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \ - --from-literal=username=postgres \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f - - fi - - wait_for_openbao - - # Ensure port-forward is running for OpenBao - echo "Ensuring port-forward for OpenBao is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart openbao - - if ! curl -sS http://127.0.0.1:18200/v1/sys/health >/dev/null 2>&1; then - echo "ERROR: OpenBao not reachable at http://127.0.0.1:18200." >&2 - echo "Please start port-forwards: $SCRIPT_DIR/init_port_forwards.sh start openbao" >&2 - exit 1 - fi - init_openbao_kv_and_store_admin_key - echo "Initialization complete. Manifests in: $OPENBAO_MANIFEST_DIR" - ;; - update|reload) - generate_openbao_manifests - generate_kerberos_configmap - apply_k8s - echo "Re-applied manifests." - ;; - *) - echo "Usage: $0 {start|stop|status|restart|initialize|update|reload}" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4491602512/init_port_forwards.sh b/MagicMock/mock.Entry().get().strip()/4491602512/init_port_forwards.sh deleted file mode 100755 index 9ebea5c..0000000 --- a/MagicMock/mock.Entry().get().strip()/4491602512/init_port_forwards.sh +++ /dev/null @@ -1,537 +0,0 @@ -#!/usr/bin/env bash -set -u - -# init_port_forwards.sh -# Portable-ish (macOS, Ubuntu, Raspberry Pi OS, Alpine) bash init-style script -# Manages kubectl port-forward daemons defined in an XML file. - -PROG="init_port_forwards" -PROLE_HOME="${PROLE_HOME:-/Users/chrisfu/dev/prole}" -VERBOSE=0 -CONFIG_FILE="$PROLE_HOME/conf/port-mappings.properties" - -usage() { - cat < [component] - -Options: - -c, --config-file=FILE Path to local-ports.properties (XML) - -v, --verbose Verbose output - -Examples: - $PROG -c ./port-mappings.properties start - $PROG stop openbao - $PROG --verbose status -EOF -} - -TARGET_ID="" - -log() { printf '%s\n' "$*"; } -vlog() { [ "$VERBOSE" -eq 1 ] && printf '[verbose] %s\n' "$*" || true; } -err() { printf '[error] %s\n' "$*" >&2; } - -have() { command -v "$1" >/dev/null 2>&1; } - -# Choose a writable state dir for pid/log files: -# - Prefer XDG_RUNTIME_DIR if set and writable -# - Else /var/run if writable (rare without root) -# - Else ~/.local/state -# - Else /tmp -state_dir() { - if [ -n "${XDG_RUNTIME_DIR:-}" ] && [ -w "${XDG_RUNTIME_DIR:-}" ]; then - printf '%s/%s' "$XDG_RUNTIME_DIR" "$PROG" - return - fi - if [ -d "/var/run" ] && [ -w "/var/run" ]; then - printf '/var/run/%s' "$PROG" - return - fi - if [ -n "${HOME:-}" ]; then - mkdir -p "$HOME/.local/state" >/dev/null 2>&1 || true - if [ -d "$HOME/.local/state" ] && [ -w "$HOME/.local/state" ]; then - printf '%s/.local/state/%s' "$HOME" "$PROG" - return - fi - fi - printf '/tmp/%s' "$PROG" -} - -STATE_DIR="$(state_dir)" -PID_DIR="$STATE_DIR/pids" -LOG_DIR="$STATE_DIR/logs" - -ensure_dirs() { - mkdir -p "$PID_DIR" "$LOG_DIR" 2>/dev/null || true - if [ ! -d "$PID_DIR" ] || [ ! -d "$LOG_DIR" ]; then - err "Unable to create state directories under: $STATE_DIR" - exit 1 - fi -} - -# Use kubectl for actions; kubecolor is used only for prettier status output. -KUBECTL="kubectl" -detect_kubectl() { - if have kubectl; then - KUBECTL="kubectl" - else - err "kubectl not found in PATH" - exit 1 - fi -} - -# Basic environment validation -validate_env() { - detect_kubectl - ensure_dirs - - if ! "$KUBECTL" version --client >/dev/null 2>&1; then - err "kubectl seems broken or not executable" - exit 1 - fi - - # Can we reach the cluster? - if ! "$KUBECTL" cluster-info >/dev/null 2>&1; then - err "kubectl cannot reach a cluster (check KUBECONFIG/context)" - err "Try: kubectl config get-contexts && kubectl config use-context " - exit 1 - fi -} - -# ---- Preflight: Docker + k3d awareness ---- -# Return 0 when Docker CLI can talk to a running daemon. -docker_is_running() { - if ! have docker; then - return 1 - fi - docker info >/dev/null 2>&1 -} - -# Hard-fail with clean guidance when Docker is not up (used for start/restart). -ensure_docker_running() { - if docker_is_running; then - return 0 - fi - if ! have docker; then - err "Docker CLI not found. Please install Docker Desktop or docker CLI." - else - err "Docker is not running. Start Docker Desktop and wait until it is ready." - fi - err "Tip (macOS): open -a Docker" - err "Then re-run: $PROG start" - exit 3 -} - -# Detect k3d cluster name from env or current kubectl context. -# - If K3D_CLUSTER is set, use it. -# - Else, if current-context starts with 'k3d-', strip prefix to get name. -detect_k3d_context() { - if [ -n "${K3D_CLUSTER:-}" ]; then - printf '%s' "$K3D_CLUSTER" - return 0 - fi - local ctx - ctx="$($KUBECTL config current-context 2>/dev/null || echo)" - case "$ctx" in - k3d-*) printf '%s' "${ctx#k3d-}"; return 0 ;; - *) return 1 ;; - esac -} - -# Return 0 if the given k3d cluster exists and is running. -k3d_cluster_is_running() { - local name="$1" - have k3d || return 1 - # Use json output when available; fall back to grep otherwise - if k3d cluster list -o json >/dev/null 2>&1; then - k3d cluster list -o json 2>/dev/null | grep -q '"name"\s*:\s*"'"$name"'"' && \ - k3d cluster list -o json 2>/dev/null | sed -n 's/.*"name"\s*:\s*"\([^"]\+\)".*"serversRunning"\s*:\s*\([0-9]\+\).*/\1 \2/p' | awk -v n="$name" '$1==n {exit ($2>0)?0:1}' - return $? - else - k3d cluster list 2>/dev/null | grep -E "^$name\s" | grep -q running - return $? - fi -} - -# If current context indicates k3d, ensure the cluster is up; otherwise no-op. -ensure_k3d_ready_if_applicable() { - local k3d_name - if ! k3d_name="$(detect_k3d_context)"; then - return 0 - fi - if ! have k3d; then - err "k3d is not installed but kubectl context suggests k3d (context=$("$KUBECTL" config current-context))." - err "Install k3d: brew install k3d (macOS)" - err "Or switch context: kubectl config use-context " - exit 4 - fi - if ! k3d_cluster_is_running "$k3d_name"; then - err "k3d cluster '$k3d_name' is not running." - err "Start it: k3d cluster start $k3d_name" - err "Then re-run: $PROG start" - exit 4 - fi -} - -pid_file_for() { printf '%s/%s.pid' "$PID_DIR" "$1"; } -log_file_for() { printf '%s/%s.log' "$LOG_DIR" "$1"; } - -is_pid_running() { - # Return 0 if pid exists and running, else 1 - # kill -0 is portable. - local pid="$1" - [ -n "$pid" ] && kill -0 "$pid" >/dev/null 2>&1 -} - -read_pid() { - local pf="$1" - [ -f "$pf" ] || return 1 - # shellcheck disable=SC2162 - read pid <"$pf" || return 1 - printf '%s' "$pid" -} - -write_pid() { - local pf="$1" pid="$2" - printf '%s\n' "$pid" >"$pf" -} - -remove_pidfile() { - local pf="$1" - rm -f "$pf" >/dev/null 2>&1 || true -} - -# ---- XML parsing (simple attribute extraction) ---- -# We parse lines containing: -# Attributes must use double quotes in the XML (as in the sample). -get_attr() { - # $1 = line, $2 = attribute name - # outputs value or empty - printf '%s\n' "$1" | sed -n "s/.*$2=\"\([^\"]*\)\".*/\1/p" -} - -foreach_mapping() { - # Calls a provided function with mapping fields: - # callback id ns target address hostPort servicePort protocol description - local callback="$1" - [ -f "$CONFIG_FILE" ] || { err "Config file not found: $CONFIG_FILE"; exit 1; } - - # Support both single-line and multi-line self-closing mapping tags, e.g.: - # OR lines spanning multiple lines until "/>". - local in_mapping=0 in_comment=0 buffer="" line - while IFS= read -r line; do - # Handle XML comments: skip anything between - if [ $in_comment -eq 1 ]; then - case "$line" in - *"-->"*) in_comment=0; continue ;; - *) continue ;; - esac - fi - case "$line" in - *""*) - # single-line comment; skip line - continue - ;; - *) - in_comment=1 - continue - ;; - esac - ;; - esac - if [ $in_mapping -eq 0 ]; then - case "$line" in - *"' - buffer="$buffer $line" - fi - - if [ $in_mapping -eq 1 ] && printf '%s' "$line" | grep -q "/>"; then - # Normalize whitespace to make attribute extraction robust - local merged - merged=$(printf '%s\n' "$buffer" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g') - - local id ns target address hostPort servicePort protocol description - id="$(get_attr "$merged" "id")" - ns="$(get_attr "$merged" "namespace")" - target="$(get_attr "$merged" "target")" - address="$(get_attr "$merged" "address")" - hostPort="$(get_attr "$merged" "hostPort")" - servicePort="$(get_attr "$merged" "servicePort")" - protocol="$(get_attr "$merged" "protocol")" - description="$(get_attr "$merged" "description")" - - # Basic validation - if [ -z "$id" ] || [ -z "$ns" ] || [ -z "$target" ] || [ -z "$hostPort" ] || [ -z "$servicePort" ]; then - err "Invalid mapping (missing required attributes): $merged" - exit 1 - fi - - # Filter by TARGET_ID if set - if [ -n "$TARGET_ID" ] && [ "$id" != "$TARGET_ID" ]; then - in_mapping=0 - buffer="" - continue - fi - - if [ -z "$address" ]; then address="127.0.0.1"; fi - if [ -z "$protocol" ]; then protocol="TCP"; fi - - vlog "mapping: id=$id ns=$ns target=$target address=$address hostPort=$hostPort servicePort=$servicePort protocol=$protocol" - "$callback" "$id" "$ns" "$target" "$address" "$hostPort" "$servicePort" "$protocol" "$description" - - # Reset accumulator - in_mapping=0 - buffer="" - fi - done <"$CONFIG_FILE" -} - -build_port_forward_cmd() { - # echo a command string - local ns="$1" target="$2" address="$3" hostPort="$4" servicePort="$5" - # kubectl port-forward -n --address : - printf '%s port-forward -n %s --address %s %s %s:%s' \ - "$KUBECTL" "$ns" "$address" "$target" "$hostPort" "$servicePort" -} - -start_port_forward() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - local pidfile logfile cmd pid - - # Aggressively stop existing processes before starting to avoid port conflicts - stop_port_forward "$id" "$ns" "$target" "$address" "$hostPort" "$servicePort" "$protocol" "$description" - - pidfile="$(pid_file_for "$id")" - logfile="$(log_file_for "$id")" - cmd="$(build_port_forward_cmd "$ns" "$target" "$address" "$hostPort" "$servicePort")" - - log "Starting: $id $address:$hostPort -> $target:$servicePort ($ns) ${description:-}" - vlog "Command: $cmd" - - # Start in background, keep output in log. - # nohup is available on macOS/Linux; redirect stdin from /dev/null to detach. - nohup sh -c "$cmd" >>"$logfile" 2>&1 /dev/null 2>&1 || true - - # wait a moment, then SIGKILL if needed - local i - for i in 1 2 3 4 5; do - if ! is_pid_running "$pid"; then break; fi - sleep 0.2 - done - if is_pid_running "$pid"; then - err "$id did not stop gracefully; sending SIGKILL" - kill -9 "$pid" >/dev/null 2>&1 || true - fi - else - if [ -f "$pidfile" ]; then - vlog "$id stale pidfile (pid=$pid not running)" - fi - fi - remove_pidfile "$pidfile" - - # Aggressively remove any other matching kubectl port-forward processes - # Search for processes that match: kubectl port-forward -n ... : - local extra_pids - extra_pids=$(ps -ef | grep "port-forward" | grep "\-n" | grep "$ns" | grep "$target" | grep "$hostPort:$servicePort" | grep -v grep | awk '{print $2}') - - for epid in $extra_pids; do - if [ "$epid" != "$pid" ]; then - log "Cleaning up orphan process for $id (pid=$epid)" - kill -9 "$epid" >/dev/null 2>&1 || true - fi - done -} - -status_one() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - local pidfile pid - - pidfile="$(pid_file_for "$id")" - pid="" - if [ -f "$pidfile" ]; then - pid="$(read_pid "$pidfile" || true)" - fi - - if [ -n "${pid:-}" ] && is_pid_running "$pid"; then - printf 'RUNNING %-12s pid=%-7s %s:%s -> %s:%s ns=%s %s\n' \ - "$id" "$pid" "$address" "$hostPort" "$target" "$servicePort" "$ns" "${description:-}" - # ps details (portable flags vary; use a conservative format) - ps -p "$pid" -o pid=,ppid=,etime=,command= 2>/dev/null | sed 's/^/ /' || true - else - printf 'STOPPED %-12s (no live pid) %s:%s -> %s:%s ns=%s %s\n' \ - "$id" "$address" "$hostPort" "$target" "$servicePort" "$ns" "${description:-}" - fi -} - -do_start() { - validate_env - # Preflight: require Docker daemon and k3d (if applicable) - ensure_docker_running - ensure_k3d_ready_if_applicable - foreach_mapping start_port_forward -} - -do_stop() { - # stop doesn't require cluster access, but it does need state dirs - ensure_dirs - foreach_mapping stop_port_forward -} - -do_restart() { - validate_env - # Preflight: require Docker daemon and k3d (if applicable) - ensure_docker_running - ensure_k3d_ready_if_applicable - foreach_mapping stop_port_forward - foreach_mapping start_port_forward -} - -do_status() { - validate_env - # Non-fatal awareness messages - if docker_is_running; then - log "[OK] Docker daemon is running" - else - if have docker; then - log "[WARN] Docker is not running" - else - log "[WARN] Docker CLI not found" - fi - fi - - local _k3d_name - if _k3d_name="$(detect_k3d_context)"; then - if have k3d; then - if k3d_cluster_is_running "$_k3d_name"; then - log "[OK] k3d cluster '$_k3d_name' is running" - else - log "[WARN] k3d cluster '$_k3d_name' is not running" - fi - else - log "[WARN] k3d not installed but context suggests k3d (cluster='$_k3d_name')" - fi - fi - - log "== Context / Cluster ==" - "$KUBECTL" config current-context 2>/dev/null | sed 's/^/ context: /' || true - "$KUBECTL" cluster-info 2>/dev/null | sed 's/^/ /' || true - log "" - - log "== Port-forward processes ==" - foreach_mapping status_one - log "" - - log "== Quick k3d awareness checks (best-effort) ==" - # If user is on k3d, current-context often includes k3d-... but not guaranteed. - # Show nodes + a few namespaces/services related to mappings (best effort). - "$KUBECTL" get nodes -o wide 2>/dev/null | sed 's/^/ /' || true - log "" - "$KUBECTL" get ns 2>/dev/null | sed 's/^/ /' || true - log "" - - # For each mapping, try to show target existence - log "== Target existence (best-effort) ==" - _target_check() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - # kubectl get -n - # If target is like "svc/name", "deploy/name", etc. - if "$KUBECTL" get -n "$ns" "$target" >/dev/null 2>&1; then - printf 'OK %-12s %s (ns=%s)\n' "$id" "$target" "$ns" - else - printf 'MISSING %-12s %s (ns=%s)\n' "$id" "$target" "$ns" - fi - } - foreach_mapping _target_check -} - -# ---- arg parsing ---- -ACTION="" - -while [ $# -gt 0 ]; do - case "$1" in - start|stop|restart|status) - if [ -z "$ACTION" ]; then - ACTION="$1" - else - TARGET_ID="$1" - fi - shift - ;; - -v|--verbose) - VERBOSE=1 - shift - ;; - -c) - shift - [ $# -gt 0 ] || { err "-c requires a file path"; usage; exit 2; } - CONFIG_FILE="$1" - shift - ;; - --config-file=*) - CONFIG_FILE="${1#*=}" - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - if [ -z "$ACTION" ]; then - err "Unknown arg: $1" - usage - exit 2 - fi - TARGET_ID="$1" - shift - ;; - esac -done - -[ -n "$ACTION" ] || { usage; exit 2; } - -case "$ACTION" in - start) do_start ;; - stop) do_stop ;; - restart) do_restart ;; - status) do_status ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4491602512/init_prole-db.sh b/MagicMock/mock.Entry().get().strip()/4491602512/init_prole-db.sh deleted file mode 100755 index b87de05..0000000 --- a/MagicMock/mock.Entry().get().strip()/4491602512/init_prole-db.sh +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_prole-db.sh -# Purpose: -# - Manage prole-db CloudNative-PG cluster operations (deploy, start, stop, etc.) - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -ACTION=${1:-} -VERSION=${2:-latest} - -# Load env -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - source "$HOME/.prole/env.sh" -fi - -CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db} -NAMESPACE=${NAMESPACE:-prole} - -CNPG_MANIFEST="$SCRIPT_DIR/../k8s/prole/prole-db.yaml" - -ensure_tools() { - for t in kubectl jq; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -get_latest_image() { - local version_file="$SCRIPT_DIR/../conf/postgresql/.version" - if [[ -f "$version_file" ]]; then - echo "prole-db:$(cat "$version_file" | tr -d '[:space:]')" - else - echo "prole-db:17.7-033" - fi -} - -start() { - ensure_tools - echo "Checking dependencies..." - - # 1. k3d is running - if ! command -v k3d >/dev/null || ! k3d cluster list >/dev/null 2>&1; then - echo "ERROR: k3d is not running or not installed." >&2 - exit 1 - fi - - # 2. cnpg operator is loaded - if ! kubectl get deployment -n cnpg-system cnpg-controller-manager >/dev/null 2>&1; then - echo "ERROR: CloudNative-PG operator is not loaded." >&2 - exit 1 - fi - - # 3. openbao is configured - if ! kubectl get statefulset openbao -n "$NAMESPACE" >/dev/null 2>&1 && ! kubectl get deployment openbao -n "$NAMESPACE" >/dev/null 2>&1; then - echo "ERROR: OpenBao is not deployed." >&2 - exit 1 - fi - - # Compare latest image with deployed - local latest_image - latest_image=$(get_latest_image) - - local current_image - current_image=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.imageName}' 2>/dev/null || echo "") - - if [[ "$current_image" != "$latest_image" ]]; then - echo "Updating cluster image from '$current_image' to '$latest_image'..." - kubectl patch cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --type merge -p "{\"spec\": {\"imageName\": \"$latest_image\"}}" - # Force a rollout to ensure the new image is pulled even if it was just a tag update (though we use unique tags) - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true - else - echo "Cluster is already using the latest image: $latest_image" - fi - - echo "Starting prole-db cluster (ensuring manifest is applied)..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" -} - -stop() { - ensure_tools - echo "Stopping prole-db cluster $CNPG_CLUSTER_NAME..." - - # Identify instances - local instances - instances=$(kubectl get pods -n "$NAMESPACE" -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" -o jsonpath='{.items[*].metadata.name}') - - if [[ -z "$instances" ]]; then - echo "No instances found for cluster $CNPG_CLUSTER_NAME." - return - fi - - # Determine replicas and primary - local primary - primary=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.status.currentPrimary}') - - echo "Primary: $primary" - - # Shutdown replicas first - for pod in $instances; do - if [[ "$pod" != "$primary" ]]; then - echo "Shutting down replica $pod..." - kubectl exec -n "$NAMESPACE" "$pod" -c postgres -- psql -U postgres -c "CHECKPOINT;" || true - fi - done - - # Shutdown primary last - if [[ -n "$primary" ]]; then - echo "Shutting down primary $primary..." - kubectl exec -n "$NAMESPACE" "$primary" -c postgres -- psql -U postgres -c "CHECKPOINT;" || true - fi - - echo "Deleting cluster resource..." - kubectl delete -f "$CNPG_MANIFEST" --ignore-not-found -} - -restart() { - ensure_tools - echo "Restarting prole-db cluster..." - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -} - -deploy() { - ensure_tools - local image - if [[ "$VERSION" == "latest" ]]; then - image=$(get_latest_image) - else - image="prole-db:$VERSION" - fi - - echo "Deploying $image to cluster $CNPG_CLUSTER_NAME..." - - # If cluster doesn't exist, use init_cloudnative_pg.sh create first - if ! kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then - echo "Cluster not found. Running init_cloudnative_pg.sh create..." - bash "$SCRIPT_DIR/init_cloudnative_pg.sh" create - fi - - # Ensure image is updated if manifest has an older version - # First, apply the manifest to ensure the cluster exists/is updated - echo "Applying manifest $CNPG_MANIFEST..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" - - # Then force the specific image version via patch if different - local current_image - current_image=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.imageName}' 2>/dev/null || echo "") - if [[ "$current_image" != "$image" ]]; then - echo "Patching cluster to use image '$image' (was '$current_image')..." - kubectl patch cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --type merge -p "{\"spec\": {\"imageName\": \"$image\"}}" - # Force a rollout - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true - fi -} - -rollback() { - ensure_tools - echo "Rollback: Updating to previous image (manually specified version or fallback)..." - if [[ "$VERSION" == "latest" ]]; then - echo "Please specify a version to rollback to. Usage: $0 rollback " - exit 1 - fi - deploy -} - -backup() { - echo "Backup - tbd, when we configure s3 or other block store" -} - -reset() { - ensure_tools - echo "Resetting prole-db cluster..." - echo "Removing cnpg instances and pods..." - kubectl delete cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --ignore-not-found - - # Wait for deletion - kubectl wait --for=delete cluster/"$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --timeout=60s || true - - echo "Reinstantiating..." - deploy -} - -case "$ACTION" in - start) - start - ;; - stop) - stop - ;; - restart) - restart - ;; - deploy) - deploy - ;; - rollback) - rollback - ;; - backup) - backup - ;; - reset) - reset - ;; - *) - echo "Usage: $0 {start|stop|restart|deploy|rollback|backup|reset} [version]" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4494452656/env.sh b/MagicMock/mock.Entry().get().strip()/4494452656/env.sh deleted file mode 100755 index e1469ac..0000000 --- a/MagicMock/mock.Entry().get().strip()/4494452656/env.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -# Prole environment configuration -# This file is generated by the installer. Source it in new shells, or execute as a wrapper: -# "$PROLE_HOME/env.sh" [args…] -# shellcheck shell=bash -export PROLE_HOME="" -export PROLE_CONF="" -export PROLE_DATA="" -export PROLE_LOGS="" -export PROLE_SERVICE="" - -# Ensure PATH works for GUI-launched shells (Docker, Ollama, etc.) -_prole_add_path() { case ":${PATH}:" in *":$1:"*) ;; *) PATH="$1:${PATH:-}" ;; esac; } -_prole_add_path "$PROLE_HOME/bin" -_prole_add_path "/opt/homebrew/bin" -_prole_add_path "/usr/local/bin" -_prole_add_path "/usr/bin" -_prole_add_path "/bin" -_prole_add_path "/usr/sbin" -_prole_add_path "/sbin" -export PATH - -# Add custom paths below if needed (examples): -# _prole_add_path "/Applications/Ollama.app/Contents/MacOS" - -# If executed with arguments, run them under this environment -if [ "$#" -gt 0 ]; then - exec "$@" -fi diff --git a/MagicMock/mock.Entry().get().strip()/4494452656/hosts.txt b/MagicMock/mock.Entry().get().strip()/4494452656/hosts.txt deleted file mode 100644 index 4e73115..0000000 --- a/MagicMock/mock.Entry().get().strip()/4494452656/hosts.txt +++ /dev/null @@ -1,15 +0,0 @@ -myrddin.prole.org 10.0.0.3 -raspberry.prole.org 10.0.0.4 -pi.prole.org 10.0.0.5 -synology.prole.org 10.0.0.203 -morgoth.prole.org 10.0.0.204 -zinfandel.prole.org 10.0.0.205 -aventage.prole.org 10.0.0.206 -retropie.prole.org 10.0.0.207 -fairyland.prole.org 10.0.0.208 -k8s.prole.org zinfandel.prole.org -mc.prole.org 73.15.20.166 -morana.prole.org 10.0.0.66 -ollama.prole.org 73.15.20.166 -svc.prole.org 73.15.20.166 -www.prole.org ghs.googlehosted.com \ No newline at end of file diff --git a/MagicMock/mock.Entry().get().strip()/4494452656/init_cloudnative_pg.sh b/MagicMock/mock.Entry().get().strip()/4494452656/init_cloudnative_pg.sh deleted file mode 100755 index 6708415..0000000 --- a/MagicMock/mock.Entry().get().strip()/4494452656/init_cloudnative_pg.sh +++ /dev/null @@ -1,326 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_cloudnative_pg.sh -# Purpose: -# - Distribute administrator ed25519 key pair to CloudNative‑PG as a Kubernetes Secret for cert auth -# - Configure Kerberos (GSSAPI) using external Kerberos KDC -# - Patch CNPG cluster to enable TLS and GSSAPI where possible -# -# Usage: -# ./init_cloudnative_pg.sh start|stop|status|restart -# ./init_cloudnative_pg.sh initialize # create/update k8s secrets/configs and patch CNPG -# ./init_cloudnative_pg.sh update|reload # re-apply/patch -# -# Requirements: -# - init_openbao.sh has been run (OpenBao running in k8s) -# - $PROLE_HOME/env.sh or $HOME/.prole/env.sh defining PROLE_SERVICE - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Load env without leaking our positional args to the env script (some env.sh may `exec "$@"`). -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -else - echo "ERROR: Missing env. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2 - exit 1 -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -if [[ -z "${PROLE_SERVICE:-}" ]]; then - echo "ERROR: PROLE_SERVICE is not defined in env." >&2 - exit 1 -fi - -ACTION=${1:-} -CNPG_CLUSTER_NAME=${2:-${CNPG_CLUSTER_NAME:-prole-db}} -NAMESPACE=${NAMESPACE:-prole} -OPENBAO_NAME=${OPENBAO_NAME:-openbao} -REALM=${REALM:-PROLE.ORG} -DOMAIN=${DOMAIN:-prole.org} -KRB5_KDC=${KRB5_KDC:-} -KRB5_ADMIN=${KRB5_ADMIN:-} - -CNPG_MANIFEST="$SCRIPT_DIR/../k8s/prole/prole-db.yaml" - -SECRETS_DIR="$PROLE_SERVICE/secrets" -ADMIN_PRIV="$SECRETS_DIR/admin_ed25519.key" -ADMIN_PUB="$SECRETS_DIR/admin_ed25519.pub" -OPENBAO_TOKEN_FILE="$SECRETS_DIR/openbao-root-token" - -ensure_tools() { - for t in kubectl curl openssl base64 jq; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -# Resolve OpenBao URL: prefer explicit env, then localhost port-forward, then cluster DNS -bao_service_url() { - if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then - echo "$PROLE_OPENBAO_URL" - return 0 - fi - # Prefer standard local port-forward managed by etc/init_port_forwards.sh - if curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then - echo "http://127.0.0.1:18200" - return 0 - fi - echo "http://$OPENBAO_NAME.$NAMESPACE.svc.cluster.local:8200" -} - -fetch_admin_keys_and_db_pass_from_bao_or_local() { - local token url - if [[ -f "$OPENBAO_TOKEN_FILE" ]]; then - token=$(cat "$OPENBAO_TOKEN_FILE") - else - token="" - fi - url=$(bao_service_url) - if [[ -n "$token" ]]; then - echo "Attempting to read admin key pair from OpenBao kv/prole/admin ..." - if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -e '.data.data' >/dev/null 2>&1; then - local priv_b64 pub_b64 - priv_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -r '.data.data.admin_private_key_b64') - pub_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/admin" | jq -r '.data.data.admin_public_key_b64') - printf "%s" "$priv_b64" | base64 -d >"$ADMIN_PRIV" - printf "%s" "$pub_b64" | base64 -d >"$ADMIN_PUB" - chmod 0600 "$ADMIN_PRIV" - fi - - echo "Attempting to read database password from OpenBao kv/prole/db ..." - if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/db" | jq -e '.data.data' >/dev/null 2>&1; then - local db_pass - db_pass=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/prole/db" | jq -r '.data.data.password') - if [[ -n "$db_pass" ]]; then - echo "Updating database user secret 'prole-db-user' from OpenBao ..." - kubectl create secret generic prole-db-user -n "$NAMESPACE" \ - --from-literal=username=prole \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -f - - - echo "Updating database superuser secret 'prole-db-superuser' from OpenBao ..." - kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \ - --from-literal=username=postgres \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -f - - fi - fi - fi - - if [[ -f "$ADMIN_PRIV" && -f "$ADMIN_PUB" ]]; then - echo "Using local admin key pair at $SECRETS_DIR" - return 0 - fi - echo "ERROR: Could not obtain admin key pair from OpenBao and no local files found." >&2 - exit 1 -} - -apply_cnpg_admin_secret() { - echo "Creating/updating Secret cnpg-admin-key ..." - kubectl create secret generic cnpg-admin-key -n "$NAMESPACE" \ - --from-file=admin.key="$ADMIN_PRIV" \ - --from-file=admin.pub="$ADMIN_PUB" \ - --dry-run=client -o yaml | kubectl apply -f - -} - -ensure_krb5_conf_configmap() { - echo "Creating/updating ConfigMap prole-krb5-conf ..." - - local kdc_val admin_val - if [[ -n "$KRB5_KDC" ]]; then - kdc_val="$KRB5_KDC" - admin_val=${KRB5_ADMIN:-$(echo "$KRB5_KDC" | cut -d, -f1)} - else - kdc_val="kdc.$DOMAIN" - admin_val="kdc.$DOMAIN" - fi - - local TMP - TMP=$(mktemp) - cat >"$TMP" </dev/null 2>&1; then - echo "CA secret $ca_secret_name already exists; skipping generation." - return 0 - fi - echo "Generating self-signed CA (RSA 4096) for CNPG ..." - local TMPD - TMPD=$(mktemp -d) - openssl genrsa -out "$TMPD/ca.key" 4096 - openssl req -x509 -new -key "$TMPD/ca.key" -out "$TMPD/ca.crt" -days 3650 -subj "/CN=Prole CNPG CA" - kubectl -n "$NAMESPACE" create secret generic "$ca_secret_name" \ - --from-file=ca.crt="$TMPD/ca.crt" \ - --from-file=ca.key="$TMPD/ca.key" \ - --dry-run=client -o yaml | kubectl apply -f - - rm -rf "$TMPD" -} - -patch_cnpg_cluster_for_auth() { - echo "Patching CNPG Cluster $CNPG_CLUSTER_NAME to enable GSSAPI and fix config (best-effort) ..." - # Best-effort patch; schema may vary with CNPG version. - # We remove krb_srvname as it is unrecognized in some PG 17 builds. - # We revert certificates to default to avoid operator TLS issues. - kubectl -n "$NAMESPACE" patch cluster "$CNPG_CLUSTER_NAME" --type merge -p "{ - \"spec\": { - \"postgresql\": { - \"parameters\": {\"krb_srvname\": null}, - \"pg_hba\": [ - \"local all postgres trust\", - \"host all postgres all scram-sha-256\", - \"host all all all gss include_realm=1 krb_realm=$REALM\", - \"host all all all scram-sha-256\" - ] - }, - \"certificates\": { - \"serverCASecret\": null, - \"clientCASecret\": null - } - } - }" >/dev/null || echo "Note: patch may need adjustment for your CNPG version." -} - -initialize() { - ensure_tools - - # Ensure port-forward is running for OpenBao (dependency) - echo "Ensuring port-forward for OpenBao is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart openbao - - fetch_admin_keys_and_db_pass_from_bao_or_local - apply_cnpg_admin_secret - ensure_krb5_conf_configmap - # generate_tls_if_missing - patch_cnpg_cluster_for_auth - - # Ensure port-forward is running for Postgres (local access) - echo "Ensuring port-forward for Postgres is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart postgres - - echo "Initialization complete for CNPG + Kerberos + cert artifacts." -} - -update_reload() { - initialize -} - -case "$ACTION" in - recreate) - ensure_tools - "$0" delete "$CNPG_CLUSTER_NAME" - "$0" create "$CNPG_CLUSTER_NAME" - ;; - create) - ensure_tools - echo "Installing CloudNative-PG operator ..." - kubectl apply --server-side -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.27/releases/cnpg-1.27.0.yaml - - echo "Creating CloudNative-PG cluster and resources for '$CNPG_CLUSTER_NAME' ..." - - kubectl apply -k "$SCRIPT_DIR/../k8s/prole" - - # Ensure prole-index-html exists for prole deployment readiness probe - if ! kubectl get configmap prole-index-html -n prole >/dev/null 2>&1; then - echo "Creating prole-index-html configmap..." - printf "

Prole

" > /tmp/index.html - kubectl create configmap prole-index-html --from-file=/tmp/index.html -n prole - rm /tmp/index.html - fi - - # Ensure prole-nginx-tls exists (self-signed for dev) - if ! kubectl get secret prole-nginx-tls -n prole >/dev/null 2>&1; then - echo "Generating self-signed prole-nginx-tls for development..." - openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout /tmp/nginx-tls.key -out /tmp/nginx-tls.crt \ - -subj "/CN=prole.org" >/dev/null 2>&1 - kubectl create secret tls prole-nginx-tls --key /tmp/nginx-tls.key --cert /tmp/nginx-tls.crt -n prole - rm /tmp/nginx-tls.key /tmp/nginx-tls.crt - fi - - echo "Initializing and patching cluster ..." - initialize - ;; - delete) - ensure_tools - echo "Deleting all resources for '$CNPG_CLUSTER_NAME' ..." - kubectl delete -k "$SCRIPT_DIR/../k8s/prole" --ignore-not-found - ;; - start) - ensure_tools - if [[ ! -f "$CNPG_MANIFEST" ]]; then - echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2 - exit 1 - fi - echo "Starting CloudNative-PG cluster from $CNPG_MANIFEST in namespace $NAMESPACE..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" - ;; - stop) - ensure_tools - if [[ ! -f "$CNPG_MANIFEST" ]]; then - echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2 - exit 1 - fi - echo "Stopping CloudNative-PG cluster using $CNPG_MANIFEST ..." - kubectl delete -f "$CNPG_MANIFEST" --ignore-not-found - ;; - status) - ensure_tools - echo "--- CloudNative-PG Cluster Status ($CNPG_CLUSTER_NAME) ---" - if kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then - kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" - echo "" - echo "CNPG Plugin Status:" - if kubectl cnpg version >/dev/null 2>&1; then - kubectl cnpg status "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" - else - echo "Note: 'kubectl cnpg' plugin not found; skipping detailed status." - fi - else - echo "Cluster '$CNPG_CLUSTER_NAME' not found in namespace '$NAMESPACE'." - fi - ;; - restart) - ensure_tools - "$0" stop - "$0" start - ;; - initialize) - initialize - ;; - update|reload) - update_reload - ;; - *) - echo "Usage: $0 {create|delete|recreate|start|stop|status|restart|initialize|update|reload} [dbname]" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4494452656/init_k8s.sh b/MagicMock/mock.Entry().get().strip()/4494452656/init_k8s.sh deleted file mode 100755 index 35f14aa..0000000 --- a/MagicMock/mock.Entry().get().strip()/4494452656/init_k8s.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# etc/init_k8s.sh -# Purpose: Manage k3d/k8s environment - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Preserve positional args while sourcing env -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -# Default values -VERBOSE=false -ENVIRONMENT="prod" -HOST="" -CLUSTER_NAME_DEFAULT="prole-dev-cluster" -CLUSTER_PORT_DEFAULT="6443" - -usage() { - cat </dev/null || { echo "ERROR: Missing required tool: $t" >&2; exit 1; } - done -} - -ensure_docker() { - if ! docker info >/dev/null 2>&1; then - echo "ERROR: Docker is not running." >&2 - exit 1 - fi -} - -initialize() { - ensure_tools - ensure_docker - - if [[ "$ENVIRONMENT" == "dev" ]]; then - if [[ -z "$CLUSTER_NAME" ]]; then - if [[ -t 0 ]]; then - read -p "Enter k3d cluster name [$CLUSTER_NAME_DEFAULT]: " CLUSTER_NAME - CLUSTER_NAME=${CLUSTER_NAME:-$CLUSTER_NAME_DEFAULT} - else - CLUSTER_NAME=$CLUSTER_NAME_DEFAULT - fi - fi - - if k3d cluster list "$CLUSTER_NAME" >/dev/null 2>&1; then - log "Cluster '$CLUSTER_NAME' already exists." - else - log "Creating k3d cluster '$CLUSTER_NAME'..." - k3d cluster create "$CLUSTER_NAME" -p "0.0.0.0:$CLUSTER_PORT_DEFAULT:6443@server:0" - fi - else - if [[ -n "$HOST" ]]; then - log "Environment is $ENVIRONMENT. Host is $HOST." - log "TODO: Fetch kubeconfig from $HOST (placeholder)." - else - log "Environment is $ENVIRONMENT. Use -h|--host to specify the remote cluster host." - fi - fi -} - -status() { - ensure_tools - log "Checking Docker status..." - if docker info >/dev/null 2>&1; then - echo "Docker is running." - else - echo "Docker is NOT running." - fi - - log "Listing k3d clusters..." - k3d cluster list -} - -get_cluster_name() { - # If a name was provided or exists in env, use it. - # Otherwise, if there is only one cluster, use it. - # Finally, use default. - if [[ -n "${CLUSTER_NAME:-}" ]]; then - echo "$CLUSTER_NAME" - return - fi - - local clusters - clusters=$(k3d cluster list --no-headers | awk '{print $1}') - local count - count=$(echo "$clusters" | grep -c . || true) - - if [[ "$count" -eq 1 ]]; then - echo "$clusters" - else - echo "$CLUSTER_NAME_DEFAULT" - fi -} - -case "$ACTION" in - initialize) - initialize - ;; - status) - status - ;; - start) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Starting k3d cluster '$CLUSTER_NAME'..." - k3d cluster start "$CLUSTER_NAME" - ;; - stop) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Stopping k3d cluster '$CLUSTER_NAME'..." - k3d cluster stop "$CLUSTER_NAME" - ;; - restart) - ensure_tools - CLUSTER_NAME=$(get_cluster_name) - log "Restarting k3d cluster '$CLUSTER_NAME'..." - k3d cluster stop "$CLUSTER_NAME" - k3d cluster start "$CLUSTER_NAME" - ;; - *) - usage - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4494452656/init_openbao.sh b/MagicMock/mock.Entry().get().strip()/4494452656/init_openbao.sh deleted file mode 100755 index 3e59151..0000000 --- a/MagicMock/mock.Entry().get().strip()/4494452656/init_openbao.sh +++ /dev/null @@ -1,490 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_openbao.sh -# Purpose: -# - Deploy OpenBao to Kubernetes (dev mode) and store admin ed25519 key pair -# Generate and apply a Kerberos krb5.conf ConfigMap for an external realm -# - Local Docker helpers for OpenBao (optional) - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -# Preserve positional args while sourcing env -__PROLE_SAVED_ARGS=("$@") -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - # shellcheck disable=SC1090 - source "$HOME/.prole/env.sh" -fi -set -- "${__PROLE_SAVED_ARGS[@]}" -unset __PROLE_SAVED_ARGS - -if [[ -z "${PROLE_SERVICE:-}" ]]; then - echo "ERROR: PROLE_SERVICE is not defined in env. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2 - exit 1 -fi - -ACTION=${1:-} - -# Defaults -NAMESPACE=${NAMESPACE:-prole} -OPENBAO_NAME=${OPENBAO_NAME:-openbao} -OPENBAO_MANIFEST_DIR="$SCRIPT_DIR/../k8s/openbao" - -# Kerberos realm defaults -KRB5_REALM=${KRB5_REALM:-PROLE.ORG} -DOMAIN=${DOMAIN:-$(echo "$KRB5_REALM" | tr 'A-Z' 'a-z')} -KRB5_KDC=${KRB5_KDC:-kdc.$DOMAIN} -KRB5_ADMIN=${KRB5_ADMIN:-} - -# Secrets and token locations -SECRETS_DIR="$PROLE_SERVICE/secrets" -mkdir -p "$SECRETS_DIR" -admin_key_priv="$SECRETS_DIR/admin_ed25519.key" -admin_key_pub="$SECRETS_DIR/admin_ed25519.pub" -root_token_file="$SECRETS_DIR/openbao-root-token" - -ensure_tools() { - for t in kubectl curl openssl base64; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -ensure_docker() { - command -v docker >/dev/null || { echo "Missing required tool: docker" >&2; exit 1; } -} - -ensure_admin_keypair() { - # Ensure admin ed25519 keypair exists and a root token is available - mkdir -p "$SECRETS_DIR" - if [[ ! -f "$admin_key_priv" || ! -f "$admin_key_pub" ]]; then - echo "Generating admin ed25519 keypair in $SECRETS_DIR ..." - openssl genpkey -algorithm ED25519 -out "$admin_key_priv" - openssl pkey -in "$admin_key_priv" -pubout -out "$admin_key_pub" - chmod 0600 "$admin_key_priv" - fi - # Ensure a root token exists for OpenBao dev server interactions - if [[ ! -f "$root_token_file" || ! -s "$root_token_file" ]]; then - openssl rand -hex 24 >"$root_token_file" - chmod 0600 "$root_token_file" - fi -} - -generate_openbao_manifests() { - mkdir -p "$OPENBAO_MANIFEST_DIR" - # Only create a simple deployment if none exists; otherwise respect current file - local deploy="$OPENBAO_MANIFEST_DIR/deployment.yaml" - if [[ ! -f "$deploy" ]]; then - cat >"$deploy" <"$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" </dev/null 2>&1; then - kubectl rollout status statefulset/$OPENBAO_NAME -n "$NAMESPACE" --timeout=120s - else - kubectl rollout status deploy/$OPENBAO_NAME -n "$NAMESPACE" --timeout=120s - fi -} - -init_openbao_kv_and_store_admin_key() { - local token - token=$(cat "$root_token_file") - # Enable kv (if not already) and write keys - local svc="http://127.0.0.1:18200" - echo "Configuring KV at $svc ..." - - # Check if kv/ is already mounted - if curl -sS -H "X-Vault-Token: $token" "$svc/v1/sys/mounts" | grep -q '"kv/":'; then - echo "KV path 'kv/' is already enabled." - else - curl -sS -H "X-Vault-Token: $token" -X POST "$svc/v1/sys/mounts/kv" \ - -d '{"type":"kv","options":{"version":"2"}}' >/dev/null 2>&1 || true - fi - - local priv pub - priv=$(base64 <"$admin_key_priv" | tr -d '\n') - pub=$(base64 <"$admin_key_pub" | tr -d '\n') - - # Check if admin key already exists and matches - local existing_data - existing_data=$(curl -sS -H "X-Vault-Token: $token" "$svc/v1/kv/data/prole/admin" 2>/dev/null || true) - if [[ -n "$existing_data" ]]; then - local ex_priv ex_pub - ex_priv=$(echo "$existing_data" | grep -o '"admin_private_key_b64":"[^"]*' | cut -d'"' -f4 || true) - ex_pub=$(echo "$existing_data" | grep -o '"admin_public_key_b64":"[^"]*' | cut -d'"' -f4 || true) - - if [[ "$ex_priv" == "$priv" && "$ex_pub" == "$pub" ]]; then - echo "Admin key pair in OpenBao kv/prole/admin is already up to date." - else - echo "Writing admin key pair to kv/prole/admin ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/admin" \ - -d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null - echo "Stored admin key pair in OpenBao kv/prole/admin." - fi - else - echo "Writing admin key pair to kv/prole/admin ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/admin" \ - -d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null - echo "Stored admin key pair in OpenBao kv/prole/admin." - fi - - # Store User SSH keys if they exist - local user_key_priv="$HOME/.ssh/id_prole_ed25519" - local user_key_pub="$HOME/.ssh/id_prole_ed25519.pub" - if [[ -f "$user_key_priv" && -f "$user_key_pub" ]]; then - local u_priv u_pub - u_priv=$(base64 <"$user_key_priv" | tr -d '\n') - u_pub=$(base64 <"$user_key_pub" | tr -d '\n') - local username=${PROLE_DB_USER:-"prole"} - - echo "Writing user SSH keys for '$username' to kv/prole/user ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/user" \ - -d "{\"data\":{\"username\":\"$username\",\"private_key_b64\":\"$u_priv\",\"public_key_b64\":\"$u_pub\"}}" >/dev/null - echo "Stored user SSH keys in OpenBao kv/prole/user." - fi - - if [[ -n "$db_pass" ]]; then - local username=${PROLE_DB_USER:-"prole"} - echo "Writing database user password for '$username' to kv/prole/db ..." - curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \ - -X POST "$svc/v1/kv/data/prole/db" \ - -d "{\"data\":{\"username\":\"$username\",\"password\":\"$db_pass\"}}" >/dev/null - echo "Stored database password in OpenBao kv/prole/db." - fi -} - -# Removed: prompt_admin_password_and_apply_secret (no AD admin secret required) - -docker_start() { - echo "Starting local Docker container for OpenBao ..." - ensure_docker - docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true - # OpenBao dev - local token - token=$(cat "$root_token_file" 2>/dev/null || true) - if [[ -z "$token" ]]; then - token=$(openssl rand -hex 24) - printf "%s" "$token" >"$root_token_file" - chmod 0600 "$root_token_file" - fi - docker run -d --name "$OPENBAO_NAME" -p 18200:8200 \ - "$OPENBAO_IMAGE" server -dev -dev-listen-address=0.0.0.0:8200 -dev-root-token-id="$token" - echo "Docker container started: $OPENBAO_NAME" -} - -docker_stop() { - docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true - echo "Stopped OpenBao container if it was running." -} - -docker_restart() { - docker_stop - docker_start -} - -# status command implementation wrapped in a function to avoid top-level 'local' -cmd_status() { - echo "--- init_primary_domain status ---" - echo "Namespace: $NAMESPACE" - echo "OpenBao name: $OPENBAO_NAME" - echo "Manifest dir: $OPENBAO_MANIFEST_DIR" - - # Files/manifests present - local ok=0 - if [[ -f "$OPENBAO_MANIFEST_DIR/deployment.yaml" ]]; then - echo "[OK] OpenBao deployment manifest exists" - else - echo "[MISSING] $OPENBAO_MANIFEST_DIR/deployment.yaml" - ok=1 - fi - if [[ -f "$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" ]]; then - echo "[OK] Kerberos ConfigMap manifest exists" - else - echo "[MISSING] $OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" - ok=1 - fi - - # K8s resources - if kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" >/dev/null 2>&1; then - local ready desired - ready=$(kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") - desired=$(kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0") - ready=${ready:-0} - desired=${desired:-0} - if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then - echo "[OK] OpenBao StatefulSet running ($ready/$desired ready)" - else - echo "[WARN] OpenBao StatefulSet not fully ready ($ready/$desired)" - ok=1 - fi - elif kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" >/dev/null 2>&1; then - # Get readiness - local ready desired - ready=$(kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0") - desired=$(kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0") - ready=${ready:-0} - desired=${desired:-0} - if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then - echo "[OK] OpenBao Deployment running ($ready/$desired ready)" - else - echo "[WARN] OpenBao Deployment not fully ready ($ready/$desired)" - ok=1 - fi - else - echo "[MISSING] OpenBao Deployment or StatefulSet '$OPENBAO_NAME' in namespace '$NAMESPACE'" - ok=1 - fi - - if kubectl -n "$NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then - echo "[OK] Kerberos ConfigMap 'prole-krb5-conf' present" - else - echo "[MISSING] Kerberos ConfigMap 'prole-krb5-conf' in namespace '$NAMESPACE'" - ok=1 - fi - - # Docker (optional local mode) - if command -v docker >/dev/null 2>&1; then - if docker ps --format '{{.Names}}' | grep -Fxq "$OPENBAO_NAME"; then - echo "[OK] Docker container '$OPENBAO_NAME' is running" - else - # Not necessarily an error; we primarily use k8s - echo "[INFO] Docker container '$OPENBAO_NAME' not running (k8s mode may be in use)" - fi - fi - - # Secrets and tokens - if [[ -f "$admin_key_priv" && -f "$admin_key_pub" ]]; then - echo "[OK] Admin ed25519 keypair present in $SECRETS_DIR" - else - echo "[MISSING] Admin keypair files in $SECRETS_DIR" - ok=1 - fi - if [[ -s "$root_token_file" ]]; then - echo "[OK] OpenBao root token file present" - else - echo "[MISSING] OpenBao root token file ($root_token_file)" - ok=1 - fi - - # OpenBao reachability and token validity (best-effort) - # Prefer explicit override, then localhost port-forward, then cluster DNS - local svc_url - if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then - svc_url="$PROLE_OPENBAO_URL" - elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then - svc_url="http://127.0.0.1:18200" - else - svc_url="http://$OPENBAO_NAME.$NAMESPACE.svc.cluster.local:8200" - fi - if curl -sS "$svc_url/v1/sys/health" >/dev/null 2>&1; then - echo "[OK] OpenBao service reachable" - if [[ -s "$root_token_file" ]]; then - local code - code=$(curl -s -o /dev/null -w "%{http_code}" -H "X-Vault-Token: $(cat "$root_token_file")" "$svc_url/v1/sys/mounts" || echo "000") - if [[ "$code" == "200" ]]; then - echo "[OK] OpenBao root token authenticates" - else - echo "[WARN] OpenBao root token did not authenticate (HTTP $code)" - ok=1 - fi - fi - else - echo "[WARN] OpenBao service not reachable at $svc_url" - ok=1 - fi - - # Service certificate existence (CNPG TLS) - local CNPG_CLUSTER_NAME - CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db} - if kubectl -n "$NAMESPACE" get secret "${CNPG_CLUSTER_NAME}-tls" >/dev/null 2>&1; then - echo "[OK] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' exists" - else - echo "[INFO] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' not found" - fi - - echo "-------------------------------------" - if [[ $ok -eq 0 ]]; then - echo "Status: OK" - return 0 - else - echo "Status: Issues detected" - return 1 - fi -} - -case "$ACTION" in - start) - ensure_tools - ensure_docker - ensure_admin_keypair - docker_start - ;; - status) - ensure_tools - cmd_status - ;; - stop) - ensure_docker - docker_stop - ;; - restart) - ensure_tools - ensure_docker - ensure_admin_keypair - docker_restart - ;; - initialize) - ensure_tools - # Read password from stdin if provided - db_pass="" - if [[ ! -t 0 ]]; then - read -r db_pass - fi - - ensure_admin_keypair - generate_openbao_manifests - generate_kerberos_configmap - apply_k8s - - if [[ -n "$db_pass" ]]; then - local username=${PROLE_DB_USER:-"prole"} - echo "Creating database user secret 'prole-db-user' for '$username' ..." - kubectl create secret generic prole-db-user -n "$NAMESPACE" \ - --from-literal=username="$username" \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f - - - echo "Creating database superuser secret 'prole-db-superuser' ..." - kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \ - --from-literal=username=postgres \ - --from-literal=password="$db_pass" \ - --dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f - - fi - - wait_for_openbao - - # Ensure port-forward is running for OpenBao - echo "Ensuring port-forward for OpenBao is active ..." - "$SCRIPT_DIR/init_port_forwards.sh" restart openbao - - if ! curl -sS http://127.0.0.1:18200/v1/sys/health >/dev/null 2>&1; then - echo "ERROR: OpenBao not reachable at http://127.0.0.1:18200." >&2 - echo "Please start port-forwards: $SCRIPT_DIR/init_port_forwards.sh start openbao" >&2 - exit 1 - fi - init_openbao_kv_and_store_admin_key - echo "Initialization complete. Manifests in: $OPENBAO_MANIFEST_DIR" - ;; - update|reload) - generate_openbao_manifests - generate_kerberos_configmap - apply_k8s - echo "Re-applied manifests." - ;; - *) - echo "Usage: $0 {start|stop|status|restart|initialize|update|reload}" >&2 - exit 2 - ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4494452656/init_port_forwards.sh b/MagicMock/mock.Entry().get().strip()/4494452656/init_port_forwards.sh deleted file mode 100755 index 9ebea5c..0000000 --- a/MagicMock/mock.Entry().get().strip()/4494452656/init_port_forwards.sh +++ /dev/null @@ -1,537 +0,0 @@ -#!/usr/bin/env bash -set -u - -# init_port_forwards.sh -# Portable-ish (macOS, Ubuntu, Raspberry Pi OS, Alpine) bash init-style script -# Manages kubectl port-forward daemons defined in an XML file. - -PROG="init_port_forwards" -PROLE_HOME="${PROLE_HOME:-/Users/chrisfu/dev/prole}" -VERBOSE=0 -CONFIG_FILE="$PROLE_HOME/conf/port-mappings.properties" - -usage() { - cat < [component] - -Options: - -c, --config-file=FILE Path to local-ports.properties (XML) - -v, --verbose Verbose output - -Examples: - $PROG -c ./port-mappings.properties start - $PROG stop openbao - $PROG --verbose status -EOF -} - -TARGET_ID="" - -log() { printf '%s\n' "$*"; } -vlog() { [ "$VERBOSE" -eq 1 ] && printf '[verbose] %s\n' "$*" || true; } -err() { printf '[error] %s\n' "$*" >&2; } - -have() { command -v "$1" >/dev/null 2>&1; } - -# Choose a writable state dir for pid/log files: -# - Prefer XDG_RUNTIME_DIR if set and writable -# - Else /var/run if writable (rare without root) -# - Else ~/.local/state -# - Else /tmp -state_dir() { - if [ -n "${XDG_RUNTIME_DIR:-}" ] && [ -w "${XDG_RUNTIME_DIR:-}" ]; then - printf '%s/%s' "$XDG_RUNTIME_DIR" "$PROG" - return - fi - if [ -d "/var/run" ] && [ -w "/var/run" ]; then - printf '/var/run/%s' "$PROG" - return - fi - if [ -n "${HOME:-}" ]; then - mkdir -p "$HOME/.local/state" >/dev/null 2>&1 || true - if [ -d "$HOME/.local/state" ] && [ -w "$HOME/.local/state" ]; then - printf '%s/.local/state/%s' "$HOME" "$PROG" - return - fi - fi - printf '/tmp/%s' "$PROG" -} - -STATE_DIR="$(state_dir)" -PID_DIR="$STATE_DIR/pids" -LOG_DIR="$STATE_DIR/logs" - -ensure_dirs() { - mkdir -p "$PID_DIR" "$LOG_DIR" 2>/dev/null || true - if [ ! -d "$PID_DIR" ] || [ ! -d "$LOG_DIR" ]; then - err "Unable to create state directories under: $STATE_DIR" - exit 1 - fi -} - -# Use kubectl for actions; kubecolor is used only for prettier status output. -KUBECTL="kubectl" -detect_kubectl() { - if have kubectl; then - KUBECTL="kubectl" - else - err "kubectl not found in PATH" - exit 1 - fi -} - -# Basic environment validation -validate_env() { - detect_kubectl - ensure_dirs - - if ! "$KUBECTL" version --client >/dev/null 2>&1; then - err "kubectl seems broken or not executable" - exit 1 - fi - - # Can we reach the cluster? - if ! "$KUBECTL" cluster-info >/dev/null 2>&1; then - err "kubectl cannot reach a cluster (check KUBECONFIG/context)" - err "Try: kubectl config get-contexts && kubectl config use-context " - exit 1 - fi -} - -# ---- Preflight: Docker + k3d awareness ---- -# Return 0 when Docker CLI can talk to a running daemon. -docker_is_running() { - if ! have docker; then - return 1 - fi - docker info >/dev/null 2>&1 -} - -# Hard-fail with clean guidance when Docker is not up (used for start/restart). -ensure_docker_running() { - if docker_is_running; then - return 0 - fi - if ! have docker; then - err "Docker CLI not found. Please install Docker Desktop or docker CLI." - else - err "Docker is not running. Start Docker Desktop and wait until it is ready." - fi - err "Tip (macOS): open -a Docker" - err "Then re-run: $PROG start" - exit 3 -} - -# Detect k3d cluster name from env or current kubectl context. -# - If K3D_CLUSTER is set, use it. -# - Else, if current-context starts with 'k3d-', strip prefix to get name. -detect_k3d_context() { - if [ -n "${K3D_CLUSTER:-}" ]; then - printf '%s' "$K3D_CLUSTER" - return 0 - fi - local ctx - ctx="$($KUBECTL config current-context 2>/dev/null || echo)" - case "$ctx" in - k3d-*) printf '%s' "${ctx#k3d-}"; return 0 ;; - *) return 1 ;; - esac -} - -# Return 0 if the given k3d cluster exists and is running. -k3d_cluster_is_running() { - local name="$1" - have k3d || return 1 - # Use json output when available; fall back to grep otherwise - if k3d cluster list -o json >/dev/null 2>&1; then - k3d cluster list -o json 2>/dev/null | grep -q '"name"\s*:\s*"'"$name"'"' && \ - k3d cluster list -o json 2>/dev/null | sed -n 's/.*"name"\s*:\s*"\([^"]\+\)".*"serversRunning"\s*:\s*\([0-9]\+\).*/\1 \2/p' | awk -v n="$name" '$1==n {exit ($2>0)?0:1}' - return $? - else - k3d cluster list 2>/dev/null | grep -E "^$name\s" | grep -q running - return $? - fi -} - -# If current context indicates k3d, ensure the cluster is up; otherwise no-op. -ensure_k3d_ready_if_applicable() { - local k3d_name - if ! k3d_name="$(detect_k3d_context)"; then - return 0 - fi - if ! have k3d; then - err "k3d is not installed but kubectl context suggests k3d (context=$("$KUBECTL" config current-context))." - err "Install k3d: brew install k3d (macOS)" - err "Or switch context: kubectl config use-context " - exit 4 - fi - if ! k3d_cluster_is_running "$k3d_name"; then - err "k3d cluster '$k3d_name' is not running." - err "Start it: k3d cluster start $k3d_name" - err "Then re-run: $PROG start" - exit 4 - fi -} - -pid_file_for() { printf '%s/%s.pid' "$PID_DIR" "$1"; } -log_file_for() { printf '%s/%s.log' "$LOG_DIR" "$1"; } - -is_pid_running() { - # Return 0 if pid exists and running, else 1 - # kill -0 is portable. - local pid="$1" - [ -n "$pid" ] && kill -0 "$pid" >/dev/null 2>&1 -} - -read_pid() { - local pf="$1" - [ -f "$pf" ] || return 1 - # shellcheck disable=SC2162 - read pid <"$pf" || return 1 - printf '%s' "$pid" -} - -write_pid() { - local pf="$1" pid="$2" - printf '%s\n' "$pid" >"$pf" -} - -remove_pidfile() { - local pf="$1" - rm -f "$pf" >/dev/null 2>&1 || true -} - -# ---- XML parsing (simple attribute extraction) ---- -# We parse lines containing: -# Attributes must use double quotes in the XML (as in the sample). -get_attr() { - # $1 = line, $2 = attribute name - # outputs value or empty - printf '%s\n' "$1" | sed -n "s/.*$2=\"\([^\"]*\)\".*/\1/p" -} - -foreach_mapping() { - # Calls a provided function with mapping fields: - # callback id ns target address hostPort servicePort protocol description - local callback="$1" - [ -f "$CONFIG_FILE" ] || { err "Config file not found: $CONFIG_FILE"; exit 1; } - - # Support both single-line and multi-line self-closing mapping tags, e.g.: - # OR lines spanning multiple lines until "/>". - local in_mapping=0 in_comment=0 buffer="" line - while IFS= read -r line; do - # Handle XML comments: skip anything between - if [ $in_comment -eq 1 ]; then - case "$line" in - *"-->"*) in_comment=0; continue ;; - *) continue ;; - esac - fi - case "$line" in - *""*) - # single-line comment; skip line - continue - ;; - *) - in_comment=1 - continue - ;; - esac - ;; - esac - if [ $in_mapping -eq 0 ]; then - case "$line" in - *"' - buffer="$buffer $line" - fi - - if [ $in_mapping -eq 1 ] && printf '%s' "$line" | grep -q "/>"; then - # Normalize whitespace to make attribute extraction robust - local merged - merged=$(printf '%s\n' "$buffer" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g') - - local id ns target address hostPort servicePort protocol description - id="$(get_attr "$merged" "id")" - ns="$(get_attr "$merged" "namespace")" - target="$(get_attr "$merged" "target")" - address="$(get_attr "$merged" "address")" - hostPort="$(get_attr "$merged" "hostPort")" - servicePort="$(get_attr "$merged" "servicePort")" - protocol="$(get_attr "$merged" "protocol")" - description="$(get_attr "$merged" "description")" - - # Basic validation - if [ -z "$id" ] || [ -z "$ns" ] || [ -z "$target" ] || [ -z "$hostPort" ] || [ -z "$servicePort" ]; then - err "Invalid mapping (missing required attributes): $merged" - exit 1 - fi - - # Filter by TARGET_ID if set - if [ -n "$TARGET_ID" ] && [ "$id" != "$TARGET_ID" ]; then - in_mapping=0 - buffer="" - continue - fi - - if [ -z "$address" ]; then address="127.0.0.1"; fi - if [ -z "$protocol" ]; then protocol="TCP"; fi - - vlog "mapping: id=$id ns=$ns target=$target address=$address hostPort=$hostPort servicePort=$servicePort protocol=$protocol" - "$callback" "$id" "$ns" "$target" "$address" "$hostPort" "$servicePort" "$protocol" "$description" - - # Reset accumulator - in_mapping=0 - buffer="" - fi - done <"$CONFIG_FILE" -} - -build_port_forward_cmd() { - # echo a command string - local ns="$1" target="$2" address="$3" hostPort="$4" servicePort="$5" - # kubectl port-forward -n --address : - printf '%s port-forward -n %s --address %s %s %s:%s' \ - "$KUBECTL" "$ns" "$address" "$target" "$hostPort" "$servicePort" -} - -start_port_forward() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - local pidfile logfile cmd pid - - # Aggressively stop existing processes before starting to avoid port conflicts - stop_port_forward "$id" "$ns" "$target" "$address" "$hostPort" "$servicePort" "$protocol" "$description" - - pidfile="$(pid_file_for "$id")" - logfile="$(log_file_for "$id")" - cmd="$(build_port_forward_cmd "$ns" "$target" "$address" "$hostPort" "$servicePort")" - - log "Starting: $id $address:$hostPort -> $target:$servicePort ($ns) ${description:-}" - vlog "Command: $cmd" - - # Start in background, keep output in log. - # nohup is available on macOS/Linux; redirect stdin from /dev/null to detach. - nohup sh -c "$cmd" >>"$logfile" 2>&1 /dev/null 2>&1 || true - - # wait a moment, then SIGKILL if needed - local i - for i in 1 2 3 4 5; do - if ! is_pid_running "$pid"; then break; fi - sleep 0.2 - done - if is_pid_running "$pid"; then - err "$id did not stop gracefully; sending SIGKILL" - kill -9 "$pid" >/dev/null 2>&1 || true - fi - else - if [ -f "$pidfile" ]; then - vlog "$id stale pidfile (pid=$pid not running)" - fi - fi - remove_pidfile "$pidfile" - - # Aggressively remove any other matching kubectl port-forward processes - # Search for processes that match: kubectl port-forward -n ... : - local extra_pids - extra_pids=$(ps -ef | grep "port-forward" | grep "\-n" | grep "$ns" | grep "$target" | grep "$hostPort:$servicePort" | grep -v grep | awk '{print $2}') - - for epid in $extra_pids; do - if [ "$epid" != "$pid" ]; then - log "Cleaning up orphan process for $id (pid=$epid)" - kill -9 "$epid" >/dev/null 2>&1 || true - fi - done -} - -status_one() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - local pidfile pid - - pidfile="$(pid_file_for "$id")" - pid="" - if [ -f "$pidfile" ]; then - pid="$(read_pid "$pidfile" || true)" - fi - - if [ -n "${pid:-}" ] && is_pid_running "$pid"; then - printf 'RUNNING %-12s pid=%-7s %s:%s -> %s:%s ns=%s %s\n' \ - "$id" "$pid" "$address" "$hostPort" "$target" "$servicePort" "$ns" "${description:-}" - # ps details (portable flags vary; use a conservative format) - ps -p "$pid" -o pid=,ppid=,etime=,command= 2>/dev/null | sed 's/^/ /' || true - else - printf 'STOPPED %-12s (no live pid) %s:%s -> %s:%s ns=%s %s\n' \ - "$id" "$address" "$hostPort" "$target" "$servicePort" "$ns" "${description:-}" - fi -} - -do_start() { - validate_env - # Preflight: require Docker daemon and k3d (if applicable) - ensure_docker_running - ensure_k3d_ready_if_applicable - foreach_mapping start_port_forward -} - -do_stop() { - # stop doesn't require cluster access, but it does need state dirs - ensure_dirs - foreach_mapping stop_port_forward -} - -do_restart() { - validate_env - # Preflight: require Docker daemon and k3d (if applicable) - ensure_docker_running - ensure_k3d_ready_if_applicable - foreach_mapping stop_port_forward - foreach_mapping start_port_forward -} - -do_status() { - validate_env - # Non-fatal awareness messages - if docker_is_running; then - log "[OK] Docker daemon is running" - else - if have docker; then - log "[WARN] Docker is not running" - else - log "[WARN] Docker CLI not found" - fi - fi - - local _k3d_name - if _k3d_name="$(detect_k3d_context)"; then - if have k3d; then - if k3d_cluster_is_running "$_k3d_name"; then - log "[OK] k3d cluster '$_k3d_name' is running" - else - log "[WARN] k3d cluster '$_k3d_name' is not running" - fi - else - log "[WARN] k3d not installed but context suggests k3d (cluster='$_k3d_name')" - fi - fi - - log "== Context / Cluster ==" - "$KUBECTL" config current-context 2>/dev/null | sed 's/^/ context: /' || true - "$KUBECTL" cluster-info 2>/dev/null | sed 's/^/ /' || true - log "" - - log "== Port-forward processes ==" - foreach_mapping status_one - log "" - - log "== Quick k3d awareness checks (best-effort) ==" - # If user is on k3d, current-context often includes k3d-... but not guaranteed. - # Show nodes + a few namespaces/services related to mappings (best effort). - "$KUBECTL" get nodes -o wide 2>/dev/null | sed 's/^/ /' || true - log "" - "$KUBECTL" get ns 2>/dev/null | sed 's/^/ /' || true - log "" - - # For each mapping, try to show target existence - log "== Target existence (best-effort) ==" - _target_check() { - local id="$1" ns="$2" target="$3" address="$4" hostPort="$5" servicePort="$6" protocol="$7" description="$8" - # kubectl get -n - # If target is like "svc/name", "deploy/name", etc. - if "$KUBECTL" get -n "$ns" "$target" >/dev/null 2>&1; then - printf 'OK %-12s %s (ns=%s)\n' "$id" "$target" "$ns" - else - printf 'MISSING %-12s %s (ns=%s)\n' "$id" "$target" "$ns" - fi - } - foreach_mapping _target_check -} - -# ---- arg parsing ---- -ACTION="" - -while [ $# -gt 0 ]; do - case "$1" in - start|stop|restart|status) - if [ -z "$ACTION" ]; then - ACTION="$1" - else - TARGET_ID="$1" - fi - shift - ;; - -v|--verbose) - VERBOSE=1 - shift - ;; - -c) - shift - [ $# -gt 0 ] || { err "-c requires a file path"; usage; exit 2; } - CONFIG_FILE="$1" - shift - ;; - --config-file=*) - CONFIG_FILE="${1#*=}" - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - if [ -z "$ACTION" ]; then - err "Unknown arg: $1" - usage - exit 2 - fi - TARGET_ID="$1" - shift - ;; - esac -done - -[ -n "$ACTION" ] || { usage; exit 2; } - -case "$ACTION" in - start) do_start ;; - stop) do_stop ;; - restart) do_restart ;; - status) do_status ;; -esac diff --git a/MagicMock/mock.Entry().get().strip()/4494452656/init_prole-db.sh b/MagicMock/mock.Entry().get().strip()/4494452656/init_prole-db.sh deleted file mode 100755 index 73251d4..0000000 --- a/MagicMock/mock.Entry().get().strip()/4494452656/init_prole-db.sh +++ /dev/null @@ -1,330 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# init_prole-db.sh -# Purpose: -# - Manage prole-db CloudNative-PG cluster operations (deploy, start, stop, etc.) - -# Initialize SCRIPT_DIR -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) - -ACTION=${1:-} -VERSION=${2:-latest} - -# Load env -if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then - set -- - source "$PROLE_HOME/env.sh" -elif [[ -f "$HOME/.prole/env.sh" ]]; then - set -- - source "$HOME/.prole/env.sh" -fi - -CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db} -NAMESPACE=${NAMESPACE:-prole} - -CNPG_MANIFEST="$SCRIPT_DIR/../k8s/prole/prole-db.yaml" - -ensure_tools() { - for t in kubectl jq; do - command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } - done -} - -get_latest_image() { - local version_file="$SCRIPT_DIR/../conf/postgresql/.version" - if [[ -f "$version_file" ]]; then - echo "prole-db:$(cat "$version_file" | tr -d '[:space:]')" - else - echo "prole-db:17.7-033" - fi -} - -start() { - ensure_tools - echo "Checking dependencies..." - - # 1. k3d is running - if ! command -v k3d >/dev/null || ! k3d cluster list >/dev/null 2>&1; then - echo "ERROR: k3d is not running or not installed." >&2 - exit 1 - fi - - # 2. cnpg operator is loaded - if ! kubectl get deployment -n cnpg-system cnpg-controller-manager >/dev/null 2>&1; then - echo "ERROR: CloudNative-PG operator is not loaded." >&2 - exit 1 - fi - - # 3. openbao is configured - if ! kubectl get statefulset openbao -n "$NAMESPACE" >/dev/null 2>&1 && ! kubectl get deployment openbao -n "$NAMESPACE" >/dev/null 2>&1; then - echo "ERROR: OpenBao is not deployed." >&2 - exit 1 - fi - - # Compare latest image with deployed - local latest_image - latest_image=$(get_latest_image) - - local current_image - current_image=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.imageName}' 2>/dev/null || echo "") - - if [[ "$current_image" != "$latest_image" ]]; then - echo "Updating cluster image from '$current_image' to '$latest_image'..." - kubectl patch cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --type merge -p "{\"spec\": {\"imageName\": \"$latest_image\"}}" - # Force a rollout to ensure the new image is pulled even if it was just a tag update (though we use unique tags) - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true - else - echo "Cluster is already using the latest image: $latest_image" - fi - - echo "Starting prole-db cluster (ensuring manifest is applied)..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" -} - -stop() { - ensure_tools - echo "Stopping prole-db cluster $CNPG_CLUSTER_NAME..." - - # Identify instances - local instances - instances=$(kubectl get pods -n "$NAMESPACE" -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" -o jsonpath='{.items[*].metadata.name}') - - if [[ -z "$instances" ]]; then - echo "No instances found for cluster $CNPG_CLUSTER_NAME." - return - fi - - # Determine replicas and primary - local primary - primary=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.status.currentPrimary}') - - echo "Primary: $primary" - - # Shutdown replicas first - for pod in $instances; do - if [[ "$pod" != "$primary" ]]; then - echo "Shutting down replica $pod..." - kubectl exec -n "$NAMESPACE" "$pod" -c postgres -- psql -U postgres -c "CHECKPOINT;" || true - fi - done - - # Shutdown primary last - if [[ -n "$primary" ]]; then - echo "Shutting down primary $primary..." - kubectl exec -n "$NAMESPACE" "$primary" -c postgres -- psql -U postgres -c "CHECKPOINT;" || true - fi - - echo "Deleting cluster resource..." - kubectl delete -f "$CNPG_MANIFEST" --ignore-not-found -} - -restart() { - ensure_tools - echo "Restarting prole-db cluster..." - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -} - -deploy() { - ensure_tools - local image - if [[ "$VERSION" == "latest" ]]; then - image=$(get_latest_image) - else - image="prole-db:$VERSION" - fi - - echo "Deploying $image to cluster $CNPG_CLUSTER_NAME..." - - # If cluster doesn't exist, use init_cloudnative_pg.sh create first - if ! kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then - echo "Cluster not found. Running init_cloudnative_pg.sh create..." - bash "$SCRIPT_DIR/init_cloudnative_pg.sh" create - fi - - # Ensure image is updated if manifest has an older version - # First, apply the manifest to ensure the cluster exists/is updated - echo "Applying manifest $CNPG_MANIFEST..." - kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST" - - # Then force the specific image version via patch if different - local current_image - current_image=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.imageName}' 2>/dev/null || echo "") - if [[ "$current_image" != "$image" ]]; then - echo "Patching cluster to use image '$image' (was '$current_image')..." - kubectl patch cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --type merge -p "{\"spec\": {\"imageName\": \"$image\"}}" - # Force a rollout - kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true - fi -} - -rollback() { - ensure_tools - echo "Rollback: Updating to previous image (manually specified version or fallback)..." - if [[ "$VERSION" == "latest" ]]; then - echo "Please specify a version to rollback to. Usage: $0 rollback " - exit 1 - fi - deploy -} - -rollout() { - ensure_tools - echo "Starting manual recreate rollout for $CNPG_CLUSTER_NAME in $NAMESPACE..." - - # 1. Monitor status - echo "Current status:" - kubectl cnpg status "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" - - # 2. Identify the primary instance - local primary - primary=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.status.currentPrimary}') - if [[ -z "$primary" ]]; then - echo "ERROR: Could not identify primary instance." >&2 - exit 1 - fi - echo "Primary instance: $primary" - - # 3. Identify all pod instances - local instances - instances=$(kubectl get pods -n "$NAMESPACE" -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" -o jsonpath='{.items[*].metadata.name}') - - # 4. Recreate non-primary instances one at a time - for pod in $instances; do - if [[ "$pod" != "$primary" ]]; then - echo "Recreating non-primary pod: $pod..." - kubectl delete pod -n "$NAMESPACE" "$pod" - - echo "Waiting for $pod to be recreated and healthy..." - while true; do - if kubectl get pod -n "$NAMESPACE" "$pod" >/dev/null 2>&1; then - local status - status=$(kubectl get pod -n "$NAMESPACE" "$pod" -o jsonpath='{.status.phase}') - local ready - ready=$(kubectl get pod -n "$NAMESPACE" "$pod" -o jsonpath='{.status.containerStatuses[0].ready}') - if [[ "$status" == "Running" && "$ready" == "true" ]]; then - echo "Pod $pod is active and healthy." - break - fi - fi - echo -n "." - sleep 5 - done - echo "" - - # Additional wait for CNPG to recognize it as joined and healthy - echo "Waiting for CNPG cluster to stabilize..." - sleep 10 - kubectl cnpg status "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" - fi - done - - # 5. When the last primary instance needs to be recreated, promote a new primary - echo "Promoting a new primary to recreate the old primary $primary..." - # We promote one of the newly recreated instances. - # kubectl cnpg promote - - local new_primary="" - for pod in $instances; do - if [[ "$pod" != "$primary" ]]; then - new_primary="$pod" - break - fi - done - - if [[ -z "$new_primary" ]]; then - echo "ERROR: No candidate for new primary found." >&2 - exit 1 - fi - - echo "Promoting $new_primary..." - kubectl cnpg promote "$CNPG_CLUSTER_NAME" "$new_primary" -n "$NAMESPACE" || { - echo "Promotion failed, but continuing rollout (CNPG might have already promoted it)..." - } - - echo "Waiting for $new_primary to become primary..." - while true; do - local current_primary - current_primary=$(kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -o jsonpath='{.status.currentPrimary}') - if [[ "$current_primary" == "$new_primary" ]]; then - echo "$new_primary is now the primary." - break - fi - echo -n "." - sleep 5 - done - echo "" - - # 6. Rebuild the last pod (the old primary) to make the cluster consistent - echo "Recreating the old primary pod: $primary..." - kubectl delete pod -n "$NAMESPACE" "$primary" - - echo "Waiting for $primary to be recreated and healthy..." - while true; do - if kubectl get pod -n "$NAMESPACE" "$primary" >/dev/null 2>&1; then - local status - status=$(kubectl get pod -n "$NAMESPACE" "$primary" -o jsonpath='{.status.phase}') - local ready - ready=$(kubectl get pod -n "$NAMESPACE" "$primary" -o jsonpath='{.status.containerStatuses[0].ready}') - if [[ "$status" == "Running" && "$ready" == "true" ]]; then - echo "Pod $primary is active and healthy." - break - fi - fi - echo -n "." - sleep 5 - done - echo "" - - echo "Rollout complete. Final cluster status:" - kubectl cnpg status "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" -} - -backup() { - echo "Backup - tbd, when we configure s3 or other block store" -} - -reset() { - ensure_tools - echo "Resetting prole-db cluster..." - echo "Removing cnpg instances and pods..." - kubectl delete cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --ignore-not-found - - # Wait for deletion - kubectl wait --for=delete cluster/"$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --timeout=60s || true - - echo "Reinstantiating..." - deploy -} - -case "$ACTION" in - start) - start - ;; - stop) - stop - ;; - restart) - restart - ;; - deploy) - deploy - ;; - rollback) - rollback - ;; - rollout) - rollout - ;; - backup) - backup - ;; - reset) - reset - ;; - *) - echo "Usage: $0 {start|stop|restart|deploy|rollback|backup|reset} [version]" >&2 - exit 2 - ;; -esac diff --git a/build/Install Prole Infrastructure/localpycs/pyimod01_archive.pyc b/build/Install Prole Infrastructure/localpycs/pyimod01_archive.pyc deleted file mode 100644 index 3e9539da181314442694a1ad066b17d55119c52a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5054 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 diff --git a/build/Install Prole Infrastructure/localpycs/pyimod02_importers.pyc b/build/Install Prole Infrastructure/localpycs/pyimod02_importers.pyc deleted file mode 100644 index 0deea34b651e17feeff4258f8c4d3784a9f5bacf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32335 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 diff --git a/build/Install Prole Infrastructure/localpycs/pyimod03_ctypes.pyc b/build/Install Prole Infrastructure/localpycs/pyimod03_ctypes.pyc deleted file mode 100644 index e7320fb3f86efbdaa09603cc14bd8d59e4034ad8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6635 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 diff --git a/build/setup/localpycs/pyimod01_archive.pyc b/build/setup/localpycs/pyimod01_archive.pyc deleted file mode 100644 index 3e9539da181314442694a1ad066b17d55119c52a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5054 zcmb7IS!^4}8J^|xz7L7IrKK;KmO{m`ocL;7N1`OhvdpX*ItT6bid;!sle^6B(zb*m z&`VRMFc3KiAT=;*y%bP@0Bz6$NzsSaNznF@3KfW58>n9Mrc?qPBoFOByIhh<6r>#q zGqeB9|If@n|M3k6ydEck8YR={B4nw3?7Rb=MRX0(#CfFXEuj$Fw@EQ+Evb-NYgWux z%?W3-7`>lxf1jqaRsFq<NN2>mFQf7|7$|j*?@tXC=6MkI-r7!&}^5m?lk(hPE|WM+L?r5 zw)tHB3K>Qs+zmdPVxjFcXevi2)~hz!(uRg%J}!!#cFX9_7Ungm@q)xaw` ze!58?oV4&8Csqt6Q8S`MCFaiyaKK!Sllo;U@$xIw;UDfNqlC9XHG0M>eF$U*3~HlIhm1Z*Z!!9f);O!t7A#j1d(As>3!yCRq#LE%3~t(F zaLo}vwtBkV;4*lOwqQ+>{k6d?$cB5dHfHa+E@|-25$R4tZlc@hHChNQSl4^#<(k=$ zVQBCsA>ICe?j>a#Y%GNS*IP=1<>@6&8NI|lqt|F7aG&)Z*6gv_hG~TVjDXn({r!kS z1IW4h%r*m-uA&rk)3W1J*Vd0x)5N@gyy z(5rjt500B6d^D&!ATZ`Q941u{4vCV&h%+-Xr>LYNX4EdGz)CV#qcJm*m~Swc#R4a& zW|`w|s3uWX?U&>63(VzsQZ-8uic}H=t3d`f$y_`)etv{WoJ%I-^pqAV)llN)W}^oCQB5-i)uMN82F(eFGY(2HGL8k5Q6%iyIIZvkuRyNoY>J$Eh>aOzF_`=(HF|}E0v?KuDfHU(K}Ns zqZ{3^T_^Gemkun)?#5ORYqdVsb?zPCn_x`OYy3&WwU)~y?toIH}oicsN6qV3CGL+IBcM;V|n)O z?7hs!zT=hNrz_!SO5^|Xb+5K<44&PHoYf|eSHcrz|HQ5p^&Yr?_1&v$!@m!H*!Dr& zM}a?dR(j5qrb^F)AdjzkPivi@w;cN;{o$)0yt?5&_o%Jw-uV5=cPH0^mF~ex+Y@lU ziN^<#_b?m@{y+2de=U1JRS#N5_l+UT-;ir8;K14(eGn3+eF`b$tjt zbZ$ryhw$)Oeg5^BQzgz$eeoYC0B|hQ45U$&JxjI=?^>aC18;ys20(-69q#h>y|hCmhh&qa=GG-l*p>fyR`4l$!e&p z9NJ%Y@833C9Dzrz?JMnT#Bcjo50^UzDy@U1v1(i2s(CfE^m=ImwA)CL=wzd)wQ8OR!t?F>kaubVh!asyC(sww}Z$Xg&uibEDNr{U7|qL~%YyTRZz zqiRMAdq8E{Y-FK5(1I;xOJMW=PJwgnfd^8SW|K9{70`iguPm6d8Z!!Zn!taJi~_EJ z@w8SF_3Uj>5Q?Ce><#Xuxsi#}^-O%sbP^b{8rn*l>bXhF^66efR)R8mjW&Xk&C=~Z zmfa>_(sLWQVbG8QZrG)}VY=0z(Uv6I>#+j&YHhw(!(l;QJv)OuZRZ6?`e0q2EvZ|D zy}_My%$aDS;5h5x2}4fQvG5mX{I(*o8HLKS3adLi#<%m-Enb<^;=wGuPXr35)0oqF zenz9|Q>M9`D9p+jT(gvpoGAe6EM)G?4R)3r)aGh}0f*x}2k7~5QK+RpE*k}m4a^xa z%TeO=&j6RlsH8|q90u4oIj6+l()I;mhf}EQ@Ohn@$+5F|#iUe(4NWchrbG>q&ud%E zQVU^tEfj?;h}M>`JqscdcF_e}oTe|)r>KQrP;8;VLD0jp=GdDYHO;{>^Wsg|BOD^j zg9WBUWeh7t=XGA5mlZCLH=>#s;MJu% zvmAa2;RlZDt4qFkY4pX(xazJG@In|Xsup~ifR5sq#&nUc^Ke2*(Sy5Fir`6R4T6zy zYpO*%j%rut3$Rd@Q{5WDfGf2xz*e)$oP_OICBYt4f|GGzf}~I@I*ze%y3MK`-_H-V zp$xFe0~(6i_M<@Svi+`o*?rf&lC1dml`K`4|5rbG6zp7K){^f{l!u=$cU`CiC(G{1 zt)by{`(Vkw>c&{7<(=0zTnGOdYTLGY9NtHP)}`#Sa93E(ZUzQ60t0JO)gN58-LHC07X#ERl~bC^)#?ZE?InJPL-Fhu$7q-CqeFDveZq zZTBv{pDuTu+VGwF`>{vip5^@8`PGq1`0x_BZAbo4$@zEym|?kZ0L}O#yU5}2?z&N^ zryA^7IdDJr>sYxXx{Zh zI4wyyD@ypetlAhR3-FbZ@Y6{;f=L(v!><{64j$%N4G%~c@E}fGYJg!fIaZc2mdH0M{1}3v)=zabfm4-)D;)0Sr=j}lmym|PG?5lN=(%%W*&NV8vj`Rx^+`ArNjR-&J|Uff9_$Oee}NMi?C}!(I5Vw= zm}yZ|;1R$U7_d%w(E#dq>#qm>i6B8nK+?0gI*WyP>JB%0E_qJE=}L9eMTjwZ9X{#5 z+GvNu_)g!I4)M?Earjv&1p`Px# diff --git a/build/setup/localpycs/pyimod02_importers.pyc b/build/setup/localpycs/pyimod02_importers.pyc deleted file mode 100644 index 0deea34b651e17feeff4258f8c4d3784a9f5bacf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32335 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 diff --git a/build/setup/localpycs/pyimod03_ctypes.pyc b/build/setup/localpycs/pyimod03_ctypes.pyc deleted file mode 100644 index e7320fb3f86efbdaa09603cc14bd8d59e4034ad8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6635 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 diff --git a/img/installWindow.png b/img/installWindow.png deleted file mode 100644 index f6a4ad3f24f1630a6dbaa111482bc14100a2176d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2003909 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