mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-27 07:14:04 +09:00
Pinning the frozen source to a digest written into the migration picks one release for every cluster, and v1.6.0 is not even the last one that ships http-cache: v1.6.1 through v1.6.3 and v1.7.0-alpha.1 all do, and 1.6 patch releases may keep doing so. A cluster on any of those would be rolled back to an older chart by the upgrade that was meant to leave its instances alone, and an air-gapped mirror resolves the constant only if someone mirrored that exact manifest. The hook runs before the upgrade re-points cozystack-packages, so the live object still names the release the instances render from, in whatever registry the operator serves it from and with whatever pull secret and verification they configured. Reading the digest off it makes the freeze a no-op by construction and needs no knowledge of which tag was last. spec.ref.digest is used when the installer pinned one; a tag or semver ref goes through status.artifact.revision, whose digest half is the manifest digest. status.artifact.digest is the checksum of the downloaded tarball and cannot stand in. A source with neither has nothing to freeze on and the migration stops there. The copy also carries the no-delete label the platform chart puts on the original, because it is the only chart source those instances have left. The tenant grant travels with the freeze as well. cozy:tenant:admin:base enumerates the apps.cozystack.io resources a tenant owner may write, and an application that has left the catalog has no business in that list: the next sweep over it takes the entry as dead config, and the failure is silent and remote, a Forbidden for the one person who still needs to remove what they have. The chart drops httpcaches, and a cluster that freezes gets a ClusterRole under the tenant admin aggregation label instead, applied before the upgrade drops the rule so the access never lapses and carrying the verb set the chart had so nothing changes for the owner. A cluster with no instance is granted nothing. The Package delete on the no-instance path has to block. The operator reconciles off an informer cache, so a reconcile in flight against the pre-delete entry re-creates the release it owns, and with it the ApplicationDefinition and the Helm storage the following lines remove. Returning immediately made the ordering a property of the command log rather than of the cluster. The preset check's SIGPIPE note named http-cache-rd as its example of a large enum, and that chart goes away here; kubernetes-rd carries the same enums. Numbered 58 rather than 57: main already carries a migration 57, the SeaweedFS volume-size-limit one, and its targetVersion is already 58. The whole shape of this retirement was worked out here rather than anywhere reusable, so docs/deprecating-applications.md writes it down as a procedure: how to pick between migrating onto a successor and freezing, what the prune takes with it, and what the migration owes a cluster on each branch. The next application to go should not have to rediscover it. Assisted-by: LLM Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
170 lines
7.8 KiB
Bash
Executable File
170 lines
7.8 KiB
Bash
Executable File
#!/bin/sh
|
|
# Fake kubectl for hack/migration-58-http-cache-freeze.bats, modelled on
|
|
# hack/testdata/migration-55-fluxcd/kubectl. It serves only the calls migration
|
|
# 58 and lib/cozystack-version.sh make, and logs each one to $FAKE_CMDLOG.
|
|
#
|
|
# Both the frozen OCIRepository and the version stamp arrive as
|
|
# `apply --filename -`, so this fake reads stdin and branches on the manifest's
|
|
# kind rather than on the argument vector.
|
|
#
|
|
# FAKE_CMDLOG file to append a one-line record of each call to
|
|
# FAKE_HRS newline list of "<ns> <name> <chartRef.name> [<kind>]"
|
|
# for the fleet scan; <kind> is the application.kind
|
|
# label, "-" or absent for none
|
|
# FAKE_LIST_FAIL non-empty => the fleet scan exits 1 with this stderr
|
|
# FAKE_OCIREPO_REF shape of the live source's ref: "digest" (default) is
|
|
# a spec.ref.digest pin, "tag" is a tag ref resolved in
|
|
# status.artifact.revision, "unresolved" is a tag ref
|
|
# with no artifact yet
|
|
# FAKE_OCIREPO_READ_FAIL non-empty => reading the source OCIRepository exits 1
|
|
# FAKE_PATCH_FAIL non-empty => every patch exits 1 with this stderr
|
|
# FAKE_DELETE_FAIL non-empty => every delete exits 1 with this stderr
|
|
set -u
|
|
|
|
log() { [ -n "${FAKE_CMDLOG:-}" ] && printf '%s\n' "$*" >> "$FAKE_CMDLOG"; }
|
|
log "KUBECTL $*"
|
|
|
|
args="$*"
|
|
arg_after() { k="$1"; shift; p=""; for a in "$@"; do [ "$p" = "$k" ] && { printf '%s' "$a"; return; }; p="$a"; done; }
|
|
|
|
case "$args" in
|
|
*"apply --filename -"*)
|
|
manifest=$(cat)
|
|
case "$manifest" in
|
|
*"kind: ConfigMap"*|*'"kind": "ConfigMap"'*)
|
|
v=$(printf '%s\n' "$manifest" | sed -n 's/^ version: "\{0,1\}\([0-9]\{1,\}\)"\{0,1\}$/\1/p' | head -1)
|
|
log "STAMP ${v:-unknown}"
|
|
;;
|
|
*OCIRepository*)
|
|
# Record the fields the migration must carry over or set, so a copy that
|
|
# silently drops the pull secret or forgets the digest is caught.
|
|
name=$(printf '%s' "$manifest" | jq -r '.metadata.name')
|
|
digest=$(printf '%s' "$manifest" | jq -r '.spec.ref.digest // "none"')
|
|
url=$(printf '%s' "$manifest" | jq -r '.spec.url // "none"')
|
|
secret=$(printf '%s' "$manifest" | jq -r '.spec.secretRef.name // "none"')
|
|
interval=$(printf '%s' "$manifest" | jq -r '.spec.interval // "none"')
|
|
owners=$(printf '%s' "$manifest" | jq -r '(.metadata.ownerReferences // []) | length')
|
|
nodelete=$(printf '%s' "$manifest" | jq -r 'if .metadata.labels["platform.cozystack.io/no-delete"] == "true" then 1 else 0 end')
|
|
verify=$(printf '%s' "$manifest" | jq -r '.spec.verify.provider // "none"')
|
|
log "APPLY-OCIREPO $name $digest $url $secret $interval verify=$verify owners=$owners nodelete=$nodelete"
|
|
;;
|
|
*"kind: ClusterRole"*)
|
|
name=$(printf '%s\n' "$manifest" | sed -n 's/^ name: //p' | head -1)
|
|
agg=$(printf '%s\n' "$manifest" | grep -c 'rbac.cozystack.io/aggregate-to-tenant-admin: "true"')
|
|
nodelete=$(printf '%s\n' "$manifest" | grep -c 'platform.cozystack.io/no-delete: "true"')
|
|
res=$(printf '%s\n' "$manifest" | sed -n 's/^ resources: \[\(.*\)\]$/\1/p' | tr -d '" ')
|
|
verbs=$(printf '%s\n' "$manifest" | sed -n 's/^ verbs: \[\(.*\)\]$/\1/p' | tr -d '" ')
|
|
log "APPLY-CLUSTERROLE $name aggregate=$agg nodelete=$nodelete resources=$res verbs=$verbs"
|
|
;;
|
|
*)
|
|
log "APPLY-UNKNOWN"
|
|
printf 'fake kubectl: unmodelled apply\n' >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
exit 0
|
|
;;
|
|
|
|
*"get ocirepositories.source.toolkit.fluxcd.io"*)
|
|
if [ -n "${FAKE_OCIREPO_READ_FAIL:-}" ]; then
|
|
printf '%s\n' "$FAKE_OCIREPO_READ_FAIL" >&2
|
|
exit 1
|
|
fi
|
|
case "${FAKE_OCIREPO_REF:-digest}" in
|
|
digest) ref='{"digest":"sha256:1111111111111111111111111111111111111111111111111111111111111111"}'
|
|
status=',"status":{"artifact":{"revision":"v1.7.0@sha256:2222222222222222222222222222222222222222222222222222222222222222","digest":"sha256:3333333333333333333333333333333333333333333333333333333333333333"}}' ;;
|
|
tag) ref='{"tag":"v1.7.0"}'
|
|
status=',"status":{"artifact":{"revision":"v1.7.0@sha256:2222222222222222222222222222222222222222222222222222222222222222","digest":"sha256:3333333333333333333333333333333333333333333333333333333333333333"}}' ;;
|
|
unresolved) ref='{"tag":"v1.7.0"}'
|
|
status='' ;;
|
|
*) printf 'fake kubectl: unknown FAKE_OCIREPO_REF %s\n' "$FAKE_OCIREPO_REF" >&2; exit 1 ;;
|
|
esac
|
|
# A Helm-owned, labelled, owner-referenced original: the copy must keep the
|
|
# spec, drop the ownership, and set its own labels.
|
|
cat <<JSON
|
|
{"apiVersion":"source.toolkit.fluxcd.io/v1","kind":"OCIRepository",
|
|
"metadata":{"name":"cozystack-packages","namespace":"cozy-system",
|
|
"labels":{"app.kubernetes.io/managed-by":"Helm","platform.cozystack.io/no-delete":"true"},
|
|
"annotations":{"meta.helm.sh/release-name":"cozy-platform"},
|
|
"ownerReferences":[{"apiVersion":"cozystack.io/v1alpha1","kind":"Package","name":"x","uid":"1","controller":true}]},
|
|
"spec":{"url":"oci://registry.example.org/cozystack/cozystack-packages","interval":"5m",
|
|
"secretRef":{"name":"registry-creds"},
|
|
"verify":{"provider":"cosign","secretRef":{"name":"cosign-pub"}},"ref":${ref}}${status}}
|
|
JSON
|
|
exit 0
|
|
;;
|
|
|
|
*"get helmreleases.helm.toolkit.fluxcd.io"*)
|
|
if [ -n "${FAKE_LIST_FAIL:-}" ]; then
|
|
printf '%s\n' "$FAKE_LIST_FAIL" >&2
|
|
exit 1
|
|
fi
|
|
printf '{"items":['
|
|
printf '%s\n' "${FAKE_HRS:-}" | {
|
|
sep=""
|
|
while read -r ns name chart kind; do
|
|
[ -n "$ns" ] || continue
|
|
if [ -n "${kind:-}" ] && [ "$kind" != "-" ]; then
|
|
labels=$(printf '{"apps.cozystack.io/application.kind":"%s"}' "$kind")
|
|
else
|
|
labels=null
|
|
fi
|
|
printf '%s{"metadata":{"namespace":"%s","name":"%s","labels":%s},"spec":{"chartRef":{"name":"%s"}}}' \
|
|
"$sep" "$ns" "$name" "$labels" "$chart"
|
|
sep=","
|
|
done
|
|
}
|
|
printf ']}\n'
|
|
exit 0
|
|
;;
|
|
|
|
*"patch packagesources.cozystack.io"*)
|
|
if [ -n "${FAKE_PATCH_FAIL:-}" ]; then printf '%s\n' "$FAKE_PATCH_FAIL" >&2; exit 1; fi
|
|
body=$(printf '%s' "$args" | sed 's/.*-p //')
|
|
keep=$(printf '%s' "$body" | grep -c 'resource-policy')
|
|
src=$(printf '%s' "$body" | grep -c 'cozystack-packages-http-cache')
|
|
disown=$(printf '%s' "$body" | grep -c '"meta.helm.sh/release-name":null')
|
|
managed=$(printf '%s' "$body" | grep -c '"app.kubernetes.io/managed-by":null')
|
|
log "PATCH-PACKAGESOURCE keep=$keep source=$src disown=$disown managed=$managed"
|
|
exit 0
|
|
;;
|
|
|
|
*"patch packages.cozystack.io"*)
|
|
if [ -n "${FAKE_PATCH_FAIL:-}" ]; then printf '%s\n' "$FAKE_PATCH_FAIL" >&2; exit 1; fi
|
|
log "PATCH-PACKAGE"
|
|
exit 0
|
|
;;
|
|
|
|
*"delete packages.cozystack.io"*)
|
|
if [ -n "${FAKE_DELETE_FAIL:-}" ]; then printf '%s\n' "$FAKE_DELETE_FAIL" >&2; exit 1; fi
|
|
case "$args" in
|
|
*--wait=false*) _w=0 ;;
|
|
*) _w=1 ;;
|
|
esac
|
|
log "DELETE-PACKAGE $(arg_after packages.cozystack.io "$@") wait=$_w"
|
|
exit 0
|
|
;;
|
|
|
|
*"delete helmreleases.helm.toolkit.fluxcd.io"*)
|
|
if [ -n "${FAKE_DELETE_FAIL:-}" ]; then printf '%s\n' "$FAKE_DELETE_FAIL" >&2; exit 1; fi
|
|
log "DELETE-HR $(arg_after -n "$@") $(arg_after helmreleases.helm.toolkit.fluxcd.io "$@")"
|
|
exit 0
|
|
;;
|
|
|
|
*"delete applicationdefinitions.cozystack.io"*)
|
|
if [ -n "${FAKE_DELETE_FAIL:-}" ]; then printf '%s\n' "$FAKE_DELETE_FAIL" >&2; exit 1; fi
|
|
log "DELETE-APPDEF $(arg_after applicationdefinitions.cozystack.io "$@")"
|
|
exit 0
|
|
;;
|
|
|
|
*"delete secret"*)
|
|
if [ -n "${FAKE_DELETE_FAIL:-}" ]; then printf '%s\n' "$FAKE_DELETE_FAIL" >&2; exit 1; fi
|
|
log "DELETE-SECRETS $(arg_after -n "$@") $(arg_after -l "$@")"
|
|
exit 0
|
|
;;
|
|
esac
|
|
|
|
log "UNHANDLED $args"
|
|
printf 'fake kubectl: unmodelled call: %s\n' "$args" >&2
|
|
exit 1
|