Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

### Features

- sks: add `rotate-karpenter-credentials` command
- sks: add `active-nodepool-templates` command

### Bug fixes

- Fix panic when config commands are used without a default account set #798
Expand Down
114 changes: 114 additions & 0 deletions cmd/compute/sks/sks_active_nodepool_templates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package sks

import (
"errors"
"fmt"
"strings"

"github.com/spf13/cobra"

exocmd "github.com/exoscale/cli/cmd"
"github.com/exoscale/cli/pkg/globalstate"
"github.com/exoscale/cli/pkg/output"
v3 "github.com/exoscale/egoscale/v3"
)

type sksActiveNodepoolTemplateOutput struct {
KubeVersion string `json:"kube_version"`
Variant string `json:"variant"`
TemplateID v3.UUID `json:"template_id"`
Template string `json:"template"`
}

type sksActiveNodepoolTemplatesOutput []sksActiveNodepoolTemplateOutput

func (o *sksActiveNodepoolTemplatesOutput) ToJSON() { output.JSON(o) }
func (o *sksActiveNodepoolTemplatesOutput) ToText() { output.Text(o) }
func (o *sksActiveNodepoolTemplatesOutput) ToTable() { output.Table(o) }

type sksActiveNodepoolTemplatesCmd struct {
exocmd.CliCommandSettings `cli-cmd:"-"`

_ bool `cli-cmd:"active-nodepool-templates"`

KubeVersion string `cli-arg:"#" cli-usage:"KUBERNETES-VERSION"`
Variant string `cli-flag:"variant" cli-usage:"nodepool template variant to resolve (standard|nvidia)"`
Zone v3.ZoneName `cli-short:"z" cli-usage:"zone to query in"`
}

func (c *sksActiveNodepoolTemplatesCmd) CmdAliases() []string {
return []string{"active-nodepool-template"}
}

func (c *sksActiveNodepoolTemplatesCmd) CmdShort() string {
return "Find active SKS nodepool templates for a Kubernetes version"
}

func (c *sksActiveNodepoolTemplatesCmd) CmdLong() string {
return fmt.Sprintf(`This command finds active SKS nodepool templates for a given Kubernetes version.

By default, both "standard" and "nvidia" variants are queried.

Supported output template annotations: %s`,
strings.Join(output.TemplateAnnotations(&sksActiveNodepoolTemplateOutput{}), ", "))
}

func (c *sksActiveNodepoolTemplatesCmd) CmdPreRun(cmd *cobra.Command, args []string) error {
exocmd.CmdSetZoneFlagFromDefault(cmd)
return exocmd.CliCommandDefaultPreRun(c, cmd, args)
}

func (c *sksActiveNodepoolTemplatesCmd) CmdRun(_ *cobra.Command, _ []string) error {
ctx := exocmd.GContext
client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, c.Zone)
if err != nil {
return err
}

var variants []v3.GetActiveNodepoolTemplateVariant
switch c.Variant {
case "":
variants = []v3.GetActiveNodepoolTemplateVariant{
v3.GetActiveNodepoolTemplateVariantStandard,
v3.GetActiveNodepoolTemplateVariantNvidia,
}
case string(v3.GetActiveNodepoolTemplateVariantStandard):
variants = []v3.GetActiveNodepoolTemplateVariant{v3.GetActiveNodepoolTemplateVariantStandard}
case string(v3.GetActiveNodepoolTemplateVariantNvidia):
variants = []v3.GetActiveNodepoolTemplateVariant{v3.GetActiveNodepoolTemplateVariantNvidia}
default:
return errors.New(`invalid variant, must be one of: "standard", "nvidia"`)
}

out := make(sksActiveNodepoolTemplatesOutput, 0, len(variants))
for _, variant := range variants {
activeTemplate, err := client.GetActiveNodepoolTemplate(ctx, c.KubeVersion, variant)
if err != nil {
return fmt.Errorf("error retrieving active %q nodepool template: %w", variant, err)
}

if activeTemplate.ActiveTemplate == "" {
return fmt.Errorf("no active template returned for variant %q", variant)
}

template, err := client.GetTemplate(ctx, activeTemplate.ActiveTemplate)
if err != nil {
return fmt.Errorf("error retrieving template details for %q: %w", activeTemplate.ActiveTemplate, err)
}

out = append(out, sksActiveNodepoolTemplateOutput{
KubeVersion: c.KubeVersion,
Variant: string(variant),
TemplateID: template.ID,
Template: template.Name,
})
}

return c.OutputFunc(&out, nil)
}

func init() {
cobra.CheckErr(exocmd.RegisterCLICommand(sksCmd, &sksActiveNodepoolTemplatesCmd{
CliCommandSettings: exocmd.DefaultCLICmdSettings(),
}))
}
73 changes: 73 additions & 0 deletions cmd/compute/sks/sks_rotate_karpenter_credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package sks

import (
"fmt"

"github.com/spf13/cobra"

exocmd "github.com/exoscale/cli/cmd"
"github.com/exoscale/cli/pkg/globalstate"
"github.com/exoscale/cli/utils"
v3 "github.com/exoscale/egoscale/v3"
)

type sksRotateKarpenterCredentialsCmd struct {
exocmd.CliCommandSettings `cli-cmd:"-"`

_ bool `cli-cmd:"rotate-karpenter-credentials"`

Cluster string `cli-arg:"#" cli-usage:"CLUSTER-NAME|ID"`
Zone v3.ZoneName `cli-flag:"zone" cli-short:"z" cli-usage:"SKS cluster zone"`
}

func (c *sksRotateKarpenterCredentialsCmd) CmdAliases() []string { return nil }

func (c *sksRotateKarpenterCredentialsCmd) CmdShort() string {
return "Rotate the Exoscale Karpenter IAM credentials for an SKS cluster"
}

func (c *sksRotateKarpenterCredentialsCmd) CmdLong() string {
return `This command rotates the Exoscale IAM credentials managed by the SKS control
plane for the Kubernetes Karpenter addon.
`
}

func (c *sksRotateKarpenterCredentialsCmd) CmdPreRun(cmd *cobra.Command, args []string) error {
exocmd.CmdSetZoneFlagFromDefault(cmd)
return exocmd.CliCommandDefaultPreRun(c, cmd, args)
}

func (c *sksRotateKarpenterCredentialsCmd) CmdRun(_ *cobra.Command, _ []string) error {
ctx := exocmd.GContext
client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, c.Zone)
if err != nil {
return err
}

resp, err := client.ListSKSClusters(ctx)
if err != nil {
return err
}

cluster, err := resp.FindSKSCluster(c.Cluster)
if err != nil {
return err
}

op, err := client.RotateSKSKarpenterCredentials(ctx, cluster.ID)
if err != nil {
return err
}

utils.DecorateAsyncOperation(fmt.Sprintf("Rotating SKS cluster %q Exoscale Karpenter credentials...", c.Cluster), func() {
_, err = client.Wait(ctx, op, v3.OperationStateSuccess)
})

return err
}

func init() {
cobra.CheckErr(exocmd.RegisterCLICommand(sksCmd, &sksRotateKarpenterCredentialsCmd{
CliCommandSettings: exocmd.DefaultCLICmdSettings(),
}))
}