File rancher-cli-2.12.1.obscpio of Package rancher-cli

07070100000000000081A400000000000000000000000168B0FB3F0000002E000000000000000000000000000000000000002100000000rancher-cli-2.12.1/.dockerignore./bin
./build
./.dapper
./dist
./.trash-cache
07070100000001000081A400000000000000000000000168B0FB3F00000079000000000000000000000000000000000000002200000000rancher-cli-2.12.1/.golangci.json{
    "linters": {
        "enable": [
            "gofmt"
        ]
    },
    "run": {
        "timeout": "10m"
    }
}07070100000002000081A400000000000000000000000168B0FB3F00000018000000000000000000000000000000000000001E00000000rancher-cli-2.12.1/CODEOWNERS*       @rancher/collie
07070100000003000081A400000000000000000000000168B0FB3F000027BE000000000000000000000000000000000000001B00000000rancher-cli-2.12.1/LICENSE
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS
07070100000004000081A400000000000000000000000168B0FB3F00000064000000000000000000000000000000000000001C00000000rancher-cli-2.12.1/MakefileTARGETS := $(shell ls scripts)

$(TARGETS):
	@./scripts/$@

.DEFAULT_GOAL := ci

.PHONY: $(TARGETS)
07070100000005000081A400000000000000000000000168B0FB3F00000A39000000000000000000000000000000000000001D00000000rancher-cli-2.12.1/README.mdRancher CLI
===========

The Rancher Command Line Interface (CLI) is a unified tool for interacting with your Rancher Server.

For usage information see: https://rancher.com/docs/rancher/v2.x/en/cli/

> **Note:** This is for version 2.x.x of the cli, for info on 1.6.x see [here](https://github.com/rancher/cli/tree/v1.6)

## Installing

Check the [releases page](https://github.com/rancher/cli/releases) for direct downloads of the binary. After you download it, you can add it to your `$PATH` or [build your own from source](#building-from-source).

## Setting up Rancher CLI with a Rancher Server

The CLI requires your Rancher Server address, along with [credentials for authentication](https://rancher.com/docs/rancher/v2.x/en/user-settings/api-keys/). Rancher CLI pulls this information from a JSON file, `cli2.json`, which is created the first time you run `rancher login`. By default, the path of this file is `~/.rancher/cli2.json`.

```
$ rancher login https://<RANCHER_SERVER_URL> -t my-secret-token
```

> **Note:** When entering your `<RANCHER_SERVER_URL>`, include the port that was exposed while you installed Rancher Server.

## Usage

Run `rancher --help` for a list of available commands.

## Building from Source

The binaries will be located in `/bin`.

### Linux Binary

Run `make build`.

### Mac Binary

Run `CROSS=1 make build`.

## Docker Image

Run `docker run --rm -it -v <PATH_TO_CONFIG>:/home/cli/.rancher/cli2.json rancher/cli2 [ARGS]`.
Pass credentials by replacing `<PATH_TO_CONFIG>` with your config file for the server.

To build `rancher/cli`, run `make`.  To use a custom Docker repository, do `REPO=custom make`, which produces a `custom/cli` image.

## Contact

For bugs, questions, comments, corrections, suggestions, etc., open an issue in
[rancher/rancher](//github.com/rancher/rancher/issues) with a title prefix of `[cli] `.

Or just [click here](//github.com/rancher/rancher/issues/new?title=%5Bcli%5D%20) to create a new issue.

## License
Copyright (c) 2014-2019 [Rancher Labs, Inc.](http://rancher.com)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
07070100000006000041ED00000000000000000000000268B0FB3F00000000000000000000000000000000000000000000001D00000000rancher-cli-2.12.1/cliclient07070100000007000081A400000000000000000000000168B0FB3F0000150B000000000000000000000000000000000000002A00000000rancher-cli-2.12.1/cliclient/cliclient.gopackage cliclient

import (
	"errors"
	"fmt"
	"strings"

	"github.com/rancher/cli/config"
	"github.com/rancher/norman/clientbase"
	ntypes "github.com/rancher/norman/types"
	capiClient "github.com/rancher/rancher/pkg/client/generated/cluster/v1beta1"
	clusterClient "github.com/rancher/rancher/pkg/client/generated/cluster/v3"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	projectClient "github.com/rancher/rancher/pkg/client/generated/project/v3"
	"github.com/sirupsen/logrus"
	"golang.org/x/sync/errgroup"
)

type MasterClient struct {
	ClusterClient    *clusterClient.Client
	ManagementClient *managementClient.Client
	ProjectClient    *projectClient.Client
	UserConfig       *config.ServerConfig
	CAPIClient       *capiClient.Client
}

// NewMasterClient returns a new MasterClient with Cluster, Management and Project
// clients populated
func NewMasterClient(config *config.ServerConfig) (*MasterClient, error) {
	mc := &MasterClient{
		UserConfig: config,
	}

	clustProj := CheckProject(mc.UserConfig.Project)
	if clustProj == nil {
		logrus.Warn("No context set; some commands will not work. Run `rancher login` again.")
	}

	var g errgroup.Group

	g.Go(mc.newManagementClient)
	g.Go(mc.newClusterClient)
	g.Go(mc.newProjectClient)
	g.Go(mc.newCAPIClient)

	if err := g.Wait(); err != nil {
		return nil, err
	}
	return mc, nil
}

// NewManagementClient returns a new MasterClient with only the Management client
func NewManagementClient(config *config.ServerConfig) (*MasterClient, error) {
	mc := &MasterClient{
		UserConfig: config,
	}

	err := mc.newManagementClient()
	if err != nil {
		return nil, err
	}
	return mc, nil
}

// NewClusterClient returns a new MasterClient with only the Cluster client
func NewClusterClient(config *config.ServerConfig) (*MasterClient, error) {
	clustProj := CheckProject(config.Project)
	if clustProj == nil {
		return nil, errors.New("no context set")
	}

	mc := &MasterClient{
		UserConfig: config,
	}

	err := mc.newClusterClient()
	if err != nil {
		return nil, err
	}
	return mc, nil
}

// NewProjectClient returns a new MasterClient with only the Project client
func NewProjectClient(config *config.ServerConfig) (*MasterClient, error) {
	clustProj := CheckProject(config.Project)
	if clustProj == nil {
		return nil, errors.New("no context set")
	}

	mc := &MasterClient{
		UserConfig: config,
	}

	err := mc.newProjectClient()
	if err != nil {
		return nil, err
	}
	return mc, nil
}

func (mc *MasterClient) newManagementClient() error {
	options := createClientOpts(mc.UserConfig)

	// Setup the management client
	mClient, err := managementClient.NewClient(options)
	if err != nil {
		return err
	}
	mc.ManagementClient = mClient

	return nil
}

func (mc *MasterClient) newClusterClient() error {
	options := createClientOpts(mc.UserConfig)
	options.URL = options.URL + "/clusters/" + mc.UserConfig.FocusedCluster()

	// Setup the project client
	cc, err := clusterClient.NewClient(options)
	if err != nil {
		if clientbase.IsNotFound(err) {
			err = fmt.Errorf("current cluster not available, try running `rancher context switch`: %w", err)
		}
		return err
	}
	mc.ClusterClient = cc

	return nil
}

func (mc *MasterClient) newProjectClient() error {
	options := createClientOpts(mc.UserConfig)
	options.URL = options.URL + "/projects/" + mc.UserConfig.Project

	// Setup the project client
	pc, err := projectClient.NewClient(options)
	if err != nil {
		if clientbase.IsNotFound(err) {
			err = fmt.Errorf("current project not available, try running `rancher context switch: %w", err)
		}
		return err
	}
	mc.ProjectClient = pc

	return nil
}

func (mc *MasterClient) newCAPIClient() error {
	options := createClientOpts(mc.UserConfig)
	options.URL = strings.TrimSuffix(options.URL, "/v3") + "/v1"

	// Setup the CAPI client
	cc, err := capiClient.NewClient(options)
	if err != nil {
		return err
	}
	mc.CAPIClient = cc

	return nil
}

func (mc *MasterClient) ByID(resource *ntypes.Resource, respObject interface{}) error {
	if strings.HasPrefix(resource.Type, "cluster.x-k8s.io") {
		return mc.CAPIClient.ByID(resource.Type, resource.ID, &respObject)
	} else if _, ok := mc.ManagementClient.APIBaseClient.Types[resource.Type]; ok {
		return mc.ManagementClient.ByID(resource.Type, resource.ID, &respObject)
	} else if _, ok := mc.ProjectClient.APIBaseClient.Types[resource.Type]; ok {
		return mc.ProjectClient.ByID(resource.Type, resource.ID, &respObject)
	} else if _, ok := mc.ClusterClient.APIBaseClient.Types[resource.Type]; ok {
		return mc.ClusterClient.ByID(resource.Type, resource.ID, &respObject)
	}
	return fmt.Errorf("MasterClient - unknown resource type %v", resource.Type)
}

func createClientOpts(config *config.ServerConfig) *clientbase.ClientOpts {
	serverURL := config.URL

	if !strings.HasSuffix(serverURL, "/v3") {
		serverURL = config.URL + "/v3"
	}

	return &clientbase.ClientOpts{
		URL:       serverURL,
		AccessKey: config.AccessKey,
		SecretKey: config.SecretKey,
		CACerts:   config.CACerts,
		ProxyURL:  config.ProxyURL,
		Timeout:   config.GetHTTPTimeout(),
	}
}

func SplitOnColon(s string) []string {
	return strings.Split(s, ":")
}

// CheckProject verifies s matches the valid project ID of <cluster>:<project>
func CheckProject(s string) []string {
	clustProj := SplitOnColon(s)

	if len(s) == 0 || len(clustProj) != 2 {
		return nil
	}

	return clustProj
}
07070100000008000041ED00000000000000000000000268B0FB3F00000000000000000000000000000000000000000000001700000000rancher-cli-2.12.1/cmd07070100000009000081A400000000000000000000000168B0FB3F00005101000000000000000000000000000000000000002200000000rancher-cli-2.12.1/cmd/cluster.gopackage cmd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"slices"
	"strconv"
	"strings"

	"github.com/rancher/cli/cliclient"
	"github.com/rancher/norman/types"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

const (
	importDescription = `
Imports an existing cluster to be used in rancher by using a generated kubectl 
command to run in your existing Kubernetes cluster.
`
	importClusterNotice = "If you get an error about 'certificate signed by unknown authority' " +
		"because your Rancher installation is running with an untrusted/self-signed SSL " +
		"certificate, run the command below instead to bypass the certificate check:"
)

type ClusterData struct {
	ID       string
	Current  string
	Cluster  managementClient.Cluster
	Name     string
	Provider string
	Nodes    int64
	CPU      string
	RAM      string
	Pods     string
}

func ClusterCommand() cli.Command {
	return cli.Command{
		Name:    "clusters",
		Aliases: []string{"cluster"},
		Usage:   "Operations on clusters",
		Action:  defaultAction(clusterLs),
		Subcommands: []cli.Command{
			{
				Name:        "ls",
				Usage:       "List clusters",
				Description: "Lists all clusters",
				ArgsUsage:   "None",
				Action:      clusterLs,
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "format",
						Usage: "'json', 'yaml' or Custom format: '{{.Cluster.ID}} {{.Cluster.Name}}'",
					},
					quietFlag,
				},
			},
			{
				Name:        "create",
				Usage:       "Creates a new empty cluster",
				Description: "Create a new custom cluster with desired configuration",
				ArgsUsage:   "[NEWCLUSTERNAME...]",
				Action:      clusterCreate,
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "description",
						Usage: "Description to apply to the cluster",
					},
					cli.BoolTFlag{
						Name:  "disable-docker-version",
						Usage: "Allow unsupported versions of docker on the nodes, [default=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",
					},
				},
			},
			{
				Name:        "import",
				Usage:       "Import an existing Kubernetes cluster into a Rancher cluster",
				Description: importDescription,
				ArgsUsage:   "[CLUSTERID CLUSTERNAME]",
				Action:      clusterImport,
				Flags: []cli.Flag{
					quietFlag,
				},
			},
			{
				Name:      "add-node",
				Usage:     "Outputs the docker command needed to add a node to an existing Rancher custom cluster",
				ArgsUsage: "[CLUSTERID CLUSTERNAME]",
				Action:    clusterAddNode,
				Flags: []cli.Flag{
					cli.StringSliceFlag{
						Name:  "label",
						Usage: "Label to apply to a node in the format [name]=[value]",
					},
					cli.BoolFlag{
						Name:  "etcd",
						Usage: "Use node for etcd",
					},
					cli.BoolFlag{
						Name:  "management",
						Usage: "Use node for management (DEPRECATED, use controlplane instead)",
					},
					cli.BoolFlag{
						Name:  "controlplane",
						Usage: "Use node for controlplane",
					},
					cli.BoolFlag{
						Name:  "worker",
						Usage: "Use node as a worker",
					},
					quietFlag,
				},
			},
			{
				Name:      "delete",
				Aliases:   []string{"rm"},
				Usage:     "Delete a cluster",
				ArgsUsage: "[CLUSTERID/CLUSTERNAME...]",
				Action:    clusterDelete,
			},
			{
				Name:      "export",
				Usage:     "Export a cluster",
				ArgsUsage: "[CLUSTERID/CLUSTERNAME...]",
				Action:    clusterExport,
			},
			{
				Name:      "kubeconfig",
				Aliases:   []string{"kf"},
				Usage:     "Return the kube config used to access the cluster",
				ArgsUsage: "[CLUSTERID CLUSTERNAME]",
				Action:    clusterKubeConfig,
			},
			{
				Name:        "add-member-role",
				Usage:       "Add a member to the cluster",
				Action:      addClusterMemberRoles,
				Description: "Examples:\n #Create the roles of 'nodes-view' and 'projects-view' for a user named 'user1'\n rancher cluster add-member-role user1 nodes-view projects-view\n",
				ArgsUsage:   "[USERNAME, ROLE...]",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "cluster-id",
						Usage: "Optional cluster ID to add member role to, defaults to the current context",
					},
				},
			},
			{
				Name:        "delete-member-role",
				Usage:       "Delete a member from the cluster",
				Action:      deleteClusterMemberRoles,
				Description: "Examples:\n #Delete the roles of 'nodes-view' and 'projects-view' for a user named 'user1'\n rancher cluster delete-member-role user1 nodes-view projects-view\n",
				ArgsUsage:   "[USERNAME, ROLE...]",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "cluster-id",
						Usage: "Optional cluster ID to remove member role from, defaults to the current context",
					},
				},
			},
			{
				Name:   "list-roles",
				Usage:  "List all available roles for a cluster",
				Action: listClusterRoles,
			},
			{
				Name:  "list-members",
				Usage: "List current members of the cluster",
				Action: func(cctx *cli.Context) error {
					client, err := GetClient(cctx)
					if err != nil {
						return err
					}

					return listClusterMembers(
						cctx,
						cctx.App.Writer,
						client.UserConfig,
						client.ManagementClient.ClusterRoleTemplateBinding,
						client.ManagementClient.Principal,
					)
				},
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "cluster-id",
						Usage: "Optional cluster ID to list members for, defaults to the current context",
					},
					cli.StringFlag{
						Name:  "format",
						Usage: "'json', 'yaml' or Custom format: '{{.ID }} {{.Member }}'",
					},
					quietFlag,
				},
			},
		},
	}
}

func clusterLs(ctx *cli.Context) error {
	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	collection, err := c.ManagementClient.Cluster.List(defaultListOpts(ctx))
	if err != nil {
		return err
	}

	writer := NewTableWriter([][]string{
		{"CURRENT", "Current"},
		{"ID", "ID"},
		{"STATE", "Cluster.State"},
		{"NAME", "Name"},
		{"PROVIDER", "Provider"},
		{"NODES", "Nodes"},
		{"CPU", "CPU"},
		{"RAM", "RAM"},
		{"PODS", "Pods"},
	}, ctx)

	defer writer.Close()

	for _, item := range collection.Data {
		var current string
		if item.ID == c.UserConfig.FocusedCluster() {
			current = "*"
		}

		writer.Write(&ClusterData{
			ID:       item.ID,
			Current:  current,
			Cluster:  item,
			Name:     getClusterName(&item),
			Provider: getClusterProvider(item),
			Nodes:    item.NodeCount,
			CPU:      getClusterCPU(item),
			RAM:      getClusterRAM(item),
			Pods:     getClusterPods(item),
		})
	}

	return writer.Err()
}

func clusterCreate(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}
	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	k8sVersion := ctx.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(ctx)
	if err != nil {
		return err
	}

	createdCluster, err := c.ManagementClient.Cluster.Create(config)
	if err != nil {
		return err
	}

	fmt.Printf("Successfully created cluster %v\n", createdCluster.Name)
	return nil
}

func clusterImport(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	resource, err := Lookup(c, ctx.Args().First(), "cluster")
	if err != nil {
		return err
	}

	cluster, err := getClusterByID(c, resource.ID)
	if err != nil {
		return err
	}

	if cluster.Driver != "" {
		return errors.New("existing k8s cluster can't be imported into this cluster")
	}

	clusterToken, err := getClusterRegToken(ctx, c, cluster.ID)
	if err != nil {
		return err
	}

	if ctx.Bool("quiet") {
		fmt.Println(clusterToken.Command)
		fmt.Println(clusterToken.InsecureCommand)
		return nil
	}

	fmt.Printf("Run the following command in your cluster:\n%s\n\n%s\n%s\n", clusterToken.Command, importClusterNotice, clusterToken.InsecureCommand)

	return nil
}

// clusterAddNode prints the command needed to add a node to a cluster
func clusterAddNode(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	resource, err := Lookup(c, ctx.Args().First(), "cluster")
	if err != nil {
		return err
	}

	cluster, err := getClusterByID(c, resource.ID)
	if err != nil {
		return err
	}

	if cluster.Driver == "rancherKubernetesEngine" || cluster.Driver == "" {
		filter := defaultListOpts(ctx)
		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 {
		return errors.New("a node can only be manually registered to a custom cluster")
	}

	clusterToken, err := getClusterRegToken(ctx, c, cluster.ID)
	if err != nil {
		return err
	}

	var roleFlags string

	if ctx.Bool("etcd") {
		roleFlags = roleFlags + " --etcd"
	}

	if ctx.Bool("management") || ctx.Bool("controlplane") {
		if ctx.Bool("management") && !ctx.Bool("quiet") {
			logrus.Info("The flag --management is deprecated and replaced by --controlplane")
		}
		roleFlags = roleFlags + " --controlplane"
	}

	if ctx.Bool("worker") {
		roleFlags = roleFlags + " --worker"
	}

	command := clusterToken.NodeCommand + roleFlags

	if labels := ctx.StringSlice("label"); labels != nil {
		for _, label := range labels {
			command = command + fmt.Sprintf(" --label %v", label)
		}
	}

	if ctx.Bool("quiet") {
		fmt.Println(command)
		return nil
	}

	fmt.Printf("Run this command on an existing machine already running a "+
		"supported version of Docker:\n%v\n", command)
	return nil
}

func clusterDelete(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	for _, cluster := range ctx.Args() {

		resource, err := Lookup(c, cluster, "cluster")
		if err != nil {
			return err
		}

		cluster, err := getClusterByID(c, resource.ID)
		if err != nil {
			return err
		}

		err = c.ManagementClient.Cluster.Delete(cluster)
		if err != nil {
			return err
		}
	}

	return nil
}

func clusterExport(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	resource, err := Lookup(c, ctx.Args().First(), "cluster")
	if err != nil {
		return err
	}

	cluster, err := getClusterByID(c, resource.ID)
	if err != nil {
		return err
	}

	if _, ok := cluster.Actions["exportYaml"]; !ok {
		return errors.New("cluster does not support being exported")
	}

	export, err := c.ManagementClient.Cluster.ActionExportYaml(cluster)
	if err != nil {
		return err
	}

	fmt.Println(export.YAMLOutput)
	return nil
}

func clusterKubeConfig(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	resource, err := Lookup(c, ctx.Args().First(), "cluster")
	if err != nil {
		return err
	}

	cluster, err := getClusterByID(c, resource.ID)
	if err != nil {
		return err
	}

	config, err := c.ManagementClient.Cluster.ActionGenerateKubeconfig(cluster)
	if err != nil {
		return err
	}
	fmt.Println(config.Config)
	return nil
}

func addClusterMemberRoles(ctx *cli.Context) error {
	if len(ctx.Args()) < 2 {
		return cli.ShowSubcommandHelp(ctx)
	}

	memberName := ctx.Args().First()

	roles := ctx.Args()[1:]

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	member, err := searchForMember(ctx, c, memberName)
	if err != nil {
		return err
	}

	clusterID := c.UserConfig.FocusedCluster()
	if ctx.String("cluster-id") != "" {
		clusterID = ctx.String("cluster-id")
	}

	for _, role := range roles {
		rtb := managementClient.ClusterRoleTemplateBinding{
			ClusterID:       clusterID,
			RoleTemplateID:  role,
			UserPrincipalID: member.ID,
		}
		if member.PrincipalType == "user" {
			rtb.UserPrincipalID = member.ID
		} else {
			rtb.GroupPrincipalID = member.ID
		}
		_, err = c.ManagementClient.ClusterRoleTemplateBinding.Create(&rtb)
		if err != nil {
			return err
		}
	}
	return nil
}

func deleteClusterMemberRoles(ctx *cli.Context) error {
	if len(ctx.Args()) < 2 {
		return cli.ShowSubcommandHelp(ctx)
	}

	memberName := ctx.Args().First()

	roles := ctx.Args()[1:]

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	member, err := searchForMember(ctx, c, memberName)
	if err != nil {
		return err
	}

	clusterID := c.UserConfig.FocusedCluster()
	if ctx.String("cluster-id") != "" {
		clusterID = ctx.String("cluster-id")
	}

	for _, role := range roles {
		filter := defaultListOpts(ctx)
		filter.Filters["clusterId"] = clusterID
		filter.Filters["roleTemplateId"] = role

		if member.PrincipalType == "user" {
			filter.Filters["userPrincipalId"] = member.ID
		} else {
			filter.Filters["groupPrincipalId"] = member.ID
		}

		bindings, err := c.ManagementClient.ClusterRoleTemplateBinding.List(filter)
		if err != nil {
			return err
		}

		for _, binding := range bindings.Data {
			err = c.ManagementClient.ClusterRoleTemplateBinding.Delete(&binding)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func listClusterRoles(ctx *cli.Context) error {
	return listRoles(ctx, "cluster")
}

type crtbLister interface {
	List(opts *types.ListOpts) (*managementClient.ClusterRoleTemplateBindingCollection, error)
}

type userConfig interface {
	FocusedCluster() string
	FocusedProject() string
}

func listClusterMembers(ctx *cli.Context, out io.Writer, config userConfig, crtbs crtbLister, principals principalGetter) error {
	clusterID := config.FocusedCluster()
	if ctx.String("cluster-id") != "" {
		clusterID = ctx.String("cluster-id")
	}

	filter := defaultListOpts(ctx)
	filter.Filters["clusterId"] = clusterID

	bindings, err := crtbs.List(filter)
	if err != nil {
		return err
	}

	rtbs := make([]RoleTemplateBinding, 0, len(bindings.Data))

	for _, binding := range bindings.Data {
		parsedTime, err := createdTimetoHuman(binding.Created)
		if err != nil {
			return err
		}

		principalID := binding.UserPrincipalID
		if binding.GroupPrincipalID != "" {
			principalID = binding.GroupPrincipalID
		}

		rtbs = append(rtbs, RoleTemplateBinding{
			ID:      binding.ID,
			Member:  getMemberNameFromPrincipal(principals, principalID),
			Role:    binding.RoleTemplateID,
			Created: parsedTime,
		})
	}

	writerConfig := &TableWriterConfig{
		Format: ctx.String("format"),
		Quiet:  ctx.Bool("quiet"),
		Writer: out,
	}

	return listRoleTemplateBindings(writerConfig, rtbs)
}

// getClusterRegToken will return an existing token or create one if none exist
func getClusterRegToken(
	ctx *cli.Context,
	c *cliclient.MasterClient,
	clusterID string,
) (managementClient.ClusterRegistrationToken, error) {
	tokenOpts := defaultListOpts(ctx)
	tokenOpts.Filters["clusterId"] = clusterID

	clusterTokenCollection, err := c.ManagementClient.ClusterRegistrationToken.List(tokenOpts)
	if err != nil {
		return managementClient.ClusterRegistrationToken{}, err
	}

	if len(clusterTokenCollection.Data) == 0 {
		crt := &managementClient.ClusterRegistrationToken{
			ClusterID: clusterID,
		}
		clusterToken, err := c.ManagementClient.ClusterRegistrationToken.Create(crt)
		if err != nil {
			return managementClient.ClusterRegistrationToken{}, err
		}
		return *clusterToken, nil
	}
	return clusterTokenCollection.Data[0], nil
}

func getClusterByID(
	c *cliclient.MasterClient,
	clusterID string,
) (*managementClient.Cluster, error) {
	cluster, err := c.ManagementClient.Cluster.ByID(clusterID)
	if err != nil {
		return nil, fmt.Errorf("no cluster found with the ID [%s], run "+
			"`rancher clusters` to see available clusters: %s", clusterID, err)
	}
	return cluster, nil
}

func getClusterProvider(cluster managementClient.Cluster) string {
	switch cluster.Driver {
	case "imported":
		switch cluster.Provider {
		case "rke2":
			return "RKE2"
		case "k3s":
			return "K3S"
		default:
			return "Imported"
		}
	case "k3s":
		return "K3S"
	case "rke2":
		return "RKE2"
	case "rancherKubernetesEngine":
		return "Rancher Kubernetes Engine"
	case "azureKubernetesService", "AKS":
		return "Azure Kubernetes Service"
	case "googleKubernetesEngine", "GKE":
		return "Google Kubernetes Engine"
	case "EKS":
		return "Elastic Kubernetes Service"
	default:
		return "Unknown"
	}
}

func getClusterCPU(cluster managementClient.Cluster) string {
	req := parseResourceString(cluster.Requested["cpu"])
	alloc := parseResourceString(cluster.Allocatable["cpu"])
	return req + "/" + alloc
}

func getClusterRAM(cluster managementClient.Cluster) string {
	req := parseResourceString(cluster.Requested["memory"])
	alloc := parseResourceString(cluster.Allocatable["memory"])
	return req + "/" + alloc + " GB"
}

// parseResourceString returns GB for Ki and Mi and CPU cores from 'm'
func parseResourceString(mem string) string {
	if strings.HasSuffix(mem, "Ki") {
		num, err := strconv.ParseFloat(strings.Replace(mem, "Ki", "", -1), 64)
		if err != nil {
			return mem
		}
		num = num / 1024 / 1024
		return strings.TrimSuffix(fmt.Sprintf("%.2f", num), ".0")
	}
	if strings.HasSuffix(mem, "Mi") {
		num, err := strconv.ParseFloat(strings.Replace(mem, "Mi", "", -1), 64)
		if err != nil {
			return mem
		}
		num = num / 1024
		return strings.TrimSuffix(fmt.Sprintf("%.2f", num), ".0")
	}
	if strings.HasSuffix(mem, "m") {
		num, err := strconv.ParseFloat(strings.Replace(mem, "m", "", -1), 64)
		if err != nil {
			return mem
		}
		num = num / 1000
		return strconv.FormatFloat(num, 'f', 2, 32)
	}
	return mem
}

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(ctx *cli.Context) (*managementClient.Cluster, error) {
	config := managementClient.Cluster{}
	config.Name = ctx.Args().First()
	config.Description = ctx.String("description")

	if !ctx.Bool("import") {
		config.RancherKubernetesEngineConfig = new(managementClient.RancherKubernetesEngineConfig)
		ignoreDockerVersion := ctx.BoolT("disable-docker-version")
		config.RancherKubernetesEngineConfig.IgnoreDockerVersion = &ignoreDockerVersion

		if ctx.String("k8s-version") != "" {
			config.RancherKubernetesEngineConfig.Version = ctx.String("k8s-version")
		}

		if ctx.String("network-provider") != "" {
			config.RancherKubernetesEngineConfig.Network = &managementClient.NetworkConfig{
				Plugin: ctx.String("network-provider"),
			}
		}

		if ctx.String("rke-config") != "" {
			bytes, err := readFileReturnJSON(ctx.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
}
0707010000000A000081A400000000000000000000000168B0FB3F000009BA000000000000000000000000000000000000002700000000rancher-cli-2.12.1/cmd/cluster_test.gopackage cmd

import (
	"bytes"
	"flag"
	"fmt"
	"net/url"
	"testing"
	"time"

	"github.com/rancher/norman/types"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/urfave/cli"
)

func TestListClusterMembers(t *testing.T) {
	t.Parallel()

	now := time.Now()

	userConfig := &fakeUserConfig{
		FocusedClusterFunc: func() string {
			return "c-fn7lc"
		},
	}

	created := now.Format(time.RFC3339)
	crtbs := &fakeCRTBLister{
		ListFunc: func(opts *types.ListOpts) (*managementClient.ClusterRoleTemplateBindingCollection, error) {
			return &managementClient.ClusterRoleTemplateBindingCollection{
				Data: []managementClient.ClusterRoleTemplateBinding{
					{
						Resource: types.Resource{
							ID: "c-fn7lc:creator-cluster-owner",
						},
						Created:         created,
						RoleTemplateID:  "cluster-owner",
						UserPrincipalID: "local://user-2p7w6",
					},
					{
						Resource: types.Resource{
							ID: "c-fn7lc:crtb-qd49d",
						},
						Created:          created,
						RoleTemplateID:   "cluster-member",
						GroupPrincipalID: "okta_group://b4qkhsnliz",
					},
				},
			}, nil
		},
	}

	principals := &fakePrincipalGetter{
		ByIDFunc: func(id string) (*managementClient.Principal, error) {
			id, err := url.PathUnescape(id)
			require.NoError(t, err)

			switch id {
			case "local://user-2p7w6":
				return &managementClient.Principal{
					Name:          "Default Admin",
					LoginName:     "admin",
					Provider:      "local",
					PrincipalType: "user",
				}, nil
			case "okta_group://b4qkhsnliz":
				return &managementClient.Principal{
					Name:          "DevOps",
					LoginName:     "devops",
					Provider:      "okta",
					PrincipalType: "group",
				}, nil
			default:
				return nil, fmt.Errorf("not found")
			}
		},
	}

	flagSet := flag.NewFlagSet("test", flag.ContinueOnError)
	cctx := cli.NewContext(nil, flagSet, nil)

	var out bytes.Buffer

	err := listClusterMembers(cctx, &out, userConfig, crtbs, principals)
	require.NoError(t, err)
	require.NotEmpty(t, out)

	humanCreated := now.Format(humanTimeFormat)
	want := [][]string{
		{"BINDING-ID", "MEMBER", "ROLE", "CREATED"},
		{"c-fn7lc:creator-cluster-owner", "Default Admin (Local User)", "cluster-owner", humanCreated},
		{"c-fn7lc:crtb-qd49d", "DevOps (Okta Group)", "cluster-member", humanCreated},
	}

	got := parseTabWriterOutput(&out)
	assert.Equal(t, want, got)
}
0707010000000B000081A400000000000000000000000168B0FB3F0000070B000000000000000000000000000000000000002300000000rancher-cli-2.12.1/cmd/cmd_test.gopackage cmd

import (
	"bufio"
	"io"
	"strings"

	"github.com/rancher/norman/types"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
)

type fakePrincipalGetter struct {
	ByIDFunc func(id string) (*managementClient.Principal, error)
}

func (g *fakePrincipalGetter) ByID(id string) (*managementClient.Principal, error) {
	if g.ByIDFunc != nil {
		return g.ByIDFunc(id)
	}
	return nil, nil
}

type fakeUserConfig struct {
	FocusedClusterFunc func() string
	FocusedProjectFunc func() string
}

func (c *fakeUserConfig) FocusedCluster() string {
	if c.FocusedClusterFunc != nil {
		return c.FocusedClusterFunc()
	}
	return ""
}

func (c *fakeUserConfig) FocusedProject() string {
	if c.FocusedProjectFunc != nil {
		return c.FocusedProjectFunc()
	}
	return ""
}

type fakeCRTBLister struct {
	ListFunc func(opts *types.ListOpts) (*managementClient.ClusterRoleTemplateBindingCollection, error)
}

func (f *fakeCRTBLister) List(opts *types.ListOpts) (*managementClient.ClusterRoleTemplateBindingCollection, error) {
	if f.ListFunc != nil {
		return f.ListFunc(opts)
	}
	return nil, nil
}

type fakePRTBLister struct {
	ListFunc func(opts *types.ListOpts) (*managementClient.ProjectRoleTemplateBindingCollection, error)
}

func (f *fakePRTBLister) List(opts *types.ListOpts) (*managementClient.ProjectRoleTemplateBindingCollection, error) {
	if f.ListFunc != nil {
		return f.ListFunc(opts)
	}
	return nil, nil
}

func parseTabWriterOutput(r io.Reader) [][]string {
	var parsed [][]string
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		var fields []string
		for _, field := range strings.Split(scanner.Text(), "  ") {
			if field == "" {
				continue
			}
			fields = append(fields, strings.TrimSpace(field))
		}
		parsed = append(parsed, fields)
	}
	return parsed
}
0707010000000C000081A400000000000000000000000168B0FB3F00003EB7000000000000000000000000000000000000002100000000rancher-cli-2.12.1/cmd/common.gopackage cmd

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"syscall"
	"text/template"
	"time"
	"unicode"

	"github.com/ghodss/yaml"
	"github.com/rancher/cli/cliclient"
	"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"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
	"k8s.io/client-go/tools/clientcmd/api"
)

const (
	letters             = "abcdefghijklmnopqrstuvwxyz0123456789"
	cfgFile             = "cli2.json"
	kubeConfigKeyFormat = "%s-%s"
	defaultHTTPTimeout  = time.Minute // Matches the default timeout of the Norman Api Client.
)

var (
	// ManagementResourceTypes lists the types we use the management client for
	ManagementResourceTypes = []string{"cluster", "node", "project"}
	// ProjectResourceTypes lists the types we use the cluster client for
	ProjectResourceTypes = []string{"secret", "namespacedSecret", "workload"}
	// ClusterResourceTypes lists the types we use the project client for
	ClusterResourceTypes = []string{"persistentVolume", "storageClass", "namespace"}

	formatFlag = cli.StringFlag{
		Name:  "format,o",
		Usage: "'json', 'yaml' or custom format",
	}

	quietFlag = cli.BoolFlag{
		Name:  "quiet,q",
		Usage: "Only display IDs or suppress help text",
	}
)

type MemberData struct {
	Name       string
	MemberType string
	AccessType string
}

type RoleTemplate struct {
	ID          string
	Name        string
	Description string
}

type RoleTemplateBinding struct {
	ID      string
	Member  string
	Role    string
	Created string
}

func listAllRoles() []string {
	roles := []string{}
	roles = append(roles, ManagementResourceTypes...)
	roles = append(roles, ProjectResourceTypes...)
	roles = append(roles, ClusterResourceTypes...)
	return roles
}

func listRoles(ctx *cli.Context, context string) error {
	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	filter := defaultListOpts(ctx)
	filter.Filters["hidden"] = false
	filter.Filters["context"] = context

	templates, err := c.ManagementClient.RoleTemplate.List(filter)
	if err != nil {
		return err
	}

	writer := NewTableWriter([][]string{
		{"ID", "ID"},
		{"NAME", "Name"},
		{"DESCRIPTION", "Description"},
	}, ctx)

	defer writer.Close()

	for _, item := range templates.Data {
		writer.Write(&RoleTemplate{
			ID:          item.ID,
			Name:        item.Name,
			Description: item.Description,
		})
	}

	return writer.Err()
}

func listRoleTemplateBindings(writerConfig *TableWriterConfig, rtbs []RoleTemplateBinding) error {
	writer := NewTableWriterWithConfig([][]string{
		{"BINDING-ID", "ID"},
		{"MEMBER", "Member"},
		{"ROLE", "Role"},
		{"CREATED", "Created"},
	}, writerConfig)
	defer writer.Close()

	for _, rtb := range rtbs {
		writer.Write(&rtb)
	}

	return writer.Err()
}

type principalGetter interface {
	ByID(id string) (*managementClient.Principal, error)
}

func getMemberNameFromPrincipal(principals principalGetter, principalID string) string {
	principal, err := principals.ByID(url.PathEscape(principalID))
	if err != nil {
		principal = parsePrincipalID(principalID)
	}

	return fmt.Sprintf(
		"%s (%s %s)",
		principal.Name,
		cases.Title(language.Und).String(principal.Provider),
		cases.Title(language.Und).String(principal.PrincipalType),
	)
}

func parsePrincipalID(principalID string) *managementClient.Principal {
	scheme, id, _ := strings.Cut(principalID, "://")
	provider, ptype, _ := strings.Cut(scheme, "_")

	if provider == "local" && ptype == "" {
		ptype = "user"
	}

	if ptype != "user" {
		ptype = "group"
	}

	return &managementClient.Principal{
		Name:          id,
		LoginName:     id,
		Provider:      provider,
		PrincipalType: ptype,
	}
}

func getKubeConfigForUser(ctx *cli.Context, user string) (*api.Config, error) {
	cf, err := loadConfig(ctx)
	if err != nil {
		return nil, err
	}

	focusedServer, err := cf.FocusedServer()
	if err != nil {
		return nil, err
	}

	kubeConfig := focusedServer.KubeConfigs[fmt.Sprintf(kubeConfigKeyFormat, user, focusedServer.FocusedCluster())]
	return kubeConfig, nil
}

func setKubeConfigForUser(ctx *cli.Context, user string, kubeConfig *api.Config) error {
	cf, err := loadConfig(ctx)
	if err != nil {
		return err
	}

	focusedServer, err := cf.FocusedServer()
	if err != nil {
		return err
	}

	if focusedServer.KubeConfigs == nil {
		focusedServer.KubeConfigs = make(map[string]*api.Config)
	}

	focusedServer.KubeConfigs[fmt.Sprintf(kubeConfigKeyFormat, user, focusedServer.FocusedCluster())] = kubeConfig
	return cf.Write()
}

func searchForMember(ctx *cli.Context, c *cliclient.MasterClient, name string) (*managementClient.Principal, error) {
	filter := defaultListOpts(ctx)
	filter.Filters["ID"] = "thisisnotathingIhope"

	// A collection is needed to get the action link
	pCollection, err := c.ManagementClient.Principal.List(filter)
	if err != nil {
		return nil, err
	}

	p := managementClient.SearchPrincipalsInput{
		Name: name,
	}

	results, err := c.ManagementClient.Principal.CollectionActionSearch(pCollection, &p)
	if err != nil {
		return nil, err
	}

	dataLength := len(results.Data)
	switch {
	case dataLength == 0:
		return nil, fmt.Errorf("no results found for %q", name)
	case dataLength == 1:
		return &results.Data[0], nil
	case dataLength >= 10:
		results.Data = results.Data[:10]
	}

	var names []string

	for _, person := range results.Data {
		names = append(names, person.Name+fmt.Sprintf(" (%s)", person.PrincipalType))
	}
	selection := selectFromList("Multiple results found:", names)

	return &results.Data[selection], nil
}

func loadAndVerifyCert(path string) (string, error) {
	caCert, err := os.ReadFile(path)
	if err != nil {
		return "", err
	}
	return verifyCert(caCert)
}

func verifyCert(caCert []byte) (string, error) {
	// replace the escaped version of the line break
	caCert = bytes.Replace(caCert, []byte(`\n`), []byte("\n"), -1)

	block, _ := pem.Decode(caCert)

	if nil == block {
		return "", errors.New("no cert was found")
	}

	parsedCert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return "", err
	}

	if !parsedCert.IsCA {
		return "", errors.New("caCerts is not valid")
	}
	return string(caCert), nil
}

func GetConfigPath(ctx *cli.Context) string {
	// path will always be set by the global flag default
	path := ctx.GlobalString("config")
	return filepath.Join(path, cfgFile)
}

func loadConfig(ctx *cli.Context) (config.Config, error) {
	path := GetConfigPath(ctx)
	return config.LoadFromPath(path)
}

func lookupConfig(ctx *cli.Context) (*config.ServerConfig, error) {
	cf, err := loadConfig(ctx)
	if err != nil {
		return nil, err
	}

	cs, err := cf.FocusedServer()
	if err != nil {
		return nil, err
	}

	return cs, nil
}

func GetClient(ctx *cli.Context) (*cliclient.MasterClient, error) {
	cf, err := lookupConfig(ctx)
	if err != nil {
		return nil, err
	}

	mc, err := cliclient.NewMasterClient(cf)
	if err != nil {
		return nil, err
	}

	return mc, nil
}

// GetResourceType maps an incoming resource type to a valid one from the schema
func GetResourceType(c *cliclient.MasterClient, resource string) (string, error) {
	if c.ManagementClient != nil {
		for key := range c.ManagementClient.APIBaseClient.Types {
			if strings.EqualFold(key, resource) {
				return key, nil
			}
		}
	}
	if c.ProjectClient != nil {
		for key := range c.ProjectClient.APIBaseClient.Types {
			if strings.EqualFold(key, resource) {
				return key, nil
			}
		}
	}
	if c.ClusterClient != nil {
		for key := range c.ClusterClient.APIBaseClient.Types {
			if strings.EqualFold(key, resource) {
				return key, nil
			}
		}
	}
	if c.CAPIClient != nil {
		for key := range c.CAPIClient.APIBaseClient.Types {
			lowerKey := strings.ToLower(key)
			if strings.HasPrefix(lowerKey, "cluster.x-k8s.io") && lowerKey == strings.ToLower(resource) {
				return key, nil
			}
		}
	}
	return "", fmt.Errorf("unknown resource type: %s", resource)
}

func Lookup(c *cliclient.MasterClient, name string, types ...string) (*ntypes.Resource, error) {
	var byName *ntypes.Resource

	for _, schemaType := range types {
		rt, err := GetResourceType(c, schemaType)
		if err != nil {
			logrus.Debugf("Error GetResourceType: %v", err)
			return nil, err
		}
		var schemaClient clientbase.APIBaseClientInterface
		// the schemaType dictates which client we need to use
		if c.CAPIClient != nil {
			if strings.HasPrefix(rt, "cluster.x-k8s.io") {
				schemaClient = c.CAPIClient
			}
		}
		if c.ManagementClient != nil {
			if _, ok := c.ManagementClient.APIBaseClient.Types[rt]; ok {
				schemaClient = c.ManagementClient
			}
		}
		if c.ProjectClient != nil {
			if _, ok := c.ProjectClient.APIBaseClient.Types[rt]; ok {
				schemaClient = c.ProjectClient
			}
		}
		if c.ClusterClient != nil {
			if _, ok := c.ClusterClient.APIBaseClient.Types[rt]; ok {
				schemaClient = c.ClusterClient
			}
		}

		// Attempt to get the resource by ID
		var resource ntypes.Resource

		if err := schemaClient.ByID(schemaType, name, &resource); !clientbase.IsNotFound(err) && err != nil {
			logrus.Debugf("Error schemaClient.ByID: %v", err)
			return nil, err
		} else if err == nil && resource.ID == name {
			return &resource, nil
		}

		// Resource was not found assuming the ID, check if it's the name of a resource
		var collection ntypes.ResourceCollection

		listOpts := &ntypes.ListOpts{
			Filters: map[string]interface{}{
				"name":         name,
				"removed_null": 1,
			},
		}

		if err := schemaClient.List(schemaType, listOpts, &collection); !clientbase.IsNotFound(err) && err != nil {
			logrus.Debugf("Error schemaClient.List: %v", err)
			return nil, err
		}

		if len(collection.Data) > 1 {
			ids := []string{}
			for _, data := range collection.Data {
				ids = append(ids, data.ID)
			}
			return nil, fmt.Errorf("Multiple resources of type %s found for name %s: %v", schemaType, name, ids)
		}

		// No matches for this schemaType, try the next one
		if len(collection.Data) == 0 {
			continue
		}

		if byName != nil {
			return nil, fmt.Errorf("Multiple resources named %s: %s:%s, %s:%s", name, collection.Data[0].Type,
				collection.Data[0].ID, byName.Type, byName.ID)
		}

		byName = &collection.Data[0]

	}

	if byName == nil {
		return nil, fmt.Errorf("Not found: %s", name)
	}

	return byName, nil
}

// RandomLetters returns a string with random letters of length n
func RandomLetters(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

func appendTabDelim(buf *bytes.Buffer, value string) {
	if buf.Len() == 0 {
		buf.WriteString(value)
	} else {
		buf.WriteString("\t")
		buf.WriteString(value)
	}
}

func SimpleFormat(values [][]string) (string, string) {
	headerBuffer := bytes.Buffer{}
	valueBuffer := bytes.Buffer{}
	for _, v := range values {
		appendTabDelim(&headerBuffer, v[0])
		if strings.Contains(v[1], "{{") {
			appendTabDelim(&valueBuffer, v[1])
		} else {
			appendTabDelim(&valueBuffer, "{{."+v[1]+"}}")
		}
	}

	headerBuffer.WriteString("\n")
	valueBuffer.WriteString("\n")

	return headerBuffer.String(), valueBuffer.String()
}

func defaultAction(fn func(ctx *cli.Context) error) func(ctx *cli.Context) error {
	return func(ctx *cli.Context) error {
		if ctx.Bool("help") {
			return cli.ShowAppHelp(ctx)
		}
		return fn(ctx)
	}
}

func printTemplate(out io.Writer, templateContent string, obj interface{}) error {
	funcMap := map[string]interface{}{
		"endpoint": FormatEndpoint,
		"ips":      FormatIPAddresses,
		"json":     FormatJSON,
	}
	tmpl, err := template.New("").Funcs(funcMap).Parse(templateContent)
	if err != nil {
		return err
	}

	return tmpl.Execute(out, obj)
}

func selectFromList(header string, choices []string) int {
	if header != "" {
		fmt.Println(header)
	}

	reader := bufio.NewReader(os.Stdin)
	selected := -1
	for selected <= 0 || selected > len(choices) {
		for i, choice := range choices {
			fmt.Printf("[%d] %s\n", i+1, choice)
		}
		fmt.Print("Select: ")

		text, _ := reader.ReadString('\n')
		text = strings.TrimSpace(text)
		num, err := strconv.Atoi(text)
		if err == nil {
			selected = num
		}
	}
	return selected - 1
}

func processExitCode(err error) error {
	if exitErr, ok := err.(*exec.ExitError); ok {
		if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
			os.Exit(status.ExitStatus())
		}
	}

	return err
}

func SplitOnColon(s string) []string {
	return strings.Split(s, ":")
}

func parseClusterAndProjectID(id string) (string, string, error) {
	// Validate id
	// Examples:
	// c-qmpbm:p-mm62v
	// c-qmpbm:project-mm62v
	// c-m-j2s7m6lq:p-mm62v
	// See https://github.com/rancher/rancher/issues/14400
	if match, _ := regexp.MatchString("((local)|(c-[[:alnum:]]{5})|(c-m-[[:alnum:]]{8})):(p|project)-[[:alnum:]]{5}", id); match {
		parts := SplitOnColon(id)
		return parts[0], parts[1], nil
	}
	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) {
	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)
}

// 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(ctx *cli.Context, c *cliclient.MasterClient) (map[string]string, error) {
	clusterNames := make(map[string]string)
	clusterCollection, err := c.ManagementClient.Cluster.List(defaultListOpts(ctx))
	if err != nil {
		return clusterNames, err
	}

	for _, cluster := range clusterCollection.Data {
		if cluster.Name == "" {
			clusterNames[cluster.ID] = cluster.ID
		} else {
			clusterNames[cluster.ID] = cluster.Name
		}
	}
	return clusterNames, nil
}

func getClusterName(cluster *managementClient.Cluster) string {
	if cluster.Name != "" {
		return cluster.Name
	}
	return cluster.ID
}

const humanTimeFormat = "02 Jan 2006 15:04:05 MST"

func createdTimetoHuman(t string) (string, error) {
	parsedTime, err := time.Parse(time.RFC3339, t)
	if err != nil {
		return "", err
	}
	return parsedTime.Format(humanTimeFormat), nil
}

func ConfigDir() (string, error) {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(homeDir, ".rancher"), nil
}

func newHTTPClient(serverConfig *config.ServerConfig, tlsConfig *tls.Config) (*http.Client, error) {
	var proxy func(*http.Request) (*url.URL, error)
	if serverConfig.ProxyURL != "" {
		proxyURL, err := url.Parse(serverConfig.ProxyURL)
		if err != nil {
			return nil, fmt.Errorf("invalid proxy address %s: %w", serverConfig.ProxyURL, err)
		}
		proxy = http.ProxyURL(proxyURL)
	} else {
		proxy = http.ProxyFromEnvironment
	}

	tr := &http.Transport{
		Proxy: proxy,
	}
	if tlsConfig != nil {
		tr.TLSClientConfig = tlsConfig
	}

	timeout := serverConfig.GetHTTPTimeout()
	if timeout == 0 {
		timeout = defaultHTTPTimeout
	}

	return &http.Client{
		Transport: tr,
		Timeout:   timeout,
	}, nil
}
0707010000000D000081A400000000000000000000000168B0FB3F00001957000000000000000000000000000000000000002600000000rancher-cli-2.12.1/cmd/common_test.gopackage cmd

import (
	"crypto/tls"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"testing"
	"time"

	"github.com/rancher/cli/config"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestParseClusterAndProjectID(t *testing.T) {
	t.Parallel()

	tests := []struct {
		id,
		cluster string
		project   string
		shouldErr bool
	}{
		{
			id:      "local:p-12345",
			cluster: "local",
			project: "p-12345",
		},
		{
			id:      "c-12345:p-12345",
			cluster: "c-12345",
			project: "p-12345",
		},
		{
			id:        "cocal:p-12345",
			shouldErr: true,
		},
		{
			id:        "c-123:p-123",
			shouldErr: true,
		},
		{
			shouldErr: true,
		},
		{
			id:      "c-m-12345678:p-12345",
			cluster: "c-m-12345678",
			project: "p-12345",
		},
		{
			id:        "c-m-123:p-12345",
			shouldErr: true,
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.id, func(t *testing.T) {
			t.Parallel()

			cluster, project, err := parseClusterAndProjectID(test.id)
			if test.shouldErr {
				require.Error(t, err)
				return
			}

			require.NoError(t, err)
			assert.Equal(t, test.cluster, cluster)
			assert.Equal(t, test.project, project)
		})
	}
}

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 {
		test := test
		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()

	tests := []struct {
		id   string
		want *managementClient.Principal
	}{
		{
			id: "local://user-2p7w6",
			want: &managementClient.Principal{
				Name:          "user-2p7w6",
				LoginName:     "user-2p7w6",
				Provider:      "local",
				PrincipalType: "user",
			},
		},
		{
			id: "okta_group://b4qkhsnliz",
			want: &managementClient.Principal{
				Name:          "b4qkhsnliz",
				LoginName:     "b4qkhsnliz",
				Provider:      "okta",
				PrincipalType: "group",
			},
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.id, func(t *testing.T) {
			t.Parallel()

			assert.Equal(t, test.want, parsePrincipalID(test.id))
		})
	}
}

func TestGetMemberNameFromPrincipal(t *testing.T) {
	t.Parallel()

	principals := &fakePrincipalGetter{
		ByIDFunc: func(id string) (*managementClient.Principal, error) {
			id, err := url.PathUnescape(id)
			require.NoError(t, err)

			switch id {
			case "local://user-2p7w6":
				return &managementClient.Principal{
					Name:          "Default Admin",
					LoginName:     "admin",
					Provider:      "local",
					PrincipalType: "user",
				}, nil
			case "okta_group://b4qkhsnliz":
				return &managementClient.Principal{
					Name:          "DevOps",
					LoginName:     "devops",
					Provider:      "okta",
					PrincipalType: "group",
				}, nil
			default:
				return nil, fmt.Errorf("not found")
			}
		},
	}

	tests := []struct {
		id   string
		want string
	}{
		{
			id:   "local://user-2p7w6",
			want: "Default Admin (Local User)",
		},
		{
			id:   "okta_group://b4qkhsnliz",
			want: "DevOps (Okta Group)",
		},
		{
			id:   "okta_user://lfql6h5tmh",
			want: "lfql6h5tmh (Okta User)",
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.id, func(t *testing.T) {
			t.Parallel()

			got := getMemberNameFromPrincipal(principals, test.id)
			assert.Equal(t, test.want, got)
		})
	}
}

func TestNewHTTPClient(t *testing.T) {
	t.Run("default timeout and no proxy", func(t *testing.T) {
		serverConfig := &config.ServerConfig{}
		tlsConfig := &tls.Config{InsecureSkipVerify: true}

		client, err := newHTTPClient(serverConfig, tlsConfig)
		require.NoError(t, err)

		assert.Equal(t, defaultHTTPTimeout, client.Timeout)

		transport, ok := client.Transport.(*http.Transport)
		require.True(t, ok)

		assert.True(t, transport.TLSClientConfig.InsecureSkipVerify)
	})

	t.Run("set timeout", func(t *testing.T) {
		serverConfig := &config.ServerConfig{
			HTTPTimeoutSeconds: 30,
		}

		client, err := newHTTPClient(serverConfig, nil)
		require.NoError(t, err)

		assert.Equal(t, 30*time.Second, client.Timeout)
	})

	t.Run("explicitly set proxy URL", func(t *testing.T) {
		httpProxy := "http://corp.example.com:8080"
		serverConfig := &config.ServerConfig{
			ProxyURL: httpProxy,
		}

		client, err := newHTTPClient(serverConfig, nil)
		require.NoError(t, err)

		transport, ok := client.Transport.(*http.Transport)
		require.True(t, ok)

		req, err := http.NewRequest(http.MethodGet, "http://example.com", nil)
		require.NoError(t, err)

		proxyURL, err := transport.Proxy(req)
		require.NoError(t, err)
		require.NotNil(t, proxyURL)
		assert.Equal(t, httpProxy, proxyURL.String())
	})

	t.Run("invalid proxy URL", func(t *testing.T) {
		invalidURL := "http://corp .example.com:8080"
		serverConfig := &config.ServerConfig{
			ProxyURL: invalidURL,
		}

		_, err := newHTTPClient(serverConfig, nil)
		require.Error(t, err)
	})

	t.Run("set proxy via env vars", func(t *testing.T) {
		httpProxy := "http://corp.example.com:8080"
		t.Setenv("HTTP_PROXY", httpProxy)
		t.Setenv("NO_PROXY", "foo.com")

		serverConfig := &config.ServerConfig{}

		client, err := newHTTPClient(serverConfig, nil)
		require.NoError(t, err)

		transport, ok := client.Transport.(*http.Transport)
		require.True(t, ok)

		req, err := http.NewRequest(http.MethodGet, "http://example.com", nil)
		require.NoError(t, err)

		proxyURL, err := transport.Proxy(req)
		require.NoError(t, err)
		require.NotNil(t, proxyURL)
		assert.Equal(t, httpProxy, proxyURL.String())

		req, err = http.NewRequest(http.MethodGet, "http://foo.com", nil)
		require.NoError(t, err)

		proxyURL, err = transport.Proxy(req)
		require.NoError(t, err)
		require.Nil(t, proxyURL)
	})
}
0707010000000E000081A400000000000000000000000168B0FB3F0000064C000000000000000000000000000000000000002200000000rancher-cli-2.12.1/cmd/context.gopackage cmd

import (
	"github.com/rancher/cli/cliclient"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

func ContextCommand() cli.Command {
	return cli.Command{
		Name:  "context",
		Usage: "Operations for the context",
		Description: `Switch or view context. A context is the server->cluster->project currently in focus.
`,
		Subcommands: []cli.Command{
			{
				Name:  "switch",
				Usage: "Switch to a new context",
				Description: `
The project arg is optional, if not passed in a list of available projects will 
be displayed and one can be selected. If only one project is available it will 
be automatically selected.
`,
				ArgsUsage: "[PROJECT_ID/PROJECT_NAME]",
				Action:    contextSwitch,
			},
			{
				Name:   "current",
				Usage:  "Display the current context",
				Action: loginContext,
			},
		},
	}
}

func contextSwitch(ctx *cli.Context) error {
	cf, err := loadConfig(ctx)
	if err != nil {
		return err
	}

	server, err := cf.FocusedServer()
	if err != nil {
		return err
	}

	c, err := cliclient.NewManagementClient(server)
	if err != nil {
		return err
	}

	var projectID string

	if ctx.NArg() == 0 {
		projectID, err = getProjectContext(ctx, c)
		if err != nil {
			return nil
		}
	} else {
		resource, err := Lookup(c, ctx.Args().First(), "project")
		if err != nil {
			return err
		}
		projectID = resource.ID
	}

	project, err := c.ManagementClient.Project.ByID(projectID)
	if err != nil {
		return nil
	}

	logrus.Infof("Setting new context to project %s", project.Name)

	server.Project = project.ID

	err = cf.Write()
	if err != nil {
		return err
	}

	return nil
}
0707010000000F000081A400000000000000000000000168B0FB3F000003DA000000000000000000000000000000000000002100000000rancher-cli-2.12.1/cmd/format.gopackage cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
)

func FormatEndpoint(data interface{}) string {
	dataSlice, ok := data.([]interface{})
	if !ok {
		return ""
	}

	buf := &bytes.Buffer{}
	for _, value := range dataSlice {
		dataMap, ok := value.(map[string]interface{})
		if !ok {
			return ""
		}

		s := fmt.Sprintf("%v:%v", dataMap["ipAddress"], dataMap["port"])
		if buf.Len() == 0 {
			buf.WriteString(s)
		} else {
			buf.WriteString(", ")
			buf.WriteString(s)
		}
	}

	return buf.String()
}

func FormatIPAddresses(data interface{}) string {
	//todo: revisit
	return ""
	//ips, ok := data.([]client.IpAddress)
	//if !ok {
	//	return ""
	//}
	//
	//ipStrings := []string{}
	//for _, ip := range ips {
	//	if ip.Address != "" {
	//		ipStrings = append(ipStrings, ip.Address)
	//	}
	//}
	//
	//return strings.Join(ipStrings, ", ")
}

func FormatJSON(data interface{}) (string, error) {
	bytes, err := json.MarshalIndent(data, "", "    ")
	return string(bytes) + "\n", err
}
07070100000010000081A400000000000000000000000168B0FB3F00000745000000000000000000000000000000000000002200000000rancher-cli-2.12.1/cmd/inspect.gopackage cmd

import (
	"strings"

	"github.com/urfave/cli"
)

func InspectCommand() cli.Command {
	return cli.Command{
		Name:  "inspect",
		Usage: "View details of resources",
		Description: `
Inspect resources by name or ID in the current context. If the 'type' is not specified inspect will search: ` + strings.Join(listAllRoles(), ", ") + `
Examples:
	# Specify the type
	$ rancher inspect --type cluster clusterFoo

	# No type is specified so defaults are checked
	$ rancher inspect myvolume

	# Inspect a project and get the output in yaml format with the projects links
	$ rancher inspect --type project --format yaml --links projectFoo
`,
		ArgsUsage: "[RESOURCEID RESOURCENAME]",
		Action:    inspectResources,
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "links",
				Usage: "Include URLs to actions and links in resource output",
			},
			cli.StringFlag{
				Name:  "type",
				Usage: "Specify the type of resource to inspect",
			},
			cli.StringFlag{
				Name:  "format",
				Usage: "'json', 'yaml' or Custom format: '{{.kind}}'",
				Value: "json",
			},
		},
	}
}

func inspectResources(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowCommandHelp(ctx, "inspect")
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	t := ctx.String("type")
	types := []string{}
	if t != "" {
		rt, err := GetResourceType(c, t)
		if err != nil {
			return err
		}
		types = append(types, rt)
	} else {
		types = listAllRoles()
	}

	resource, err := Lookup(c, ctx.Args().First(), types...)
	if err != nil {
		return err
	}
	mapResource := map[string]interface{}{}

	err = c.ByID(resource, &mapResource)
	if err != nil {
		return err
	}

	if !ctx.Bool("links") {
		delete(mapResource, "links")
		delete(mapResource, "actions")
	}
	writer := NewTableWriter(nil, ctx)
	writer.Write(mapResource)
	writer.Close()

	return writer.Err()
}
07070100000011000081A400000000000000000000000168B0FB3F00000D31000000000000000000000000000000000000002200000000rancher-cli-2.12.1/cmd/kubectl.gopackage cmd

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/rancher/norman/clientbase"
	client "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/urfave/cli"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/clientcmd/api"
)

func KubectlCommand() cli.Command {
	return cli.Command{
		Name:            "kubectl",
		Usage:           "Run kubectl commands",
		Description:     "Use the current cluster context to run kubectl commands in the cluster",
		Action:          runKubectl,
		SkipFlagParsing: true,
	}
}

func runKubectl(ctx *cli.Context) error {
	args := ctx.Args()
	if len(args) > 0 && (args[0] == "-h" || args[0] == "--help") {
		return cli.ShowCommandHelp(ctx, "kubectl")
	}

	path, err := exec.LookPath("kubectl")
	if err != nil {
		return fmt.Errorf("kubectl is required to be set in your path to use this "+
			"command. See https://kubernetes.io/docs/tasks/tools/install-kubectl/ "+
			"for more info. Error: %s", err.Error())
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	config, err := loadConfig(ctx)
	if err != nil {
		return err
	}

	currentRancherServer, err := config.FocusedServer()
	if err != nil {
		return err
	}

	currentToken := currentRancherServer.AccessKey
	t, err := c.ManagementClient.Token.ByID(currentToken)
	if err != nil {
		return err
	}

	currentUser := t.UserID
	kubeConfig, err := getKubeConfigForUser(ctx, currentUser)
	if err != nil {
		return err
	}

	var isTokenValid bool
	if kubeConfig != nil {
		tokenID, err := extractKubeconfigTokenID(*kubeConfig)
		if err != nil {
			return err
		}
		isTokenValid, err = validateToken(tokenID, c.ManagementClient.Token)
		if err != nil {
			return err
		}
	}

	if kubeConfig == nil || !isTokenValid {
		cluster, err := getClusterByID(c, c.UserConfig.FocusedCluster())
		if err != nil {
			return err
		}

		config, err := c.ManagementClient.Cluster.ActionGenerateKubeconfig(cluster)
		if err != nil {
			return err
		}

		kubeConfigBytes := []byte(config.Config)
		kubeConfig, err = clientcmd.Load(kubeConfigBytes)
		if err != nil {
			return err
		}

		if err := setKubeConfigForUser(ctx, currentUser, kubeConfig); err != nil {
			return err
		}
	}

	tmpfile, err := os.CreateTemp("", "rancher-")
	if err != nil {
		return err
	}
	defer os.Remove(tmpfile.Name())

	if err := clientcmd.WriteToFile(*kubeConfig, tmpfile.Name()); err != nil {
		return err
	}
	if err := tmpfile.Close(); err != nil {
		return err
	}

	cmd := exec.Command(path, ctx.Args()...)
	cmd.Env = append(os.Environ(), "KUBECONFIG="+tmpfile.Name())
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err = cmd.Run()
	if err != nil {
		return err
	}
	return nil
}

func extractKubeconfigTokenID(kubeconfig api.Config) (string, error) {
	if len(kubeconfig.AuthInfos) != 1 {
		return "", fmt.Errorf("invalid kubeconfig, expected to contain exactly 1 user")
	}
	var parts []string
	for _, val := range kubeconfig.AuthInfos {
		parts = strings.Split(val.Token, ":")
		if len(parts) != 2 {
			return "", fmt.Errorf("failed to parse kubeconfig token")
		}
	}

	return parts[0], nil
}

func validateToken(tokenID string, tokenClient client.TokenOperations) (bool, error) {
	token, err := tokenClient.ByID(tokenID)
	if err != nil {
		if !clientbase.IsNotFound(err) {
			return false, err
		}
		return false, nil
	}
	return !token.Expired, nil
}
07070100000012000081A400000000000000000000000168B0FB3F00004322000000000000000000000000000000000000002800000000rancher-cli-2.12.1/cmd/kubectl_token.gopackage cmd

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"math/big"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/rancher/cli/config"
	apiv3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/tidwall/gjson"
	"github.com/urfave/cli"
	"golang.org/x/term"
)

const deleteExample = `
Example:
	# Delete a cached credential
	$ rancher token delete cluster1_c-1234

	# Delete multiple cached credentials
	$ rancher token delete cluster1_c-1234 cluster2_c-2345

	# Delete all credentials
	$ rancher token delete all
`

type LoginInput struct {
	server       string
	userID       string
	clusterID    string
	authProvider string
	caCerts      string
	skipVerify   bool
}

const (
	authProviderURL = "%s/v3-public/authProviders"
	authTokenURL    = "%s/v3-public/authTokens/%s"
)

var samlProviders = map[string]bool{
	"pingProvider":       true,
	"adfsProvider":       true,
	"keyCloakProvider":   true,
	"oktaProvider":       true,
	"shibbolethProvider": true,
}

var oauthProviders = map[string]bool{
	"azureADProvider": true,
}

var supportedAuthProviders = map[string]bool{
	"localProvider":           true,
	"freeIpaProvider":         true,
	"openLdapProvider":        true,
	"activeDirectoryProvider": true,

	// all saml providers
	"pingProvider":       true,
	"adfsProvider":       true,
	"keyCloakProvider":   true,
	"oktaProvider":       true,
	"shibbolethProvider": true,

	// oauth providers
	"azureADProvider": true,
}

func CredentialCommand() cli.Command {
	configDir, err := ConfigDir()
	if err != nil {
		if runtime.GOOS == "windows" {
			configDir = "%HOME%\\.rancher"
		} else {
			configDir = "${HOME}/.rancher"
		}
	}
	return cli.Command{
		Name:   "token",
		Usage:  "Authenticate and generate new kubeconfig token",
		Action: runCredential,
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "server",
				Usage: "Name of rancher server",
			},
			cli.StringFlag{
				Name:  "user",
				Usage: "user-id",
			},
			cli.StringFlag{
				Name:  "cluster",
				Usage: "cluster-id",
			},
			cli.StringFlag{
				Name:  "auth-provider",
				Usage: "Name of Auth Provider to use for authentication",
			},
			cli.StringFlag{
				Name:  "cacerts",
				Usage: "Location of CaCerts to use",
			},
			cli.BoolFlag{
				Name:  "skip-verify",
				Usage: "Skip verification of the CACerts presented by the Server",
			},
		},
		Subcommands: []cli.Command{
			{
				Name:   "delete",
				Usage:  fmt.Sprintf("Delete cached token used for kubectl login at [%s] \n %s", configDir, deleteExample),
				Action: deleteCachedCredential,
			},
		},
	}
}

func runCredential(ctx *cli.Context) error {
	server := ctx.String("server")
	if server == "" {
		return errors.New("name of rancher server is required")
	}

	serverURL, err := url.Parse(server)
	if err != nil {
		return err
	}
	if serverURL.Scheme == "" {
		server = fmt.Sprintf("https://%s", server)
	}

	userID := ctx.String("user")
	if userID == "" {
		return errors.New("user-id is required")
	}
	clusterID := ctx.String("cluster")

	serverConfig, err := lookupServerConfig(ctx)
	if err != nil {
		return fmt.Errorf("error looking up server config: %w", err)
	}

	cachedCredName := fmt.Sprintf("%s_%s", userID, clusterID)
	cachedCred, err := loadCachedCredential(ctx, serverConfig, cachedCredName)
	if err != nil {
		customPrint(fmt.Errorf("LoadToken: %v", err))
	}
	if cachedCred != nil {
		return json.NewEncoder(os.Stdout).Encode(cachedCred)
	}

	input := &LoginInput{
		server:       server,
		userID:       userID,
		clusterID:    clusterID,
		authProvider: ctx.String("auth-provider"),
		caCerts:      ctx.String("cacerts"),
		skipVerify:   ctx.Bool("skip-verify"),
	}

	tlsConfig, err := getTLSConfig(input.skipVerify, input.caCerts)
	if err != nil {
		return err
	}

	client, err := newHTTPClient(serverConfig, tlsConfig)
	if err != nil {
		return err
	}

	newCred, err := loginAndGenerateCred(client, input)
	if err != nil {
		return err
	}

	if err := cacheCredential(ctx, serverConfig, cachedCredName, newCred); err != nil {
		customPrint(fmt.Errorf("CacheToken: %v", err))
	}

	return json.NewEncoder(os.Stdout).Encode(newCred)
}

func deleteCachedCredential(ctx *cli.Context) error {
	if len(ctx.Args()) == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}

	cf, err := loadConfig(ctx)
	if err != nil {
		return err
	}

	// dir is always set by global default.
	dir := ctx.GlobalString("config")

	if len(cf.Servers) == 0 {
		customPrint(fmt.Sprintf("there are no cached tokens in [%s]", dir))
		return nil
	}

	if ctx.Args().First() == "all" {
		customPrint(fmt.Sprintf("removing cached tokens in [%s]", dir))
		for _, server := range cf.Servers {
			server.KubeCredentials = make(map[string]*config.ExecCredential)
		}
		return cf.Write()
	}

	for _, key := range ctx.Args() {
		customPrint(fmt.Sprintf("removing [%s]", key))
		for _, server := range cf.Servers {
			delete(server.KubeCredentials, key)
		}
	}

	return cf.Write()
}

func loadCachedCredential(ctx *cli.Context, serverConfig *config.ServerConfig, key string) (*config.ExecCredential, error) {
	cred := serverConfig.KubeToken(key)
	if cred == nil {
		return cred, nil
	}
	ts := cred.Status.ExpirationTimestamp
	if ts != nil && ts.Time.Before(time.Now()) {
		cf, err := loadConfig(ctx)
		if err != nil {
			return nil, err
		}
		cf.Servers[ctx.String("server")].KubeCredentials[key] = nil
		if err := cf.Write(); err != nil {
			return nil, err
		}
		return nil, nil
	}

	return cred, nil
}

// there is overlap between this and the lookupConfig() function. However, lookupConfig() requires
// a server to be previously set in the Config, which might not be the case if rancher token
// is run before rancher login. Perhaps we can depricate rancher token down the line and defer
// all it does to login.
func lookupServerConfig(ctx *cli.Context) (*config.ServerConfig, error) {
	server := ctx.String("server")
	if server == "" {
		return nil, errors.New("name of rancher server is required")
	}

	cf, err := loadConfig(ctx)
	if err != nil {
		return nil, err
	}

	sc := cf.Servers[server]
	if sc == nil {
		sc = &config.ServerConfig{
			KubeCredentials: make(map[string]*config.ExecCredential),
		}
		cf.Servers[server] = sc
		if err := cf.Write(); err != nil {
			return nil, err
		}
	}
	return sc, nil
}

func cacheCredential(ctx *cli.Context, serverConfig *config.ServerConfig, key string, cred *config.ExecCredential) error {
	// cache only if valid
	if cred.Status.Token == "" {
		return nil
	}

	server := ctx.String("server")
	if server == "" {
		return errors.New("name of rancher server is required")
	}

	cf, err := loadConfig(ctx)
	if err != nil {
		return err
	}

	if serverConfig.KubeCredentials == nil {
		serverConfig.KubeCredentials = make(map[string]*config.ExecCredential)
	}
	serverConfig.KubeCredentials[key] = cred
	cf.Servers[server] = serverConfig

	return cf.Write()
}

func loginAndGenerateCred(client *http.Client, input *LoginInput) (*config.ExecCredential, error) {
	authProviders, err := getAuthProviders(client, input.server)
	if err != nil {
		return nil, err
	}

	selectedProvider, err := selectAuthProvider(authProviders, input.authProvider)
	if err != nil {
		return nil, err
	}
	input.authProvider = selectedProvider.GetType()

	token := managementClient.Token{}
	if samlProviders[input.authProvider] {
		token, err = samlAuth(client, input)
		if err != nil {
			return nil, err
		}
	} else if oauthProviders[input.authProvider] {
		tokenPtr, err := oauthAuth(client, input, selectedProvider)
		if err != nil {
			return nil, err
		}
		token = *tokenPtr
	} else {
		customPrint(fmt.Sprintf("Enter credentials for %s \n", input.authProvider))
		token, err = basicAuth(client, input)
		if err != nil {
			return nil, err
		}
	}

	cred := &config.ExecCredential{
		TypeMeta: config.TypeMeta{
			Kind:       "ExecCredential",
			APIVersion: "client.authentication.k8s.io/v1beta1",
		},
		Status: &config.ExecCredentialStatus{},
	}
	cred.Status.Token = token.Token
	if token.ExpiresAt == "" {
		return cred, nil
	}
	ts, err := time.Parse(time.RFC3339, token.ExpiresAt)
	if err != nil {
		customPrint(fmt.Sprintf("\n error parsing time %s %v", token.ExpiresAt, err))
		return nil, err
	}
	cred.Status.ExpirationTimestamp = &config.Time{Time: ts}
	return cred, nil

}

func basicAuth(client *http.Client, input *LoginInput) (managementClient.Token, error) {
	token := managementClient.Token{}
	username, err := customPrompt("Enter username: ", true)
	if err != nil {
		return token, err
	}

	password, err := customPrompt("Enter password: ", false)
	if err != nil {
		return token, err
	}

	responseType := "kubeconfig"
	if input.clusterID != "" {
		responseType = fmt.Sprintf("%s_%s", responseType, input.clusterID)
	}

	reqBody := fmt.Sprintf(`{"responseType":%q, "username":%q, "password":%q}`, responseType, username, password)

	loginURL := fmt.Sprintf("%s/v3-public/%ss/%s?action=login", input.server, input.authProvider,
		strings.ToLower(strings.Replace(input.authProvider, "Provider", "", 1)))

	req, err := http.NewRequest(http.MethodPost, loginURL, bytes.NewBufferString(reqBody))
	if err != nil {
		return token, fmt.Errorf("error creating request: %w", err)
	}

	resp, respBody, err := doRequest(client, req)
	if err == nil && resp.StatusCode != http.StatusCreated {
		err = fmt.Errorf("unexpected http status code %d", resp.StatusCode)

		apiError := map[string]interface{}{}
		if rerr := json.Unmarshal(respBody, &apiError); rerr == nil {
			if responseType := apiError["type"]; responseType == "error" {
				err = fmt.Errorf("error logging user in: code: [%v] message:[%v]", apiError["code"], apiError["message"])
			}
		}
	}
	if err != nil {
		return token, fmt.Errorf("error logging user in: %w", err)
	}

	err = json.Unmarshal(respBody, &token)
	if err != nil {
		return token, err
	}

	return token, nil
}

func samlAuth(client *http.Client, input *LoginInput) (managementClient.Token, error) {
	token := managementClient.Token{}

	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return token, err
	}
	publicKey := privateKey.PublicKey
	marshalKey, err := json.Marshal(publicKey)
	if err != nil {
		return token, err
	}
	encodedKey := base64.StdEncoding.EncodeToString(marshalKey)

	id, err := generateKey()
	if err != nil {
		return token, err
	}

	responseType := "kubeconfig"
	if input.clusterID != "" {
		responseType = fmt.Sprintf("%s_%s", responseType, input.clusterID)
	}

	tokenURL := fmt.Sprintf(authTokenURL, input.server, id)

	getReq, err := http.NewRequest(http.MethodGet, tokenURL, bytes.NewBuffer(nil))
	if err != nil {
		return token, err
	}
	getReq.Header.Set("content-type", "application/json")
	getReq.Header.Set("accept", "application/json")

	deleteReq, err := http.NewRequest(http.MethodDelete, tokenURL, bytes.NewBuffer(nil))
	if err != nil {
		return token, err
	}
	deleteReq.Header.Set("content-type", "application/json")
	deleteReq.Header.Set("accept", "application/json")

	loginURL := fmt.Sprintf("%s/dashboard/auth/login?requestId=%s&publicKey=%s&responseType=%s",
		input.server, id, encodedKey, responseType)

	customPrint(fmt.Sprintf("\nLogin to Rancher Server at %s \n", loginURL))

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	// Timeout for the login flow.
	timeout := time.NewTimer(15 * time.Minute)
	defer timeout.Stop()
	// Poll for the auth token every 10 seconds.
	poll := time.NewTicker(10 * time.Second)
	defer poll.Stop()

loop:
	for {
		select {
		case <-poll.C:
			// Fetch the auth token.
			_, respBody, err := doRequest(client, getReq)
			if err != nil {
				return token, err
			}
			err = json.Unmarshal(respBody, &token)
			if err != nil {
				return token, err
			}
			if token.Token == "" {
				continue
			}
			decoded, err := base64.StdEncoding.DecodeString(token.Token)
			if err != nil {
				return token, err
			}
			decryptedBytes, err := privateKey.Decrypt(nil, decoded, &rsa.OAEPOptions{Hash: crypto.SHA256})
			if err != nil {
				panic(err)
			}
			token.Token = string(decryptedBytes)

			// Delete the auth token.
			resp, _, err := doRequest(client, deleteReq)
			if err == nil && resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusNotFound {
				err = fmt.Errorf("unexpected http status code %d", resp.StatusCode)
			}
			if err != nil {
				// Log the error and move on.
				customPrint(fmt.Errorf("error deleting auth token: %s", err))
			}

			return token, nil

		case <-timeout.C:
			customPrint("timed out waiting for the auth token")
			break loop

		case <-interrupt:
			customPrint("interrupted waiting for the auth token")
			break loop
		}
	}

	return token, nil
}

type TypedProvider interface {
	GetType() string
}

func getAuthProviders(client *http.Client, server string) ([]TypedProvider, error) {
	authProvidersURL := fmt.Sprintf(authProviderURL, server)
	req, err := http.NewRequest(http.MethodGet, authProvidersURL, nil)
	if err != nil {
		return nil, fmt.Errorf("error creating request: %w", err)
	}

	resp, respBody, err := doRequest(client, req)
	if err == nil && resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("unexpected http status code %d", resp.StatusCode)
	}
	if err != nil {
		return nil, fmt.Errorf("error listing auth providers: %w", err)
	}

	if !gjson.ValidBytes(respBody) {
		return nil, fmt.Errorf("invalid JSON response from %s", authProvidersURL)
	}
	data := gjson.GetBytes(respBody, "data").Array()

	var supportedProviders []TypedProvider
	for _, provider := range data {
		providerType := provider.Get("type").String()

		if providerType != "" && supportedAuthProviders[providerType] {
			var typedProvider TypedProvider

			switch providerType {
			case "azureADProvider":
				typedProvider = &apiv3.AzureADProvider{}
			case "localProvider":
				typedProvider = &apiv3.LocalProvider{}
			default:
				typedProvider = &apiv3.AuthProvider{}
			}

			err = json.Unmarshal([]byte(provider.Raw), typedProvider)
			if err != nil {
				return nil, fmt.Errorf("attempting to decode the auth provider of type %s: %w", providerType, err)
			}

			if typedProvider.GetType() == "localProvider" {
				supportedProviders = append([]TypedProvider{typedProvider}, supportedProviders...)
			} else {
				supportedProviders = append(supportedProviders, typedProvider)
			}
		}
	}

	return supportedProviders, err
}

func selectAuthProvider(authProviders []TypedProvider, providerType string) (TypedProvider, error) {
	if len(authProviders) == 0 {
		return nil, errors.New("no auth provider configured")
	}

	// if providerType was specified, look for it
	if providerType != "" {
		for _, p := range authProviders {
			if p.GetType() == providerType {
				return p, nil
			}
		}
		return nil, fmt.Errorf("provider %s not found", providerType)
	}

	// otherwise ask to the user (if more than one)
	if len(authProviders) == 1 {
		return authProviders[0], nil
	}

	var providers []string
	for i, val := range authProviders {
		providers = append(providers, fmt.Sprintf("%d - %s", i, val.GetType()))
	}

	for try := 0; try < 3; try++ {
		customPrint(fmt.Sprintf("Auth providers:\n%v", strings.Join(providers, "\n")))
		providerIndexStr, err := customPrompt("Select auth provider: ", true)
		if err != nil {
			continue
		}

		providerIndex, err := strconv.Atoi(providerIndexStr)
		if err != nil || (providerIndex < 0 || providerIndex > len(providers)-1) {
			customPrint("Pick a valid auth provider")
			continue
		}

		return authProviders[providerIndex], nil
	}

	return nil, errors.New("invalid auth provider")
}

func generateKey() (string, error) {
	characters := "abcdfghjklmnpqrstvwxz12456789"
	tokenLength := 32
	token := make([]byte, tokenLength)
	for i := range token {
		r, err := rand.Int(rand.Reader, big.NewInt(int64(len(characters))))
		if err != nil {
			return "", err
		}
		token[i] = characters[r.Int64()]
	}

	return string(token), nil
}

func getTLSConfig(skipVerify bool, caCerts string) (*tls.Config, error) {
	config := &tls.Config{
		InsecureSkipVerify: skipVerify,
	}

	if caCerts == "" {
		return config, nil
	}

	// load custom certs
	cert, err := loadAndVerifyCert(caCerts)
	if err != nil {
		return nil, err
	}

	roots := x509.NewCertPool()
	ok := roots.AppendCertsFromPEM([]byte(cert))
	if !ok {
		return nil, err
	}
	config.RootCAs = roots

	return config, nil
}

func doRequest(client *http.Client, req *http.Request) (*http.Response, []byte, error) {
	resp, err := client.Do(req)
	if err != nil {
		return resp, nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return resp, nil, err
	}

	return resp, body, nil
}

func customPrompt(msg string, show bool) (result string, err error) {
	fmt.Fprint(os.Stderr, msg)
	if show {
		_, err = fmt.Fscan(os.Stdin, &result)
	} else {
		var data []byte
		data, err = term.ReadPassword(int(os.Stdin.Fd()))
		result = string(data)
		fmt.Fprintf(os.Stderr, "\n")
	}
	return result, err
}

func customPrint(data interface{}) {
	fmt.Fprintf(os.Stderr, "%v \n", data)
}
07070100000013000081A400000000000000000000000168B0FB3F00000B4F000000000000000000000000000000000000002E00000000rancher-cli-2.12.1/cmd/kubectl_token_oauth.gopackage cmd

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	apiv3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"golang.org/x/oauth2"
)

func oauthAuth(client *http.Client, input *LoginInput, provider TypedProvider) (*managementClient.Token, error) {
	oauthConfig, err := newOauthConfig(provider)
	if err != nil {
		return nil, err
	}

	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, client) // Set the custom HTTP client.

	deviceAuthResp, err := oauthConfig.DeviceAuth(ctx)
	if err != nil {
		return nil, err
	}

	customPrint(fmt.Sprintf(
		"\nTo sign in, use a web browser to open the page %s and enter the code %s to authenticate.\n",
		deviceAuthResp.VerificationURI,
		deviceAuthResp.UserCode,
	))

	oauthToken, err := oauthConfig.DeviceAccessToken(ctx, deviceAuthResp)
	if err != nil {
		return nil, err
	}

	token, err := rancherLogin(client, input, provider, oauthToken)
	if err != nil {
		return nil, fmt.Errorf("error during rancher login: %w", err)
	}

	return token, nil
}

func newOauthConfig(provider TypedProvider) (*oauth2.Config, error) {
	var oauthProvider apiv3.OAuthProvider

	switch p := provider.(type) {
	case *apiv3.AzureADProvider:
		oauthProvider = p.OAuthProvider
	default:
		return nil, fmt.Errorf("provider %s is not a supported OAuth provider", provider.GetType())
	}

	return &oauth2.Config{
		ClientID: oauthProvider.ClientID,
		Scopes:   oauthProvider.Scopes,
		Endpoint: oauth2.Endpoint{
			AuthURL:       oauthProvider.AuthURL,
			DeviceAuthURL: oauthProvider.DeviceAuthURL,
			TokenURL:      oauthProvider.TokenURL,
		},
	}, nil
}

func rancherLogin(client *http.Client, input *LoginInput, provider TypedProvider, oauthToken *oauth2.Token) (*managementClient.Token, error) {
	// login with id_token
	providerName := strings.ToLower(strings.TrimSuffix(input.authProvider, "Provider"))
	url := fmt.Sprintf("%s/v3-public/%ss/%s?action=login", input.server, provider.GetType(), providerName)

	responseType := "kubeconfig"
	if input.clusterID != "" {
		responseType = fmt.Sprintf("%s_%s", responseType, input.clusterID)
	}

	jsonBody, err := json.Marshal(map[string]interface{}{
		"responseType": responseType,
		"id_token":     oauthToken.Extra("id_token"),
	})
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(jsonBody))
	if err != nil {
		return nil, fmt.Errorf("error creating request: %w", err)
	}

	resp, respBody, err := doRequest(client, req)
	if err == nil && resp.StatusCode != http.StatusCreated {
		err = fmt.Errorf("unexpected http status code %d", resp.StatusCode)
	}
	if err != nil {
		return nil, err
	}

	token := &managementClient.Token{}
	err = json.Unmarshal(respBody, token)
	if err != nil {
		return nil, err
	}

	return token, nil
}
07070100000014000081A400000000000000000000000168B0FB3F0000162F000000000000000000000000000000000000002D00000000rancher-cli-2.12.1/cmd/kubectl_token_test.gopackage cmd

import (
	"flag"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/rancher/cli/config"
	apiv3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/urfave/cli"
)

func TestGetAuthProviders(t *testing.T) {
	setupServer := func(response string) *httptest.Server {
		return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprint(w, response)
		}))
	}

	client := &http.Client{Timeout: time.Second}

	tt := []struct {
		name              string
		server            *httptest.Server
		expectedProviders []TypedProvider
		expectedErr       string
	}{
		{
			name:   "response ok",
			server: setupServer(responseOK),
			expectedProviders: []TypedProvider{
				&apiv3.LocalProvider{
					AuthProvider: apiv3.AuthProvider{
						Type: "localProvider",
					},
				},
				&apiv3.AzureADProvider{
					AuthProvider: apiv3.AuthProvider{
						Type: "azureADProvider",
					},
					RedirectURL: "https://login.microsoftonline.com/258928db-3ed6-49fb-9a7e-52e492ffb066/oauth2/v2.0/authorize?client_id=56168f69-a732-48e2-aa21-8aa0909d0976&redirect_uri=https://rancher.mydomain.com/verify-auth-azure&response_type=code&scope=openid",
					TenantID:    "258928db-3ed6-49fb-9a7e-52e492ffb066",
					OAuthProvider: apiv3.OAuthProvider{
						ClientID: "56168f69-a732-48e2-aa21-8aa0909d0976",
						Scopes:   []string{"openid", "profile", "email"},
						OAuthEndpoint: apiv3.OAuthEndpoint{
							AuthURL:       "https://login.microsoftonline.com/258928db-3ed6-49fb-9a7e-52e492ffb066/oauth2/v2.0/authorize",
							DeviceAuthURL: "https://login.microsoftonline.com/258928db-3ed6-49fb-9a7e-52e492ffb066/oauth2/v2.0/devicecode",
							TokenURL:      "https://login.microsoftonline.com/258928db-3ed6-49fb-9a7e-52e492ffb066/oauth2/v2.0/token",
						},
					},
				},
			},
		},
		{
			name:        "json error",
			server:      setupServer(`hnjskjnksnj`),
			expectedErr: "invalid JSON response from",
		},
	}

	for _, tc := range tt {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Cleanup(tc.server.Close)

			got, err := getAuthProviders(client, tc.server.URL)

			if tc.expectedErr != "" {
				assert.ErrorContains(t, err, tc.expectedErr)
				assert.Nil(t, got)
			} else {
				assert.Equal(t, tc.expectedProviders, got)
				assert.Nil(t, err)
			}
		})
	}
}

var responseOK = `{
    "data": [
        {
            "actions": {
                "login": "…/v3-public/azureADProviders/azuread?action=login"
            },
            "authUrl": "https://login.microsoftonline.com/258928db-3ed6-49fb-9a7e-52e492ffb066/oauth2/v2.0/authorize",
            "baseType": "authProvider",
            "clientId": "56168f69-a732-48e2-aa21-8aa0909d0976",
            "creatorId": null,
            "deviceAuthUrl": "https://login.microsoftonline.com/258928db-3ed6-49fb-9a7e-52e492ffb066/oauth2/v2.0/devicecode",
            "id": "azuread",
            "links": {
                "self": "…/v3-public/azureADProviders/azuread"
            },
            "redirectUrl": "https://login.microsoftonline.com/258928db-3ed6-49fb-9a7e-52e492ffb066/oauth2/v2.0/authorize?client_id=56168f69-a732-48e2-aa21-8aa0909d0976&redirect_uri=https://rancher.mydomain.com/verify-auth-azure&response_type=code&scope=openid",
            "scopes": [
                "openid",
                "profile",
                "email"
            ],
            "tenantId": "258928db-3ed6-49fb-9a7e-52e492ffb066",
            "tokenUrl": "https://login.microsoftonline.com/258928db-3ed6-49fb-9a7e-52e492ffb066/oauth2/v2.0/token",
            "type": "azureADProvider"
        },
        {
            "actions": {
                "login": "…/v3-public/localProviders/local?action=login"
            },
            "baseType": "authProvider",
            "creatorId": null,
            "id": "local",
            "links": {
                "self": "…/v3-public/localProviders/local"
            },
            "type": "localProvider"
        }
    ]
}`

func TestCacheCredential(t *testing.T) {
	flagSet := flag.NewFlagSet("test", 0)
	flagSet.String("server", "rancher.example.com", "doc")
	flagSet.String("config", t.TempDir(), "doc")
	cliCtx := cli.NewContext(nil, flagSet, nil)

	serverConfig, err := lookupServerConfig(cliCtx)
	if err != nil {
		t.Fatal(err)
	}

	cred := &config.ExecCredential{Status: &config.ExecCredentialStatus{Token: "test-token"}}

	err = cacheCredential(cliCtx, serverConfig, "dev-server", cred)
	require.NoError(t, err)

	cfg, err := loadConfig(cliCtx)
	require.NoError(t, err)

	expires := &config.Time{Time: time.Now().Add(time.Hour * 2)}
	cfg.CurrentServer = "rancher.example.com"
	cfg.Servers["rancher.example.com"].KubeCredentials["dev-server"].Status.ClientKeyData = "this-is-not-real"
	cfg.Servers["rancher.example.com"].KubeCredentials["dev-server"].Status.ExpirationTimestamp = expires

	err = cfg.Write()
	require.NoError(t, err)

	serverConfig, err = cfg.FocusedServer()
	require.NoError(t, err)

	cred = &config.ExecCredential{Status: &config.ExecCredentialStatus{Token: "new-token"}}

	err = cacheCredential(cliCtx, serverConfig, "local", cred)
	require.NoError(t, err)

	cfg, err = loadConfig(cliCtx)
	require.NoError(t, err)

	clientKeyData := cfg.Servers["rancher.example.com"].KubeCredentials["dev-server"].Status.ClientKeyData
	assert.Equal(t, "this-is-not-real", clientKeyData)

	expirationTimestamp := cfg.Servers["rancher.example.com"].KubeCredentials["dev-server"].Status.ExpirationTimestamp
	require.NotNil(t, expirationTimestamp)
	assert.True(t, expirationTimestamp.Time.Equal(expires.Time))
}
07070100000015000081A400000000000000000000000168B0FB3F00002154000000000000000000000000000000000000002000000000rancher-cli-2.12.1/cmd/login.gopackage cmd

import (
	"bufio"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/sirupsen/logrus"

	"github.com/grantae/certinfo"
	"github.com/rancher/cli/cliclient"
	"github.com/rancher/cli/config"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/urfave/cli"
)

type LoginData struct {
	Project     managementClient.Project
	Index       int
	ClusterName string
}

type CACertResponse struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

func LoginCommand() cli.Command {
	return cli.Command{
		Name:      "login",
		Aliases:   []string{"l"},
		Usage:     "Login to a Rancher server",
		Action:    loginSetup,
		ArgsUsage: "[SERVERURL]",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "context",
				Usage: "Set the context during login",
			},
			cli.StringFlag{
				Name:  "token,t",
				Usage: "Token from the Rancher UI",
			},
			cli.StringFlag{
				Name:  "cacert",
				Usage: "Location of the CACerts to use",
			},
			cli.StringFlag{
				Name:  "name",
				Usage: "Name of the Server",
			},
			cli.BoolFlag{
				Name:  "skip-verify",
				Usage: "Skip verification of the CACerts presented by the Server",
			},
		},
	}
}

func loginSetup(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowCommandHelp(ctx, "login")
	}

	cf, err := loadConfig(ctx)
	if err != nil {
		return err
	}

	serverName := ctx.String("name")
	if serverName == "" {
		serverName = "rancherDefault"
	}

	serverConfig := &config.ServerConfig{}

	// Validate the url and drop the path
	u, err := url.ParseRequestURI(ctx.Args().First())
	if err != nil {
		return fmt.Errorf("Failed to parse SERVERURL (%s), make sure it is a valid HTTPS URL (e.g. https://rancher.yourdomain.com or https://1.1.1.1). Error: %s", ctx.Args().First(), err)
	}

	u.Path = ""
	serverConfig.URL = u.String()

	if ctx.String("token") != "" {
		auth := SplitOnColon(ctx.String("token"))
		if len(auth) != 2 {
			return errors.New("invalid token")
		}
		serverConfig.AccessKey = auth[0]
		serverConfig.SecretKey = auth[1]
		serverConfig.TokenKey = ctx.String("token")
	} else {
		// This can be removed once username and password is accepted
		return errors.New("token flag is required")
	}

	if ctx.String("cacert") != "" {
		cert, err := loadAndVerifyCert(ctx.String("cacert"))
		if err != nil {
			return err
		}
		serverConfig.CACerts = cert

	}

	c, err := cliclient.NewManagementClient(serverConfig)
	if err != nil {
		if _, ok := err.(*url.Error); ok && strings.Contains(err.Error(), "certificate signed by unknown authority") {
			// no cert was provided and it's most likely a self signed cert if
			// we get here so grab the cacert and see if the user accepts the server
			c, err = getCertFromServer(ctx, serverConfig)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	proj, err := getProjectContext(ctx, c)
	if err != nil {
		return err
	}

	// Set the default server and proj for the user
	serverConfig.Project = proj
	cf.CurrentServer = serverName
	cf.Servers[serverName] = serverConfig

	err = cf.Write()
	if err != nil {
		return err
	}

	return nil
}

func getProjectContext(ctx *cli.Context, c *cliclient.MasterClient) (string, error) {
	// If context is given
	if ctx.String("context") != "" {
		context := ctx.String("context")
		// Check if given context is in valid format
		_, _, err := parseClusterAndProjectID(context)
		if err != nil {
			return "", fmt.Errorf("Unable to parse context (%s). Please provide context as local:p-xxxxx, c-xxxxx:p-xxxxx, c-xxxxx:project-xxxxx, c-m-xxxxxxxx:p-xxxxx or c-m-xxxxxxxx:project-xxxxx", context)
		}
		// Check if context exists
		_, err = Lookup(c, context, "project")
		if err != nil {
			return "", fmt.Errorf("Unable to find context (%s). Make sure the context exists and you have permissions to use it. Error: %s", context, err)
		}
		return context, nil
	}

	projectCollection, err := c.ManagementClient.Project.List(defaultListOpts(ctx))
	if err != nil {
		return "", err
	}

	projDataLen := len(projectCollection.Data)

	if projDataLen == 0 {
		logrus.Warn("No projects found, context could not be set. Please create a project and run `rancher login` again.")
		return "", nil
	}

	if projDataLen == 1 {
		logrus.Infof("Only 1 project available: %s", projectCollection.Data[0].Name)
		return projectCollection.Data[0].ID, nil
	}

	if projDataLen == 2 {
		var sysProj bool
		var defaultID string
		for _, proj := range projectCollection.Data {
			if proj.Name == "Default" {
				defaultID = proj.ID
			}
			if proj.Name == "System" {
				sysProj = true
			}
			if sysProj && defaultID != "" {
				return defaultID, nil
			}
		}
	}

	clusterNames, err := getClusterNames(ctx, c)
	if err != nil {
		return "", err
	}

	writer := NewTableWriter([][]string{
		{"NUMBER", "Index"},
		{"CLUSTER NAME", "ClusterName"},
		{"PROJECT ID", "Project.ID"},
		{"PROJECT NAME", "Project.Name"},
		{"PROJECT DESCRIPTION", "Project.Description"},
	}, ctx)

	for i, item := range projectCollection.Data {
		writer.Write(&LoginData{
			Project:     item,
			Index:       i + 1,
			ClusterName: clusterNames[item.ClusterID],
		})
	}

	writer.Close()
	if nil != writer.Err() {
		return "", writer.Err()
	}

	fmt.Print("Select a Project:")

	reader := bufio.NewReader(os.Stdin)

	errMessage := fmt.Sprintf("Invalid input, enter a number between 1 and %v: ", len(projectCollection.Data))
	var selection int

	for {
		input, err := reader.ReadString('\n')
		if err != nil {
			return "", err
		}
		input = strings.TrimSpace(input)

		if input != "" {
			i, err := strconv.Atoi(input)
			if err != nil {
				fmt.Print(errMessage)
				continue
			}
			if i <= len(projectCollection.Data) && i != 0 {
				selection = i - 1
				break
			}
			fmt.Print(errMessage)
			continue
		}
	}
	return projectCollection.Data[selection].ID, nil
}

func getCertFromServer(ctx *cli.Context, serverConfig *config.ServerConfig) (*cliclient.MasterClient, error) {
	req, err := http.NewRequest("GET", serverConfig.URL+"/v3/settings/cacerts", nil)
	if err != nil {
		return nil, err
	}

	req.SetBasicAuth(serverConfig.AccessKey, serverConfig.SecretKey)

	tlsConfig := &tls.Config{InsecureSkipVerify: true}
	client, err := newHTTPClient(serverConfig, tlsConfig)
	if err != nil {
		return nil, err
	}

	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()

	content, err := io.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	var certReponse *CACertResponse
	err = json.Unmarshal(content, &certReponse)
	if err != nil {
		return nil, fmt.Errorf("Unable to parse response from %s/v3/settings/cacerts\nError: %s\nResponse:\n%s", serverConfig.URL, err, content)
	}

	cert, err := verifyCert([]byte(certReponse.Value))
	if err != nil {
		return nil, err
	}

	// Get the server cert chain in a printable form
	serverCerts, err := processServerChain(res)
	if err != nil {
		return nil, err
	}

	if !ctx.Bool("skip-verify") {
		if ok := verifyUserAcceptsCert(serverCerts, serverConfig.URL); !ok {
			return nil, errors.New("CACert of server was not accepted, unable to login")
		}
	}

	serverConfig.CACerts = cert

	return cliclient.NewManagementClient(serverConfig)
}

func verifyUserAcceptsCert(certs []string, url string) bool {
	fmt.Printf("The authenticity of server '%s' can't be established.\n", url)
	fmt.Printf("Cert chain is : %v \n", certs)
	fmt.Print("Do you want to continue connecting (yes/no)? ")

	scanner := bufio.NewScanner(os.Stdin)

	for scanner.Scan() {
		input := scanner.Text()
		input = strings.ToLower(strings.TrimSpace(input))

		if input == "yes" || input == "y" {
			return true
		} else if input == "no" || input == "n" {
			return false
		}
		fmt.Printf("Please type 'yes' or 'no': ")
	}
	return false
}

func processServerChain(res *http.Response) ([]string, error) {
	var allCerts []string

	for _, cert := range res.TLS.PeerCertificates {
		result, err := certinfo.CertificateText(cert)
		if err != nil {
			return allCerts, err
		}
		allCerts = append(allCerts, result)
	}
	return allCerts, nil
}

func loginContext(ctx *cli.Context) error {
	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	cluster, err := getClusterByID(c, c.UserConfig.FocusedCluster())
	if err != nil {
		return err
	}
	clusterName := getClusterName(cluster)

	project, err := getProjectByID(c, c.UserConfig.Project)
	if err != nil {
		return err
	}

	fmt.Printf("Cluster:%s Project:%s\n", clusterName, project.Name)
	return nil
}
07070100000016000081A400000000000000000000000168B0FB3F00000952000000000000000000000000000000000000002200000000rancher-cli-2.12.1/cmd/machine.gopackage cmd

import (
	"fmt"

	"github.com/rancher/cli/cliclient"
	capiClient "github.com/rancher/rancher/pkg/client/generated/cluster/v1beta1"
	"github.com/urfave/cli"
)

type MachineData struct {
	ID      string
	Machine capiClient.Machine
	Name    string
}

func MachineCommand() cli.Command {
	return cli.Command{
		Name:    "machines",
		Aliases: []string{"machine"},
		Usage:   "Operations on machines",
		Action:  defaultAction(machineLs),
		Subcommands: []cli.Command{
			{
				Name:        "ls",
				Usage:       "List machines",
				Description: "\nLists all machines in the current cluster.",
				ArgsUsage:   "None",
				Action:      machineLs,
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "format",
						Usage: "'json', 'yaml' or Custom format: '{{.Machine.ID}} {{.Machine.Name}}'",
					},
					quietFlag,
				},
			},
		},
	}
}

func machineLs(ctx *cli.Context) error {
	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	collection, err := getMachinesList(ctx, c)
	if err != nil {
		return err
	}

	writer := NewTableWriter([][]string{
		{"ID", "ID"},
		{"NAME", "Name"},
		{"PHASE", "Machine.Status.Phase"},
	}, ctx)

	defer writer.Close()

	for _, item := range collection.Data {
		writer.Write(&MachineData{
			ID:      item.ID,
			Machine: item,
			Name:    getMachineName(item),
		})
	}

	return writer.Err()
}

func getMachinesList(
	ctx *cli.Context,
	c *cliclient.MasterClient,
) (*capiClient.MachineCollection, error) {
	filter := defaultListOpts(ctx)
	return c.CAPIClient.Machine.List(filter)
}

func getMachineByNodeName(
	ctx *cli.Context,
	c *cliclient.MasterClient,
	nodeName string,
) (capiClient.Machine, error) {
	machineCollection, err := getMachinesList(ctx, c)
	if err != nil {
		return capiClient.Machine{}, err
	}

	for _, machine := range machineCollection.Data {
		if machine.Status.NodeRef != nil && machine.Status.NodeRef.Name == nodeName {
			return machine, nil
		}
	}

	return capiClient.Machine{}, fmt.Errorf("no machine found with associated to node [%s], run "+
		"`rancher machines` to see available nodes", nodeName)
}

func getMachineName(machine capiClient.Machine) string {
	if machine.Name != "" {
		return machine.Name
	} else if machine.Status.NodeRef != nil {
		return machine.Status.NodeRef.Name
	} else if machine.InfrastructureRef != nil {
		return machine.InfrastructureRef.Name
	}
	return machine.ID
}
07070100000017000081A400000000000000000000000168B0FB3F00001448000000000000000000000000000000000000002400000000rancher-cli-2.12.1/cmd/namespace.gopackage cmd

import (
	"fmt"

	"github.com/rancher/cli/cliclient"
	clusterClient "github.com/rancher/rancher/pkg/client/generated/cluster/v3"
	"github.com/urfave/cli"
)

type NamespaceData struct {
	ID        string
	Namespace clusterClient.Namespace
}

func NamespaceCommand() cli.Command {
	return cli.Command{
		Name:    "namespaces",
		Aliases: []string{"namespace"},
		Usage:   "Operations on namespaces",
		Action:  defaultAction(namespaceLs),
		Flags: []cli.Flag{
			quietFlag,
		},
		Subcommands: []cli.Command{
			{
				Name:        "ls",
				Usage:       "List namespaces",
				Description: "\nLists all namespaces in the current project.",
				ArgsUsage:   "None",
				Action:      namespaceLs,
				Flags: []cli.Flag{
					cli.BoolFlag{
						Name:  "all-namespaces",
						Usage: "List all namespaces in the current cluster",
					},
					cli.StringFlag{
						Name:  "format",
						Usage: "'json', 'yaml' or Custom format: '{{.Namespace.ID}} {{.Namespace.Name}}'",
					},
					quietFlag,
				},
			},
			{
				Name:        "create",
				Usage:       "Create a namespace",
				Description: "\nCreates a namespace in the current cluster.",
				ArgsUsage:   "[NEWPROJECTNAME...]",
				Action:      namespaceCreate,
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "description",
						Usage: "Description to apply to the namespace",
					},
				},
			},
			{
				Name:      "delete",
				Aliases:   []string{"rm"},
				Usage:     "Delete a namespace by name or ID",
				ArgsUsage: "[NAMESPACEID NAMESPACENAME]",
				Action:    namespaceDelete,
			},
			{
				Name:      "move",
				Usage:     "Move a namespace to a different project",
				ArgsUsage: "[NAMESPACEID/NAMESPACENAME PROJECTID]",
				Action:    namespaceMove,
			},
		},
	}
}

func namespaceLs(ctx *cli.Context) error {
	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	collection, err := getNamespaceList(ctx, c)
	if err != nil {
		return err
	}

	if !ctx.Bool("all-namespaces") {
		var projectNamespaces []clusterClient.Namespace

		for _, namespace := range collection.Data {
			if namespace.ProjectID == c.UserConfig.Project {
				projectNamespaces = append(projectNamespaces, namespace)
			}

		}
		collection.Data = projectNamespaces
	}

	writer := NewTableWriter([][]string{
		{"ID", "ID"},
		{"NAME", "Namespace.Name"},
		{"STATE", "Namespace.State"},
		{"PROJECT", "Namespace.ProjectID"},
		{"DESCRIPTION", "Namespace.Description"},
	}, ctx)

	defer writer.Close()

	for _, item := range collection.Data {
		writer.Write(&NamespaceData{
			ID:        item.ID,
			Namespace: item,
		})
	}

	return writer.Err()
}

func namespaceCreate(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	newNamespace := &clusterClient.Namespace{
		Name:        ctx.Args().First(),
		ProjectID:   c.UserConfig.Project,
		Description: ctx.String("description"),
	}

	_, err = c.ClusterClient.Namespace.Create(newNamespace)
	if err != nil {
		return err
	}

	return nil
}

func namespaceDelete(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	for _, arg := range ctx.Args() {
		resource, err := Lookup(c, arg, "namespace")
		if err != nil {
			return err
		}

		namespace, err := getNamespaceByID(c, resource.ID)
		if err != nil {
			return err
		}

		err = c.ClusterClient.Namespace.Delete(namespace)
		if err != nil {
			return err
		}
	}

	return nil
}

func namespaceMove(ctx *cli.Context) error {
	if ctx.NArg() < 2 {
		return cli.ShowSubcommandHelp(ctx)
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	resource, err := Lookup(c, ctx.Args().First(), "namespace")
	if err != nil {
		return err
	}

	namespace, err := getNamespaceByID(c, resource.ID)
	if err != nil {
		return err
	}

	projResource, err := Lookup(c, ctx.Args().Get(1), "project")
	if err != nil {
		return err
	}

	proj, err := getProjectByID(c, projResource.ID)
	if err != nil {
		return err
	}

	if anno, ok := namespace.Annotations["cattle.io/appIds"]; ok && anno != "" {
		return fmt.Errorf("namespace %s cannot be moved", namespace.Name)
	}

	if _, ok := namespace.Actions["move"]; ok {
		move := &clusterClient.NamespaceMove{
			ProjectID: proj.ID,
		}
		return c.ClusterClient.Namespace.ActionMove(namespace, move)
	}

	update := make(map[string]string)
	update["projectId"] = proj.ID

	_, err = c.ClusterClient.Namespace.Update(namespace, update)
	if err != nil {
		return err
	}

	return nil
}

func getNamespaceList(
	ctx *cli.Context,
	c *cliclient.MasterClient,
) (*clusterClient.NamespaceCollection, error) {
	collection, err := c.ClusterClient.Namespace.List(defaultListOpts(ctx))
	if err != nil {
		return nil, err
	}
	return collection, nil
}

func getNamespaceByID(
	c *cliclient.MasterClient,
	namespaceID string,
) (*clusterClient.Namespace, error) {
	namespace, err := c.ClusterClient.Namespace.ByID(namespaceID)
	if err != nil {
		return nil, fmt.Errorf("no namespace found with the ID [%s], run "+
			"`rancher namespaces` to see available namespaces: %s", namespaceID, err)
	}
	return namespace, nil
}
07070100000018000081A400000000000000000000000168B0FB3F00000F7D000000000000000000000000000000000000001F00000000rancher-cli-2.12.1/cmd/node.gopackage cmd

import (
	"fmt"

	"github.com/sirupsen/logrus"

	"github.com/rancher/cli/cliclient"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/urfave/cli"
)

type NodeData struct {
	ID   string
	Node managementClient.Node
	Name string
	Pool string
}

func NodeCommand() cli.Command {
	return cli.Command{
		Name:    "nodes",
		Aliases: []string{"node"},
		Usage:   "Operations on nodes",
		Action:  defaultAction(nodeLs),
		Subcommands: []cli.Command{
			{
				Name:        "ls",
				Usage:       "List nodes",
				Description: "\nLists all nodes in the current cluster.",
				ArgsUsage:   "None",
				Action:      nodeLs,
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "format",
						Usage: "'json', 'yaml' or Custom format: '{{.Node.ID}} {{.Node.Name}}'",
					},
					quietFlag,
				},
			},
			{
				Name:      "delete",
				Aliases:   []string{"rm"},
				Usage:     "Delete a node by ID",
				ArgsUsage: "[NODEID NODENAME]",
				Action:    nodeDelete,
			},
		},
	}
}

func nodeLs(ctx *cli.Context) error {
	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	collection, err := getNodesList(ctx, c, c.UserConfig.FocusedCluster())
	if err != nil {
		return err
	}

	nodePools, err := getNodePools(ctx, c)
	if err != nil {
		return err
	}

	writer := NewTableWriter([][]string{
		{"ID", "ID"},
		{"NAME", "Name"},
		{"STATE", "Node.State"},
		{"POOL", "Pool"},
		{"DESCRIPTION", "Node.Description"},
	}, ctx)

	defer writer.Close()

	for _, item := range collection.Data {
		writer.Write(&NodeData{
			ID:   item.ID,
			Node: item,
			Name: getNodeName(item),
			Pool: getNodePoolName(item, nodePools),
		})
	}

	return writer.Err()
}

func nodeDelete(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	for _, arg := range ctx.Args() {
		resource, err := Lookup(c, arg, "node")
		if err != nil {
			return err
		}

		node, err := getNodeByID(ctx, c, resource.ID)
		if err != nil {
			return err
		}

		if _, ok := node.Links["remove"]; !ok {
			logrus.Warnf("node %v is externally managed and must be deleted "+
				"through the provider", getNodeName(node))
			continue
		}

		err = c.ManagementClient.Node.Delete(&node)
		if err != nil {
			return err
		}
	}
	return nil
}

func getNodesList(
	ctx *cli.Context,
	c *cliclient.MasterClient,
	clusterID string,
) (*managementClient.NodeCollection, error) {
	filter := defaultListOpts(ctx)
	filter.Filters["clusterId"] = clusterID

	collection, err := c.ManagementClient.Node.List(filter)
	if err != nil {
		return nil, err
	}
	return collection, nil
}

func getNodeByID(
	ctx *cli.Context,
	c *cliclient.MasterClient,
	nodeID string,
) (managementClient.Node, error) {
	nodeCollection, err := getNodesList(ctx, c, c.UserConfig.FocusedCluster())
	if err != nil {
		return managementClient.Node{}, err
	}

	for _, node := range nodeCollection.Data {
		if node.ID == nodeID {
			return node, nil
		}
	}

	return managementClient.Node{}, fmt.Errorf("no node found with the ID [%s], run "+
		"`rancher nodes` to see available nodes", nodeID)
}

func getNodeName(node managementClient.Node) string {
	if node.Name != "" {
		return node.Name
	} else if node.NodeName != "" {
		return node.NodeName
	} else if node.RequestedHostname != "" {
		return node.RequestedHostname
	}
	return node.ID
}

func getNodePools(
	ctx *cli.Context,
	c *cliclient.MasterClient,
) (*managementClient.NodePoolCollection, error) {
	filter := defaultListOpts(ctx)
	filter.Filters["clusterId"] = c.UserConfig.FocusedCluster()

	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 ""
}
07070100000019000081A400000000000000000000000168B0FB3F000022D8000000000000000000000000000000000000002200000000rancher-cli-2.12.1/cmd/project.gopackage cmd

import (
	"fmt"
	"io"

	"github.com/rancher/cli/cliclient"
	"github.com/rancher/norman/types"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/urfave/cli"
)

type ProjectData struct {
	ID      string
	Project managementClient.Project
}

func ProjectCommand() cli.Command {
	return cli.Command{
		Name:    "projects",
		Aliases: []string{"project"},
		Usage:   "Operations on projects",
		Action:  defaultAction(projectLs),
		Subcommands: []cli.Command{
			{
				Name:        "ls",
				Usage:       "List projects",
				Description: "\nLists all projects in the current cluster.",
				ArgsUsage:   "None",
				Action:      projectLs,
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "format",
						Usage: "'json', 'yaml' or Custom format: '{{.Project.ID}} {{.Project.Name}}'",
					},
					quietFlag,
				},
			},
			{
				Name:        "create",
				Usage:       "Create a project",
				Description: "\nCreates a project in the current cluster.",
				ArgsUsage:   "[NEWPROJECTNAME...]",
				Action:      projectCreate,
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "cluster",
						Usage: "Cluster ID to create the project in",
					},
					cli.StringFlag{
						Name:  "description",
						Usage: "Description to apply to the project",
					},
				},
			},
			{
				Name:      "delete",
				Aliases:   []string{"rm"},
				Usage:     "Delete a project by ID",
				ArgsUsage: "[PROJECTID PROJECTNAME]",
				Action:    projectDelete,
			},
			{
				Name:        "add-member-role",
				Usage:       "Add a member to the project",
				Action:      addProjectMemberRoles,
				Description: "Examples:\n #Create the roles of 'create-ns' and 'services-manage' for a user named 'user1'\n rancher project add-member-role user1 create-ns services-manage\n",
				ArgsUsage:   "[USERNAME, ROLE...]",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "project-id",
						Usage: "Optional project ID to apply this change to, defaults to the current context",
					},
				},
			},
			{
				Name:        "delete-member-role",
				Usage:       "Delete a member from the project",
				Action:      deleteProjectMemberRoles,
				Description: "Examples:\n #Delete the roles of 'create-ns' and 'services-manage' for a user named 'user1'\n rancher project delete-member-role user1 create-ns services-manage\n",
				ArgsUsage:   "[USERNAME, ROLE...]",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "project-id",
						Usage: "Optional project ID to apply this change to, defaults to the current context",
					},
				},
			},
			{
				Name:   "list-roles",
				Usage:  "List all available roles for a project",
				Action: listProjectRoles,
			},
			{
				Name:  "list-members",
				Usage: "List current members of the project",
				Action: func(cctx *cli.Context) error {
					client, err := GetClient(cctx)
					if err != nil {
						return err
					}

					return listProjectMembers(
						cctx,
						cctx.App.Writer,
						client.UserConfig,
						client.ManagementClient.ProjectRoleTemplateBinding,
						client.ManagementClient.Principal,
					)
				},
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "project-id",
						Usage: "Optional project ID to list members for, defaults to the current context",
					},
					cli.StringFlag{
						Name:  "format",
						Usage: "'json', 'yaml' or Custom format: '{{.ID }} {{.Member }}'",
					},
					quietFlag,
				},
			},
		},
	}
}

func projectLs(ctx *cli.Context) error {
	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	collection, err := getProjectList(ctx, c)
	if err != nil {
		return err
	}

	writer := NewTableWriter([][]string{
		{"ID", "ID"},
		{"NAME", "Project.Name"},
		{"STATE", "Project.State"},
		{"DESCRIPTION", "Project.Description"},
	}, ctx)

	defer writer.Close()

	for _, item := range collection.Data {
		writer.Write(&ProjectData{
			ID:      item.ID,
			Project: item,
		})
	}

	return writer.Err()
}

func projectCreate(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	clusterID := c.UserConfig.FocusedCluster()
	if ctx.String("cluster") != "" {
		resource, err := Lookup(c, ctx.String("cluster"), "cluster")
		if err != nil {
			return err
		}
		clusterID = resource.ID
	}

	newProj := &managementClient.Project{
		Name:        ctx.Args().First(),
		ClusterID:   clusterID,
		Description: ctx.String("description"),
	}

	_, err = c.ManagementClient.Project.Create(newProj)
	if err != nil {
		return err
	}
	return nil
}

func projectDelete(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowSubcommandHelp(ctx)
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	for _, arg := range ctx.Args() {
		resource, err := Lookup(c, arg, "project")
		if err != nil {
			return err
		}

		project, err := getProjectByID(c, resource.ID)
		if err != nil {
			return err
		}

		err = c.ManagementClient.Project.Delete(project)
		if err != nil {
			return err
		}
	}

	return nil
}

func addProjectMemberRoles(ctx *cli.Context) error {
	if len(ctx.Args()) < 2 {
		return cli.ShowSubcommandHelp(ctx)
	}

	memberName := ctx.Args().First()

	roles := ctx.Args()[1:]

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	member, err := searchForMember(ctx, c, memberName)
	if err != nil {
		return err
	}

	projectID := c.UserConfig.Project
	if ctx.String("project-id") != "" {
		projectID = ctx.String("project-id")
	}

	for _, role := range roles {
		rtb := managementClient.ProjectRoleTemplateBinding{
			ProjectID:      projectID,
			RoleTemplateID: role,
		}
		if member.PrincipalType == "user" {
			rtb.UserPrincipalID = member.ID
		} else {
			rtb.GroupPrincipalID = member.ID
		}
		_, err = c.ManagementClient.ProjectRoleTemplateBinding.Create(&rtb)
		if err != nil {
			return err
		}
	}
	return nil
}

func deleteProjectMemberRoles(ctx *cli.Context) error {
	if len(ctx.Args()) < 2 {
		return cli.ShowSubcommandHelp(ctx)
	}

	memberName := ctx.Args().First()

	roles := ctx.Args()[1:]

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	member, err := searchForMember(ctx, c, memberName)
	if err != nil {
		return err
	}

	projectID := c.UserConfig.Project
	if ctx.String("project-id") != "" {
		projectID = ctx.String("project-id")
	}

	for _, role := range roles {
		filter := defaultListOpts(ctx)
		filter.Filters["projectId"] = projectID
		filter.Filters["roleTemplateId"] = role

		if member.PrincipalType == "user" {
			filter.Filters["userPrincipalId"] = member.ID
		} else {
			filter.Filters["groupPrincipalId"] = member.ID
		}

		bindings, err := c.ManagementClient.ProjectRoleTemplateBinding.List(filter)
		if err != nil {
			return err
		}

		for _, binding := range bindings.Data {
			err = c.ManagementClient.ProjectRoleTemplateBinding.Delete(&binding)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func listProjectRoles(ctx *cli.Context) error {
	return listRoles(ctx, "project")
}

type prtbLister interface {
	List(opts *types.ListOpts) (*managementClient.ProjectRoleTemplateBindingCollection, error)
}

func listProjectMembers(ctx *cli.Context, out io.Writer, config userConfig, prtbs prtbLister, principals principalGetter) error {
	projectID := config.FocusedProject()
	if ctx.String("project-id") != "" {
		projectID = ctx.String("project-id")
	}

	filter := defaultListOpts(ctx)
	filter.Filters["projectId"] = projectID

	bindings, err := prtbs.List(filter)
	if err != nil {
		return err
	}

	rtbs := make([]RoleTemplateBinding, 0, len(bindings.Data))

	for _, binding := range bindings.Data {
		parsedTime, err := createdTimetoHuman(binding.Created)
		if err != nil {
			return err
		}

		principalID := binding.UserPrincipalID
		if binding.GroupPrincipalID != "" {
			principalID = binding.GroupPrincipalID
		}

		rtbs = append(rtbs, RoleTemplateBinding{
			ID:      binding.ID,
			Member:  getMemberNameFromPrincipal(principals, principalID),
			Role:    binding.RoleTemplateID,
			Created: parsedTime,
		})
	}

	writerConfig := &TableWriterConfig{
		Format: ctx.String("format"),
		Quiet:  ctx.Bool("quiet"),
		Writer: out,
	}

	return listRoleTemplateBindings(writerConfig, rtbs)
}

func getProjectList(
	ctx *cli.Context,
	c *cliclient.MasterClient,
) (*managementClient.ProjectCollection, error) {
	filter := defaultListOpts(ctx)
	filter.Filters["clusterId"] = c.UserConfig.FocusedCluster()

	collection, err := c.ManagementClient.Project.List(filter)
	if err != nil {
		return nil, err
	}
	return collection, nil
}

func getProjectByID(
	c *cliclient.MasterClient,
	projectID string,
) (*managementClient.Project, error) {
	project, err := c.ManagementClient.Project.ByID(projectID)
	if err != nil {
		return nil, fmt.Errorf("no project found with the ID [%s], run "+
			"`rancher projects` to see available projects: %s", projectID, err)
	}
	return project, nil
}
0707010000001A000081A400000000000000000000000168B0FB3F000009C2000000000000000000000000000000000000002700000000rancher-cli-2.12.1/cmd/project_test.gopackage cmd

import (
	"bytes"
	"flag"
	"fmt"
	"net/url"
	"testing"
	"time"

	"github.com/rancher/norman/types"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/urfave/cli"
)

func TestListProjectMembers(t *testing.T) {
	t.Parallel()

	now := time.Now()

	userConfig := &fakeUserConfig{
		FocusedProjectFunc: func() string {
			return "c-fn7lc:p-9mdxl"
		},
	}

	created := now.Format(time.RFC3339)
	prtbs := &fakePRTBLister{
		ListFunc: func(opts *types.ListOpts) (*managementClient.ProjectRoleTemplateBindingCollection, error) {
			return &managementClient.ProjectRoleTemplateBindingCollection{
				Data: []managementClient.ProjectRoleTemplateBinding{
					{
						Resource: types.Resource{
							ID: "p-9mdxl:creator-project-owner",
						},
						Created:         created,
						RoleTemplateID:  "project-owner",
						UserPrincipalID: "local://user-2p7w6",
					},
					{
						Resource: types.Resource{
							ID: "p-9mdxl:prtb-mqcvk",
						},
						Created:          created,
						RoleTemplateID:   "project-member",
						GroupPrincipalID: "okta_group://b4qkhsnliz",
					},
				},
			}, nil
		},
	}

	principals := &fakePrincipalGetter{
		ByIDFunc: func(id string) (*managementClient.Principal, error) {
			id, err := url.PathUnescape(id)
			require.NoError(t, err)

			switch id {
			case "local://user-2p7w6":
				return &managementClient.Principal{
					Name:          "Default Admin",
					LoginName:     "admin",
					Provider:      "local",
					PrincipalType: "user",
				}, nil
			case "okta_group://b4qkhsnliz":
				return &managementClient.Principal{
					Name:          "DevOps",
					LoginName:     "devops",
					Provider:      "okta",
					PrincipalType: "group",
				}, nil
			default:
				return nil, fmt.Errorf("not found")
			}
		},
	}

	flagSet := flag.NewFlagSet("test", flag.ContinueOnError)
	cctx := cli.NewContext(nil, flagSet, nil)

	var out bytes.Buffer

	err := listProjectMembers(cctx, &out, userConfig, prtbs, principals)
	require.NoError(t, err)
	require.NotEmpty(t, out)

	humanCreated := now.Format(humanTimeFormat)
	want := [][]string{
		{"BINDING-ID", "MEMBER", "ROLE", "CREATED"},
		{"p-9mdxl:creator-project-owner", "Default Admin (Local User)", "project-owner", humanCreated},
		{"p-9mdxl:prtb-mqcvk", "DevOps (Okta Group)", "project-member", humanCreated},
	}

	got := parseTabWriterOutput(&out)
	assert.Equal(t, want, got)
}
0707010000001B000081A400000000000000000000000168B0FB3F00000ABE000000000000000000000000000000000000001D00000000rancher-cli-2.12.1/cmd/ps.gopackage cmd

import (
	"strconv"

	"github.com/rancher/cli/cliclient"
	"github.com/urfave/cli"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
)

type PSHolder struct {
	NameSpace string
	Name      string
	Type      string
	State     string
	Image     string
	Scale     string
}

func PsCommand() cli.Command {
	return cli.Command{
		Name:  "ps",
		Usage: "Show workloads in a project",
		Description: `Show information on the workloads in a project. Defaults to the current context.
Examples:
	# Show workloads in the current context
	$ rancher ps

	# Show workloads in a specific project and output the results in yaml
	$ rancher ps --project projectFoo --format yaml
`,
		Action: psLs,
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "project",
				Usage: "Optional project to show workloads for",
			},
			cli.StringFlag{
				Name:  "format",
				Usage: "'json', 'yaml' or Custom format: '{{.Name}} {{.Image}}'",
			},
		},
	}
}

func psLs(ctx *cli.Context) error {
	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	if ctx.String("project") != "" {
		//Verify the project given is valid
		resource, err := Lookup(c, ctx.String("project"), "project")
		if err != nil {
			return err
		}

		sc, err := lookupConfig(ctx)
		if err != nil {
			return err
		}
		sc.Project = resource.ID

		projClient, err := cliclient.NewProjectClient(sc)
		if err != nil {
			return err
		}
		c.ProjectClient = projClient.ProjectClient
	}

	workLoads, err := c.ProjectClient.Workload.List(defaultListOpts(ctx))
	if err != nil {
		return err
	}

	wlWriter := NewTableWriter([][]string{
		{"NAMESPACE", "NameSpace"},
		{"NAME", "Name"},
		{"TYPE", "Type"},
		{"STATE", "State"},
		{"IMAGE", "Image"},
		{"SCALE", "Scale"},
	}, ctx)

	defer wlWriter.Close()

	titleCaser := cases.Title(language.Und)

	for _, item := range workLoads.Data {
		var scale string

		if item.Scale == nil {
			scale = "-"
		} else {
			scale = strconv.Itoa(int(*item.Scale))
		}

		item.Type = titleCaser.String(item.Type)

		wlWriter.Write(&PSHolder{
			NameSpace: item.NamespaceId,
			Name:      item.Name,
			Type:      item.Type,
			State:     item.State,
			Image:     item.Containers[0].Image,
			Scale:     scale,
		})
	}

	opts := defaultListOpts(ctx)
	opts.Filters["workloadId"] = ""

	orphanPods, err := c.ProjectClient.Pod.List(opts)
	if err != nil {
		return err
	}

	if len(orphanPods.Data) > 0 {
		for _, item := range orphanPods.Data {
			item.Type = titleCaser.String(item.Type)
			wlWriter.Write(&PSHolder{
				NameSpace: item.NamespaceId,
				Name:      item.Name,
				Type:      item.Type,
				State:     item.State,
				Image:     item.Containers[0].Image,
				Scale:     "Standalone", // a single pod doesn't have scale
			})
		}
	}

	return nil
}
0707010000001C000081A400000000000000000000000168B0FB3F00001815000000000000000000000000000000000000002100000000rancher-cli-2.12.1/cmd/server.gopackage cmd

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strconv"
	"strings"

	"github.com/rancher/cli/config"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
	"golang.org/x/exp/maps"
)

type serverData struct {
	Index   int
	Current string
	Name    string
	URL     string
}

// ServerCommand defines the 'rancher server' sub-commands
func ServerCommand() cli.Command {
	cfg := &config.Config{}

	return cli.Command{
		Name:  "server",
		Usage: "Operations for the server",
		Description: `Switch or view the server currently in focus.
`,
		Before: loadAndValidateConfig(cfg),
		Subcommands: []cli.Command{
			{
				Name:  "current",
				Usage: "Display the current server",
				Action: func(ctx *cli.Context) error {
					return serverCurrent(ctx.App.Writer, cfg)
				},
			},
			{
				Name:      "delete",
				Usage:     "Delete a server from the local config",
				ArgsUsage: "[SERVER_NAME]",
				Description: `
The server arg is optional, if not passed in a list of available servers will
be displayed and one can be selected.
`,
				Action: func(ctx *cli.Context) error {
					serverName, err := getSelectedServer(ctx, cfg)
					if err != nil {
						return err
					}
					return serverDelete(cfg, serverName)
				},
			},
			{
				Name:      "ls",
				Usage:     "List all servers",
				ArgsUsage: "None",
				Action: func(ctx *cli.Context) error {
					format := ctx.String("format")
					return serverLs(ctx.App.Writer, cfg, format)
				},
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "format",
						Usage: "'json', 'yaml' or Custom format: '{{.Name}} {{.URL}}'",
					},
				},
			},
			{
				Name:      "switch",
				Usage:     "Switch to a new server",
				ArgsUsage: "[SERVER_NAME]",
				Description: `
		The server arg is optional, if not passed in a list of available servers will
		be displayed and one can be selected.
		`,
				Action: func(ctx *cli.Context) error {
					serverName, err := getSelectedServer(ctx, cfg)
					if err != nil {
						return err
					}
					return serverSwitch(cfg, serverName)
				},
			},
		},
	}
}

// serverCurrent command to display the name of the current server in the local config
func serverCurrent(out io.Writer, cfg *config.Config) error {
	serverName := cfg.CurrentServer

	currentServer, found := cfg.Servers[serverName]
	if !found {
		return errors.New("current server not set")
	}

	fmt.Fprintf(out, "Name: %s URL: %s\n", serverName, currentServer.URL)
	return nil
}

// serverDelete command to delete a server from the local config
func serverDelete(cfg *config.Config, serverName string) error {
	_, ok := cfg.Servers[serverName]
	if !ok {
		return errors.New("server not found")
	}
	delete(cfg.Servers, serverName)

	if cfg.CurrentServer == serverName {
		cfg.CurrentServer = ""
	}

	err := cfg.Write()
	if err != nil {
		return err
	}
	logrus.Infof("Server %s deleted", serverName)
	return nil
}

// serverLs command to list rancher servers from the local config
func serverLs(out io.Writer, cfg *config.Config, format string) error {
	writerConfig := &TableWriterConfig{
		Writer: out,
		Format: format,
	}

	writer := NewTableWriterWithConfig([][]string{
		{"CURRENT", "Current"},
		{"NAME", "Name"},
		{"URL", "URL"},
	}, writerConfig)

	defer writer.Close()

	servers := getServers(cfg)
	for _, server := range servers {
		writer.Write(server)
	}

	return writer.Err()
}

// serverSwitch will alter and write the config to switch rancher server.
func serverSwitch(cf *config.Config, serverName string) error {
	_, ok := cf.Servers[serverName]
	if !ok {
		return errors.New("server not found")
	}

	if len(cf.Servers[serverName].Project) == 0 {
		logrus.Warn("No context set; some commands will not work. Run 'rancher context switch'")
	}

	cf.CurrentServer = serverName

	err := cf.Write()
	if err != nil {
		return err
	}
	return nil
}

// getSelectedServer will get the selected server if provided as argument,
// or it will prompt the user to select one.
func getSelectedServer(ctx *cli.Context, cfg *config.Config) (string, error) {
	serverName := ctx.Args().First()
	if serverName != "" {
		return serverName, nil
	}
	return serverFromInput(ctx, cfg)
}

// serverFromInput displays the list of servers from the local config and
// prompt the user to select one.
func serverFromInput(ctx *cli.Context, cf *config.Config) (string, error) {
	servers := getServers(cf)

	if err := displayListServers(ctx, servers); err != nil {
		return "", err
	}

	fmt.Print("Select a Server:")
	reader := bufio.NewReader(os.Stdin)

	errMessage := fmt.Sprintf("Invalid input, enter a number between 1 and %v: ", len(servers))
	var selection int

	for {
		input, err := reader.ReadString('\n')
		if err != nil {
			return "", err
		}
		input = strings.TrimSpace(input)

		if input != "" {
			i, err := strconv.Atoi(input)
			if err != nil {
				fmt.Print(errMessage)
				continue
			}
			if i <= len(servers) && i != 0 {
				selection = i - 1
				break
			}
			fmt.Print(errMessage)
			continue
		}
	}

	return servers[selection].Name, nil
}

// displayListServers displays the list of rancher servers
func displayListServers(ctx *cli.Context, servers []*serverData) error {
	writer := NewTableWriter([][]string{
		{"INDEX", "Index"},
		{"NAME", "Name"},
		{"URL", "URL"},
	}, ctx)

	defer writer.Close()

	for _, server := range servers {
		writer.Write(server)
	}
	return writer.Err()
}

// getServers returns an ordered slice (by name) of serverData
func getServers(cfg *config.Config) []*serverData {
	serverNames := maps.Keys(cfg.Servers)
	sort.Strings(serverNames)

	servers := []*serverData{}

	for i, server := range serverNames {
		var current string
		if server == cfg.CurrentServer {
			current = "*"
		}

		servers = append(servers, &serverData{
			Index:   i + 1,
			Name:    server,
			Current: current,
			URL:     cfg.Servers[server].URL,
		})
	}

	return servers
}

func loadAndValidateConfig(cfg *config.Config) cli.BeforeFunc {
	return func(ctx *cli.Context) error {
		conf, err := loadConfig(ctx)
		if err != nil {
			return err
		}
		*cfg = conf

		if len(cfg.Servers) == 0 {
			return errors.New("no servers are currently configured")
		}
		return nil
	}
}
0707010000001D000081A400000000000000000000000168B0FB3F000019A2000000000000000000000000000000000000002600000000rancher-cli-2.12.1/cmd/server_test.gopackage cmd

import (
	"bytes"
	"os"
	"testing"

	"github.com/rancher/cli/config"
	"github.com/stretchr/testify/assert"
)

func TestServerCurrentCommand(t *testing.T) {
	tt := []struct {
		name           string
		config         *config.Config
		expectedOutput string
		expectedErr    string
	}{
		{
			name:           "existing current server set",
			config:         newTestConfig(),
			expectedOutput: "Name: server1 URL: https://myserver-1.com\n",
		},
		{
			name: "empty current server",
			config: func() *config.Config {
				cfg := newTestConfig()
				cfg.CurrentServer = ""
				return cfg
			}(),
			expectedErr: "current server not set",
		},
		{
			name: "non existing current server set",
			config: &config.Config{
				CurrentServer: "notfound-server",
				Servers: map[string]*config.ServerConfig{
					"my-server": {URL: "https://myserver.com"},
				},
			},
			expectedErr: "current server not set",
		},
	}
	for _, tc := range tt {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			out := &bytes.Buffer{}

			err := serverCurrent(out, tc.config)
			if tc.expectedErr != "" {
				assert.EqualError(t, err, tc.expectedErr)
			} else {
				assert.NoError(t, err)
			}

			assert.Equal(t, tc.expectedOutput, out.String())
		})
	}
}

func TestServerDelete(t *testing.T) {
	tt := []struct {
		name                  string
		actualCurrentServer   string
		serverToDelete        string
		expectedCurrentServer string
		expectedErr           string
	}{
		{
			name:                  "delete a different server will delete it",
			actualCurrentServer:   "server1",
			serverToDelete:        "server3",
			expectedCurrentServer: "server1",
		},
		{
			name:                  "delete the same server will blank the current",
			actualCurrentServer:   "server1",
			serverToDelete:        "server1",
			expectedCurrentServer: "",
		},
		{
			name:                  "delete a non existing server",
			actualCurrentServer:   "server1",
			serverToDelete:        "server-nope",
			expectedCurrentServer: "server1",
			expectedErr:           "server not found",
		},
	}
	for _, tc := range tt {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			tmpConfig, err := os.CreateTemp("", "*-rancher-config.json")
			assert.NoError(t, err)
			defer os.Remove(tmpConfig.Name())

			// setup test config
			cfg := newTestConfig()
			cfg.Path = tmpConfig.Name()
			cfg.CurrentServer = tc.actualCurrentServer

			// do test and check resulting config
			err = serverDelete(cfg, tc.serverToDelete)
			if err != nil {
				assert.EqualError(t, err, tc.expectedErr)
			} else {
				assert.NoError(t, err)
			}

			assert.Equal(t, tc.expectedCurrentServer, cfg.CurrentServer)
			assert.Empty(t, cfg.Servers[tc.serverToDelete])
		})
	}
}

func TestServerSwitch(t *testing.T) {
	tt := []struct {
		name                  string
		actualCurrentServer   string
		serverName            string
		expectedCurrentServer string
		expectedErr           string
	}{
		{
			name:                  "switch to different server updates the current server",
			actualCurrentServer:   "server1",
			serverName:            "server3",
			expectedCurrentServer: "server3",
		},
		{
			name:                  "switch to same server is no-op",
			actualCurrentServer:   "server1",
			serverName:            "server1",
			expectedCurrentServer: "server1",
		},
		{
			name:                  "switch to non existing server",
			actualCurrentServer:   "server1",
			serverName:            "server-nope",
			expectedCurrentServer: "server1",
			expectedErr:           "server not found",
		},
		{
			name:                  "switch to empty server fails",
			actualCurrentServer:   "server1",
			serverName:            "",
			expectedCurrentServer: "server1",
			expectedErr:           "server not found",
		},
	}
	for _, tc := range tt {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			tmpConfig, err := os.CreateTemp("", "*-rancher-config.json")
			assert.NoError(t, err)
			defer os.Remove(tmpConfig.Name())

			// setup test config
			cfg := newTestConfig()
			cfg.Path = tmpConfig.Name()
			cfg.CurrentServer = tc.actualCurrentServer

			// do test and check resulting config
			err = serverSwitch(cfg, tc.serverName)
			if err != nil {
				assert.EqualError(t, err, tc.expectedErr)
			} else {
				assert.NoError(t, err)
			}

			assert.Equal(t, tc.expectedCurrentServer, cfg.CurrentServer)
		})
	}
}

func TestServerLs(t *testing.T) {
	tt := []struct {
		name           string
		config         *config.Config
		format         string
		expectedOutput string
		expectedErr    bool
	}{
		{
			name: "list servers",
			expectedOutput: `CURRENT   NAME      URL
*         server1   https://myserver-1.com
          server2   https://myserver-2.com
          server3   https://myserver-3.com
`,
		},
		{
			name:           "list empty config",
			config:         &config.Config{},
			format:         "",
			expectedOutput: "CURRENT   NAME      URL\n",
		},
		{
			name:   "list servers with json format",
			format: "json",
			expectedOutput: `{"Index":1,"Current":"*","Name":"server1","URL":"https://myserver-1.com"}
{"Index":2,"Current":"","Name":"server2","URL":"https://myserver-2.com"}
{"Index":3,"Current":"","Name":"server3","URL":"https://myserver-3.com"}
`,
		},
		{
			name:   "list servers with yaml format",
			format: "yaml",
			expectedOutput: `Current: '*'
Index: 1
Name: server1
URL: https://myserver-1.com

Current: ""
Index: 2
Name: server2
URL: https://myserver-2.com

Current: ""
Index: 3
Name: server3
URL: https://myserver-3.com

`,
		},
		{
			name:   "list servers with custom format",
			format: "{{.URL}}",
			expectedOutput: `https://myserver-1.com
https://myserver-2.com
https://myserver-3.com
`,
		},
		{
			name:        "list servers with custom format",
			format:      "{{.err}}",
			expectedErr: true,
		},
	}
	for _, tc := range tt {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			out := &bytes.Buffer{}

			if tc.config == nil {
				tc.config = newTestConfig()
			}

			// do test and check resulting config
			err := serverLs(out, tc.config, tc.format)
			if tc.expectedErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}

			assert.Equal(t, tc.expectedOutput, out.String())
		})
	}
}

func newTestConfig() *config.Config {
	return &config.Config{
		CurrentServer: "server1",
		Servers: map[string]*config.ServerConfig{
			"server1": {URL: "https://myserver-1.com"},
			"server2": {URL: "https://myserver-2.com"},
			"server3": {URL: "https://myserver-3.com"},
		},
	}
}
0707010000001E000081A400000000000000000000000168B0FB3F00000D69000000000000000000000000000000000000002300000000rancher-cli-2.12.1/cmd/settings.gopackage cmd

import (
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

type settingHolder struct {
	ID      string
	Setting managementClient.Setting
}

func SettingsCommand() cli.Command {
	return cli.Command{
		Name:        "settings",
		Aliases:     []string{"setting"},
		Usage:       "Show settings for the current server",
		Description: "List get or set settings for the current Rancher server",
		Action:      defaultAction(settingsLs),
		Flags: []cli.Flag{
			formatFlag,
		},
		Subcommands: []cli.Command{
			{
				Name:        "ls",
				Usage:       "List settings",
				Description: "Lists all settings in the current cluster.",
				ArgsUsage:   "[SETTINGNAME]",
				Action:      settingsLs,
				Flags: []cli.Flag{
					formatFlag,
					quietFlag,
				},
			},
			{
				Name:   "get",
				Usage:  "Print a setting",
				Action: settingGet,
				Flags: []cli.Flag{
					formatFlag,
				},
			},
			{
				Name:      "set",
				Usage:     "Set the value for a setting",
				Action:    settingSet,
				ArgsUsage: "[SETTINGNAME VALUE]",
				Flags: []cli.Flag{
					formatFlag,
					cli.BoolFlag{
						Name:  "default",
						Usage: "Reset the setting back to it's default value. If the default value is (blank) it will be set to that.",
					},
				},
			},
		},
	}
}

func settingsLs(ctx *cli.Context) error {
	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	settings, err := c.ManagementClient.Setting.List(defaultListOpts(ctx))
	if err != nil {
		return err
	}

	writer := NewTableWriter([][]string{
		{"ID", "ID"},
		{"NAME", "Setting.Name"},
		{"VALUE", "Setting.Value"},
	}, ctx)

	defer writer.Close()

	for _, setting := range settings.Data {
		writer.Write(&settingHolder{
			ID:      setting.ID,
			Setting: setting,
		})
	}
	return writer.Err()
}

func settingGet(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowCommandHelp(ctx, "settings")
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	resource, err := Lookup(c, ctx.Args().First(), "setting")
	if err != nil {
		return err
	}

	setting, err := c.ManagementClient.Setting.ByID(resource.ID)
	if err != nil {
		return err
	}

	writer := NewTableWriter([][]string{
		{"ID", "ID"},
		{"NAME", "Setting.Name"},
		{"VALUE", "Setting.Value"},
		{"DEFAULT", "Setting.Default"},
		{"CUSTOMIZED", "Setting.Customized"},
	}, ctx)

	defer writer.Close()

	writer.Write(&settingHolder{
		ID:      setting.ID,
		Setting: *setting,
	})

	return writer.Err()
}

func settingSet(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowCommandHelp(ctx, "settings")
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	resource, err := Lookup(c, ctx.Args().First(), "setting")
	if err != nil {
		return err
	}

	setting, err := c.ManagementClient.Setting.ByID(resource.ID)
	if err != nil {
		return err
	}

	update := make(map[string]string)
	if ctx.Bool("default") {
		update["value"] = setting.Default
	} else {
		update["value"] = ctx.Args().Get(1)
	}

	updatedSetting, err := c.ManagementClient.Setting.Update(setting, update)
	if err != nil {
		return err
	}

	var updatedValue string
	if updatedSetting.Value == "" {
		updatedValue = "(blank)"
	} else {
		updatedValue = updatedSetting.Value
	}
	logrus.Infof("Successfully updated setting %s with a new value of: %s", updatedSetting.Name, updatedValue)

	return nil
}
0707010000001F000081A400000000000000000000000168B0FB3F00001522000000000000000000000000000000000000001E00000000rancher-cli-2.12.1/cmd/ssh.gopackage cmd

import (
	"archive/zip"
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path"
	"strings"

	"github.com/rancher/cli/cliclient"
	managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/urfave/cli"
)

const sshDescription = `
For any nodes created through Rancher using docker-machine,
you can SSH into the node. This is not supported for any custom nodes.
Examples:
	# SSH into a node by ID/name
	$ rancher ssh nodeFoo
	# SSH into a node by ID/name using the external IP address
	$ rancher ssh -e nodeFoo
	# SSH into a node by name but specify the login name to use
	$ rancher ssh -l login1 nodeFoo
	# SSH into a node by specifying login name and node using the @ syntax while adding a command to run
	$ rancher ssh login1@nodeFoo -- netstat -p tcp
`

func SSHCommand() cli.Command {
	return cli.Command{
		Name:        "ssh",
		Usage:       "SSH into a node",
		Description: sshDescription,
		Action:      nodeSSH,
		ArgsUsage:   "[NODE_ID/NODE_NAME]",
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "external,e",
				Usage: "Use the external ip address of the node",
			},
			cli.StringFlag{
				Name:  "login,l",
				Usage: "The login name",
			},
		},
	}
}

func nodeSSH(ctx *cli.Context) error {
	args := ctx.Args()
	if len(args) > 0 && (args[0] == "-h" || args[0] == "--help") {
		return cli.ShowCommandHelp(ctx, "ssh")
	}

	if ctx.NArg() == 0 {
		return cli.ShowCommandHelp(ctx, "ssh")
	}

	user := ctx.String("login")
	nodeName := ctx.Args().First()

	if strings.Contains(nodeName, "@") {
		user = strings.Split(nodeName, "@")[0]
		nodeName = strings.Split(nodeName, "@")[1]
	}

	args = args[1:]

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	sshNode, key, err := getNodeAndKey(ctx, c, nodeName)
	if err != nil {
		return err
	}

	if user == "" {
		user = sshNode.SshUser
	}
	ipAddress := sshNode.IPAddress
	if ctx.Bool("external") {
		ipAddress = sshNode.ExternalIPAddress
	}

	return processExitCode(callSSH(key, ipAddress, user, args))
}

func getNodeAndKey(ctx *cli.Context, c *cliclient.MasterClient, nodeName string) (managementClient.Node, []byte, error) {
	sshNode := managementClient.Node{}
	resource, err := Lookup(c, nodeName, "node")
	if err != nil {
		return sshNode, nil, err
	}

	sshNode, err = getNodeByID(ctx, c, resource.ID)
	if err != nil {
		return sshNode, nil, err
	}

	link := sshNode.Links["nodeConfig"]
	if link == "" {
		// Get the machine and use that instead.
		machine, err := getMachineByNodeName(ctx, c, sshNode.NodeName)
		if err != nil {
			return sshNode, nil, fmt.Errorf("failed to find SSH key for node [%s]", nodeName)
		}

		link = machine.Links["sshkeys"]
	}

	key, sshUser, err := getSSHKey(c, link, getNodeName(sshNode))
	if err != nil {
		return sshNode, nil, err
	}
	if sshUser != "" {
		sshNode.SshUser = sshUser
	}

	return sshNode, key, nil
}

func callSSH(content []byte, ip string, user string, args []string) error {
	dest := fmt.Sprintf("%s@%s", user, ip)

	tmpfile, err := os.CreateTemp("", "ssh")
	if err != nil {
		return err
	}
	defer os.Remove(tmpfile.Name())

	if err := os.Chmod(tmpfile.Name(), 0600); err != nil {
		return err
	}

	_, err = tmpfile.Write(content)
	if err != nil {
		return err
	}

	if err := tmpfile.Close(); err != nil {
		return err
	}

	cmd := exec.Command("ssh", append([]string{"-i", tmpfile.Name(), dest}, args...)...)
	cmd.Stdout = os.Stdout
	cmd.Stdin = os.Stdin
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func getSSHKey(c *cliclient.MasterClient, link, nodeName string) ([]byte, string, error) {
	if link == "" {
		return nil, "", fmt.Errorf("failed to find SSH key for %s", nodeName)
	}

	req, err := http.NewRequest("GET", link, nil)
	if err != nil {
		return nil, "", err
	}
	req.SetBasicAuth(c.UserConfig.AccessKey, c.UserConfig.SecretKey)
	req.Header.Add("Accept-Encoding", "zip")

	var tlsConfig *tls.Config
	if c.UserConfig.CACerts != "" {
		roots := x509.NewCertPool()
		ok := roots.AppendCertsFromPEM([]byte(c.UserConfig.CACerts))
		if !ok {
			return []byte{}, "", err
		}
		tlsConfig = &tls.Config{
			RootCAs: roots,
		}
	}

	client, err := newHTTPClient(c.UserConfig, tlsConfig)
	if err != nil {
		return nil, "", err
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, "", err
	}
	defer resp.Body.Close()

	zipFiles, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, "", err
	}

	if resp.StatusCode != 200 {
		return nil, "", fmt.Errorf("%s", zipFiles)
	}

	zipReader, err := zip.NewReader(bytes.NewReader(zipFiles), resp.ContentLength)
	if err != nil {
		return nil, "", err
	}

	var sshKey []byte
	var sshUser string
	for _, file := range zipReader.File {
		if path.Base(file.Name) == "id_rsa" {
			sshKey, err = readFile(file)
			if err != nil {
				return nil, "", err
			}
		} else if path.Base(file.Name) == "config.json" {
			config, err := readFile(file)
			if err != nil {
				return nil, "", err
			}

			var data map[string]interface{}
			err = json.Unmarshal(config, &data)
			if err != nil {
				return nil, "", err
			}
			sshUser, _ = data["SSHUser"].(string)
		}
	}
	if len(sshKey) == 0 {
		return sshKey, "", errors.New("can't find private key file")
	}
	return sshKey, sshUser, nil
}

func readFile(file *zip.File) ([]byte, error) {
	r, err := file.Open()
	if err != nil {
		return nil, err
	}
	defer r.Close()
	return io.ReadAll(r)
}
07070100000020000081A400000000000000000000000168B0FB3F00000393000000000000000000000000000000000000001D00000000rancher-cli-2.12.1/cmd/up.gopackage cmd

import (
	"os"

	"github.com/rancher/cli/cliclient"
	client "github.com/rancher/rancher/pkg/client/generated/management/v3"
	"github.com/urfave/cli"
)

func UpCommand() cli.Command {
	return cli.Command{
		Name:   "up",
		Usage:  "apply compose config",
		Action: defaultAction(apply),
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "file,f",
				Usage: "The location of compose config file",
			},
		},
	}
}

func apply(ctx *cli.Context) error {
	cf, err := lookupConfig(ctx)
	if err != nil {
		return err
	}
	c, err := cliclient.NewManagementClient(cf)
	if err != nil {
		return err
	}

	filePath := ctx.String("file")
	compose, err := os.ReadFile(filePath)
	if err != nil {
		return err
	}

	globalComposeConfig := &client.ComposeConfig{
		RancherCompose: string(compose),
	}
	if _, err := c.ManagementClient.ComposeConfig.Create(globalComposeConfig); err != nil {
		return err
	}
	return nil
}
07070100000021000081A400000000000000000000000168B0FB3F0000029A000000000000000000000000000000000000002200000000rancher-cli-2.12.1/cmd/util_ls.gopackage cmd

import (
	"github.com/rancher/norman/types"
	"github.com/urfave/cli"
)

func baseListOpts() *types.ListOpts {
	return &types.ListOpts{
		Filters: map[string]interface{}{
			"limit": -1,
			"all":   true,
		},
	}
}

func defaultListOpts(ctx *cli.Context) *types.ListOpts {
	listOpts := baseListOpts()
	if ctx != nil && !ctx.Bool("all") {
		listOpts.Filters["removed_null"] = "1"
		listOpts.Filters["state_ne"] = []string{
			"inactive",
			"stopped",
			"removing",
		}
		delete(listOpts.Filters, "all")
	}
	if ctx != nil && ctx.Bool("system") {
		delete(listOpts.Filters, "system")
	} else {
		listOpts.Filters["system"] = "false"
	}
	return listOpts
}
07070100000022000081A400000000000000000000000168B0FB3F0000083F000000000000000000000000000000000000001F00000000rancher-cli-2.12.1/cmd/wait.gopackage cmd

import (
	"fmt"
	"strings"
	"time"

	ntypes "github.com/rancher/norman/types"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

var (
	waitTypes = []string{"cluster", "project"}
)

func WaitCommand() cli.Command {
	return cli.Command{
		Name:      "wait",
		Usage:     "Wait for resources " + strings.Join(waitTypes, ", "),
		ArgsUsage: "[ID/NAME]",
		Action:    defaultAction(wait),
		Flags: []cli.Flag{
			cli.IntFlag{
				Name:  "timeout",
				Usage: "Time in seconds to wait for a resource",
				Value: 120,
			},
		},
	}
}

func wait(ctx *cli.Context) error {
	if ctx.NArg() == 0 {
		return cli.ShowCommandHelp(ctx, "wait")
	}

	c, err := GetClient(ctx)
	if err != nil {
		return err
	}

	resource, err := Lookup(c, ctx.Args().First(), waitTypes...)
	if err != nil {
		return err
	}

	mapResource := map[string]interface{}{}

	// Initial check shortcut
	err = c.ByID(resource, &mapResource)
	if err != nil {
		return err
	}

	ok, err := checkDone(resource, mapResource)
	if err != nil {
		return err
	}
	if ok {
		return nil
	}

	timeout := time.After(time.Duration(ctx.Int("timeout")) * time.Second)
	ticker := time.NewTicker(time.Second)

	for {
		select {
		case <-timeout:
			return fmt.Errorf("Timeout reached %v:%v transitioningMessage: %v", resource.Type, resource.ID, mapResource["transitioningMessage"])
		case <-ticker.C:
			err = c.ByID(resource, &mapResource)
			if err != nil {
				return err
			}

			ok, err := checkDone(resource, mapResource)
			if err != nil {
				return err
			}
			if ok {
				return nil
			}
		}
	}
}

func checkDone(resource *ntypes.Resource, data map[string]interface{}) (bool, error) {
	transitioning := fmt.Sprint(data["transitioning"])
	logrus.Debugf("%s:%s transitioning=%s state=%v", resource.Type, resource.ID, transitioning,
		data["state"])

	switch transitioning {
	case "yes":
		return false, nil
	case "error":
		if data["state"] == "provisioning" {
			break
		}
		return false, fmt.Errorf("%v:%v failed, transitioningMessage: %v", resource.Type, resource.ID, data["transitioningMessage"])
	}

	return data["state"] == "active", nil
}
07070100000023000081A400000000000000000000000168B0FB3F00000916000000000000000000000000000000000000002100000000rancher-cli-2.12.1/cmd/writer.gopackage cmd

import (
	"encoding/json"
	"io"
	"os"
	"text/tabwriter"

	"github.com/ghodss/yaml"
	"github.com/urfave/cli"
)

type TableWriter struct {
	HeaderFormat  string
	ValueFormat   string
	err           error
	headerPrinted bool
	Writer        *tabwriter.Writer
}

type TableWriterConfig struct {
	Quiet  bool
	Format string
	Writer io.Writer
}

func NewTableWriter(values [][]string, ctx *cli.Context) *TableWriter {
	cfg := &TableWriterConfig{
		Writer: os.Stdout,
		Quiet:  ctx.Bool("quiet"),
		Format: ctx.String("format"),
	}

	return NewTableWriterWithConfig(values, cfg)
}

func NewTableWriterWithConfig(values [][]string, config *TableWriterConfig) *TableWriter {
	writer := config.Writer
	if writer == nil {
		writer = os.Stdout
	}

	t := &TableWriter{
		Writer: tabwriter.NewWriter(writer, 10, 1, 3, ' ', 0),
	}
	t.HeaderFormat, t.ValueFormat = SimpleFormat(values)

	// remove headers if quiet or with a different format
	if config.Quiet || config.Format != "" {
		t.HeaderFormat = ""
	}

	// when quiet show only the ID
	if config.Quiet {
		t.ValueFormat = "{{.ID}}\n"
	}

	// check for custom formatting
	if config.Format != "" {
		customFormat := config.Format

		// add a newline for other custom formats
		if customFormat != "json" && customFormat != "yaml" {
			customFormat += "\n"
		}
		t.ValueFormat = customFormat
	}

	return t
}

func (t *TableWriter) Err() error {
	return t.err
}

func (t *TableWriter) writeHeader() {
	if t.HeaderFormat != "" && !t.headerPrinted {
		t.headerPrinted = true
		t.err = printTemplate(t.Writer, t.HeaderFormat, struct{}{})
		if t.err != nil {
			return
		}
	}
}

func (t *TableWriter) Write(obj interface{}) {
	if t.err != nil {
		return
	}

	t.writeHeader()
	if t.err != nil {
		return
	}

	if t.ValueFormat == "json" {
		content, err := json.Marshal(obj)
		t.err = err
		if t.err != nil {
			return
		}
		_, t.err = t.Writer.Write(append(content, byte('\n')))
	} else if t.ValueFormat == "yaml" {
		content, err := yaml.Marshal(obj)
		t.err = err
		if t.err != nil {
			return
		}
		_, t.err = t.Writer.Write(append(content, byte('\n')))
	} else {
		t.err = printTemplate(t.Writer, t.ValueFormat, obj)
	}
}

func (t *TableWriter) Close() error {
	if t.err != nil {
		return t.err
	}
	t.writeHeader()
	if t.err != nil {
		return t.err
	}
	return t.Writer.Flush()
}
07070100000024000041ED00000000000000000000000268B0FB3F00000000000000000000000000000000000000000000001A00000000rancher-cli-2.12.1/config07070100000025000081A400000000000000000000000168B0FB3F00001198000000000000000000000000000000000000002400000000rancher-cli-2.12.1/config/config.gopackage config

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
	"k8s.io/client-go/tools/clientcmd/api"
)

var ErrNoConfigurationFound = errors.New("no configuration found, run `login`")

// Config holds the main config for the user
type Config struct {
	Servers map[string]*ServerConfig
	//Path to the config file
	Path string `json:"path,omitempty"`
	// CurrentServer the user has in focus
	CurrentServer string
}

// ServerConfig holds the config for each server the user has setup
type ServerConfig struct {
	AccessKey          string                     `json:"accessKey"`
	SecretKey          string                     `json:"secretKey"`
	TokenKey           string                     `json:"tokenKey"`
	URL                string                     `json:"url"`
	Project            string                     `json:"project"`
	CACerts            string                     `json:"cacert"`
	KubeCredentials    map[string]*ExecCredential `json:"kubeCredentials"`
	KubeConfigs        map[string]*api.Config     `json:"kubeConfigs"`
	ProxyURL           string                     `json:"proxyUrl"`
	HTTPTimeoutSeconds int                        `json:"httpTimeoutSeconds"`
}

func (c *ServerConfig) GetHTTPTimeout() time.Duration {
	return time.Duration(c.HTTPTimeoutSeconds) * time.Second
}

// LoadFromPath attempts to load a config from the given file path. If the file
// doesn't exist, an empty config is returned.
func LoadFromPath(path string) (Config, error) {
	cf := Config{
		Path:    path,
		Servers: make(map[string]*ServerConfig),
	}

	content, err := os.ReadFile(path)
	if err != nil {
		// it's okay if the file is empty, we still return a valid config
		if os.IsNotExist(err) {
			return cf, nil
		}

		return cf, err
	}

	if err := json.Unmarshal(content, &cf); err != nil {
		return cf, fmt.Errorf("unmarshaling %s: %w", path, err)
	}
	cf.Path = path

	return cf, nil
}

// GetFilePermissionWarnings returns the following warnings based on the file permission:
// - one warning if the file is group-readable
// - one warning if the file is world-readable
// We want this because configuration may have sensitive information (eg: creds).
// A nil error is returned if the file doesn't exist.
func GetFilePermissionWarnings(path string) ([]string, error) {
	// Permission bits on Windows are not representative
	// of the actual ACLs set for a given file. As the owner
	// bit is always used for all three bits on Windows, this check
	// will always fail, even if the file has restricted access to admins only.
	if runtime.GOOS == "windows" {
		return nil, nil
	}
	info, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return []string{}, nil
		}
		return []string{}, fmt.Errorf("get file info: %w", err)
	}

	var warnings []string
	if info.Mode()&0040 > 0 {
		warnings = append(warnings, fmt.Sprintf("Rancher configuration file %s is group-readable. This is insecure.", path))
	}
	if info.Mode()&0004 > 0 {
		warnings = append(warnings, fmt.Sprintf("Rancher configuration file %s is world-readable. This is insecure.", path))
	}
	return warnings, nil
}

func (c Config) Write() error {
	err := os.MkdirAll(filepath.Dir(c.Path), 0700)
	if err != nil {
		return err
	}
	logrus.Infof("Saving config to %s", c.Path)
	p := c.Path
	c.Path = ""
	output, err := os.OpenFile(p, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer output.Close()

	return json.NewEncoder(output).Encode(c)
}

func (c Config) FocusedServer() (*ServerConfig, error) {
	currentServer, found := c.Servers[c.CurrentServer]
	if !found || currentServer == nil {
		return nil, ErrNoConfigurationFound
	}
	return currentServer, nil
}

func (c ServerConfig) FocusedCluster() string {
	cluster, _, ok := strings.Cut(c.Project, ":")
	if !ok {
		return ""
	}
	return cluster
}

func (c ServerConfig) FocusedProject() string {
	return c.Project
}

func (c ServerConfig) KubeToken(key string) *ExecCredential {
	return c.KubeCredentials[key]
}

func (c ServerConfig) EnvironmentURL() (string, error) {
	url, err := baseURL(c.URL)
	if err != nil {
		return "", err
	}
	return url, nil
}

func baseURL(fullURL string) (string, error) {
	idx := strings.LastIndex(fullURL, "/v3")
	if idx == -1 {
		u, err := url.Parse(fullURL)
		if err != nil {
			return "", err
		}
		newURL := url.URL{
			Scheme: u.Scheme,
			Host:   u.Host,
		}
		return newURL.String(), nil
	}
	return fullURL[:idx], nil
}
07070100000026000081A400000000000000000000000168B0FB3F0000127B000000000000000000000000000000000000002900000000rancher-cli-2.12.1/config/config_test.gopackage config

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
)

const (
	validFile = `
{
  "Servers": {
    "rancherDefault": {
      "accessKey": "the-access-key",
      "secretKey": "the-secret-key",
      "tokenKey": "the-token-key",
      "url": "https://example.com",
      "project": "cluster-id:project-id",
      "cacert": "",
      "kubeCredentials": null,
      "kubeConfigs": null
    }
  },
  "CurrentServer": "rancherDefault"
}`
	invalidFile = `invalid config file`
)

func TestGetFilePermissionWarnings(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name             string
		mode             os.FileMode
		expectedWarnings int
	}{
		{
			name:             "neither group-readable nor world-readable",
			mode:             os.FileMode(0600),
			expectedWarnings: 0,
		},
		{
			name:             "group-readable and world-readable",
			mode:             os.FileMode(0644),
			expectedWarnings: 2,
		},
		{
			name:             "group-readable",
			mode:             os.FileMode(0640),
			expectedWarnings: 1,
		},
		{
			name:             "world-readable",
			mode:             os.FileMode(0604),
			expectedWarnings: 1,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			assert := assert.New(t)

			dir, err := os.MkdirTemp("", "rancher-cli-test-*")
			assert.NoError(err)
			defer os.RemoveAll(dir)

			path := filepath.Join(dir, "cli2.json")
			err = os.WriteFile(path, []byte(validFile), tt.mode)
			assert.NoError(err)

			warnings, err := GetFilePermissionWarnings(path)
			assert.NoError(err)
			assert.Len(warnings, tt.expectedWarnings)
		})
	}
}

func TestPermission(t *testing.T) {
	t.Parallel()

	// New config files should have 0600 permissions
	t.Run("new config file", func(t *testing.T) {
		t.Parallel()
		assert := assert.New(t)

		dir, err := os.MkdirTemp("", "rancher-cli-test-*")
		assert.NoError(err)
		defer os.RemoveAll(dir)

		path := filepath.Join(dir, "cli2.json")
		conf, err := LoadFromPath(path)
		assert.NoError(err)

		err = conf.Write()
		assert.NoError(err)

		info, err := os.Stat(path)
		assert.NoError(err)
		assert.Equal(os.FileMode(0600), info.Mode())

		// make sure new file doesn't create permission warnings
		warnings, err := GetFilePermissionWarnings(path)
		assert.NoError(err)
		assert.Len(warnings, 0)
	})
	// Already existing config files should keep their current permissions
	t.Run("existing config file", func(t *testing.T) {
		t.Parallel()
		assert := assert.New(t)

		dir, err := os.MkdirTemp("", "rancher-cli-test-*")
		assert.NoError(err)
		defer os.RemoveAll(dir)

		path := filepath.Join(dir, "cli2.json")
		err = os.WriteFile(path, []byte(validFile), 0700)
		assert.NoError(err)

		conf, err := LoadFromPath(path)
		assert.NoError(err)

		err = conf.Write()
		assert.NoError(err)

		info, err := os.Stat(path)
		assert.NoError(err)
		assert.Equal(os.FileMode(0700), info.Mode())
	})
}

func TestLoadFromPath(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name         string
		content      string
		expectedConf Config
		expectedErr  bool
	}{
		{
			name:    "valid config",
			content: validFile,
			expectedConf: Config{
				Servers: map[string]*ServerConfig{
					"rancherDefault": {
						AccessKey: "the-access-key",
						SecretKey: "the-secret-key",
						TokenKey:  "the-token-key",
						URL:       "https://example.com",
						Project:   "cluster-id:project-id",
						CACerts:   "",
					},
				},
				CurrentServer: "rancherDefault",
			},
		},
		{
			name:    "invalid config",
			content: invalidFile,
			expectedConf: Config{
				Servers: map[string]*ServerConfig{},
			},
			expectedErr: true,
		},
		{
			name:    "non existing file",
			content: "",
			expectedConf: Config{
				Servers:       map[string]*ServerConfig{},
				CurrentServer: "",
			},
		},
	}

	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			assert := assert.New(t)

			dir, err := os.MkdirTemp("", "rancher-cli-test-*")
			assert.NoError(err)
			defer os.RemoveAll(dir)

			path := filepath.Join(dir, "cli2.json")
			// make sure the path points to the temp dir created in the test
			tt.expectedConf.Path = path

			if tt.content != "" {
				err = os.WriteFile(path, []byte(tt.content), 0600)
				assert.NoError(err)
			}

			conf, err := LoadFromPath(path)
			if tt.expectedErr {
				assert.Error(err)
				// We kept the old behavior of returning a valid config even in
				// case of an error so we assert it here. If you change this
				// behavior, make sure there aren't any regressions.
				assert.Equal(tt.expectedConf, conf)
				return
			}

			assert.NoError(err)
			assert.Equal(tt.expectedConf, conf)
		})
	}
}
07070100000027000081A400000000000000000000000168B0FB3F00000B38000000000000000000000000000000000000002900000000rancher-cli-2.12.1/config/kube_config.gopackage config

import "time"

// ExecCredential is used by exec-based plugins to communicate credentials to
// HTTP transports. //v1beta1/types.go
type ExecCredential struct {
	TypeMeta `json:",inline"`

	// Spec holds information passed to the plugin by the transport. This contains
	// request and runtime specific information, such as if the session is interactive.
	Spec ExecCredentialSpec `json:"spec,omitempty"`

	// Status is filled in by the plugin and holds the credentials that the transport
	// should use to contact the API.
	// +optional
	Status *ExecCredentialStatus `json:"status,omitempty"`
}

// ExecCredentialSpec holds request and runtime specific information provided by
// the transport.
type ExecCredentialSpec struct{}

// ExecCredentialStatus holds credentials for the transport to use.
// Token and ClientKeyData are sensitive fields. This data should only be
// transmitted in-memory between client and exec plugin process. Exec plugin
// itself should at least be protected via file permissions.
type ExecCredentialStatus struct {
	// ExpirationTimestamp indicates a time when the provided credentials expire.
	// +optional
	ExpirationTimestamp *Time `json:"expirationTimestamp,omitempty"`
	// Token is a bearer token used by the client for request authentication.
	Token string `json:"token,omitempty"`
	// PEM-encoded client TLS certificates (including intermediates, if any).
	ClientCertificateData string `json:"clientCertificateData,omitempty"`
	// PEM-encoded private key for the above certificate.
	ClientKeyData string `json:"clientKeyData,omitempty"`
}

// TypeMeta describes an individual object in an API response or request
// with strings representing the type of the object and its API schema version.
// Structures that are versioned or persisted should inline TypeMeta.
type TypeMeta struct {
	// Kind is a string value representing the REST resource this object represents.
	// Servers may infer this from the endpoint the client submits requests to.
	// Cannot be updated.
	// In CamelCase.
	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
	// +optional
	Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`

	// APIVersion defines the versioned schema of this representation of an object.
	// Servers should convert recognized schemas to the latest internal value, and
	// may reject unrecognized values.
	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
	// +optional
	APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt,name=apiVersion"`
}

// Time is a wrapper around time.Time which supports correct
// marshaling to YAML and JSON.  Wrappers are provided for many
// of the factory methods that the time package offers.
type Time struct {
	time.Time `protobuf:"-"`
}
07070100000028000041ED00000000000000000000000268B0FB3F00000000000000000000000000000000000000000000001B00000000rancher-cli-2.12.1/contrib07070100000029000081ED00000000000000000000000168B0FB3F000001C9000000000000000000000000000000000000002300000000rancher-cli-2.12.1/contrib/rancher#!/bin/bash

[[ -d ~/.rancher ]] || mkdir -p  ~/.rancher
[[ -d ~/.ssh ]] || mkdir -p  ~/.ssh
[[ -e ~/.ssh/known_hosts ]] || touch ~/.ssh/known_hosts
[[ -e ~/.rancher/cli.json ]] || echo "{"accessKey":"","secretKey":"","url":"","environment":""}" >  ~/.rancher/cli.json

IMAGE=${IMAGE:-rancher/cli}

exec docker run --rm -it --net host -v ~/.rancher/cli.json:/root/.rancher/cli.json  -v ~/.ssh/known_hosts:/root/.ssh/known_hosts -v $(pwd):/mnt ${IMAGE} "$@"
0707010000002A000081A400000000000000000000000168B0FB3F00000F9A000000000000000000000000000000000000001A00000000rancher-cli-2.12.1/go.modmodule github.com/rancher/cli

go 1.24.0

toolchain go1.24.6

replace (
	k8s.io/apiserver => k8s.io/apiserver v0.33.2
	k8s.io/client-go => k8s.io/client-go v0.33.2
	k8s.io/component-base => k8s.io/component-base v0.33.2
	k8s.io/kubernetes => k8s.io/kubernetes v1.33.2
)

require (
	github.com/ghodss/yaml v1.0.0
	github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b
	github.com/rancher/norman v0.6.1
	github.com/rancher/rancher/pkg/apis v0.0.0-20250828225308-ed376a329731
	github.com/rancher/rancher/pkg/client v0.0.0-20250828225308-ed376a329731
	github.com/sirupsen/logrus v1.9.3
	github.com/stretchr/testify v1.10.0
	github.com/tidwall/gjson v1.17.0
	github.com/urfave/cli v1.22.14
	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
	golang.org/x/oauth2 v0.30.0
	golang.org/x/sync v0.16.0
	golang.org/x/term v0.33.0
	golang.org/x/text v0.27.0
	k8s.io/client-go v12.0.0+incompatible
)

require (
	github.com/beorn7/perks v1.0.1 // indirect
	github.com/blang/semver/v4 v4.0.0 // indirect
	github.com/cespare/xxhash/v2 v2.3.0 // indirect
	github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
	github.com/emicklei/go-restful/v3 v3.12.2 // indirect
	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
	github.com/go-logr/logr v1.4.3 // indirect
	github.com/go-openapi/jsonpointer v0.21.0 // indirect
	github.com/go-openapi/jsonreference v0.21.0 // indirect
	github.com/go-openapi/swag v0.23.0 // indirect
	github.com/gogo/protobuf v1.3.2 // indirect
	github.com/google/gnostic-models v0.6.9 // indirect
	github.com/google/go-cmp v0.7.0 // indirect
	github.com/google/uuid v1.6.0 // indirect
	github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
	github.com/josharian/intern v1.0.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/mailru/easyjson v0.7.7 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
	github.com/pkg/errors v0.9.1 // indirect
	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
	github.com/prometheus/client_golang v1.22.0 // indirect
	github.com/prometheus/client_model v0.6.1 // indirect
	github.com/prometheus/common v0.62.0 // indirect
	github.com/prometheus/procfs v0.15.1 // indirect
	github.com/rancher/aks-operator v1.12.1 // indirect
	github.com/rancher/eks-operator v1.12.1 // indirect
	github.com/rancher/fleet/pkg/apis v0.13.0 // indirect
	github.com/rancher/gke-operator v1.12.1 // indirect
	github.com/rancher/lasso v0.2.3 // indirect
	github.com/rancher/rke v1.8.0 // indirect
	github.com/rancher/wrangler/v3 v3.2.3 // indirect
	github.com/russross/blackfriday/v2 v2.1.0 // indirect
	github.com/spf13/pflag v1.0.6 // indirect
	github.com/tidwall/match v1.1.1 // indirect
	github.com/tidwall/pretty v1.2.0 // indirect
	github.com/x448/float16 v0.8.4 // indirect
	go.opentelemetry.io/otel v1.36.0 // indirect
	go.opentelemetry.io/otel/trace v1.36.0 // indirect
	go.yaml.in/yaml/v2 v2.4.2 // indirect
	golang.org/x/net v0.42.0 // indirect
	golang.org/x/sys v0.34.0 // indirect
	golang.org/x/time v0.12.0 // indirect
	google.golang.org/protobuf v1.36.6 // indirect
	gopkg.in/inf.v0 v0.9.1 // indirect
	gopkg.in/yaml.v2 v2.4.0 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
	k8s.io/api v0.33.4 // indirect
	k8s.io/apimachinery v0.33.4 // indirect
	k8s.io/apiserver v0.33.4 // indirect
	k8s.io/component-base v0.33.4 // indirect
	k8s.io/klog/v2 v2.130.1 // indirect
	k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
	k8s.io/kubernetes v1.33.4 // indirect
	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
	sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
	sigs.k8s.io/randfill v1.0.0 // indirect
	sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
	sigs.k8s.io/yaml v1.6.0 // indirect
)
0707010000002B000081A400000000000000000000000168B0FB3F00005411000000000000000000000000000000000000001A00000000rancher-cli-2.12.1/go.sumgithub.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b h1:NGgE5ELokSf2tZ/bydyDUKrvd/jP8lrAoPNeBuMOTOk=
github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b/go.mod h1:zT/uzhdQGTqlwTq7Lpbj3JoJQWfPfIJ1tE0OidAmih8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY=
github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rancher/aks-operator v1.12.1 h1:xh6zog+7+kQLwoaRDqz/LMf9/SD3L3G4tNsgY8wQmh8=
github.com/rancher/aks-operator v1.12.1/go.mod h1:egCt7JaCf4SqBtF+PbHS6mJ06pbXvkWfjF1o3VhHqVs=
github.com/rancher/eks-operator v1.12.1 h1:ZsaDyQa1Ykrf9b1Ux11DICIV4JXRy14kWKqE0JfAZg0=
github.com/rancher/eks-operator v1.12.1/go.mod h1:3hz8e7XqekMw/76TShanMS/iMBcQoom2j7/B9nyKbVQ=
github.com/rancher/fleet/pkg/apis v0.13.0 h1:soXF3cKWD9sMRcX5OlVnOY8aoPr+/FF8DP/zZtLLe3g=
github.com/rancher/fleet/pkg/apis v0.13.0/go.mod h1:LpFIRtWNJe6Tf4EGJ/brPtutyTUlFj2hJDso1/5CO7Y=
github.com/rancher/gke-operator v1.12.1 h1:zH2DEQsea+kG61cN0+1NWDQp0FyN28o2XIm5ieRLCKU=
github.com/rancher/gke-operator v1.12.1/go.mod h1:TbOTInXvdC1fPAxj2BHlRIh2j7ezTVQ7eIJW+udz5Xg=
github.com/rancher/lasso v0.2.3 h1:74/z/C/O3ykhyMrRuEgc9kVyYiSoS7kp5BAijlcyXDg=
github.com/rancher/lasso v0.2.3/go.mod h1:G+KeeOaKRjp+qGp0bV6VbLhYrq1vHbJPbDh40ejg5yE=
github.com/rancher/norman v0.6.1 h1:JVIxkWZcbxtrcSK4WIwsmyB07TzTvoqqf/R0fZB7ylk=
github.com/rancher/norman v0.6.1/go.mod h1:jRcnEruyY6olqtImy+q+zw0/iXX7YqLF4/K4kh5hR40=
github.com/rancher/rancher/pkg/apis v0.0.0-20250828225308-ed376a329731 h1:W78mcek4ZkClmXPi2QgJbQwibgBV9dxEfTio5IWfpkQ=
github.com/rancher/rancher/pkg/apis v0.0.0-20250828225308-ed376a329731/go.mod h1:CdcIOwWzSIdCL50hDB1hu178TiC2SeNWLjxRwKeSmfc=
github.com/rancher/rancher/pkg/client v0.0.0-20250828225308-ed376a329731 h1:E0o91QJi+rku2ZmrJV4Sq0tWsRYhssfYh9WsDYkHsKQ=
github.com/rancher/rancher/pkg/client v0.0.0-20250828225308-ed376a329731/go.mod h1:CBcnRTj0CcXGUpShIt8RvwKrZW5iXvPsLxmSEN7al/M=
github.com/rancher/rke v1.8.0 h1:87jeoOccnnNCq27YgWgMh4o0GVrrVKbw+zfo+cHMZlo=
github.com/rancher/rke v1.8.0/go.mod h1:x9N1abruzDFMwTpqq2cnaDYpKCptlNoW8VraNWB6Pc4=
github.com/rancher/wrangler/v3 v3.2.3 h1:s35Dpu/oIbXI1GV/FmYQaAex60+NXe67An3Z5zzzyEY=
github.com/rancher/wrangler/v3 v3.2.3/go.mod h1:TA1QuuQxrtn/kmJbBLW/l24IcfHBmSXBa9an3IRlqQQ=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.33.4 h1:oTzrFVNPXBjMu0IlpA2eDDIU49jsuEorGHB4cvKupkk=
k8s.io/api v0.33.4/go.mod h1:VHQZ4cuxQ9sCUMESJV5+Fe8bGnqAARZ08tSTdHWfeAc=
k8s.io/apimachinery v0.33.4 h1:SOf/JW33TP0eppJMkIgQ+L6atlDiP/090oaX0y9pd9s=
k8s.io/apimachinery v0.33.4/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
k8s.io/apiserver v0.33.2 h1:KGTRbxn2wJagJowo29kKBp4TchpO1DRO3g+dB/KOJN4=
k8s.io/apiserver v0.33.2/go.mod h1:9qday04wEAMLPWWo9AwqCZSiIn3OYSZacDyu/AcoM/M=
k8s.io/client-go v0.33.2 h1:z8CIcc0P581x/J1ZYf4CNzRKxRvQAwoAolYPbtQes+E=
k8s.io/client-go v0.33.2/go.mod h1:9mCgT4wROvL948w6f6ArJNb7yQd7QsvqavDeZHvNmHo=
k8s.io/component-base v0.33.2 h1:sCCsn9s/dG3ZrQTX/Us0/Sx2R0G5kwa0wbZFYoVp/+0=
k8s.io/component-base v0.33.2/go.mod h1:/41uw9wKzuelhN+u+/C59ixxf4tYQKW7p32ddkYNe2k=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
k8s.io/kubernetes v1.33.2 h1:Vk3hsCaazyMQ6CXhu029AEPlBoYsEnD8oEIC0bP2pWQ=
k8s.io/kubernetes v1.33.2/go.mod h1:nrt8sldmckKz2fCZhgRX3SKfS2e+CzXATPv6ITNkU00=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
0707010000002C000081A400000000000000000000000168B0FB3F00000EDF000000000000000000000000000000000000001B00000000rancher-cli-2.12.1/main.gopackage main

import (
	"errors"
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/rancher/cli/cmd"
	"github.com/rancher/cli/config"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

var VERSION = "dev"

var AppHelpTemplate = `{{.Usage}}

Usage: {{.Name}} {{if .Flags}}[OPTIONS] {{end}}COMMAND [arg...]

Version: {{.Version}}
{{if .Flags}}
Options:
  {{range .Flags}}{{if .Hidden}}{{else}}{{.}}
  {{end}}{{end}}{{end}}
Commands:
  {{range .Commands}}{{.Name}}{{with .Aliases}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
  {{end}}
Run '{{.Name}} COMMAND --help' for more information on a command.
`

var CommandHelpTemplate = `{{.Usage}}
{{if .Description}}{{.Description}}{{end}}
Usage: 
	{{.HelpName}} {{if .Flags}}[OPTIONS] {{end}}{{if ne "None" .ArgsUsage}}{{if ne "" .ArgsUsage}}{{.ArgsUsage}}{{else}}[arg...]{{end}}{{end}}

{{if .Flags}}Options:{{range .Flags}}
	 {{.}}{{end}}{{end}}
`

var SubcommandHelpTemplate = `{{.Usage}}
{{if .Description}}{{.Description}}{{end}}
Usage:
   {{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}

Commands:{{range .VisibleCategories}}{{if .Name}}
   {{.Name}}:{{end}}{{range .VisibleCommands}}
     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
{{end}}{{if .VisibleFlags}}
Options:
   {{range .VisibleFlags}}{{.}}
   {{end}}{{end}}
`

func main() {
	if err := mainErr(); err != nil {
		logrus.Fatal(err)
	}
}

func mainErr() error {
	cli.AppHelpTemplate = AppHelpTemplate
	cli.CommandHelpTemplate = CommandHelpTemplate
	cli.SubcommandHelpTemplate = SubcommandHelpTemplate

	app := cli.NewApp()
	app.Name = "rancher"
	app.Usage = "Rancher CLI, managing containers one UTF-8 character at a time"
	app.Before = func(ctx *cli.Context) error {
		if ctx.GlobalBool("debug") {
			logrus.SetLevel(logrus.DebugLevel)
		}

		path := cmd.GetConfigPath(ctx)
		warnings, err := config.GetFilePermissionWarnings(path)
		if err != nil {
			// We don't want to block the execution of the CLI in that case
			logrus.Errorf("Unable to verify config file permission: %s. Continuing.", err)
		}
		for _, warning := range warnings {
			logrus.Warning(warning)
		}

		return nil
	}
	app.Version = VERSION
	app.Author = "Rancher Labs, Inc."
	app.Email = ""

	configDir, err := cmd.ConfigDir()
	if err != nil {
		return err
	}

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "debug",
			Usage: "Debug logging",
		},
		cli.StringFlag{
			Name:   "config, c",
			Usage:  "Path to rancher config",
			EnvVar: "RANCHER_CONFIG_DIR",
			Value:  configDir,
		},
	}
	app.Commands = []cli.Command{
		cmd.ClusterCommand(),
		cmd.ContextCommand(),
		cmd.InspectCommand(),
		cmd.KubectlCommand(),
		cmd.LoginCommand(),
		cmd.MachineCommand(),
		cmd.NamespaceCommand(),
		cmd.NodeCommand(),
		cmd.ProjectCommand(),
		cmd.PsCommand(),
		cmd.ServerCommand(),
		cmd.SettingsCommand(),
		cmd.SSHCommand(),
		cmd.UpCommand(),
		cmd.WaitCommand(),
		cmd.CredentialCommand(),
	}

	parsed, err := parseArgs(os.Args)
	if err != nil {
		logrus.Error(err)
		os.Exit(1)
	}

	return app.Run(parsed)
}

var singleAlphaLetterRegxp = regexp.MustCompile("[a-zA-Z]")

func parseArgs(args []string) ([]string, error) {
	result := []string{}
	for _, arg := range args {
		if strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") && len(arg) > 1 {
			for i, c := range arg[1:] {
				if string(c) == "=" {
					if i < 1 {
						return nil, errors.New("invalid input with '-' and '=' flag")
					}
					result[len(result)-1] = result[len(result)-1] + arg[i+1:]
					break
				} else if singleAlphaLetterRegxp.MatchString(string(c)) {
					result = append(result, "-"+string(c))
				} else {
					return nil, fmt.Errorf("invalid input %s in flag", string(c))
				}
			}
		} else {
			result = append(result, arg)
		}
	}
	return result, nil
}
0707010000002D000081A400000000000000000000000168B0FB3F000003BE000000000000000000000000000000000000002000000000rancher-cli-2.12.1/main_test.gopackage main

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestParseArgs(t *testing.T) {
	t.Parallel()

	tests := []struct {
		input     string
		want      []string
		shouldErr bool
	}{
		{
			input: "-itd",
			want:  []string{"-i", "-t", "-d"},
		},
		{
			input: "-itf=b",
			want:  []string{"-i", "-t", "-f=b"},
		},
		{
			input:     "-itd#",
			shouldErr: true,
		},
		{
			input: "-f=b",
			want:  []string{"-f=b"},
		},
		{
			input:     "-=b",
			shouldErr: true,
		},
		{
			input: "-",
			want:  []string{"-"},
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.input, func(t *testing.T) {
			t.Parallel()

			got, err := parseArgs([]string{"rancher", "run", "--debug", test.input})
			if test.shouldErr {
				require.Error(t, err)
				return
			}

			require.NoError(t, err)
			assert.Equal(t, append([]string{"rancher", "run", "--debug"}, test.want...), got)
		})
	}
}
0707010000002E000041ED00000000000000000000000268B0FB3F00000000000000000000000000000000000000000000001B00000000rancher-cli-2.12.1/package0707010000002F000081A400000000000000000000000168B0FB3F00000214000000000000000000000000000000000000002600000000rancher-cli-2.12.1/package/DockerfileFROM registry.suse.com/bci/bci-base:15.7

ARG user=cli

RUN zypper -n update && \
    zypper -n install ca-certificates openssh-clients && \
    zypper clean -a && rm -rf /tmp/* /var/tmp/* /usr/share/doc/packages/* /usr/share/doc/manual/* /var/log/*

RUN echo "$user:x:1000:1000::/home/$user:/bin/bash" >> /etc/passwd && \
    echo "$user:x:1000:" >> /etc/group && \
    mkdir /home/$user && \
    chown -R $user:$user /home/$user

COPY rancher /usr/bin/

WORKDIR /home/$user

USER 1000:1000

ENTRYPOINT ["rancher"]
CMD  ["--help"]
07070100000030000041ED00000000000000000000000268B0FB3F00000000000000000000000000000000000000000000001B00000000rancher-cli-2.12.1/scripts07070100000031000081ED00000000000000000000000168B0FB3F00000379000000000000000000000000000000000000002100000000rancher-cli-2.12.1/scripts/build#!/bin/bash -e

source $(dirname $0)/version

cd $(dirname $0)/..

declare -A OS_ARCH_ARG

OS_PLATFORM_ARG=(linux windows darwin)
OS_ARCH_ARG[linux]="amd64 arm arm64 s390x"
OS_ARCH_ARG[windows]="386 amd64"
OS_ARCH_ARG[darwin]="amd64 arm64"

CGO_ENABLED=0 go build -ldflags="-w -s -X main.VERSION=$VERSION -extldflags -static" -o bin/rancher

if [ -n "$CROSS" ]; then
    rm -rf build/bin
    mkdir -p build/bin
    for OS in ${OS_PLATFORM_ARG[@]}; do
        for ARCH in ${OS_ARCH_ARG[${OS}]}; do
            OUTPUT_BIN="build/bin/rancher_$OS-$ARCH"
            if test "$OS" = "windows"; then
                OUTPUT_BIN="${OUTPUT_BIN}.exe"
            fi
            echo "Building binary for $OS/$ARCH..."
            GOARCH=$ARCH GOOS=$OS CGO_ENABLED=0 go build \
                  -ldflags="-w -X main.VERSION=$VERSION" \
                  -o ${OUTPUT_BIN} ./
        done
    done
fi
07070100000032000081ED00000000000000000000000168B0FB3F00000051000000000000000000000000000000000000001E00000000rancher-cli-2.12.1/scripts/ci#!/bin/bash
set -e

cd $(dirname $0)

./build
./test
./lint
./validate
./package
07070100000033000081ED00000000000000000000000168B0FB3F00000057000000000000000000000000000000000000002000000000rancher-cli-2.12.1/scripts/lint#!/bin/bash
set -e

cd $(dirname $0)/..

echo Running: golangci-lint
golangci-lint run
07070100000034000081ED00000000000000000000000168B0FB3F00000777000000000000000000000000000000000000002300000000rancher-cli-2.12.1/scripts/package#!/bin/bash
set -e

source $(dirname $0)/version

cd $(dirname $0)/..

DIST=$(pwd)/dist/artifacts

mkdir -p $DIST/${VERSION} $DIST/latest

for i in build/bin/*; do
    if [ ! -e $i ]; then
        continue
    fi

    BASE=build/archive
    DIR=${BASE}/rancher-${VERSION}

    rm -rf $BASE
    mkdir -p $BASE $DIR

    EXT=
    if [[ $i =~ .*windows.* ]]; then
        EXT=.exe
    fi

    cp $i ${DIR}/rancher${EXT}

    arch=$(echo $i | cut -f2 -d_)
    mkdir -p $DIST/${VERSION}/binaries/$arch
    mkdir -p $DIST/latest/binaries/$arch
    cp $i $DIST/${VERSION}/binaries/$arch/rancher${EXT}
    if [ -z "${EXT}" ]; then
        gzip -c $i > $DIST/${VERSION}/binaries/$arch/rancher.gz
        xz -c $i > $DIST/${VERSION}/binaries/$arch/rancher.xz
    fi

    rm -rf $DIST/latest/binaries/$arch
    mkdir -p $DIST/latest/binaries
    cp -rf $DIST/${VERSION}/binaries/$arch $DIST/latest/binaries

    (
        cd $BASE
        NAME=$(basename $i | sed 's/_/-/g')
        if [ -z "$EXT" ]; then
            tar cvzf $DIST/${VERSION}/${NAME}-${VERSION}.tar.gz .
            cp $DIST/${VERSION}/${NAME}-${VERSION}.tar.gz $DIST/latest/${NAME}.tar.gz

            tar cvJf $DIST/${VERSION}/${NAME}-${VERSION}.tar.xz .
            cp $DIST/${VERSION}/${NAME}-${VERSION}.tar.xz $DIST/latest/${NAME}.tar.xz
        else
            NAME=$(echo $NAME | sed 's/'${EXT}'//g')
            zip -r $DIST/${VERSION}/${NAME}-${VERSION}.zip *
            cp $DIST/${VERSION}/${NAME}-${VERSION}.zip $DIST/latest/${NAME}.zip
        fi
    )
done


ARCH=${ARCH:-"amd64"}
SUFFIX=""
[ "${ARCH}" != "amd64" ] && SUFFIX="_${ARCH}"

cd package

TAG=${TAG:-${VERSION}${SUFFIX}}
REPO=${REPO:-rancher}

if echo $TAG | grep -q dirty; then
    TAG=dev
fi

if [ -n "$GITHUB_TAG" ]; then
    TAG=$GITHUB_TAG
fi

cp ../bin/rancher .
docker build -t ${REPO}/cli:${TAG} .

echo ${REPO}/cli:${TAG} > ../dist/images
echo Built ${REPO}/cli:${TAG}
07070100000035000081ED00000000000000000000000168B0FB3F00000023000000000000000000000000000000000000002300000000rancher-cli-2.12.1/scripts/release#!/bin/bash

exec $(dirname $0)/ci
07070100000036000081ED00000000000000000000000168B0FB3F00000063000000000000000000000000000000000000002000000000rancher-cli-2.12.1/scripts/test#!/bin/bash
set -e

cd $(dirname $0)/..

echo Running tests

go test -race -cover -tags=test ./...
07070100000037000081ED00000000000000000000000168B0FB3F000000E1000000000000000000000000000000000000002400000000rancher-cli-2.12.1/scripts/validate#!/bin/bash
set -e

cd $(dirname $0)/..

echo Tidying up modules
go mod tidy

echo Verifying modules
go mod verify

if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
  echo "Encountered dirty repo!"
  exit 1
fi07070100000038000081ED00000000000000000000000168B0FB3F00000135000000000000000000000000000000000000002300000000rancher-cli-2.12.1/scripts/version#!/bin/bash

if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
    DIRTY="-dirty"
fi

COMMIT=$(git rev-parse --short HEAD)
GIT_TAG=${GITHUB_TAG:-$(git tag -l --contains HEAD | head -n 1)}

if [[ -z "$DIRTY" && -n "$GIT_TAG" ]]; then
    VERSION=$GIT_TAG
else
    VERSION="${COMMIT}${DIRTY}"
fi
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!424 blocks
openSUSE Build Service is sponsored by