-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkubectl-ktool.sh
More file actions
executable file
·366 lines (322 loc) · 13.6 KB
/
kubectl-ktool.sh
File metadata and controls
executable file
·366 lines (322 loc) · 13.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
#!/bin/bash
#
# kubectl-ktool: A kubectl plugin for the Konnector agent.
# --- CONFIGURATION ---
RELEASE="%%RELEASE_VERSION%%"
GITHUB_USER="PaloAltoNetworks"
GITHUB_REPO="ktool"
SCRIPT_NAME="kubectl-ktool.sh"
GITHUB_API_URL="https://api.github.com/repos/${GITHUB_USER}/${GITHUB_REPO}/releases/latest"
INSTALLATION_SCRIPT_URL="https://raw.githubusercontent.com/${GITHUB_USER}/${GITHUB_REPO}/main/install.sh"
NAMESPACE="panw"
KUBECTL_BASE_CMD="kubectl"
HELM_BASE_CMD="helm"
# --- Helper Functions ---
error() {
echo "Error: $1" >&2
exit 1
}
WARN() {
echo -e "\033[33mWarning:\033[0m $1" >&2
}
# --- Detailed Usage Function ---
usage() {
echo "Usage: kubectl ktool <command> [options]"
echo
echo "A tool for managing and collecting support data for the Konnector agent."
echo
echo "Commands:"
echo " collect-logs Collects a comprehensive diagnostic support bundle."
echo " upgrade Upgrades this tool to the latest version from GitHub."
echo " version Prints the current version of this tool."
echo
echo "Options for 'collect-logs':"
echo " -n, --namespace=<namespace> The namespace where the agent is installed. (Default: panw)"
echo " --kubeconfig=<path> Path to a specific kubeconfig file to use."
echo " --context=<context> The name of the kubeconfig context to use."
echo
echo "Note: Options can be passed with a space (e.g., --namespace my-ns) or an equals sign (e.g., --namespace=my-ns)."
echo
echo "Run 'kubectl ktool <command> --help' for more information on a specific command."
exit 1
}
# --- Automatic Update Check Logic ---
# get_latest_release gets the latest release tag from GitHub.
# It encapsulates the logic to fetch and parse the release information.
#
# Arguments:
# $@: Additional arguments to pass to curl (e.g., --max-time).
#
# Outputs:
# The latest release tag string (e.g., "ktool-v1.2.3").
# Returns empty and a non-zero exit code on failure.
get_latest_release() {
local LATEST_RELEASE_JSON
LATEST_RELEASE_JSON=$(curl -fsSL "$@" "${GITHUB_API_URL}" 2>/dev/null)
if [ -z "$LATEST_RELEASE_JSON" ]; then
return 1
fi
local LATEST_RELEASE
LATEST_RELEASE=$(echo "$LATEST_RELEASE_JSON" | grep -m 1 '"tag_name":' | cut -d'"' -f4)
if [ -z "$LATEST_RELEASE" ]; then
return 1
fi
echo "$LATEST_RELEASE"
}
# parse_major_version extracts the major version number from a release string.
#
# Arguments:
# $1: The release string (e.g., "v1.2.3").
#
# Outputs:
# The major version number (e.g., "1").
# Returns empty and a non-zero exit code on failure.
parse_major_version() {
local release_string="$1"
if [[ "$release_string" =~ v([0-9]+) ]]; then
echo "${BASH_REMATCH[1]}"
else
return 1
fi
}
# check_for_updates checks if a newer version of the script is available.
# It performs a quick check in the background and warns the user if an
# update is found. It enforces mandatory updates for new major versions.
#
# Arguments:
# $1: The command argument passed to the main script (e.g., "version", "upgrade").
check_for_updates() {
local command_arg="$1"
if [[ "$command_arg" == "upgrade" ]]; then
return
fi
local LATEST_RELEASE
LATEST_RELEASE=$(get_latest_release --max-time 3)
if [ -z "$LATEST_RELEASE" ] || [ "$RELEASE" == "$LATEST_RELEASE" ]; then
return
fi
local CURRENT_MAJOR_RELEASE
CURRENT_MAJOR_RELEASE=$(parse_major_version "$RELEASE")
if [ -z "$CURRENT_MAJOR_RELEASE" ]; then
WARN "Could not parse current release version: ${RELEASE}"
return
fi
local LATEST_MAJOR_RELEASE
LATEST_MAJOR_RELEASE=$(parse_major_version "$LATEST_RELEASE")
if [ -z "$LATEST_MAJOR_RELEASE" ]; then
WARN "Could not parse latest release version: ${LATEST_RELEASE}"
return
fi
if [ "$LATEST_MAJOR_RELEASE" -gt "$CURRENT_MAJOR_RELEASE" ]; then
# For major version changes, the update is considered mandatory for most commands.
case "$command_arg" in
version|""|-h|--help)
# For non-critical commands, just show a strong warning.
WARN "MANDATORY UPDATE RECOMMENDED. A new major release (${LATEST_RELEASE}) is available. Please run 'kubectl ktool upgrade'."
;;
*)
# For all other commands, block execution and demand an upgrade.
error "Mandatory update required. A new major release (${LATEST_RELEASE}) is available. Please run 'kubectl ktool upgrade'."
;;
esac
else
# For minor or patch updates, a simple warning is sufficient.
WARN "A new release (${LATEST_RELEASE}) is available. Please run 'kubectl ktool upgrade' to update."
fi
}
# handle_upgrade manages the script upgrade process.
# It replaces the current script with the latest version from GitHub using the
# official install.sh script.
handle_upgrade() {
echo "Current release: ${RELEASE}"
echo "Fetching latest release information from GitHub..."
local LATEST_RELEASE
LATEST_RELEASE=$(get_latest_release)
if [ -z "$LATEST_RELEASE" ]; then
error "Could not fetch release information from GitHub. Check network connection or API response."
fi
echo "Latest release available: ${LATEST_RELEASE}"
if [ "$RELEASE" == "$LATEST_RELEASE" ]; then
echo "You are already using the latest release."
exit 0
fi
echo "Upgrading to ${LATEST_RELEASE}..."
if curl -fsSL "${INSTALLATION_SCRIPT_URL}" | bash; then
echo "Upgrade complete to release ${LATEST_RELEASE}."
else
error "The upgrade process failed."
fi
exit 0
}
# --- Version Command Logic ---
handle_version() {
echo "${RELEASE}"
}
# @description Parses command-line arguments to set up base commands for kubectl and helm.
# This function populates the global variables: KUBECTL_BASE_CMD, HELM_BASE_CMD, and NAMESPACE.
# @param "$@" The command-line arguments to parse.
setup_kube_commands() {
local KUBECONFIG_FLAG=""
local CONTEXT_FLAG=""
local HELM_CONTEXT_FLAG=""
shift
while [[ "$#" -gt 0 ]]; do
case $1 in
-n|--namespace|--namespace=*)
if [[ "$1" == *=* ]]; then
NAMESPACE="${1#*=}"
shift 1
else
if [[ -z "$2" || "$2" == -* ]]; then echo "ERROR: Option '$1' requires an argument." >&2; exit 1; fi
NAMESPACE="$2"
shift 2
fi
;;
--kubeconfig|--kubeconfig=*)
if [[ "$1" == *=* ]]; then
KUBECONFIG_FLAG="--kubeconfig ${1#*=}"
shift 1
else
if [[ -z "$2" || "$2" == -* ]]; then echo "ERROR: Option '$1' requires an argument." >&2; exit 1; fi
KUBECONFIG_FLAG="--kubeconfig $2"
shift 2
fi
;;
--context|--context=*)
if [[ "$1" == *=* ]]; then
local context_val="${1#*=}"
CONTEXT_FLAG="--context ${context_val}"
HELM_CONTEXT_FLAG="--kube-context ${context_val}"
shift 1
else
if [[ -z "$2" || "$2" == -* ]]; then echo "ERROR: Option '$1' requires an argument." >&2; exit 1; fi
CONTEXT_FLAG="--context $2"
HELM_CONTEXT_FLAG="--kube-context $2"
shift 2
fi
;;
*)
echo "ERROR: Unknown option: $1" >&2
exit 1
;;
esac
done
KUBECTL_BASE_CMD="kubectl ${KUBECONFIG_FLAG} ${CONTEXT_FLAG} -n ${NAMESPACE}"
HELM_BASE_CMD="helm ${KUBECONFIG_FLAG} ${HELM_CONTEXT_FLAG} -n ${NAMESPACE}"
echo "--> Verifying namespace '${NAMESPACE}' exists..."
if ! ${KUBECTL_BASE_CMD} get namespace "${NAMESPACE}" &> /dev/null; then
echo "ERROR: Namespace '${NAMESPACE}' not found. Please verify the namespace name and your cluster context." >&2
exit 1
fi
}
# --- Collect Logs Logic ---
check_dependencies() {
for cmd in helm tar; do
if ! command -v "$cmd" &> /dev/null; then
echo "ERROR: '$cmd' command not found. Please ensure it's installed and in your PATH." >&2
exit 1
fi
done
}
collect_logs() {
check_dependencies
setup_kube_commands "$@"
KONNECTOR_HELM_RELEASE="konnector"
K8S_MANAGER_HELM_RELEASE="k8s-connector-release"
BUNDLE_DIR="konnector-support-bundle-${NAMESPACE}-${RELEASE}-$(date +"%Y%m%d-%H%M%S")"
trap 'if [ -n "${BUNDLE_DIR}" ]; then rm -rf "${BUNDLE_DIR}"; fi' EXIT INT TERM
echo "Starting support bundle collection for namespace: ${NAMESPACE}"
echo "Output will be saved to ${BUNDLE_DIR}.tar.gz"
if ! mkdir -p "${BUNDLE_DIR}"; then
echo "ERROR: Failed to create temporary bundle directory: ${BUNDLE_DIR}" >&2
exit 1
fi
collect_cmd() {
local title="$1"
local cmd="$2"
local file="$3"
local cleanup_on_fail="$4"
echo " -> Collecting ${title}..."
if ! bash -c "$cmd" > "${BUNDLE_DIR}/${file}" 2>&1; then
if [ "$cleanup_on_fail" = "--cleanup-on-fail" ]; then
rm -f "${BUNDLE_DIR}/${file}"
else
echo "WARN: Collection for '${title}' failed. See ${BUNDLE_DIR}/${file} for details." >&2
fi
fi
}
echo "[1/6] Collecting Cluster Information..."
mkdir -p "${BUNDLE_DIR}/cluster-info"
collect_cmd "Cluster info" "${KUBECTL_BASE_CMD} cluster-info" "cluster-info/info.txt"
collect_cmd "Kubernetes version" "${KUBECTL_BASE_CMD} version" "cluster-info/version.txt"
collect_cmd "Node details" "${KUBECTL_BASE_CMD} get nodes -o wide" "cluster-info/nodes.txt"
echo "[2/6] Collecting Namespace Information..."
mkdir -p "${BUNDLE_DIR}/namespace-info"
collect_cmd "Events in namespace" "${KUBECTL_BASE_CMD} get events --sort-by='.lastTimestamp'" "namespace-info/events.txt"
echo "[3/6] Collecting Helm Release Information..."
mkdir -p "${BUNDLE_DIR}/helm"
collect_cmd "Helm status for ${KONNECTOR_HELM_RELEASE}" "${HELM_BASE_CMD} status ${KONNECTOR_HELM_RELEASE} "helm/status-${KONNECTOR_HELM_RELEASE}.txt"
collect_cmd "Helm values for ${KONNECTOR_HELM_RELEASE}" "${HELM_BASE_CMD} get values ${KONNECTOR_HELM_RELEASE} -a" "helm/values-${KONNECTOR_HELM_RELEASE}.yaml"
collect_cmd "Helm status for ${K8S_MANAGER_HELM_RELEASE}" "${HELM_BASE_CMD} status ${K8S_MANAGER_HELM_RELEASE} "helm/status-${K8S_MANAGER_HELM_RELEASE}.txt"
collect_cmd "Helm values for ${K8S_MANAGER_HELM_RELEASE}" "${HELM_BASE_CMD} get values ${K8S_MANAGER_HELM_RELEASE} -a" "helm/values-${K8S_MANAGER_HELM_RELEASE}.yaml"
echo "[4/6] Collecting Workload Statuses..."
mkdir -p "${BUNDLE_DIR}/workloads"
collect_cmd "All workloads (wide)" "${KUBECTL_BASE_CMD} get all -o wide" "workloads/get-all-wide.txt"
collect_cmd "All workloads (yaml)" "${KUBECTL_BASE_CMD} get all -o yaml" "workloads/get-all.yaml"
echo " -> Describing all workloads..."
for kind in pod deployment statefulset daemonset service configmap replicaset ingress job cronjob; do
RESOURCES=$(${KUBECTL_BASE_CMD} get "$kind" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null)
if [ -n "$RESOURCES" ]; then
mkdir -p "${BUNDLE_DIR}/workloads/${kind}"
for name in $RESOURCES; do
collect_cmd "${kind}/${name}" "${KUBECTL_BASE_CMD} describe ${kind} ${name}" "workloads/${kind}/${name}.describe.txt"
done
fi
done
echo "[5/6] Collecting Pod Logs..."
mkdir -p "${BUNDLE_DIR}/logs"
PODS=$(${KUBECTL_BASE_CMD} get pods -o jsonpath='{.items[*].metadata.name}' 2>/dev/null)
for pod in $PODS; do
(
CONTAINERS=$(${KUBECTL_BASE_CMD} get pod "$pod" -o jsonpath='{.spec.containers[*].name} {.spec.initContainers[*].name}' 2>/dev/null)
for container in $CONTAINERS; do
collect_cmd "Logs for ${pod}/${container}" "${KUBECTL_BASE_CMD} logs ${pod} -c ${container}" "logs/${pod}_${container}.log" "--cleanup-on-fail"
collect_cmd "Previous logs for ${pod}/${container}" "${KUBECTL_BASE_CMD} logs ${pod} -c ${container} --previous" "logs/${pod}_${container}.previous.log" "--cleanup-on-fail"
done
) &
done
wait
echo "Log collection complete."
echo "[6/6] Collecting Operator Configurations..."
mkdir -p "${BUNDLE_DIR}/operator"
collect_cmd "Validating Webhooks" "${KUBECTL_BASE_CMD} get validatingwebhookconfigurations -l 'app.kubernetes.io/instance in (${KONNECTOR_HELM_RELEASE}, ${K8S_MANAGER_HELM_RELEASE})' -o yaml" "operator/validating-webhooks.yaml"
collect_cmd "Mutating Webhooks" "${KUBECTL_BASE_CMD} get mutatingwebhookconfigurations -l 'app.kubernetes.io/instance in (${KONNECTOR_HELM_RELEASE}, ${K8S_MANAGER_HELM_RELEASE})' -o yaml" "operator/mutating-webhooks.yaml"
echo "Packaging support bundle..."
if ! tar -czf "${BUNDLE_DIR}.tar.gz" "${BUNDLE_DIR}"; then
echo "ERROR: Failed to create tarball for support bundle." >&2
exit 1
fi
echo "Support bundle created successfully: ${BUNDLE_DIR}.tar.gz"
}
main() {
# Run the synchronous update check. This is critical for the blocking logic.
check_for_updates "$1"
case "$1" in
collect-logs)
collect_logs "$@"
;;
upgrade)
handle_upgrade
;;
version)
handle_version
;;
""|-h|--help)
usage
;;
*)
error "Unknown command '$1'"
;;
esac
}
main "$@"