diff --git a/docs/modules/stackablectl/pages/commands/release.adoc b/docs/modules/stackablectl/pages/commands/release.adoc index fb3c750e..80059440 100644 --- a/docs/modules/stackablectl/pages/commands/release.adoc +++ b/docs/modules/stackablectl/pages/commands/release.adoc @@ -14,25 +14,35 @@ To list the available Stackable releases run the following command: [source,console] ---- $ stackablectl release list -┌───┬─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────┐ -│ # ┆ RELEASE ┆ RELEASE DATE ┆ DESCRIPTION │ -╞═══╪═════════╪══════════════╪═════════════════════════════════════════════════════════════════════════════╡ -│ 1 ┆ 23.7 ┆ 2023-07-26 ┆ Sixth release focusing on resources and pod overrides │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 2 ┆ 23.4 ┆ 2023-05-17 ┆ Fifth release focusing on affinities and product status │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 3 ┆ 23.1 ┆ 2023-01-27 ┆ Fourth release focusing on image selection and logging │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 4 ┆ 22.11 ┆ 2022-11-14 ┆ Third release focusing on resource management │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 5 ┆ 22.09 ┆ 2022-09-09 ┆ Second release focusing on security and OpenShift support │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 6 ┆ 22.06 ┆ 2022-06-30 ┆ First official release of the Stackable Data Platform │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 7 ┆ latest ┆ 2023-07-26 ┆ Always pointing to the latest stable version of the Stackable Data Platform │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 8 ┆ dev ┆ 2023-01-27 ┆ Development versions from main branch. Not stable! │ -└───┴─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────┘ +┌────┬─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────┐ +│ # ┆ RELEASE ┆ RELEASE DATE ┆ DESCRIPTION │ +╞════╪═════════╪══════════════╪═════════════════════════════════════════════════════════════════════════════════╡ +│ 1 ┆ 25.3 ┆ 2025-03-24 ┆ The March 2025 release │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 2 ┆ 24.11 ┆ 2024-11-18 ┆ The November 2024 release │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 3 ┆ 24.7 ┆ 2024-07-24 ┆ Security focused release to reduce CVEs and to build product images from source │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 4 ┆ 24.3 ┆ 2024-03-20 ┆ Security focused release to improve platform authentication and authorization │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 5 ┆ 23.11 ┆ 2023-11-30 ┆ Seventh release focusing on PodDisruptionBudgets and graceful shutdown │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 6 ┆ 23.7 ┆ 2023-07-26 ┆ Sixth release focusing on resources and pod overrides │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 7 ┆ 23.4 ┆ 2023-05-17 ┆ Fifth release focusing on affinities and product status │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 8 ┆ 23.1 ┆ 2023-01-27 ┆ Fourth release focusing on image selection and logging │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 9 ┆ 22.11 ┆ 2022-11-14 ┆ Third release focusing on resource management │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 10 ┆ 22.09 ┆ 2022-09-09 ┆ Second release focusing on security and OpenShift support │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 11 ┆ 22.06 ┆ 2022-06-30 ┆ First official release of the Stackable Data Platform │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 12 ┆ latest ┆ 2025-03-24 ┆ Always pointing to the latest stable version of the Stackable Data Platform │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 13 ┆ dev ┆ 1970-01-01 ┆ Development versions from main branch. Not stable! │ +└────┴─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────┘ ---- Detailed information of a release can be queried with the `stackablectl release describe` command: @@ -128,3 +138,35 @@ Installed product trino=23.7.0 Installed product zookeeper=23.7.0 Installed release 23.7 ---- + +== Upgrading Releases + +As described in the xref:home::release-notes.adoc[Upgrade sections of the Release Notes], the upgrade process can be achieved by the following three steps: + +. Uninstalling the previous release with `stackablectl release uninstall ` +. Replacing the CRDs with `kubectl replace` +. Installing the next release with `stackablectl release install ` + +For convenience `stackablectl` also provides an upgrade functionality which executes those steps by itself. +To upgrade a release, run the following command: + +[source,console] +---- +$ stackablectl release upgrade 25.3 + +Upgraded to release '25.3' + +Use "stackablectl operator installed" to list installed operators. +---- + +The above command only upgrades the currently installed operators. +To include additional operators in the installation step, use the `--include`/`-i` subcommands and specify the desired operators. + +For example +[source,console] +---- +$ stackablectl release upgrade 25.3 -i druid -i nifi +---- +would upgrade the existing operators as well as install the Stackable operators for Apache Druid and Apache NiFi. + +Likewise, operators can be exluded from the upgrade using the `--exclude`/`-e` subcommands. diff --git a/docs/modules/stackablectl/partials/commands/release.adoc b/docs/modules/stackablectl/partials/commands/release.adoc index a2030bd4..eac12e67 100644 --- a/docs/modules/stackablectl/partials/commands/release.adoc +++ b/docs/modules/stackablectl/partials/commands/release.adoc @@ -10,6 +10,7 @@ Commands: describe Print out detailed release information install Install a specific release uninstall Uninstall a release + upgrade Upgrade a release help Print this message or the help of the given subcommand(s) Options: diff --git a/extra/completions/_stackablectl b/extra/completions/_stackablectl index 25c69764..768a339a 100644 --- a/extra/completions/_stackablectl +++ b/extra/completions/_stackablectl @@ -418,6 +418,35 @@ repo\:"index.yaml-based repositories\: resolution (dev, test, stable) is based o ':RELEASE -- Name of the release to uninstall:' \ && ret=0 ;; +(upgrade) +_arguments "${_arguments_options[@]}" : \ +'*-i+[List of product operators to upgrade]:INCLUDED_PRODUCTS: ' \ +'*--include=[List of product operators to upgrade]:INCLUDED_PRODUCTS: ' \ +'*-e+[Blacklist of product operators to install]:EXCLUDED_PRODUCTS: ' \ +'*--exclude=[Blacklist of product operators to install]:EXCLUDED_PRODUCTS: ' \ +'--operator-namespace=[Namespace in the cluster used to deploy the operators]:OPERATOR_NAMESPACE: ' \ +'--operator-ns=[Namespace in the cluster used to deploy the operators]:OPERATOR_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' \ +'--chart-source=[Source the charts from either a OCI registry or from index.yaml-based repositories]:CHART_SOURCE:((oci\:"OCI registry" +repo\:"index.yaml-based repositories\: resolution (dev, test, stable) is based on the version and thus will be operator-specific"))' \ +'--no-cache[Do not cache the remote (default) demo, stack and release files]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +'-V[Print version]' \ +'--version[Print version]' \ +':RELEASE -- Upgrade to the specified release:' \ +&& ret=0 +;; (help) _arguments "${_arguments_options[@]}" : \ ":: :_stackablectl__release__help_commands" \ @@ -446,6 +475,10 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ && ret=0 ;; +(upgrade) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; (help) _arguments "${_arguments_options[@]}" : \ && ret=0 @@ -1315,6 +1348,10 @@ _arguments "${_arguments_options[@]}" : \ (uninstall) _arguments "${_arguments_options[@]}" : \ && ret=0 +;; +(upgrade) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 ;; esac ;; @@ -1820,6 +1857,7 @@ _stackablectl__help__release_commands() { 'describe:Print out detailed release information' \ 'install:Install a specific release' \ 'uninstall:Uninstall a release' \ +'upgrade:Upgrade a release' \ ) _describe -t commands 'stackablectl help release commands' commands "$@" } @@ -1843,6 +1881,11 @@ _stackablectl__help__release__uninstall_commands() { local commands; commands=() _describe -t commands 'stackablectl help release uninstall commands' commands "$@" } +(( $+functions[_stackablectl__help__release__upgrade_commands] )) || +_stackablectl__help__release__upgrade_commands() { + local commands; commands=() + _describe -t commands 'stackablectl help release upgrade commands' commands "$@" +} (( $+functions[_stackablectl__help__stack_commands] )) || _stackablectl__help__stack_commands() { local commands; commands=( @@ -1971,6 +2014,7 @@ _stackablectl__release_commands() { 'describe:Print out detailed release information' \ 'install:Install a specific release' \ 'uninstall:Uninstall a release' \ +'upgrade:Upgrade a release' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'stackablectl release commands' commands "$@" @@ -1987,6 +2031,7 @@ _stackablectl__release__help_commands() { 'describe:Print out detailed release information' \ 'install:Install a specific release' \ 'uninstall:Uninstall a release' \ +'upgrade:Upgrade a release' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'stackablectl release help commands' commands "$@" @@ -2016,6 +2061,11 @@ _stackablectl__release__help__uninstall_commands() { local commands; commands=() _describe -t commands 'stackablectl release help uninstall commands' commands "$@" } +(( $+functions[_stackablectl__release__help__upgrade_commands] )) || +_stackablectl__release__help__upgrade_commands() { + local commands; commands=() + _describe -t commands 'stackablectl release help upgrade commands' commands "$@" +} (( $+functions[_stackablectl__release__install_commands] )) || _stackablectl__release__install_commands() { local commands; commands=() @@ -2031,6 +2081,11 @@ _stackablectl__release__uninstall_commands() { local commands; commands=() _describe -t commands 'stackablectl release uninstall commands' commands "$@" } +(( $+functions[_stackablectl__release__upgrade_commands] )) || +_stackablectl__release__upgrade_commands() { + local commands; commands=() + _describe -t commands 'stackablectl release upgrade commands' commands "$@" +} (( $+functions[_stackablectl__stack_commands] )) || _stackablectl__stack_commands() { local commands; commands=( diff --git a/extra/completions/stackablectl.bash b/extra/completions/stackablectl.bash index 095ed06b..bd247a0c 100644 --- a/extra/completions/stackablectl.bash +++ b/extra/completions/stackablectl.bash @@ -201,6 +201,9 @@ _stackablectl() { stackablectl__help__release,uninstall) cmd="stackablectl__help__release__uninstall" ;; + stackablectl__help__release,upgrade) + cmd="stackablectl__help__release__upgrade" + ;; stackablectl__help__stack,describe) cmd="stackablectl__help__stack__describe" ;; @@ -267,6 +270,9 @@ _stackablectl() { stackablectl__release,uninstall) cmd="stackablectl__release__uninstall" ;; + stackablectl__release,upgrade) + cmd="stackablectl__release__upgrade" + ;; stackablectl__release__help,describe) cmd="stackablectl__release__help__describe" ;; @@ -282,6 +288,9 @@ _stackablectl() { stackablectl__release__help,uninstall) cmd="stackablectl__release__help__uninstall" ;; + stackablectl__release__help,upgrade) + cmd="stackablectl__release__help__upgrade" + ;; stackablectl__stack,describe) cmd="stackablectl__stack__describe" ;; @@ -2883,7 +2892,7 @@ _stackablectl() { return 0 ;; stackablectl__help__release) - opts="list describe install uninstall" + opts="list describe install uninstall upgrade" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2952,6 +2961,20 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + stackablectl__help__release__upgrade) + 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__stack) opts="list describe install" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -3985,7 +4008,7 @@ _stackablectl() { return 0 ;; stackablectl__release) - opts="-l -d -s -r -h -V --log-level --no-cache --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --chart-source --help --version list describe install uninstall help" + opts="-l -d -s -r -h -V --log-level --no-cache --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --chart-source --help --version list describe install uninstall upgrade help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4249,7 +4272,7 @@ _stackablectl() { return 0 ;; stackablectl__release__help) - opts="list describe install uninstall help" + opts="list describe install uninstall upgrade help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4332,6 +4355,20 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + stackablectl__release__help__upgrade) + 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__release__install) opts="-i -e -c -l -d -s -r -h -V --include --exclude --operator-ns --operator-namespace --cluster --cluster-name --cluster-nodes --cluster-cp-nodes --log-level --no-cache --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --chart-source --help --version " if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -4776,6 +4813,158 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + stackablectl__release__upgrade) + opts="-i -e -l -d -s -r -h -V --include --exclude --operator-ns --operator-namespace --log-level --no-cache --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --chart-source --help --version " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --include) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -i) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -e) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --operator-namespace) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --operator-ns) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --log-level) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -l) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --demo-file) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + -d) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --stack-file) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + -s) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --release-file) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + -r) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + 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 + ;; + --chart-source) + COMPREPLY=($(compgen -W "oci repo" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; stackablectl__stack) opts="-l -d -s -r -h -V --release --log-level --no-cache --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --chart-source --help --version list describe install help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then diff --git a/extra/completions/stackablectl.elv b/extra/completions/stackablectl.elv index f943defe..2e55ca7d 100644 --- a/extra/completions/stackablectl.elv +++ b/extra/completions/stackablectl.elv @@ -224,6 +224,7 @@ set edit:completion:arg-completer[stackablectl] = {|@words| cand describe 'Print out detailed release information' cand install 'Install a specific release' cand uninstall 'Uninstall a release' + cand upgrade 'Upgrade a release' cand help 'Print this message or the help of the given subcommand(s)' } &'stackablectl;release;list'= { @@ -319,11 +320,37 @@ set edit:completion:arg-completer[stackablectl] = {|@words| cand -V 'Print version' cand --version 'Print version' } + &'stackablectl;release;upgrade'= { + cand -i 'List of product operators to upgrade' + cand --include 'List of product operators to upgrade' + cand -e 'Blacklist of product operators to install' + cand --exclude 'Blacklist of product operators to install' + cand --operator-namespace 'Namespace in the cluster used to deploy the operators' + cand --operator-ns 'Namespace in the cluster used to deploy the operators' + cand -l 'Log level this application uses' + cand --log-level 'Log level this application uses' + cand -d 'Provide one or more additional (custom) demo file(s)' + cand --demo-file 'Provide one or more additional (custom) demo file(s)' + cand -s 'Provide one or more additional (custom) stack file(s)' + cand --stack-file 'Provide one or more additional (custom) stack file(s)' + cand -r 'Provide one or more additional (custom) release file(s)' + cand --release-file 'Provide one or more additional (custom) release file(s)' + cand --helm-repo-stable 'Provide a custom Helm stable repository URL' + cand --helm-repo-test 'Provide a custom Helm test repository URL' + cand --helm-repo-dev 'Provide a custom Helm dev repository URL' + cand --chart-source 'Source the charts from either a OCI registry or from index.yaml-based repositories' + cand --no-cache 'Do not cache the remote (default) demo, stack and release files' + cand -h 'Print help (see more with ''--help'')' + cand --help 'Print help (see more with ''--help'')' + cand -V 'Print version' + cand --version 'Print version' + } &'stackablectl;release;help'= { cand list 'List available releases' cand describe 'Print out detailed release information' cand install 'Install a specific release' cand uninstall 'Uninstall a release' + cand upgrade 'Upgrade a release' cand help 'Print this message or the help of the given subcommand(s)' } &'stackablectl;release;help;list'= { @@ -334,6 +361,8 @@ set edit:completion:arg-completer[stackablectl] = {|@words| } &'stackablectl;release;help;uninstall'= { } + &'stackablectl;release;help;upgrade'= { + } &'stackablectl;release;help;help'= { } &'stackablectl;stack'= { @@ -916,6 +945,7 @@ set edit:completion:arg-completer[stackablectl] = {|@words| cand describe 'Print out detailed release information' cand install 'Install a specific release' cand uninstall 'Uninstall a release' + cand upgrade 'Upgrade a release' } &'stackablectl;help;release;list'= { } @@ -925,6 +955,8 @@ set edit:completion:arg-completer[stackablectl] = {|@words| } &'stackablectl;help;release;uninstall'= { } + &'stackablectl;help;release;upgrade'= { + } &'stackablectl;help;stack'= { cand list 'List available stacks' cand describe 'Describe a specific stack' diff --git a/extra/completions/stackablectl.fish b/extra/completions/stackablectl.fish index 7fe840ee..28cb3fa0 100644 --- a/extra/completions/stackablectl.fish +++ b/extra/completions/stackablectl.fish @@ -132,22 +132,23 @@ complete -c stackablectl -n "__fish_stackablectl_using_subcommand operator; and complete -c stackablectl -n "__fish_stackablectl_using_subcommand operator; and __fish_seen_subcommand_from help" -f -a "uninstall" -d 'Uninstall one or more operators' complete -c stackablectl -n "__fish_stackablectl_using_subcommand operator; and __fish_seen_subcommand_from help" -f -a "installed" -d 'List installed operators' complete -c stackablectl -n "__fish_stackablectl_using_subcommand operator; and __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_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s l -l log-level -d 'Log level this application uses' -r -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s r -l release-file -d 'Provide one or more additional (custom) release file(s)' -r -F -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -l helm-repo-stable -d 'Provide a custom Helm stable repository URL' -r -f -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -l helm-repo-test -d 'Provide a custom Helm test repository URL' -r -f -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -l helm-repo-dev -d 'Provide a custom Helm dev repository URL' -r -f -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -l chart-source -d 'Source the charts from either a OCI registry or from index.yaml-based repositories' -r -f -a "{oci\t'OCI registry',repo\t'index.yaml-based repositories: resolution (dev, test, stable) is based on the version and thus will be operator-specific'}" -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s V -l version -d 'Print version' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -f -a "list" -d 'List available releases' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -f -a "describe" -d 'Print out detailed release information' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -f -a "install" -d 'Install a specific release' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -f -a "uninstall" -d 'Uninstall a release' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s l -l log-level -d 'Log level this application uses' -r +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s r -l release-file -d 'Provide one or more additional (custom) release file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -l helm-repo-stable -d 'Provide a custom Helm stable repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -l helm-repo-test -d 'Provide a custom Helm test repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -l helm-repo-dev -d 'Provide a custom Helm dev repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -l chart-source -d 'Source the charts from either a OCI registry or from index.yaml-based repositories' -r -f -a "{oci\t'OCI registry',repo\t'index.yaml-based repositories: resolution (dev, test, stable) is based on the version and thus will be operator-specific'}" +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s V -l version -d 'Print version' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "list" -d 'List available releases' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "describe" -d 'Print out detailed release information' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "install" -d 'Install a specific release' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "uninstall" -d 'Uninstall a release' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "upgrade" -d 'Upgrade a release' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from list" -s o -l output -r -f -a "{plain\t'Print output formatted as plain text',table\t'Print output formatted as a table',json\t'Print output formatted as JSON',yaml\t'Print output formatted as YAML'}" complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from list" -s l -l log-level -d 'Log level this application uses' -r complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from list" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F @@ -202,10 +203,25 @@ complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and _ complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from uninstall" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from uninstall" -s h -l help -d 'Print help (see more with \'--help\')' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from uninstall" -s V -l version -d 'Print version' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s i -l include -d 'List of product operators to upgrade' -r +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s e -l exclude -d 'Blacklist of product operators to install' -r +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l operator-namespace -l operator-ns -d 'Namespace in the cluster used to deploy the operators' -r +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s l -l log-level -d 'Log level this application uses' -r +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s r -l release-file -d 'Provide one or more additional (custom) release file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l helm-repo-stable -d 'Provide a custom Helm stable repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l helm-repo-test -d 'Provide a custom Helm test repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l helm-repo-dev -d 'Provide a custom Helm dev repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l chart-source -d 'Source the charts from either a OCI registry or from index.yaml-based repositories' -r -f -a "{oci\t'OCI registry',repo\t'index.yaml-based repositories: resolution (dev, test, stable) is based on the version and thus will be operator-specific'}" +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s V -l version -d 'Print version' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from help" -f -a "list" -d 'List available releases' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from help" -f -a "describe" -d 'Print out detailed release information' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from help" -f -a "install" -d 'Install a specific release' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from help" -f -a "uninstall" -d 'Uninstall a release' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from help" -f -a "upgrade" -d 'Upgrade a release' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __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_stackablectl_using_subcommand stack; and not __fish_seen_subcommand_from list describe install help" -l release -d 'Target a specific Stackable release' -r complete -c stackablectl -n "__fish_stackablectl_using_subcommand stack; and not __fish_seen_subcommand_from list describe install help" -s l -l log-level -d 'Log level this application uses' -r @@ -534,6 +550,7 @@ complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fi complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from release" -f -a "describe" -d 'Print out detailed release information' complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from release" -f -a "install" -d 'Install a specific release' complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from release" -f -a "uninstall" -d 'Uninstall a release' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from release" -f -a "upgrade" -d 'Upgrade a release' complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from stack" -f -a "list" -d 'List available stacks' complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from stack" -f -a "describe" -d 'Describe a specific stack' complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from stack" -f -a "install" -d 'Install a specific stack' diff --git a/extra/completions/stackablectl.nu b/extra/completions/stackablectl.nu index 448f1f63..6d899b1b 100644 --- a/extra/completions/stackablectl.nu +++ b/extra/completions/stackablectl.nu @@ -315,6 +315,30 @@ module completions { --version(-V) # Print version ] + def "nu-complete stackablectl release upgrade chart_source" [] { + [ "oci" "repo" ] + } + + # Upgrade a release + export extern "stackablectl release upgrade" [ + RELEASE: string # Upgrade to the specified release + --include(-i): string # List of product operators to upgrade + --exclude(-e): string # Blacklist of product operators to install + --operator-namespace: string # Namespace in the cluster used to deploy the operators + --operator-ns: string # Namespace in the cluster used to deploy the operators + --log-level(-l): string # Log level this application uses + --no-cache # Do not cache the remote (default) demo, stack and release files + --demo-file(-d): string # Provide one or more additional (custom) demo file(s) + --stack-file(-s): string # Provide one or more additional (custom) stack file(s) + --release-file(-r): string # Provide one or more additional (custom) release file(s) + --helm-repo-stable: string # Provide a custom Helm stable repository URL + --helm-repo-test: string # Provide a custom Helm test repository URL + --helm-repo-dev: string # Provide a custom Helm dev repository URL + --chart-source: string@"nu-complete stackablectl release upgrade chart_source" # Source the charts from either a OCI registry or from index.yaml-based repositories + --help(-h) # Print help (see more with '--help') + --version(-V) # Print version + ] + # Print this message or the help of the given subcommand(s) export extern "stackablectl release help" [ ] @@ -335,6 +359,10 @@ module completions { export extern "stackablectl release help uninstall" [ ] + # Upgrade a release + export extern "stackablectl release help upgrade" [ + ] + # Print this message or the help of the given subcommand(s) export extern "stackablectl release help help" [ ] @@ -967,6 +995,10 @@ module completions { export extern "stackablectl help release uninstall" [ ] + # Upgrade a release + export extern "stackablectl help release upgrade" [ + ] + # Interact with stacks, which are ready-to-use product combinations export extern "stackablectl help stack" [ ] diff --git a/rust/stackable-cockpit/src/engine/minikube/mod.rs b/rust/stackable-cockpit/src/engine/minikube/mod.rs index dab6e959..308de131 100644 --- a/rust/stackable-cockpit/src/engine/minikube/mod.rs +++ b/rust/stackable-cockpit/src/engine/minikube/mod.rs @@ -10,7 +10,7 @@ use crate::{ #[derive(Debug, Snafu)] pub enum Error { #[snafu(display( - "failed to determine if a Minikube cluster named '{cluster_name}' already exists" + "failed to determine if a Minikube cluster named {cluster_name:?} already exists" ))] CheckCluster { source: std::io::Error, diff --git a/rust/stackable-cockpit/src/helm.rs b/rust/stackable-cockpit/src/helm.rs index c43578d3..4cca0bba 100644 --- a/rust/stackable-cockpit/src/helm.rs +++ b/rust/stackable-cockpit/src/helm.rs @@ -122,8 +122,7 @@ impl Display for InstallReleaseStatus { } => { write!( f, - "The release {} ({}) is already installed (requested {}), skipping.", - release_name, current_version, requested_version + "The release {release_name} ({current_version}) is already installed (requested {requested_version}), skipping." ) } InstallReleaseStatus::ReleaseAlreadyInstalledUnspecified { @@ -132,16 +131,11 @@ impl Display for InstallReleaseStatus { } => { write!( f, - "The release {} ({}) is already installed and no specific version was requested, skipping.", - release_name, current_version + "The release {release_name} ({current_version}) is already installed and no specific version was requested, skipping." ) } InstallReleaseStatus::Installed(release_name) => { - write!( - f, - "The release {} was successfully installed.", - release_name - ) + write!(f, "The release {release_name} was successfully installed.") } } } @@ -157,17 +151,12 @@ impl Display for UninstallReleaseStatus { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { UninstallReleaseStatus::NotInstalled(release_name) => { - write!( - f, - "The release {} is not installed, skipping.", - release_name - ) + write!(f, "The release {release_name} is not installed, skipping.") } UninstallReleaseStatus::Uninstalled(release_name) => { write!( f, - "The release {} was successfully uninstalled.", - release_name + "The release {release_name} was successfully uninstalled." ) } } @@ -282,10 +271,7 @@ fn install_release( ); if let Some(error) = helm_sys::to_helm_error(&result) { - error!( - "Go wrapper function go_install_helm_release encountered an error: {}", - error - ); + error!("Go wrapper function go_install_helm_release encountered an error: {error}"); return Err(Error::InstallRelease { source: InstallReleaseError::HelmWrapper { error }, @@ -312,10 +298,7 @@ pub fn uninstall_release( let result = helm_sys::uninstall_helm_release(release_name, namespace, suppress_output); if let Some(err) = helm_sys::to_helm_error(&result) { - error!( - "Go wrapper function go_uninstall_helm_release encountered an error: {}", - err - ); + error!("Go wrapper function go_uninstall_helm_release encountered an error: {err}"); return Err(Error::UninstallRelease { error: err }); } @@ -325,10 +308,7 @@ pub fn uninstall_release( )); } - info!( - "The Helm release {} is not installed, skipping.", - release_name - ); + info!("The Helm release {release_name} is not installed, skipping."); Ok(UninstallReleaseStatus::NotInstalled( release_name.to_string(), @@ -352,10 +332,7 @@ pub fn list_releases(namespace: &str) -> Result, Error> { let result = helm_sys::list_helm_releases(namespace); if let Some(err) = helm_sys::to_helm_error(&result) { - error!( - "Go wrapper function go_helm_list_releases encountered an error: {}", - err - ); + error!("Go wrapper function go_helm_list_releases encountered an error: {err}"); return Err(Error::ListReleases { error: err }); } @@ -381,10 +358,7 @@ pub fn add_repo(repository_name: &str, repository_url: &str) -> Result<(), Error let result = helm_sys::add_helm_repository(repository_name, repository_url); if let Some(err) = helm_sys::to_helm_error(&result) { - error!( - "Go wrapper function go_add_helm_repo encountered an error: {}", - err - ); + error!("Go wrapper function go_add_helm_repo encountered an error: {err}"); return Err(Error::AddRepo { error: err }); } @@ -403,7 +377,7 @@ where let url = Url::parse(repo_url.as_ref()).context(UrlParseSnafu)?; let url = url.join(HELM_REPO_INDEX_FILE).context(UrlParseSnafu)?; - debug!("Using {} to retrieve Helm index file", url); + debug!("Using {url} to retrieve Helm index file"); // TODO (Techassi): Use the FileTransferClient for that let index_file_content = reqwest::get(url) diff --git a/rust/stackable-cockpit/src/platform/cluster/resource_request.rs b/rust/stackable-cockpit/src/platform/cluster/resource_request.rs index b254bd75..d3ada92c 100644 --- a/rust/stackable-cockpit/src/platform/cluster/resource_request.rs +++ b/rust/stackable-cockpit/src/platform/cluster/resource_request.rs @@ -32,8 +32,10 @@ impl Display for ResourceRequests { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "CPU: {}, Memory: {}, PVC space: {}", - self.cpu.0, self.memory.0, self.pvc.0 + "CPU: {cpu}, Memory: {memory}, PVC space: {pvc}", + cpu = self.cpu.0, + memory = self.memory.0, + pvc = self.pvc.0 ) } } @@ -67,8 +69,8 @@ pub enum ResourceRequestsError { #[derive(Debug, Snafu)] pub enum ResourceRequestsValidationError { #[snafu(display( - "The {object_name} requires {} CPU core(s), but there are only {} CPU core(s) available in the cluster", - required.as_cpu_count(), available.as_cpu_count() + "The {object_name} requires {required_cpu} CPU core(s), but there are only {available_cpu} CPU core(s) available in the cluster", + required_cpu = required.as_cpu_count(), available_cpu = available.as_cpu_count() ))] InsufficientCpu { available: CpuQuantity, diff --git a/rust/stackable-cockpit/src/platform/credentials.rs b/rust/stackable-cockpit/src/platform/credentials.rs index f03caaa9..5b96da02 100644 --- a/rust/stackable-cockpit/src/platform/credentials.rs +++ b/rust/stackable-cockpit/src/platform/credentials.rs @@ -25,7 +25,12 @@ pub struct Credentials { impl Display for Credentials { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}:{}", self.username, self.password) + write!( + f, + "{username}:{password}", + username = self.username, + password = self.password + ) } } diff --git a/rust/stackable-cockpit/src/platform/demo/spec.rs b/rust/stackable-cockpit/src/platform/demo/spec.rs index e2903516..6c4c5589 100644 --- a/rust/stackable-cockpit/src/platform/demo/spec.rs +++ b/rust/stackable-cockpit/src/platform/demo/spec.rs @@ -29,13 +29,13 @@ pub type DemoParameter = Parameter; #[derive(Debug, Snafu)] pub enum Error { - #[snafu(display("no stack named '{name}'"))] + #[snafu(display("no stack named {name:?}"))] NoSuchStack { name: String }, #[snafu(display("demo resource requests error"), context(false))] DemoResourceRequests { source: ResourceRequestsError }, - #[snafu(display("cannot install demo in namespace '{requested}', only '{}' supported", supported.join(", ")))] + #[snafu(display("cannot install demo in namespace {requested:?}, only {supported:?} supported", supported = supported.join(", ")))] UnsupportedNamespace { requested: String, supported: Vec, diff --git a/rust/stackable-cockpit/src/platform/manifests.rs b/rust/stackable-cockpit/src/platform/manifests.rs index c1b3a566..d9d23794 100644 --- a/rust/stackable-cockpit/src/platform/manifests.rs +++ b/rust/stackable-cockpit/src/platform/manifests.rs @@ -22,7 +22,7 @@ use crate::{ #[derive(Debug, Snafu)] pub enum Error { /// This error indicates that parsing a string into a path or URL failed. - #[snafu(display("failed to parse '{path_or_url}' as path/url"))] + #[snafu(display("failed to parse {path_or_url:?} as path/url"))] ParsePathOrUrl { source: PathOrUrlParseError, path_or_url: String, diff --git a/rust/stackable-cockpit/src/platform/release/spec.rs b/rust/stackable-cockpit/src/platform/release/spec.rs index e17120d2..13f589a9 100644 --- a/rust/stackable-cockpit/src/platform/release/spec.rs +++ b/rust/stackable-cockpit/src/platform/release/spec.rs @@ -14,6 +14,11 @@ use crate::{ operator::{self, ChartSourceType, OperatorSpec}, product, }, + utils::{ + k8s::{self, Client}, + path::{IntoPathOrUrl as _, PathOrUrlParseError}, + }, + xfer::{self, processor::Text}, }; type Result = std::result::Result; @@ -23,6 +28,17 @@ pub enum Error { #[snafu(display("failed to parse operator spec"))] OperatorSpecParse { source: operator::SpecParseError }, + /// This error indicates that parsing a string into a path or URL failed. + #[snafu(display("failed to parse {path_or_url:?} as path/url"))] + ParsePathOrUrl { + source: PathOrUrlParseError, + path_or_url: String, + }, + + /// This error indicates that receiving remote content failed. + #[snafu(display("failed to receive remote content"))] + FileTransfer { source: xfer::Error }, + #[snafu(display("failed to install release using Helm"))] HelmInstall { source: helm::Error }, @@ -31,6 +47,9 @@ pub enum Error { #[snafu(display("failed to launch background task"))] BackgroundTask { source: JoinError }, + + #[snafu(display("failed to deploy manifests using the kube client"))] + DeployManifest { source: k8s::Error }, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -118,14 +137,99 @@ impl ReleaseSpec { .await } + /// Upgrades a release by upgrading individual operators. + #[instrument(skip_all, fields( + %namespace, + indicatif.pb_show = true + ))] + pub async fn upgrade_crds( + &self, + include_products: &[String], + exclude_products: &[String], + namespace: &str, + k8s_client: &Client, + transfer_client: &xfer::Client, + ) -> Result<()> { + info!("Upgrading CRDs for release"); + Span::current().pb_set_style(&PROGRESS_BAR_STYLE); + + include_products.iter().for_each(|product| { + Span::current().record("product.included", product); + }); + exclude_products.iter().for_each(|product| { + Span::current().record("product.excluded", product); + }); + + let operators = self.filter_products(include_products, exclude_products); + + Span::current().pb_set_length(operators.len() as u64); + + for (product_name, product) in operators { + info!("Upgrading CRDs for {product_name}-operator"); + let iter_span = tracing::info_span!("upgrade_crds_iter", indicatif.pb_show = true); + + async move { + Span::current().pb_set_message(format!("Ugrading CRDs for {product_name}-operator").as_str()); + + let release_branch = match product.version.pre.as_str() { + "dev" => "main".to_string(), + _ => { + product.version.to_string() + } + }; + + let request_url = &format!( + "https://raw.githubusercontent.com/stackabletech/{product_name}-operator/{release_branch}/deploy/helm/{product_name}-operator/crds/crds.yaml" + ); + let request_url = request_url.into_path_or_url().context(ParsePathOrUrlSnafu { + path_or_url: request_url.clone(), + })?; + + // Get CRD manifests from request_url + let crd_manifests: String = transfer_client + .get(&request_url, &Text) + .await + .context(FileTransferSnafu)?; + + // Upgrade CRDs + k8s_client + .replace_crds(&crd_manifests) + .await + .context(DeployManifestSnafu)?; + + info!("Upgraded {product_name}-operator CRDs"); + + Ok::<(), Error>(()) + }.instrument(iter_span).await?; + + Span::current().pb_inc(1); + } + + Ok(()) + } + #[instrument(skip_all, fields(indicatif.pb_show = true))] - pub fn uninstall(&self, namespace: &str) -> Result<()> { + pub fn uninstall( + &self, + include_products: &[String], + exclude_products: &[String], + namespace: &str, + ) -> Result<()> { info!("Uninstalling release"); + include_products.iter().for_each(|product| { + Span::current().record("product.included", product); + }); + exclude_products.iter().for_each(|product| { + Span::current().record("product.excluded", product); + }); + + let operators = self.filter_products(include_products, exclude_products); + Span::current().pb_set_style(&PROGRESS_BAR_STYLE); - Span::current().pb_set_length(self.products.len() as u64); + Span::current().pb_set_length(operators.len() as u64); - for (product_name, product_spec) in &self.products { + for (product_name, product_spec) in operators { info!("Uninstalling {product_name}-operator"); // Create operator spec diff --git a/rust/stackable-cockpit/src/platform/service.rs b/rust/stackable-cockpit/src/platform/service.rs index f82d50fe..73ff576c 100644 --- a/rust/stackable-cockpit/src/platform/service.rs +++ b/rust/stackable-cockpit/src/platform/service.rs @@ -20,22 +20,22 @@ pub enum Error { #[snafu(display("failed to fetch data from Kubernetes API"))] KubeClientFetch { source: k8s::Error }, - #[snafu(display("missing namespace for service '{service}'"))] + #[snafu(display("missing namespace for service {service:?}"))] MissingServiceNamespace { service: String }, - #[snafu(display("missing spec for service '{service}'"))] + #[snafu(display("missing spec for service {service:?}"))] MissingServiceSpec { service: String }, - #[snafu(display("failed to get status of node '{node_name}'"))] + #[snafu(display("failed to get status of node {node_name:?}"))] GetNodeStatus { node_name: String }, - #[snafu(display("failed to get address of node '{node_name}'"))] + #[snafu(display("failed to get address of node {node_name:?}"))] GetNodeAddress { node_name: String }, - #[snafu(display("failed to find an ExternalIP or InternalIP for node '{node_name}'"))] + #[snafu(display("failed to find an ExternalIP or InternalIP for node {node_name:?}"))] NoIpForNode { node_name: String }, - #[snafu(display("failed to find node '{node_name}' in node_name_ip_mapping"))] + #[snafu(display("failed to find node {node_name:?} in node_name_ip_mapping"))] NodeMissingInIpMapping { node_name: String }, } diff --git a/rust/stackable-cockpit/src/platform/stack/spec.rs b/rust/stackable-cockpit/src/platform/stack/spec.rs index e95fb628..c5fd2939 100644 --- a/rust/stackable-cockpit/src/platform/stack/spec.rs +++ b/rust/stackable-cockpit/src/platform/stack/spec.rs @@ -49,7 +49,7 @@ pub enum Error { /// This error indicates that the stack doesn't support being installed in /// the provided namespace. - #[snafu(display("unable install stack in namespace {requested:?}, only '{}' supported", supported.join(", ")))] + #[snafu(display("unable install stack in namespace {requested:?}, only {supported:?} supported", supported = supported.join(", ")))] UnsupportedNamespace { requested: String, supported: Vec, diff --git a/rust/stackable-cockpit/src/platform/stacklet/mod.rs b/rust/stackable-cockpit/src/platform/stacklet/mod.rs index b2f915c0..7c4cd449 100644 --- a/rust/stackable-cockpit/src/platform/stacklet/mod.rs +++ b/rust/stackable-cockpit/src/platform/stacklet/mod.rs @@ -50,7 +50,7 @@ pub enum Error { #[snafu(display("failed to fetch data from the Kubernetes API"))] KubeClientFetch { source: k8s::Error }, - #[snafu(display("no namespace set for custom resource '{crd_name}'"))] + #[snafu(display("no namespace set for custom resource {crd_name:?}"))] CustomCrdNamespace { crd_name: String }, #[snafu(display("failed to deserialize cluster conditions from JSON"))] @@ -197,6 +197,9 @@ 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()), + kind: format!( + "{product_name}Cluster", + product_name = product_name.capitalize() + ), } } diff --git a/rust/stackable-cockpit/src/utils/k8s/client.rs b/rust/stackable-cockpit/src/utils/k8s/client.rs index 629aa9e4..e678b71b 100644 --- a/rust/stackable-cockpit/src/utils/k8s/client.rs +++ b/rust/stackable-cockpit/src/utils/k8s/client.rs @@ -38,41 +38,45 @@ pub enum Error { #[snafu(display("failed to patch/create Kubernetes object"))] KubeClientPatch { source: kube::error::Error }, + #[snafu(display("failed to replace Kubernetes object"))] + KubeClientReplace { + source: kube::error::Error, + gvk: GroupVersionKind, + }, + #[snafu(display("failed to deserialize YAML data"))] DeserializeYaml { source: serde_yaml::Error }, #[snafu(display("failed to run GVK discovery"))] GVKDiscoveryRun { source: kube::error::Error }, - #[snafu(display("GVK {gvk:?} is not known"))] - GVKUnkown { gvk: GroupVersionKind }, - #[snafu(display("failed to deploy manifest because type of object {object:?} is not set"))] ObjectType { object: DynamicObject }, - #[snafu(display("failed to deploy manifest because GVK {group}/{kind}@{version} cannot be resolved", + // Using output close to Display for ObjectRef https://docs.rs/kube-runtime/0.99.0/src/kube_runtime/reflector/object_ref.rs.html#292-296 + #[snafu(display("failed to resolve GVK: {kind}.{version}.{group}", group = gvk.group, version = gvk.version, kind = gvk.kind ))] - DiscoveryResolve { gvk: GroupVersionKind }, + GVKResolve { gvk: GroupVersionKind }, #[snafu(display("failed to convert byte string into UTF-8 string"))] ByteStringConvert { source: FromUtf8Error }, - #[snafu(display("missing namespace for service '{service}'"))] + #[snafu(display("missing namespace for service {service:?}"))] MissingServiceNamespace { service: String }, #[snafu(display("failed to retrieve cluster information"))] ClusterInformation { source: cluster::Error }, - #[snafu(display("invalid or empty secret data in '{secret_name}'"))] + #[snafu(display("invalid or empty secret data in {secret_name:?}"))] InvalidSecretData { secret_name: String }, - #[snafu(display("no username key in credentials secret '{secret_name}'"))] + #[snafu(display("no username key in credentials secret {secret_name:?}"))] NoUsernameKey { secret_name: String }, - #[snafu(display("no password key in credentials secret '{secret_name}'"))] + #[snafu(display("no password key in credentials secret {secret_name:?}"))] NoPasswordKey { secret_name: String }, } @@ -128,7 +132,7 @@ impl Client { let (resource, capabilities) = self .resolve_gvk(&gvk) .await? - .context(GVKUnkownSnafu { gvk })?; + .context(GVKResolveSnafu { gvk })?; let api: Api = match capabilities.scope { Scope::Cluster => { @@ -152,6 +156,49 @@ impl Client { Ok(()) } + /// Replaces CRDs defined the in raw `crds` YAML string. This + /// method will fail if it is unable to parse the CRDs, unable to + /// resolve GVKs or unable to replace/create the dynamic objects. + pub async fn replace_crds(&self, crds: &str) -> Result<()> { + for crd in serde_yaml::Deserializer::from_str(crds) { + let mut object = DynamicObject::deserialize(crd).context(DeserializeYamlSnafu)?; + + let object_type = object.types.as_ref().ok_or( + ObjectTypeSnafu { + object: object.clone(), + } + .build(), + )?; + + let gvk = Self::gvk_of_typemeta(object_type); + let (resource, _) = self + .resolve_gvk(&gvk) + .await? + .with_context(|| GVKResolveSnafu { gvk: gvk.clone() })?; + + // CRDs are cluster scoped + let api: Api = Api::all_with(self.client.clone(), &resource); + + if let Some(resource) = api + .get_opt(&object.name_any()) + .await + .context(KubeClientFetchSnafu)? + { + object.metadata.resource_version = resource.resource_version(); + api.replace(&object.name_any(), &PostParams::default(), &object) + .await + .with_context(|_| KubeClientReplaceSnafu { gvk })?; + } else { + // Create CRD if a previous version wasn't found + api.create(&PostParams::default(), &object) + .await + .context(KubeClientPatchSnafu)?; + } + } + + Ok(()) + } + /// Lists objects by looking up a GVK via the discovery. It returns an /// optional list of dynamic objects. The method returns [`Ok(None)`] /// if the client was unable to resolve the GVK. An error is returned diff --git a/rust/stackable-cockpit/src/utils/k8s/conditions.rs b/rust/stackable-cockpit/src/utils/k8s/conditions.rs index 06abc174..82f52118 100644 --- a/rust/stackable-cockpit/src/utils/k8s/conditions.rs +++ b/rust/stackable-cockpit/src/utils/k8s/conditions.rs @@ -43,7 +43,11 @@ impl ConditionsExt for Vec { self.iter() .map(|c| { DisplayCondition::new( - format!("{}: {}", c.type_, c.status), + format!( + "{condition_type}: {status}", + condition_type = c.type_, + status = c.status + ), Some(c.message.clone()), c.is_good(), ) @@ -57,7 +61,11 @@ impl ConditionsExt for Vec { self.iter() .map(|c| { DisplayCondition::new( - format!("{}: {}", c.type_, c.status), + format!( + "{condition_type}: {status}", + condition_type = c.type_, + status = c.status + ), c.message.clone(), c.is_good(), ) @@ -79,7 +87,11 @@ impl ConditionsExt for Vec { self.iter() .map(|c| { DisplayCondition::new( - format!("{}: {}", c.type_, c.status), + format!( + "{condition_type}: {status}", + condition_type = c.type_, + status = c.status + ), c.message.clone(), c.is_good(), ) diff --git a/rust/stackable-cockpit/src/utils/k8s/labels.rs b/rust/stackable-cockpit/src/utils/k8s/labels.rs index 7f9d5b17..d79c127b 100644 --- a/rust/stackable-cockpit/src/utils/k8s/labels.rs +++ b/rust/stackable-cockpit/src/utils/k8s/labels.rs @@ -37,7 +37,7 @@ pub trait ListParamsExt { impl ListParamsExt for ListParams { fn add_label(&mut self, label: impl Into) { match self.label_selector.as_mut() { - Some(labels) => labels.push_str(format!(",{}", label.into()).as_str()), + Some(labels) => labels.push_str(format!(",{label}", label = label.into()).as_str()), None => self.label_selector = Some(label.into()), } } diff --git a/rust/stackable-cockpit/src/utils/mod.rs b/rust/stackable-cockpit/src/utils/mod.rs index a42bcc0d..ee2018a4 100644 --- a/rust/stackable-cockpit/src/utils/mod.rs +++ b/rust/stackable-cockpit/src/utils/mod.rs @@ -8,5 +8,5 @@ pub mod templating; /// Returns the name of the operator used in the Helm repository. pub fn operator_chart_name(name: &str) -> String { - format!("{}-operator", name) + format!("{name}-operator") } diff --git a/rust/stackable-cockpit/src/utils/params.rs b/rust/stackable-cockpit/src/utils/params.rs index 623aeac4..d688009d 100644 --- a/rust/stackable-cockpit/src/utils/params.rs +++ b/rust/stackable-cockpit/src/utils/params.rs @@ -34,7 +34,7 @@ pub enum IntoParametersError { #[snafu(display("failed to parse raw parameter"))] RawParse { source: RawParameterParseError }, - #[snafu(display("invalid parameter '{parameter}', expected one of {expected}"))] + #[snafu(display("invalid parameter {parameter:?}, expected one of {expected:?}"))] InvalidParameter { parameter: String, expected: String }, } @@ -106,7 +106,7 @@ pub enum RawParameterParseError { impl Display for RawParameter { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}={}", self.name, self.value) + write!(f, "{name}={value}", name = self.name, value = self.value) } } diff --git a/rust/stackable-cockpit/src/xfer/mod.rs b/rust/stackable-cockpit/src/xfer/mod.rs index 87dde643..3792305b 100644 --- a/rust/stackable-cockpit/src/xfer/mod.rs +++ b/rust/stackable-cockpit/src/xfer/mod.rs @@ -120,6 +120,8 @@ impl Client { .client .execute(req) .await + .context(FetchRemoteContentSnafu)? + .error_for_status() .context(FetchRemoteContentSnafu)?; result.text().await.context(FetchRemoteContentSnafu) diff --git a/rust/stackablectl/CHANGELOG.md b/rust/stackablectl/CHANGELOG.md index a5d6b7e1..e039bbe7 100644 --- a/rust/stackablectl/CHANGELOG.md +++ b/rust/stackablectl/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. This should unblock demos to run in all namespaces, as they can template the namespace e.g. for the FQDN of services ([#355]). - Add progress reporting ([#376]). - Include SparkConnectServer in the `stacklet list` output ([#380]). +- Add release upgrade functionality to `stackablectl release` command through `upgrade` subcommand ([#379]). ### Changed @@ -24,6 +25,7 @@ All notable changes to this project will be documented in this file. [#373]: https://github.com/stackabletech/stackable-cockpit/pull/373 [#376]: https://github.com/stackabletech/stackable-cockpit/pull/376 [#380]: https://github.com/stackabletech/stackable-cockpit/pull/380 +[#379]: https://github.com/stackabletech/stackable-cockpit/pull/379 ## [25.3.0] - 2025-03-27 diff --git a/rust/stackablectl/src/cmds/debug.rs b/rust/stackablectl/src/cmds/debug.rs index cf0feaa1..83ad165c 100644 --- a/rust/stackablectl/src/cmds/debug.rs +++ b/rust/stackablectl/src/cmds/debug.rs @@ -340,8 +340,8 @@ impl Drop for AsyncStdin { let status = unsafe { libc::fcntl(self.fd.as_raw_fd(), libc::F_SETFL, self.old_flags) }; if status == -1 { panic!( - "unable to revert stdin flags: {}", - std::io::Error::last_os_error() + "unable to revert stdin flags: {error}", + error = std::io::Error::last_os_error() ); } } diff --git a/rust/stackablectl/src/cmds/demo.rs b/rust/stackablectl/src/cmds/demo.rs index 9480e3dd..bffc38a0 100644 --- a/rust/stackablectl/src/cmds/demo.rs +++ b/rust/stackablectl/src/cmds/demo.rs @@ -125,13 +125,13 @@ pub enum CmdError { #[snafu(display("failed to serialize JSON output"))] SerializeJsonOutput { source: serde_json::Error }, - #[snafu(display("no demo with name '{name}'"))] + #[snafu(display("no demo with name {name:?}"))] NoSuchDemo { name: String }, - #[snafu(display("no stack with name '{name}'"))] + #[snafu(display("no stack with name {name:?}"))] NoSuchStack { name: String }, - #[snafu(display("no release '{release}'"))] + #[snafu(display("no release {release:?}"))] NoSuchRelease { release: String }, #[snafu(display("failed to get latest release"))] @@ -302,7 +302,10 @@ async fn describe_cmd( result .with_command_hint( - format!("stackablectl demo install {}", args.demo_name), + format!( + "stackablectl demo install {demo_name}", + demo_name = args.demo_name + ), "install the demo", ) .with_command_hint("stackablectl demo list", "list all available demos") @@ -398,11 +401,11 @@ async fn install_cmd( })?; let operator_cmd = format!( - "stackablectl operator installed{}", - if args.namespaces.operator_namespace != DEFAULT_OPERATOR_NAMESPACE { + "stackablectl operator installed{option}", + option = if args.namespaces.operator_namespace != DEFAULT_OPERATOR_NAMESPACE { format!( - " --operator-namespace {}", - args.namespaces.operator_namespace + " --operator-namespace {namespace}", + namespace = args.namespaces.operator_namespace ) } else { "".into() @@ -410,9 +413,12 @@ async fn install_cmd( ); let stacklet_cmd = format!( - "stackablectl stacklet list{}", - if args.namespaces.namespace != DEFAULT_NAMESPACE { - format!(" --namespace {}", args.namespaces.namespace) + "stackablectl stacklet list{option}", + option = if args.namespaces.namespace != DEFAULT_NAMESPACE { + format!( + " --namespace {namespace}", + namespace = args.namespaces.namespace + ) } else { "".into() } @@ -421,7 +427,10 @@ async fn install_cmd( output .with_command_hint(operator_cmd, "display the installed operators") .with_command_hint(stacklet_cmd, "display the installed stacklets") - .with_output(format!("Installed demo '{}'", args.demo_name)); + .with_output(format!( + "Installed demo {demo_name:?}", + demo_name = args.demo_name + )); Ok(output.render()) } diff --git a/rust/stackablectl/src/cmds/operator.rs b/rust/stackablectl/src/cmds/operator.rs index 84c269cd..cb2e903c 100644 --- a/rust/stackablectl/src/cmds/operator.rs +++ b/rust/stackablectl/src/cmds/operator.rs @@ -141,7 +141,7 @@ pub enum CmdError { version: String, }, - #[snafu(display("unknown repository name '{name}'"))] + #[snafu(display("unknown repository name {name:?}"))] UnknownRepoName { name: String }, #[snafu(display("Helm error"))] @@ -159,7 +159,7 @@ pub enum CmdError { #[snafu(display("failed to create Kubernetes client"))] KubeClientCreate { source: k8s::Error }, - #[snafu(display("failed to create namespace '{namespace}'"))] + #[snafu(display("failed to create namespace {namespace:?}"))] NamespaceCreate { source: namespace::Error, namespace: String, @@ -292,7 +292,10 @@ async fn describe_cmd(args: &OperatorDescribeArgs, cli: &Cli) -> Result Result Result, + + /// Blacklist of product operators to install + #[arg(short, long = "exclude", group = "products")] + excluded_products: Vec, + + /// Namespace in the cluster used to deploy the operators + #[arg(long, default_value = DEFAULT_OPERATOR_NAMESPACE, visible_aliases(["operator-ns"]))] + pub operator_namespace: String, +} + #[derive(Debug, Args)] pub struct ReleaseUninstallArgs { /// Name of the release to uninstall @@ -97,6 +125,9 @@ pub struct ReleaseUninstallArgs { #[derive(Debug, Snafu)] pub enum CmdError { + #[snafu(display("Helm error"))] + HelmError { source: helm::Error }, + #[snafu(display("failed to serialize YAML output"))] SerializeYamlOutput { source: serde_yaml::Error }, @@ -109,9 +140,15 @@ pub enum CmdError { #[snafu(display("failed to build release list"))] BuildList { source: list::Error }, + #[snafu(display("no release {release:?}"))] + NoSuchRelease { release: String }, + #[snafu(display("failed to install release"))] ReleaseInstall { source: release::Error }, + #[snafu(display("failed to upgrade CRDs for release"))] + CrdUpgrade { source: release::Error }, + #[snafu(display("failed to uninstall release"))] ReleaseUninstall { source: release::Error }, @@ -121,7 +158,7 @@ pub enum CmdError { #[snafu(display("failed to create Kubernetes client"))] KubeClientCreate { source: k8s::Error }, - #[snafu(display("failed to create namespace '{namespace}'"))] + #[snafu(display("failed to create namespace {namespace:?}"))] NamespaceCreate { source: namespace::Error, namespace: String, @@ -147,6 +184,9 @@ impl ReleaseArgs { ReleaseCommands::Describe(args) => describe_cmd(args, cli, release_list).await, ReleaseCommands::Install(args) => install_cmd(args, cli, release_list).await, ReleaseCommands::Uninstall(args) => uninstall_cmd(args, cli, release_list).await, + ReleaseCommands::Upgrade(args) => { + upgrade_cmd(args, cli, release_list, &transfer_client).await + } } } } @@ -253,7 +293,10 @@ async fn describe_cmd( result .with_command_hint( - format!("stackablectl release install {}", args.release), + format!( + "stackablectl release install {release}", + release = args.release + ), "install the release", ) .with_command_hint("stackablectl release list", "list all available releases") @@ -264,7 +307,9 @@ async fn describe_cmd( OutputType::Json => serde_json::to_string(&release).context(SerializeJsonOutputSnafu), OutputType::Yaml => serde_yaml::to_string(&release).context(SerializeYamlOutputSnafu), }, - None => Ok("No such release".into()), + None => Err(CmdError::NoSuchRelease { + release: args.release.clone(), + }), } } @@ -311,11 +356,102 @@ async fn install_cmd( "stackablectl operator installed", "list installed operators", ) - .with_output(format!("Installed release '{}'", args.release)); + .with_output(format!( + "Installed release {release:?}", + release = args.release + )); + + Ok(output.render()) + } + None => Err(CmdError::NoSuchRelease { + release: args.release.clone(), + }), + } +} + +#[instrument(skip_all, fields(indicatif.pb_show = true))] +async fn upgrade_cmd( + args: &ReleaseUpgradeArgs, + cli: &Cli, + release_list: release::ReleaseList, + transfer_client: &xfer::Client, +) -> Result { + info!(release = %args.release, "Upgrading release"); + Span::current().pb_set_message("Upgrading release"); + + match release_list.get(&args.release) { + Some(release) => { + let mut output = cli.result(); + let client = Client::new().await.context(KubeClientCreateSnafu)?; + + // Get all currently installed operators to only upgrade those + let installed_charts: Vec = + helm::list_releases(&args.operator_namespace).context(HelmSnafu)?; + + let mut operators: Vec = operator::VALID_OPERATORS + .iter() + .filter(|operator| { + installed_charts + .iter() + .any(|release| release.name == utils::operator_chart_name(operator)) + }) + .map(|operator| operator.to_string()) + .collect(); + + // Uninstall the old operator release first + release + .uninstall( + &operators, + &args.excluded_products, + &args.operator_namespace, + ) + .context(ReleaseUninstallSnafu)?; + + // If operators were added to args.included_products, install them as well + for product in &args.included_products { + if !operators.contains(product) { + operators.push(product.clone()); + } + } + + // Upgrade the CRDs for all the operators to be upgraded + release + .upgrade_crds( + &operators, + &args.excluded_products, + &args.operator_namespace, + &client, + transfer_client, + ) + .await + .context(CrdUpgradeSnafu)?; + + // Install the new operator release + release + .install( + &operators, + &args.excluded_products, + &args.operator_namespace, + &ChartSourceType::from(cli.chart_type()), + ) + .await + .context(ReleaseInstallSnafu)?; + + output + .with_command_hint( + "stackablectl operator installed", + "list installed operators", + ) + .with_output(format!( + "Upgraded to release {release:?}", + release = args.release + )); Ok(output.render()) } - None => Ok("No such release".into()), + None => Err(CmdError::NoSuchRelease { + release: args.release.clone(), + }), } } @@ -330,17 +466,22 @@ async fn uninstall_cmd( match release_list.get(&args.release) { Some(release) => { release - .uninstall(&args.operator_namespace) + .uninstall(&Vec::new(), &Vec::new(), &args.operator_namespace) .context(ReleaseUninstallSnafu)?; let mut result = cli.result(); result .with_command_hint("stackablectl release list", "list available releases") - .with_output(format!("Uninstalled release '{}'", args.release)); + .with_output(format!( + "Uninstalled release {release:?}", + release = args.release + )); Ok(result.render()) } - None => Ok("No such release".into()), + None => Err(CmdError::NoSuchRelease { + release: args.release.clone(), + }), } } diff --git a/rust/stackablectl/src/cmds/stack.rs b/rust/stackablectl/src/cmds/stack.rs index 01816929..a027166d 100644 --- a/rust/stackablectl/src/cmds/stack.rs +++ b/rust/stackablectl/src/cmds/stack.rs @@ -121,7 +121,7 @@ pub enum CmdError { #[snafu(display("failed to serialize JSON output"))] SerializeJsonOutput { source: serde_json::Error }, - #[snafu(display("no release '{release}'"))] + #[snafu(display("no release {release:?}"))] NoSuchRelease { release: String }, #[snafu(display("failed to get latest release"))] @@ -291,7 +291,10 @@ fn describe_cmd( result .with_command_hint( - format!("stackablectl stack install {}", args.stack_name), + format!( + "stackablectl stack install {stack_name}", + stack_name = args.stack_name + ), "install the stack", ) .with_command_hint("stackablectl stack list", "list all available stacks") @@ -361,11 +364,11 @@ async fn install_cmd( })?; let operator_cmd = format!( - "stackablectl operator installed{}", - if args.namespaces.operator_namespace != DEFAULT_OPERATOR_NAMESPACE { + "stackablectl operator installed{option}", + option = if args.namespaces.operator_namespace != DEFAULT_OPERATOR_NAMESPACE { format!( - " --operator-namespace {}", - args.namespaces.operator_namespace + " --operator-namespace {namespace}", + namespace = args.namespaces.operator_namespace ) } else { "".into() @@ -373,9 +376,12 @@ async fn install_cmd( ); let stacklet_cmd = format!( - "stackablectl stacklet list{}", - if args.namespaces.namespace != DEFAULT_NAMESPACE { - format!(" --namespace {}", args.namespaces.namespace) + "stackablectl stacklet list{option}", + option = if args.namespaces.namespace != DEFAULT_NAMESPACE { + format!( + " --namespace {namespace}", + namespace = args.namespaces.namespace + ) } else { "".into() } @@ -384,7 +390,10 @@ async fn install_cmd( output .with_command_hint(operator_cmd, "display the installed operators") .with_command_hint(stacklet_cmd, "display the installed stacklets") - .with_output(format!("Installed stack '{}'", args.stack_name)); + .with_output(format!( + "Installed stack {stack_name:?}", + stack_name = args.stack_name + )); Ok(output.render()) } diff --git a/rust/stackablectl/src/cmds/stacklet.rs b/rust/stackablectl/src/cmds/stacklet.rs index 332d45a0..62c8aff2 100644 --- a/rust/stackablectl/src/cmds/stacklet.rs +++ b/rust/stackablectl/src/cmds/stacklet.rs @@ -191,7 +191,7 @@ async fn list_cmd(args: &StackletListArgs, cli: &Cli) -> Result Result Ok("No credentials".into()), } @@ -280,7 +282,7 @@ fn render_condition_error( ) -> Option { if !is_good.unwrap_or(true) { let message = message.unwrap_or("-".into()); - return Some(format!("[{}]: {}", error_index, message)); + return Some(format!("[{error_index}]: {message}")); } None @@ -294,7 +296,7 @@ fn color_condition(condition: &str, is_good: Option, error_index: usize) - if is_good { condition.to_owned() } else { - format!("{}: See [{}]", condition, error_index) + format!("{condition}: See [{error_index}]") } } None => condition.to_owned(), @@ -308,6 +310,6 @@ fn render_errors(errors: Vec) -> Option { } else if errors.len() == 1 { Some(errors[0].clone()) } else { - Some(format!("{}\n---\n", errors.join("\n"))) + Some(format!("{errors}\n---\n", errors = errors.join("\n"))) } } diff --git a/rust/stackablectl/src/main.rs b/rust/stackablectl/src/main.rs index 9ad159d2..d6f96b24 100644 --- a/rust/stackablectl/src/main.rs +++ b/rust/stackablectl/src/main.rs @@ -6,7 +6,7 @@ use tracing::{Level, metadata::LevelFilter}; use tracing_indicatif::{ IndicatifLayer, filter::{IndicatifFilter, hide_indicatif_span_fields}, - indicatif_eprintln, + indicatif_eprintln, indicatif_println, }; use tracing_subscriber::{ Layer as _, @@ -63,12 +63,13 @@ async fn main() -> Result<(), Error> { } match app.run().await { - Ok(result) => print!("{result}"), + Ok(result) => indicatif_println!("{result}"), Err(err) => { let mut output = app.error(); output.with_error_report(err); - eprint!("{}", output.render()) + indicatif_eprintln!("{error}", error = output.render()); + std::process::exit(1); } } diff --git a/rust/stackablectl/src/output/mod.rs b/rust/stackablectl/src/output/mod.rs index eb1fe85f..20ef91ef 100644 --- a/rust/stackablectl/src/output/mod.rs +++ b/rust/stackablectl/src/output/mod.rs @@ -55,7 +55,7 @@ where let mut report = String::new(); // Print top most error - write!(report, "An unrecoverable error occured: {}\n\n", self)?; + write!(report, "An unrecoverable error occured: {self}\n\n")?; writeln!( report, "Caused by these errors (recent errors listed first):" @@ -65,7 +65,7 @@ where let mut index = 1; while let Some(source) = error.source() { - writeln!(report, " {}: {}", index, source)?; + writeln!(report, " {index}: {source}")?; error = source; index += 1; } diff --git a/rust/stackablectl/src/output/result.rs b/rust/stackablectl/src/output/result.rs index 28409bc9..1f36d397 100644 --- a/rust/stackablectl/src/output/result.rs +++ b/rust/stackablectl/src/output/result.rs @@ -55,9 +55,9 @@ impl ResultContext { description: impl Into, ) -> &mut Self { self.command_hints.push(format!( - "Use \"{}\" to {}.", - command.into(), - description.into() + "Use \"{command}\" to {description}.", + command = command.into(), + description = description.into() )); self diff --git a/rust/xtask/src/docs.rs b/rust/xtask/src/docs.rs index 6f7f4b8b..d36feb65 100644 --- a/rust/xtask/src/docs.rs +++ b/rust/xtask/src/docs.rs @@ -43,8 +43,8 @@ pub fn generate() -> Result<(), GenDocsError> { .unwrap() .join(DOCS_BASE_PATH) .join(format!( - "{}.adoc", - if cmd.get_name() == cli.get_name() { + "{name}.adoc", + name = if cmd.get_name() == cli.get_name() { "index" } else { cmd.get_name() diff --git a/web/build.rs b/web/build.rs index 3e8de5e0..40145831 100644 --- a/web/build.rs +++ b/web/build.rs @@ -47,8 +47,8 @@ fn main() { } write!( File::create(out_dir.join("vite-asset-map.rs")).unwrap(), - "{}", - asset_map.build() + "{asset_map}", + asset_map = asset_map.build() ) .unwrap(); }