187 lines
7.0 KiB
Bash
Executable File
187 lines
7.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Registry plumbing: REGISTRY_MODE none | local | mirror | remote. A script, not ctlptl,
|
|
# because ctlptl cannot express `mirror`.
|
|
# Usage: registry.sh up | down | status
|
|
# Notes: docs/notes/registry.md
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")"
|
|
|
|
source ./lib/config.sh
|
|
load_config
|
|
|
|
REG_NAME="${CLUSTER}-registry"
|
|
REG_PORT="${REGISTRY_PORT:-5005}"
|
|
K="kubectl --context ${KUBECONTEXT}"
|
|
|
|
# ── CA trust ───────────────────────────────────────────────────────────────
|
|
# Copy REGISTRY_CA_FILE into every kind node's trust store (nodes don't inherit host trust).
|
|
install_ca_into_nodes() {
|
|
[ -n "${REGISTRY_CA_FILE:-}" ] || return 0
|
|
|
|
if [ ! -r "$REGISTRY_CA_FILE" ]; then
|
|
echo "REGISTRY_CA_FILE is set but not readable: $REGISTRY_CA_FILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo " distributing CA to kind nodes"
|
|
local node
|
|
for node in $(kind get nodes --name "$CLUSTER"); do
|
|
docker cp "$REGISTRY_CA_FILE" "$node:/usr/local/share/ca-certificates/corp-registry.crt"
|
|
docker exec "$node" update-ca-certificates >/dev/null 2>&1
|
|
docker exec "$node" systemctl restart containerd
|
|
done
|
|
}
|
|
|
|
# Point containerd at a registry host. The cluster config already set
|
|
# config_path=/etc/containerd/certs.d, so this is a per-node drop-in and needs no
|
|
# cluster recreate — which is what lets registry mode change on a live cluster.
|
|
write_hosts_toml() {
|
|
local host="$1" upstream="$2" skip_verify="${3:-false}"
|
|
local node
|
|
for node in $(kind get nodes --name "$CLUSTER"); do
|
|
docker exec "$node" mkdir -p "/etc/containerd/certs.d/${host}"
|
|
docker exec -i "$node" cp /dev/stdin "/etc/containerd/certs.d/${host}/hosts.toml" <<TOML
|
|
server = "${upstream}"
|
|
|
|
[host."${upstream}"]
|
|
capabilities = ["pull", "resolve"]
|
|
skip_verify = ${skip_verify}
|
|
TOML
|
|
done
|
|
}
|
|
|
|
# ── the local container (local + mirror) ───────────────────────────────────
|
|
|
|
start_registry_container() {
|
|
if [ "$(docker inspect -f '{{.State.Running}}' "$REG_NAME" 2>/dev/null || true)" = "true" ]; then
|
|
echo " registry container '$REG_NAME' already running"
|
|
return
|
|
fi
|
|
docker rm -f "$REG_NAME" >/dev/null 2>&1 || true
|
|
|
|
local args=(-d --restart=always --name "$REG_NAME"
|
|
-p "127.0.0.1:${REG_PORT}:5000")
|
|
|
|
if [ "$REGISTRY_MODE" = "mirror" ]; then
|
|
if [ -z "${REGISTRY_REMOTE_URL:-}" ]; then
|
|
echo "REGISTRY_MODE=mirror needs REGISTRY_REMOTE_URL in ctrl/.env" >&2
|
|
exit 1
|
|
fi
|
|
echo " starting pull-through cache of ${REGISTRY_REMOTE_URL}"
|
|
args+=(-e "REGISTRY_PROXY_REMOTEURL=${REGISTRY_REMOTE_URL}")
|
|
[ -n "${REGISTRY_USER:-}" ] && args+=(-e "REGISTRY_PROXY_USERNAME=${REGISTRY_USER}")
|
|
[ -n "${REGISTRY_PASSWORD:-}" ] && args+=(-e "REGISTRY_PROXY_PASSWORD=${REGISTRY_PASSWORD}")
|
|
if [ -n "${REGISTRY_CA_FILE:-}" ]; then
|
|
args+=(-v "$(readlink -f "$REGISTRY_CA_FILE"):/etc/ssl/certs/corp-ca.crt:ro")
|
|
fi
|
|
else
|
|
echo " starting local registry"
|
|
fi
|
|
|
|
docker run "${args[@]}" "$REGISTRY_IMAGE" >/dev/null
|
|
}
|
|
|
|
# The registry must share a network with the nodes so they can resolve it by
|
|
# container name; localhost inside a node is the node, not the host.
|
|
join_kind_network() {
|
|
if docker inspect -f '{{json .NetworkSettings.Networks}}' "$REG_NAME" | grep -q '"kind"'; then
|
|
return
|
|
fi
|
|
docker network connect kind "$REG_NAME" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
# The documented contract that tells tooling (Tilt, skaffold) where the local
|
|
# registry is, so they don't have to be configured separately.
|
|
apply_hosting_configmap() {
|
|
$K apply -f - <<YAML >/dev/null
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: local-registry-hosting
|
|
namespace: kube-public
|
|
data:
|
|
localRegistryHosting.v1: |
|
|
host: "localhost:${REG_PORT}"
|
|
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
|
|
YAML
|
|
}
|
|
|
|
# ── modes ──────────────────────────────────────────────────────────────────
|
|
|
|
up() {
|
|
echo "registry: ${REGISTRY_MODE}"
|
|
case "$REGISTRY_MODE" in
|
|
none)
|
|
echo " no registry — images are built straight into the node"
|
|
;;
|
|
|
|
local|mirror)
|
|
start_registry_container
|
|
join_kind_network
|
|
install_ca_into_nodes
|
|
# Nodes reach the registry by container name on the shared network;
|
|
# the host reaches it on localhost:PORT. Both names must resolve.
|
|
write_hosts_toml "localhost:${REG_PORT}" "http://${REG_NAME}:5000"
|
|
if [ "$REGISTRY_MODE" = "mirror" ]; then
|
|
# Anything asking for docker.io transparently goes to the cache.
|
|
write_hosts_toml "docker.io" "http://${REG_NAME}:5000"
|
|
fi
|
|
apply_hosting_configmap
|
|
echo " ready at localhost:${REG_PORT}"
|
|
;;
|
|
|
|
remote)
|
|
if [ -z "${REGISTRY_REMOTE_URL:-}" ]; then
|
|
echo "REGISTRY_MODE=remote needs REGISTRY_REMOTE_URL in ctrl/.env" >&2
|
|
exit 1
|
|
fi
|
|
install_ca_into_nodes
|
|
local host="${REGISTRY_REMOTE_URL#*://}"; host="${host%%/*}"
|
|
if [ -n "${REGISTRY_USER:-}" ]; then
|
|
echo " creating imagePullSecret for ${host}"
|
|
$K create secret docker-registry regcred \
|
|
--docker-server="$host" \
|
|
--docker-username="$REGISTRY_USER" \
|
|
--docker-password="$REGISTRY_PASSWORD" \
|
|
--dry-run=client -o yaml | $K apply -f - >/dev/null
|
|
# Attach to the default ServiceAccount so plain pods inherit it.
|
|
$K patch serviceaccount default \
|
|
-p '{"imagePullSecrets":[{"name":"regcred"}]}' >/dev/null
|
|
fi
|
|
echo " pulling directly from ${host}"
|
|
;;
|
|
|
|
*)
|
|
echo "unknown REGISTRY_MODE '$REGISTRY_MODE' (expected none|local|mirror|remote)" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
down() {
|
|
if docker inspect "$REG_NAME" >/dev/null 2>&1; then
|
|
echo "removing registry container '$REG_NAME'"
|
|
docker rm -f "$REG_NAME" >/dev/null
|
|
fi
|
|
}
|
|
|
|
status() {
|
|
echo "mode ${REGISTRY_MODE}"
|
|
if docker inspect "$REG_NAME" >/dev/null 2>&1; then
|
|
echo "container ${REG_NAME} $(docker inspect -f '{{.State.Status}}' "$REG_NAME")"
|
|
echo "endpoint localhost:${REG_PORT}"
|
|
else
|
|
echo "container none"
|
|
fi
|
|
[ -n "${REGISTRY_REMOTE_URL:-}" ] && echo "upstream ${REGISTRY_REMOTE_URL}"
|
|
[ -n "${REGISTRY_CA_FILE:-}" ] && echo "ca ${REGISTRY_CA_FILE}"
|
|
return 0
|
|
}
|
|
|
|
case "${1:-status}" in
|
|
up) up ;;
|
|
down) down ;;
|
|
status) status ;;
|
|
*) echo "usage: $0 [up|down|status]" >&2; exit 1 ;;
|
|
esac
|