Merge pull request #34502 from fabianofranz/cli_usability_improvements

Automatic merge from submit-queue

Improvements to CLI usability and maintainability

Improves `kubectl` from an usability perspective by

1. Fixing how we handle terminal width in help. Some sections like the flags use the entire available width, while others like long descriptions breaks lines but don't follow a well established max width (screenshot below). This PR adds a new responsive writer that will adjust to terminal width and set 80, 100, or 120 columns as the max width, but not more than that given POSIX best practices and recommendations for better readability.
![terminal_width](https://cloud.githubusercontent.com/assets/158611/19253184/b23a983e-8f1f-11e6-9bae-667dd5981485.png)
2. Adds our own normalizers for long descriptions and cmd examples which allows us better control about how things like lists, paragraphs, line breaks, etc are printed. Features markdown support. Looks like `templates.LongDesc` and `templates.Examples` instead of `dedent.Dedend`.
3. Allows simple reordering and reuse of help and usage sections.
3. Adds `verify-cli-conventions.sh` which intends to run tests to make sure cmd developers are using what we propose as [kubectl conventions](https://github.com/kubernetes/kubernetes/blob/master/docs/devel/kubectl-conventions.md). Just a couple simple tests for now but the framework is there and it's easy to extend.
4. Update [kubectl conventions](https://github.com/kubernetes/kubernetes/blob/master/docs/devel/kubectl-conventions.md) to use our own normalizers instead of `dedent.Dedent`.

**Release note**:
<!--  Steps to write your release note:
1. Use the release-note-* labels to set the release note state (if you have access) 
2. Enter your extended release note in the below block; leaving it blank means using the PR title as the release note. If no release note is required, just write `NONE`. 
-->
```release-note
Improves how 'kubectl' uses the terminal size when printing help and usage.
```

@kubernetes/kubectl
This commit is contained in:
Kubernetes Submit Queue
2016-10-17 23:41:23 -07:00
committed by GitHub
78 changed files with 1654 additions and 599 deletions

8
Godeps/Godeps.json generated
View File

@@ -65,6 +65,10 @@
"Comment": "v7.0.6-4-g2492d97",
"Rev": "2492d97b402e00797833c03ac5fa1c572c7bb29a"
},
{
"ImportPath": "github.com/MakeNowJust/heredoc",
"Rev": "1d91351acdc1cb2f2c995864674b754134b86ca7"
},
{
"ImportPath": "github.com/Microsoft/go-winio",
"Comment": "v0.1.0",
@@ -1537,6 +1541,10 @@
"Comment": "v2.1.1-5-g1b4ae6f",
"Rev": "1b4ae6fb4e77b095934d4430860ff202060169f8"
},
{
"ImportPath": "github.com/mitchellh/go-wordwrap",
"Rev": "ad45545899c7b13c020ea92b2072220eefad42b8"
},
{
"ImportPath": "github.com/mitchellh/mapstructure",
"Rev": "740c764bc6149d3f1806231418adb9f52c11bcbf"

58
Godeps/LICENSES generated
View File

@@ -51179,6 +51179,35 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
================================================================================
================================================================================
= vendor/github.com/MakeNowJust/heredoc licensed under: =
The MIT License (MIT)
Copyright (c) 2014 TSUYUSATO Kitsune
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
= vendor/github.com/MakeNowJust/heredoc/LICENSE 15e1c8f1d3c204c05f71630afacbc92b -
================================================================================
================================================================================
= vendor/github.com/matttproud/golang_protobuf_extensions/pbutil licensed under: =
@@ -52706,6 +52735,35 @@ Apache License
================================================================================
================================================================================
= vendor/github.com/mitchellh/go-wordwrap licensed under: =
The MIT License (MIT)
Copyright (c) 2014 Mitchell Hashimoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
= vendor/github.com/mitchellh/go-wordwrap/LICENSE.md 56da355a12d4821cda57b8f23ec34bc4 -
================================================================================
================================================================================
= vendor/github.com/mitchellh/mapstructure licensed under: =

View File

@@ -0,0 +1,48 @@
/*
Copyright 2016 The Kubernetes Authors.
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
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.
*/
package main
import (
"fmt"
"io/ioutil"
"os"
"k8s.io/kubernetes/pkg/kubectl/cmd"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
cmdsanity "k8s.io/kubernetes/pkg/kubectl/cmd/util/sanity"
)
var (
skip = []string{}
)
func main() {
errors := []error{}
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
result := cmdsanity.CheckCmdTree(kubectl, cmdsanity.AllCmdChecks, []string{})
errors = append(errors, result...)
if len(errors) > 0 {
for i, err := range errors {
fmt.Fprintf(os.Stderr, "%d. %s\n\n", i+1, err)
}
os.Exit(1)
}
fmt.Fprintln(os.Stdout, "Congrats, CLI looks good!")
}

View File

@@ -301,17 +301,13 @@ Sample command skeleton:
// MineRecommendedName is the recommended command name for kubectl mine.
const MineRecommendedName = "mine"
// MineConfig contains all the options for running the mine cli command.
type MineConfig struct {
mineLatest bool
}
// Long command description and examples.
var (
mineLong = dedent.Dedent(`
mineLong = templates.LongDesc(`
mine which is described here
with lots of details.`)
mineExample = dedent.Dedent(`
mineExample = templates.Examples(`
# Run my command's first action
kubectl mine first_action
@@ -319,6 +315,11 @@ var (
kubectl mine second_action --flag`)
)
// MineConfig contains all the options for running the mine cli command.
type MineConfig struct {
mineLatest bool
}
// NewCmdMine implements the kubectl mine command.
func NewCmdMine(parent, name string, f *cmdutil.Factory, out io.Writer) *cobra.Command {
opts := &MineConfig{}

View File

@@ -1,6 +1,7 @@
cluster/addons/fluentd-elasticsearch/es-image
cluster/images/etcd/attachlease
cluster/images/etcd/rollback
cmd/clicheck
cmd/gendocs
cmd/genkubedocs
cmd/genman

40
hack/verify-cli-conventions.sh Executable file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
# Copyright 2016 The Kubernetes Authors.
#
# 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
#
# 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.
set -o errexit
set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
source "${KUBE_ROOT}/hack/lib/init.sh"
kube::golang::setup_env
BINS=(
cmd/clicheck
)
make -C "${KUBE_ROOT}" WHAT="${BINS[*]}"
clicheck=$(kube::util::find-binary "clicheck")
if ! output=`$clicheck 2>&1`
then
echo "FAILURE: CLI is not following one or more required conventions:"
echo "$output"
exit 1
else
echo "SUCCESS: CLI is following all tested conventions."
fi

View File

@@ -23,11 +23,11 @@ import (
"io"
"github.com/golang/glog"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -59,17 +59,17 @@ type AnnotateOptions struct {
}
var (
annotate_long = dedent.Dedent(`
annotate_long = templates.LongDesc(`
Update the annotations on one or more resources.
An annotation is a key/value pair that can hold larger (compared to a label), and possibly not human-readable, data.
It is intended to store non-identifying auxiliary data, especially data manipulated by tools and system extensions.
If --overwrite is true, then existing annotations can be overwritten, otherwise attempting to overwrite an annotation will result in an error.
If --resource-version is specified, then updates will use this resource version, otherwise the existing resource-version will be used.
* An annotation is a key/value pair that can hold larger (compared to a label), and possibly not human-readable, data.
* It is intended to store non-identifying auxiliary data, especially data manipulated by tools and system extensions.
* If --overwrite is true, then existing annotations can be overwritten, otherwise attempting to overwrite an annotation will result in an error.
* If --resource-version is specified, then updates will use this resource version, otherwise the existing resource-version will be used.
`) + valid_resources
` + valid_resources)
annotate_example = dedent.Dedent(`
annotate_example = templates.Examples(`
# Update pod 'foo' with the annotation 'description' and the value 'my frontend'.
# If the same annotation is set multiple times, only the last value will be applied
kubectl annotate pods foo description='my frontend'

View File

@@ -22,7 +22,6 @@ import (
"time"
"github.com/jonboulle/clockwork"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
@@ -32,6 +31,7 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/labels"
@@ -58,7 +58,7 @@ const (
)
var (
apply_long = dedent.Dedent(`
apply_long = templates.LongDesc(`
Apply a configuration to a resource by filename or stdin.
This resource will be created if it doesn't exist yet.
To use 'apply', always create the resource initially with either 'apply' or 'create --save-config'.
@@ -67,7 +67,7 @@ var (
Alpha Disclaimer: the --prune functionality is not yet complete. Do not use unless you are aware of what the current state is. See https://issues.k8s.io/34274.`)
apply_example = dedent.Dedent(`
apply_example = templates.Examples(`
# Apply the configuration in pod.json to a pod.
kubectl apply -f ./pod.json

View File

@@ -22,13 +22,13 @@ import (
"net/url"
"github.com/golang/glog"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/unversioned/remotecommand"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
remotecommandserver "k8s.io/kubernetes/pkg/kubelet/server/remotecommand"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
@@ -36,7 +36,7 @@ import (
)
var (
attach_example = dedent.Dedent(`
attach_example = templates.Examples(`
# Get output from running pod 123456-7890, using the first container by default
kubectl attach 123456-7890

View File

@@ -20,9 +20,8 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
@@ -31,13 +30,13 @@ import (
)
var (
autoscaleLong = dedent.Dedent(`
autoscaleLong = templates.LongDesc(`
Creates an autoscaler that automatically chooses and sets the number of pods that run in a kubernetes cluster.
Looks up a Deployment, ReplicaSet, or ReplicationController by name and creates an autoscaler that uses the given resource as a reference.
An autoscaler can automatically increase or decrease number of pods deployed within the system as needed.`)
autoscaleExample = dedent.Dedent(`
autoscaleExample = templates.Examples(`
# Auto scale a deployment "foo", with the number of pods between 2 and 10, target CPU utilization specified so a default autoscaling policy will be used:
kubectl autoscale deployment foo --min=2 --max=10

View File

@@ -23,6 +23,7 @@ import (
"strconv"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
@@ -30,8 +31,9 @@ import (
"github.com/spf13/cobra"
)
var longDescr = `Display addresses of the master and services with label kubernetes.io/cluster-service=true
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.`
var longDescr = templates.LongDesc(`
Display addresses of the master and services with label kubernetes.io/cluster-service=true
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.`)
func NewCmdClusterInfo(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{

View File

@@ -26,6 +26,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
@@ -46,18 +47,18 @@ func NewCmdClusterInfoDump(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
return cmd
}
const (
dumpLong = `
var (
dumpLong = templates.LongDesc(`
Dumps cluster info out suitable for debugging and diagnosing cluster problems. By default, dumps everything to
stdout. You can optionally specify a directory with --output-directory. If you specify a directory, kubernetes will
build a set of files in that directory. By default only dumps things in the 'kube-system' namespace, but you can
switch to a different namespace with the --namespaces flag, or specify --all-namespaces to dump all namespaces.
The command also dumps the logs of all of the pods in the cluster, these logs are dumped into different directories
based on namespace and pod name.
`
based on namespace and pod name.`)
dumpExample = `# Dump current cluster state to stdout
dumpExample = templates.Examples(`
# Dump current cluster state to stdout
kubectl cluster-info dump
# Dump current cluster state to /path/to/cluster-state
@@ -67,7 +68,7 @@ kubectl cluster-info dump --output-directory=/path/to/cluster-state
kubectl cluster-info dump --all-namespaces
# Dump a set of namespaces to /path/to/cluster-state
kubectl cluster-info dump --namespaces default,kube-system --output-directory=/path/to/cluster-state`
kubectl cluster-info dump --namespaces default,kube-system --output-directory=/path/to/cluster-state`)
)
func setupOutputWriter(cmd *cobra.Command, defaultWriter io.Writer, filename string) io.Writer {

View File

@@ -169,6 +169,7 @@ __custom_func() {
// and add a short forms entry in expandResourceShortcut() when appropriate.
// TODO: This should be populated using the discovery information from apiserver.
valid_resources = `Valid resource types include:
* clusters (valid only for federation apiservers)
* componentstatuses (aka 'cs')
* configmaps (aka 'cm')
@@ -194,33 +195,6 @@ __custom_func() {
* serviceaccounts (aka 'sa')
* services (aka 'svc')
`
usage_template = `{{if gt .Aliases 0}}
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}
Available Sub-commands:{{range .Commands}}{{if .IsAvailableCommand}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}
Usage:{{if .Runnable}}
{{if .HasFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{ if .HasSubCommands }}
{{ .CommandPath}} [command]
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
help_template = `{{with or .Long .Short }}{{. | trim}}{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
)
// NewKubectlCommand creates the `kubectl` command and its nested children.
@@ -229,9 +203,10 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
cmds := &cobra.Command{
Use: "kubectl",
Short: "kubectl controls the Kubernetes cluster manager",
Long: `kubectl controls the Kubernetes cluster manager.
Long: templates.LongDesc(`
kubectl controls the Kubernetes cluster manager.
Find more information at https://github.com/kubernetes/kubernetes.`,
Find more information at https://github.com/kubernetes/kubernetes.`),
Run: runHelp,
BashCompletionFunction: bash_completion_func,
}

View File

@@ -20,21 +20,19 @@ import (
"bytes"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
completion_long = dedent.Dedent(`
completion_long = templates.LongDesc(`
Output shell completion code for the given shell (bash or zsh).
This command prints shell code which must be evaluation to provide interactive
completion of kubectl commands.
`)
completion_example = dedent.Dedent(`
$ source <(kubectl completion bash)
will load the kubectl completion code for bash. Note that this depends on the
@@ -45,11 +43,11 @@ var (
$ source $(brew --prefix)/etc/bash_completion
$ source <(kubectl completion bash)
If you use zsh*, the following will load kubectl zsh completion:
If you use zsh[1], the following will load kubectl zsh completion:
$ source <(kubectl completion zsh)
* zsh completions are only supported in versions of zsh >= 5.2`)
[1] zsh completions are only supported in versions of zsh >= 5.2`)
)
var (
@@ -69,7 +67,6 @@ func NewCmdCompletion(f cmdutil.Factory, out io.Writer) *cobra.Command {
Use: "completion SHELL",
Short: "Output shell completion code for the given shell (bash or zsh)",
Long: completion_long,
Example: completion_example,
Run: func(cmd *cobra.Command, args []string) {
err := RunCompletion(f, out, cmd, args)
cmdutil.CheckErr(err)

View File

@@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
)
// NewCmdConfig creates a command object for the "config" action, and adds all child commands to it.
@@ -35,13 +36,14 @@ func NewCmdConfig(pathOptions *clientcmd.PathOptions, out io.Writer) *cobra.Comm
cmd := &cobra.Command{
Use: "config SUBCOMMAND",
Short: "Modify kubeconfig files",
Long: `Modify kubeconfig files using subcommands like "kubectl config set current-context my-context"
Long: templates.LongDesc(`
Modify kubeconfig files using subcommands like "kubectl config set current-context my-context"
The loading order follows these rules:
1. If the --` + pathOptions.ExplicitFileFlag + ` flag is set, then only that file is loaded. The flag may only be set once and no merging takes place.
2. If $` + pathOptions.EnvVar + ` environment variable is set, then it is used a list of paths (normal path delimitting rules for your system). These paths are merged. When a value is modified, it is modified in the file that defines the stanza. When a value is created, it is created in the first file that exists. If no files in the chain exist, then it creates the last file in the list.
3. Otherwise, ` + path.Join("${HOME}", pathOptions.GlobalFileSubpath) + ` is used and no merging takes place.
`,
3. Otherwise, ` + path.Join("${HOME}", pathOptions.GlobalFileSubpath) + ` is used and no merging takes place.`),
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},

View File

@@ -24,11 +24,11 @@ import (
"path/filepath"
"strings"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/flag"
)
@@ -55,8 +55,9 @@ const (
)
var (
create_authinfo_long = fmt.Sprintf(`
create_authinfo_long = fmt.Sprintf(templates.LongDesc(`
Sets a user entry in kubeconfig
Specifying a name that already exists will merge new fields on top of existing values.
Client-certificate flags:
@@ -68,10 +69,9 @@ Specifying a name that already exists will merge new fields on top of existing v
Basic auth flags:
--%v=basic_user --%v=basic_password
Bearer token and basic auth are mutually exclusive.
`, clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword)
Bearer token and basic auth are mutually exclusive.`), clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword)
create_authinfo_example = dedent.Dedent(`
create_authinfo_example = templates.Examples(`
# Set only the "client-key" field on the "cluster-admin"
# entry, without touching other values:
kubectl config set-credentials cluster-admin --client-key=~/.kube/admin.key

View File

@@ -23,11 +23,11 @@ import (
"io/ioutil"
"path/filepath"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
"k8s.io/kubernetes/pkg/util/flag"
)
@@ -42,10 +42,12 @@ type createClusterOptions struct {
}
var (
create_cluster_long = dedent.Dedent(`
create_cluster_long = templates.LongDesc(`
Sets a cluster entry in kubeconfig.
Specifying a name that already exists will merge new fields on top of existing values for those fields.`)
create_cluster_example = dedent.Dedent(`
create_cluster_example = templates.Examples(`
# Set only the server field on the e2e cluster entry without touching other values.
kubectl config set-cluster e2e --server=https://1.2.3.4

View File

@@ -21,11 +21,11 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
"k8s.io/kubernetes/pkg/util/flag"
)
@@ -38,10 +38,12 @@ type createContextOptions struct {
}
var (
create_context_long = dedent.Dedent(`
create_context_long = templates.LongDesc(`
Sets a context entry in kubeconfig
Specifying a name that already exists will merge new fields on top of existing values for those fields.`)
create_context_example = dedent.Dedent(`
create_context_example = templates.Examples(`
# Set the user field on the gce context entry without touching other values
kubectl config set-context gce --user=cluster-admin`)
)

View File

@@ -20,10 +20,10 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
@@ -32,9 +32,10 @@ type CurrentContextOptions struct {
}
var (
current_context_long = dedent.Dedent(`
current_context_long = templates.LongDesc(`
Displays the current-context`)
current_context_example = dedent.Dedent(`
current_context_example = templates.Examples(`
# Display the current-context
kubectl config current-context`)
)

View File

@@ -26,7 +26,7 @@ import (
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/sets"
@@ -41,14 +41,15 @@ type GetContextsOptions struct {
out io.Writer
}
const (
getContextsLong = `Displays one or many contexts from the kubeconfig file.`
var (
getContextsLong = templates.LongDesc(`Displays one or many contexts from the kubeconfig file.`)
getContextsExample = `# List all the contexts in your kubeconfig file
getContextsExample = templates.Examples(`
# List all the contexts in your kubeconfig file
kubectl config get-contexts
# Describe one context in your kubeconfig file.
kubectl config get-contexts my-context`
kubectl config get-contexts my-context`)
)
// NewCmdConfigGetContexts creates a command object for the "get-contexts" action, which

View File

@@ -24,10 +24,10 @@ import (
"reflect"
"strings"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
"k8s.io/kubernetes/pkg/util/flag"
)
@@ -43,9 +43,11 @@ type setOptions struct {
setRawBytes flag.Tristate
}
var set_long = dedent.Dedent(`
var set_long = templates.LongDesc(`
Sets an individual value in a kubeconfig file
PROPERTY_NAME is a dot delimited name where each token represents either an attribute name or a map key. Map keys may not contain dots.
PROPERTY_VALUE is the new value you wish to set. Binary fields such as 'certificate-authority-data' expect a base64 encoded string unless the --set-raw-bytes flag is used.`)
func NewCmdConfigSet(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {

View File

@@ -22,8 +22,8 @@ import (
"io"
"reflect"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
)
@@ -33,8 +33,9 @@ type unsetOptions struct {
propertyName string
}
var unset_long = dedent.Dedent(`
var unset_long = templates.LongDesc(`
Unsets an individual value in a kubeconfig file
PROPERTY_NAME is a dot delimited name where each token represents either an attribute name or a map key. Map keys may not contain dots.`)
func NewCmdConfigUnset(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {

View File

@@ -21,13 +21,13 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/flag"
)
@@ -41,11 +41,12 @@ type ViewOptions struct {
}
var (
view_long = dedent.Dedent(`
view_long = templates.LongDesc(`
Display merged kubeconfig settings or a specified kubeconfig file.
You can use --output jsonpath={...} to extract specific values using a jsonpath expression.`)
view_example = dedent.Dedent(`
view_example = templates.Examples(`
# Show Merged kubeconfig settings.
kubectl config view

View File

@@ -21,12 +21,11 @@ import (
"io"
"os"
"github.com/renstrom/dedent"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -35,7 +34,7 @@ import (
)
var (
convert_long = dedent.Dedent(`
convert_long = templates.LongDesc(`
Convert config files between different API versions. Both YAML
and JSON formats are accepted.
@@ -44,10 +43,9 @@ var (
not supported, convert to latest version.
The default output will be printed to stdout in YAML format. One can use -o option
to change to output destination.
`)
to change to output destination.`)
convert_example = dedent.Dedent(`
convert_example = templates.Examples(`
# Convert 'pod.yaml' to latest version and print to stdout.
kubectl convert -f pod.yaml
@@ -56,8 +54,7 @@ var (
kubectl convert -f pod.yaml --local -o json
# Convert all files under current directory to latest version and create them all.
kubectl convert -f . | kubectl create -f -
`)
kubectl convert -f . | kubectl create -f -`)
)
// NewCmdConvert creates a command object for the generic "convert" action, which

View File

@@ -20,22 +20,23 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
)
var (
create_long = dedent.Dedent(`
create_long = templates.LongDesc(`
Create a resource by filename or stdin.
JSON and YAML formats are accepted.`)
create_example = dedent.Dedent(`
create_example = templates.Examples(`
# Create a pod using the data in pod.json.
kubectl create -f ./pod.json

View File

@@ -20,15 +20,15 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
configMapLong = dedent.Dedent(`
configMapLong = templates.LongDesc(`
Create a configmap based on a file, directory, or specified literal value.
A single configmap may package one or more key/value pairs.
@@ -38,10 +38,9 @@ var (
When creating a configmap based on a directory, each file whose basename is a valid key in the directory will be
packaged into the configmap. Any directory entries except regular files are ignored (e.g. subdirectories,
symlinks, devices, pipes, etc).
`)
symlinks, devices, pipes, etc).`)
configMapExample = dedent.Dedent(`
configMapExample = templates.Examples(`
# Create a new configmap named my-config with keys for each file in folder bar
kubectl create configmap my-config --from-file=path/to/bar

View File

@@ -20,18 +20,18 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
deploymentLong = dedent.Dedent(`
deploymentLong = templates.LongDesc(`
Create a deployment with the specified name.`)
deploymentExample = dedent.Dedent(`
deploymentExample = templates.Examples(`
# Create a new deployment named my-dep that runs the busybox image.
kubectl create deployment my-dep --image=busybox`)
)

View File

@@ -20,18 +20,18 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
namespaceLong = dedent.Dedent(`
namespaceLong = templates.LongDesc(`
Create a namespace with the specified name.`)
namespaceExample = dedent.Dedent(`
namespaceExample = templates.Examples(`
# Create a new namespace named my-namespace
kubectl create namespace my-namespace`)
)

View File

@@ -20,22 +20,22 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
quotaLong = dedent.Dedent(`
quotaLong = templates.LongDesc(`
Create a resourcequota with the specified name, hard limits and optional scopes`)
quotaExample = dedent.Dedent(`
// Create a new resourcequota named my-quota
quotaExample = templates.Examples(`
# Create a new resourcequota named my-quota
$ kubectl create quota my-quota --hard=cpu=1,memory=1G,pods=2,services=3,replicationcontrollers=2,resourcequotas=1,secrets=5,persistentvolumeclaims=10
// Create a new resourcequota named best-effort
# Create a new resourcequota named best-effort
$ kubectl create quota best-effort --hard=pods=100 --scopes=BestEffort`)
)

View File

@@ -20,10 +20,10 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
@@ -45,7 +45,7 @@ func NewCmdCreateSecret(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
}
var (
secretLong = dedent.Dedent(`
secretLong = templates.LongDesc(`
Create a secret based on a file, directory, or specified literal value.
A single secret may package one or more key/value pairs.
@@ -55,10 +55,9 @@ var (
When creating a secret based on a directory, each file whose basename is a valid key in the directory will be
packaged into the secret. Any directory entries except regular files are ignored (e.g. subdirectories,
symlinks, devices, pipes, etc).
`)
symlinks, devices, pipes, etc).`)
secretExample = dedent.Dedent(`
secretExample = templates.Examples(`
# Create a new secret named my-secret with keys for each file in folder bar
kubectl create secret generic my-secret --from-file=path/to/bar
@@ -118,13 +117,15 @@ func CreateSecretGeneric(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command
}
var (
secretForDockerRegistryLong = dedent.Dedent(`
secretForDockerRegistryLong = templates.LongDesc(`
Create a new secret for use with Docker registries.
Dockercfg secrets are used to authenticate against Docker registries.
When using the Docker command line to push images, you can authenticate to a given registry by running
'docker login DOCKER_REGISTRY_SERVER --username=DOCKER_USER --password=DOCKER_PASSWORD --email=DOCKER_EMAIL'.
$ docker login DOCKER_REGISTRY_SERVER --username=DOCKER_USER --password=DOCKER_PASSWORD --email=DOCKER_EMAIL'.
That produces a ~/.dockercfg file that is used by subsequent 'docker push' and 'docker pull' commands to
authenticate to the registry.
@@ -132,7 +133,7 @@ var (
nodes to pull images on your behalf, they have to have the credentials. You can provide this information
by creating a dockercfg secret and attaching it to your service account.`)
secretForDockerRegistryExample = dedent.Dedent(`
secretForDockerRegistryExample = templates.Examples(`
# If you don't already have a .dockercfg file, you can create a dockercfg secret directly by using:
kubectl create secret docker-registry my-secret --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL`)
)
@@ -198,12 +199,12 @@ func CreateSecretDockerRegistry(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.
}
var (
secretForTLSLong = dedent.Dedent(`
secretForTLSLong = templates.LongDesc(`
Create a TLS secret from the given public/private key pair.
The public/private key pair must exist before hand. The public key certificate must be .PEM encoded and match the given private key.`)
secretForTLSExample = dedent.Dedent(`
secretForTLSExample = templates.Examples(`
# Create a new TLS secret named tls-secret with the given key pair:
kubectl create secret tls tls-secret --cert=path/to/tls.cert --key=path/to/tls.key`)
)

View File

@@ -20,11 +20,11 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
@@ -47,10 +47,10 @@ func NewCmdCreateService(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
}
var (
serviceClusterIPLong = dedent.Dedent(`
serviceClusterIPLong = templates.LongDesc(`
Create a clusterIP service with the specified name.`)
serviceClusterIPExample = dedent.Dedent(`
serviceClusterIPExample = templates.Examples(`
# Create a new clusterIP service named my-cs
kubectl create service clusterip my-cs --tcp=5678:8080
@@ -110,10 +110,10 @@ func CreateServiceClusterIP(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Comm
}
var (
serviceNodePortLong = dedent.Dedent(`
serviceNodePortLong = templates.LongDesc(`
Create a nodeport service with the specified name.`)
serviceNodePortExample = dedent.Dedent(`
serviceNodePortExample = templates.Examples(`
# Create a new nodeport service named my-ns
kubectl create service nodeport my-ns --tcp=5678:8080`)
)
@@ -167,10 +167,10 @@ func CreateServiceNodePort(f cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Comma
}
var (
serviceLoadBalancerLong = dedent.Dedent(`
serviceLoadBalancerLong = templates.LongDesc(`
Create a LoadBalancer service with the specified name.`)
serviceLoadBalancerExample = dedent.Dedent(`
serviceLoadBalancerExample = templates.Examples(`
# Create a new nodeport service named my-lbs
kubectl create service loadbalancer my-lbs --tcp=5678:8080`)
)

View File

@@ -20,18 +20,18 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
serviceAccountLong = dedent.Dedent(`
serviceAccountLong = templates.LongDesc(`
Create a service account with the specified name.`)
serviceAccountExample = dedent.Dedent(`
serviceAccountExample = templates.Examples(`
# Create a new service account named my-service-account
$ kubectl create serviceaccount my-service-account`)
)

View File

@@ -21,19 +21,19 @@ import (
"io"
"time"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
var (
delete_long = dedent.Dedent(`
delete_long = templates.LongDesc(`
Delete resources by filenames, stdin, resources and names, or by resources and label selector.
JSON and YAML formats are accepted.
@@ -43,7 +43,8 @@ var (
Note that the delete command does NOT do resource version checks, so if someone
submits an update to a resource right when you submit a delete, their update
will be lost along with the rest of the resource.`)
delete_example = dedent.Dedent(`
delete_example = templates.Examples(`
# Delete a pod using the type and name specified in pod.json.
kubectl delete -f ./pod.json

View File

@@ -21,12 +21,12 @@ import (
"io"
"strings"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
apierrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -34,7 +34,7 @@ import (
)
var (
describe_long = dedent.Dedent(`
describe_long = templates.LongDesc(`
Show details of a specific resource or group of resources.
This command joins many API calls together to form a detailed description of a
given resource or group of resources.
@@ -44,9 +44,9 @@ var (
will first check for an exact match on TYPE and NAME_PREFIX. If no such resource
exists, it will output details for every resource that has a name prefixed with NAME_PREFIX.
`) + valid_resources
` + valid_resources)
describe_example = dedent.Dedent(`
describe_example = templates.Examples(`
# Describe a node
kubectl describe nodes kubernetes-node-emt8.c.myproject.internal

View File

@@ -23,7 +23,6 @@ import (
"reflect"
"strings"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
@@ -31,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubelet/types"
@@ -71,13 +71,12 @@ const (
)
var (
cordon_long = dedent.Dedent(`
Mark node as unschedulable.
`)
cordon_example = dedent.Dedent(`
cordon_long = templates.LongDesc(`
Mark node as unschedulable.`)
cordon_example = templates.Examples(`
# Mark node "foo" as unschedulable.
kubectl cordon foo
`)
kubectl cordon foo`)
)
func NewCmdCordon(f cmdutil.Factory, out io.Writer) *cobra.Command {
@@ -97,13 +96,12 @@ func NewCmdCordon(f cmdutil.Factory, out io.Writer) *cobra.Command {
}
var (
uncordon_long = dedent.Dedent(`
Mark node as schedulable.
`)
uncordon_example = dedent.Dedent(`
uncordon_long = templates.LongDesc(`
Mark node as schedulable.`)
uncordon_example = templates.Examples(`
# Mark node "foo" as schedulable.
$ kubectl uncordon foo
`)
$ kubectl uncordon foo`)
)
func NewCmdUncordon(f cmdutil.Factory, out io.Writer) *cobra.Command {
@@ -123,7 +121,7 @@ func NewCmdUncordon(f cmdutil.Factory, out io.Writer) *cobra.Command {
}
var (
drain_long = dedent.Dedent(`
drain_long = templates.LongDesc(`
Drain node in preparation for maintenance.
The given node will be marked unschedulable to prevent new pods from arriving.
@@ -139,16 +137,14 @@ var (
When you are ready to put the node back into service, use kubectl uncordon, which
will make the node schedulable again.
![Workflow](http://kubernetes.io/images/docs/kubectl_drain.svg)
`)
![Workflow](http://kubernetes.io/images/docs/kubectl_drain.svg)`)
drain_example = dedent.Dedent(`
drain_example = templates.Examples(`
# Drain node "foo", even if there are pods not managed by a ReplicationController, ReplicaSet, Job, or DaemonSet on it.
$ kubectl drain foo --force
# As above, but abort if there are pods not managed by a ReplicationController, ReplicaSet, Job, or DaemonSet, and use a grace period of 15 minutes.
$ kubectl drain foo --grace-period=900
`)
$ kubectl drain foo --grace-period=900`)
)
func NewCmdDrain(f cmdutil.Factory, out io.Writer) *cobra.Command {

View File

@@ -27,12 +27,12 @@ import (
gruntime "runtime"
"strings"
"github.com/renstrom/dedent"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor"
"k8s.io/kubernetes/pkg/kubectl/resource"
@@ -47,7 +47,7 @@ import (
)
var (
editLong = dedent.Dedent(`
editLong = templates.LongDesc(`
Edit a resource from the default editor.
The edit command allows you to directly edit any API resource you can retrieve via the
@@ -68,7 +68,7 @@ var (
to apply your changes to the newer version of the resource, or update your temporary
saved copy to include the latest resource version.`)
editExample = dedent.Dedent(`
editExample = templates.Examples(`
# Edit the service named 'docker-registry':
kubectl edit svc/docker-registry

View File

@@ -22,13 +22,13 @@ import (
"net/url"
dockerterm "github.com/docker/docker/pkg/term"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/unversioned/remotecommand"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
remotecommandserver "k8s.io/kubernetes/pkg/kubelet/server/remotecommand"
"k8s.io/kubernetes/pkg/util/interrupt"
@@ -36,7 +36,7 @@ import (
)
var (
exec_example = dedent.Dedent(`
exec_example = templates.Examples(`
# Get output from running 'date' from pod 123456-7890, using the first container by default
kubectl exec 123456-7890 date

View File

@@ -20,27 +20,27 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
explainExamples = dedent.Dedent(`
explainLong = templates.LongDesc(`
Documentation of resources.
` + valid_resources)
explainExamples = templates.Examples(`
# Get the documentation of the resource and its fields
kubectl explain pods
# Get the documentation of a specific field of a resource
kubectl explain pods.spec.containers`)
explainLong = dedent.Dedent(`
Documentation of resources.
`) + valid_resources
)
// NewCmdExplain returns a cobra command for swagger docs

View File

@@ -22,10 +22,10 @@ import (
"regexp"
"strings"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -33,12 +33,9 @@ import (
)
var (
expose_resources = dedent.Dedent(`
pod (po), service (svc), replicationcontroller (rc),
deployment (deploy), replicaset (rs)
`)
expose_resources = `pod (po), service (svc), replicationcontroller (rc), deployment (deploy), replicaset (rs)`
expose_long = dedent.Dedent(`
expose_long = templates.LongDesc(`
Expose a resource as a new Kubernetes service.
Looks up a deployment, service, replica set, replication controller or pod by name and uses the selector
@@ -48,9 +45,11 @@ var (
--port and the exposed resource has multiple ports, all will be re-used by the new service. Also if no
labels are specified, the new service will re-use the labels from the resource it exposes.
Possible resources include (case insensitive): `) + expose_resources
Possible resources include (case insensitive):
expose_example = dedent.Dedent(`
` + expose_resources)
expose_example = templates.Examples(`
# Create a service for a replicated nginx, which serves on port 80 and connects to the containers on port 8000.
kubectl expose rc nginx --port=80 --target-port=8000

View File

@@ -20,12 +20,12 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -43,17 +43,18 @@ type GetOptions struct {
}
var (
get_long = dedent.Dedent(`
get_long = templates.LongDesc(`
Display one or many resources.
`) + valid_resources + dedent.Dedent(`
` + valid_resources + `
This command will hide resources that have completed. For instance, pods that are in the Succeeded or Failed phases.
You can see the full results for any resource by providing the '--show-all' flag.
By specifying the output as 'template' and providing a Go template as the value
of the --template flag, you can filter the attributes of the fetched resource(s).`)
get_example = dedent.Dedent(`
get_example = templates.Examples(`
# List all pods in ps output format.
kubectl get pods

View File

@@ -22,11 +22,13 @@ import (
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
const help_long = `Help provides help for any command in the application.
Simply type kubectl help [path to command] for full details.`
var help_long = templates.LongDesc(`
Help provides help for any command in the application.
Simply type kubectl help [path to command] for full details.`)
func NewCmdHelp(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{

View File

@@ -24,11 +24,11 @@ import (
"strings"
"github.com/golang/glog"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -61,13 +61,14 @@ type LabelOptions struct {
}
var (
label_long = dedent.Dedent(`
label_long = templates.LongDesc(`
Update the labels on a resource.
A label must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters.
If --overwrite is true, then existing labels can be overwritten, otherwise attempting to overwrite a label will result in an error.
If --resource-version is specified, then updates will use this resource version, otherwise the existing resource-version will be used.`)
label_example = dedent.Dedent(`
* A label must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters.
* If --overwrite is true, then existing labels can be overwritten, otherwise attempting to overwrite a label will result in an error.
* If --resource-version is specified, then updates will use this resource version, otherwise the existing resource-version will be used.`)
label_example = templates.Examples(`
# Update pod 'foo' with the label 'unhealthy' and the value 'true'.
kubectl label pods foo unhealthy=true

View File

@@ -23,20 +23,20 @@ import (
"os"
"time"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
)
var (
logs_example = dedent.Dedent(`
logs_example = templates.Examples(`
# Return snapshot logs from pod nginx with only one container
kubectl logs nginx

View File

@@ -22,11 +22,11 @@ import (
"strings"
"github.com/evanphx/json-patch"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -48,14 +48,14 @@ type PatchOptions struct {
}
var (
patch_long = dedent.Dedent(`
patch_long = templates.LongDesc(`
Update field(s) of a resource using strategic merge patch
JSON and YAML formats are accepted.
Please refer to the models in https://htmlpreview.github.io/?https://github.com/kubernetes/kubernetes/blob/HEAD/docs/api-reference/v1/definitions.html to find if a field is mutable.`)
patch_example = dedent.Dedent(`
patch_example = templates.Examples(`
# Partially update a node using strategic merge patch
kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}'

View File

@@ -23,7 +23,6 @@ import (
"os"
"os/signal"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
@@ -31,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/unversioned/portforward"
"k8s.io/kubernetes/pkg/client/unversioned/remotecommand"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
@@ -48,7 +48,7 @@ type PortForwardOptions struct {
}
var (
portforward_example = dedent.Dedent(`
portforward_example = templates.Examples(`
# Listen on ports 5000 and 6000 locally, forwarding data to/from ports 5000 and 6000 in the pod
kubectl port-forward mypod 5000 6000

View File

@@ -24,15 +24,32 @@ import (
"strings"
"github.com/golang/glog"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
default_port = 8001
proxy_example = dedent.Dedent(`
proxy_long = templates.LongDesc(`
To proxy all of the kubernetes api and nothing else, use:
$ kubectl proxy --api-prefix=/
To proxy only part of the kubernetes api and also some static files:
$ kubectl proxy --www=/my/files --www-prefix=/static/ --api-prefix=/api/
The above lets you 'curl localhost:8001/api/v1/pods'.
To proxy the entire kubernetes api at a different root, use:
$ kubectl proxy --api-prefix=/custom/
The above lets you 'curl localhost:8001/custom/api/v1/pods'`)
proxy_example = templates.Examples(`
# Run a proxy to kubernetes apiserver on port 8011, serving static content from ./local/www/
kubectl proxy --port=8011 --www=./local/www/
@@ -49,23 +66,7 @@ func NewCmdProxy(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix]",
Short: "Run a proxy to the Kubernetes API server",
Long: dedent.Dedent(`
To proxy all of the kubernetes api and nothing else, use:
kubectl proxy --api-prefix=/
To proxy only part of the kubernetes api and also some static files:
kubectl proxy --www=/my/files --www-prefix=/static/ --api-prefix=/api/
The above lets you 'curl localhost:8001/api/v1/pods'.
To proxy the entire kubernetes api at a different root, use:
kubectl proxy --api-prefix=/custom/
The above lets you 'curl localhost:8001/custom/api/v1/pods'
`),
Long: proxy_long,
Example: proxy_example,
Run: func(cmd *cobra.Command, args []string) {
err := RunProxy(f, out, cmd)

View File

@@ -23,12 +23,12 @@ import (
"os"
"path/filepath"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -36,15 +36,17 @@ import (
)
var (
replace_long = dedent.Dedent(`
replace_long = templates.LongDesc(`
Replace a resource by filename or stdin.
JSON and YAML formats are accepted. If replacing an existing resource, the
complete resource spec must be provided. This can be obtained by
$ kubectl get TYPE NAME -o yaml
Please refer to the models in https://htmlpreview.github.io/?https://github.com/kubernetes/kubernetes/blob/HEAD/docs/api-reference/v1/definitions.html to find if a field is mutable.`)
replace_example = dedent.Dedent(`
replace_example = templates.Examples(`
# Replace a pod using the data in pod.json.
kubectl replace -f ./pod.json

View File

@@ -25,7 +25,6 @@ import (
"github.com/golang/glog"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
@@ -33,6 +32,7 @@ import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
@@ -40,16 +40,16 @@ import (
)
var (
rollingUpdate_long = dedent.Dedent(`
rollingUpdate_long = templates.LongDesc(`
Perform a rolling update of the given ReplicationController.
Replaces the specified replication controller with a new replication controller by updating one pod at a time to use the
new PodTemplate. The new-controller.json must specify the same namespace as the
existing replication controller and overwrite at least one (common) label in its replicaSelector.
![Workflow](http://kubernetes.io/images/docs/kubectl_rollingupdate.svg)
`)
rollingUpdate_example = dedent.Dedent(`
![Workflow](http://kubernetes.io/images/docs/kubectl_rollingupdate.svg)`)
rollingUpdate_example = templates.Examples(`
# Update pods of frontend-v1 using new replication controller data in frontend-v2.json.
kubectl rolling-update frontend-v1 -f frontend-v2.json
@@ -64,8 +64,7 @@ var (
kubectl rolling-update frontend --image=image:v2
# Abort and reverse an existing rollout in progress (from frontend-v1 to frontend-v2).
kubectl rolling-update frontend-v1 frontend-v2 --rollback
`)
kubectl rolling-update frontend-v1 frontend-v2 --rollback`)
)
var (

View File

@@ -21,15 +21,18 @@ import (
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
rollout_long = dedent.Dedent(`
rollout_long = templates.LongDesc(`
Manage a deployment using subcommands like "kubectl rollout undo deployment/abc"`)
rollout_example = dedent.Dedent(`
rollout_example = templates.Examples(`
# Rollback to the previous deployment
kubectl rollout undo deployment/abc`)
rollout_valid_resources = dedent.Dedent(`
Valid resource types include:
* deployments

View File

@@ -20,8 +20,8 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
@@ -29,9 +29,10 @@ import (
)
var (
history_long = dedent.Dedent(`
history_long = templates.LongDesc(`
View previous rollout revisions and configurations.`)
history_example = dedent.Dedent(`
history_example = templates.Examples(`
# View the rollout history of a deployment
kubectl rollout history deployment/abc

View File

@@ -19,11 +19,11 @@ package rollout
import (
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -44,14 +44,14 @@ type PauseConfig struct {
}
var (
pause_long = dedent.Dedent(`
pause_long = templates.LongDesc(`
Mark the provided resource as paused
Paused resources will not be reconciled by a controller.
Use \"kubectl rollout resume\" to resume a paused resource.
Currently only deployments support being paused.`)
pause_example = dedent.Dedent(`
pause_example = templates.Examples(`
# Mark the nginx deployment as paused. Any current state of
# the deployment will continue its function, new updates to the deployment will not
# have an effect as long as the deployment is paused.

View File

@@ -19,11 +19,11 @@ package rollout
import (
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -44,14 +44,14 @@ type ResumeConfig struct {
}
var (
resume_long = dedent.Dedent(`
resume_long = templates.LongDesc(`
Resume a paused resource
Paused resources will not be reconciled by a controller. By resuming a
resource, we allow it to be reconciled again.
Currently only deployments support being resumed.`)
resume_example = dedent.Dedent(`
resume_example = templates.Examples(`
# Resume an already paused deployment
kubectl rollout resume deployment/nginx`)
)

View File

@@ -20,8 +20,8 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/util/interrupt"
@@ -31,7 +31,7 @@ import (
)
var (
status_long = dedent.Dedent(`
status_long = templates.LongDesc(`
Show the status of the rollout.
By default 'rollout status' will watch the status of the latest rollout
@@ -40,7 +40,8 @@ var (
'rollout status' will continue watching the latest revision. If you want to
pin to a specific revision and abort if it is rolled over by another revision,
use --revision=N where N is the revision you need to watch for.`)
status_example = dedent.Dedent(`
status_example = templates.Examples(`
# Watch the rollout status of a deployment
kubectl rollout status deployment/nginx`)
)

View File

@@ -19,9 +19,9 @@ package rollout
import (
"io"
"github.com/renstrom/dedent"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -46,9 +46,10 @@ type UndoOptions struct {
}
var (
undo_long = dedent.Dedent(`
undo_long = templates.LongDesc(`
Rollback to a previous rollout.`)
undo_example = dedent.Dedent(`
undo_example = templates.Examples(`
# Rollback to the previous deployment
kubectl rollout undo deployment/abc

View File

@@ -22,7 +22,6 @@ import (
"os"
"time"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"github.com/docker/distribution/reference"
@@ -36,6 +35,7 @@ import (
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned"
conditions "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -45,10 +45,12 @@ import (
)
var (
run_long = dedent.Dedent(`
run_long = templates.LongDesc(`
Create and run a particular image, possibly replicated.
Creates a deployment or job to manage the created container(s).`)
run_example = dedent.Dedent(`
run_example = templates.Examples(`
# Start a single instance of nginx.
kubectl run nginx --image=nginx

View File

@@ -21,24 +21,26 @@ import (
"io"
"os"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
var (
scale_long = dedent.Dedent(`
scale_long = templates.LongDesc(`
Set a new size for a Deployment, ReplicaSet, Replication Controller, or Job.
Scale also allows users to specify one or more preconditions for the scale action.
If --current-replicas or --resource-version is specified, it is validated before the
scale is attempted, and it is guaranteed that the precondition holds true when the
scale is sent to the server.`)
scale_example = dedent.Dedent(`
scale_example = templates.Examples(`
# Scale a replicaset named 'foo' to 3.
kubectl scale --replicas=3 rs/foo

View File

@@ -19,17 +19,16 @@ package set
import (
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
var (
set_long = dedent.Dedent(`
set_long = templates.LongDesc(`
Configure application resources
These commands help you make changes to existing application resources.`)
set_example = dedent.Dedent(``)
)
func NewCmdSet(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
@@ -37,7 +36,6 @@ func NewCmdSet(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
Use: "set SUBCOMMAND",
Short: "Set specific features on objects",
Long: set_long,
Example: set_example,
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},

View File

@@ -20,10 +20,10 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -59,12 +59,13 @@ var (
image_resources = `
pod (po), replicationcontroller (rc), deployment (deploy), daemonset (ds), job, replicaset (rs)`
image_long = dedent.Dedent(`
image_long = templates.LongDesc(`
Update existing container image(s) of resources.
Possible resources include (case insensitive):`) + image_resources
Possible resources include (case insensitive):
` + image_resources)
image_example = dedent.Dedent(`
image_example = templates.Examples(`
# Set a deployment's nginx container image to 'nginx:1.9.1', and its busybox container image to 'busybox'.
kubectl set image deployment/nginx busybox=busybox nginx=nginx:1.9.1

View File

@@ -25,12 +25,35 @@ import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
)
var (
resources_long = templates.LongDesc(`
Specify compute resource requirements (cpu, memory) for any resource that defines a pod template. If a pod is successfully scheduled, it is guaranteed the amount of resource requested, but may burst up to its specified limits.
for each compute resource, if a limit is specified and a request is omitted, the request will default to the limit.
Possible resources include (case insensitive):`)
resources_example = templates.Examples(`
# Set a deployments nginx container cpu limits to "200m" and memory to "512Mi"
kubectl set resources deployment nginx -c=nginx --limits=cpu=200m,memory=512Mi
# Set the resource request and limits for all containers in nginx
kubectl set resources deployment nginx --limits=cpu=200m,memory=512Mi --requests=cpu=100m,memory=256Mi
# Remove the resource requests for resources on containers in nginx
kubectl set resources deployment nginx --limits=cpu=0,memory=0 --requests=cpu=0,memory=0
# Print the result (in yaml format) of updating nginx container limits from a local, without hitting the server
kubectl set resources -f path/to/file.yaml --limits=cpu=200m,memory=512Mi --dry-run -o yaml`)
)
// ResourcesOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
// referencing the cmd.Flags
type ResourcesOptions struct {
@@ -59,32 +82,6 @@ type ResourcesOptions struct {
Resources []string
}
const (
resources_long = `Specify compute resource requirements (cpu, memory) for any resource that defines a pod template. If a pod is successfully scheduled, it is guaranteed the amount of resource requested, but may burst up to its specified limits.
for each compute resource, if a limit is specified and a request is omitted, the request will default to the limit.
Possible resources include (case insensitive):`
resources_example = `
# Set a deployments nginx container cpu limits to "200m and memory to "512Mi"
kubectl set resources deployment nginx -c=nginx --limits=cpu=200m,memory=512Mi
# Set the resource request and limits for all containers in nginx
kubectl set resources deployment nginx --limits=cpu=200m,memory=512Mi --requests=cpu=100m,memory=256Mi
# Remove the resource requests for resources on containers in nginx
kubectl set resources deployment nginx --limits=cpu=0,memory=0 --requests=cpu=0,memory=0
# Print the result (in yaml format) of updating nginx container limits from a local, without hitting the server
kubectl set resources -f path/to/file.yaml --limits=cpu=200m,memory=512Mi --dry-run -o yaml
`
)
func NewCmdResources(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
options := &ResourcesOptions{
Out: out,
@@ -100,7 +97,7 @@ func NewCmdResources(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.
cmd := &cobra.Command{
Use: "resources (-f FILENAME | TYPE NAME) ([--limits=LIMITS & --requests=REQUESTS]",
Short: "update resource requests/limits on objects with pod templates",
Long: resources_long + "\n" + pod_specs[2:],
Long: resources_long + " " + pod_specs[2:],
Example: resources_example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args))

View File

@@ -20,14 +20,14 @@ import (
"fmt"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
var (
stop_long = dedent.Dedent(`
stop_long = templates.LongDesc(`
Deprecated: Gracefully shut down a resource by name or filename.
The stop command is deprecated, all its functionalities are covered by delete command.
@@ -35,7 +35,8 @@ var (
Attempts to shut down and delete a resource that supports graceful termination.
If the resource is scalable it will be scaled to 0 before deletion.`)
stop_example = dedent.Dedent(`
stop_example = templates.Examples(`
# Shut down foo.
kubectl stop replicationcontroller foo

View File

@@ -24,11 +24,11 @@ import (
"encoding/json"
"github.com/golang/glog"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@@ -53,15 +53,16 @@ type TaintOptions struct {
}
var (
taint_long = dedent.Dedent(`
taint_long = templates.LongDesc(`
Update the taints on one or more nodes.
A taint consists of a key, value, and effect. As an argument here, it is expressed as key=value:effect.
The key must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters.
The value must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters.
The effect must be NoSchedule or PreferNoSchedule.
Currently taint can only apply to node.`)
taint_example = dedent.Dedent(`
* A taint consists of a key, value, and effect. As an argument here, it is expressed as key=value:effect.
* The key must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters.
* The value must begin with a letter or number, and may contain letters, numbers, hyphens, dots, and underscores, up to %[1]d characters.
* The effect must be NoSchedule or PreferNoSchedule.
* Currently taint can only apply to node.`)
taint_example = templates.Examples(`
# Update node 'foo' with a taint with key 'dedicated' and value 'special-user' and effect 'NoSchedule'.
# If a taint with that key and effect already exists, its value is replaced as specified.
kubectl taint nodes foo dedicated=special-user:NoSchedule

View File

@@ -0,0 +1,61 @@
/*
Copyright 2016 The Kubernetes Authors.
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
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.
*/
package templates
import (
"github.com/spf13/cobra"
)
type CommandGroup struct {
Message string
Commands []*cobra.Command
}
type CommandGroups []CommandGroup
func (g CommandGroups) Add(c *cobra.Command) {
for _, group := range g {
for _, command := range group.Commands {
c.AddCommand(command)
}
}
}
func (g CommandGroups) Has(c *cobra.Command) bool {
for _, group := range g {
for _, command := range group.Commands {
if command == c {
return true
}
}
}
return false
}
func AddAdditionalCommands(g CommandGroups, message string, cmds []*cobra.Command) CommandGroups {
group := CommandGroup{Message: message}
for _, c := range cmds {
// Don't show commands that have no short description
if !g.Has(c) && len(c.Short) != 0 {
group.Commands = append(group.Commands, c)
}
}
if len(group.Commands) == 0 {
return g
}
return append(g, group)
}

View File

@@ -0,0 +1,145 @@
/*
Copyright 2016 The Kubernetes Authors.
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
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.
*/
package templates
import (
"bytes"
"fmt"
"strings"
"github.com/russross/blackfriday"
)
const linebreak = "\n"
// ASCIIRenderer implements blackfriday.Renderer
var _ blackfriday.Renderer = &ASCIIRenderer{}
// ASCIIRenderer is a blackfriday.Renderer intended for rendering markdown
// documents as plain text, well suited for human reading on terminals.
type ASCIIRenderer struct {
Indentation string
listItemCount uint
listLevel uint
}
// NormalText gets a text chunk *after* the markdown syntax was already
// processed and does a final cleanup on things we don't expect here, like
// removing linebreaks on things that are not a paragraph break (auto unwrap).
func (r *ASCIIRenderer) NormalText(out *bytes.Buffer, text []byte) {
raw := string(text)
lines := strings.Split(raw, linebreak)
for _, line := range lines {
trimmed := strings.Trim(line, " \n\t")
out.WriteString(trimmed)
out.WriteString(" ")
}
}
// List renders the start and end of a list.
func (r *ASCIIRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
r.listLevel++
out.WriteString(linebreak)
text()
r.listLevel--
}
// ListItem renders list items and supports both ordered and unordered lists.
func (r *ASCIIRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
if flags&blackfriday.LIST_ITEM_BEGINNING_OF_LIST != 0 {
r.listItemCount = 1
} else {
r.listItemCount++
}
indent := strings.Repeat(r.Indentation, int(r.listLevel))
var bullet string
if flags&blackfriday.LIST_TYPE_ORDERED != 0 {
bullet += fmt.Sprintf("%d.", r.listItemCount)
} else {
bullet += "*"
}
out.WriteString(indent + bullet + " ")
r.fw(out, text)
out.WriteString(linebreak)
}
// Paragraph renders the start and end of a paragraph.
func (r *ASCIIRenderer) Paragraph(out *bytes.Buffer, text func() bool) {
out.WriteString(linebreak)
text()
out.WriteString(linebreak)
}
// BlockCode renders a chunk of text that represents source code.
func (r *ASCIIRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
out.WriteString(linebreak)
lines := []string{}
for _, line := range strings.Split(string(text), linebreak) {
indented := r.Indentation + line
lines = append(lines, indented)
}
out.WriteString(strings.Join(lines, linebreak))
}
func (r *ASCIIRenderer) GetFlags() int { return 0 }
func (r *ASCIIRenderer) HRule(out *bytes.Buffer) {
out.WriteString(linebreak + "----------" + linebreak)
}
func (r *ASCIIRenderer) LineBreak(out *bytes.Buffer) { out.WriteString(linebreak) }
func (r *ASCIIRenderer) TitleBlock(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) Header(out *bytes.Buffer, text func() bool, level int, id string) { text() }
func (r *ASCIIRenderer) BlockHtml(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) BlockQuote(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) TableRow(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) TableHeaderCell(out *bytes.Buffer, text []byte, align int) { r.fw(out, text) }
func (r *ASCIIRenderer) TableCell(out *bytes.Buffer, text []byte, align int) { r.fw(out, text) }
func (r *ASCIIRenderer) Footnotes(out *bytes.Buffer, text func() bool) { text() }
func (r *ASCIIRenderer) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) { r.fw(out, text) }
func (r *ASCIIRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) { r.fw(out, link) }
func (r *ASCIIRenderer) CodeSpan(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) DoubleEmphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) Emphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) RawHtmlTag(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) TripleEmphasis(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) StrikeThrough(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) FootnoteRef(out *bytes.Buffer, ref []byte, id int) { r.fw(out, ref) }
func (r *ASCIIRenderer) Entity(out *bytes.Buffer, entity []byte) { r.fw(out, entity) }
func (r *ASCIIRenderer) Smartypants(out *bytes.Buffer, text []byte) { r.fw(out, text) }
func (r *ASCIIRenderer) DocumentHeader(out *bytes.Buffer) {}
func (r *ASCIIRenderer) DocumentFooter(out *bytes.Buffer) {}
func (r *ASCIIRenderer) TocHeaderWithAnchor(text []byte, level int, anchor string) {}
func (r *ASCIIRenderer) TocHeader(text []byte, level int) {}
func (r *ASCIIRenderer) TocFinalize() {}
func (r *ASCIIRenderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {
r.fw(out, header, body)
}
func (r *ASCIIRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
r.fw(out, link)
}
func (r *ASCIIRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
r.fw(out, link)
}
func (r *ASCIIRenderer) fw(out *bytes.Buffer, text ...[]byte) {
for _, t := range text {
out.Write(t)
}
}

View File

@@ -0,0 +1,91 @@
/*
Copyright 2016 The Kubernetes Authors.
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
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.
*/
package templates
import (
"strings"
"github.com/MakeNowJust/heredoc"
"github.com/russross/blackfriday"
"github.com/spf13/cobra"
)
const Indentation = ` `
// LongDesc normalizes a command's long description to follow the conventions.
func LongDesc(s string) string {
return normalizer{s}.heredoc().markdown().trim().string
}
// Examples normalizes a command's examples to follow the conventions.
func Examples(s string) string {
return normalizer{s}.trim().indent().string
}
// Normalize perform all required normalizations on a given command.
func Normalize(cmd *cobra.Command) *cobra.Command {
if len(cmd.Long) > 0 {
cmd.Long = LongDesc(cmd.Long)
}
if len(cmd.Example) > 0 {
cmd.Example = Examples(cmd.Example)
}
return cmd
}
// NormalizeAll perform all required normalizations in the entire command tree.
func NormalizeAll(cmd *cobra.Command) *cobra.Command {
if cmd.HasSubCommands() {
for _, subCmd := range cmd.Commands() {
NormalizeAll(subCmd)
}
}
Normalize(cmd)
return cmd
}
type normalizer struct {
string
}
func (s normalizer) markdown() normalizer {
bytes := []byte(s.string)
formatted := blackfriday.Markdown(bytes, &ASCIIRenderer{Indentation: Indentation}, 0)
s.string = string(formatted)
return s
}
func (s normalizer) heredoc() normalizer {
s.string = heredoc.Doc(s.string)
return s
}
func (s normalizer) trim() normalizer {
s.string = strings.TrimSpace(s.string)
return s
}
func (s normalizer) indent() normalizer {
indentedLines := []string{}
for _, line := range strings.Split(s.string, "\n") {
trimmed := strings.TrimSpace(line)
indented := Indentation + trimmed
indentedLines = append(indentedLines, indented)
}
s.string = strings.Join(indentedLines, "\n")
return s
}

View File

@@ -23,73 +23,12 @@ import (
"text/template"
"unicode"
"k8s.io/kubernetes/pkg/util/term"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
)
// Content of this package was borrowed from openshift/origin.
type CommandGroup struct {
Message string
Commands []*cobra.Command
}
type CommandGroups []CommandGroup
func (g CommandGroups) Add(c *cobra.Command) {
for _, group := range g {
for _, command := range group.Commands {
c.AddCommand(command)
}
}
}
func (g CommandGroups) Has(c *cobra.Command) bool {
for _, group := range g {
for _, command := range group.Commands {
if command == c {
return true
}
}
}
return false
}
func AddAdditionalCommands(g CommandGroups, message string, cmds []*cobra.Command) CommandGroups {
group := CommandGroup{Message: message}
for _, c := range cmds {
// Don't show commands that has no short description
if !g.Has(c) && len(c.Short) != 0 {
group.Commands = append(group.Commands, c)
}
}
if len(group.Commands) == 0 {
return g
}
return append(g, group)
}
func filter(cmds []*cobra.Command, names ...string) []*cobra.Command {
out := []*cobra.Command{}
for _, c := range cmds {
if c.Hidden {
continue
}
skip := false
for _, name := range names {
if name == c.Name() {
skip = true
break
}
}
if skip {
continue
}
out = append(out, c)
}
return out
}
type FlagExposer interface {
ExposeFlags(cmd *cobra.Command, flags ...string) FlagExposer
}
@@ -98,27 +37,30 @@ func ActsAsRootCommand(cmd *cobra.Command, filters []string, groups ...CommandGr
if cmd == nil {
panic("nil root command")
}
cmd.SetHelpTemplate(MainHelpTemplate())
templater := &templater{
RootCmd: cmd,
UsageTemplate: MainUsageTemplate(),
HelpTemplate: MainHelpTemplate(),
CommandGroups: groups,
Filtered: filters,
}
cmd.SetUsageFunc(templater.UsageFunc())
cmd.SetHelpFunc(templater.HelpFunc())
return templater
}
func UseOptionsTemplates(cmd *cobra.Command) {
cmd.SetHelpTemplate(OptionsHelpTemplate())
templater := &templater{
UsageTemplate: OptionsUsageTemplate(),
HelpTemplate: OptionsHelpTemplate(),
}
cmd.SetUsageFunc(templater.UsageFunc())
cmd.SetHelpFunc(templater.HelpFunc())
}
type templater struct {
UsageTemplate string
HelpTemplate string
RootCmd *cobra.Command
CommandGroups
Filtered []string
@@ -129,11 +71,31 @@ func (templater *templater) ExposeFlags(cmd *cobra.Command, flags ...string) Fla
return templater
}
func (templater *templater) HelpFunc() func(*cobra.Command, []string) {
return func(c *cobra.Command, s []string) {
t := template.New("help")
t.Funcs(templater.templateFuncs())
template.Must(t.Parse(templater.HelpTemplate))
out := term.NewResponsiveWriter(c.OutOrStdout())
err := t.Execute(out, c)
if err != nil {
c.Println(err)
}
}
}
func (templater *templater) UsageFunc(exposedFlags ...string) func(*cobra.Command) error {
return func(c *cobra.Command) error {
t := template.New("custom")
t := template.New("usage")
t.Funcs(templater.templateFuncs(exposedFlags...))
template.Must(t.Parse(templater.UsageTemplate))
out := term.NewResponsiveWriter(c.OutOrStderr())
return t.Execute(out, c)
}
}
t.Funcs(template.FuncMap{
func (templater *templater) templateFuncs(exposedFlags ...string) template.FuncMap {
return template.FuncMap{
"trim": strings.TrimSpace,
"trimRight": func(s string) string { return strings.TrimRightFunc(s, unicode.IsSpace) },
"trimLeft": func(s string) string { return strings.TrimLeftFunc(s, unicode.IsSpace) },
@@ -144,8 +106,8 @@ func (templater *templater) UsageFunc(exposedFlags ...string) func(*cobra.Comman
"flagsNotIntersected": flagsNotIntersected,
"visibleFlags": visibleFlags,
"flagsUsages": flagsUsages,
"indentLines": indentLines,
"cmdGroups": templater.cmdGroups,
"cmdGroupsString": templater.cmdGroupsString,
"rootCmd": templater.rootCmdName,
"isRootCmd": templater.isRootCmd,
"optionsCmdFor": templater.optionsCmdFor,
@@ -161,10 +123,6 @@ func (templater *templater) UsageFunc(exposedFlags ...string) func(*cobra.Comman
}
return exposed
},
})
template.Must(t.Parse(templater.UsageTemplate))
return t.Execute(c.OutOrStdout(), c)
}
}
@@ -182,6 +140,20 @@ func (templater *templater) cmdGroups(c *cobra.Command, all []*cobra.Command) []
}
}
func (t *templater) cmdGroupsString(c *cobra.Command) string {
groups := []string{}
for _, cmdGroup := range t.cmdGroups(c, c.Commands()) {
cmds := []string{cmdGroup.Message}
for _, cmd := range cmdGroup.Commands {
if cmd.Runnable() {
cmds = append(cmds, " "+rpad(cmd.Name(), cmd.NamePadding())+" "+cmd.Short)
}
}
groups = append(groups, strings.Join(cmds, "\n"))
}
return strings.Join(groups, "\n\n")
}
func (t *templater) rootCmdName(c *cobra.Command) string {
return t.rootCmd(c).CommandPath()
}
@@ -298,3 +270,24 @@ func visibleFlags(l *flag.FlagSet) *flag.FlagSet {
})
return f
}
func filter(cmds []*cobra.Command, names ...string) []*cobra.Command {
out := []*cobra.Command{}
for _, c := range cmds {
if c.Hidden {
continue
}
skip := false
for _, name := range names {
if name == c.Name() {
skip = true
break
}
}
if skip {
continue
}
out = append(out, c)
}
return out
}

View File

@@ -16,84 +16,87 @@ limitations under the License.
package templates
import "strings"
func MainHelpTemplate() string {
return decorate(mainHelpTemplate, false)
}
func MainUsageTemplate() string {
return decorate(mainUsageTemplate, true) + "\n"
}
func OptionsHelpTemplate() string {
return decorate(optionsHelpTemplate, false)
}
func OptionsUsageTemplate() string {
return decorate(optionsUsageTemplate, false)
}
func decorate(template string, trim bool) string {
if trim && len(strings.Trim(template, " ")) > 0 {
template = strings.Trim(template, "\n")
}
return template
}
import (
"strings"
"unicode"
)
const (
vars = `{{$isRootCmd := isRootCmd .}}` +
// SectionVars is the help template section that declares variables to be used in the template.
SectionVars = `{{$isRootCmd := isRootCmd .}}` +
`{{$rootCmd := rootCmd .}}` +
`{{$visibleFlags := visibleFlags (flagsNotIntersected .LocalFlags .PersistentFlags)}}` +
`{{$explicitlyExposedFlags := exposed .}}` +
`{{$optionsCmdFor := optionsCmdFor .}}` +
`{{$usageLine := usageLine .}}`
mainHelpTemplate = `{{with or .Long .Short }}{{. | trim}}{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
// SectionAliases is the help template section that displays command aliases.
SectionAliases = `{{if gt .Aliases 0}}Aliases:
{{.NameAndAliases}}
mainUsageTemplate = vars +
// ALIASES
`{{if gt .Aliases 0}}
{{end}}`
Aliases:
{{.NameAndAliases}}{{end}}` +
// SectionExamples is the help template section that displays command examples.
SectionExamples = `{{if .HasExample}}Examples:
{{trimRight .Example}}
// EXAMPLES
`{{if .HasExample}}
{{end}}`
Examples:
{{ indentLines (.Example | trimLeft) 2 }}{{end}}` +
// SectionSubcommands is the help template section that displays the command's subcommands.
SectionSubcommands = `{{if .HasAvailableSubCommands}}{{cmdGroupsString .}}
// SUBCOMMANDS
`{{ if .HasAvailableSubCommands}}
{{range cmdGroups . .Commands}}
{{.Message}}
{{range .Commands}}{{if .Runnable}} {{rpad .Name .NamePadding }} {{.Short}}
{{end}}{{end}}{{end}}{{end}}` +
{{end}}`
// VISIBLE FLAGS
`{{ if or $visibleFlags.HasFlags $explicitlyExposedFlags.HasFlags}}
// SectionFlags is the help template section that displays the command's flags.
SectionFlags = `{{ if or $visibleFlags.HasFlags $explicitlyExposedFlags.HasFlags}}Options:
{{ if $visibleFlags.HasFlags}}{{trimRight (flagsUsages $visibleFlags)}}{{end}}{{ if $explicitlyExposedFlags.HasFlags}}{{trimRight (flagsUsages $explicitlyExposedFlags)}}{{end}}
Options:
{{ if $visibleFlags.HasFlags}}{{flagsUsages $visibleFlags}}{{end}}{{ if $explicitlyExposedFlags.HasFlags}}{{flagsUsages $explicitlyExposedFlags}}{{end}}{{end}}` +
{{end}}`
// USAGE LINE
`{{if and .Runnable (ne .UseLine "") (ne .UseLine $rootCmd)}}
Usage:
// SectionUsage is the help template section that displays the command's usage.
SectionUsage = `{{if and .Runnable (ne .UseLine "") (ne .UseLine $rootCmd)}}Usage:
{{$usageLine}}
{{end}}` +
// TIPS: --help
`{{ if .HasSubCommands }}
Use "{{$rootCmd}} <command> --help" for more information about a given command.{{end}}` +
{{end}}`
// TIPS: global options
`{{ if $optionsCmdFor}}
Use "{{$optionsCmdFor}}" for a list of global command-line options (applies to all commands).{{end}}`
// SectionTipsHelp is the help template section that displays the '--help' hint.
SectionTipsHelp = `{{if .HasSubCommands}}Use "{{$rootCmd}} <command> --help" for more information about a given command.
{{end}}`
optionsHelpTemplate = ``
// SectionTipsGlobalOptions is the help template section that displays the 'options' hint for displaying global flags.
SectionTipsGlobalOptions = `{{if $optionsCmdFor}}Use "{{$optionsCmdFor}}" for a list of global command-line options (applies to all commands).
{{end}}`
)
optionsUsageTemplate = `{{ if .HasInheritedFlags}}The following options can be passed to any command:
// MainHelpTemplate if the template for 'help' used by most commands.
func MainHelpTemplate() string {
return `{{with or .Long .Short }}{{. | trim}}{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
}
// MainUsageTemplate if the template for 'usage' used by most commands.
func MainUsageTemplate() string {
sections := []string{
"\n\n",
SectionVars,
SectionAliases,
SectionExamples,
SectionSubcommands,
SectionFlags,
SectionUsage,
SectionTipsHelp,
SectionTipsGlobalOptions,
}
return strings.TrimRightFunc(strings.Join(sections, ""), unicode.IsSpace)
}
// OptionsHelpTemplate if the template for 'help' used by the 'options' command.
func OptionsHelpTemplate() string {
return ""
}
// OptionsUsageTemplate if the template for 'usage' used by the 'options' command.
func OptionsUsageTemplate() string {
return `{{ if .HasInheritedFlags}}The following options can be passed to any command:
{{flagsUsages .InheritedFlags}}{{end}}`
)
}

View File

@@ -21,15 +21,15 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
)
// TopOptions contains all the options for running the top cli command.
type TopOptions struct{}
var (
topLong = dedent.Dedent(`
topLong = templates.LongDesc(`
Display Resource (CPU/Memory/Storage) usage.
The top command allows you to see the resource consumption for nodes or pods.`)

View File

@@ -20,11 +20,11 @@ import (
"errors"
"io"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/metricsutil"
"k8s.io/kubernetes/pkg/labels"
@@ -40,12 +40,12 @@ type TopNodeOptions struct {
}
var (
topNodeLong = dedent.Dedent(`
topNodeLong = templates.LongDesc(`
Display Resource (CPU/Memory/Storage) usage of nodes.
The top-node command allows you to see the resource consumption of nodes.`)
topNodeExample = dedent.Dedent(`
topNodeExample = templates.Examples(`
# Show metrics for all nodes
kubectl top node

View File

@@ -24,12 +24,12 @@ import (
"k8s.io/kubernetes/pkg/api"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/metricsutil"
"k8s.io/kubernetes/pkg/labels"
"github.com/golang/glog"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
)
@@ -47,7 +47,7 @@ type TopPodOptions struct {
const metricsCreationDelay = 2 * time.Minute
var (
topPodLong = dedent.Dedent(`
topPodLong = templates.LongDesc(`
Display Resource (CPU/Memory/Storage) usage of pods.
The 'top pod' command allows you to see the resource consumption of pods.
@@ -55,7 +55,7 @@ var (
Due to the metrics pipeline delay, they may be unavailable for a few minutes
since pod creation.`)
topPodExample = dedent.Dedent(`
topPodExample = templates.Examples(`
# Show metrics for all pods in the default namespace
kubectl top pod

View File

@@ -0,0 +1,94 @@
/*
Copyright 2016 The Kubernetes Authors.
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
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.
*/
package sanity
import (
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
)
type CmdCheck func(cmd *cobra.Command) []error
var (
AllCmdChecks = []CmdCheck{
CheckLongDesc,
CheckExamples,
}
)
func CheckCmdTree(cmd *cobra.Command, checks []CmdCheck, skip []string) []error {
cmdPath := cmd.CommandPath()
for _, skipCmdPath := range skip {
if cmdPath == skipCmdPath {
fmt.Fprintf(os.Stdout, "-----+ skipping command %s\n", cmdPath)
return []error{}
}
}
errors := []error{}
if cmd.HasSubCommands() {
for _, subCmd := range cmd.Commands() {
errors = append(errors, CheckCmdTree(subCmd, checks, skip)...)
}
}
fmt.Fprintf(os.Stdout, "-----+ checking command %s\n", cmdPath)
for _, check := range checks {
if err := check(cmd); err != nil && len(err) > 0 {
errors = append(errors, err...)
}
}
return errors
}
func CheckLongDesc(cmd *cobra.Command) []error {
cmdPath := cmd.CommandPath()
long := cmd.Long
if len(long) > 0 {
if strings.Trim(long, " \t\n") != long {
return []error{fmt.Errorf(`command %q: long description is not normalized
↳ make sure you are calling templates.LongDesc (from pkg/cmd/templates) before assigning cmd.Long`, cmdPath)}
}
}
return nil
}
func CheckExamples(cmd *cobra.Command) []error {
cmdPath := cmd.CommandPath()
examples := cmd.Example
errors := []error{}
if len(examples) > 0 {
for _, line := range strings.Split(examples, "\n") {
if !strings.HasPrefix(line, templates.Indentation) {
errors = append(errors, fmt.Errorf(`command %q: examples are not normalized
↳ make sure you are calling templates.Examples (from pkg/cmd/templates) before assigning cmd.Example`, cmdPath))
}
if trimmed := strings.TrimSpace(line); strings.HasPrefix(trimmed, "//") {
errors = append(errors, fmt.Errorf(`command %q: we use # to start comments in examples instead of //`, cmdPath))
}
}
}
return errors
}

View File

@@ -0,0 +1,124 @@
/*
Copyright 2016 The Kubernetes Authors.
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
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.
*/
package term
import (
"io"
"os"
"github.com/docker/docker/pkg/term"
wordwrap "github.com/mitchellh/go-wordwrap"
)
type wordWrapWriter struct {
limit uint
writer io.Writer
}
// NewResponsiveWriter creates a Writer that detects the column width of the
// terminal we are in, and adjusts every line width to fit and use recommended
// terminal sizes for better readability. Does proper word wrapping automatically.
// if terminal width >= 120 columns use 120 columns
// if terminal width >= 100 columns use 100 columns
// if terminal width >= 80 columns use 80 columns
// In case we're not in a terminal or if it's smaller than 80 columns width,
// doesn't do any wrapping.
func NewResponsiveWriter(w io.Writer) io.Writer {
file, ok := w.(*os.File)
if !ok {
return w
}
fd := file.Fd()
if !term.IsTerminal(fd) {
return w
}
terminalSize := GetSize(fd)
if terminalSize == nil {
return w
}
var limit uint
switch {
case terminalSize.Width >= 120:
limit = 120
case terminalSize.Width >= 100:
limit = 100
case terminalSize.Width >= 80:
limit = 80
}
return NewWordWrapWriter(w, limit)
}
// NewWordWrapWriter is a Writer that supports a limit of characters on every line
// and does auto word wrapping that respects that limit.
func NewWordWrapWriter(w io.Writer, limit uint) io.Writer {
return &wordWrapWriter{
limit: limit,
writer: w,
}
}
func (w wordWrapWriter) Write(p []byte) (nn int, err error) {
if w.limit == 0 {
return w.writer.Write(p)
}
original := string(p)
wrapped := wordwrap.WrapString(original, w.limit)
return w.writer.Write([]byte(wrapped))
}
// NewPunchCardWriter is a NewWordWrapWriter that limits the line width to 80 columns.
func NewPunchCardWriter(w io.Writer) io.Writer {
return NewWordWrapWriter(w, 80)
}
type maxWidthWriter struct {
maxWidth uint
currentWidth uint
written uint
writer io.Writer
}
// NewMaxWidthWriter is a Writer that supports a limit of characters on every
// line, but doesn't do any word wrapping automatically.
func NewMaxWidthWriter(w io.Writer, maxWidth uint) io.Writer {
return &maxWidthWriter{
maxWidth: maxWidth,
writer: w,
}
}
func (m maxWidthWriter) Write(p []byte) (nn int, err error) {
for _, b := range p {
if m.currentWidth == m.maxWidth {
m.writer.Write([]byte{'\n'})
m.currentWidth = 0
}
if b == '\n' {
m.currentWidth = 0
}
_, err := m.writer.Write([]byte{b})
if err != nil {
return int(m.written), err
}
m.written++
m.currentWidth++
}
return len(p), nil
}

View File

@@ -0,0 +1,98 @@
/*
Copyright 2016 The Kubernetes Authors.
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
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.
*/
package term
import (
"bytes"
"strings"
"testing"
)
const test = "Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube Kube"
func TestWordWrapWriter(t *testing.T) {
testcases := map[string]struct {
input string
maxWidth uint
}{
"max 10": {input: test, maxWidth: 10},
"max 80": {input: test, maxWidth: 80},
"max 120": {input: test, maxWidth: 120},
"max 5000": {input: test, maxWidth: 5000},
}
for k, tc := range testcases {
b := bytes.NewBufferString("")
w := NewWordWrapWriter(b, tc.maxWidth)
_, err := w.Write([]byte(tc.input))
if err != nil {
t.Errorf("%s: Unexpected error: %v", k, err)
}
result := b.String()
if !strings.Contains(result, "Kube") {
t.Errorf("%s: Expected to contain \"Kube\"", k)
}
if len(result) < len(tc.input) {
t.Errorf("%s: Unexpectedly short string, got %d wanted at least %d chars: %q", k, len(result), len(tc.input), result)
}
for _, line := range strings.Split(result, "\n") {
if len(line) > int(tc.maxWidth) {
t.Errorf("%s: Every line must be at most %d chars long, got %d: %q", k, tc.maxWidth, len(line), line)
}
}
for _, word := range strings.Split(result, " ") {
if !strings.Contains(word, "Kube") {
t.Errorf("%s: Unexpected broken word: %q", k, word)
}
}
}
}
func TestMaxWidthWriter(t *testing.T) {
testcases := map[string]struct {
input string
maxWidth uint
}{
"max 10": {input: test, maxWidth: 10},
"max 80": {input: test, maxWidth: 80},
"max 120": {input: test, maxWidth: 120},
"max 5000": {input: test, maxWidth: 5000},
}
for k, tc := range testcases {
b := bytes.NewBufferString("")
w := NewMaxWidthWriter(b, tc.maxWidth)
_, err := w.Write([]byte(tc.input))
if err != nil {
t.Errorf("%s: Unexpected error: %v", k, err)
}
result := b.String()
if !strings.Contains(result, "Kube") {
t.Errorf("%s: Expected to contain \"Kube\"", k)
}
if len(result) < len(tc.input) {
t.Errorf("%s: Unexpectedly short string, got %d wanted at least %d chars: %q", k, len(result), len(tc.input), result)
}
lines := strings.Split(result, "\n")
for i, line := range lines {
if len(line) > int(tc.maxWidth) {
t.Errorf("%s: Every line must be at most %d chars long, got %d: %q", k, tc.maxWidth, len(line), line)
}
if i < len(lines)-1 && len(line) != int(tc.maxWidth) {
t.Errorf("%s: Lines except the last one are expected to be exactly %d chars long, got %d: %q", k, tc.maxWidth, len(line), line)
}
}
}
}

21
vendor/github.com/MakeNowJust/heredoc/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 TSUYUSATO Kitsune
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

53
vendor/github.com/MakeNowJust/heredoc/README.md generated vendored Normal file
View File

@@ -0,0 +1,53 @@
#heredoc [![Build Status](https://drone.io/github.com/MakeNowJust/heredoc/status.png)](https://drone.io/github.com/MakeNowJust/heredoc/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](https://gowalker.org/github.com/MakeNowJust/heredoc)
##About
Package heredoc provides the here-document with keeping indent.
##Install
```console
$ go get github.com/MakeNowJust/heredoc
```
##Import
```go
// usual
import "github.com/MakeNowJust/heredoc"
// shortcuts
import . "github.com/MakeNowJust/heredoc/dot"
```
##Example
```go
package main
import (
"fmt"
. "github.com/MakeNowJust/heredoc/dot"
)
func main() {
fmt.Println(D(`
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, ...
`))
// Output:
// Lorem ipsum dolor sit amet, consectetur adipisicing elit,
// sed do eiusmod tempor incididunt ut labore et dolore magna
// aliqua. Ut enim ad minim veniam, ...
//
}
```
##API Document
- [Go Walker - github.com/MakeNowJust/heredoc](https://gowalker.org/github.com/MakeNowJust/heredoc)
- [Go Walker - github.com/MakeNowJust/heredoc/dot](https://gowalker.org/github.com/MakeNowJust/heredoc/dot)
##License
This software is released under the MIT License, see LICENSE.

89
vendor/github.com/MakeNowJust/heredoc/heredoc.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
// Copyright (c) 2014 TSUYUSATO Kitsune
// This software is released under the MIT License.
// http://opensource.org/licenses/mit-license.php
// Package heredoc provides the here-document with keeping indent.
//
// Golang supports raw-string syntax.
// doc := `
// Foo
// Bar
// `
// But raw-string cannot recognize indent. Thus such content is indented string, equivalent to
// "\n\tFoo\n\tBar\n"
// I dont't want this!
//
// However this problem is solved by package heredoc.
// doc := heredoc.Doc(`
// Foo
// Bar
// `)
// It is equivalent to
// "Foo\nBar\n"
package heredoc
import (
"fmt"
"strings"
"unicode"
)
// heredoc.Doc retutns unindented string as here-document.
//
// Process of making here-document:
// 1. Find most little indent size. (Skip empty lines)
// 2. Remove this indents of lines.
func Doc(raw string) string {
skipFirstLine := false
if raw[0] == '\n' {
raw = raw[1:]
} else {
skipFirstLine = true
}
minIndentSize := int(^uint(0) >> 1) // Max value of type int
lines := strings.Split(raw, "\n")
// 1.
for i, line := range lines {
if i == 0 && skipFirstLine {
continue
}
indentSize := 0
for _, r := range []rune(line) {
if unicode.IsSpace(r) {
indentSize += 1
} else {
break
}
}
if len(line) == indentSize {
if i == len(lines)-1 && indentSize < minIndentSize {
lines[i] = ""
}
} else if indentSize < minIndentSize {
minIndentSize = indentSize
}
}
// 2.
for i, line := range lines {
if i == 0 && skipFirstLine {
continue
}
if len(lines[i]) >= minIndentSize {
lines[i] = line[minIndentSize:]
}
}
return strings.Join(lines, "\n")
}
// heredoc.Docf returns unindented and formatted string as here-document.
// This format is same with package fmt's format.
func Docf(raw string, args ...interface{}) string {
return fmt.Sprintf(Doc(raw), args...)
}

21
vendor/github.com/mitchellh/go-wordwrap/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Mitchell Hashimoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

39
vendor/github.com/mitchellh/go-wordwrap/README.md generated vendored Normal file
View File

@@ -0,0 +1,39 @@
# go-wordwrap
`go-wordwrap` (Golang package: `wordwrap`) is a package for Go that
automatically wraps words into multiple lines. The primary use case for this
is in formatting CLI output, but of course word wrapping is a generally useful
thing to do.
## Installation and Usage
Install using `go get github.com/mitchellh/go-wordwrap`.
Full documentation is available at
http://godoc.org/github.com/mitchellh/go-wordwrap
Below is an example of its usage ignoring errors:
```go
wrapped := wordwrap.WrapString("foo bar baz", 3)
fmt.Println(wrapped)
```
Would output:
```
foo
bar
baz
```
## Word Wrap Algorithm
This library doesn't use any clever algorithm for word wrapping. The wrapping
is actually very naive: whenever there is whitespace or an explicit linebreak.
The goal of this library is for word wrapping CLI output, so the input is
typically pretty well controlled human language. Because of this, the naive
approach typically works just fine.
In the future, we'd like to make the algorithm more advanced. We would do
so without breaking the API.

73
vendor/github.com/mitchellh/go-wordwrap/wordwrap.go generated vendored Normal file
View File

@@ -0,0 +1,73 @@
package wordwrap
import (
"bytes"
"unicode"
)
// WrapString wraps the given string within lim width in characters.
//
// Wrapping is currently naive and only happens at white-space. A future
// version of the library will implement smarter wrapping. This means that
// pathological cases can dramatically reach past the limit, such as a very
// long word.
func WrapString(s string, lim uint) string {
// Initialize a buffer with a slightly larger size to account for breaks
init := make([]byte, 0, len(s))
buf := bytes.NewBuffer(init)
var current uint
var wordBuf, spaceBuf bytes.Buffer
for _, char := range s {
if char == '\n' {
if wordBuf.Len() == 0 {
if current+uint(spaceBuf.Len()) > lim {
current = 0
} else {
current += uint(spaceBuf.Len())
spaceBuf.WriteTo(buf)
}
spaceBuf.Reset()
} else {
current += uint(spaceBuf.Len() + wordBuf.Len())
spaceBuf.WriteTo(buf)
spaceBuf.Reset()
wordBuf.WriteTo(buf)
wordBuf.Reset()
}
buf.WriteRune(char)
current = 0
} else if unicode.IsSpace(char) {
if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
current += uint(spaceBuf.Len() + wordBuf.Len())
spaceBuf.WriteTo(buf)
spaceBuf.Reset()
wordBuf.WriteTo(buf)
wordBuf.Reset()
}
spaceBuf.WriteRune(char)
} else {
wordBuf.WriteRune(char)
if current+uint(spaceBuf.Len()+wordBuf.Len()) > lim && uint(wordBuf.Len()) < lim {
buf.WriteRune('\n')
current = 0
spaceBuf.Reset()
}
}
}
if wordBuf.Len() == 0 {
if current+uint(spaceBuf.Len()) <= lim {
spaceBuf.WriteTo(buf)
}
} else {
spaceBuf.WriteTo(buf)
wordBuf.WriteTo(buf)
}
return buf.String()
}