From 8dcb63301f6e6ea19529a34ac684c2e98c130eb6 Mon Sep 17 00:00:00 2001 From: Peter Matseykanets Date: Tue, 23 Jun 2026 18:14:18 -0400 Subject: [PATCH 1/3] Remove RKE1 related code RKE1 types (NodePool, RancherKubernetesEngineConfig, NetworkConfig) were removed from rancher/rancher in rancher/rancher#54586. Drop all CLI code that depended on these types: - cluster create: remove --disable-docker-version, --import, --k8s-version, --network-provider, --rke-config flags and the RKE engine config block - cluster add-node: remove NodePool.List() guard; simplify to check cluster.Driver == "" for custom clusters - nodes ls: remove POOL column and getNodePools/getNodePoolName helpers - common: remove readFileReturnJSON, hasPrefix (now dead) --- cmd/cluster.go | 115 +------------------------------------------------ cmd/common.go | 19 -------- cmd/node.go | 30 ------------- 3 files changed, 1 insertion(+), 163 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index 871601b74..c08f65479 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -2,11 +2,9 @@ package cmd import ( "context" - "encoding/json" "errors" "fmt" "io" - "slices" "strconv" "strings" @@ -71,28 +69,6 @@ func ClusterCommand() *cli.Command { Name: "description", Usage: "Description to apply to the cluster", }, - &cli.BoolFlag{ - Name: "disable-docker-version", - Usage: "Allow unsupported versions of docker on the nodes, [default=true]", - Value: true, - }, - &cli.BoolFlag{ - Name: "import", - Usage: "Mark the cluster for import, this is required if the cluster is going to be used to import an existing k8s cluster", - }, - &cli.StringFlag{ - Name: "k8s-version", - Usage: "Kubernetes version to use for the cluster, pass in 'list' to see available versions", - }, - &cli.StringFlag{ - Name: "network-provider", - Usage: "Network provider for the cluster (flannel, canal, calico)", - Value: "canal", - }, - &cli.StringFlag{ - Name: "rke-config", - Usage: "Location of an rke config file to import. Can be JSON or YAML format", - }, }, }, { @@ -274,22 +250,6 @@ func clusterCreate(ctx context.Context, cmd *cli.Command) error { return err } - k8sVersion := cmd.String("k8s-version") - if k8sVersion != "" { - k8sVersions, err := getClusterK8sOptions(c) - if err != nil { - return err - } - - if slices.Contains(k8sVersions, k8sVersion) { - fmt.Println("Available Kubernetes versions:") - for _, val := range k8sVersions { - fmt.Println(val) - } - return nil - } - } - config, err := getClusterConfig(cmd) if err != nil { return err @@ -365,18 +325,7 @@ func clusterAddNode(ctx context.Context, cmd *cli.Command) error { return err } - if cluster.Driver == "rancherKubernetesEngine" || cluster.Driver == "" { - filter := defaultListOpts(cmd) - filter.Filters["clusterId"] = cluster.ID - nodePools, err := c.ManagementClient.NodePool.List(filter) - if err != nil { - return err - } - - if len(nodePools.Data) > 0 { - return errors.New("a node can't be manually registered to a cluster utilizing node-pools") - } - } else { + if cluster.Driver != "" { return errors.New("a node can only be manually registered to a custom cluster") } @@ -774,72 +723,10 @@ func getClusterPods(cluster managementClient.Cluster) string { return cluster.Requested["pods"] + "/" + cluster.Allocatable["pods"] } -func getClusterK8sOptions(c *cliclient.MasterClient) ([]string, error) { - var options []string - - setting, err := c.ManagementClient.Setting.ByID("k8s-version-to-images") - if err != nil { - return nil, err - } - - var objmap map[string]*json.RawMessage - err = json.Unmarshal([]byte(setting.Value), &objmap) - if err != nil { - return nil, err - } - - for key := range objmap { - options = append(options, key) - } - return options, nil -} func getClusterConfig(cmd *cli.Command) (*managementClient.Cluster, error) { config := managementClient.Cluster{} config.Name = cmd.Args().First() config.Description = cmd.String("description") - - if !cmd.Bool("import") { - config.RancherKubernetesEngineConfig = new(managementClient.RancherKubernetesEngineConfig) - ignoreDockerVersion := cmd.Bool("disable-docker-version") - config.RancherKubernetesEngineConfig.IgnoreDockerVersion = &ignoreDockerVersion - - if cmd.String("k8s-version") != "" { - config.RancherKubernetesEngineConfig.Version = cmd.String("k8s-version") - } - - if cmd.String("network-provider") != "" { - config.RancherKubernetesEngineConfig.Network = &managementClient.NetworkConfig{ - Plugin: cmd.String("network-provider"), - } - } - - if cmd.String("rke-config") != "" { - bytes, err := readFileReturnJSON(cmd.String("rke-config")) - if err != nil { - return nil, err - } - - var jsonObject map[string]interface{} - if err = json.Unmarshal(bytes, &jsonObject); err != nil { - return nil, err - } - - // Most values in RancherKubernetesEngineConfig are defined with struct tags for both JSON and YAML in camelCase. - // Changing the tags will be a breaking change. For proper deserialization, we must convert all keys to camelCase. - // Note that we ignore kebab-case keys. Users themselves should ensure any relevant keys - // (especially top-level keys in `services`, like `kube-api` or `kube-controller`) are camelCase or snake-case in cluster config. - convertSnakeCaseKeysToCamelCase(jsonObject) - - marshalled, err := json.Marshal(jsonObject) - if err != nil { - return nil, err - } - if err = json.Unmarshal(marshalled, &config); err != nil { - return nil, err - } - } - } - return &config, nil } diff --git a/cmd/common.go b/cmd/common.go index 4fdd3163d..e0bcc95b7 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -22,9 +22,7 @@ import ( "syscall" "text/template" "time" - "unicode" - "github.com/ghodss/yaml" "github.com/rancher/cli/cliclient" "github.com/rancher/cli/config" "github.com/rancher/norman/clientbase" @@ -552,18 +550,6 @@ func parseClusterAndProjectID(id string) (string, string, error) { return "", "", fmt.Errorf("unable to extract clusterid and projectid from [%s]", id) } -// Return a JSON blob of the file at path -func readFileReturnJSON(path string) ([]byte, error) { - file, err := os.ReadFile(path) - if err != nil { - return []byte{}, err - } - // This is probably already JSON if true - if hasPrefix(file, []byte("{")) { - return file, nil - } - return yaml.YAMLToJSON(file) -} // renameKeys renames the keys in a given map of arbitrary depth with a provided function for string keys. func renameKeys(input map[string]interface{}, f func(string) string) { @@ -582,11 +568,6 @@ func convertSnakeCaseKeysToCamelCase(input map[string]interface{}) { renameKeys(input, convert.ToJSONKey) } -// Return true if the first non-whitespace bytes in buf is prefix. -func hasPrefix(buf []byte, prefix []byte) bool { - trim := bytes.TrimLeftFunc(buf, unicode.IsSpace) - return bytes.HasPrefix(trim, prefix) -} // getClusterNames maps cluster ID to name and defaults to ID if name is blank func getClusterNames(cmd *cli.Command, c *cliclient.MasterClient) (map[string]string, error) { diff --git a/cmd/node.go b/cmd/node.go index f36f72fc4..8b10b67ea 100644 --- a/cmd/node.go +++ b/cmd/node.go @@ -15,7 +15,6 @@ type NodeData struct { ID string Node managementClient.Node Name string - Pool string } func NodeCommand() *cli.Command { @@ -61,16 +60,10 @@ func nodeLs(ctx context.Context, cmd *cli.Command) error { return err } - nodePools, err := getNodePools(cmd, c) - if err != nil { - return err - } - writer := NewTableWriter([][]string{ {"ID", "ID"}, {"NAME", "Name"}, {"STATE", "Node.State"}, - {"POOL", "Pool"}, {"DESCRIPTION", "Node.Description"}, }, cmd) @@ -81,7 +74,6 @@ func nodeLs(ctx context.Context, cmd *cli.Command) error { ID: item.ID, Node: item, Name: getNodeName(item), - Pool: getNodePoolName(item, nodePools), }) } @@ -169,25 +161,3 @@ func getNodeName(node managementClient.Node) string { return node.ID } -func getNodePools( - cmd *cli.Command, - c *cliclient.MasterClient, -) (*managementClient.NodePoolCollection, error) { - filter := defaultListOpts(cmd) - filter.Filters["clusterId"] = c.UserConfig.GetCurrentCluster() - - collection, err := c.ManagementClient.NodePool.List(filter) - if err != nil { - return nil, err - } - return collection, nil -} - -func getNodePoolName(node managementClient.Node, pools *managementClient.NodePoolCollection) string { - for _, pool := range pools.Data { - if node.NodePoolID == pool.ID { - return pool.HostnamePrefix - } - } - return "" -} From 7549708c1d53bea420119542c9f56864859755a1 Mon Sep 17 00:00:00 2001 From: Peter Matseykanets Date: Tue, 23 Jun 2026 18:22:11 -0400 Subject: [PATCH 2/3] style: gofmt --- cmd/cluster.go | 1 - cmd/common.go | 2 -- cmd/node.go | 1 - 3 files changed, 4 deletions(-) diff --git a/cmd/cluster.go b/cmd/cluster.go index c08f65479..c0e06a24d 100644 --- a/cmd/cluster.go +++ b/cmd/cluster.go @@ -723,7 +723,6 @@ func getClusterPods(cluster managementClient.Cluster) string { return cluster.Requested["pods"] + "/" + cluster.Allocatable["pods"] } - func getClusterConfig(cmd *cli.Command) (*managementClient.Cluster, error) { config := managementClient.Cluster{} config.Name = cmd.Args().First() diff --git a/cmd/common.go b/cmd/common.go index e0bcc95b7..ca1518881 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -550,7 +550,6 @@ func parseClusterAndProjectID(id string) (string, string, error) { return "", "", fmt.Errorf("unable to extract clusterid and projectid from [%s]", id) } - // renameKeys renames the keys in a given map of arbitrary depth with a provided function for string keys. func renameKeys(input map[string]interface{}, f func(string) string) { for k, v := range input { @@ -568,7 +567,6 @@ func convertSnakeCaseKeysToCamelCase(input map[string]interface{}) { renameKeys(input, convert.ToJSONKey) } - // getClusterNames maps cluster ID to name and defaults to ID if name is blank func getClusterNames(cmd *cli.Command, c *cliclient.MasterClient) (map[string]string, error) { clusterNames := make(map[string]string) diff --git a/cmd/node.go b/cmd/node.go index 8b10b67ea..91ec5061c 100644 --- a/cmd/node.go +++ b/cmd/node.go @@ -160,4 +160,3 @@ func getNodeName(node managementClient.Node) string { } return node.ID } - From afd3edee2c85775e33a489aac9f773d327785e13 Mon Sep 17 00:00:00 2001 From: Peter Matseykanets Date: Tue, 23 Jun 2026 18:31:52 -0400 Subject: [PATCH 3/3] Fix ssh.go SshUser removal and drop snake/camel helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Node.SshUser was removed in rancher/rancher#54586 alongside NodePool. Refactor getNodeAndKey to return sshUser as a separate value instead of storing it on the Node struct. Also remove convertSnakeCaseKeysToCamelCase, renameKeys, and their tests — these were only used by the rke-config import path that was already removed. --- cmd/common.go | 18 ------------------ cmd/common_test.go | 36 ------------------------------------ cmd/ssh.go | 19 ++++++++----------- 3 files changed, 8 insertions(+), 65 deletions(-) diff --git a/cmd/common.go b/cmd/common.go index ca1518881..357807eb4 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -27,7 +27,6 @@ import ( "github.com/rancher/cli/config" "github.com/rancher/norman/clientbase" ntypes "github.com/rancher/norman/types" - "github.com/rancher/norman/types/convert" managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3" "github.com/sirupsen/logrus" "github.com/urfave/cli/v3" @@ -550,23 +549,6 @@ func parseClusterAndProjectID(id string) (string, string, error) { return "", "", fmt.Errorf("unable to extract clusterid and projectid from [%s]", id) } -// renameKeys renames the keys in a given map of arbitrary depth with a provided function for string keys. -func renameKeys(input map[string]interface{}, f func(string) string) { - for k, v := range input { - delete(input, k) - newKey := f(k) - input[newKey] = v - if innerMap, ok := v.(map[string]interface{}); ok { - renameKeys(innerMap, f) - } - } -} - -// convertSnakeCaseKeysToCamelCase takes a map and recursively transforms all snake_case keys into camelCase keys. -func convertSnakeCaseKeysToCamelCase(input map[string]interface{}) { - renameKeys(input, convert.ToJSONKey) -} - // getClusterNames maps cluster ID to name and defaults to ID if name is blank func getClusterNames(cmd *cli.Command, c *cliclient.MasterClient) (map[string]string, error) { clusterNames := make(map[string]string) diff --git a/cmd/common_test.go b/cmd/common_test.go index 2c8eee834..b88d97d7e 100644 --- a/cmd/common_test.go +++ b/cmd/common_test.go @@ -5,7 +5,6 @@ import ( "fmt" "net/http" "net/url" - "strconv" "testing" "time" @@ -73,41 +72,6 @@ func TestParseClusterAndProjectID(t *testing.T) { } } -func TestConvertSnakeCaseKeysToCamelCase(t *testing.T) { - t.Parallel() - - tests := []struct { - input map[string]any - want map[string]any - }{ - { - map[string]any{"foo_bar": "hello"}, - map[string]any{"fooBar": "hello"}, - }, - { - map[string]any{"fooBar": "hello"}, - map[string]any{"fooBar": "hello"}, - }, - { - map[string]any{"foobar": "hello", "some_key": "valueUnmodified", "bar-baz": "bar-baz"}, - map[string]any{"foobar": "hello", "someKey": "valueUnmodified", "bar-baz": "bar-baz"}, - }, - { - map[string]any{"foo_bar": "hello", "backup_config": map[string]any{"hello_world": true}, "config_id": 123}, - map[string]any{"fooBar": "hello", "backupConfig": map[string]any{"helloWorld": true}, "configId": 123}, - }, - } - - for i, test := range tests { - t.Run(strconv.Itoa(i), func(t *testing.T) { - t.Parallel() - - convertSnakeCaseKeysToCamelCase(test.input) - assert.Equal(t, test.input, test.want) - }) - } -} - func TestParsePrincipalID(t *testing.T) { t.Parallel() diff --git a/cmd/ssh.go b/cmd/ssh.go index 58bdaa77e..00c04ee68 100644 --- a/cmd/ssh.go +++ b/cmd/ssh.go @@ -82,13 +82,13 @@ func nodeSSH(ctx context.Context, cmd *cli.Command) error { return err } - sshNode, key, err := getNodeAndKey(cmd, c, nodeName) + sshNode, key, sshUser, err := getNodeAndKey(cmd, c, nodeName) if err != nil { return err } if user == "" { - user = sshNode.SshUser + user = sshUser } ipAddress := sshNode.IPAddress if cmd.Bool("external") { @@ -98,16 +98,16 @@ func nodeSSH(ctx context.Context, cmd *cli.Command) error { return processExitCode(callSSH(key, ipAddress, user, args)) } -func getNodeAndKey(cmd *cli.Command, c *cliclient.MasterClient, nodeName string) (managementClient.Node, []byte, error) { +func getNodeAndKey(cmd *cli.Command, c *cliclient.MasterClient, nodeName string) (managementClient.Node, []byte, string, error) { sshNode := managementClient.Node{} resource, err := Lookup(c, nodeName, "node") if err != nil { - return sshNode, nil, err + return sshNode, nil, "", err } sshNode, err = getNodeByID(cmd, c, resource.ID) if err != nil { - return sshNode, nil, err + return sshNode, nil, "", err } link := sshNode.Links["nodeConfig"] @@ -115,7 +115,7 @@ func getNodeAndKey(cmd *cli.Command, c *cliclient.MasterClient, nodeName string) // Get the machine and use that instead. machine, err := getMachineByNodeName(cmd, c, sshNode.NodeName) if err != nil { - return sshNode, nil, fmt.Errorf("failed to find SSH key for node [%s]", nodeName) + return sshNode, nil, "", fmt.Errorf("failed to find SSH key for node [%s]", nodeName) } link = machine.Links["sshkeys"] @@ -123,13 +123,10 @@ func getNodeAndKey(cmd *cli.Command, c *cliclient.MasterClient, nodeName string) key, sshUser, err := getSSHKey(c, link, getNodeName(sshNode)) if err != nil { - return sshNode, nil, err - } - if sshUser != "" { - sshNode.SshUser = sshUser + return sshNode, nil, "", err } - return sshNode, key, nil + return sshNode, key, sshUser, nil } func callSSH(content []byte, ip string, user string, args []string) error {