Files

185 lines
5.6 KiB
PowerShell

<#
.SYNOPSIS
Runs an Inspektor Gadget (IG) trace on an AKS node via `kubectl debug`.
.DESCRIPTION
Handles the mechanical, error-prone assembly of the IG invocation:
- resolves the target node from a pod (or takes a node directly)
- injects the pinned IG image + version
- applies the correct default -Timeout for the gadget type
- adds the k8s namespace/pod/container filters
- handles the special `tcpdump` gadget (pcap-ng output piped to tcpdump)
The privileged debug pod requires explicit user approval and appropriate RBAC.
Use -DryRun to print the assembled command without running it.
.PARAMETER Gadget
Gadget to run, e.g. trace_dns, snapshot_socket, tcpdump (required).
.PARAMETER Pod
Pod name; the node is resolved automatically.
.PARAMETER Namespace
Namespace of the pod (required with -Pod).
.PARAMETER Node
Run directly against a node (node-wide scope).
.PARAMETER Container
Scope to a specific container.
.PARAMETER Timeout
Override the gadget-type default timeout (seconds).
.PARAMETER Filter
Extra IG flags, passed through verbatim (e.g. -Filter --max-entries,20).
.PARAMETER Pf
tcpdump packet filter (tcpdump gadget only, e.g. "port 80").
.PARAMETER IgVersion
Override the pinned IG image tag.
.PARAMETER DryRun
Print the assembled command; do not execute.
.EXAMPLE
./run-ig.ps1 -Gadget trace_dns -Pod web-0 -Namespace default
.EXAMPLE
./run-ig.ps1 -Gadget snapshot_process -Node aks-nodepool1-1234
.EXAMPLE
./run-ig.ps1 -Gadget tcpdump -Pod web-0 -Namespace default -Pf "port 80"
.EXAMPLE
./run-ig.ps1 -Gadget traceloop -Pod web-0 -Namespace default -Filter --syscall-filters,open,connect
.EXAMPLE
./run-ig.ps1 -Gadget trace_dns -Pod web-0 -Namespace default -DryRun
#>
[CmdletBinding()]
param(
[string]$Gadget,
[string]$Pod,
[Alias('Ns')]
[string]$Namespace,
[string]$Node,
[string]$Container,
[int]$Timeout,
[string[]]$Filter,
[string]$Pf,
# Pinned IG image tag. Bump this default (and run-ig.sh) to update the IG version.
[string]$IgVersion = 'v0.51.0',
[switch]$DryRun
)
$IgImageRepo = 'mcr.microsoft.com/oss/v2/inspektor-gadget/ig'
if (-not $Gadget) {
Write-Error 'Provide -Gadget <name> (e.g. trace_dns, snapshot_socket, tcpdump).'
exit 2
}
if (-not $Node -and -not $Pod) {
Write-Error 'Provide either -Node <node> or -Pod <pod> -Namespace <namespace>.'
exit 2
}
if ($Pod -and -not $Namespace) {
Write-Error '-Pod requires -Namespace <namespace>.'
exit 2
}
if ($Pf -and $Gadget -ne 'tcpdump') {
Write-Error '-Pf is only valid for the tcpdump gadget.'
exit 2
}
# Default timeout by gadget type, inferred from the gadget name prefix.
# snapshot_* / top_* -> 5s (point-in-time / quick aggregate)
# trace_* / profile_* / tcpdump -> 30s (streaming / sampling)
function Get-DefaultTimeout([string]$g) {
switch -Wildcard ($g) {
'snapshot_*' { return 5 }
'top_*' { return 5 }
'trace_*' { return 30 }
'profile_*' { return 30 }
'tcpdump' { return 30 }
default { return 30 } # unknown gadget: use the safer streaming default
}
}
if (-not $PSBoundParameters.ContainsKey('Timeout') -or $Timeout -le 0) {
$Timeout = Get-DefaultTimeout $Gadget
}
# Resolve the node name from the pod when not given directly.
if (-not $Node) {
$Node = ((& kubectl get pod $Pod -n $Namespace -o "jsonpath={.spec.nodeName}" 2>$null) | Out-String).Trim()
if (-not $Node) {
Write-Error "Could not resolve node for pod '$Pod' in namespace '$Namespace'."
exit 1
}
}
$IgImage = "${IgImageRepo}:${IgVersion}"
# Assemble the k8s scoping filters.
$filters = @()
if ($Namespace) { $filters += @('--k8s-namespace', $Namespace) }
if ($Pod) { $filters += @('--k8s-podname', $Pod) }
if ($Container) { $filters += @('--k8s-containername', $Container) }
# Base kubectl debug invocation.
$debug = @('debug', '--profile=sysadmin', "node/$Node", '--attach', '--quiet', "--image=$IgImage", '--')
if ($Gadget -eq 'tcpdump') {
# tcpdump emits raw pcap-ng; pipe through tcpdump for readable output when available.
$igCmd = @('ig', 'run', "tcpdump:$IgVersion", '-o', 'pcap-ng') + $filters + @('--timeout', "$Timeout")
if ($Pf) { $igCmd += @('--pf', $Pf) }
if ($Filter) { $igCmd += $Filter }
}
else {
$igCmd = @('ig', 'run', "${Gadget}:$IgVersion", '-o', 'json') + $filters + @('--timeout', "$Timeout")
if ($Filter) { $igCmd += $Filter }
}
$fullArgs = $debug + $igCmd
# Pretty-print a shell-quoted version of the command for display.
function Format-Cmd([string[]]$parts) {
($parts | ForEach-Object {
if ($_ -match '\s') { '"' + $_ + '"' } else { $_ }
}) -join ' '
}
$displayCmd = 'kubectl ' + (Format-Cmd $fullArgs)
# The tcpdump gadget is only piped through `tcpdump` when that binary is present.
# Reflect the real behavior in the displayed command so -DryRun does not mislead.
$tcpdumpAvail = $Gadget -eq 'tcpdump' -and [bool](Get-Command tcpdump -ErrorAction SilentlyContinue)
if ($tcpdumpAvail) {
$displayCmd = "$displayCmd | tcpdump -nvr -"
}
Write-Host "Gadget: $Gadget"
Write-Host "Node: $Node"
Write-Host "Timeout: ${Timeout}s"
Write-Host "Image: $IgImage"
Write-Host "Command: $displayCmd"
if ($Gadget -eq 'tcpdump' -and -not $tcpdumpAvail) {
Write-Host 'Note: tcpdump not found; emitting raw pcap-ng to stdout.'
}
if ($DryRun) {
Write-Host '(dry-run: command not executed)'
exit 0
}
Write-Host "Ran gadget $Gadget on node $Node (timeout ${Timeout}s)"
if ($tcpdumpAvail) {
& kubectl @fullArgs | & tcpdump -nvr -
}
else {
& kubectl @fullArgs
}