Kubectl exec support resource/name format
This commit is contained in:
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
dockerterm "github.com/docker/docker/pkg/term"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -27,10 +28,12 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/interrupt"
|
||||
@@ -40,27 +43,34 @@ import (
|
||||
|
||||
var (
|
||||
execExample = templates.Examples(i18n.T(`
|
||||
# Get output from running 'date' from pod 123456-7890, using the first container by default
|
||||
kubectl exec 123456-7890 date
|
||||
# Get output from running 'date' command from pod mypod, using the first container by default
|
||||
kubectl exec mypod date
|
||||
|
||||
# Get output from running 'date' in ruby-container from pod 123456-7890
|
||||
kubectl exec 123456-7890 -c ruby-container date
|
||||
# Get output from running 'date' command in ruby-container from pod mypod
|
||||
kubectl exec mypod -c ruby-container date
|
||||
|
||||
# Switch to raw terminal mode, sends stdin to 'bash' in ruby-container from pod 123456-7890
|
||||
# Switch to raw terminal mode, sends stdin to 'bash' in ruby-container from pod mypod
|
||||
# and sends stdout/stderr from 'bash' back to the client
|
||||
kubectl exec 123456-7890 -c ruby-container -i -t -- bash -il
|
||||
kubectl exec mypod -c ruby-container -i -t -- bash -il
|
||||
|
||||
# List contents of /usr from the first container of pod 123456-7890 and sort by modification time.
|
||||
# List contents of /usr from the first container of pod mypod and sort by modification time.
|
||||
# If the command you want to execute in the pod has any flags in common (e.g. -i),
|
||||
# you must use two dashes (--) to separate your command's flags/arguments.
|
||||
# Also note, do not surround your command and its flags/arguments with quotes
|
||||
# unless that is how you would execute it normally (i.e., do ls -t /usr, not "ls -t /usr").
|
||||
kubectl exec 123456-7890 -i -t -- ls -t /usr
|
||||
kubectl exec mypod -i -t -- ls -t /usr
|
||||
|
||||
# Get output from running 'date' command from the first pod of the deployment mydeployment, using the first container by default
|
||||
kubectl exec deploy/mydeployment date
|
||||
|
||||
# Get output from running 'date' command from the first pod of the service myservice, using the first container by default
|
||||
kubectl exec svc/myservice date
|
||||
`))
|
||||
)
|
||||
|
||||
const (
|
||||
execUsageStr = "expected 'exec POD_NAME COMMAND [ARG1] [ARG2] ... [ARGN]'.\nPOD_NAME and COMMAND are required arguments for the exec command"
|
||||
execUsageStr = "expected 'exec (POD | TYPE/NAME) COMMAND [ARG1] [ARG2] ... [ARGN]'.\nPOD or TYPE/NAME and COMMAND are required arguments for the exec command"
|
||||
defaultPodExecTimeout = 60 * time.Second
|
||||
)
|
||||
|
||||
func NewCmdExec(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
|
||||
@@ -72,7 +82,7 @@ func NewCmdExec(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C
|
||||
Executor: &DefaultRemoteExecutor{},
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "exec POD [-c CONTAINER] -- COMMAND [args...]",
|
||||
Use: "exec (POD | TYPE/NAME) [-c CONTAINER] -- COMMAND [args...]",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("Execute a command in a container"),
|
||||
Long: "Execute a command in a container.",
|
||||
@@ -84,6 +94,7 @@ func NewCmdExec(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.C
|
||||
cmdutil.CheckErr(options.Run())
|
||||
},
|
||||
}
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodExecTimeout)
|
||||
cmd.Flags().StringVarP(&options.PodName, "pod", "p", options.PodName, "Pod name")
|
||||
cmd.Flags().MarkDeprecated("pod", "This flag is deprecated and will be removed in future. Use exec POD_NAME instead.")
|
||||
// TODO support UID
|
||||
@@ -137,14 +148,21 @@ type StreamOptions struct {
|
||||
type ExecOptions struct {
|
||||
StreamOptions
|
||||
|
||||
Command []string
|
||||
ResourceName string
|
||||
Command []string
|
||||
|
||||
FullCmdName string
|
||||
SuggestedCmdUsage string
|
||||
|
||||
Executor RemoteExecutor
|
||||
PodClient coreclient.PodsGetter
|
||||
Config *restclient.Config
|
||||
Builder func() *resource.Builder
|
||||
ExecutablePodFn polymorphichelpers.AttachablePodForObjectFunc
|
||||
restClientGetter genericclioptions.RESTClientGetter
|
||||
|
||||
Pod *corev1.Pod
|
||||
Executor RemoteExecutor
|
||||
PodClient coreclient.PodsGetter
|
||||
GetPodTimeout time.Duration
|
||||
Config *restclient.Config
|
||||
}
|
||||
|
||||
// Complete verifies command line arguments and loads data from the command environment
|
||||
@@ -159,32 +177,39 @@ func (p *ExecOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn []s
|
||||
}
|
||||
p.Command = argsIn
|
||||
} else {
|
||||
p.PodName = argsIn[0]
|
||||
p.ResourceName = argsIn[0]
|
||||
p.Command = argsIn[1:]
|
||||
if len(p.Command) < 1 {
|
||||
return cmdutil.UsageErrorf(cmd, execUsageStr)
|
||||
}
|
||||
}
|
||||
|
||||
namespace, _, err := f.ToRawKubeConfigLoader().Namespace()
|
||||
var err error
|
||||
|
||||
p.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Namespace = namespace
|
||||
|
||||
p.ExecutablePodFn = polymorphichelpers.AttachablePodForObjectFn
|
||||
|
||||
p.GetPodTimeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
|
||||
if err != nil {
|
||||
return cmdutil.UsageErrorf(cmd, err.Error())
|
||||
}
|
||||
|
||||
p.Builder = f.NewBuilder
|
||||
p.restClientGetter = f
|
||||
|
||||
cmdParent := cmd.Parent()
|
||||
if cmdParent != nil {
|
||||
p.FullCmdName = cmdParent.CommandPath()
|
||||
}
|
||||
if len(p.FullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "describe") {
|
||||
p.SuggestedCmdUsage = fmt.Sprintf("Use '%s describe pod/%s -n %s' to see all of the containers in this pod.", p.FullCmdName, p.PodName, p.Namespace)
|
||||
p.SuggestedCmdUsage = fmt.Sprintf("Use '%s describe %s -n %s' to see all of the containers in this pod.", p.FullCmdName, p.ResourceName, p.Namespace)
|
||||
}
|
||||
|
||||
config, err := f.ToRESTConfig()
|
||||
p.Config, err = f.ToRESTConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Config = config
|
||||
|
||||
clientset, err := f.KubernetesClientSet()
|
||||
if err != nil {
|
||||
@@ -197,8 +222,8 @@ func (p *ExecOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn []s
|
||||
|
||||
// Validate checks that the provided exec options are specified.
|
||||
func (p *ExecOptions) Validate() error {
|
||||
if len(p.PodName) == 0 {
|
||||
return fmt.Errorf("pod name must be specified")
|
||||
if len(p.PodName) == 0 && len(p.ResourceName) == 0 {
|
||||
return fmt.Errorf("pod or type/name must be specified")
|
||||
}
|
||||
if len(p.Command) == 0 {
|
||||
return fmt.Errorf("you must specify at least one command for the container")
|
||||
@@ -206,9 +231,6 @@ func (p *ExecOptions) Validate() error {
|
||||
if p.Out == nil || p.ErrOut == nil {
|
||||
return fmt.Errorf("both output and error output must be provided")
|
||||
}
|
||||
if p.Executor == nil || p.PodClient == nil || p.Config == nil {
|
||||
return fmt.Errorf("client, client config, and executor must be provided")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -266,11 +288,33 @@ func (o *StreamOptions) SetupTTY() term.TTY {
|
||||
|
||||
// Run executes a validated remote execution against a pod.
|
||||
func (p *ExecOptions) Run() error {
|
||||
pod, err := p.PodClient.Pods(p.Namespace).Get(p.PodName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
var err error
|
||||
// we still need legacy pod getter when PodName in ExecOptions struct is provided,
|
||||
// since there are any other command run this function by providing Podname with PodsGetter
|
||||
// and without resource builder, eg: `kubectl cp`.
|
||||
if len(p.PodName) != 0 {
|
||||
p.Pod, err = p.PodClient.Pods(p.Namespace).Get(p.PodName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
builder := p.Builder().
|
||||
WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
|
||||
NamespaceParam(p.Namespace).DefaultNamespace().ResourceNames("pods", p.ResourceName)
|
||||
|
||||
obj, err := builder.Do().Object()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Pod, err = p.ExecutablePodFn(p.restClientGetter, obj, p.GetPodTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pod := p.Pod
|
||||
|
||||
if pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed {
|
||||
return fmt.Errorf("cannot exec into a container in a completed pod; current phase is %s", pod.Status.Phase)
|
||||
}
|
||||
|
Reference in New Issue
Block a user