mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 17:34:31 +00:00
feat: complete end-to-end installation and storage integration. This commit marks a significant milestone where the end-to-end installation process is now fully functional. Key changes: Integrated Garage storage service (S3-compatible); Implemented Prole DB backup; Enhanced Kerberos integration; Updated default namespace to prole-chrisfu-deadbeef; Streamlined dependencies (removed Ollama); Added installation validation and testing scripts; Improved installer UI and deployment logic.
This commit is contained in:
parent
519235558a
commit
f0e75f6107
5
env.sh
5
env.sh
@ -8,9 +8,9 @@ export PROLE_CONF="/Users/chrisfu/dev/prole/conf"
|
||||
export PROLE_DATA="/Users/chrisfu/dev/prole/data"
|
||||
export PROLE_LOGS="/Users/chrisfu/dev/prole/logs"
|
||||
export PROLE_SERVICE="/Users/chrisfu/dev/prole/etc"
|
||||
export NAMESPACE="prole-chrisfu-a05806"
|
||||
export NAMESPACE="prole-chrisfu-deadbeef"
|
||||
|
||||
# Ensure PATH works for GUI-launched shells (Docker, Ollama, etc.)
|
||||
# Ensure PATH works for GUI-launched shells (Docker, etc.)
|
||||
_prole_add_path() { case ":${PATH}:" in *":$1:"*) ;; *) PATH="$1:${PATH:-}" ;; esac; }
|
||||
_prole_add_path "$PROLE_HOME/bin"
|
||||
_prole_add_path "/opt/homebrew/bin"
|
||||
@ -22,7 +22,6 @@ _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
|
||||
|
||||
286
etc/init_garage_store.sh
Executable file
286
etc/init_garage_store.sh
Executable file
@ -0,0 +1,286 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# init_garage_store.sh
|
||||
# Purpose:
|
||||
# - Deploy Garage (S3-compatible object store) in Kubernetes
|
||||
# - Initialize single-node layout for immediate use
|
||||
|
||||
# Initialize SCRIPT_DIR
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
ACTION=${1:-}
|
||||
|
||||
# Load env
|
||||
if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then
|
||||
__PROLE_SAVED_ARGS=("$@")
|
||||
set --
|
||||
# shellcheck disable=SC1090
|
||||
source "$PROLE_HOME/env.sh"
|
||||
set -- "${__PROLE_SAVED_ARGS[@]}"
|
||||
unset __PROLE_SAVED_ARGS
|
||||
elif [[ -f "$HOME/.prole/env.sh" ]]; then
|
||||
__PROLE_SAVED_ARGS=("$@")
|
||||
set --
|
||||
# shellcheck disable=SC1090
|
||||
source "$HOME/.prole/env.sh"
|
||||
set -- "${__PROLE_SAVED_ARGS[@]}"
|
||||
unset __PROLE_SAVED_ARGS
|
||||
fi
|
||||
|
||||
if [[ -n "${GARAGE_INIT_LOG:-}" ]]; then
|
||||
mkdir -p "$(dirname "$GARAGE_INIT_LOG")"
|
||||
exec > >(tee -a "$GARAGE_INIT_LOG") 2>&1
|
||||
fi
|
||||
|
||||
NAMESPACE=${NAMESPACE:-default}
|
||||
GARAGE_NAME=${GARAGE_NAME:-garage}
|
||||
GARAGE_SECRET_NAME=${GARAGE_SECRET_NAME:-garage-secrets}
|
||||
GARAGE_NODE_CAPACITY=${GARAGE_NODE_CAPACITY:-10GB}
|
||||
GARAGE_ZONE=${GARAGE_ZONE:-local}
|
||||
|
||||
# Support both PROLE_HOME/k8s and sibling k8s directory
|
||||
if [[ -d "$SCRIPT_DIR/../k8s/prole" ]]; then
|
||||
GARAGE_MANIFEST_DIR="$SCRIPT_DIR/../k8s/prole"
|
||||
elif [[ -n "${PROLE_HOME:-}" && -d "$PROLE_HOME/k8s/prole" ]]; then
|
||||
GARAGE_MANIFEST_DIR="$PROLE_HOME/k8s/prole"
|
||||
else
|
||||
GARAGE_MANIFEST_DIR="$SCRIPT_DIR/../k8s/prole"
|
||||
fi
|
||||
|
||||
GARAGE_FILES=(
|
||||
"$GARAGE_MANIFEST_DIR/garage-configmap.yaml"
|
||||
"$GARAGE_MANIFEST_DIR/garage-statefulset.yaml"
|
||||
"$GARAGE_MANIFEST_DIR/garage-service.yaml"
|
||||
)
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: $0 [start|stop|restart|status]
|
||||
|
||||
Actions:
|
||||
start Apply Garage manifests and initialize layout
|
||||
stop Delete Garage manifests
|
||||
restart Re-apply Garage manifests and initialize layout
|
||||
status Show Garage resources
|
||||
USAGE
|
||||
exit 1
|
||||
}
|
||||
|
||||
ensure_tools() {
|
||||
for t in kubectl openssl; do
|
||||
command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; }
|
||||
done
|
||||
}
|
||||
|
||||
ensure_namespace() {
|
||||
if ! kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then
|
||||
echo "Creating namespace '$NAMESPACE' ..."
|
||||
kubectl create namespace "$NAMESPACE" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_secrets() {
|
||||
if kubectl get secret "$GARAGE_SECRET_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Creating Garage secrets in namespace '$NAMESPACE' ..."
|
||||
local rpc_secret admin_token metrics_token
|
||||
rpc_secret=$(openssl rand -hex 32)
|
||||
admin_token=$(openssl rand -base64 32)
|
||||
metrics_token=$(openssl rand -base64 32)
|
||||
|
||||
kubectl create secret generic "$GARAGE_SECRET_NAME" -n "$NAMESPACE" \
|
||||
--from-literal=rpc_secret="$rpc_secret" \
|
||||
--from-literal=admin_token="$admin_token" \
|
||||
--from-literal=metrics_token="$metrics_token"
|
||||
}
|
||||
|
||||
apply_manifests() {
|
||||
for f in "${GARAGE_FILES[@]}"; do
|
||||
if [[ -f "$f" ]]; then
|
||||
kubectl apply -n "$NAMESPACE" -f "$f"
|
||||
else
|
||||
echo "ERROR: Missing manifest: $f" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
ensure_container_command() {
|
||||
if ! kubectl get statefulset "$GARAGE_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local cmd args
|
||||
cmd=$(kubectl get statefulset "$GARAGE_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.template.spec.containers[?(@.name=="garage")].command}' 2>/dev/null || true)
|
||||
args=$(kubectl get statefulset "$GARAGE_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.template.spec.containers[?(@.name=="garage")].args}' 2>/dev/null || true)
|
||||
|
||||
if [[ -z "$cmd" || "$cmd" == "[]" || "$cmd" != *"/garage"* || -z "$args" || "$args" == "[]" || "$args" != *"server"* ]]; then
|
||||
echo "Patching Garage container command/args ..."
|
||||
kubectl patch statefulset "$GARAGE_NAME" -n "$NAMESPACE" --type merge -p '{
|
||||
"spec": {
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "garage",
|
||||
"command": ["/garage"],
|
||||
"args": ["server"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}' >/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
restart_statefulset() {
|
||||
if kubectl get statefulset "$GARAGE_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then
|
||||
echo "Restarting Garage StatefulSet to pick up config changes ..."
|
||||
kubectl rollout restart statefulset/$GARAGE_NAME -n "$NAMESPACE" || true
|
||||
fi
|
||||
}
|
||||
|
||||
delete_manifests() {
|
||||
for f in "${GARAGE_FILES[@]}"; do
|
||||
if [[ -f "$f" ]]; then
|
||||
kubectl delete -n "$NAMESPACE" -f "$f" --ignore-not-found
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
dump_debug() {
|
||||
echo "---- Garage debug ----"
|
||||
kubectl get statefulset "$GARAGE_NAME" -n "$NAMESPACE" -o wide || true
|
||||
kubectl get pods -n "$NAMESPACE" -l "app=$GARAGE_NAME" -o wide || true
|
||||
kubectl get svc "$GARAGE_NAME" -n "$NAMESPACE" -o wide || true
|
||||
kubectl get events -n "$NAMESPACE" --sort-by=.metadata.creationTimestamp | tail -n 50 || true
|
||||
|
||||
local pod
|
||||
pod=$(kubectl get pods -n "$NAMESPACE" -l "app=$GARAGE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
|
||||
if [[ -n "$pod" ]]; then
|
||||
echo "--- pod: $pod (describe) ---"
|
||||
kubectl describe pod "$pod" -n "$NAMESPACE" || true
|
||||
echo "--- logs (current) ---"
|
||||
kubectl logs -n "$NAMESPACE" "$pod" --tail=200 || true
|
||||
echo "--- logs (previous) ---"
|
||||
kubectl logs -n "$NAMESPACE" "$pod" --previous --tail=200 || true
|
||||
fi
|
||||
echo "---- Garage debug end ----"
|
||||
}
|
||||
|
||||
wait_ready() {
|
||||
echo "Waiting for Garage StatefulSet to become ready ..."
|
||||
if ! kubectl rollout status statefulset/$GARAGE_NAME -n "$NAMESPACE" --timeout=180s; then
|
||||
echo "ERROR: Garage StatefulSet did not become ready in time." >&2
|
||||
dump_debug
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
init_layout() {
|
||||
local pod
|
||||
pod=$(kubectl get pods -n "$NAMESPACE" -l "app=$GARAGE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
|
||||
if [[ -z "$pod" ]]; then
|
||||
echo "ERROR: Garage pod not found in namespace '$NAMESPACE'." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local node_id=""
|
||||
echo "Determining Garage node ID..."
|
||||
for i in {1..30}; do
|
||||
node_id=$(kubectl exec -n "$NAMESPACE" "$pod" -- /garage node id -q 2>/dev/null | awk '{print $1}' || true)
|
||||
if [[ -n "$node_id" ]]; then
|
||||
break
|
||||
fi
|
||||
echo "Waiting for Garage node ID to be available... ($i/30)"
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [[ -z "$node_id" ]]; then
|
||||
echo "ERROR: Unable to determine Garage node ID after 30 attempts." >&2
|
||||
dump_debug
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract the short ID for better matching
|
||||
local short_id
|
||||
short_id=$(echo "$node_id" | cut -d'@' -f1)
|
||||
echo "Garage node ID: $short_id"
|
||||
|
||||
local layout
|
||||
layout=$(kubectl exec -n "$NAMESPACE" "$pod" -- /garage layout show 2>/dev/null || true)
|
||||
if ! echo "$layout" | grep -q "$short_id"; then
|
||||
echo "Assigning Garage node role (capacity: $GARAGE_NODE_CAPACITY, zone: $GARAGE_ZONE) ..."
|
||||
# Retry assigning role as it might fail if node is not yet fully ready in the cluster logic
|
||||
local max_assign_retries=10
|
||||
local assign_count=0
|
||||
while ! kubectl exec -n "$NAMESPACE" "$pod" -- /garage layout assign -z "$GARAGE_ZONE" -c "$GARAGE_NODE_CAPACITY" "$short_id"; do
|
||||
if [[ $assign_count -ge $max_assign_retries ]]; then
|
||||
echo "ERROR: Failed to assign Garage node role after $max_assign_retries retries." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Retrying Garage layout assign... ($((assign_count + 1))/$max_assign_retries)"
|
||||
sleep 2
|
||||
assign_count=$((assign_count + 1))
|
||||
done
|
||||
|
||||
# Get current version for apply
|
||||
local version
|
||||
version=$(echo "$layout" | grep "Version:" | awk '{print $2}' || echo "0")
|
||||
if [[ -z "$version" ]]; then version=0; fi
|
||||
local next_version=$((version + 1))
|
||||
echo "Applying Garage layout (version $next_version) ..."
|
||||
kubectl exec -n "$NAMESPACE" "$pod" -- /garage layout apply --version "$next_version" || true
|
||||
else
|
||||
echo "Garage layout already assigned for node $short_id."
|
||||
fi
|
||||
}
|
||||
|
||||
status() {
|
||||
ensure_tools
|
||||
echo "Garage status in namespace '$NAMESPACE':"
|
||||
kubectl get statefulset "$GARAGE_NAME" -n "$NAMESPACE" || true
|
||||
kubectl get pods -n "$NAMESPACE" -l "app=$GARAGE_NAME" || true
|
||||
kubectl get svc "$GARAGE_NAME" -n "$NAMESPACE" || true
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
start)
|
||||
ensure_tools
|
||||
ensure_namespace
|
||||
ensure_secrets
|
||||
echo "Applying Garage manifests in namespace '$NAMESPACE'..."
|
||||
apply_manifests
|
||||
ensure_container_command
|
||||
restart_statefulset
|
||||
wait_ready
|
||||
init_layout
|
||||
;;
|
||||
stop)
|
||||
ensure_tools
|
||||
echo "Deleting Garage manifests from namespace '$NAMESPACE'..."
|
||||
delete_manifests
|
||||
;;
|
||||
restart)
|
||||
ensure_tools
|
||||
ensure_namespace
|
||||
ensure_secrets
|
||||
echo "Re-applying Garage manifests in namespace '$NAMESPACE'..."
|
||||
apply_manifests
|
||||
ensure_container_command
|
||||
restart_statefulset
|
||||
wait_ready
|
||||
init_layout
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
325
etc/init_kerberos.sh
Executable file
325
etc/init_kerberos.sh
Executable file
@ -0,0 +1,325 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# init_kerberos.sh
|
||||
# Purpose:
|
||||
# - Configure Kerberos realm settings for CloudNative-PG pods
|
||||
# - Optionally run a Kerberos authentication test inside a Kubernetes pod
|
||||
# - Optionally provision an in-cluster port forwarder to a Samba AD DC
|
||||
#
|
||||
# Usage:
|
||||
# ./init_kerberos.sh initialize # apply krb5.conf to CNPG pods (best-effort)
|
||||
# ./init_kerberos.sh test # run test pod + kinit (with optional AD port forward)
|
||||
# ./init_kerberos.sh status # show detected config + resources
|
||||
# ./init_kerberos.sh cleanup # remove AD forwarder resources
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
ACTION=${1:-initialize}
|
||||
|
||||
# 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"
|
||||
fi
|
||||
set -- "${__PROLE_SAVED_ARGS[@]}"
|
||||
unset __PROLE_SAVED_ARGS
|
||||
|
||||
NAMESPACE=${NAMESPACE:-default}
|
||||
CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db}
|
||||
|
||||
KRB5_REALM=${KRB5_REALM:-${REALM:-}}
|
||||
KRB5_KDC=${KRB5_KDC:-}
|
||||
KRB5_ADMIN=${KRB5_ADMIN:-}
|
||||
KRB5_USER=${KRB5_USER:-${KRB5_USERNAME:-}}
|
||||
KRB5_PASSWORD=${KRB5_PASSWORD:-}
|
||||
|
||||
# AD DC forwarding (socat proxy) options
|
||||
KRB5_AD_PORT_FORWARD=${KRB5_AD_PORT_FORWARD:-}
|
||||
KRB5_AD_PROXY_NAME=${KRB5_AD_PROXY_NAME:-prole-kerberos-ad-forwarder}
|
||||
KRB5_AD_SERVICE_NAME=${KRB5_AD_SERVICE_NAME:-prole-kerberos-ad-dc}
|
||||
KRB5_AD_PROXY_IMAGE=${KRB5_AD_PROXY_IMAGE:-alpine/socat}
|
||||
KRB5_AD_PROXY_HOST_NETWORK=${KRB5_AD_PROXY_HOST_NETWORK:-1}
|
||||
KRB5_AD_TCP_PORTS=${KRB5_AD_TCP_PORTS:-"88 389 445 464 636"}
|
||||
KRB5_AD_UDP_PORTS=${KRB5_AD_UDP_PORTS:-"88 464"}
|
||||
|
||||
log() { printf '%s\n' "$*"; }
|
||||
err() { printf '%s\n' "$*" >&2; }
|
||||
|
||||
ensure_tools() {
|
||||
for t in kubectl; do
|
||||
command -v "$t" >/dev/null || { err "Missing required tool: $t"; exit 1; }
|
||||
done
|
||||
}
|
||||
|
||||
ensure_namespace() {
|
||||
if ! kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then
|
||||
log "Creating namespace '$NAMESPACE' ..."
|
||||
kubectl create namespace "$NAMESPACE" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
primary_kdc() {
|
||||
if [[ -z "${KRB5_KDC}" ]]; then
|
||||
echo ""
|
||||
return 0
|
||||
fi
|
||||
printf '%s' "${KRB5_KDC}" | awk -F',' '{print $1}' | xargs
|
||||
}
|
||||
|
||||
is_private_ip() {
|
||||
local ip="$1"
|
||||
if [[ "$ip" =~ ^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^169\.254\. ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
default_port_forward_if_local() {
|
||||
if [[ -n "${KRB5_AD_PORT_FORWARD}" ]]; then
|
||||
return
|
||||
fi
|
||||
local kdc_host
|
||||
kdc_host=$(primary_kdc)
|
||||
if [[ -n "$kdc_host" ]] && is_private_ip "$kdc_host"; then
|
||||
KRB5_AD_PORT_FORWARD=1
|
||||
else
|
||||
KRB5_AD_PORT_FORWARD=0
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_krb5_conf_configmap() {
|
||||
if kubectl -n "$NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then
|
||||
return
|
||||
fi
|
||||
log "ConfigMap prole-krb5-conf not found. Running init_openbao.sh update..."
|
||||
if [[ -x "$SCRIPT_DIR/init_openbao.sh" ]]; then
|
||||
KRB5_REALM="$KRB5_REALM" KRB5_KDC="$KRB5_KDC" KRB5_ADMIN="$KRB5_ADMIN" \
|
||||
"$SCRIPT_DIR/init_openbao.sh" update || true
|
||||
else
|
||||
err "init_openbao.sh not found; cannot create krb5.conf ConfigMap."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
get_krb5_conf() {
|
||||
kubectl -n "$NAMESPACE" get configmap prole-krb5-conf -o jsonpath='{.data.krb5\.conf}' 2>/dev/null || true
|
||||
}
|
||||
|
||||
apply_krb5_conf_to_cnpg_pods() {
|
||||
local conf
|
||||
conf=$(get_krb5_conf)
|
||||
if [[ -z "$conf" ]]; then
|
||||
err "Missing krb5.conf data in ConfigMap prole-krb5-conf."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local pods
|
||||
pods=$(kubectl -n "$NAMESPACE" get pods -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null || true)
|
||||
if [[ -z "$pods" ]]; then
|
||||
err "No CNPG pods found for cluster '$CNPG_CLUSTER_NAME' in namespace '$NAMESPACE'."
|
||||
return 1
|
||||
fi
|
||||
|
||||
for pod in $pods; do
|
||||
log "Updating /etc/krb5.conf in pod $pod ..."
|
||||
if printf '%s' "$conf" | kubectl -n "$NAMESPACE" exec -i "$pod" -c postgres -- sh -c 'cat > /etc/krb5.conf'; then
|
||||
log "[OK] Updated /etc/krb5.conf in $pod"
|
||||
else
|
||||
err "[WARN] Unable to write /etc/krb5.conf in $pod (permission?)."
|
||||
err " Consider mounting ConfigMap prole-krb5-conf into the CNPG pods or updating the image."
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
apply_ad_forwarder() {
|
||||
local kdc_host
|
||||
kdc_host=$(primary_kdc)
|
||||
if [[ -z "$kdc_host" ]]; then
|
||||
err "KRB5_KDC is required to set up the AD port forwarder."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local host_net_block=""
|
||||
local dns_policy_block=""
|
||||
if [[ "$KRB5_AD_PROXY_HOST_NETWORK" == "1" ]]; then
|
||||
host_net_block=" hostNetwork: true"
|
||||
dns_policy_block=" dnsPolicy: ClusterFirstWithHostNet"
|
||||
fi
|
||||
|
||||
local tcp_cmd=""
|
||||
local udp_cmd=""
|
||||
local port
|
||||
for port in $KRB5_AD_TCP_PORTS; do
|
||||
tcp_cmd+="socat -d -d TCP-LISTEN:${port},fork,reuseaddr TCP:${kdc_host}:${port} & "
|
||||
done
|
||||
for port in $KRB5_AD_UDP_PORTS; do
|
||||
udp_cmd+="socat -d -d UDP-LISTEN:${port},fork,reuseaddr UDP:${kdc_host}:${port} & "
|
||||
done
|
||||
|
||||
local forward_cmd="${tcp_cmd}${udp_cmd}wait"
|
||||
|
||||
log "Applying AD DC forwarder '${KRB5_AD_PROXY_NAME}' in namespace '${NAMESPACE}' (target ${kdc_host}) ..."
|
||||
cat <<EOF | kubectl apply -n "$NAMESPACE" -f -
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ${KRB5_AD_PROXY_NAME}
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: ${KRB5_AD_PROXY_NAME}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ${KRB5_AD_PROXY_NAME}
|
||||
spec:
|
||||
${host_net_block}
|
||||
${dns_policy_block}
|
||||
containers:
|
||||
- name: ad-forwarder
|
||||
image: ${KRB5_AD_PROXY_IMAGE}
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
- >-
|
||||
${forward_cmd}
|
||||
ports:
|
||||
$(for port in $KRB5_AD_TCP_PORTS; do printf " - containerPort: %s\n protocol: TCP\n" "$port"; done)
|
||||
$(for port in $KRB5_AD_UDP_PORTS; do printf " - containerPort: %s\n protocol: UDP\n" "$port"; done)
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ${KRB5_AD_SERVICE_NAME}
|
||||
namespace: ${NAMESPACE}
|
||||
spec:
|
||||
selector:
|
||||
app: ${KRB5_AD_PROXY_NAME}
|
||||
ports:
|
||||
$(
|
||||
for port in $KRB5_AD_TCP_PORTS; do
|
||||
name="tcp-${port}"
|
||||
printf " - name: %s\n port: %s\n targetPort: %s\n protocol: TCP\n" "$name" "$port" "$port"
|
||||
done
|
||||
for port in $KRB5_AD_UDP_PORTS; do
|
||||
name="udp-${port}"
|
||||
printf " - name: %s\n port: %s\n targetPort: %s\n protocol: UDP\n" "$name" "$port" "$port"
|
||||
done
|
||||
)
|
||||
EOF
|
||||
|
||||
kubectl -n "$NAMESPACE" rollout status deploy/${KRB5_AD_PROXY_NAME} --timeout=120s || true
|
||||
}
|
||||
|
||||
cleanup_ad_forwarder() {
|
||||
log "Removing AD forwarder resources (if present)..."
|
||||
kubectl -n "$NAMESPACE" delete service "$KRB5_AD_SERVICE_NAME" --ignore-not-found
|
||||
kubectl -n "$NAMESPACE" delete deployment "$KRB5_AD_PROXY_NAME" --ignore-not-found
|
||||
}
|
||||
|
||||
initialize() {
|
||||
ensure_tools
|
||||
ensure_namespace
|
||||
|
||||
if [[ -z "${KRB5_REALM}" || -z "${KRB5_KDC}" ]]; then
|
||||
err "ERROR: KRB5_REALM and KRB5_KDC must be set for Kerberos initialization."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ensure_krb5_conf_configmap
|
||||
apply_krb5_conf_to_cnpg_pods
|
||||
log "Kerberos initialization complete."
|
||||
}
|
||||
|
||||
run_test() {
|
||||
ensure_tools
|
||||
ensure_namespace
|
||||
|
||||
if [[ -z "${KRB5_REALM}" || -z "${KRB5_KDC}" || -z "${KRB5_USER}" || -z "${KRB5_PASSWORD}" ]]; then
|
||||
err "ERROR: Missing Kerberos configuration. Ensure KRB5_REALM, KRB5_KDC, KRB5_USER, KRB5_PASSWORD are set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
default_port_forward_if_local
|
||||
|
||||
local effective_kdc="$KRB5_KDC"
|
||||
if [[ "${KRB5_AD_PORT_FORWARD}" == "1" ]]; then
|
||||
apply_ad_forwarder
|
||||
effective_kdc="${KRB5_AD_SERVICE_NAME}"
|
||||
fi
|
||||
|
||||
log "Using KDC endpoint for test: ${effective_kdc}"
|
||||
|
||||
# Ensure krb5.conf configmap is updated for the test endpoint
|
||||
if [[ -x "$SCRIPT_DIR/init_openbao.sh" ]]; then
|
||||
KRB5_REALM="$KRB5_REALM" KRB5_KDC="$effective_kdc" KRB5_ADMIN="$KRB5_ADMIN" \
|
||||
"$SCRIPT_DIR/init_openbao.sh" update || true
|
||||
fi
|
||||
|
||||
if [[ -x "$SCRIPT_DIR/init_kerberos_test.sh" ]]; then
|
||||
KRB5_REALM="$KRB5_REALM" KRB5_KDC="$effective_kdc" KRB5_ADMIN="$KRB5_ADMIN" \
|
||||
KRB5_USER="$KRB5_USER" KRB5_PASSWORD="$KRB5_PASSWORD" \
|
||||
"$SCRIPT_DIR/init_kerberos_test.sh" test
|
||||
else
|
||||
err "init_kerberos_test.sh not found."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
status() {
|
||||
ensure_tools
|
||||
log "--- init_kerberos status ---"
|
||||
log "Namespace: $NAMESPACE"
|
||||
log "CNPG cluster: $CNPG_CLUSTER_NAME"
|
||||
log "KRB5_REALM: ${KRB5_REALM:-<unset>}"
|
||||
log "KRB5_KDC: ${KRB5_KDC:-<unset>}"
|
||||
log "KRB5_USER: ${KRB5_USER:-<unset>}"
|
||||
log "AD forwarder enabled: ${KRB5_AD_PORT_FORWARD:-<auto>}"
|
||||
log "AD forwarder deployment: $KRB5_AD_PROXY_NAME"
|
||||
log "AD forwarder service: $KRB5_AD_SERVICE_NAME"
|
||||
if kubectl -n "$NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then
|
||||
log "[OK] ConfigMap prole-krb5-conf present"
|
||||
else
|
||||
log "[MISSING] ConfigMap prole-krb5-conf"
|
||||
fi
|
||||
if kubectl -n "$NAMESPACE" get deploy "$KRB5_AD_PROXY_NAME" >/dev/null 2>&1; then
|
||||
log "[OK] AD forwarder deployment present"
|
||||
else
|
||||
log "[INFO] AD forwarder deployment not present"
|
||||
fi
|
||||
if kubectl -n "$NAMESPACE" get svc "$KRB5_AD_SERVICE_NAME" >/dev/null 2>&1; then
|
||||
log "[OK] AD forwarder service present"
|
||||
else
|
||||
log "[INFO] AD forwarder service not present"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
initialize|configure|update|reload)
|
||||
initialize
|
||||
;;
|
||||
test)
|
||||
run_test
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
cleanup)
|
||||
ensure_tools
|
||||
ensure_namespace
|
||||
cleanup_ad_forwarder
|
||||
;;
|
||||
*)
|
||||
err "Usage: $0 {initialize|configure|update|reload|test|status|cleanup}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
195
etc/init_kerberos_test.sh
Executable file
195
etc/init_kerberos_test.sh
Executable file
@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# init_kerberos_test.sh
|
||||
# Purpose:
|
||||
# - Run Kerberos authentication checks inside a Kubernetes pod
|
||||
# - Uses the prole-krb5-conf ConfigMap for krb5.conf
|
||||
|
||||
# Initialize SCRIPT_DIR
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
ACTION=${1:-test}
|
||||
|
||||
# Load env
|
||||
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
|
||||
|
||||
NAMESPACE=${NAMESPACE:-default}
|
||||
KRB5_REALM=${KRB5_REALM:-${REALM:-}}
|
||||
KRB5_KDC=${KRB5_KDC:-}
|
||||
KRB5_USER=${KRB5_USER:-${KRB5_USERNAME:-}}
|
||||
KRB5_PASSWORD=${KRB5_PASSWORD:-}
|
||||
KRB5_TEST_IMAGE=${KRB5_TEST_IMAGE:-${PROLE_KRB_TEST_IMAGE:-}}
|
||||
KEEP_POD=${KEEP_POD:-0}
|
||||
KRB5_TEST_HOST_NETWORK=${KRB5_TEST_HOST_NETWORK:-0}
|
||||
KRB5_TEST_DNS_POLICY=${KRB5_TEST_DNS_POLICY:-}
|
||||
|
||||
ensure_tools() {
|
||||
for t in kubectl; do
|
||||
command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; }
|
||||
done
|
||||
}
|
||||
|
||||
ensure_namespace() {
|
||||
if ! kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then
|
||||
echo "Creating namespace '$NAMESPACE' ..."
|
||||
kubectl create namespace "$NAMESPACE" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
detect_image() {
|
||||
if [[ -n "$KRB5_TEST_IMAGE" ]]; then
|
||||
echo "$KRB5_TEST_IMAGE"
|
||||
return
|
||||
fi
|
||||
local version_file
|
||||
if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/conf/postgresql/.version" ]]; then
|
||||
version_file="$PROLE_HOME/conf/postgresql/.version"
|
||||
elif [[ -f "$SCRIPT_DIR/../conf/postgresql/.version" ]]; then
|
||||
version_file="$SCRIPT_DIR/../conf/postgresql/.version"
|
||||
else
|
||||
version_file=""
|
||||
fi
|
||||
|
||||
if [[ -n "$version_file" && -f "$version_file" ]]; then
|
||||
echo "prole-db:$(cat "$version_file" | tr -d '[:space:]')"
|
||||
else
|
||||
echo "prole-db:latest"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_configmap() {
|
||||
if kubectl -n "$NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then
|
||||
return
|
||||
fi
|
||||
echo "ConfigMap prole-krb5-conf not found. Running init_openbao.sh update..."
|
||||
if [[ -x "$SCRIPT_DIR/init_openbao.sh" ]]; then
|
||||
KRB5_REALM="$KRB5_REALM" KRB5_KDC="$KRB5_KDC" "$SCRIPT_DIR/init_openbao.sh" update || true
|
||||
fi
|
||||
}
|
||||
|
||||
create_test_pod() {
|
||||
local pod_name="$1"
|
||||
local image="$2"
|
||||
local host_net_block=""
|
||||
if [[ "$KRB5_TEST_HOST_NETWORK" == "1" ]]; then
|
||||
local dns_policy
|
||||
dns_policy=${KRB5_TEST_DNS_POLICY:-Default}
|
||||
host_net_block=$' hostNetwork: true\n dnsPolicy: '"$dns_policy"$'\n'
|
||||
fi
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: ${pod_name}
|
||||
namespace: ${NAMESPACE}
|
||||
labels:
|
||||
app: prole-kerberos-test
|
||||
spec:
|
||||
${host_net_block} restartPolicy: Never
|
||||
containers:
|
||||
- name: kerberos-test
|
||||
image: ${image}
|
||||
command: ["sleep","3600"]
|
||||
volumeMounts:
|
||||
- name: krb5-conf
|
||||
mountPath: /etc/krb5.conf
|
||||
subPath: krb5.conf
|
||||
volumes:
|
||||
- name: krb5-conf
|
||||
configMap:
|
||||
name: prole-krb5-conf
|
||||
items:
|
||||
- key: krb5.conf
|
||||
path: krb5.conf
|
||||
EOF
|
||||
}
|
||||
|
||||
run_test() {
|
||||
ensure_tools
|
||||
ensure_namespace
|
||||
|
||||
if [[ -z "$KRB5_REALM" || -z "$KRB5_USER" || -z "$KRB5_PASSWORD" || -z "$KRB5_KDC" ]]; then
|
||||
echo "ERROR: Missing Kerberos configuration. Ensure KRB5_REALM, KRB5_KDC, KRB5_USER, KRB5_PASSWORD are set." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ensure_configmap
|
||||
|
||||
local image pod_name
|
||||
image=$(detect_image)
|
||||
pod_name="prole-krb-test-$(date +%s)"
|
||||
|
||||
echo "Creating Kerberos test pod '$pod_name' in namespace '$NAMESPACE' using image '$image'..."
|
||||
create_test_pod "$pod_name" "$image"
|
||||
|
||||
echo "Waiting for pod to become ready..."
|
||||
if ! kubectl -n "$NAMESPACE" wait --for=condition=Ready pod/"$pod_name" --timeout=90s; then
|
||||
echo "Pod did not become ready. Describing pod:"
|
||||
kubectl -n "$NAMESPACE" describe pod "$pod_name" || true
|
||||
if [[ "$KEEP_POD" != "1" ]]; then
|
||||
kubectl -n "$NAMESPACE" delete pod "$pod_name" --ignore-not-found
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Checking for kinit in pod..."
|
||||
if ! kubectl -n "$NAMESPACE" exec "$pod_name" -- sh -c 'command -v kinit >/dev/null 2>&1'; then
|
||||
echo "ERROR: kinit not found in test pod image '$image'." >&2
|
||||
if [[ "$KEEP_POD" != "1" ]]; then
|
||||
kubectl -n "$NAMESPACE" delete pod "$pod_name" --ignore-not-found
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Running kinit for ${KRB5_USER}@${KRB5_REALM} ..."
|
||||
if ! printf '%s\n' "$KRB5_PASSWORD" | kubectl -n "$NAMESPACE" exec -i "$pod_name" -- kinit "${KRB5_USER}@${KRB5_REALM}"; then
|
||||
echo "ERROR: kinit failed for ${KRB5_USER}@${KRB5_REALM}." >&2
|
||||
if [[ "$KEEP_POD" != "1" ]]; then
|
||||
kubectl -n "$NAMESPACE" delete pod "$pod_name" --ignore-not-found
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Kerberos ticket cache:"
|
||||
kubectl -n "$NAMESPACE" exec "$pod_name" -- klist || true
|
||||
|
||||
if [[ "${REALM_JOIN:-0}" == "1" ]]; then
|
||||
echo "Attempting realm join inside test pod..."
|
||||
if kubectl -n "$NAMESPACE" exec "$pod_name" -- sh -c 'command -v realm >/dev/null 2>&1'; then
|
||||
printf '%s\n' "$KRB5_PASSWORD" | kubectl -n "$NAMESPACE" exec -i "$pod_name" -- realm join --user "$KRB5_USER" "$KRB5_REALM" || true
|
||||
else
|
||||
echo "realm command not found in image; skipping realm join."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$KEEP_POD" != "1" ]]; then
|
||||
kubectl -n "$NAMESPACE" delete pod "$pod_name" --ignore-not-found
|
||||
else
|
||||
echo "KEEP_POD=1 set; leaving test pod running: $pod_name"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
test)
|
||||
run_test
|
||||
;;
|
||||
cleanup)
|
||||
ensure_tools
|
||||
echo "Deleting kerberos test pods in namespace '$NAMESPACE'..."
|
||||
kubectl -n "$NAMESPACE" delete pod -l app=prole-kerberos-test --ignore-not-found
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {test|cleanup}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
187
etc/init_prole-db-backup.sh
Executable file
187
etc/init_prole-db-backup.sh
Executable file
@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# init_prole-db-backup.sh
|
||||
# Purpose:
|
||||
# - Configure CloudNative-PG to backup to Garage (S3-compatible)
|
||||
# - Create initial backup
|
||||
|
||||
# Initialize SCRIPT_DIR
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
ACTION=${1:-start}
|
||||
|
||||
# Load env
|
||||
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
|
||||
|
||||
NAMESPACE=${NAMESPACE:-default}
|
||||
CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db}
|
||||
GARAGE_NAME=${GARAGE_NAME:-garage}
|
||||
GARAGE_BACKUP_BUCKET=${GARAGE_BACKUP_BUCKET:-prole-db-backups}
|
||||
GARAGE_BACKUP_KEY_NAME=${GARAGE_BACKUP_KEY_NAME:-prole-db-backup}
|
||||
GARAGE_BACKUP_SECRET_NAME=${GARAGE_BACKUP_SECRET_NAME:-prole-db-barman-s3}
|
||||
GARAGE_S3_ENDPOINT=${GARAGE_S3_ENDPOINT:-http://$GARAGE_NAME.$NAMESPACE.svc.cluster.local:3900}
|
||||
RUN_FIRST_BACKUP=${RUN_FIRST_BACKUP:-1}
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: $0 [start|backup|status]
|
||||
|
||||
Actions:
|
||||
start Configure Garage-backed backups and run initial backup
|
||||
backup Trigger a new backup now
|
||||
status Show backup resources
|
||||
USAGE
|
||||
exit 1
|
||||
}
|
||||
|
||||
ensure_tools() {
|
||||
for t in kubectl; do
|
||||
command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; }
|
||||
done
|
||||
}
|
||||
|
||||
ensure_namespace() {
|
||||
if ! kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then
|
||||
echo "Creating namespace '$NAMESPACE' ..."
|
||||
kubectl create namespace "$NAMESPACE" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_cluster() {
|
||||
if ! kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then
|
||||
echo "ERROR: CNPG cluster '$CNPG_CLUSTER_NAME' not found in namespace '$NAMESPACE'." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
get_garage_pod() {
|
||||
kubectl get pods -n "$NAMESPACE" -l "app=$GARAGE_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true
|
||||
}
|
||||
|
||||
garage_exec() {
|
||||
local pod
|
||||
pod=$(get_garage_pod)
|
||||
if [[ -z "$pod" ]]; then
|
||||
echo "ERROR: Garage pod not found in namespace '$NAMESPACE'." >&2
|
||||
exit 1
|
||||
fi
|
||||
kubectl exec -n "$NAMESPACE" "$pod" -- /garage "$@"
|
||||
}
|
||||
|
||||
parse_key_output() {
|
||||
local output="$1"
|
||||
local access_key secret_key
|
||||
access_key=$(echo "$output" | sed -nE 's/^(Access key ID|Key ID):[[:space:]]+//p' | head -n1)
|
||||
secret_key=$(echo "$output" | sed -nE 's/^(Secret access key|Secret key):[[:space:]]+//p' | head -n1)
|
||||
if [[ -z "$access_key" || -z "$secret_key" ]]; then
|
||||
return 1
|
||||
fi
|
||||
printf "%s\n%s" "$access_key" "$secret_key"
|
||||
}
|
||||
|
||||
ensure_garage_bucket_and_key() {
|
||||
echo "Ensuring Garage bucket and access key for backups ..."
|
||||
local key_info parsed access_key secret_key
|
||||
if key_info=$(garage_exec key info --show-secret "$GARAGE_BACKUP_KEY_NAME" 2>/dev/null); then
|
||||
:
|
||||
else
|
||||
key_info=$(garage_exec key create "$GARAGE_BACKUP_KEY_NAME")
|
||||
fi
|
||||
|
||||
if ! parsed=$(parse_key_output "$key_info"); then
|
||||
echo "ERROR: Unable to parse Garage key output." >&2
|
||||
echo "$key_info" >&2
|
||||
exit 1
|
||||
fi
|
||||
access_key=$(echo "$parsed" | sed -n '1p')
|
||||
secret_key=$(echo "$parsed" | sed -n '2p')
|
||||
|
||||
if ! garage_exec bucket info "$GARAGE_BACKUP_BUCKET" >/dev/null 2>&1; then
|
||||
garage_exec bucket create "$GARAGE_BACKUP_BUCKET"
|
||||
fi
|
||||
|
||||
garage_exec bucket allow --read --write --owner --key "$GARAGE_BACKUP_KEY_NAME" "$GARAGE_BACKUP_BUCKET" || true
|
||||
|
||||
echo "Creating/updating Kubernetes secret '$GARAGE_BACKUP_SECRET_NAME' ..."
|
||||
kubectl create secret generic "$GARAGE_BACKUP_SECRET_NAME" -n "$NAMESPACE" \
|
||||
--from-literal=ACCESS_KEY_ID="$access_key" \
|
||||
--from-literal=SECRET_ACCESS_KEY="$secret_key" \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
}
|
||||
|
||||
configure_cnpg_backup() {
|
||||
echo "Configuring CNPG backup to use Garage bucket '$GARAGE_BACKUP_BUCKET' ..."
|
||||
kubectl patch cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" --type merge -p "{
|
||||
\"spec\": {
|
||||
\"backup\": {
|
||||
\"barmanObjectStore\": {
|
||||
\"destinationPath\": \"s3://$GARAGE_BACKUP_BUCKET/\",
|
||||
\"endpointURL\": \"$GARAGE_S3_ENDPOINT\",
|
||||
\"s3Credentials\": {
|
||||
\"accessKeyId\": {\"name\": \"$GARAGE_BACKUP_SECRET_NAME\", \"key\": \"ACCESS_KEY_ID\"},
|
||||
\"secretAccessKey\": {\"name\": \"$GARAGE_BACKUP_SECRET_NAME\", \"key\": \"SECRET_ACCESS_KEY\"}
|
||||
},
|
||||
\"wal\": {\"compression\": \"gzip\"},
|
||||
\"data\": {\"compression\": \"gzip\"}
|
||||
},
|
||||
\"retentionPolicy\": \"30d\"
|
||||
}
|
||||
}
|
||||
}"
|
||||
}
|
||||
|
||||
trigger_backup() {
|
||||
local backup_name
|
||||
backup_name="${CNPG_CLUSTER_NAME}-backup-$(date +%Y%m%d%H%M%S)"
|
||||
echo "Triggering backup $backup_name ..."
|
||||
kubectl apply -n "$NAMESPACE" -f - <<BACKUP
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Backup
|
||||
metadata:
|
||||
name: $backup_name
|
||||
spec:
|
||||
cluster:
|
||||
name: $CNPG_CLUSTER_NAME
|
||||
BACKUP
|
||||
}
|
||||
|
||||
status() {
|
||||
ensure_tools
|
||||
echo "Backups for cluster '$CNPG_CLUSTER_NAME' in namespace '$NAMESPACE':"
|
||||
kubectl get backup -n "$NAMESPACE" | grep "$CNPG_CLUSTER_NAME" || true
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
start)
|
||||
ensure_tools
|
||||
ensure_namespace
|
||||
ensure_cluster
|
||||
ensure_garage_bucket_and_key
|
||||
configure_cnpg_backup
|
||||
if [[ "$RUN_FIRST_BACKUP" == "1" ]]; then
|
||||
trigger_backup
|
||||
fi
|
||||
;;
|
||||
backup)
|
||||
ensure_tools
|
||||
ensure_namespace
|
||||
ensure_cluster
|
||||
trigger_backup
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
@ -86,6 +86,12 @@ start() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4. garage is configured
|
||||
if ! kubectl get statefulset garage -n "$NAMESPACE" >/dev/null 2>&1 && ! kubectl get deployment garage -n "$NAMESPACE" >/dev/null 2>&1; then
|
||||
echo "ERROR: Garage is not deployed." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure cluster exists before attempting to patch image
|
||||
if ! kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then
|
||||
echo "Cluster '$CNPG_CLUSTER_NAME' not found in namespace '$NAMESPACE'. Applying manifest..."
|
||||
@ -318,7 +324,8 @@ rollout() {
|
||||
}
|
||||
|
||||
backup() {
|
||||
echo "Backup - tbd, when we configure s3 or other block store"
|
||||
ensure_tools
|
||||
bash "$SCRIPT_DIR/init_prole-db-backup.sh" start
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
95
final_comprehensive_test.sh
Executable file
95
final_comprehensive_test.sh
Executable file
@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=========================================="
|
||||
echo " Prole Installer - Final Verification"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Test suite
|
||||
TESTS_PASSED=0
|
||||
TESTS_TOTAL=0
|
||||
|
||||
run_test() {
|
||||
TESTS_TOTAL=$((TESTS_TOTAL + 1))
|
||||
echo -n "[$TESTS_TOTAL] $1... "
|
||||
shift
|
||||
if "$@" > /dev/null 2>&1; then
|
||||
echo "✓"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
return 0
|
||||
else
|
||||
echo "✗"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Core functionality tests
|
||||
run_test "Install.py imports" python3 -c "from install import ProleController, get_resource_path"
|
||||
run_test "Ncurses modules" python3 -c "from installer.ncurses_installer import run_ncurses_installer"
|
||||
run_test "Command-line interface" python3 install.py --help
|
||||
run_test "Makefile" make help
|
||||
|
||||
# Resource tests
|
||||
run_test "Image resources" python3 -c "
|
||||
from pathlib import Path
|
||||
import sys
|
||||
sys.path.insert(0, str(Path.cwd()))
|
||||
from install import get_resource_path
|
||||
for img in ['img/proleIcon.png', 'img/proleLogo.png', 'img/proleLogoSepia.png']:
|
||||
if not get_resource_path(img).exists():
|
||||
sys.exit(1)
|
||||
"
|
||||
|
||||
run_test "Binary executable" test -x prole-net/prole-scan
|
||||
run_test "App bundle" test -d "prole-app/dist/Prole Tools.app"
|
||||
|
||||
# Build system tests
|
||||
run_test "Spec generator" python3 scripts/generate_spec.py
|
||||
run_test "Icon conversion" make build/prole.icns
|
||||
|
||||
# Verify spec includes everything
|
||||
echo -n "[$((TESTS_TOTAL + 1))] Spec includes all resources... "
|
||||
TESTS_TOTAL=$((TESTS_TOTAL + 1))
|
||||
if grep -q "prole-app/dist/Prole Tools.app" installer.spec && \
|
||||
grep -q "prole-net/prole-scan" installer.spec && \
|
||||
grep -q "img.*img" installer.spec; then
|
||||
echo "✓"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
echo "✗"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Test Results: $TESTS_PASSED/$TESTS_TOTAL passed"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
if [ $TESTS_PASSED -eq $TESTS_TOTAL ]; then
|
||||
echo "✓ All tests passed!"
|
||||
echo ""
|
||||
echo "Features ready:"
|
||||
echo " ✓ Ncurses terminal interface"
|
||||
echo " ✓ Automatic display detection"
|
||||
echo " ✓ Image resources embedded"
|
||||
echo " ✓ Binary executables embedded (prole-scan)"
|
||||
echo " ✓ App bundles embedded (Prole Tools.app)"
|
||||
echo " ✓ Build system configured"
|
||||
echo ""
|
||||
echo "Embedded resources:"
|
||||
echo " • Images: ~11 MB"
|
||||
echo " • prole-scan: 6.8 MB (universal)"
|
||||
echo " • Prole Tools.app: ~12 MB"
|
||||
echo " • Expected final size: ~50-100 MB"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Build: make package"
|
||||
echo " 2. Test: ./dist/Prole\\ Installer.app/Contents/MacOS/prole-installer"
|
||||
echo " 3. Install: cp -r 'dist/Prole Installer.app' /Applications/"
|
||||
echo ""
|
||||
exit 0
|
||||
else
|
||||
echo "✗ Some tests failed"
|
||||
exit 1
|
||||
fi
|
||||
134
final_test.sh
Executable file
134
final_test.sh
Executable file
@ -0,0 +1,134 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=============================================="
|
||||
echo " FINAL COMPREHENSIVE TEST"
|
||||
echo " Prole Database Installer"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
TESTS_PASSED=0
|
||||
TESTS_TOTAL=0
|
||||
|
||||
test_item() {
|
||||
TESTS_TOTAL=$((TESTS_TOTAL + 1))
|
||||
echo -n "[$TESTS_TOTAL] $1... "
|
||||
shift
|
||||
if "$@" > /dev/null 2>&1; then
|
||||
echo "✓"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
return 0
|
||||
else
|
||||
echo "✗"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Core Features
|
||||
echo "CORE FEATURES"
|
||||
test_item "Install.py imports" python3 -c "from install import ProleController, get_resource_path"
|
||||
test_item "Ncurses interface" python3 -c "from installer.ncurses_installer import run_ncurses_installer"
|
||||
test_item "Display auto-detection" python3 -c "from install import has_display"
|
||||
test_item "CLI arguments" python3 install.py --help
|
||||
|
||||
# Build System
|
||||
echo ""
|
||||
echo "BUILD SYSTEM"
|
||||
test_item "Makefile" make help
|
||||
test_item "Spec generator" python3 scripts/generate_spec.py
|
||||
test_item "Icon conversion" make build/prole.icns
|
||||
|
||||
# Image Resources
|
||||
echo ""
|
||||
echo "IMAGE RESOURCES"
|
||||
test_item "proleIcon.png" test -f img/proleIcon.png
|
||||
test_item "proleLogo.png" test -f img/proleLogo.png
|
||||
test_item "proleLogoSepia.png" test -f img/proleLogoSepia.png
|
||||
|
||||
# Binary Resources
|
||||
echo ""
|
||||
echo "BINARY RESOURCES"
|
||||
test_item "prole-scan exists" test -f prole-net/prole-scan
|
||||
test_item "prole-scan executable" test -x prole-net/prole-scan
|
||||
|
||||
# App Bundle
|
||||
echo ""
|
||||
echo "APP BUNDLE"
|
||||
test_item "Prole Tools.app exists" test -d "prole-app/dist/Prole Tools.app"
|
||||
|
||||
# Docker Build Context
|
||||
echo ""
|
||||
echo "DOCKER BUILD CONTEXT"
|
||||
test_item "prole-db directory" test -d prole-db
|
||||
test_item "Dockerfile exists" test -f prole-db/Dockerfile
|
||||
|
||||
# Spec Verification
|
||||
echo ""
|
||||
echo "SPEC FILE VERIFICATION"
|
||||
python3 scripts/generate_spec.py > /dev/null 2>&1
|
||||
test_item "Spec includes img" grep -q "img" installer.spec
|
||||
test_item "Spec includes prole-db" grep -q "prole-db" installer.spec
|
||||
test_item "Spec includes prole-scan" grep -q "prole-scan" installer.spec
|
||||
test_item "Spec includes Prole Tools" grep -q "Prole Tools" installer.spec
|
||||
|
||||
# Resource Path Tests
|
||||
echo ""
|
||||
echo "RESOURCE PATH RESOLUTION"
|
||||
test_item "Image paths work" python3 -c "
|
||||
from pathlib import Path
|
||||
import sys
|
||||
sys.path.insert(0, str(Path.cwd()))
|
||||
from install import get_resource_path
|
||||
assert get_resource_path('img/proleIcon.png').exists()
|
||||
"
|
||||
test_item "Binary paths work" python3 -c "
|
||||
from pathlib import Path
|
||||
import sys
|
||||
sys.path.insert(0, str(Path.cwd()))
|
||||
from install import get_resource_path
|
||||
assert get_resource_path('prole-net/prole-scan').exists()
|
||||
"
|
||||
test_item "Docker context works" python3 -c "
|
||||
from pathlib import Path
|
||||
import sys
|
||||
sys.path.insert(0, str(Path.cwd()))
|
||||
from install import get_resource_path
|
||||
assert get_resource_path('prole-db').exists()
|
||||
"
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo " RESULTS: $TESTS_PASSED/$TESTS_TOTAL PASSED"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
if [ $TESTS_PASSED -eq $TESTS_TOTAL ]; then
|
||||
echo "✓✓✓ ALL TESTS PASSED ✓✓✓"
|
||||
echo ""
|
||||
echo "READY TO BUILD:"
|
||||
echo " make package"
|
||||
echo ""
|
||||
echo "FEATURES IMPLEMENTED:"
|
||||
echo " ✓ Ncurses terminal interface"
|
||||
echo " ✓ Automatic display detection"
|
||||
echo " ✓ Static universal binary build system"
|
||||
echo " ✓ Image resources embedded"
|
||||
echo " ✓ prole-scan binary embedded"
|
||||
echo " ✓ Prole Tools.app embedded"
|
||||
echo " ✓ Docker build context embedded"
|
||||
echo " ✓ Docker build uses ~/.prole/build (writable)"
|
||||
echo ""
|
||||
echo "PACKAGE WILL INCLUDE:"
|
||||
echo " • Images (~11 MB)"
|
||||
echo " • prole-scan (6.8 MB)"
|
||||
echo " • Prole Tools.app (~12 MB)"
|
||||
echo " • prole-db build context (~1-5 MB)"
|
||||
echo " • Python runtime + deps (~30-50 MB)"
|
||||
echo " • Total: ~50-100 MB"
|
||||
echo ""
|
||||
exit 0
|
||||
else
|
||||
echo "✗ SOME TESTS FAILED"
|
||||
echo "Fix issues before building package"
|
||||
exit 1
|
||||
fi
|
||||
1125
install.py
1125
install.py
File diff suppressed because it is too large
Load Diff
@ -163,15 +163,6 @@ DEPENDENCIES = [
|
||||
"check_cmd": "k3d --version",
|
||||
"bin": "k3d",
|
||||
},
|
||||
{
|
||||
"id": "ollama",
|
||||
"name": "Ollama",
|
||||
"description": "Local LLM runtime used by the deployment agent",
|
||||
"url": "https://ollama.com",
|
||||
"install_cmd": "brew install ollama",
|
||||
"check_cmd": "ollama --version",
|
||||
"bin": "ollama",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
||||
@ -442,43 +442,6 @@ def _ensure_docker_running(timeout: int = 120) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _ensure_ollama_running(timeout: int = 60) -> bool:
|
||||
"""Start Ollama serve if not running; return True when the HTTP API is reachable."""
|
||||
def api_ok() -> bool:
|
||||
try:
|
||||
# Using curl if available
|
||||
r = subprocess.run(["bash", "-lc", "curl -s http://127.0.0.1:11434/api/tags >/dev/null"], timeout=8)
|
||||
return r.returncode == 0
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
if api_ok():
|
||||
return True
|
||||
|
||||
# Prefer Homebrew service if available
|
||||
try:
|
||||
has_brew = subprocess.run(["bash", "-lc", "command -v brew >/dev/null"], timeout=5).returncode == 0
|
||||
if has_brew:
|
||||
subprocess.run(["bash", "-lc", "brew services start ollama"], timeout=20)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fallback: nohup ollama serve &
|
||||
if not api_ok():
|
||||
try:
|
||||
subprocess.Popen(["bash", "-lc", "nohup ollama serve >/tmp/ollama.log 2>&1 &"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
import time as _t
|
||||
start = _t.time()
|
||||
while _t.time() - start < timeout:
|
||||
if api_ok():
|
||||
return True
|
||||
_t.sleep(2)
|
||||
return False
|
||||
|
||||
|
||||
def _ensure_workstation_container() -> bool:
|
||||
"""Run or start the prole-workstation container with required ports."""
|
||||
version = ws.get_workstation_version(cfg.PROJECT_ROOT)
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Prole Installer: Ollama
|
||||
# Installs Ollama via Homebrew if missing
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "[prole] Checking Ollama..."
|
||||
if ! command -v ollama >/dev/null 2>&1; then
|
||||
echo "[prole] Installing Ollama via brew..."
|
||||
brew install ollama
|
||||
else
|
||||
echo "[prole] Ollama already installed"
|
||||
fi
|
||||
|
||||
echo "[prole] Ollama OK"
|
||||
@ -2,7 +2,7 @@ apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: prole-krb5-conf
|
||||
namespace: prole-chrisfu-a05806
|
||||
namespace: prole-chrisfu-deadbeef
|
||||
data:
|
||||
krb5.conf: |
|
||||
[libdefaults]
|
||||
|
||||
43
k8s/prole/garage-configmap.yaml
Normal file
43
k8s/prole/garage-configmap.yaml
Normal file
@ -0,0 +1,43 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: garage-config
|
||||
labels:
|
||||
app: garage
|
||||
data:
|
||||
garage.toml: |
|
||||
replication_factor = 1
|
||||
consistency_mode = "consistent"
|
||||
|
||||
metadata_dir = "/var/lib/garage/meta"
|
||||
data_dir = "/var/lib/garage/data"
|
||||
metadata_snapshots_dir = "/var/lib/garage/snapshots"
|
||||
metadata_fsync = true
|
||||
data_fsync = false
|
||||
disable_scrub = false
|
||||
use_local_tz = false
|
||||
|
||||
db_engine = "lmdb"
|
||||
|
||||
block_size = "1M"
|
||||
block_ram_buffer_max = "256MiB"
|
||||
block_max_concurrent_reads = 16
|
||||
block_max_concurrent_writes_per_request = 10
|
||||
|
||||
compression_level = 1
|
||||
|
||||
rpc_secret_file = "/var/lib/garage/secrets/rpc_secret"
|
||||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_public_addr = "garage:3901"
|
||||
bootstrap_peers = []
|
||||
|
||||
[s3_api]
|
||||
api_bind_addr = "[::]:3900"
|
||||
s3_region = "garage"
|
||||
root_domain = ".s3.garage"
|
||||
|
||||
[admin]
|
||||
api_bind_addr = "0.0.0.0:3903"
|
||||
metrics_require_token = true
|
||||
metrics_token_file = "/var/lib/garage/secrets/metrics_token"
|
||||
admin_token_file = "/var/lib/garage/secrets/admin_token"
|
||||
20
k8s/prole/garage-service.yaml
Normal file
20
k8s/prole/garage-service.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: garage
|
||||
labels:
|
||||
app: garage
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: garage
|
||||
ports:
|
||||
- name: s3
|
||||
port: 3900
|
||||
targetPort: s3
|
||||
- name: rpc
|
||||
port: 3901
|
||||
targetPort: rpc
|
||||
- name: admin
|
||||
port: 3903
|
||||
targetPort: admin
|
||||
71
k8s/prole/garage-statefulset.yaml
Normal file
71
k8s/prole/garage-statefulset.yaml
Normal file
@ -0,0 +1,71 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: garage
|
||||
labels:
|
||||
app: garage
|
||||
spec:
|
||||
serviceName: garage
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: garage
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: garage
|
||||
spec:
|
||||
containers:
|
||||
- name: garage
|
||||
image: dxflrs/garage:v1.3.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /garage
|
||||
args:
|
||||
- server
|
||||
env:
|
||||
- name: GARAGE_ALLOW_WORLD_READABLE_SECRETS
|
||||
value: "true"
|
||||
ports:
|
||||
- name: s3
|
||||
containerPort: 3900
|
||||
- name: rpc
|
||||
containerPort: 3901
|
||||
- name: admin
|
||||
containerPort: 3903
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: s3
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: s3
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 20
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/garage.toml
|
||||
subPath: garage.toml
|
||||
readOnly: true
|
||||
- name: secrets
|
||||
mountPath: /var/lib/garage/secrets
|
||||
readOnly: true
|
||||
- name: data
|
||||
mountPath: /var/lib/garage
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: garage-config
|
||||
- name: secrets
|
||||
secret:
|
||||
secretName: garage-secrets
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
@ -2,6 +2,9 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- garage-configmap.yaml
|
||||
- garage-statefulset.yaml
|
||||
- garage-service.yaml
|
||||
- prole-db.yaml
|
||||
- prole-db-postgres-service.yaml
|
||||
- prole-configmap.yaml
|
||||
|
||||
55
network_description.txt
Normal file
55
network_description.txt
Normal file
@ -0,0 +1,55 @@
|
||||
Network Discovery Summary:
|
||||
Primary Router: 10.0.0.1 (eero_5d:50:f2)
|
||||
DNS Servers:
|
||||
Detected Devices:
|
||||
- 10.0.0.66 [72:ff:7f:82:e9:ed] (72:ff:7f:82:e9:ed): Ports [22, 445, 5900, 11434, 88], Services: ['SSH', 'VNC', 'SMB/CIFS (Possible Windows/AD)', 'Ollama', 'Active Directory Related']
|
||||
- 10.0.0.189 [00:17:88:a3:2f:cc] (PhilipsLight_a3:2f:cc): Ports [80, 443], Services: ['Web Server']
|
||||
- 10.0.0.1 [9c:57:bc:5d:50:f2] (eero_5d:50:f2): Ports [53], Services: ['DNS']
|
||||
- 10.0.0.33 [ec:b5:fa:b0:76:e4] (PhilipsLight_b0:76:e4): Ports [80, 443], Services: ['Web Server']
|
||||
- 10.0.0.46 [d4:f7:d5:40:ab:17] (SonyInteract_40:ab:17): Ports [], Services: []
|
||||
- 10.0.0.163 [b0:8b:a8:f8:96:92] (AmazonTechno_f8:96:92): Ports [], Services: []
|
||||
- 10.0.0.203 [00:11:32:3b:2f:08] (Synology_3b:2f:08): Ports [22, 80, 443, 2049, 445], Services: ['SSH', 'NFS', 'Web Server', 'SMB/CIFS (Possible Windows/AD)']
|
||||
- 10.0.0.100 [24:fc:e5:51:cf:74] (SamsungElect_51:cf:74): Ports [], Services: []
|
||||
- 10.0.0.73 [4c:a9:19:b3:12:f8] (TuyaSmart_b3:12:f8): Ports [], Services: []
|
||||
- 10.0.0.62 [b8:06:0d:b2:4f:4c] (TuyaSmart_b2:4f:4c): Ports [], Services: []
|
||||
- 10.0.0.107 [b8:06:0d:b7:7c:56] (TuyaSmart_b7:7c:56): Ports [], Services: []
|
||||
- 10.0.0.66\ [72:ff:7f:82:e9:ed] (72:ff:7f:82:e9:ed): Ports [], Services: []
|
||||
- 10.0.0.2 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.3 [Unknown] (Unknown): Ports [22, 53, 2049, 445, 5900, 88, 389, 636], Services: ['DNS', 'SSH', 'NFS', 'VNC', 'SMB/CIFS (Possible Windows/AD)', 'Active Directory Related']
|
||||
- 10.0.0.4 [Unknown] (Unknown): Ports [22, 53, 443, 2049], Services: ['DNS', 'SSH', 'NFS', 'Web Server']
|
||||
- 10.0.0.5 [Unknown] (Unknown): Ports [22, 53, 443, 2049, 5900], Services: ['DNS', 'SSH', 'NFS', 'VNC', 'Web Server']
|
||||
- 10.0.0.26 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.35 [Unknown] (Unknown): Ports [22], Services: ['SSH']
|
||||
- 10.0.0.36 [Unknown] (Unknown): Ports [22], Services: ['SSH']
|
||||
- 10.0.0.37 [Unknown] (Unknown): Ports [80], Services: ['Web Server']
|
||||
- 10.0.0.41 [Unknown] (Unknown): Ports [22], Services: ['SSH']
|
||||
- 10.0.0.45 [Unknown] (Unknown): Ports [80, 443], Services: ['Web Server']
|
||||
- 10.0.0.48 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.58 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.95 [Unknown] (Unknown): Ports [22, 53, 443, 2049, 5900], Services: ['DNS', 'SSH', 'NFS', 'VNC', 'Web Server']
|
||||
- 10.0.0.99 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.111 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.112 [Unknown] (Unknown): Ports [80, 443], Services: ['Web Server']
|
||||
- 10.0.0.113 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.116 [Unknown] (Unknown): Ports [445], Services: ['SMB/CIFS (Possible Windows/AD)']
|
||||
- 10.0.0.117 [Unknown] (Unknown): Ports [80], Services: ['Web Server']
|
||||
- 10.0.0.123 [Unknown] (Unknown): Ports [80], Services: ['Web Server']
|
||||
- 10.0.0.124 [Unknown] (Unknown): Ports [80], Services: ['Web Server']
|
||||
- 10.0.0.127 [Unknown] (Unknown): Ports [22], Services: ['SSH']
|
||||
- 10.0.0.128 [Unknown] (Unknown): Ports [22, 445, 5900, 88], Services: ['SSH', 'VNC', 'SMB/CIFS (Possible Windows/AD)', 'Active Directory Related']
|
||||
- 10.0.0.130 [Unknown] (Unknown): Ports [53], Services: ['DNS']
|
||||
- 10.0.0.143 [Unknown] (Unknown): Ports [53], Services: ['DNS']
|
||||
- 10.0.0.145 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.166 [Unknown] (Unknown): Ports [445, 5900, 88], Services: ['VNC', 'SMB/CIFS (Possible Windows/AD)', 'Active Directory Related']
|
||||
- 10.0.0.170 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.175 [Unknown] (Unknown): Ports [80], Services: ['Web Server']
|
||||
- 10.0.0.180 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.188 [Unknown] (Unknown): Ports [80, 443], Services: ['Web Server']
|
||||
- 10.0.0.193 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.196 [Unknown] (Unknown): Ports [53], Services: ['DNS']
|
||||
- 10.0.0.199 [Unknown] (Unknown): Ports [], Services: []
|
||||
- 10.0.0.204 [Unknown] (Unknown): Ports [22, 3389, 445, 5900, 11434], Services: ['SSH', 'VNC', 'SMB/CIFS (Possible Windows/AD)', 'RDP (Windows)', 'Ollama']
|
||||
- 10.0.0.205 [Unknown] (Unknown): Ports [22, 445, 5900, 88], Services: ['SSH', 'VNC', 'SMB/CIFS (Possible Windows/AD)', 'Active Directory Related']
|
||||
- 10.0.0.206 [Unknown] (Unknown): Ports [80], Services: ['Web Server']
|
||||
- 10.0.0.207 [Unknown] (Unknown): Ports [22, 5900], Services: ['SSH', 'VNC']
|
||||
Ollama Instances found at: 10.0.0.204, 10.0.0.66
|
||||
1298
retropie_facts.json
Normal file
1298
retropie_facts.json
Normal file
File diff suppressed because it is too large
Load Diff
94
test_all_prole_home_fixes.sh
Executable file
94
test_all_prole_home_fixes.sh
Executable file
@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=============================================="
|
||||
echo " Comprehensive .prole Directory Tests"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
TESTS_PASSED=0
|
||||
TESTS_TOTAL=0
|
||||
|
||||
test_item() {
|
||||
TESTS_TOTAL=$((TESTS_TOTAL + 1))
|
||||
echo -n "[$TESTS_TOTAL] $1... "
|
||||
shift
|
||||
if "$@" > /dev/null 2>&1; then
|
||||
echo "✓"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
return 0
|
||||
else
|
||||
echo "✗"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "DIRECTORY STRUCTURE"
|
||||
test_item "Create .prole directory" python3 -c "from pathlib import Path; (Path.home() / '.prole').mkdir(exist_ok=True)"
|
||||
test_item "Create build directory" python3 -c "from pathlib import Path; (Path.home() / '.prole' / 'build').mkdir(parents=True, exist_ok=True)"
|
||||
test_item "Create scan directory" python3 -c "from pathlib import Path; (Path.home() / '.prole' / 'scan').mkdir(parents=True, exist_ok=True)"
|
||||
|
||||
echo ""
|
||||
echo "DOCKER BUILD FIX"
|
||||
test_item "prole-db directory exists" test -d prole-db
|
||||
test_item "Dockerfile exists" test -f prole-db/Dockerfile
|
||||
test_item "Build directory creation" python3 -c "from pathlib import Path; d = Path.home() / '.prole' / 'build' / 'prole-db'; d.mkdir(parents=True, exist_ok=True); assert d.exists()"
|
||||
test_item "Build directory writable" python3 -c "from pathlib import Path; f = Path.home() / '.prole' / 'build' / 'test.txt'; f.write_text('test'); f.unlink()"
|
||||
|
||||
echo ""
|
||||
echo "NETWORK SCAN FIX"
|
||||
test_item "prole-scan binary exists" test -x prole-net/prole-scan
|
||||
test_item "Scan directory creation" python3 -c "from pathlib import Path; d = Path.home() / '.prole' / 'scan'; d.mkdir(parents=True, exist_ok=True); assert d.exists()"
|
||||
test_item "Scan directory writable" python3 -c "from pathlib import Path; f = Path.home() / '.prole' / 'scan' / 'test.txt'; f.write_text('test'); f.unlink()"
|
||||
|
||||
echo ""
|
||||
echo "CODE VERIFICATION"
|
||||
test_item "Build uses .prole/build" grep -q "prole_home / \"build\"" install.py
|
||||
test_item "Scan uses .prole/scan" grep -q "prole_home / \"scan\"" install.py
|
||||
test_item "Docker build copies context" grep -q "shutil.copytree(source_dir, build_dir)" install.py
|
||||
test_item "Scan runs with cwd" grep -q "cwd=str(scan_dir)" install.py
|
||||
|
||||
echo ""
|
||||
echo "SPEC FILE VERIFICATION"
|
||||
python3 scripts/generate_spec.py > /dev/null 2>&1
|
||||
test_item "Spec includes prole-db" grep -q "prole-db" installer.spec
|
||||
test_item "Spec includes prole-scan" grep -q "prole-scan" installer.spec
|
||||
|
||||
# Cleanup
|
||||
echo ""
|
||||
echo "CLEANUP"
|
||||
python3 << 'PYTEST'
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
prole_home = Path.home() / ".prole"
|
||||
if prole_home.exists():
|
||||
shutil.rmtree(prole_home)
|
||||
print(" ✓ Cleaned up test directories")
|
||||
PYTEST
|
||||
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo " RESULTS: $TESTS_PASSED/$TESTS_TOTAL PASSED"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
if [ $TESTS_PASSED -eq $TESTS_TOTAL ]; then
|
||||
echo "✓✓✓ ALL PROLE HOME TESTS PASSED ✓✓✓"
|
||||
echo ""
|
||||
echo "FIXES IMPLEMENTED:"
|
||||
echo " ✓ Docker build uses ~/.prole/build/prole-db/"
|
||||
echo " ✓ Network scan uses ~/.prole/scan/"
|
||||
echo " ✓ Both work from read-only PyInstaller bundle"
|
||||
echo ""
|
||||
echo "DIRECTORY STRUCTURE:"
|
||||
echo " ~/.prole/"
|
||||
echo " ├── build/"
|
||||
echo " │ └── prole-db/ (Docker build context)"
|
||||
echo " └── scan/ (Network scan working dir)"
|
||||
echo ""
|
||||
echo "DISK USAGE: ~2-6 MB total"
|
||||
echo ""
|
||||
exit 0
|
||||
else
|
||||
echo "✗ SOME TESTS FAILED"
|
||||
exit 1
|
||||
fi
|
||||
42
test_build_system.sh
Executable file
42
test_build_system.sh
Executable file
@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== Prole Installer Build System Test ==="
|
||||
echo ""
|
||||
|
||||
echo "1. Testing Makefile..."
|
||||
make help > /dev/null && echo " ✓ Makefile works"
|
||||
|
||||
echo ""
|
||||
echo "2. Testing icon file..."
|
||||
test -f img/proleIcon.png && echo " ✓ Icon file exists"
|
||||
|
||||
echo ""
|
||||
echo "3. Testing Python modules..."
|
||||
python3 -c "from install import ProleController; ProleController('.')" && echo " ✓ ProleController works"
|
||||
python3 -c "from installer.ncurses_installer import run_ncurses_installer" && echo " ✓ Ncurses installer imports"
|
||||
python3 -c "from installer.ncurses_ui import CursesWindow" && echo " ✓ Ncurses UI imports"
|
||||
|
||||
echo ""
|
||||
echo "4. Testing command-line interface..."
|
||||
python3 install.py --help > /dev/null && echo " ✓ --help works"
|
||||
|
||||
echo ""
|
||||
echo "5. Testing spec generator..."
|
||||
test -f scripts/generate_spec.py && python3 scripts/generate_spec.py && echo " ✓ Spec generator works"
|
||||
|
||||
echo ""
|
||||
echo "6. Testing icon conversion..."
|
||||
make clean > /dev/null 2>&1
|
||||
make build/prole.icns > /dev/null 2>&1 && echo " ✓ Icon conversion works"
|
||||
test -f build/prole.icns && echo " ✓ ICNS file created"
|
||||
|
||||
echo ""
|
||||
echo "=== All Tests Passed ==="
|
||||
echo ""
|
||||
echo "Ready to build!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Install build dependencies: make install"
|
||||
echo " 2. Build the installer: make package"
|
||||
echo " 3. Test the build: make test"
|
||||
110
test_docker_build_fix.sh
Executable file
110
test_docker_build_fix.sh
Executable file
@ -0,0 +1,110 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "========================================"
|
||||
echo " Docker Build Fix Verification"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# Test 1: prole-db exists
|
||||
echo "1. Testing prole-db directory..."
|
||||
if [ -d "prole-db" ] && [ -f "prole-db/Dockerfile" ]; then
|
||||
echo " ✓ prole-db directory with Dockerfile exists"
|
||||
else
|
||||
echo " ✗ prole-db directory or Dockerfile missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: Spec includes prole-db
|
||||
echo "2. Testing spec file includes prole-db..."
|
||||
python3 scripts/generate_spec.py > /dev/null 2>&1
|
||||
if grep -q "('prole-db', 'prole-db')" installer.spec; then
|
||||
echo " ✓ prole-db included in spec"
|
||||
else
|
||||
echo " ✗ prole-db not in spec"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: Resource path resolution
|
||||
echo "3. Testing resource path resolution..."
|
||||
python3 << 'PYTEST'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
sys.path.insert(0, str(Path.cwd()))
|
||||
from install import get_resource_path
|
||||
|
||||
prole_db = get_resource_path("prole-db")
|
||||
if prole_db.exists() and (prole_db / "Dockerfile").exists():
|
||||
print(" ✓ get_resource_path('prole-db') works")
|
||||
else:
|
||||
print(" ✗ Resource path resolution failed")
|
||||
sys.exit(1)
|
||||
PYTEST
|
||||
if [ $? -ne 0 ]; then exit 1; fi
|
||||
|
||||
# Test 4: Build directory creation
|
||||
echo "4. Testing .prole/build directory creation..."
|
||||
python3 << 'PYTEST'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
prole_home = Path.home() / ".prole"
|
||||
build_dir = prole_home / "build" / "prole-db-test"
|
||||
|
||||
# Create and verify
|
||||
build_dir.mkdir(parents=True, exist_ok=True)
|
||||
if build_dir.exists():
|
||||
print(f" ✓ Created: {build_dir}")
|
||||
# Cleanup
|
||||
shutil.rmtree(prole_home / "build" / "prole-db-test")
|
||||
else:
|
||||
print(" ✗ Failed to create build directory")
|
||||
sys.exit(1)
|
||||
PYTEST
|
||||
if [ $? -ne 0 ]; then exit 1; fi
|
||||
|
||||
# Test 5: Copy operation
|
||||
echo "5. Testing build context copy..."
|
||||
python3 << 'PYTEST'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
sys.path.insert(0, str(Path.cwd()))
|
||||
from install import get_resource_path
|
||||
|
||||
prole_home = Path.home() / ".prole"
|
||||
build_dir = prole_home / "build" / "prole-db-test"
|
||||
source_dir = get_resource_path("prole-db")
|
||||
|
||||
try:
|
||||
if build_dir.exists():
|
||||
shutil.rmtree(build_dir)
|
||||
shutil.copytree(source_dir, build_dir)
|
||||
|
||||
# Verify
|
||||
if (build_dir / "Dockerfile").exists():
|
||||
print(f" ✓ Copied build context successfully")
|
||||
# Cleanup
|
||||
shutil.rmtree(prole_home / "build" / "prole-db-test")
|
||||
else:
|
||||
print(" ✗ Copy incomplete")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f" ✗ Copy failed: {e}")
|
||||
sys.exit(1)
|
||||
PYTEST
|
||||
if [ $? -ne 0 ]; then exit 1; fi
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " ✓ All Docker Build Fix Tests Passed"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "The Docker build will now work correctly:"
|
||||
echo " • From source: Uses PROJECT_ROOT/prole-db"
|
||||
echo " • From package: Copies to ~/.prole/build/prole-db"
|
||||
echo ""
|
||||
echo "Build directory: ~/.prole/build/prole-db"
|
||||
echo "Build command: docker build -t prole-db:TAG ."
|
||||
echo ""
|
||||
84
test_embedded_resources.sh
Executable file
84
test_embedded_resources.sh
Executable file
@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=== Testing Embedded Resources ==="
|
||||
echo ""
|
||||
|
||||
# Test 1: Images
|
||||
echo "1. Testing image resources..."
|
||||
python3 -c "
|
||||
from pathlib import Path
|
||||
import sys
|
||||
sys.path.insert(0, str(Path.cwd()))
|
||||
from install import get_resource_path
|
||||
|
||||
images = [
|
||||
'img/proleIcon.png',
|
||||
'img/proleLogo.png',
|
||||
'img/proleLogoSepia.png',
|
||||
]
|
||||
|
||||
for img in images:
|
||||
path = get_resource_path(img)
|
||||
if not path.exists():
|
||||
print(f'✗ Missing: {img}')
|
||||
sys.exit(1)
|
||||
print('✓ All images present')
|
||||
"
|
||||
if [ $? -ne 0 ]; then exit 1; fi
|
||||
|
||||
# Test 2: Binaries
|
||||
echo "2. Testing binary resources..."
|
||||
if [ -f "prole-net/prole-scan" ] && [ -x "prole-net/prole-scan" ]; then
|
||||
echo " ✓ prole-scan binary present and executable"
|
||||
else
|
||||
echo " ✗ prole-scan missing or not executable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: App bundle
|
||||
echo "3. Testing Prole Tools.app bundle..."
|
||||
if [ -d "prole-app/dist/Prole Tools.app" ]; then
|
||||
echo " ✓ Prole Tools.app present"
|
||||
else
|
||||
echo " ✗ Prole Tools.app missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 4: Spec file
|
||||
echo "4. Testing spec file generation..."
|
||||
python3 scripts/generate_spec.py > /dev/null 2>&1
|
||||
if [ -f "installer.spec" ]; then
|
||||
echo " ✓ installer.spec generated"
|
||||
else
|
||||
echo " ✗ installer.spec generation failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 5: Verify spec includes everything
|
||||
echo "5. Verifying spec includes all resources..."
|
||||
grep -q "prole-app/dist/Prole Tools.app" installer.spec && \
|
||||
grep -q "prole-net/prole-scan" installer.spec && \
|
||||
grep -q "img" installer.spec
|
||||
if [ $? -eq 0 ]; then
|
||||
echo " ✓ All resources included in spec"
|
||||
else
|
||||
echo " ✗ Some resources missing from spec"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 6: Check binary size
|
||||
echo "6. Checking binary sizes..."
|
||||
SCAN_SIZE=$(ls -lh prole-net/prole-scan | awk '{print $5}')
|
||||
echo " prole-scan: $SCAN_SIZE (universal binary)"
|
||||
APP_SIZE=$(du -sh "prole-app/dist/Prole Tools.app" | awk '{print $1}')
|
||||
echo " Prole Tools.app: $APP_SIZE"
|
||||
|
||||
echo ""
|
||||
echo "=== All Embedded Resource Tests Passed ==="
|
||||
echo ""
|
||||
echo "Resources ready for packaging:"
|
||||
echo " ✓ Images (proleIcon.png, proleLogo.png, proleLogoSepia.png)"
|
||||
echo " ✓ Binary (prole-net/prole-scan)"
|
||||
echo " ✓ App bundle (prole-app/dist/Prole Tools.app)"
|
||||
echo ""
|
||||
echo "Build with: make package"
|
||||
89
test_network_scan_fix.sh
Executable file
89
test_network_scan_fix.sh
Executable file
@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "========================================"
|
||||
echo " Network Scan Fix Verification"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# Test 1: Scan directory creation
|
||||
echo "1. Testing scan directory creation..."
|
||||
python3 << 'PYTEST'
|
||||
from pathlib import Path
|
||||
prole_home = Path.home() / ".prole"
|
||||
scan_dir = prole_home / "scan"
|
||||
scan_dir.mkdir(parents=True, exist_ok=True)
|
||||
if scan_dir.exists() and scan_dir.is_dir():
|
||||
print(" ✓ Scan directory created")
|
||||
else:
|
||||
print(" ✗ Failed to create scan directory")
|
||||
exit(1)
|
||||
PYTEST
|
||||
if [ $? -ne 0 ]; then exit 1; fi
|
||||
|
||||
# Test 2: Scan directory is writable
|
||||
echo "2. Testing scan directory is writable..."
|
||||
python3 << 'PYTEST'
|
||||
from pathlib import Path
|
||||
scan_dir = Path.home() / ".prole" / "scan"
|
||||
test_file = scan_dir / "test.txt"
|
||||
try:
|
||||
test_file.write_text("test")
|
||||
test_file.unlink()
|
||||
print(" ✓ Scan directory is writable")
|
||||
except Exception as e:
|
||||
print(f" ✗ Scan directory not writable: {e}")
|
||||
exit(1)
|
||||
PYTEST
|
||||
if [ $? -ne 0 ]; then exit 1; fi
|
||||
|
||||
# Test 3: prole-scan binary exists
|
||||
echo "3. Testing prole-scan binary..."
|
||||
if [ -x "prole-net/prole-scan" ]; then
|
||||
echo " ✓ prole-scan binary exists and is executable"
|
||||
else
|
||||
echo " ✗ prole-scan binary missing or not executable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 4: prole-scan can run from scan directory
|
||||
echo "4. Testing prole-scan runs from writable directory..."
|
||||
mkdir -p /tmp/scan-test
|
||||
cd /tmp/scan-test
|
||||
/Users/chrisfu/dev/prole/prole-net/prole-scan 2>&1 &
|
||||
SCAN_PID=$!
|
||||
sleep 2
|
||||
if ps -p $SCAN_PID > /dev/null 2>&1; then
|
||||
echo " ✓ prole-scan runs successfully"
|
||||
kill $SCAN_PID 2>/dev/null
|
||||
wait 2>/dev/null
|
||||
else
|
||||
echo " ✗ prole-scan failed to start"
|
||||
exit 1
|
||||
fi
|
||||
cd /Users/chrisfu/dev/prole
|
||||
|
||||
# Test 5: Code uses scan directory
|
||||
echo "5. Testing code uses scan directory..."
|
||||
if grep -q "scan_dir = prole_home / \"scan\"" install.py && \
|
||||
grep -q "cwd=str(scan_dir)" install.py; then
|
||||
echo " ✓ Code properly uses scan directory"
|
||||
else
|
||||
echo " ✗ Code not updated to use scan directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Cleanup test directories
|
||||
rm -rf ~/.prole/scan 2>/dev/null
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " ✓ All Network Scan Fix Tests Passed"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "Network scan will now work correctly:"
|
||||
echo " • From source: Uses writable cwd"
|
||||
echo " • From package: Uses ~/.prole/scan"
|
||||
echo ""
|
||||
echo "Scan directory: ~/.prole/scan"
|
||||
echo "Purpose: Writable working directory for prole-scan"
|
||||
echo ""
|
||||
79
test_resource_paths.py
Executable file
79
test_resource_paths.py
Executable file
@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test that resource paths work correctly."""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Add current directory to path
|
||||
sys.path.insert(0, str(Path.cwd()))
|
||||
|
||||
from install import get_resource_path
|
||||
|
||||
def test_resource_path():
|
||||
"""Test get_resource_path function."""
|
||||
print("Testing get_resource_path() function:")
|
||||
print()
|
||||
|
||||
test_cases = [
|
||||
('img/proleIcon.png', 'App icon'),
|
||||
('img/proleLogoSepia.png', 'Background logo (sepia)'),
|
||||
('img/proleLogo.png', 'Main logo'),
|
||||
('img/proleLogoBlueprint.png', 'Blueprint logo'),
|
||||
('img/proleIconblueprint.png', 'Blueprint icon'),
|
||||
]
|
||||
|
||||
all_passed = True
|
||||
for rel_path, description in test_cases:
|
||||
result = get_resource_path(rel_path)
|
||||
exists = result.exists()
|
||||
status = "✓" if exists else "✗"
|
||||
|
||||
print(f"{status} {description}")
|
||||
print(f" Path: {rel_path}")
|
||||
print(f" Resolved: {result}")
|
||||
print(f" Exists: {exists}")
|
||||
print()
|
||||
|
||||
if not exists:
|
||||
all_passed = False
|
||||
|
||||
return all_passed
|
||||
|
||||
def test_pyinstaller_simulation():
|
||||
"""Simulate PyInstaller environment."""
|
||||
print("Simulating PyInstaller environment:")
|
||||
print()
|
||||
|
||||
# Temporarily set _MEIPASS to simulate PyInstaller
|
||||
test_meipass = Path.cwd()
|
||||
sys._MEIPASS = str(test_meipass)
|
||||
|
||||
try:
|
||||
result = get_resource_path('img/proleIcon.png')
|
||||
print(f" sys._MEIPASS: {sys._MEIPASS}")
|
||||
print(f" Resolved path: {result}")
|
||||
print(f" Exists: {result.exists()}")
|
||||
print()
|
||||
return result.exists()
|
||||
finally:
|
||||
# Clean up
|
||||
delattr(sys, '_MEIPASS')
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("=" * 60)
|
||||
print("Resource Path Tests")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
test1 = test_resource_path()
|
||||
test2 = test_pyinstaller_simulation()
|
||||
|
||||
print("=" * 60)
|
||||
if test1 and test2:
|
||||
print("✓ All tests passed!")
|
||||
print()
|
||||
print("Images will be correctly included in PyInstaller build.")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("✗ Some tests failed!")
|
||||
sys.exit(1)
|
||||
Loading…
Reference in New Issue
Block a user