Created
June 25, 2025 15:37
-
-
Save mbtamuli/578e4b3409a26b0cfee040d699351c1e to your computer and use it in GitHub Desktop.
Kubectl Rsync
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # | |
| # krsync - Kubernetes rsync wrapper | |
| # | |
| # This script enables rsync to work with Kubernetes pods by acting as a bridge | |
| # between rsync and kubectl exec. It requires rsync to be installed in the pod | |
| # | |
| # It supports two syntaxes: | |
| # | |
| # 1. Simple pod name (uses default namespace): | |
| # krsync [rsync-options] source pod_name:destination | |
| # | |
| # 2. Pod with namespace: | |
| # krsync [rsync-options] source pod_name@namespace:destination | |
| # | |
| # Example usage: | |
| # ./krsync -avP . my-pod@my-namespace:/app/ | |
| # ./krsync -avzhP --delete . my-pod:/app/ | |
| # | |
| # The script operates in two phases: | |
| # 1. Initial phase: Launches rsync with this script as the remote shell | |
| # 2. RSH phase: Executes commands in the pod via kubectl exec | |
| # | |
| # Based on: https://serverfault.com/questions/741670/rsync-files-to-a-kubernetes-pod | |
| # | |
| set -euo pipefail # Exit on error, undefined vars, and pipe failures | |
| # Phase 1: Initial execution - launch rsync with this script as remote shell | |
| if [[ -z "${KRSYNC_STARTED:-}" ]]; then | |
| export KRSYNC_STARTED=true | |
| # Validate that we have arguments | |
| if [[ $# -eq 0 ]]; then | |
| echo "Error: No arguments provided" >&2 | |
| echo "Usage: $0 [rsync-options] source pod[@namespace]:destination" >&2 | |
| exit 1 | |
| fi | |
| # Launch rsync with this script as the remote shell handler | |
| # --blocking-io ensures proper I/O handling with kubectl exec | |
| exec rsync --blocking-io --rsh "$0" "$@" | |
| fi | |
| # Phase 2: RSH mode - we're being called by rsync as the remote shell | |
| # At this point, rsync passes us the target and command to execute | |
| # Initialize variables with safe defaults | |
| namespace_flag="" | |
| pod_name="" | |
| # Validate we have at least one argument (the pod specification) | |
| if [[ $# -eq 0 ]]; then | |
| echo "Error: No pod specification provided in RSH mode" >&2 | |
| exit 1 | |
| fi | |
| pod_name="$1" | |
| shift | |
| # Parse pod specification and extract namespace if provided | |
| if [[ "$pod_name" == *"@"* ]]; then | |
| # Handle pod@namespace syntax: extract namespace and pod separately | |
| namespace_name="${pod_name#*@}" # Everything after @ | |
| pod_name="${pod_name%@*}" # Everything before @ | |
| # Validate extracted components | |
| if [[ -z "$namespace_name" ]]; then | |
| echo "Error: Empty namespace specified in pod@namespace syntax" >&2 | |
| exit 1 | |
| fi | |
| if [[ -z "$pod_name" ]]; then | |
| echo "Error: Empty pod name specified in pod@namespace syntax" >&2 | |
| exit 1 | |
| fi | |
| namespace_flag="--namespace=${namespace_name}" | |
| elif [[ "$pod_name" == "-l" ]]; then | |
| # Handle legacy rsync -l flag format: rsync -l pod namespace ... | |
| # This happens when rsync interprets pod@namespace differently | |
| if [[ $# -lt 2 ]]; then | |
| echo "Error: Insufficient arguments for -l flag format" >&2 | |
| exit 1 | |
| fi | |
| pod_name="$1" | |
| shift | |
| namespace_name="$1" | |
| shift | |
| # Validate components | |
| if [[ -z "$namespace_name" ]]; then | |
| echo "Error: Empty namespace specified with -l flag" >&2 | |
| exit 1 | |
| fi | |
| if [[ -z "$pod_name" ]]; then | |
| echo "Error: Empty pod name specified with -l flag" >&2 | |
| exit 1 | |
| fi | |
| namespace_flag="--namespace=${namespace_name}" | |
| fi | |
| # Validate final pod name | |
| if [[ -z "$pod_name" ]]; then | |
| echo "Error: No valid pod name could be determined" >&2 | |
| exit 1 | |
| fi | |
| # Execute the command in the specified pod using kubectl | |
| # --stdin: Pass stdin to the container (required for rsync data transfer) | |
| # --tty=false: Don't allocate a TTY (prevents interference with binary data) | |
| # shellcheck disable=SC2086 # namespace_flag needs to expand as separate words | |
| exec kubectl ${namespace_flag} exec --stdin --tty=false "$pod_name" -- "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment