Proposal for new kubecfg design (kubectl)

***UPDATE: This proposal was reworked many times throughout this PR. I am keeping this parent message the same for historical purposes, but you can find the closest description of the final design in [this comment](https://github.com/GoogleCloudPlatform/kubernetes/pull/1325#issuecomment-57416174) (with the exception that `inspect` became `describe`).***

### Intent

The intent of this pull request is to propose a design for the present and future of kubecfg. The conversation from #1167 was the primary input into this design. The idea is to have kubecfg solve two use cases:

1. Provide a great user experience to view the current status of a Kubernetes cluster and to execute simple commands on the cluster.
1. Provide a tool which will accept configuration with generic resources to submit changes to and diff against a Kubernetes cluster.

The problem with the current design is that it is too generic to effectively support (1). The proposed redesign enables custom commands per resource (e.g. kubecfg pods, kubecfg services, etc.) but generic subcommands to satisfy (2) (kubecfg submit, kubecfg diff, kubecfg reconcile, etc.).

### Proposed set of subcommands

*Only a subset of these have been implemented in the attached commit.*

* `kubecfg2` - Parent command.
  * `pods`
    * `list [<id>]` - List pods in a column-oriented view (see [Examples](#examples) below). By default, the first argument passed in acts as a filter by ID.
    * `get <id>` - List info about a pod in a row-oriented view. Designed to show more metadata about a given pod, and over time can be enhanced to make navigating the cluster from a given pod very easy. Currently I'm adding a list of any replication controllers whose selectors match (and therefore who control) the selected pod.
    * `delete <id>` - Delete a pod.
    * `create [-f <filename>]` - In the future we can accept directories or even data from stdin.
  * `rc` - For replication controllers. Happy to use a different abbreviation but replicationControllers seemed too long.
    * `list [<id>]`
    * `get <id>` - RC's `get` command could become extremely rich. For now I've added Pods Status which queries for pods under this replication controller and shows counts for running, waiting and terminated, but in the future you could do diffs against the controller's PodTemplate and show counts for matching and non-matching pods. You could also show the first 3-4 pod ID's per category and then show &lt;X more ...&gt;, making debugging even easier.
    * `create [-f <filename>]`
    * `update [-f <filename>]`
    * `delete <id>` - I have removed the delete/stop/rm replication controller confusion in favor of just `delete` and `resize`, where delete takes an optional --force flag if replicas > 0.
    * `resize <id> <replicas>`
    * *Removed* `rollingupdate <controller> <image> <time>` - I've included this here for parity but I would like to propose taking it out. This is the one command that I don't think belongs in kubecfg ... it's too long-running, fraught with edge cases, and in reality would probably want to take more into consideration (like fluctuations in metrics, % of pods that come up successfully, etc.). I think this level of functionality belongs in an independent client that uses the API that can look at more components in the infrastructure than just kubernetes.
  * `minions`
    * `list [<id>]`
    * `get <id>`
  * `services`
    * `list [<id>]`
    * `get <id>`
    * `create [-f <filename>]`
    * `update [-f <filename>]`
    * `delete <id>`
    * *Postponed* `resolve <id>` - Just an idea, but it could give you a random IP/port combination to connect to. May be useful for quickly locating a node to test for a given service.
  * *Postponed* `submit [<filename>...]` - This command exists to satisfy the second use case above. Reconcile and submit any changes from a given set of config(s), from files or from stdin. If you had an entire directory tree of files that had configs that represented your cluster state, you could use this command to submit them all to either create or update your cluster.
  * *Postponed* `diff [<filename>...]` - A dry-run version of `submit`.
  * `run [-p <port spec>] <image> <replicas> <controller>` - Same as today.

### Examples

Here is what the current commit produces:

*These examples are out of date - see [this comment](https://github.com/GoogleCloudPlatform/kubernetes/pull/1325#issuecomment-56786115) for the latest.*

```
$ kubecfg2
kubecfg2 controls the Kubernetes cluster manager.

Find more information at https://github.com/GoogleCloudPlatform/kubernetes.

Usage:
  kubecfg2 [command]

Available Commands:
  version                   Print version of client and server
  pods                      List, inspect and modify pods
  rc                        List, inspect and modify replication controllers
  help [command]            Help about any command

 Available Flags:
  -a, --auth="/Users/sam/.kubernetes_auth": Path to the auth info file. If missing, prompt the user. Only used if doing https.
      --help=false: help for kubecfg2
  -h, --host="": Kubernetes apiserver to connect to
  -m, --match-version=false: Require server version to match client version

Use "kubecfg2 help [command]" for more information about that command.

```
```
$ kubecfg2 help pods list
List one or more pods. Pass in an ID to filter.

Usage:
  kubecfg2 pods list [<id>] [flags]

 Available Flags:
  -a, --auth="/Users/sam/.kubernetes_auth": Path to the auth info file. If missing, prompt the user. Only used if doing https.
      --help=false: help for list
  -h, --host="": Kubernetes apiserver to connect to
  -m, --match-version=false: Require server version to match client version
  -l, --selector="": Selector (label query) to filter on


Use "kubecfg2 help [command]" for more information about that command.

```
```
$ kubecfg2 pods list
ID                                      IMAGE(S)                HOST            LABELS                                  STATUS
4893ea09-3d0d-11e4-b3ad-0800279696e1    dockerfile/nginx        127.0.0.1/      replicationController=MyNginxController Running
48943371-3d0d-11e4-b3ad-0800279696e1    dockerfile/nginx        127.0.0.1/      replicationController=MyNginxController Running
```
```
$ kubecfg2 pods list 4893ea09-3d0d-11e4-b3ad-0800279696e1
ID                                      IMAGE(S)                HOST            LABELS                                  STATUS
4893ea09-3d0d-11e4-b3ad-0800279696e1    dockerfile/nginx        127.0.0.1/      replicationController=MyNginxController Running
```
```
$ kubecfg2 pods get 4893ea09-3d0d-11e4-b3ad-0800279696e1
ID:                             4893ea09-3d0d-11e4-b3ad-0800279696e1
Image(s):                       dockerfile/nginx
Host:                           127.0.0.1/
Labels:                         replicationController=MyNginxController
Status:                         Running
Replication Controllers:        MyNginxController (2/2 replicas created)
```
```
$ kubecfg2 rc list
ID                      IMAGE(S)                SELECTOR                                REPLICAS
MyNginxController       dockerfile/nginx        replicationController=MyNginxController 2
```
```
$ kubecfg2 rc get MyNginxController
ID:             MyNginxController
Image(s):       dockerfile/nginx
Selector:       replicationController=MyNginxController
Labels:         <none>
Replicas:       2 current / 2 desired
Pods Status:    2 running / 0 waiting / 0 terminated
```

### Other notes

* I am currently using [spf13/cobra](https://github.com/spf13/cobra) but am also considering [codegangsta/cli](https://github.com/codegangsta/cli). The latter may allow us to write out the command definitions in a cleaner way and support bash autocompletion, but the former allows persistent flags. Open to using either one.
* Some of these commands (especially `get` commands that show a lot of info) may result in lots of queries to the server. There are several ways to handle this: (1) don't consider it an issue, (2) offer a "succinct mode" that tries to do as little as possible and (3) over time backport functionality that is particularly useful directly into apiserver as new API endpoints. Especially with (3), you could consider kubecfg a proving ground for new convenience functionality to see whether it's worth implementing in apiserver directly.

### File layout

* cmd/kubecfg2/kubecfg2.go - All cobra and CLI-related code remains in this file/package.
* pkg/kubecfg2/&lt;subcommand&gt;.go - All logic per subcommand lives in one file per subcommand.
* pkg/kubecfg2/kubecfg2.go - Common functions used by cmd/kubecfg2 and pkg/kubecfg2 packages.

### Open Questions
* Can we leave rollingupdate out of this version for the merge? If it's needed for demo purposes, can it be reimplemented somewhere outside of this tool?
* @erictune pointed out that `get` is not the best name for above. `info` and `details` are other options but ideas here are welcome.

Okay, I will add the todo.

On Thu, Oct 9, 2014 at 8:43 PM, Clayton Coleman <notifications@github.com>
wrote:

> In pkg/kubecfg/cmd/get.go:
>
> > +See the License for the specific language governing permissions and
> > +limitations under the License.
> > +*/
> > +
> > +package cmd
> > +
> > +import (
> > +	"io"
> > +
> > +	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg"
> > +	"github.com/spf13/cobra"
> > +)
> > +
> > +func NewCmdGet(out io.Writer) *cobra.Command {
> > +	cmd := &cobra.Command{
> > +		Use:   "get [[-f|--format=]table|json|yaml|template] [-t <file>|--template=<file>] <resource> [<id>]",
>
>  On Oct 9, 2014, at 7:44 PM, Sam Ghods <notifications@github.com> wrote:
> In pkg/kubecfg/cmd/get.go: > +See the License for the specific language
> governing permissions and > +limitations under the License. > +*/ > + >
> +package cmd > + > +import ( > + "io" > + > + "
> github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg" > + "
> github.com/spf13/cobra" > +) > + > +func NewCmdGet(out io.Writer)
> *cobra.Command { > + cmd := &cobra.Command{ > + Use: "get
> [[-f|--format=]table|json|yaml|template] [-t <file>|--template=<file>]
> <resource> [<id>]", Got it, cool. That makes sense. In that case though,
> does it make sense to just override the purpose of --api-version? In your
> example, when I write up my script, I would just lock --api-version to
> v1beta1. If v1beta1 is no longer supported by the server, then the client
> would get v1beta3 from the server, but then automatically convert the
> objects back to v1beta1.
> I don't know that we should have the client pick a version for the user in
> all cases, and having only one flag locks us into that. The format version
> is very specific and would exist to ensure clients get predictable results
> or an error if they can't be satisfied. It's likely used only in
> automation, but in the future we may also vary client behavior based on api
> version, so combining them does seem wrong. We can always add this in the
> future, it's just this is a fairly common request from system admins to
> have very predictable behavior when generating output.
> … <#148f827d4e19ae48_>
>  (Since the objects are back-converted, there should be no perceivable
> difference to the user between v1beta3->v1beta1 objects or v1beta1
> objects.) Is there a strong need for having them as two separate
> parameters? I worry it will be confusing to explain the two different API
> lock flags to users. — Reply to this email directly or view it on GitHub.
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/GoogleCloudPlatform/kubernetes/pull/1325/files#r18687465>
> .
>



-- 
Sam