This file is indexed.

/usr/share/bash-completion/completions/juju2 is in juju-2.0 2.0~beta4-0ubuntu2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
# juju-core.bash_completion.sh: dynamic bash completion for juju cmdline,
# from parsed (and cached) juju status output.
#
# Author: JuanJo Ciarlante <jjo@canonical.com>
# Copyright 2013+, Canonical Ltd.
# License: GPLv3
#

# Print (return) all machines
_juju_machines_from_file() {
python -c '
import json, sys; j=json.load(sys.stdin)
print "\n".join(j["machines"].keys());' < ${1?}
}

# Print (return) all units, each optionally postfixed by $2 (eg. 'myservice/0:')
_juju_units_from_file() {
python -c '
trail="'${2}'"
import json, sys; j=json.load(sys.stdin)
all_units=[]
for k,v in j["services"].items():
    if v.get("units"):
        all_units.extend(v.get("units",{}).keys())
print "\n".join([unit + trail for unit in all_units])
' < ${1?}
}

# Print (return) all services
_juju_services_from_file() {
python -c '
import json, sys; j=json.load(sys.stdin)
print "\n".join(j["services"].keys());' < ${1?}
}

# Print (return) both services and units, currently used for juju status completion
_juju_services_and_units_from_file() {
    _juju_services_from_file "$@"
    _juju_units_from_file "$@"
}

# Print (return) all juju commands
_juju_list_commands() {
    juju help commands 2>/dev/null | awk '{print $1}'
}

# Print (return) flags for juju action, shamelessly excluding
# -m/--model for cleaner completion for common usage cases
# (e.g. juju ssh <TAB>, etc)
_juju_flags_for() {
    test -z "${1}" && return 0
    juju help ${1} 2>/dev/null |egrep -o --  '(^|-)-[a-z-]+'|egrep -v -- '^(-m|--model)'|sort -u
}

# Print (return) guessed completion function for cmd.
# Guessing is done by parsing 1st line of juju help <cmd>,
# see case switch below.
_juju_completion_func_for_cmd() {
    local action=${1} cword=${2}
    # if cword==1 or action==help, use _juju_list_commands
    if [ "${cword}" -eq 1 -o "${action}" = help ]; then
        echo _juju_list_commands
        return 0
    fi
    # parse 1st line of juju help <cmd>, to guess the completion function
    case $(juju help ${action} 2>/dev/null| head -1) in
        # special case for ssh, scp which have 'service' in 1st line of help:
        *\<unit*|*juju?ssh*|*juju?scp*)    echo _juju_units_from_file;;
        *\<service*)    echo _juju_services_from_file;;
        *\<machine*)    echo _juju_machines_from_file;;
        *pattern*)      echo _juju_services_and_units_from_file;; # e.g. status
        ?*)     echo true ;;  # help ok, existing command, no more expansion
        *)      echo false;;  # failed, not a command
    esac
}

# Print (return) filename from juju status cached output (if not expired),
# create cache dirs if needed
# - setups caching dir if non-existent
# - caches juju status output, $cache_mins minutes max
_juju_get_status_filename() {
    local cache_mins=60     # ttl=60 mins
    local cache_dir=$HOME/.cache/juju
    local juju_status_file=${cache_dir}/juju-status-${JUJU_MODEL:-default}
    # setup caching dir under ~/.cache/juju
    test -d ${cache_dir} || install -d ${cache_dir} -m 700
    # if can't find a fresh (age < $cache_mins) saved file, with a ~reasonable size ...
    if [[ -z $(find "${juju_status_file}" -mmin -${cache_mins} -a -size +32c 2> /dev/null) ]]; then
        # ... create it
        juju status --format=json > "${juju_status_file}".tmp && \
            mv "${juju_status_file}".tmp "${juju_status_file}"
        rm -f "${juju_status_file}".tmp
    fi
    if [ -r "${juju_status_file}" ]; then
        echo "${juju_status_file}"
    else
        return 1
    fi
}
# Main completion function wrap:
# calls passed completion function, also adding flags for cmd
_juju_complete_with_func() {
    local action="${1}" func=${2?}
    local cur

    # scp is special, as we want ':' appended to unit names,
    # and filename completion also.
    local postfix_str= compgen_xtra=
    if [ "${action}" = "scp" ]; then
        local orig_comp_wordbreaks="${COMP_WORDBREAKS}"
        COMP_WORDBREAKS="${COMP_WORDBREAKS/:/}"
        postfix_str=':'
        compgen_xtra='-A file'
        compopt -o nospace
    fi
    juju_status_file=
    # if func name ends with 'from_file', set juju_status_file
    [[ ${func} =~ .*from_file ]] &&  juju_status_file=$(_juju_get_status_filename)
    # build COMPREPLY from passed function stdout, and _juju_flags_for $action
    cur="${COMP_WORDS[COMP_CWORD]}"
    COMPREPLY=( $( compgen ${compgen_xtra} -W "$(${func} ${juju_status_file} ${postfix_str}) $(_juju_flags_for "${action}")" -- ${cur} ))
    if [ "${action}" = "scp" ]; then
        COMP_WORDBREAKS="${orig_comp_wordbreaks}"
        compopt +o nospace
    fi
    return 0
}

# Not used here, available to the user for quick cache removal
_juju_completion_cache_rm() {
    rm -fv $HOME/.cache/juju/juju-status-${JUJU_MODEL:-default}
}

# main completion function entry point
_juju() {
    local action parsing_func
    action="${COMP_WORDS[1]}"
    COMPREPLY=()
    parsing_func=$(_juju_completion_func_for_cmd "${action}" ${COMP_CWORD})
    test -z "${parsing_func}" && return 0
    _juju_complete_with_func "${action}" "${parsing_func}"
    return $?
}
complete -F _juju juju
# vim: ai et sw=2 ts=2