mirror of https://github.com/k3s-io/k3s
Merge pull request #59705 from phsiao/15180_port_forward_with_resource_name
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. kubectl port-forward allows using resource name to select a matching pod **What this PR does / why we need it**: #15180 describes use cases that port-foward should use resource name for selecting a pod. **Which issue(s) this PR fixes**: Add support so resource/name can be used to select a pod. **Special notes for your reviewer**: I decided to reuse `AttachablePodForObject` to select a pod using resource name, and extended it to support Service (which it did not). I think that should not be a problem, and may help improve attach's use case. If it makes more sense to fork the function I'd be happy to do so. The practice of waiting for pods to become ready is also copied over. In keeping the change to minimal, I also decided to resolve pod from resource name in Complete(), following the pattern in attach. **Release note**: ```release-note kubectl port-forward now allows using resource name (e.g., deployment/www) to select a matching pod, as well as allows the use of --pod-running-timeout to wait till at least one pod is running. kubectl port-forward no longer support deprecated -p flag ```pull/6/head
commit
1d97b6a4f1
|
@ -23,6 +23,7 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
|
@ -51,15 +52,32 @@ type PortForwardOptions struct {
|
|||
}
|
||||
|
||||
var (
|
||||
portforwardLong = templates.LongDesc(i18n.T(`
|
||||
Forward one or more local ports to a pod.
|
||||
|
||||
Use resource type/name such as deployment/mydeployment to select a pod. Resource type defaults to 'pod' if omitted.
|
||||
|
||||
If there are multiple pods matching the criteria, a pod will be selected automatically. The
|
||||
forwarding session ends when the selected pod terminates, and rerun of the command is needed
|
||||
to resume forwarding.`))
|
||||
|
||||
portforwardExample = templates.Examples(i18n.T(`
|
||||
# Listen on ports 5000 and 6000 locally, forwarding data to/from ports 5000 and 6000 in the pod
|
||||
kubectl port-forward mypod 5000 6000
|
||||
kubectl port-forward pod/mypod 5000 6000
|
||||
|
||||
# Listen on ports 5000 and 6000 locally, forwarding data to/from ports 5000 and 6000 in a pod selected by the deployment
|
||||
kubectl port-forward deployment/mydeployment 5000 6000
|
||||
|
||||
# Listen on port 8888 locally, forwarding to 5000 in the pod
|
||||
kubectl port-forward mypod 8888:5000
|
||||
kubectl port-forward pod/mypod 8888:5000
|
||||
|
||||
# Listen on a random port locally, forwarding to 5000 in the pod
|
||||
kubectl port-forward mypod :5000`))
|
||||
kubectl port-forward pod/mypod :5000`))
|
||||
)
|
||||
|
||||
const (
|
||||
// Amount of time to wait until at least one pod is running
|
||||
defaultPodPortForwardWaitTimeout = 60 * time.Second
|
||||
)
|
||||
|
||||
func NewCmdPortForward(f cmdutil.Factory, cmdOut, cmdErr io.Writer) *cobra.Command {
|
||||
|
@ -70,10 +88,10 @@ func NewCmdPortForward(f cmdutil.Factory, cmdOut, cmdErr io.Writer) *cobra.Comma
|
|||
},
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "port-forward POD [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]",
|
||||
Use: "port-forward TYPE/NAME [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: i18n.T("Forward one or more local ports to a pod"),
|
||||
Long: "Forward one or more local ports to a pod.",
|
||||
Long: portforwardLong,
|
||||
Example: portforwardExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := opts.Complete(f, cmd, args); err != nil {
|
||||
|
@ -87,7 +105,7 @@ func NewCmdPortForward(f cmdutil.Factory, cmdOut, cmdErr io.Writer) *cobra.Comma
|
|||
}
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringP("pod", "p", "", "Pod name")
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodPortForwardWaitTimeout)
|
||||
// TODO support UID
|
||||
return cmd
|
||||
}
|
||||
|
@ -116,17 +134,8 @@ func (f *defaultPortForwarder) ForwardPorts(method string, url *url.URL, opts Po
|
|||
// Complete completes all the required options for port-forward cmd.
|
||||
func (o *PortForwardOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
o.PodName = cmdutil.GetFlagString(cmd, "pod")
|
||||
if len(o.PodName) == 0 && len(args) == 0 {
|
||||
return cmdutil.UsageErrorf(cmd, "POD is required for port-forward")
|
||||
}
|
||||
|
||||
if len(o.PodName) != 0 {
|
||||
printDeprecationWarning("port-forward POD", "-p POD")
|
||||
o.Ports = args
|
||||
} else {
|
||||
o.PodName = args[0]
|
||||
o.Ports = args[1:]
|
||||
if len(args) < 2 {
|
||||
return cmdutil.UsageErrorf(cmd, "TYPE/NAME and list of ports are required for port-forward")
|
||||
}
|
||||
|
||||
o.Namespace, _, err = f.DefaultNamespace()
|
||||
|
@ -134,6 +143,32 @@ func (o *PortForwardOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg
|
|||
return err
|
||||
}
|
||||
|
||||
builder := f.NewBuilder().
|
||||
Internal().
|
||||
ContinueOnError().
|
||||
NamespaceParam(o.Namespace).DefaultNamespace()
|
||||
|
||||
getPodTimeout, err := cmdutil.GetPodRunningTimeoutFlag(cmd)
|
||||
if err != nil {
|
||||
return cmdutil.UsageErrorf(cmd, err.Error())
|
||||
}
|
||||
|
||||
resourceName := args[0]
|
||||
builder.ResourceNames("pods", resourceName)
|
||||
|
||||
obj, err := builder.Do().Object()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
forwardablePod, err := f.AttachablePodForObject(obj, getPodTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.PodName = forwardablePod.Name
|
||||
o.Ports = args[1:]
|
||||
|
||||
clientset, err := f.ClientSet()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -157,7 +192,7 @@ func (o *PortForwardOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg
|
|||
// Validate validates all the required options for port-forward cmd.
|
||||
func (o PortForwardOptions) Validate() error {
|
||||
if len(o.PodName) == 0 {
|
||||
return fmt.Errorf("pod name must be specified")
|
||||
return fmt.Errorf("pod name or resource type/name must be specified")
|
||||
}
|
||||
|
||||
if len(o.Ports) < 1 {
|
||||
|
|
|
@ -70,6 +70,7 @@ func testPortForward(t *testing.T, flags map[string]string, args []string) {
|
|||
var err error
|
||||
f, tf, codec, ns := cmdtesting.NewAPIFactory()
|
||||
tf.Client = &fake.RESTClient{
|
||||
VersionedAPIPath: "/api/v1",
|
||||
GroupVersion: schema.GroupVersion{Group: ""},
|
||||
NegotiatedSerializer: ns,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
|
@ -131,7 +132,3 @@ func testPortForward(t *testing.T, flags map[string]string, args []string) {
|
|||
func TestPortForward(t *testing.T) {
|
||||
testPortForward(t, nil, []string{"foo", ":5000", ":1000"})
|
||||
}
|
||||
|
||||
func TestPortForwardWithPFlag(t *testing.T) {
|
||||
testPortForward(t, map[string]string{"pod": "foo"}, []string{":5000", ":1000"})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue