# Functions for Bash completion of some 'triton' option/arg types. function complete_tritonprofile { local word="$1" local candidates candidates=$(ls -1 ~/.triton/profiles.d/*.json 2>/dev/null \ | sed -E 's/^.*\/([^\/]+)\.json$/\1/') compgen $compgen_opts -W "$candidates" -- "$word" } # # Get completions for a given type of Triton (server-side) data. # # Usage: # _complete_tritondata $type # e.g. _complete_tritondata images # # The easiest/slowest thing to do to complete images would be to just call: # triton [profile-related-args] images -Ho name # or similar. Too slow. # # The next easiest would be this: # candidates=$(TRITON_COMPLETE=$type $COMP_LINE) # where `triton` is setup to specially just handle completions if # `TRITON_COMPLETE` is set. That special handling writes out a cache file to # avoid hitting the server every time. This is still too slow because the # node.js startup time for `triton` is too slow (around 1s on my laptop). # # The next choice is to (a) use the special `TRITON_COMPLETE` handling to # fetch data from the server and write out a cache file, but (b) attempt to # find and use that cache file without calling node.js code. The win is # (at least in my usage) faster response time to a . The cost is # reproducing (imperfectly) in Bash the logic for determining the Triton profile # info to find the cache. # function _complete_tritondata { local type=$1 # First, find the Triton CLI profile. local profile profile=$(echo "$COMP_LINE" | grep -- '\s\+-p\s*\w\+\s\+' | sed -E 's/.* +-p *([^ ]+) +.*/\1/') if [[ -z "$profile" ]]; then profile=$TRITON_PROFILE fi if [[ -z "$profile" ]]; then profile=$(grep '"profile":' ~/.triton/config.json | cut -d'"' -f4) fi if [[ -z "$profile" ]]; then profile=env fi trace " profile: $profile" # Then, determine the account and url that go into the cache dir. # TODO: include -a/-U options that change from profile values # TODO: subuser support local url local account local profileFile profileFile=$HOME/.triton/profiles.d/$profile.json if [[ "$profile" == "env" ]]; then url=$TRITON_URL if [[ -z "$url" ]]; then url=$SDC_URL fi account=$TRITON_ACCOUNT if [[ -z "$account" ]]; then account=$SDC_ACCOUNT fi elif [[ -f $profileFile ]]; then url=$(grep '"url":' $profileFile | cut -d'"' -f4) account=$(grep '"account":' $profileFile | cut -d'"' -f4) fi trace " url: $url" trace " account: $account" # Mimic node-triton/lib/common.js#profileSlug local profileSlug profileSlug="$(echo "$account" | sed -E 's/@/_/g')@$(echo "$url" | sed -E 's#^https?://##')" profileSlug="$(echo "$profileSlug" | sed -E 's/[^a-zA-Z0-9_@-]/_/g')" local cacheFile cacheFile="$HOME/.triton/cache/$profileSlug/$type.completions" trace " cacheFile: $cacheFile" # If we have a cache file, remove it and regenerate if it is >5 minutes old. # # Dev Note: This 5min TTL should match what `lib/cli.js#_emitCompletions()` # is using. local candidates if [[ ! -f "$cacheFile" ]]; then candidates=$(TRITON_COMPLETE=$type $COMP_LINE) else local mtime mtime=$(stat -r "$cacheFile" | awk '{print $10}') local ttl=300 # 5 minutes in seconds local age age=$(echo "$(date +%s) - $mtime" | bc) if [[ $age -gt $ttl ]]; then # Out of date. Regenerate the cache file. trace " cacheFile out-of-date (mtime=$mtime, age=$age, ttl=$ttl)" rm "$cacheFile" candidates=$(TRITON_COMPLETE=$type $COMP_LINE) else trace " cacheFile is in-date (mtime=$mtime, age=$age, ttl=$ttl)" candidates=$(cat "$cacheFile") fi fi echo "$candidates" } function complete_tritonpackage { local word="$1" candidates=$(_complete_tritondata packages) compgen $compgen_opts -W "$candidates" -- "$word" } function complete_tritonimage { local word="$1" candidates=$(_complete_tritondata images) compgen $compgen_opts -W "$candidates" -- "$word" } function complete_tritoninstance { local word="$1" candidates=$(_complete_tritondata instances) compgen $compgen_opts -W "$candidates" -- "$word" } function complete_tritonnetwork { local word="$1" candidates=$(_complete_tritondata networks) compgen $compgen_opts -W "$candidates" -- "$word" } function complete_tritonvolume { local word="$1" candidates=$(_complete_tritondata volumes) compgen $compgen_opts -W "$candidates" -- "$word" } function complete_tritonfwrule { local word="$1" candidates=$(_complete_tritondata fwrules) compgen $compgen_opts -W "$candidates" -- "$word" } function complete_tritonkey { local word="$1" candidates=$(_complete_tritondata keys) compgen $compgen_opts -W "$candidates" -- "$word" } function complete_tritonaffinityrule { local word="$1" candidates=$(_complete_tritondata affinityrules) # Triton affinity rules typically have '=' in them, e.g.: # triton create -a inst==db0 ... # This means we run afoul of the '=' in COMP_WORDBREAKS which results in # triton create -a inst= # leading to: # triton create -a inst=inst== # The answer is to strip off at the last '=' in the returned completions. if [[ "$word" == *"="* ]]; then local uptolastequal uptolastequal="${word%=*}=" compgen $compgen_opts -W "$candidates" -- "$word" \ | cut -c$(( ${#uptolastequal} + 1 ))- else compgen $compgen_opts -W "$candidates" -- "$word" fi } function complete_tritonupdateaccountfield { local word="$1" local candidates candidates="{{UPDATE_ACCOUNT_FIELDS}}" compgen $compgen_opts -W "$candidates" -- "$word" } function complete_tritonupdatefwrulefield { local word="$1" local candidates candidates="{{UPDATE_FWRULE_FIELDS}}" compgen $compgen_opts -W "$candidates" -- "$word" }