Skip to content

Commit 20035bb

Browse files
committed
Update uninstall script to prompt user to remove non-quickstart CRs
Signed-off-by: Thomas Cooper <[email protected]>
1 parent 0844bab commit 20035bb

1 file changed

Lines changed: 222 additions & 16 deletions

File tree

uninstall.sh

Lines changed: 222 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,95 @@ timeout_seconds() {
6767
echo "$val"
6868
}
6969

70+
# Discover CR type plural names for CRDs whose API group ends with the given suffix.
71+
# Arguments: api_group_suffix (e.g. "strimzi.io"), all_crds (pre-fetched CRD data)
72+
# Output: space-separated list of plural CR type names, or empty string
73+
filter_cr_types() {
74+
local suffix="$1"
75+
local all_crds="$2"
76+
echo "$all_crds" \
77+
| awk -v suf="$suffix" 'index($1, suf) == length($1) - length(suf) + 1 { printf "%s ", $2 }' \
78+
| sed 's/ $//'
79+
}
80+
81+
# Check if /dev/tty is available for interactive prompts.
82+
# Needed for `curl | bash` where stdin is the pipe.
83+
is_interactive() {
84+
[ -c /dev/tty ] 2>/dev/null
85+
}
86+
87+
# Prompt user with a yes/no question via /dev/tty. Defaults to No.
88+
# Arguments: prompt message
89+
# Returns 0 for yes, 1 for no
90+
prompt_yes_no() {
91+
local prompt="$1"
92+
local answer
93+
echo -en "${YELLOW}${prompt} [y/N]: ${NC}" > /dev/tty
94+
read -r answer < /dev/tty
95+
case "$answer" in
96+
[yY]|[yY][eE][sS]) return 0 ;;
97+
*) return 1 ;;
98+
esac
99+
}
100+
101+
# List unlabeled CRs for given CR types across all namespaces.
102+
# Arguments: cr_types (space-separated list)
103+
# Returns 0 if any unlabeled CRs found, 1 if none
104+
list_unlabeled_crs() {
105+
local cr_types="$1"
106+
local found=false
107+
for cr_type in $cr_types; do
108+
local count
109+
count=$(kubectl get "$cr_type" -A --selector='!app.kubernetes.io/part-of' --no-headers 2>/dev/null | wc -l | tr -d ' ') || count=0
110+
if [ "$count" -gt 0 ]; then
111+
found=true
112+
kubectl get "$cr_type" -A --selector='!app.kubernetes.io/part-of' 2>/dev/null || true
113+
fi
114+
done
115+
[ "$found" = true ]
116+
}
117+
118+
# Delete unlabeled CRs for given CR types across all namespaces.
119+
# Arguments: cr_types (space-separated list)
120+
delete_unlabeled_crs() {
121+
local cr_types="$1"
122+
for cr_type in $cr_types; do
123+
local count
124+
count=$(kubectl get "$cr_type" -A --selector='!app.kubernetes.io/part-of' --no-headers 2>/dev/null | wc -l | tr -d ' ') || count=0
125+
if [ "$count" -gt 0 ]; then
126+
kubectl delete "$cr_type" -A --selector='!app.kubernetes.io/part-of' --ignore-not-found=true --wait=false 2>/dev/null || true
127+
fi
128+
done
129+
}
130+
131+
# Wait for unlabeled CRs of given types to be fully removed.
132+
# Arguments: cr_types (space-separated list)
133+
# Returns 0 if all removed, 1 if timeout
134+
wait_for_unlabeled_cr_removal() {
135+
local cr_types="$1"
136+
local max_wait
137+
max_wait=$(timeout_seconds)
138+
local elapsed=0
139+
140+
while [ $elapsed -lt "$max_wait" ]; do
141+
local total=0
142+
for cr_type in $cr_types; do
143+
local count
144+
count=$(kubectl get "$cr_type" -A --selector='!app.kubernetes.io/part-of' --no-headers 2>/dev/null | wc -l | tr -d ' ')
145+
total=$((total + count))
146+
done
147+
if [ "$total" -eq 0 ]; then
148+
return 0
149+
fi
150+
if [ "${interrupted:-false}" = true ]; then
151+
return 1
152+
fi
153+
sleep 5
154+
elapsed=$((elapsed + 5))
155+
done
156+
return 1
157+
}
158+
70159
# Wait for all custom resources of a given type to be fully removed
71160
# Returns 0 if all removed, 1 if timeout
72161
wait_for_cr_removal() {
@@ -82,6 +171,9 @@ wait_for_cr_removal() {
82171
if [ "$count" -eq 0 ]; then
83172
return 0
84173
fi
174+
if [ "${interrupted:-false}" = true ]; then
175+
return 1
176+
fi
85177
sleep 5
86178
elapsed=$((elapsed + 5))
87179
done
@@ -111,14 +203,10 @@ has_unlabeled_crs() {
111203
return 1
112204
}
113205

114-
# Strimzi CR types to check for shared usage
115-
STRIMZI_CR_TYPES="kafkas kafkanodepools kafkatopics kafkausers kafkaconnects kafkamirrormakers kafkamirrormaker2s kafkabridges kafkaconnectors kafkarebalances"
116-
117-
# Apicurio Registry CR types
118-
APICURIO_CR_TYPES="apicurioregistry3s"
119-
120-
# StreamsHub Console CR types
121-
CONSOLE_CR_TYPES="consoles"
206+
# API group suffixes used to discover CR types dynamically from installed CRDs
207+
STRIMZI_API_GROUP_SUFFIX="strimzi.io"
208+
APICURIO_API_GROUP_SUFFIX="apicur.io"
209+
CONSOLE_API_GROUP_SUFFIX="streamshub.github.com"
122210

123211
main() {
124212
echo ""
@@ -130,17 +218,134 @@ main() {
130218
fi
131219
echo ""
132220

221+
# Discover CR types dynamically from installed CRDs
222+
local all_crds
223+
all_crds=$(kubectl get crds \
224+
-o jsonpath='{range .items[*]}{.spec.group}{" "}{.spec.names.plural}{"\n"}{end}' \
225+
2>/dev/null || true)
226+
227+
STRIMZI_CR_TYPES=$(filter_cr_types "$STRIMZI_API_GROUP_SUFFIX" "$all_crds")
228+
APICURIO_CR_TYPES=$(filter_cr_types "$APICURIO_API_GROUP_SUFFIX" "$all_crds")
229+
CONSOLE_CR_TYPES=$(filter_cr_types "$CONSOLE_API_GROUP_SUFFIX" "$all_crds")
230+
231+
info "Discovered CR types:"
232+
info " Strimzi: ${STRIMZI_CR_TYPES:-<none>}"
233+
info " Apicurio Registry: ${APICURIO_CR_TYPES:-<none>}"
234+
info " StreamsHub Console: ${CONSOLE_CR_TYPES:-<none>}"
235+
echo ""
236+
237+
# Catch Ctrl+C so the shell doesn't silently exit before reaching interactive prompts
238+
local interrupted=false
239+
trap 'warn "Caught interrupt, continuing cleanup..."; interrupted=true' INT
240+
133241
# Track which operator groups have shared CRDs
134242
local strimzi_shared=false
135243
local apicurio_shared=false
136244
local console_shared=false
137245

138-
# --- Phase 1: Delete operands (stack layer) ---
246+
# --- Phase 1: Interactive cleanup of user-created CRs ---
247+
# Must run BEFORE stack deletion so operators are still alive to process finalizers
248+
info "Phase 1: Checking for user-created custom resources..."
249+
250+
local has_user_crs=false
251+
252+
# Check each operator group for unlabeled CRs
253+
local strimzi_user_crs=false
254+
local apicurio_user_crs=false
255+
local console_user_crs=false
256+
257+
if list_unlabeled_crs "$STRIMZI_CR_TYPES" > /dev/null 2>&1; then
258+
strimzi_user_crs=true
259+
has_user_crs=true
260+
fi
261+
if list_unlabeled_crs "$APICURIO_CR_TYPES" > /dev/null 2>&1; then
262+
apicurio_user_crs=true
263+
has_user_crs=true
264+
fi
265+
if list_unlabeled_crs "$CONSOLE_CR_TYPES" > /dev/null 2>&1; then
266+
console_user_crs=true
267+
has_user_crs=true
268+
fi
269+
270+
if [ "$has_user_crs" = true ]; then
271+
if is_interactive; then
272+
warn "Found user-created custom resources (not part of the quick-start)."
273+
warn "These resources will cause operator CRDs to be retained if not deleted."
274+
echo ""
275+
276+
if [ "$strimzi_user_crs" = true ]; then
277+
info " Strimzi resources without quick-start label:"
278+
list_unlabeled_crs "$STRIMZI_CR_TYPES" 2>/dev/null | sed 's/^/ /'
279+
echo ""
280+
if prompt_yes_no " Delete these Strimzi resources?"; then
281+
info " Deleting unlabeled Strimzi resources..."
282+
delete_unlabeled_crs "$STRIMZI_CR_TYPES"
283+
if wait_for_unlabeled_cr_removal "$STRIMZI_CR_TYPES"; then
284+
info " Strimzi resources removed"
285+
strimzi_user_crs=false
286+
else
287+
warn " Some Strimzi resources were not fully removed within timeout"
288+
fi
289+
else
290+
info " Skipping Strimzi resource deletion"
291+
fi
292+
echo ""
293+
fi
294+
295+
if [ "$apicurio_user_crs" = true ]; then
296+
info " Apicurio Registry resources without quick-start label:"
297+
list_unlabeled_crs "$APICURIO_CR_TYPES" 2>/dev/null | sed 's/^/ /'
298+
echo ""
299+
if prompt_yes_no " Delete these Apicurio Registry resources?"; then
300+
info " Deleting unlabeled Apicurio Registry resources..."
301+
delete_unlabeled_crs "$APICURIO_CR_TYPES"
302+
if wait_for_unlabeled_cr_removal "$APICURIO_CR_TYPES"; then
303+
info " Apicurio Registry resources removed"
304+
apicurio_user_crs=false
305+
else
306+
warn " Some Apicurio Registry resources were not fully removed within timeout"
307+
fi
308+
else
309+
info " Skipping Apicurio Registry resource deletion"
310+
fi
311+
echo ""
312+
fi
313+
314+
if [ "$console_user_crs" = true ]; then
315+
info " StreamsHub Console resources without quick-start label:"
316+
list_unlabeled_crs "$CONSOLE_CR_TYPES" 2>/dev/null | sed 's/^/ /'
317+
echo ""
318+
if prompt_yes_no " Delete these StreamsHub Console resources?"; then
319+
info " Deleting unlabeled Console resources..."
320+
delete_unlabeled_crs "$CONSOLE_CR_TYPES"
321+
if wait_for_unlabeled_cr_removal "$CONSOLE_CR_TYPES"; then
322+
info " Console resources removed"
323+
console_user_crs=false
324+
else
325+
warn " Some Console resources were not fully removed within timeout"
326+
fi
327+
else
328+
info " Skipping Console resource deletion"
329+
fi
330+
echo ""
331+
fi
332+
else
333+
warn "Non-interactive mode: skipping user-created resource cleanup."
334+
warn "User-created CRs were detected but cannot prompt for deletion."
335+
warn "Phase 3 will treat these as shared resources and retain their CRDs."
336+
echo ""
337+
fi
338+
else
339+
info " No user-created custom resources found"
340+
fi
341+
echo ""
342+
343+
# --- Phase 2: Delete operands (stack layer) ---
139344
local stack_url
140345
stack_url=$(kustomize_url "stack")
141-
info "Phase 1: Deleting operands..."
346+
info "Phase 2: Deleting operands..."
142347
info "Deleting: ${stack_url}"
143-
kubectl delete -k "${stack_url}" --ignore-not-found=true 2>/dev/null || true
348+
kubectl delete -k "${stack_url}" --ignore-not-found=true --wait=false 2>/dev/null || true
144349
echo ""
145350

146351
# --- Wait for CRs to be fully removed ---
@@ -184,10 +389,11 @@ main() {
184389
if [ "$removal_failed" = true ]; then
185390
warn "Some custom resources were not fully removed. Proceeding with caution..."
186391
fi
392+
interrupted=false
187393
echo ""
188394

189-
# --- Phase 2: Shared-cluster safety checks ---
190-
info "Phase 2: Checking for shared CRD usage..."
395+
# --- Phase 3: Shared-cluster safety checks ---
396+
info "Phase 3: Checking for shared CRD usage..."
191397

192398
if has_unlabeled_crs "$STRIMZI_CR_TYPES"; then
193399
strimzi_shared=true
@@ -211,17 +417,17 @@ main() {
211417
fi
212418
echo ""
213419

214-
# --- Phase 3: Delete operators and CRDs ---
420+
# --- Phase 4: Delete operators and CRDs ---
215421
if [ "$strimzi_shared" = false ] && [ "$apicurio_shared" = false ] && [ "$console_shared" = false ]; then
216422
# No shared CRDs — safe to delete the entire base layer
217423
local base_url
218424
base_url=$(kustomize_url "base")
219-
info "Phase 3: Deleting operators and CRDs (no shared usage detected)..."
425+
info "Phase 4: Deleting operators and CRDs (no shared usage detected)..."
220426
info "Deleting: ${base_url}"
221427
kubectl delete -k "${base_url}" --ignore-not-found=true 2>/dev/null || true
222428
else
223429
# Some operator groups have shared CRDs — selective cleanup
224-
info "Phase 3: Selective operator removal (some CRDs are shared)..."
430+
info "Phase 4: Selective operator removal (some CRDs are shared)..."
225431

226432
if [ "$strimzi_shared" = false ]; then
227433
info " Removing Strimzi operator (full removal including CRDs)..."

0 commit comments

Comments
 (0)