Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

mygithub.libinneed.workers.dev/stackitcloud/stackit-cli

Package Overview
Dependencies
Versions
173
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mygithub.libinneed.workers.dev/stackitcloud/stackit-cli - npm Package Compare versions

Comparing version
v0.37.0-test3
to
v0.37.0
+0
-16
.github/workflows/release.yaml

@@ -78,12 +78,2 @@ # STACKIT CLI release workflow.

GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
- name: Install Docker
run: |
# Install Docker on macOS runner
brew install --cask docker
# Start Docker daemon
sudo /Applications/Docker.app/Contents/MacOS/Docker &
# Wait for Docker to be ready
sleep 30
docker --version
echo "Docker is now available for createrepo_c operations"
- name: Publish packages to APT repo

@@ -95,7 +85,1 @@ if: contains(github.ref_name, '-') == false

run: ./scripts/publish-apt-packages.sh
- name: Publish packages to RPM repo
if: contains(github.ref_name, '-') == false
env:
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
GPG_PRIVATE_KEY_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
run: ./scripts/publish-rpm-packages.sh
+1
-1

@@ -16,5 +16,5 @@ name: Renovate

- name: Self-hosted Renovate
uses: renovatebot/github-action@v43.0.5
uses: renovatebot/github-action@v43.0.3
with:
configurationFile: .github/renovate.json
token: ${{ secrets.RENOVATE_TOKEN }}

@@ -168,3 +168,4 @@ version: 2

# if the tag has a prerelease indicator (e.g. v0.0.1-alpha1)
skip_upload: auto
# Temporarily not skipping prereleases to test integration with Winget
# skip_upload: auto
repository:

@@ -171,0 +172,0 @@ owner: stackitcloud

@@ -32,3 +32,3 @@ ## stackit mongodbflex user create

--instance-id string ID of the instance
--role strings Roles of the user, possible values are ["read" "readWrite" "readWriteAnyDatabase"] (default [read])
--role strings Roles of the user, possible values are ["read" "readWrite"] (default [read])
--username string Username of the user. If not specified, a random username will be assigned

@@ -35,0 +35,0 @@ ```

@@ -26,3 +26,3 @@ ## stackit mongodbflex user update

--instance-id string ID of the instance
--role strings Roles of the user, possible values are ["read" "readWrite" "readWriteAnyDatabase"] (default [])
--role strings Roles of the user, possible values are ["read" "readWrite"] (default [])
```

@@ -29,0 +29,0 @@

## stackit ske cluster create
Creates a SKE cluster
Creates an SKE cluster

@@ -18,9 +18,9 @@ ### Synopsis

```
Create a SKE cluster using default configuration
Create an SKE cluster using default configuration
$ stackit ske cluster create my-cluster
Create a SKE cluster using an API payload sourced from the file "./payload.json"
Create an SKE cluster using an API payload sourced from the file "./payload.json"
$ stackit ske cluster create my-cluster --payload @./payload.json
Create a SKE cluster using an API payload provided as a JSON string
Create an SKE cluster using an API payload provided as a JSON string
$ stackit ske cluster create my-cluster --payload "{...}"

@@ -27,0 +27,0 @@

@@ -16,3 +16,3 @@ ## stackit ske cluster delete

```
Delete a SKE cluster with name "my-cluster"
Delete an SKE cluster with name "my-cluster"
$ stackit ske cluster delete my-cluster

@@ -19,0 +19,0 @@ ```

## stackit ske cluster describe
Shows details of a SKE cluster
Shows details of a SKE cluster
### Synopsis
Shows details of a STACKIT Kubernetes Engine (SKE) cluster.
Shows details of a STACKIT Kubernetes Engine (SKE) cluster.

@@ -16,6 +16,6 @@ ```

```
Get details of a SKE cluster with name "my-cluster"
Get details of an SKE cluster with name "my-cluster"
$ stackit ske cluster describe my-cluster
Get details of a SKE cluster with name "my-cluster" in JSON format
Get details of an SKE cluster with name "my-cluster" in JSON format
$ stackit ske cluster describe my-cluster --output-format json

@@ -22,0 +22,0 @@ ```

## stackit ske cluster update
Updates a SKE cluster
Updates an SKE cluster

@@ -18,6 +18,6 @@ ### Synopsis

```
Update a SKE cluster using an API payload sourced from the file "./payload.json"
Update an SKE cluster using an API payload sourced from the file "./payload.json"
$ stackit ske cluster update my-cluster --payload @./payload.json
Update a SKE cluster using an API payload provided as a JSON string
Update an SKE cluster using an API payload provided as a JSON string
$ stackit ske cluster update my-cluster --payload "{...}"

@@ -24,0 +24,0 @@

@@ -33,12 +33,8 @@ ## stackit ske cluster

* [stackit ske](./stackit_ske.md) - Provides functionality for SKE
* [stackit ske cluster create](./stackit_ske_cluster_create.md) - Creates a SKE cluster
* [stackit ske cluster create](./stackit_ske_cluster_create.md) - Creates an SKE cluster
* [stackit ske cluster delete](./stackit_ske_cluster_delete.md) - Deletes a SKE cluster
* [stackit ske cluster describe](./stackit_ske_cluster_describe.md) - Shows details of a SKE cluster
* [stackit ske cluster describe](./stackit_ske_cluster_describe.md) - Shows details of a SKE cluster
* [stackit ske cluster generate-payload](./stackit_ske_cluster_generate-payload.md) - Generates a payload to create/update SKE clusters
* [stackit ske cluster hibernate](./stackit_ske_cluster_hibernate.md) - Trigger hibernate for a SKE cluster
* [stackit ske cluster list](./stackit_ske_cluster_list.md) - Lists all SKE clusters
* [stackit ske cluster maintenance](./stackit_ske_cluster_maintenance.md) - Trigger maintenance for a SKE cluster
* [stackit ske cluster reconcile](./stackit_ske_cluster_reconcile.md) - Trigger reconcile for a SKE cluster
* [stackit ske cluster update](./stackit_ske_cluster_update.md) - Updates a SKE cluster
* [stackit ske cluster wakeup](./stackit_ske_cluster_wakeup.md) - Trigger wakeup from hibernation for a SKE cluster
* [stackit ske cluster update](./stackit_ske_cluster_update.md) - Updates an SKE cluster
## stackit ske kubeconfig create
Creates or update a kubeconfig for a SKE cluster
Creates or update a kubeconfig for an SKE cluster
### Synopsis
Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster, if the config exists in the kubeconfig file the information will be updated.
Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster, if the config exits in the kubeconfig file the information will be updated.

@@ -9,0 +9,0 @@ By default, the kubeconfig information of the SKE cluster is merged into the default kubeconfig file of the current user. If the kubeconfig file doesn't exist, a new one will be created.

@@ -33,4 +33,4 @@ ## stackit ske kubeconfig

* [stackit ske](./stackit_ske.md) - Provides functionality for SKE
* [stackit ske kubeconfig create](./stackit_ske_kubeconfig_create.md) - Creates or update a kubeconfig for a SKE cluster
* [stackit ske kubeconfig create](./stackit_ske_kubeconfig_create.md) - Creates or update a kubeconfig for an SKE cluster
* [stackit ske kubeconfig login](./stackit_ske_kubeconfig_login.md) - Login plugin for kubernetes clients
+26
-26

@@ -12,3 +12,3 @@ module github.com/stackitcloud/stackit-cli

github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf
github.com/jedib0t/go-pretty/v6 v6.6.8
github.com/jedib0t/go-pretty/v6 v6.6.7
github.com/lmittmann/tint v1.1.2

@@ -19,20 +19,20 @@ github.com/mattn/go-colorable v0.1.14

github.com/spf13/viper v1.20.1
github.com/stackitcloud/stackit-sdk-go/core v0.17.3
github.com/stackitcloud/stackit-sdk-go/services/alb v0.6.1
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.8.1
github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1
github.com/stackitcloud/stackit-sdk-go/services/git v0.7.1
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.27.1
github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v1.5.1
github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.24.1
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v1.2.1
github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.17.1
github.com/stackitcloud/stackit-sdk-go/services/runcommand v1.3.1
github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.13.1
github.com/stackitcloud/stackit-sdk-go/services/serverbackup v1.3.2
github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.2.1
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.9.1
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.2
github.com/stackitcloud/stackit-sdk-go/services/ske v1.3.0
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.1
github.com/stackitcloud/stackit-sdk-go/core v0.17.2
github.com/stackitcloud/stackit-sdk-go/services/alb v0.6.0
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.8.0
github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.0
github.com/stackitcloud/stackit-sdk-go/services/git v0.7.0
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.27.0
github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v1.5.0
github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.24.0
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v1.2.0
github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.17.0
github.com/stackitcloud/stackit-sdk-go/services/runcommand v1.3.0
github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.13.0
github.com/stackitcloud/stackit-sdk-go/services/serverbackup v1.3.1
github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.2.0
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.9.0
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.1
github.com/stackitcloud/stackit-sdk-go/services/ske v1.1.0
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.0
github.com/zalando/go-keyring v0.2.6

@@ -243,9 +243,9 @@ golang.org/x/mod v0.26.0

github.com/spf13/cast v1.7.1 // indirect
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.1
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1
github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.1
github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.3.1
github.com/stackitcloud/stackit-sdk-go/services/observability v0.9.1
github.com/stackitcloud/stackit-sdk-go/services/rabbitmq v0.25.1
github.com/stackitcloud/stackit-sdk-go/services/redis v0.25.1
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.0
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.0
github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.0
github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.3.0
github.com/stackitcloud/stackit-sdk-go/services/observability v0.9.0
github.com/stackitcloud/stackit-sdk-go/services/rabbitmq v0.25.0
github.com/stackitcloud/stackit-sdk-go/services/redis v0.25.0
github.com/subosito/gotenv v1.6.0 // indirect

@@ -252,0 +252,0 @@ go.uber.org/multierr v1.11.0 // indirect

@@ -20,45 +20,5 @@ # Installation

```shell
brew install --cask stackit
brew install stackit
```
#### Formula deprecated
The homebrew formula is deprecated, will no longer be updated and will be removed after 2026-01-22.
You need to install the STACKIT CLI as cask.
Therefor you need to uninstall the formula and reinstall it as cask.
Your profiles should normally remain. To ensure that nothing will be gone, you should backup them.
1. Export your existing profiles. This will create a json file in your current directory.
```shell
stackit config profile export default
```
2. If you have multiple profiles, then execute the export command for each of them. You can find your profiles via:
```shell
stackit config profile list
stackit config profile export <profile-name>
```
3. Uninstall the formula.
```shell
brew uninstall stackit
```
4. Install the STACKIT CLI as cask.
```shell
brew install --cask stackit
```
5. Check if your configs are still stored.
```shell
stackit config profile list
```
6. In case the profiles are gone, import your profiles via:
```shell
$ stackit config profile import -c @default.json --name myProfile
```
### Linux

@@ -65,0 +25,0 @@

package getaccesstoken
import (
"encoding/json"
"fmt"
"github.com/goccy/go-yaml"
"github.com/spf13/cobra"

@@ -14,10 +10,4 @@ "github.com/stackitcloud/stackit-cli/internal/cmd/params"

"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
)
type inputModel struct {
*globalflags.GlobalFlagModel
}
func NewCmd(params *params.CmdParams) *cobra.Command {

@@ -34,8 +24,3 @@ cmd := &cobra.Command{

),
RunE: func(cmd *cobra.Command, _ []string) error {
model, err := parseInput(params.Printer, cmd)
if err != nil {
return err
}
RunE: func(_ *cobra.Command, _ []string) error {
userSessionExpired, err := auth.UserSessionExpired()

@@ -55,28 +40,4 @@ if err != nil {

switch model.OutputFormat {
case print.JSONOutputFormat:
details, err := json.MarshalIndent(map[string]string{
"access_token": accessToken,
}, "", " ")
if err != nil {
return fmt.Errorf("marshal image list: %w", err)
}
params.Printer.Outputln(string(details))
return nil
case print.YAMLOutputFormat:
details, err := yaml.MarshalWithOptions(map[string]string{
"access_token": accessToken,
}, yaml.IndentSequence(true), yaml.UseJSONMarshaler())
if err != nil {
return fmt.Errorf("marshal image list: %w", err)
}
params.Printer.Outputln(string(details))
return nil
default:
params.Printer.Outputln(accessToken)
return nil
}
params.Printer.Outputf("%s\n", accessToken)
return nil
},

@@ -86,20 +47,1 @@ }

}
func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
globalFlags := globalflags.Parse(p, cmd)
model := inputModel{
GlobalFlagModel: globalFlags,
}
if p.IsVerbosityDebug() {
modelStr, err := print.BuildDebugStrFromInputModel(model)
if err != nil {
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
} else {
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
}
}
return &model, nil
}

@@ -106,3 +106,3 @@ package create

func configureFlags(cmd *cobra.Command) {
roleOptions := []string{"read", "readWrite", "readWriteAnyDatabase"}
roleOptions := []string{"read", "readWrite"}

@@ -109,0 +109,0 @@ cmd.Flags().Var(flags.UUIDFlag(), instanceIdFlag, "ID of the instance")

@@ -100,3 +100,3 @@ package update

func configureFlags(cmd *cobra.Command) {
roleOptions := []string{"read", "readWrite", "readWriteAnyDatabase"}
roleOptions := []string{"read", "readWrite"}

@@ -103,0 +103,0 @@ cmd.Flags().Var(flags.UUIDFlag(), instanceIdFlag, "ID of the instance")

@@ -9,8 +9,4 @@ package cluster

generatepayload "github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/generate-payload"
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/hibernate"
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/list"
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/maintenance"
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/reconcile"
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/update"
"github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/wakeup"
"github.com/stackitcloud/stackit-cli/internal/pkg/args"

@@ -41,6 +37,2 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/utils"

cmd.AddCommand(update.NewCmd(params))
cmd.AddCommand(hibernate.NewCmd(params))
cmd.AddCommand(maintenance.NewCmd(params))
cmd.AddCommand(reconcile.NewCmd(params))
cmd.AddCommand(wakeup.NewCmd(params))
}

@@ -43,3 +43,3 @@ package create

Use: fmt.Sprintf("create %s", clusterNameArg),
Short: "Creates a SKE cluster",
Short: "Creates an SKE cluster",
Long: fmt.Sprintf("%s\n%s\n%s",

@@ -53,9 +53,9 @@ "Creates a STACKIT Kubernetes Engine (SKE) cluster.",

examples.NewExample(
`Create a SKE cluster using default configuration`,
`Create an SKE cluster using default configuration`,
"$ stackit ske cluster create my-cluster"),
examples.NewExample(
`Create a SKE cluster using an API payload sourced from the file "./payload.json"`,
`Create an SKE cluster using an API payload sourced from the file "./payload.json"`,
"$ stackit ske cluster create my-cluster --payload @./payload.json"),
examples.NewExample(
`Create a SKE cluster using an API payload provided as a JSON string`,
`Create an SKE cluster using an API payload provided as a JSON string`,
`$ stackit ske cluster create my-cluster --payload "{...}"`),

@@ -62,0 +62,0 @@ examples.NewExample(

@@ -38,3 +38,3 @@ package delete

examples.NewExample(
`Delete a SKE cluster with name "my-cluster"`,
`Delete an SKE cluster with name "my-cluster"`,
"$ stackit ske cluster delete my-cluster"),

@@ -41,0 +41,0 @@ ),

@@ -34,11 +34,11 @@ package describe

Use: fmt.Sprintf("describe %s", clusterNameArg),
Short: "Shows details of a SKE cluster",
Long: "Shows details of a STACKIT Kubernetes Engine (SKE) cluster.",
Short: "Shows details of a SKE cluster",
Long: "Shows details of a STACKIT Kubernetes Engine (SKE) cluster.",
Args: args.SingleArg(clusterNameArg, nil),
Example: examples.Build(
examples.NewExample(
`Get details of a SKE cluster with name "my-cluster"`,
`Get details of an SKE cluster with name "my-cluster"`,
"$ stackit ske cluster describe my-cluster"),
examples.NewExample(
`Get details of a SKE cluster with name "my-cluster" in JSON format`,
`Get details of an SKE cluster with name "my-cluster" in JSON format`,
"$ stackit ske cluster describe my-cluster --output-format json"),

@@ -45,0 +45,0 @@ ),

@@ -40,3 +40,3 @@ package update

Use: fmt.Sprintf("update %s", clusterNameArg),
Short: "Updates a SKE cluster",
Short: "Updates an SKE cluster",
Long: fmt.Sprintf("%s\n%s\n%s",

@@ -50,6 +50,6 @@ "Updates a STACKIT Kubernetes Engine (SKE) cluster.",

examples.NewExample(
`Update a SKE cluster using an API payload sourced from the file "./payload.json"`,
`Update an SKE cluster using an API payload sourced from the file "./payload.json"`,
"$ stackit ske cluster update my-cluster --payload @./payload.json"),
examples.NewExample(
`Update a SKE cluster using an API payload provided as a JSON string`,
`Update an SKE cluster using an API payload provided as a JSON string`,
`$ stackit ske cluster update my-cluster --payload "{...}"`),

@@ -56,0 +56,0 @@ examples.NewExample(

@@ -47,5 +47,5 @@ package create

Use: fmt.Sprintf("create %s", clusterNameArg),
Short: "Creates or update a kubeconfig for a SKE cluster",
Short: "Creates or update a kubeconfig for an SKE cluster",
Long: fmt.Sprintf("%s\n\n%s\n%s\n%s\n%s",
"Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster, if the config exists in the kubeconfig file the information will be updated.",
"Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster, if the config exits in the kubeconfig file the information will be updated.",
"By default, the kubeconfig information of the SKE cluster is merged into the default kubeconfig file of the current user. If the kubeconfig file doesn't exist, a new one will be created.",

@@ -52,0 +52,0 @@ "You can override this behavior by specifying a custom filepath with the --filepath flag.\n",

@@ -31,9 +31,9 @@ package ske

func addSubcommands(cmd *cobra.Command, params *params.CmdParams) {
cmd.AddCommand(cluster.NewCmd(params))
cmd.AddCommand(credentials.NewCmd(params))
cmd.AddCommand(describe.NewCmd(params))
cmd.AddCommand(disable.NewCmd(params))
cmd.AddCommand(enable.NewCmd(params))
cmd.AddCommand(kubeconfig.NewCmd(params))
cmd.AddCommand(disable.NewCmd(params))
cmd.AddCommand(cluster.NewCmd(params))
cmd.AddCommand(credentials.NewCmd(params))
cmd.AddCommand(options.NewCmd(params))
}
## stackit ske cluster hibernate
Trigger hibernate for a SKE cluster
### Synopsis
Trigger hibernate for a STACKIT Kubernetes Engine (SKE) cluster.
```
stackit ske cluster hibernate CLUSTER_NAME [flags]
```
### Examples
```
Trigger hibernate for a SKE cluster with name "my-cluster"
$ stackit ske cluster hibernate my-cluster
```
### Options
```
-h, --help Help for "stackit ske cluster hibernate"
```
### Options inherited from parent commands
```
-y, --assume-yes If set, skips all confirmation prompts
--async If set, runs the command asynchronously
-o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"]
-p, --project-id string Project ID
--region string Target region for region-specific requests
--verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
```
### SEE ALSO
* [stackit ske cluster](./stackit_ske_cluster.md) - Provides functionality for SKE cluster
## stackit ske cluster maintenance
Trigger maintenance for a SKE cluster
### Synopsis
Trigger maintenance for a STACKIT Kubernetes Engine (SKE) cluster.
```
stackit ske cluster maintenance CLUSTER_NAME [flags]
```
### Examples
```
Trigger maintenance for a SKE cluster with name "my-cluster"
$ stackit ske cluster maintenance my-cluster
```
### Options
```
-h, --help Help for "stackit ske cluster maintenance"
```
### Options inherited from parent commands
```
-y, --assume-yes If set, skips all confirmation prompts
--async If set, runs the command asynchronously
-o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"]
-p, --project-id string Project ID
--region string Target region for region-specific requests
--verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
```
### SEE ALSO
* [stackit ske cluster](./stackit_ske_cluster.md) - Provides functionality for SKE cluster
## stackit ske cluster reconcile
Trigger reconcile for a SKE cluster
### Synopsis
Trigger reconcile for a STACKIT Kubernetes Engine (SKE) cluster.
```
stackit ske cluster reconcile CLUSTER_NAME [flags]
```
### Examples
```
Trigger reconcile for a SKE cluster with name "my-cluster"
$ stackit ske cluster reconcile my-cluster
```
### Options
```
-h, --help Help for "stackit ske cluster reconcile"
```
### Options inherited from parent commands
```
-y, --assume-yes If set, skips all confirmation prompts
--async If set, runs the command asynchronously
-o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"]
-p, --project-id string Project ID
--region string Target region for region-specific requests
--verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
```
### SEE ALSO
* [stackit ske cluster](./stackit_ske_cluster.md) - Provides functionality for SKE cluster
## stackit ske cluster wakeup
Trigger wakeup from hibernation for a SKE cluster
### Synopsis
Trigger wakeup from hibernation for a STACKIT Kubernetes Engine (SKE) cluster.
```
stackit ske cluster wakeup CLUSTER_NAME [flags]
```
### Examples
```
Trigger wakeup from hibernation for a SKE cluster with name "my-cluster"
$ stackit ske cluster wakeup my-cluster
```
### Options
```
-h, --help Help for "stackit ske cluster wakeup"
```
### Options inherited from parent commands
```
-y, --assume-yes If set, skips all confirmation prompts
--async If set, runs the command asynchronously
-o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"]
-p, --project-id string Project ID
--region string Target region for region-specific requests
--verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info")
```
### SEE ALSO
* [stackit ske cluster](./stackit_ske_cluster.md) - Provides functionality for SKE cluster
package hibernate
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/uuid"
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
)
type testCtxKey struct{}
const (
testRegion = "eu01"
testClusterName = "my-cluster"
)
var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo")
var testClient = &ske.APIClient{}
var testProjectId = uuid.NewString()
func fixtureArgValues(mods ...func(argValues []string)) []string {
argValues := []string{
testClusterName,
}
for _, mod := range mods {
mod(argValues)
}
return argValues
}
func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
flagValues := map[string]string{
globalflags.ProjectIdFlag: testProjectId,
globalflags.RegionFlag: testRegion,
}
for _, mod := range mods {
mod(flagValues)
}
return flagValues
}
func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
model := &inputModel{
GlobalFlagModel: &globalflags.GlobalFlagModel{
ProjectId: testProjectId,
Region: testRegion,
Verbosity: globalflags.VerbosityDefault,
},
ClusterName: testClusterName,
}
for _, mod := range mods {
mod(model)
}
return model
}
func fixtureRequest(mods ...func(request *ske.ApiTriggerHibernateRequest)) ske.ApiTriggerHibernateRequest {
request := testClient.TriggerHibernate(testCtx, testProjectId, testRegion, testClusterName)
for _, mod := range mods {
mod(&request)
}
return request
}
func TestParseInput(t *testing.T) {
tests := []struct {
description string
argValues []string
flagValues map[string]string
isValid bool
expectedModel *inputModel
}{
{
description: "base",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(),
isValid: true,
expectedModel: fixtureInputModel(),
},
{
description: "missing project id",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
delete(fv, globalflags.ProjectIdFlag)
}),
isValid: false,
},
{
description: "invalid project id - empty string",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
fv[globalflags.ProjectIdFlag] = ""
}),
isValid: false,
},
{
description: "invalid uuid format",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
fv[globalflags.ProjectIdFlag] = "not-a-uuid"
}),
isValid: false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
p := print.NewPrinter()
cmd := &cobra.Command{}
err := globalflags.Configure(cmd.Flags())
if err != nil {
t.Fatalf("configure global flags: %v", err)
}
for flag, value := range tt.flagValues {
err := cmd.Flags().Set(flag, value)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
}
}
if len(tt.argValues) == 0 {
_, err := parseInput(p, cmd, tt.argValues)
if err == nil && !tt.isValid {
t.Fatalf("expected error due to missing args")
}
return
}
model, err := parseInput(p, cmd, tt.argValues)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("error parsing flags: %v", err)
}
if !tt.isValid {
t.Fatalf("did not fail on invalid input")
}
diff := cmp.Diff(model, tt.expectedModel)
if diff != "" {
t.Fatalf("data does not match:\n%s", diff)
}
})
}
}
func TestBuildRequest(t *testing.T) {
tests := []struct {
description string
model *inputModel
expectedRequest ske.ApiTriggerHibernateRequest
}{
{
description: "base",
model: fixtureInputModel(),
expectedRequest: fixtureRequest(),
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
request := buildRequest(testCtx, tt.model, testClient)
diff := cmp.Diff(request, tt.expectedRequest,
cmpopts.EquateComparable(testCtx),
cmp.AllowUnexported(tt.expectedRequest),
)
if diff != "" {
t.Fatalf("request mismatch:\n%s", diff)
}
})
}
}
package hibernate
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
"github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client"
"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
"github.com/stackitcloud/stackit-sdk-go/services/ske/wait"
)
const (
clusterNameArg = "CLUSTER_NAME"
)
type inputModel struct {
*globalflags.GlobalFlagModel
ClusterName string
}
func NewCmd(params *params.CmdParams) *cobra.Command {
cmd := &cobra.Command{
Use: fmt.Sprintf("hibernate %s", clusterNameArg),
Short: "Trigger hibernate for a SKE cluster",
Long: "Trigger hibernate for a STACKIT Kubernetes Engine (SKE) cluster.",
Args: args.SingleArg(clusterNameArg, nil),
Example: examples.Build(
examples.NewExample(
`Trigger hibernate for a SKE cluster with name "my-cluster"`,
"$ stackit ske cluster hibernate my-cluster"),
),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
model, err := parseInput(params.Printer, cmd, args)
if err != nil {
return err
}
// Configure API client
apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
if err != nil {
return err
}
projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd)
if err != nil {
params.Printer.Debug(print.ErrorLevel, "get project name: %v", err)
projectLabel = model.ProjectId
}
if !model.AssumeYes {
prompt := fmt.Sprintf("Are you sure you want to trigger hibernate for %q in project %q?", model.ClusterName, projectLabel)
err = params.Printer.PromptForConfirmation(prompt)
if err != nil {
return err
}
}
// Call API
req := buildRequest(ctx, model, apiClient)
_, err = req.Execute()
if err != nil {
return fmt.Errorf("hibernate SKE cluster: %w", err)
}
// Wait for async operation, if async mode not enabled
if !model.Async {
s := spinner.New(params.Printer)
s.Start("Hibernating cluster")
_, err = wait.TriggerClusterHibernationWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.ClusterName).WaitWithContext(ctx)
if err != nil {
return fmt.Errorf("wait for SKE cluster hibernation: %w", err)
}
s.Stop()
}
operationState := "Hibernated"
if model.Async {
operationState = "Triggered hibernation of"
}
params.Printer.Outputf("%s cluster %q\n", operationState, model.ClusterName)
return nil
},
}
return cmd
}
func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
clusterName := inputArgs[0]
globalFlags := globalflags.Parse(p, cmd)
if globalFlags.ProjectId == "" {
return nil, &errors.ProjectIdError{}
}
model := inputModel{
GlobalFlagModel: globalFlags,
ClusterName: clusterName,
}
if p.IsVerbosityDebug() {
modelStr, err := print.BuildDebugStrFromInputModel(model)
if err != nil {
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
} else {
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
}
}
return &model, nil
}
func buildRequest(ctx context.Context, model *inputModel, apiClient *ske.APIClient) ske.ApiTriggerHibernateRequest {
req := apiClient.TriggerHibernate(ctx, model.ProjectId, model.Region, model.ClusterName)
return req
}
package maintenance
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/uuid"
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
)
type testCtxKey struct{}
const (
testRegion = "eu01"
testClusterName = "my-cluster"
)
var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo")
var testClient = &ske.APIClient{}
var testProjectId = uuid.NewString()
func fixtureArgValues(mods ...func([]string)) []string {
argValues := []string{
testClusterName,
}
for _, m := range mods {
m(argValues)
}
return argValues
}
func fixtureFlagValues(mods ...func(map[string]string)) map[string]string {
flagValues := map[string]string{
globalflags.ProjectIdFlag: testProjectId,
globalflags.RegionFlag: testRegion,
}
for _, m := range mods {
m(flagValues)
}
return flagValues
}
func fixtureInputModel(mods ...func(*inputModel)) *inputModel {
model := &inputModel{
GlobalFlagModel: &globalflags.GlobalFlagModel{
ProjectId: testProjectId,
Region: testRegion,
Verbosity: globalflags.VerbosityDefault,
},
ClusterName: testClusterName,
}
for _, m := range mods {
m(model)
}
return model
}
func fixtureRequest(mods ...func(*ske.ApiTriggerMaintenanceRequest)) ske.ApiTriggerMaintenanceRequest {
request := testClient.TriggerMaintenance(testCtx, testProjectId, testRegion, testClusterName)
for _, m := range mods {
m(&request)
}
return request
}
func TestParseInput(t *testing.T) {
tests := []struct {
description string
argValues []string
flagValues map[string]string
isValid bool
expectedModel *inputModel
}{
{
description: "base",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(),
isValid: true,
expectedModel: fixtureInputModel(),
},
{
description: "missing project id",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
delete(fv, globalflags.ProjectIdFlag)
}),
isValid: false,
},
{
description: "invalid project id - empty string",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
fv[globalflags.ProjectIdFlag] = ""
}),
isValid: false,
},
{
description: "invalid uuid format",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
fv[globalflags.ProjectIdFlag] = "not-a-uuid"
}),
isValid: false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
p := print.NewPrinter()
cmd := &cobra.Command{}
err := globalflags.Configure(cmd.Flags())
if err != nil {
t.Fatalf("configure global flags: %v", err)
}
for flag, value := range tt.flagValues {
err := cmd.Flags().Set(flag, value)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
}
}
if len(tt.argValues) == 0 {
_, err := parseInput(p, cmd, tt.argValues)
if err == nil && !tt.isValid {
t.Fatalf("expected error due to missing args")
}
return
}
model, err := parseInput(p, cmd, tt.argValues)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("error parsing input: %v", err)
}
if !tt.isValid {
t.Fatalf("did not fail on invalid input")
}
diff := cmp.Diff(model, tt.expectedModel)
if diff != "" {
t.Fatalf("input model mismatch:\n%s", diff)
}
})
}
}
func TestBuildRequest(t *testing.T) {
tests := []struct {
description string
model *inputModel
expectedRequest ske.ApiTriggerMaintenanceRequest
}{
{
description: "base",
model: fixtureInputModel(),
expectedRequest: fixtureRequest(),
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
got := buildRequest(testCtx, tt.model, testClient)
want := tt.expectedRequest
diff := cmp.Diff(got, want,
cmpopts.EquateComparable(testCtx),
cmp.AllowUnexported(want),
)
if diff != "" {
t.Fatalf("request mismatch:\n%s", diff)
}
})
}
}
package maintenance
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-cli/internal/pkg/projectname"
"github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client"
"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
"github.com/stackitcloud/stackit-sdk-go/services/ske/wait"
)
const (
clusterNameArg = "CLUSTER_NAME"
)
type inputModel struct {
*globalflags.GlobalFlagModel
ClusterName string
}
func NewCmd(params *params.CmdParams) *cobra.Command {
cmd := &cobra.Command{
Use: fmt.Sprintf("maintenance %s", clusterNameArg),
Short: "Trigger maintenance for a SKE cluster",
Long: "Trigger maintenance for a STACKIT Kubernetes Engine (SKE) cluster.",
Args: args.SingleArg(clusterNameArg, nil),
Example: examples.Build(
examples.NewExample(
`Trigger maintenance for a SKE cluster with name "my-cluster"`,
"$ stackit ske cluster maintenance my-cluster"),
),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
model, err := parseInput(params.Printer, cmd, args)
if err != nil {
return err
}
// Configure API client
apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
if err != nil {
return err
}
projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd)
if err != nil {
params.Printer.Debug(print.ErrorLevel, "get project name: %v", err)
projectLabel = model.ProjectId
}
if !model.AssumeYes {
prompt := fmt.Sprintf("Are you sure you want to trigger maintenance for %q in project %q?", model.ClusterName, projectLabel)
err = params.Printer.PromptForConfirmation(prompt)
if err != nil {
return err
}
}
// Call API
req := buildRequest(ctx, model, apiClient)
_, err = req.Execute()
if err != nil {
return fmt.Errorf("trigger maintenance SKE cluster: %w", err)
}
// Wait for async operation, if async mode not enabled
if !model.Async {
s := spinner.New(params.Printer)
s.Start("Performing cluster maintenance")
_, err = wait.TriggerClusterMaintenanceWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.ClusterName).WaitWithContext(ctx)
if err != nil {
return fmt.Errorf("wait for SKE cluster maintenance to complete: %w", err)
}
s.Stop()
}
operationState := "Performed maintenance for"
if model.Async {
operationState = "Triggered maintenance for"
}
params.Printer.Outputf("%s cluster %q\n", operationState, model.ClusterName)
return nil
},
}
return cmd
}
func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
clusterName := inputArgs[0]
globalFlags := globalflags.Parse(p, cmd)
if globalFlags.ProjectId == "" {
return nil, &errors.ProjectIdError{}
}
model := inputModel{
GlobalFlagModel: globalFlags,
ClusterName: clusterName,
}
if p.IsVerbosityDebug() {
modelStr, err := print.BuildDebugStrFromInputModel(model)
if err != nil {
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
} else {
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
}
}
return &model, nil
}
func buildRequest(ctx context.Context, model *inputModel, apiClient *ske.APIClient) ske.ApiTriggerMaintenanceRequest {
req := apiClient.TriggerMaintenance(ctx, model.ProjectId, model.Region, model.ClusterName)
return req
}
package reconcile
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/uuid"
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
)
type testCtxKey struct{}
const (
testRegion = "eu01"
testClusterName = "my-cluster"
)
var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo")
var testClient = &ske.APIClient{}
var testProjectId = uuid.NewString()
func fixtureArgValues(mods ...func([]string)) []string {
argValues := []string{
testClusterName,
}
for _, m := range mods {
m(argValues)
}
return argValues
}
func fixtureFlagValues(mods ...func(map[string]string)) map[string]string {
flagValues := map[string]string{
globalflags.ProjectIdFlag: testProjectId,
globalflags.RegionFlag: testRegion,
}
for _, m := range mods {
m(flagValues)
}
return flagValues
}
func fixtureInputModel(mods ...func(*inputModel)) *inputModel {
model := &inputModel{
GlobalFlagModel: &globalflags.GlobalFlagModel{
ProjectId: testProjectId,
Region: testRegion,
Verbosity: globalflags.VerbosityDefault,
},
ClusterName: testClusterName,
}
for _, m := range mods {
m(model)
}
return model
}
func fixtureRequest(mods ...func(request *ske.ApiTriggerReconcileRequest)) ske.ApiTriggerHibernateRequest {
request := testClient.TriggerReconcile(testCtx, testProjectId, testRegion, testClusterName)
for _, m := range mods {
m(&request)
}
return request
}
func TestParseInput(t *testing.T) {
tests := []struct {
description string
argValues []string
flagValues map[string]string
isValid bool
expectedModel *inputModel
}{
{
description: "base",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(),
isValid: true,
expectedModel: fixtureInputModel(),
},
{
description: "missing project id",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
delete(fv, globalflags.ProjectIdFlag)
}),
isValid: false,
},
{
description: "invalid project id - empty string",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
fv[globalflags.ProjectIdFlag] = ""
}),
isValid: false,
},
{
description: "invalid uuid format",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
fv[globalflags.ProjectIdFlag] = "not-a-uuid"
}),
isValid: false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
p := print.NewPrinter()
cmd := &cobra.Command{}
err := globalflags.Configure(cmd.Flags())
if err != nil {
t.Fatalf("configure global flags: %v", err)
}
for flag, value := range tt.flagValues {
err := cmd.Flags().Set(flag, value)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
}
}
if len(tt.argValues) == 0 {
_, err := parseInput(p, cmd, tt.argValues)
if err == nil && !tt.isValid {
t.Fatalf("expected error due to missing args")
}
return
}
model, err := parseInput(p, cmd, tt.argValues)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("error parsing input: %v", err)
}
if !tt.isValid {
t.Fatalf("did not fail on invalid input")
}
diff := cmp.Diff(model, tt.expectedModel)
if diff != "" {
t.Fatalf("input model mismatch:\n%s", diff)
}
})
}
}
func TestBuildRequest(t *testing.T) {
tests := []struct {
description string
model *inputModel
expectedRequest ske.ApiTriggerHibernateRequest
}{
{
description: "base",
model: fixtureInputModel(),
expectedRequest: fixtureRequest(),
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
got := buildRequest(testCtx, tt.model, testClient)
want := tt.expectedRequest
diff := cmp.Diff(got, want,
cmpopts.EquateComparable(testCtx),
cmp.AllowUnexported(want),
)
if diff != "" {
t.Fatalf("request mismatch:\n%s", diff)
}
})
}
}
package reconcile
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client"
"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
"github.com/stackitcloud/stackit-sdk-go/services/ske/wait"
)
const (
clusterNameArg = "CLUSTER_NAME"
)
type inputModel struct {
*globalflags.GlobalFlagModel
ClusterName string
}
func NewCmd(params *params.CmdParams) *cobra.Command {
cmd := &cobra.Command{
Use: fmt.Sprintf("reconcile %s", clusterNameArg),
Short: "Trigger reconcile for a SKE cluster",
Long: "Trigger reconcile for a STACKIT Kubernetes Engine (SKE) cluster.",
Args: args.SingleArg(clusterNameArg, nil),
Example: examples.Build(
examples.NewExample(
`Trigger reconcile for a SKE cluster with name "my-cluster"`,
"$ stackit ske cluster reconcile my-cluster"),
),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
model, err := parseInput(params.Printer, cmd, args)
if err != nil {
return err
}
// Configure API client
apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
if err != nil {
return err
}
// Call API
req := buildRequest(ctx, model, apiClient)
_, err = req.Execute()
if err != nil {
return fmt.Errorf("reconcile SKE cluster: %w", err)
}
// Wait for async operation, if async mode not enabled
if !model.Async {
s := spinner.New(params.Printer)
s.Start("Performing cluster reconciliation")
_, err = wait.TriggerClusterReconciliationWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.ClusterName).WaitWithContext(ctx)
if err != nil {
return fmt.Errorf("wait for SKE cluster reconciliation: %w", err)
}
s.Stop()
}
operationState := "Performed reconciliation for"
if model.Async {
operationState = "Triggered reconcile for"
}
params.Printer.Outputf("%s cluster %q\n", operationState, model.ClusterName)
return nil
},
}
return cmd
}
func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
clusterName := inputArgs[0]
globalFlags := globalflags.Parse(p, cmd)
if globalFlags.ProjectId == "" {
return nil, &errors.ProjectIdError{}
}
model := inputModel{
GlobalFlagModel: globalFlags,
ClusterName: clusterName,
}
if p.IsVerbosityDebug() {
modelStr, err := print.BuildDebugStrFromInputModel(model)
if err != nil {
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
} else {
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
}
}
return &model, nil
}
func buildRequest(ctx context.Context, model *inputModel, apiClient *ske.APIClient) ske.ApiTriggerReconcileRequest {
req := apiClient.TriggerReconcile(ctx, model.ProjectId, model.Region, model.ClusterName)
return req
}
package wakeup
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/uuid"
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
)
type testCtxKey struct{}
const (
testRegion = "eu01"
testClusterName = "my-cluster"
)
var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo")
var testClient = &ske.APIClient{}
var testProjectId = uuid.NewString()
func fixtureArgValues(mods ...func([]string)) []string {
argValues := []string{testClusterName}
for _, m := range mods {
m(argValues)
}
return argValues
}
func fixtureFlagValues(mods ...func(map[string]string)) map[string]string {
flags := map[string]string{
globalflags.ProjectIdFlag: testProjectId,
globalflags.RegionFlag: testRegion,
}
for _, m := range mods {
m(flags)
}
return flags
}
func fixtureInputModel(mods ...func(*inputModel)) *inputModel {
model := &inputModel{
GlobalFlagModel: &globalflags.GlobalFlagModel{
ProjectId: testProjectId,
Region: testRegion,
Verbosity: globalflags.VerbosityDefault,
},
ClusterName: testClusterName,
}
for _, m := range mods {
m(model)
}
return model
}
func fixtureRequest(mods ...func(*ske.ApiTriggerWakeupRequest)) ske.ApiTriggerWakeupRequest {
req := testClient.TriggerWakeup(testCtx, testProjectId, testRegion, testClusterName)
for _, m := range mods {
m(&req)
}
return req
}
func TestParseInput(t *testing.T) {
tests := []struct {
description string
argValues []string
flagValues map[string]string
isValid bool
expectedModel *inputModel
}{
{
description: "base",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(),
isValid: true,
expectedModel: fixtureInputModel(),
},
{
description: "missing project id",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
delete(fv, globalflags.ProjectIdFlag)
}),
isValid: false,
},
{
description: "invalid project id - empty string",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
fv[globalflags.ProjectIdFlag] = ""
}),
isValid: false,
},
{
description: "invalid uuid format",
argValues: fixtureArgValues(),
flagValues: fixtureFlagValues(func(fv map[string]string) {
fv[globalflags.ProjectIdFlag] = "not-a-uuid"
}),
isValid: false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
p := print.NewPrinter()
cmd := &cobra.Command{}
err := globalflags.Configure(cmd.Flags())
if err != nil {
t.Fatalf("configure global flags: %v", err)
}
for flag, value := range tt.flagValues {
err := cmd.Flags().Set(flag, value)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
}
}
if len(tt.argValues) == 0 {
_, err := parseInput(p, cmd, tt.argValues)
if err == nil && !tt.isValid {
t.Fatalf("expected failure due to missing args")
}
return
}
model, err := parseInput(p, cmd, tt.argValues)
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("unexpected error: %v", err)
}
if !tt.isValid {
t.Fatalf("did not fail on invalid input")
}
diff := cmp.Diff(model, tt.expectedModel)
if diff != "" {
t.Fatalf("input model mismatch:\n%s", diff)
}
})
}
}
func TestBuildRequest(t *testing.T) {
tests := []struct {
description string
model *inputModel
expectedRequest ske.ApiTriggerHibernateRequest
}{
{
description: "base",
model: fixtureInputModel(),
expectedRequest: fixtureRequest(),
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
got := buildRequest(testCtx, tt.model, testClient)
want := tt.expectedRequest
diff := cmp.Diff(got, want,
cmpopts.EquateComparable(testCtx),
cmp.AllowUnexported(want),
)
if diff != "" {
t.Fatalf("request mismatch:\n%s", diff)
}
})
}
}
package wakeup
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
"github.com/stackitcloud/stackit-cli/internal/pkg/args"
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
"github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client"
"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
"github.com/stackitcloud/stackit-sdk-go/services/ske"
"github.com/stackitcloud/stackit-sdk-go/services/ske/wait"
)
const (
clusterNameArg = "CLUSTER_NAME"
)
type inputModel struct {
*globalflags.GlobalFlagModel
ClusterName string
}
func NewCmd(params *params.CmdParams) *cobra.Command {
cmd := &cobra.Command{
Use: fmt.Sprintf("wakeup %s", clusterNameArg),
Short: "Trigger wakeup from hibernation for a SKE cluster",
Long: "Trigger wakeup from hibernation for a STACKIT Kubernetes Engine (SKE) cluster.",
Args: args.SingleArg(clusterNameArg, nil),
Example: examples.Build(
examples.NewExample(
`Trigger wakeup from hibernation for a SKE cluster with name "my-cluster"`,
"$ stackit ske cluster wakeup my-cluster"),
),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
model, err := parseInput(params.Printer, cmd, args)
if err != nil {
return err
}
// Configure API client
apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion)
if err != nil {
return err
}
// Call API
req := buildRequest(ctx, model, apiClient)
_, err = req.Execute()
if err != nil {
return fmt.Errorf("wakeup SKE cluster: %w", err)
}
// Wait for async operation, if async mode not enabled
if !model.Async {
s := spinner.New(params.Printer)
s.Start("Performing cluster wakeup")
_, err = wait.TriggerClusterWakeupWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.ClusterName).WaitWithContext(ctx)
if err != nil {
return fmt.Errorf("wait for SKE cluster wakeup: %w", err)
}
s.Stop()
}
operationState := "Performed wakeup of"
if model.Async {
operationState = "Triggered wakeup of"
}
params.Printer.Outputf("%s cluster %q\n", operationState, model.ClusterName)
return nil
},
}
return cmd
}
func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
clusterName := inputArgs[0]
globalFlags := globalflags.Parse(p, cmd)
if globalFlags.ProjectId == "" {
return nil, &errors.ProjectIdError{}
}
model := inputModel{
GlobalFlagModel: globalFlags,
ClusterName: clusterName,
}
if p.IsVerbosityDebug() {
modelStr, err := print.BuildDebugStrFromInputModel(model)
if err != nil {
p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err)
} else {
p.Debug(print.DebugLevel, "parsed input values: %s", modelStr)
}
}
return &model, nil
}
func buildRequest(ctx context.Context, model *inputModel, apiClient *ske.APIClient) ske.ApiTriggerWakeupRequest {
req := apiClient.TriggerWakeup(ctx, model.ProjectId, model.Region, model.ClusterName)
return req
}
#!/bin/bash
# This script is used to publish new packages to the CLI RPM repository
# Usage: ./publish-rpm-packages.sh
set -eo pipefail
ROOT_DIR=$(git rev-parse --show-toplevel)
PACKAGES_BUCKET_URL="https://packages.stackit.cloud"
RPM_REPO_PATH="rpm/cli"
RPM_BUCKET_NAME="distribution"
CUSTOM_KEYRING_FILE="rpm-keyring.gpg"
GORELEASER_PACKAGES_FOLDER="dist/"
TEMP_DIR=$(mktemp -d)
# We need to disable the key database daemon (keyboxd)
# This can be done by removing "use-keyboxd" from ~/.gnupg/common.conf (see https://github.com/gpg/gnupg/blob/master/README)
echo -n >~/.gnupg/common.conf
# Create a local mirror of the current state of the remote RPM repository
printf ">>> Creating mirror \n"
curl ${PACKAGES_BUCKET_URL}/${RPM_REPO_PATH}/repodata/repomd.xml >${TEMP_DIR}/repomd.xml || echo "No existing repository found, creating new one"
# Create RPM repository structure
mkdir -p ${TEMP_DIR}/rpm-repo/RPMS
# Copy existing RPMs from remote repository (if any)
printf "\n>>> Downloading existing RPMs \n"
aws s3 sync s3://${RPM_BUCKET_NAME}/${RPM_REPO_PATH}/RPMS/ ${TEMP_DIR}/rpm-repo/RPMS/ --endpoint-url https://object.storage.eu01.onstackit.cloud || echo "No existing RPMs found"
# Copy new generated .rpm packages to the local repo
# Note: GoReleaser already signs these RPM packages with embedded signatures
printf "\n>>> Adding new packages to local repo \n"
cp ${GORELEASER_PACKAGES_FOLDER}/*.rpm ${TEMP_DIR}/rpm-repo/RPMS/
# Create RPM repository metadata using createrepo_c in Docker
printf "\n>>> Creating RPM repository metadata \n"
docker run --rm \
-v "${TEMP_DIR}/rpm-repo:/repo" \
fedora:latest \
bash -c "
# Install createrepo_c
dnf install -y createrepo_c
# Create repository metadata
createrepo_c /repo
"
# Sign the repository metadata using the same GPG key as APT
if [ -n "$GPG_PRIVATE_KEY_FINGERPRINT" ] && [ -n "$GPG_PASSPHRASE" ]; then
printf "\n>>> Signing repository metadata \n"
gpg --batch --yes --pinentry-mode loopback --local-user="${GPG_PRIVATE_KEY_FINGERPRINT}" --passphrase="${GPG_PASSPHRASE}" --detach-sign --armor ${TEMP_DIR}/rpm-repo/repodata/repomd.xml
else
echo ">>> Skipping repository metadata signing (GPG environment variables not set)"
fi
# Upload to S3
printf "\n>>> Uploading to S3 \n"
aws s3 sync ${TEMP_DIR}/rpm-repo/ s3://${RPM_BUCKET_NAME}/${RPM_REPO_PATH}/ --endpoint-url https://object.storage.eu01.onstackit.cloud
# Clean up
rm -rf ${TEMP_DIR}
printf "\n>>> RPM repository published successfully to ${PACKAGES_BUCKET_URL}/${RPM_REPO_PATH} \n"

Sorry, the diff of this file is too big to display