Created
November 1, 2017 22:57
-
-
Save yonran/c5590a52179aa4841d1067991338b920 to your computer and use it in GitHub Desktop.
Functions to replace kubernetes configmap and secret from directory
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
#!/usr/bin/env bash | |
# Convert a directory to a ConfigMap json file as when doing kubectl create configmap --from-file … | |
# | |
# Example usage: | |
# 1. kubectl create cm test-cm --from-file path/to/cmdir | |
# | |
# 2. Before running kubectl replace, you should diff the directories: | |
# dirToCm test-cm path/to/cmdir | cmToDir /tmp/test-cm-new | |
# kubectl get cm test-cm -o json | cmToDir /tmp/test-cm-orig | |
# diff /tmp/test-cm-orig /tmp/test-cm-new | |
# | |
# 3. When you are satisfied at the diff, run kubectl replace: | |
# dirToCm test-cm path/to/cmdir | kubectl replace -f - | |
function dirToCm() { | |
local name= | |
local USAGE="$(cat <<EOF | |
Convert files to configmap json structure. Supports --from-file and | |
--from-literal in the same way as kubectl create configmap. | |
Usage: | |
dirToCm --from-file=<FILESOURCE>... --from-literal=<KEY>=<VALUE>... <NAME> | |
Options: | |
--from-file=<FILESOURCE>... Source from files. Can be one of <FILE>, | |
<KEY>=<FILE>, or <DIR>. Can be specified multiple times or | |
comma-separated. | |
--from-literal=<KEY>=<VALUE>... Source from literal strings. | |
EOF | |
)" | |
local -a fileSources=() # each item is file, key=file, or directory | |
local -a literalSources=() # each item is key=value | |
while (($# > 0)); do | |
case "$1" in | |
--from-file) local oldIFS="$IFS"; IFS=,; fileSources+=($2); IFS="$oldIFS"; shift;; | |
--from-file=*) local oldIFS="$IFS"; IFS=,; fileSources+=(${1#*=}); IFS="$oldIFS";; | |
--from-literal) literalSources+=("$2"); shift;; | |
--from-literal=*) literalSources+=("${1#*=}");; | |
--help) echo "$USAGE"; return 0;; | |
-*) echo >&2 "Unknown flag \"$1\""$'\n'"$USAGE"; return 1;; | |
*) name="$1";; | |
esac | |
shift | |
done | |
if [[ -z "$name" ]]; then echo >&2 "Required argument NAME must be set"$'\n'"$USAGE"; return 1; fi | |
jq -n 0 > /dev/null || { echo >&2 "Make sure jq is installed" ; return 1; } | |
local CM="$(jq -n --arg name "$name" '{apiVersion: "v1", data: {}, kind: "ConfigMap", metadata: {name: $name}}')" | |
local literalSource | |
for literalSource in "${literalSources[@]:+"${literalSources[@]}"}"; do | |
local keyName="${literalSource%%=*}" | |
local value="${literalSource#*=}" | |
if [[ "$keyName" = "$literalSource" ]]; then | |
echo >&2 "invalid literal source $literalSource, expected key=value" | |
return 1 | |
fi | |
CM=$(echo "$CM" | jq --arg keyName "$keyName" --arg value "$value" '.data[$keyName] = $value'); | |
done | |
local fileSource | |
for fileSource in "${fileSources[@]:+"${fileSources[@]}"}"; do | |
local keyName="${fileSource%%=*}" | |
local filePath="${fileSource#*=}" | |
if [[ -f "$filePath" ]]; then | |
if [[ "$keyName" = "$fileSource" ]]; then keyName=$(basename "$fileSource"); fi | |
local content="$(cat "$filePath"; printf x)"; content="${content%x}" | |
CM=$(echo "$CM" | jq --arg keyName "$keyName" --arg content "$content" '.data[$keyName] = $content'); | |
elif [[ -d "$filePath" ]]; then | |
if [[ "$keyName" != "$fileSource" ]]; then echo >&2 "cannot give a key name for a directory path $fileSource"; return 1; fi | |
local file | |
local RESET_DOTGLOB="$(shopt -p dotglob)" | |
shopt -s dotglob | |
for file in "$filePath"/*; do | |
# Hack to keep trailing newline in bash command substitution https://stackoverflow.com/a/15184414/471341 | |
if ! { | |
if [[ -f "$file" ]]; then | |
keyName="$(basename "$file")" | |
local content="$(cat "$file"; printf x)"; content="${content%x}" && | |
CM=$(echo "$CM" | jq --arg keyName "$keyName" --arg content "$content" '.data[$keyName] = $content'); | |
fi | |
}; then | |
local returnval=$? | |
$RESET_DOTGLOB | |
return $returnval | |
fi | |
done | |
$RESET_DOTGLOB | |
else | |
echo >&2 "Not a file or directory: $filePath (--from-file $fileSource)" | |
return 1 | |
fi | |
done | |
echo "$CM" | |
} | |
function dirToSecret() { | |
dirToCm "$@" | jq '{apiVersion: "v1", data: .data | to_entries | [.[] | .value |= @base64] | from_entries, kind: "Secret", metadata: {name: .metadata.name}}' | |
} | |
# Convert a ConfigMap json file to a directory. | |
# Useful for saving the existing configmap to the filesystem so it can be compared | |
# e.g. dirToCm /tmp/dir1 | cmToDir /tmp/dir2 | |
# e.g. kubectl get cm … -o json | cmToDir /tmp/dir | |
# diff /tmp/dir path/to/new-cmdir | |
function cmToDir() { | |
jq -n '""|@base64d' > /dev/null || { echo >&2 "Make sure jq 1.5+ is installed (for @base64d)" ; return 1; } | |
if [ -z "${1:-}" ]; then | |
echo >&2 $'Missing parameter DIR\nUsage: kubectl get cm <NAME> -o json | cmToDir <DIR>' | |
return 1 | |
fi | |
local dir="$1" | |
if ! [ -d "$1" ]; then | |
mkdir "$1" || return 2 | |
fi | |
local CM=$(cat) | |
local ENTRIES=$(echo "$CM" | jq '.data | to_entries') | |
for i in $(seq 0 $(echo "$ENTRIES" | jq 'length - 1')); do | |
local filename=$(echo "$ENTRIES" | jq --arg i $i '.[$i|tonumber].key' -r) | |
echo >&2 "writing $i $filename" | |
# --join-output is like --raw-output but without any added newlines | |
echo "$ENTRIES" | jq --arg i $i '.[$i|tonumber].value' --join-output > "$dir/$filename" | |
done | |
} | |
function secretToDir() { | |
if [[ -z "${1:-}" ]]; then | |
echo >&2 $'Missing parameter DIR\nUsage: kubectl get secret <NAME> -o json | secretToDir <DIR>' | |
return 1 | |
fi | |
jq '.data |= (to_entries | [.[] | .value |= @base64d] | from_entries)' | cmToDir "$1" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment