diff --git a/extra/completions/_stackablectl b/extra/completions/_stackablectl index e3c334fd..459b1484 100644 --- a/extra/completions/_stackablectl +++ b/extra/completions/_stackablectl @@ -593,7 +593,7 @@ esac ;; esac ;; -(stacklets) +(stacklet) _arguments "${_arguments_options[@]}" \ '-l+[Log level this application uses]:LOG_LEVEL: ' \ '--log-level=[Log level this application uses]:LOG_LEVEL: ' \ @@ -612,17 +612,43 @@ _arguments "${_arguments_options[@]}" \ '--help[Print help (see more with '\''--help'\'')]' \ '-V[Print version]' \ '--version[Print version]' \ -":: :_stackablectl__stacklets_commands" \ -"*::: :->stacklets" \ +":: :_stackablectl__stacklet_commands" \ +"*::: :->stacklet" \ && ret=0 case $state in - (stacklets) + (stacklet) words=($line[1] "${words[@]}") (( CURRENT += 1 )) - curcontext="${curcontext%:*:*}:stackablectl-stacklets-command-$line[1]:" + curcontext="${curcontext%:*:*}:stackablectl-stacklet-command-$line[1]:" case $line[1] in - (list) + (credentials) +_arguments "${_arguments_options[@]}" \ +'-n+[Namespace in the cluster used to deploy the products]:PRODUCT_NAMESPACE: ' \ +'--product-namespace=[Namespace in the cluster used to deploy the products]:PRODUCT_NAMESPACE: ' \ +'--product-ns=[Namespace in the cluster used to deploy the products]:PRODUCT_NAMESPACE: ' \ +'-l+[Log level this application uses]:LOG_LEVEL: ' \ +'--log-level=[Log level this application uses]:LOG_LEVEL: ' \ +'*-d+[Provide one or more additional (custom) demo file(s)]:DEMO_FILE:_files' \ +'*--demo-file=[Provide one or more additional (custom) demo file(s)]:DEMO_FILE:_files' \ +'*-s+[Provide one or more additional (custom) stack file(s)]:STACK_FILE:_files' \ +'*--stack-file=[Provide one or more additional (custom) stack file(s)]:STACK_FILE:_files' \ +'*-r+[Provide one or more additional (custom) release file(s)]:RELEASE_FILE:_files' \ +'*--release-file=[Provide one or more additional (custom) release file(s)]:RELEASE_FILE:_files' \ +'--helm-repo-stable=[Provide a custom Helm stable repository URL]:URL:_urls' \ +'--helm-repo-test=[Provide a custom Helm test repository URL]:URL:_urls' \ +'--helm-repo-dev=[Provide a custom Helm dev repository URL]:URL:_urls' \ +'--no-cache[Do not cache the remote (default) demo, stack and release files]' \ +'--offline[Do not request any remote files via the network]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +'-V[Print version]' \ +'--version[Print version]' \ +':product_name -- The name of the product, for example '\''superset'\'':' \ +':stacklet_name -- The name of the stacklet, for example '\''superset'\'':' \ +&& ret=0 +;; +(list) _arguments "${_arguments_options[@]}" \ '-o+[]:OUTPUT_TYPE:((plain\:"Print output formatted as plain text" json\:"Print output formatted as JSON" @@ -658,7 +684,7 @@ yaml\:"Print output formatted as YAML"))' \ ;; (help) _arguments "${_arguments_options[@]}" \ -":: :_stackablectl__stacklets__help_commands" \ +":: :_stackablectl__stacklet__help_commands" \ "*::: :->help" \ && ret=0 @@ -666,9 +692,13 @@ _arguments "${_arguments_options[@]}" \ (help) words=($line[1] "${words[@]}") (( CURRENT += 1 )) - curcontext="${curcontext%:*:*}:stackablectl-stacklets-help-command-$line[1]:" + curcontext="${curcontext%:*:*}:stackablectl-stacklet-help-command-$line[1]:" case $line[1] in - (list) + (credentials) +_arguments "${_arguments_options[@]}" \ +&& ret=0 +;; +(list) _arguments "${_arguments_options[@]}" \ && ret=0 ;; @@ -1182,19 +1212,23 @@ _arguments "${_arguments_options[@]}" \ ;; esac ;; -(stacklets) +(stacklet) _arguments "${_arguments_options[@]}" \ -":: :_stackablectl__help__stacklets_commands" \ -"*::: :->stacklets" \ +":: :_stackablectl__help__stacklet_commands" \ +"*::: :->stacklet" \ && ret=0 case $state in - (stacklets) + (stacklet) words=($line[1] "${words[@]}") (( CURRENT += 1 )) - curcontext="${curcontext%:*:*}:stackablectl-help-stacklets-command-$line[1]:" + curcontext="${curcontext%:*:*}:stackablectl-help-stacklet-command-$line[1]:" case $line[1] in - (list) + (credentials) +_arguments "${_arguments_options[@]}" \ +&& ret=0 +;; +(list) _arguments "${_arguments_options[@]}" \ && ret=0 ;; @@ -1301,7 +1335,7 @@ _stackablectl_commands() { 'operator:Interact with single operator instead of the full platform' \ 'release:Interact with all operators of the platform which are released together' \ 'stack:Interact with stacks, which are ready-to-use product combinations' \ -'stacklets:Interact with deployed stacklets, which are bundles of resources and containers required to run the product' \ +'stacklet:Interact with deployed stacklets, which are bundles of resources and containers required to run the product' \ 'demo:Interact with demos, which are end-to-end usage demonstrations of the Stackable data platform' \ 'completions:Generate shell completions for this tool' \ 'cache:Interact with locally cached files' \ @@ -1375,6 +1409,21 @@ _stackablectl__help__completions_commands() { ) _describe -t commands 'stackablectl help completions commands' commands "$@" } +(( $+functions[_stackablectl__help__stacklet__credentials_commands] )) || +_stackablectl__help__stacklet__credentials_commands() { + local commands; commands=() + _describe -t commands 'stackablectl help stacklet credentials commands' commands "$@" +} +(( $+functions[_stackablectl__stacklet__credentials_commands] )) || +_stackablectl__stacklet__credentials_commands() { + local commands; commands=() + _describe -t commands 'stackablectl stacklet credentials commands' commands "$@" +} +(( $+functions[_stackablectl__stacklet__help__credentials_commands] )) || +_stackablectl__stacklet__help__credentials_commands() { + local commands; commands=() + _describe -t commands 'stackablectl stacklet help credentials commands' commands "$@" +} (( $+functions[_stackablectl__demo_commands] )) || _stackablectl__demo_commands() { local commands; commands=( @@ -1519,7 +1568,7 @@ _stackablectl__help_commands() { 'operator:Interact with single operator instead of the full platform' \ 'release:Interact with all operators of the platform which are released together' \ 'stack:Interact with stacks, which are ready-to-use product combinations' \ -'stacklets:Interact with deployed stacklets, which are bundles of resources and containers required to run the product' \ +'stacklet:Interact with deployed stacklets, which are bundles of resources and containers required to run the product' \ 'demo:Interact with demos, which are end-to-end usage demonstrations of the Stackable data platform' \ 'completions:Generate shell completions for this tool' \ 'cache:Interact with locally cached files' \ @@ -1580,18 +1629,19 @@ _stackablectl__stack__help__help_commands() { local commands; commands=() _describe -t commands 'stackablectl stack help help commands' commands "$@" } -(( $+functions[_stackablectl__stacklets__help_commands] )) || -_stackablectl__stacklets__help_commands() { +(( $+functions[_stackablectl__stacklet__help_commands] )) || +_stackablectl__stacklet__help_commands() { local commands; commands=( -'list:List deployed services' \ +'credentials:Display credentials for a stacklet' \ +'list:List deployed stacklets' \ 'help:Print this message or the help of the given subcommand(s)' \ ) - _describe -t commands 'stackablectl stacklets help commands' commands "$@" + _describe -t commands 'stackablectl stacklet help commands' commands "$@" } -(( $+functions[_stackablectl__stacklets__help__help_commands] )) || -_stackablectl__stacklets__help__help_commands() { +(( $+functions[_stackablectl__stacklet__help__help_commands] )) || +_stackablectl__stacklet__help__help_commands() { local commands; commands=() - _describe -t commands 'stackablectl stacklets help help commands' commands "$@" + _describe -t commands 'stackablectl stacklet help help commands' commands "$@" } (( $+functions[_stackablectl__demo__help__install_commands] )) || _stackablectl__demo__help__install_commands() { @@ -1713,10 +1763,10 @@ _stackablectl__help__stack__list_commands() { local commands; commands=() _describe -t commands 'stackablectl help stack list commands' commands "$@" } -(( $+functions[_stackablectl__help__stacklets__list_commands] )) || -_stackablectl__help__stacklets__list_commands() { +(( $+functions[_stackablectl__help__stacklet__list_commands] )) || +_stackablectl__help__stacklet__list_commands() { local commands; commands=() - _describe -t commands 'stackablectl help stacklets list commands' commands "$@" + _describe -t commands 'stackablectl help stacklet list commands' commands "$@" } (( $+functions[_stackablectl__operator__help__list_commands] )) || _stackablectl__operator__help__list_commands() { @@ -1748,15 +1798,15 @@ _stackablectl__stack__list_commands() { local commands; commands=() _describe -t commands 'stackablectl stack list commands' commands "$@" } -(( $+functions[_stackablectl__stacklets__help__list_commands] )) || -_stackablectl__stacklets__help__list_commands() { +(( $+functions[_stackablectl__stacklet__help__list_commands] )) || +_stackablectl__stacklet__help__list_commands() { local commands; commands=() - _describe -t commands 'stackablectl stacklets help list commands' commands "$@" + _describe -t commands 'stackablectl stacklet help list commands' commands "$@" } -(( $+functions[_stackablectl__stacklets__list_commands] )) || -_stackablectl__stacklets__list_commands() { +(( $+functions[_stackablectl__stacklet__list_commands] )) || +_stackablectl__stacklet__list_commands() { local commands; commands=() - _describe -t commands 'stackablectl stacklets list commands' commands "$@" + _describe -t commands 'stackablectl stacklet list commands' commands "$@" } (( $+functions[_stackablectl__help__operator_commands] )) || _stackablectl__help__operator_commands() { @@ -1821,20 +1871,22 @@ _stackablectl__stack_commands() { ) _describe -t commands 'stackablectl stack commands' commands "$@" } -(( $+functions[_stackablectl__help__stacklets_commands] )) || -_stackablectl__help__stacklets_commands() { +(( $+functions[_stackablectl__help__stacklet_commands] )) || +_stackablectl__help__stacklet_commands() { local commands; commands=( -'list:List deployed services' \ +'credentials:Display credentials for a stacklet' \ +'list:List deployed stacklets' \ ) - _describe -t commands 'stackablectl help stacklets commands' commands "$@" + _describe -t commands 'stackablectl help stacklet commands' commands "$@" } -(( $+functions[_stackablectl__stacklets_commands] )) || -_stackablectl__stacklets_commands() { +(( $+functions[_stackablectl__stacklet_commands] )) || +_stackablectl__stacklet_commands() { local commands; commands=( -'list:List deployed services' \ +'credentials:Display credentials for a stacklet' \ +'list:List deployed stacklets' \ 'help:Print this message or the help of the given subcommand(s)' \ ) - _describe -t commands 'stackablectl stacklets commands' commands "$@" + _describe -t commands 'stackablectl stacklet commands' commands "$@" } (( $+functions[_stackablectl__help__operator__uninstall_commands] )) || _stackablectl__help__operator__uninstall_commands() { diff --git a/extra/completions/stackablectl.bash b/extra/completions/stackablectl.bash index 9f048250..23c52906 100644 --- a/extra/completions/stackablectl.bash +++ b/extra/completions/stackablectl.bash @@ -33,8 +33,8 @@ _stackablectl() { stackablectl,stack) cmd="stackablectl__stack" ;; - stackablectl,stacklets) - cmd="stackablectl__stacklets" + stackablectl,stacklet) + cmd="stackablectl__stacklet" ;; stackablectl__cache,clean) cmd="stackablectl__cache__clean" @@ -123,8 +123,8 @@ _stackablectl() { stackablectl__help,stack) cmd="stackablectl__help__stack" ;; - stackablectl__help,stacklets) - cmd="stackablectl__help__stacklets" + stackablectl__help,stacklet) + cmd="stackablectl__help__stacklet" ;; stackablectl__help__cache,clean) cmd="stackablectl__help__cache__clean" @@ -186,8 +186,11 @@ _stackablectl() { stackablectl__help__stack,list) cmd="stackablectl__help__stack__list" ;; - stackablectl__help__stacklets,list) - cmd="stackablectl__help__stacklets__list" + stackablectl__help__stacklet,credentials) + cmd="stackablectl__help__stacklet__credentials" + ;; + stackablectl__help__stacklet,list) + cmd="stackablectl__help__stacklet__list" ;; stackablectl__operator,describe) cmd="stackablectl__operator__describe" @@ -279,17 +282,23 @@ _stackablectl() { stackablectl__stack__help,list) cmd="stackablectl__stack__help__list" ;; - stackablectl__stacklets,help) - cmd="stackablectl__stacklets__help" + stackablectl__stacklet,credentials) + cmd="stackablectl__stacklet__credentials" + ;; + stackablectl__stacklet,help) + cmd="stackablectl__stacklet__help" ;; - stackablectl__stacklets,list) - cmd="stackablectl__stacklets__list" + stackablectl__stacklet,list) + cmd="stackablectl__stacklet__list" ;; - stackablectl__stacklets__help,help) - cmd="stackablectl__stacklets__help__help" + stackablectl__stacklet__help,credentials) + cmd="stackablectl__stacklet__help__credentials" ;; - stackablectl__stacklets__help,list) - cmd="stackablectl__stacklets__help__list" + stackablectl__stacklet__help,help) + cmd="stackablectl__stacklet__help__help" + ;; + stackablectl__stacklet__help,list) + cmd="stackablectl__stacklet__help__list" ;; *) ;; @@ -298,7 +307,7 @@ _stackablectl() { case "${cmd}" in stackablectl) - opts="-l -d -s -r -h -V --log-level --no-cache --offline --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --help --version operator release stack stacklets demo completions cache help" + opts="-l -d -s -r -h -V --log-level --no-cache --offline --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --help --version operator release stack stacklet demo completions cache help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1254,7 +1263,7 @@ _stackablectl() { return 0 ;; stackablectl__help) - opts="operator release stack stacklets demo completions cache help" + opts="operator release stack stacklet demo completions cache help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1645,8 +1654,8 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - stackablectl__help__stacklets) - opts="list" + stackablectl__help__stacklet) + opts="credentials list" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1659,7 +1668,21 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - stackablectl__help__stacklets__list) + stackablectl__help__stacklet__credentials) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + stackablectl__help__stacklet__list) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) @@ -2995,8 +3018,8 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - stackablectl__stacklets) - opts="-l -d -s -r -h -V --log-level --no-cache --offline --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --help --version list help" + stackablectl__stacklet) + opts="-l -d -s -r -h -V --log-level --no-cache --offline --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --help --version credentials list help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3053,12 +3076,96 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - stackablectl__stacklets__help) - opts="list help" + stackablectl__stacklet__credentials) + opts="-n -l -d -s -r -h -V --product-ns --product-namespace --log-level --no-cache --offline --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --help --version " if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 fi + case "${prev}" in + --product-namespace) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --product-ns) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -n) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --log-level) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -l) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --demo-file) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -d) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stack-file) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -s) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --release-file) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -r) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --helm-repo-stable) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --helm-repo-test) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --helm-repo-dev) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + stackablectl__stacklet__help) + opts="credentials list help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + stackablectl__stacklet__help__credentials) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi case "${prev}" in *) COMPREPLY=() @@ -3067,7 +3174,7 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - stackablectl__stacklets__help__help) + stackablectl__stacklet__help__help) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) @@ -3081,7 +3188,7 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - stackablectl__stacklets__help__list) + stackablectl__stacklet__help__list) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) @@ -3095,7 +3202,7 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - stackablectl__stacklets__list) + stackablectl__stacklet__list) opts="-c -o -n -l -d -s -r -h -V --color --output --operator-ns --operator-namespace --product-ns --product-namespace --log-level --no-cache --offline --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --help --version" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) diff --git a/extra/completions/stackablectl.fish b/extra/completions/stackablectl.fish index 69a58ef4..6a55948c 100644 --- a/extra/completions/stackablectl.fish +++ b/extra/completions/stackablectl.fish @@ -12,7 +12,7 @@ complete -c stackablectl -n "__fish_use_subcommand" -s V -l version -d 'Print ve complete -c stackablectl -n "__fish_use_subcommand" -f -a "operator" -d 'Interact with single operator instead of the full platform' complete -c stackablectl -n "__fish_use_subcommand" -f -a "release" -d 'Interact with all operators of the platform which are released together' complete -c stackablectl -n "__fish_use_subcommand" -f -a "stack" -d 'Interact with stacks, which are ready-to-use product combinations' -complete -c stackablectl -n "__fish_use_subcommand" -f -a "stacklets" -d 'Interact with deployed stacklets, which are bundles of resources and containers required to run the product' +complete -c stackablectl -n "__fish_use_subcommand" -f -a "stacklet" -d 'Interact with deployed stacklets, which are bundles of resources and containers required to run the product' complete -c stackablectl -n "__fish_use_subcommand" -f -a "demo" -d 'Interact with demos, which are end-to-end usage demonstrations of the Stackable data platform' complete -c stackablectl -n "__fish_use_subcommand" -f -a "completions" -d 'Generate shell completions for this tool' complete -c stackablectl -n "__fish_use_subcommand" -f -a "cache" -d 'Interact with locally cached files' @@ -243,36 +243,50 @@ complete -c stackablectl -n "__fish_seen_subcommand_from stack; and __fish_seen_ complete -c stackablectl -n "__fish_seen_subcommand_from stack; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install; and not __fish_seen_subcommand_from help" -f -a "describe" -d 'Describe a specific stack' complete -c stackablectl -n "__fish_seen_subcommand_from stack; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install; and not __fish_seen_subcommand_from help" -f -a "install" -d 'Install a specific stack' complete -c stackablectl -n "__fish_seen_subcommand_from stack; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s l -l log-level -d 'Log level this application uses' -r -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s r -l release-file -d 'Provide one or more additional (custom) release file(s)' -r -F -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -l helm-repo-stable -d 'Provide a custom Helm stable repository URL' -r -f -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -l helm-repo-test -d 'Provide a custom Helm test repository URL' -r -f -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -l helm-repo-dev -d 'Provide a custom Helm dev repository URL' -r -f -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -l offline -d 'Do not request any remote files via the network' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s V -l version -d 'Print version' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -f -a "list" -d 'List deployed services' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -s o -l output -r -f -a "{plain Print output formatted as plain text,json Print output formatted as JSON,yaml Print output formatted as YAML}" -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -l operator-namespace -l operator-ns -d 'Namespace where the operators are deployed' -r -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -s n -l product-namespace -l product-ns -d 'Namespace where the products (e.g. stacks or demos) are deployed' -r -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -s l -l log-level -d 'Log level this application uses' -r -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -s r -l release-file -d 'Provide one or more additional (custom) release file(s)' -r -F -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -l helm-repo-stable -d 'Provide a custom Helm stable repository URL' -r -f -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -l helm-repo-test -d 'Provide a custom Helm test repository URL' -r -f -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -l helm-repo-dev -d 'Provide a custom Helm dev repository URL' -r -f -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -s c -l color -d 'Controls if the output will use color. This only applies to the output type \'plain\'' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -l offline -d 'Do not request any remote files via the network' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from list" -s V -l version -d 'Print version' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -f -a "list" -d 'List deployed services' -complete -c stackablectl -n "__fish_seen_subcommand_from stacklets; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s l -l log-level -d 'Log level this application uses' -r +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s r -l release-file -d 'Provide one or more additional (custom) release file(s)' -r -F +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -l helm-repo-stable -d 'Provide a custom Helm stable repository URL' -r -f +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -l helm-repo-test -d 'Provide a custom Helm test repository URL' -r -f +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -l helm-repo-dev -d 'Provide a custom Helm dev repository URL' -r -f +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -l offline -d 'Do not request any remote files via the network' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -s V -l version -d 'Print version' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -f -a "credentials" -d 'Display credentials for a stacklet' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -f -a "list" -d 'List deployed stacklets' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -s n -l product-namespace -l product-ns -d 'Namespace in the cluster used to deploy the products' -r +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -s l -l log-level -d 'Log level this application uses' -r +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -s r -l release-file -d 'Provide one or more additional (custom) release file(s)' -r -F +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -l helm-repo-stable -d 'Provide a custom Helm stable repository URL' -r -f +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -l helm-repo-test -d 'Provide a custom Helm test repository URL' -r -f +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -l helm-repo-dev -d 'Provide a custom Helm dev repository URL' -r -f +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -l offline -d 'Do not request any remote files via the network' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from credentials" -s V -l version -d 'Print version' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -s o -l output -r -f -a "{plain Print output formatted as plain text,json Print output formatted as JSON,yaml Print output formatted as YAML}" +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -l operator-namespace -l operator-ns -d 'Namespace where the operators are deployed' -r +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -s n -l product-namespace -l product-ns -d 'Namespace where the products (e.g. stacks or demos) are deployed' -r +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -s l -l log-level -d 'Log level this application uses' -r +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -s r -l release-file -d 'Provide one or more additional (custom) release file(s)' -r -F +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -l helm-repo-stable -d 'Provide a custom Helm stable repository URL' -r -f +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -l helm-repo-test -d 'Provide a custom Helm test repository URL' -r -f +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -l helm-repo-dev -d 'Provide a custom Helm dev repository URL' -r -f +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -s c -l color -d 'Controls if the output will use color. This only applies to the output type \'plain\'' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -l offline -d 'Do not request any remote files via the network' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from list" -s V -l version -d 'Print version' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -f -a "credentials" -d 'Display credentials for a stacklet' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -f -a "list" -d 'List deployed stacklets' +complete -c stackablectl -n "__fish_seen_subcommand_from stacklet; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c stackablectl -n "__fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install; and not __fish_seen_subcommand_from help" -s l -l log-level -d 'Log level this application uses' -r complete -c stackablectl -n "__fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install; and not __fish_seen_subcommand_from help" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F complete -c stackablectl -n "__fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install; and not __fish_seen_subcommand_from help" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F @@ -428,14 +442,14 @@ complete -c stackablectl -n "__fish_seen_subcommand_from cache; and __fish_seen_ complete -c stackablectl -n "__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from clean; and not __fish_seen_subcommand_from help" -f -a "list" -d 'List cached files' complete -c stackablectl -n "__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from clean; and not __fish_seen_subcommand_from help" -f -a "clean" -d 'Clean cached files' complete -c stackablectl -n "__fish_seen_subcommand_from cache; and __fish_seen_subcommand_from help; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from clean; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "operator" -d 'Interact with single operator instead of the full platform' -complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "release" -d 'Interact with all operators of the platform which are released together' -complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "stack" -d 'Interact with stacks, which are ready-to-use product combinations' -complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "stacklets" -d 'Interact with deployed stacklets, which are bundles of resources and containers required to run the product' -complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "demo" -d 'Interact with demos, which are end-to-end usage demonstrations of the Stackable data platform' -complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "completions" -d 'Generate shell completions for this tool' -complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "cache" -d 'Interact with locally cached files' -complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "operator" -d 'Interact with single operator instead of the full platform' +complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "release" -d 'Interact with all operators of the platform which are released together' +complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "stack" -d 'Interact with stacks, which are ready-to-use product combinations' +complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "stacklet" -d 'Interact with deployed stacklets, which are bundles of resources and containers required to run the product' +complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "demo" -d 'Interact with demos, which are end-to-end usage demonstrations of the Stackable data platform' +complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "completions" -d 'Generate shell completions for this tool' +complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "cache" -d 'Interact with locally cached files' +complete -c stackablectl -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from release; and not __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from completions; and not __fish_seen_subcommand_from cache; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install; and not __fish_seen_subcommand_from uninstall; and not __fish_seen_subcommand_from installed" -f -a "list" -d 'List available operators' complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install; and not __fish_seen_subcommand_from uninstall; and not __fish_seen_subcommand_from installed" -f -a "describe" -d 'Print out detailed operator information' complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from operator; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install; and not __fish_seen_subcommand_from uninstall; and not __fish_seen_subcommand_from installed" -f -a "install" -d 'Install one or more operators' @@ -448,7 +462,8 @@ complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_s complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install" -f -a "list" -d 'List available stacks' complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install" -f -a "describe" -d 'Describe a specific stack' complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from stack; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install" -f -a "install" -d 'Install a specific stack' -complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from stacklets; and not __fish_seen_subcommand_from list" -f -a "list" -d 'List deployed services' +complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list" -f -a "credentials" -d 'Display credentials for a stacklet' +complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from stacklet; and not __fish_seen_subcommand_from credentials; and not __fish_seen_subcommand_from list" -f -a "list" -d 'List deployed stacklets' complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install" -f -a "list" -d 'List available demos' complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install" -f -a "describe" -d 'Print out detailed demo information' complete -c stackablectl -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from demo; and not __fish_seen_subcommand_from list; and not __fish_seen_subcommand_from describe; and not __fish_seen_subcommand_from install" -f -a "install" -d 'Install a specific demo' diff --git a/extra/man/stackablectl.1 b/extra/man/stackablectl.1 index e103641d..4082c852 100644 --- a/extra/man/stackablectl.1 +++ b/extra/man/stackablectl.1 @@ -80,7 +80,7 @@ Interact with all operators of the platform which are released together stackablectl\-stack(1) Interact with stacks, which are ready\-to\-use product combinations .TP -stackablectl\-stacklets(1) +stackablectl\-stacklet(1) Interact with deployed stacklets, which are bundles of resources and containers required to run the product .TP stackablectl\-demo(1) diff --git a/rust/stackable-cockpit/src/platform/credentials.rs b/rust/stackable-cockpit/src/platform/credentials.rs new file mode 100644 index 00000000..df95e46f --- /dev/null +++ b/rust/stackable-cockpit/src/platform/credentials.rs @@ -0,0 +1,88 @@ +use std::fmt::Display; + +use kube::{core::DynamicObject, ResourceExt}; +use serde::Serialize; +use snafu::{OptionExt, ResultExt, Snafu}; + +use crate::utils::k8s::{KubeClient, KubeClientError}; + +pub type Result = std::result::Result; + +#[derive(Debug, Snafu)] +pub enum CredentialsError { + #[snafu(display("kubernetes error"))] + KubeError { source: KubeClientError }, + + #[snafu(display("no credentials secret found"))] + NoSecret, +} + +#[derive(Debug, Serialize)] +pub struct Credentials { + pub username: String, + pub password: String, +} + +impl Display for Credentials { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", self.username, self.password) + } +} + +/// Retrieves the credentials looking up a secret identified by `secret_name` +/// in `secret_namespace`. The function returns [`Ok(None)`] if `username_key` +/// and/or `password_key` are not found or the product does not provide +/// any credentials. +pub async fn get_credentials( + kube_client: &KubeClient, + product_name: &str, + stacklet: &DynamicObject, +) -> Result> { + // FIXME (Techassi): This should be discoverable, instead of hard-coding + // supported products. Additionally, all the username and password keys + // should be the same to further simplify the implementation. This is + // part of many upcoming changes unifying the SDP. + + // FIXME (Techassi): Add a separate CredentialsError which indicates what + // went wrong when retrieving credentials: + // - No permissions to do so, most likely a 403 + // - Secret not found, most likely a 404 + // - No credentialsSecret present, None below + // - Username and/or password key not found, don't return Option + + let credentials = match product_name { + "airflow" | "superset" => { + let secret_name = stacklet.data["spec"]["clusterConfig"]["credentialsSecret"] + .as_str() + .context(NoSecretSnafu)?; + + kube_client + .get_credentials_from_secret( + secret_name, + &stacklet.namespace().unwrap(), + "adminUser.username", + "adminUser.password", + ) + .await + .context(KubeSnafu)? + } + "nifi" => { + let secret_name = stacklet.data["spec"]["clusterConfig"]["credentialsSecret"] + .as_str() + .context(NoSecretSnafu)?; + + kube_client + .get_credentials_from_secret( + secret_name, + &stacklet.namespace().unwrap(), + "username", + "password", + ) + .await + .context(KubeSnafu)? + } + _ => return Ok(None), + }; + + Ok(Some(credentials)) +} diff --git a/rust/stackable-cockpit/src/platform/mod.rs b/rust/stackable-cockpit/src/platform/mod.rs index 95d9b467..3d28d09e 100644 --- a/rust/stackable-cockpit/src/platform/mod.rs +++ b/rust/stackable-cockpit/src/platform/mod.rs @@ -1,4 +1,5 @@ pub mod cluster; +pub mod credentials; pub mod demo; pub mod namespace; pub mod operator; diff --git a/rust/stackable-cockpit/src/platform/service.rs b/rust/stackable-cockpit/src/platform/service.rs index 1b82a251..27ee3eae 100644 --- a/rust/stackable-cockpit/src/platform/service.rs +++ b/rust/stackable-cockpit/src/platform/service.rs @@ -40,12 +40,11 @@ pub enum ServiceError { } pub async fn get_service_endpoints( + kube_client: &KubeClient, product_name: &str, object_name: &str, object_namespace: &str, ) -> Result, ServiceError> { - let kube_client = KubeClient::new().await.context(KubeClientSnafu)?; - let service_list_params = ListParams::from_product(product_name, Some(object_name), ProductLabel::Name); @@ -57,7 +56,7 @@ pub async fn get_service_endpoints( let mut endpoints = IndexMap::new(); for service in services { - match get_service_endpoint_urls(&kube_client, &service, object_name).await { + match get_service_endpoint_urls(kube_client, &service, object_name).await { Ok(urls) => endpoints.extend(urls), Err(err) => warn!( "Failed to get endpoint_urls of service {service_name}: {err}", diff --git a/rust/stackable-cockpit/src/platform/stacklet/grafana.rs b/rust/stackable-cockpit/src/platform/stacklet/grafana.rs index dc228d8a..27f07421 100644 --- a/rust/stackable-cockpit/src/platform/stacklet/grafana.rs +++ b/rust/stackable-cockpit/src/platform/stacklet/grafana.rs @@ -1,10 +1,9 @@ use kube::{api::ListParams, ResourceExt}; -use snafu::ResultExt; use crate::{ platform::{ service::get_service_endpoint_urls, - stacklet::{KubeSnafu, Stacklet, StackletError}, + stacklet::{Stacklet, StackletError}, }, utils::k8s::{KubeClient, ListParamsExt, ProductLabel}, }; @@ -16,10 +15,7 @@ pub(super) async fn list( let mut stacklets = Vec::new(); let params = ListParams::from_product("grafana", None, ProductLabel::Name); - let services = kube_client - .list_services(namespace, ¶ms) - .await - .context(KubeSnafu)?; + let services = kube_client.list_services(namespace, ¶ms).await?; for service in services { let service_name = service.name_any(); diff --git a/rust/stackable-cockpit/src/platform/stacklet/minio.rs b/rust/stackable-cockpit/src/platform/stacklet/minio.rs index b5bdcfb2..344f0e7a 100644 --- a/rust/stackable-cockpit/src/platform/stacklet/minio.rs +++ b/rust/stackable-cockpit/src/platform/stacklet/minio.rs @@ -1,10 +1,9 @@ use kube::{api::ListParams, ResourceExt}; -use snafu::ResultExt; use crate::{ platform::{ service::get_service_endpoint_urls, - stacklet::{KubeSnafu, Stacklet, StackletError}, + stacklet::{Stacklet, StackletError}, }, utils::k8s::KubeClient, }; @@ -17,10 +16,7 @@ pub(super) async fn list( // The helm-chart uses `app` instead of `app.kubernetes.io/app`, so we can't use `ListParams::from_product` here let params = ListParams::default().labels("app=minio,app.kubernetes.io/managed-by=Helm"); - let services = kube_client - .list_services(namespace, ¶ms) - .await - .context(KubeSnafu)?; + let services = kube_client.list_services(namespace, ¶ms).await?; let console_services = services .iter() .filter(|s| s.name_unchecked().ends_with("-console")); diff --git a/rust/stackable-cockpit/src/platform/stacklet/mod.rs b/rust/stackable-cockpit/src/platform/stacklet/mod.rs index c8e1ca10..308bcc95 100644 --- a/rust/stackable-cockpit/src/platform/stacklet/mod.rs +++ b/rust/stackable-cockpit/src/platform/stacklet/mod.rs @@ -10,7 +10,10 @@ use utoipa::ToSchema; use crate::{ constants::PRODUCT_NAMES, - platform::service::{get_service_endpoints, ServiceError}, + platform::{ + credentials::{get_credentials, Credentials, CredentialsError}, + service::{get_service_endpoints, ServiceError}, + }, utils::{ k8s::{ConditionsExt, DisplayCondition, KubeClient, KubeClientError}, string::Casing, @@ -45,7 +48,7 @@ pub struct Stacklet { #[derive(Debug, Snafu)] pub enum StackletError { - #[snafu(display("kubernetes error"))] + #[snafu(display("kubernetes error"), context(false))] KubeError { source: KubeClientError }, #[snafu(display("no namespace set for custom resource '{crd_name}'"))] @@ -62,8 +65,8 @@ pub enum StackletError { /// namespaces are returned. If `namespace` is [`Some`], only stacklets installed /// in the specified namespace are returned. The `options` allow further /// customization of the returned information. -pub async fn list(namespace: Option<&str>) -> Result, StackletError> { - let kube_client = KubeClient::new().await.context(KubeSnafu)?; +pub async fn list_stacklets(namespace: Option<&str>) -> Result, StackletError> { + let kube_client = KubeClient::new().await?; let mut stacklets = list_stackable_stacklets(&kube_client, namespace).await?; stacklets.extend(grafana::list(&kube_client, namespace).await?); @@ -74,6 +77,36 @@ pub async fn list(namespace: Option<&str>) -> Result, StackletErro Ok(stacklets) } +pub async fn get_credentials_for_product( + namespace: &str, + object_name: &str, + product_name: &str, +) -> Result, StackletError> { + let kube_client = KubeClient::new().await?; + + let product_gvk = gvk_from_product_name(product_name); + let product_cluster = match kube_client + .get_namespaced_object(namespace, object_name, &product_gvk) + .await? + { + Some(obj) => obj, + None => { + info!( + "Failed to retrieve credentials because the gvk {product_gvk:?} cannot be resolved" + ); + return Ok(None); + } + }; + + let credentials = match get_credentials(&kube_client, product_name, &product_cluster).await { + Ok(credentials) => credentials, + Err(CredentialsError::NoSecret) => None, + Err(CredentialsError::KubeError { source }) => return Err(source.into()), + }; + + Ok(credentials) +} + async fn list_stackable_stacklets( kube_client: &KubeClient, namespace: Option<&str>, @@ -82,11 +115,7 @@ async fn list_stackable_stacklets( let mut stacklets = Vec::new(); for (product_name, product_gvk) in product_list { - let objects = match kube_client - .list_objects(&product_gvk, namespace) - .await - .context(KubeSnafu)? - { + let objects = match kube_client.list_objects(&product_gvk, namespace).await? { Some(obj) => obj, None => { info!( @@ -112,9 +141,10 @@ async fn list_stackable_stacklets( None => continue, }; - let endpoints = get_service_endpoints(product_name, &object_name, &object_namespace) - .await - .context(ServiceSnafu)?; + let endpoints = + get_service_endpoints(kube_client, product_name, &object_name, &object_namespace) + .await + .context(ServiceSnafu)?; stacklets.push(Stacklet { namespace: Some(object_namespace), @@ -146,15 +176,17 @@ fn build_products_gvk_list<'a>(product_names: &[&'a str]) -> IndexMap<&'a str, G continue; } - map.insert( - *product_name, - GroupVersionKind { - group: format!("{product_name}.stackable.tech"), - version: "v1alpha1".into(), - kind: format!("{}Cluster", product_name.capitalize()), - }, - ); + map.insert(*product_name, gvk_from_product_name(product_name)); } map } + +// FIXME: Support SparkApplication +fn gvk_from_product_name(product_name: &str) -> GroupVersionKind { + GroupVersionKind { + group: format!("{product_name}.stackable.tech"), + version: "v1alpha1".into(), + kind: format!("{}Cluster", product_name.capitalize()), + } +} diff --git a/rust/stackable-cockpit/src/platform/stacklet/opensearch.rs b/rust/stackable-cockpit/src/platform/stacklet/opensearch.rs index f99ba91e..24c56697 100644 --- a/rust/stackable-cockpit/src/platform/stacklet/opensearch.rs +++ b/rust/stackable-cockpit/src/platform/stacklet/opensearch.rs @@ -1,10 +1,9 @@ use kube::{api::ListParams, ResourceExt}; -use snafu::ResultExt; use crate::{ platform::{ service::get_service_endpoint_urls, - stacklet::{KubeSnafu, Stacklet, StackletError}, + stacklet::{Stacklet, StackletError}, }, utils::k8s::{KubeClient, ListParamsExt, ProductLabel}, }; @@ -16,10 +15,7 @@ pub(super) async fn list( let mut stacklets = Vec::new(); let params = ListParams::from_product("opensearch-dashboards", None, ProductLabel::Name); - let services = kube_client - .list_services(namespace, ¶ms) - .await - .context(KubeSnafu)?; + let services = kube_client.list_services(namespace, ¶ms).await?; for service in services { let service_name = service.name_any(); diff --git a/rust/stackable-cockpit/src/platform/stacklet/prometheus.rs b/rust/stackable-cockpit/src/platform/stacklet/prometheus.rs index 07f81de8..3c09f812 100644 --- a/rust/stackable-cockpit/src/platform/stacklet/prometheus.rs +++ b/rust/stackable-cockpit/src/platform/stacklet/prometheus.rs @@ -1,10 +1,9 @@ use kube::{api::ListParams, ResourceExt}; -use snafu::ResultExt; use crate::{ platform::{ service::get_service_endpoint_urls, - stacklet::{KubeSnafu, Stacklet, StackletError}, + stacklet::{Stacklet, StackletError}, }, utils::k8s::KubeClient, }; @@ -17,10 +16,7 @@ pub(super) async fn list( // The helm-chart uses `app` instead of `app.kubernetes.io/app`, so we can't use `ListParams::from_product` here let params = ListParams::default().labels("app=kube-prometheus-stack-prometheus"); - let services = kube_client - .list_services(namespace, ¶ms) - .await - .context(KubeSnafu)?; + let services = kube_client.list_services(namespace, ¶ms).await?; for service in services { let service_name = service.name_any(); diff --git a/rust/stackable-cockpit/src/utils/k8s/byte_string.rs b/rust/stackable-cockpit/src/utils/k8s/byte_string.rs new file mode 100644 index 00000000..f7d77d01 --- /dev/null +++ b/rust/stackable-cockpit/src/utils/k8s/byte_string.rs @@ -0,0 +1,21 @@ +use std::{error::Error, fmt::Display, string::FromUtf8Error}; + +use k8s_openapi::ByteString; + +/// The [`ByteStringExt`] enables [`ByteString`] to be converted to a [`String`]. +pub trait ByteStringExt { + type Error: Error + Display; + + fn try_to_string(&self) -> Result; +} + +impl ByteStringExt for ByteString { + type Error = FromUtf8Error; + + fn try_to_string(&self) -> Result { + // NOTE (Techassi): This extension can possible be moved to `k8s-openapi`. + // First we need to make sure the [`ByteString`] data is UTF-8 data, and + // UTF-8 data only. + String::from_utf8(self.0.to_owned()) + } +} diff --git a/rust/stackable-cockpit/src/utils/k8s/client.rs b/rust/stackable-cockpit/src/utils/k8s/client.rs index 31958ccf..8ba83ab7 100644 --- a/rust/stackable-cockpit/src/utils/k8s/client.rs +++ b/rust/stackable-cockpit/src/utils/k8s/client.rs @@ -11,11 +11,14 @@ use kube::{ Api, Client, Discovery, ResourceExt, }; use serde::Deserialize; -use snafu::{ResultExt, Snafu}; +use snafu::{OptionExt, ResultExt, Snafu}; use crate::{ - constants::REDACTED_PASSWORD, - platform::cluster::{ClusterError, ClusterInfo}, + platform::{ + cluster::{ClusterError, ClusterInfo}, + credentials::Credentials, + }, + utils::k8s::ByteStringExt, }; #[cfg(doc)] @@ -38,9 +41,6 @@ pub enum KubeClientError { #[snafu(display("failed to deploy manifest because GVK {gvk:?} cannot be resolved"))] DiscoveryError { gvk: GroupVersionKind }, - #[snafu(display("invalid secret data (empty)"))] - InvalidSecretData, - #[snafu(display("failed to convert byte string into UTF-8 string"))] ByteStringConvertError { source: FromUtf8Error }, @@ -49,6 +49,15 @@ pub enum KubeClientError { #[snafu(display("failed to retrieve cluster information"))] ClusterError { source: ClusterError }, + + #[snafu(display("invalid secret data (empty)"))] + InvalidSecretData, + + #[snafu(display("no username key in credentials secret"))] + NoUsernameKey, + + #[snafu(display("no password key in credentials secret"))] + NoPasswordKey, } pub struct KubeClient { @@ -141,6 +150,23 @@ impl KubeClient { Ok(Some(objects)) } + pub async fn get_namespaced_object( + &self, + namespace: &str, + object_name: &str, + gvk: &GroupVersionKind, + ) -> Result, KubeClientError> { + let object_api_resource = match self.discovery.resolve_gvk(gvk) { + Some((object_api_resource, _)) => object_api_resource, + None => { + return Ok(None); + } + }; + + let api = Api::namespaced_with(self.client.clone(), namespace, &object_api_resource); + Ok(Some(api.get(object_name).await.context(KubeSnafu)?)) + } + /// Lists [`Service`]s by matching labels. The services can be matched by /// the product labels. [`ListParamsExt`] provides a utility function to /// create [`ListParams`] based on a product name and optional instance @@ -168,31 +194,26 @@ impl KubeClient { secret_name: &str, secret_namespace: &str, username_key: &str, - password_key: Option<&str>, - ) -> Result> { + password_key: &str, + ) -> Result { let secret_api: Api = Api::namespaced(self.client.clone(), secret_namespace); let secret = secret_api.get(secret_name).await.context(KubeSnafu)?; - let secret_data = secret.data.ok_or(InvalidSecretDataSnafu {}.build())?; + let secret_data = secret.data.context(InvalidSecretDataSnafu)?; - let username = match secret_data.get(username_key) { - Some(username) => { - String::from_utf8(username.0.clone()).context(ByteStringConvertSnafu)? - } - None => return Ok(None), - }; + let username = secret_data + .get(username_key) + .context(NoUsernameKeySnafu)? + .try_to_string() + .context(ByteStringConvertSnafu)?; - let password = match password_key { - Some(key) => match secret_data.get(key) { - Some(password) => { - String::from_utf8(password.0.clone()).context(ByteStringConvertSnafu)? - } - None => return Ok(None), - }, - None => REDACTED_PASSWORD.to_string(), - }; + let password = secret_data + .get(password_key) + .context(NoPasswordKeySnafu)? + .try_to_string() + .context(ByteStringConvertSnafu)?; - Ok(Some((username, password))) + Ok(Credentials { username, password }) } /// Lists [`Deployment`]s by matching labels. The services can be matched diff --git a/rust/stackable-cockpit/src/utils/k8s/mod.rs b/rust/stackable-cockpit/src/utils/k8s/mod.rs index 7e1ff1fe..7ace0220 100644 --- a/rust/stackable-cockpit/src/utils/k8s/mod.rs +++ b/rust/stackable-cockpit/src/utils/k8s/mod.rs @@ -1,7 +1,9 @@ +mod byte_string; mod client; mod conditions; mod labels; +pub use byte_string::*; pub use client::*; pub use conditions::*; pub use labels::*; diff --git a/rust/stackable-cockpitd/src/handlers/stacklets.rs b/rust/stackable-cockpitd/src/handlers/stacklets.rs index 3c22be5b..c5b8bcc3 100644 --- a/rust/stackable-cockpitd/src/handlers/stacklets.rs +++ b/rust/stackable-cockpitd/src/handlers/stacklets.rs @@ -13,5 +13,5 @@ pub fn router() -> Router { (status = 200, body = Vec), ))] pub async fn get_stacklets() -> Json> { - Json(platform::stacklet::list(None).await.unwrap()) + Json(platform::stacklet::list_stacklets(None).await.unwrap()) } diff --git a/rust/stackablectl/README.md b/rust/stackablectl/README.md index 5a815734..54e6de31 100644 --- a/rust/stackablectl/README.md +++ b/rust/stackablectl/README.md @@ -18,7 +18,7 @@ Commands: operator Interact with single operator instead of the full platform release Interact with all operators of the platform which are released together stack Interact with stacks, which are ready-to-use product combinations - stacklets Interact with deployed stacklets, which are bundles of resources and containers required to run the product + stacklet Interact with deployed stacklets, which are bundles of resources and containers required to run the product demo Interact with demos, which are end-to-end usage demonstrations of the Stackable data platform completions Generate shell completions for this tool cache Interact with locally cached files diff --git a/rust/stackablectl/src/cli/mod.rs b/rust/stackablectl/src/cli/mod.rs index 040110ca..17354b04 100644 --- a/rust/stackablectl/src/cli/mod.rs +++ b/rust/stackablectl/src/cli/mod.rs @@ -17,7 +17,7 @@ use stackable_cockpit::{ use crate::{ args::{CommonFileArgs, CommonRepoArgs}, - cmds::{cache, completions, demo, operator, release, stack, stacklets}, + cmds::{cache, completions, demo, operator, release, stack, stacklet}, constants::{ ENV_KEY_DEMO_FILES, ENV_KEY_RELEASE_FILES, ENV_KEY_STACK_FILES, REMOTE_DEMO_FILE, REMOTE_RELEASE_FILE, REMOTE_STACK_FILE, USER_DIR_APPLICATION_NAME, @@ -37,7 +37,7 @@ pub enum Error { Stack { source: stack::CmdError }, #[snafu(display("stacklets command error"))] - Stacklets { source: stacklets::CmdError }, + Stacklet { source: stacklet::CmdError }, #[snafu(display("demo command error"))] Demo { source: demo::CmdError }, @@ -186,7 +186,7 @@ impl Cli { Commands::Operator(args) => args.run(self).await.context(OperatorSnafu), Commands::Release(args) => args.run(self, cache).await.context(ReleaseSnafu), Commands::Stack(args) => args.run(self, cache).await.context(StackSnafu), - Commands::Stacklets(args) => args.run(self).await.context(StackletsSnafu), + Commands::Stacklet(args) => args.run(self).await.context(StackletSnafu), Commands::Demo(args) => args.run(self, cache).await.context(DemoSnafu), Commands::Completions(args) => args.run().context(CompletionsSnafu), Commands::Cache(args) => args.run(cache).await.context(CacheSnafu), @@ -210,7 +210,7 @@ pub enum Commands { /// Interact with deployed stacklets, which are bundles of resources and /// containers required to run the product. - #[command(aliases(["stacklet", "stl", "sl"]))] + #[command(aliases(["stl", "sl"]))] #[command( long_about = "Interact with deployed stacklets, which are bundles of resources and containers required to run the product. @@ -219,7 +219,7 @@ Each stacklet consists of init containers, app containers, sidecar containers and additional Kubernetes resources like StatefulSets, ConfigMaps, Services and CRDs." )] - Stacklets(stacklets::StackletsArgs), + Stacklet(stacklet::StackletArgs), /// Interact with demos, which are end-to-end usage demonstrations of the Stackable data platform Demo(demo::DemoArgs), diff --git a/rust/stackablectl/src/cmds/mod.rs b/rust/stackablectl/src/cmds/mod.rs index 720a6714..a19d1112 100644 --- a/rust/stackablectl/src/cmds/mod.rs +++ b/rust/stackablectl/src/cmds/mod.rs @@ -4,4 +4,4 @@ pub mod demo; pub mod operator; pub mod release; pub mod stack; -pub mod stacklets; +pub mod stacklet; diff --git a/rust/stackablectl/src/cmds/stacklets.rs b/rust/stackablectl/src/cmds/stacklet.rs similarity index 71% rename from rust/stackablectl/src/cmds/stacklets.rs rename to rust/stackablectl/src/cmds/stacklet.rs index f33abe19..a5768237 100644 --- a/rust/stackablectl/src/cmds/stacklets.rs +++ b/rust/stackablectl/src/cmds/stacklet.rs @@ -1,11 +1,15 @@ use clap::{Args, Subcommand}; -use comfy_table::{presets::UTF8_FULL, ContentArrangement, Table}; +use comfy_table::{ + presets::{NOTHING, UTF8_FULL}, + ContentArrangement, Table, +}; use nu_ansi_term::Color::{Green, Red}; use snafu::{ResultExt, Snafu}; use tracing::{info, instrument}; use stackable_cockpit::{ - platform::stacklet::{list, StackletError}, + constants::DEFAULT_PRODUCT_NAMESPACE, + platform::stacklet::{get_credentials_for_product, list_stacklets, StackletError}, utils::k8s::DisplayCondition, }; @@ -15,19 +19,45 @@ use crate::{ utils::use_colored_output, }; +const CREDENTIALS_HINT: &str = "Use \"stackablectl stacklet credentials [OPTIONS] \" to display credentials for deployed stacklets."; + #[derive(Debug, Args)] -pub struct StackletsArgs { +pub struct StackletArgs { #[command(subcommand)] subcommand: StackletCommands, } #[derive(Debug, Subcommand)] pub enum StackletCommands { - /// List deployed services + /// Display credentials for a stacklet + #[command(aliases(["creds", "cr"]))] + Credentials(StackletCredentialsArgs), + + /// List deployed stacklets #[command(alias("ls"))] List(StackletListArgs), } +#[derive(Debug, Args)] +pub struct StackletCredentialsArgs { + /// The name of the product, for example 'superset'. + product_name: String, + + /// The name of the stacklet, for example 'superset'. + stacklet_name: String, + + /// Namespace in the cluster used to deploy the products. + #[arg( + long, + short = 'n', + global = true, + default_value = DEFAULT_PRODUCT_NAMESPACE, + visible_aliases(["product-ns"]), + long_help = "Namespace in the cluster used to deploy the products. Use this to select +a different namespace for credential lookup.")] + pub product_namespace: String, +} + #[derive(Debug, Args)] pub struct StackletListArgs { /// Controls if the output will use color. This only applies to the output @@ -35,7 +65,7 @@ pub struct StackletListArgs { #[arg(short = 'c', long = "color")] use_color: bool, - #[arg(short, long = "output", value_enum, default_value_t = Default::default())] + #[arg(short, long = "output", value_enum, default_value_t)] output_type: OutputType, #[command(flatten)] @@ -44,9 +74,12 @@ pub struct StackletListArgs { #[derive(Debug, Snafu)] pub enum CmdError { - #[snafu(display("service list error"))] + #[snafu(display("failed to list stacklets"))] StackletListError { source: StackletError }, + #[snafu(display("failed to retrieve credentials for stacklet"))] + StackletCredentialsError { source: StackletError }, + #[snafu(display("unable to format yaml output"))] YamlOutputFormatError { source: serde_yaml::Error }, @@ -54,10 +87,11 @@ pub enum CmdError { JsonOutputFormatError { source: serde_json::Error }, } -impl StackletsArgs { +impl StackletArgs { pub async fn run(&self, common_args: &Cli) -> Result { match &self.subcommand { StackletCommands::List(args) => list_cmd(args, common_args).await, + StackletCommands::Credentials(args) => credentials_cmd(args).await, } } } @@ -69,7 +103,7 @@ async fn list_cmd(args: &StackletListArgs, common_args: &Cli) -> Result Result Result Result { + info!("Displaying stacklet credentials"); + + match get_credentials_for_product( + &args.product_namespace, + &args.stacklet_name, + &args.product_name, + ) + .await + .context(StackletCredentialsSnafu)? + { + Some(credentials) => { + let mut table = Table::new(); + + table + .set_content_arrangement(ContentArrangement::Dynamic) + .load_preset(NOTHING) + .add_row(vec!["USERNAME", &credentials.username]) + .add_row(vec!["PASSWORD", &credentials.password]); + + let output = format!( + "Credentials for {} ({}) in namespace '{}':", + args.product_name, args.stacklet_name, args.product_namespace + ); + + Ok(format!("{}\n\n{}", output, table)) + } + None => Ok("No credentials".into()), + } +} + pub struct ConditionOutput { summary: String, errors: Vec,