From ec86f44f9d739f15742d11032b1499cf836bc561 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Mon, 22 Dec 2025 17:43:36 +0100 Subject: [PATCH] feat(sks): add IPv6 support in CLI --- CHANGELOG.md | 1 + cmd/compute/sks/sks_create.go | 48 +++++++++++++----------- cmd/compute/sks/sks_nodepool.go | 5 +++ cmd/compute/sks/sks_nodepool_add.go | 51 +++++++++++++++----------- cmd/compute/sks/sks_nodepool_show.go | 7 ++++ cmd/compute/sks/sks_nodepool_update.go | 10 +++++ 6 files changed, 78 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6a9ac7e6..dec64a192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - chores: add git version/commit in user agent header #769 - Prompting for validation before deleting deployments and models (dedicated-inference) - Ability to delete multiple deployments and models at once (dedicated-inference) +- Add IPv6 public IP assignment support for SKS Nodepools #774 ## 1.88.0 diff --git a/cmd/compute/sks/sks_create.go b/cmd/compute/sks/sks_create.go index 89d739475..cdef001bc 100644 --- a/cmd/compute/sks/sks_create.go +++ b/cmd/compute/sks/sks_create.go @@ -58,6 +58,7 @@ type sksCreateCmd struct { NodepoolSecurityGroups []string `cli-flag:"nodepool-security-group" cli-usage:"default Nodepool Security Group NAME|ID (can be specified multiple times)"` NodepoolSize int64 `cli-usage:"default Nodepool size. If 0, no default Nodepool will be added to the cluster."` NodepoolTaints []string `cli-flag:"nodepool-taint" cli-usage:"Kubernetes taint to apply to default Nodepool Nodes (format: KEY=VALUE:EFFECT, can be specified multiple times)"` + NodepoolIPv6 bool `cli-flag:"nodepool-ipv6" cli-usage:"assign IPv6 Public IPs to default Nodepool Nodes"` OIDCClientID string `cli-flag:"oidc-client-id" cli-usage:"OpenID client ID"` OIDCGroupsClaim string `cli-flag:"oidc-groups-claim" cli-usage:"OpenID JWT claim to use as the user's group"` OIDCGroupsPrefix string `cli-flag:"oidc-groups-prefix" cli-usage:"OpenID prefix prepended to group claims"` @@ -224,29 +225,32 @@ func (c *sksCreateCmd) CmdRun(cmd *cobra.Command, _ []string) error { //nolint:g nodepoolName = c.NodepoolName } - nodepoolReq, err := createNodepoolRequest( - ctx, - client, - CreateNodepoolOpts{ - Name: nodepoolName, - Description: c.NodepoolDescription, - DiskSize: c.NodepoolDiskSize, - InstancePrefix: c.NodepoolInstancePrefix, - Size: c.NodepoolSize, - InstanceType: c.NodepoolInstanceType, - Labels: c.NodepoolLabels, - AntiAffinityGroups: c.NodepoolAntiAffinityGroups, - DeployTarget: c.NodepoolDeployTarget, - PrivateNetworks: c.NodepoolPrivateNetworks, - SecurityGroups: c.NodepoolSecurityGroups, - Taints: c.NodepoolTaints, - KubeletImageGC: &v3.KubeletImageGC{ - MinAge: c.NodepoolImageGcMinAge, - LowThreshold: c.NodepoolImageGcLowThreshold, - HighThreshold: c.NodepoolImageGcHighThreshold, - }, + opts := CreateNodepoolOpts{ + Name: nodepoolName, + Description: c.NodepoolDescription, + DiskSize: c.NodepoolDiskSize, + InstancePrefix: c.NodepoolInstancePrefix, + Size: c.NodepoolSize, + InstanceType: c.NodepoolInstanceType, + Labels: c.NodepoolLabels, + AntiAffinityGroups: c.NodepoolAntiAffinityGroups, + DeployTarget: c.NodepoolDeployTarget, + PrivateNetworks: c.NodepoolPrivateNetworks, + SecurityGroups: c.NodepoolSecurityGroups, + Taints: c.NodepoolTaints, + KubeletImageGC: &v3.KubeletImageGC{ + MinAge: c.NodepoolImageGcMinAge, + LowThreshold: c.NodepoolImageGcLowThreshold, + HighThreshold: c.NodepoolImageGcHighThreshold, }, - ) + } + + if c.NodepoolIPv6 { + v := v3.PublicIPAssignmentDual + opts.PublicIPAssignment = &v + } + + nodepoolReq, err := createNodepoolRequest(ctx, client, opts) if err != nil { return err } diff --git a/cmd/compute/sks/sks_nodepool.go b/cmd/compute/sks/sks_nodepool.go index 1fac82bb0..c7bf1901f 100644 --- a/cmd/compute/sks/sks_nodepool.go +++ b/cmd/compute/sks/sks_nodepool.go @@ -60,6 +60,7 @@ type CreateNodepoolOpts struct { SecurityGroups []string Taints []string KubeletImageGC *v3.KubeletImageGC + PublicIPAssignment *v3.PublicIPAssignment } func createNodepoolRequest( @@ -78,6 +79,10 @@ func createNodepoolRequest( KubeletImageGC: opts.KubeletImageGC, } + if opts.PublicIPAssignment != nil { + nodepoolReq.PublicIPAssignment = v3.CreateSKSNodepoolRequestPublicIPAssignment(*opts.PublicIPAssignment) + } + aaGroups, err := lookupAntiAffinityGroups(ctx, client, opts.AntiAffinityGroups) if err != nil { return nodepoolReq, err diff --git a/cmd/compute/sks/sks_nodepool_add.go b/cmd/compute/sks/sks_nodepool_add.go index 54ab388b3..d99eb3289 100644 --- a/cmd/compute/sks/sks_nodepool_add.go +++ b/cmd/compute/sks/sks_nodepool_add.go @@ -43,6 +43,7 @@ type sksNodepoolAddCmd struct { StorageLvm bool `cli-usage:"Create nodes with non-standard partitioning for persistent storage"` Taints []string `cli-flag:"taint" cli-usage:"Kubernetes taint to apply to Nodepool Nodes (format: KEY=VALUE:EFFECT, can be specified multiple times)"` Zone string `cli-short:"z" cli-usage:"SKS cluster zone"` + IPv6 bool `cli-flag:"ipv6" cli-usage:"Enable public IPv6 assignment to Nodepool nodes"` } func (c *sksNodepoolAddCmd) CmdAliases() []string { return nil } @@ -87,29 +88,35 @@ func (c *sksNodepoolAddCmd) CmdRun(_ *cobra.Command, _ []string) error { } } - nodepoolReq, err := createNodepoolRequest( - ctx, - client, - CreateNodepoolOpts{ - Name: c.Name, - Description: c.Description, - DiskSize: c.DiskSize, - InstancePrefix: c.InstancePrefix, - Size: c.Size, - InstanceType: c.InstanceType, - Labels: labels, - AntiAffinityGroups: c.AntiAffinityGroups, - DeployTarget: c.DeployTarget, - PrivateNetworks: c.PrivateNetworks, - SecurityGroups: c.SecurityGroups, - Taints: c.Taints, - KubeletImageGC: &v3.KubeletImageGC{ - MinAge: c.ImageGcMinAge, - LowThreshold: c.ImageGcLowThreshold, - HighThreshold: c.ImageGcHighThreshold, - }, + opts := CreateNodepoolOpts{ + Name: c.Name, + Description: c.Description, + DiskSize: c.DiskSize, + InstancePrefix: c.InstancePrefix, + Size: c.Size, + InstanceType: c.InstanceType, + Labels: labels, + AntiAffinityGroups: c.AntiAffinityGroups, + DeployTarget: c.DeployTarget, + PrivateNetworks: c.PrivateNetworks, + SecurityGroups: c.SecurityGroups, + Taints: c.Taints, + KubeletImageGC: &v3.KubeletImageGC{ + MinAge: c.ImageGcMinAge, + LowThreshold: c.ImageGcLowThreshold, + HighThreshold: c.ImageGcHighThreshold, }, - ) + } + + if c.IPv6 { + v := v3.PublicIPAssignmentDual + opts.PublicIPAssignment = &v + } else { + v := v3.PublicIPAssignmentInet4 + opts.PublicIPAssignment = &v + } + + nodepoolReq, err := createNodepoolRequest(ctx, client, opts) if err != nil { return err } diff --git a/cmd/compute/sks/sks_nodepool_show.go b/cmd/compute/sks/sks_nodepool_show.go index 3d95f6c0d..21b2cc2ff 100644 --- a/cmd/compute/sks/sks_nodepool_show.go +++ b/cmd/compute/sks/sks_nodepool_show.go @@ -24,6 +24,7 @@ type sksNodepoolShowOutput struct { InstanceType string `json:"instance_type"` Template string `json:"template"` DiskSize int64 `json:"disk_size"` + IPv6 bool `json:"ipv6_enabled" outputLabel:"IPv6"` AntiAffinityGroups []string `json:"anti_affinity_groups"` SecurityGroups []string `json:"security_groups"` PrivateNetworks []string `json:"private_networks"` @@ -99,6 +100,11 @@ func (c *sksNodepoolShowCmd) CmdRun(_ *cobra.Command, _ []string) error { return errors.New("nodepool not found") } + ipv6Enabled := false + if nodepool.PublicIPAssignment == v3.SKSNodepoolPublicIPAssignmentDual { + ipv6Enabled = true + } + out := sksNodepoolShowOutput{ AddOns: func() (v []string) { if nodepool.Addons != nil { @@ -124,6 +130,7 @@ func (c *sksNodepoolShowCmd) CmdRun(_ *cobra.Command, _ []string) error { PrivateNetworks: make([]string, 0), Size: nodepool.Size, State: string(nodepool.State), + IPv6: ipv6Enabled, Taints: func() (v []string) { if nodepool.Taints != nil { v = make([]string, 0) diff --git a/cmd/compute/sks/sks_nodepool_update.go b/cmd/compute/sks/sks_nodepool_update.go index b4b872cf9..52ab9b90c 100644 --- a/cmd/compute/sks/sks_nodepool_update.go +++ b/cmd/compute/sks/sks_nodepool_update.go @@ -34,6 +34,7 @@ type sksNodepoolUpdateCmd struct { SecurityGroups []string `cli-flag:"security-group" cli-usage:"Nodepool Security Group NAME|ID (can be specified multiple times)"` Taints []string `cli-flag:"taint" cli-usage:"Kubernetes taint to apply to Nodepool Nodes (format: KEY=VALUE:EFFECT, can be specified multiple times)"` Zone v3.ZoneName `cli-short:"z" cli-usage:"SKS cluster zone"` + IPv6 bool `cli-flag:"ipv6" cli-usage:"Enable public IPv6 assignment to Nodepool nodes"` } func (c *sksNodepoolUpdateCmd) CmdAliases() []string { return nil } @@ -191,6 +192,15 @@ func (c *sksNodepoolUpdateCmd) CmdRun(cmd *cobra.Command, _ []string) error { // updated = true } + if cmd.Flags().Changed(exocmd.MustCLICommandFlagName(c, &c.IPv6)) { + if c.IPv6 { + updateReq.PublicIPAssignment = v3.UpdateSKSNodepoolRequestPublicIPAssignmentDual + } else { + updateReq.PublicIPAssignment = v3.UpdateSKSNodepoolRequestPublicIPAssignmentInet4 + } + updated = true + } + if updated { op, err := client.UpdateSKSNodepool(ctx, cluster.ID, nodepool.ID, updateReq) utils.DecorateAsyncOperation(fmt.Sprintf("Updating Nodepool %q...", c.Nodepool), func() {