Client-go: Add port-forwarding example

Created on 12 Dec 2016  路  6Comments  路  Source: kubernetes/client-go

This would be really helpful, as I'm coming from another language. In particular, how to come about the dialer param here: https://github.com/kubernetes/client-go/blob/75399f68c88d27167979821ac280936cfb13cfd6/tools/portforward/portforward.go#L111

kinsupport

Most helpful comment

@gtaylor Please try this to obtain the dialer :

import(
        "flag"
    "fmt"
    "os"
    "path/filepath"
        "net/http"
    "net/url"
        "strings"

        "k8s.io/client-go/tools/clientcmd"
        "k8s.io/client-go/tools/portforward"
        "k8s.io/client-go/transport/spdy"
        "k8s.io/client-go/util/homedir"
)

...

var kubeconfig *string
if home := homedir.HomeDir(); home != "" {
    kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
    kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
flag.Parse()

config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
    panic(err)
}
roundTripper, upgrader, err := spdy.RoundTripperFor(config)
if err != nil {
    panic(err)
}

path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", "default", podName)
hostIP := strings.TrimLeft(config.Host, "htps:/")
serverURL := url.URL{Scheme: "https", Path: path, Host: hostIP}

dialer := spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, http.MethodPost, &serverURL)

What about stopChan, readyChan, out and errOut, you can initialize them like this:

import "bytes"

stopChan, readyChan := make(chan struct{}, 1), make(chan struct{}, 1)
out, errOut := new(bytes.Buffer), new(bytes.Buffer)

The usage is following (with error handling simplified, you would probably want to add more context to the error messages, and/or use log messages instead of panics):

forwarder, err := portforward.New(dialer, ports, stopChan, readyChan, out, errOut)
if err != nil {
    panic(err)
}

go func() {
    for range readyChan { // Kubernetes will close this channel when it has something to tell us.
    }
    if len(errOut.String()) != 0 {
        panic(errOut.String())
    } else if len(out.String()) != 0 {
        fmt.Println(out.String())
    }
}()

if err = forwarder.ForwardPorts(); err != nil { // Locks until stopChan is closed.
    panic(err)
}

Just close the stopChan when you don't need this connection anymore.

All 6 comments

You can take a look at how kubectl use the equivalent packages in the main repo: https://github.com/kubernetes/kubernetes/blob/v1.6.0-alpha.0/pkg/kubectl/cmd/portforward.go#L107

This is still unclear. Did you mean we shouldn't use client-go to execute a port-forward ? @caesarxuchao

@gtaylor Please try this to obtain the dialer :

import(
        "flag"
    "fmt"
    "os"
    "path/filepath"
        "net/http"
    "net/url"
        "strings"

        "k8s.io/client-go/tools/clientcmd"
        "k8s.io/client-go/tools/portforward"
        "k8s.io/client-go/transport/spdy"
        "k8s.io/client-go/util/homedir"
)

...

var kubeconfig *string
if home := homedir.HomeDir(); home != "" {
    kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
    kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
}
flag.Parse()

config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
    panic(err)
}
roundTripper, upgrader, err := spdy.RoundTripperFor(config)
if err != nil {
    panic(err)
}

path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", "default", podName)
hostIP := strings.TrimLeft(config.Host, "htps:/")
serverURL := url.URL{Scheme: "https", Path: path, Host: hostIP}

dialer := spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, http.MethodPost, &serverURL)

What about stopChan, readyChan, out and errOut, you can initialize them like this:

import "bytes"

stopChan, readyChan := make(chan struct{}, 1), make(chan struct{}, 1)
out, errOut := new(bytes.Buffer), new(bytes.Buffer)

The usage is following (with error handling simplified, you would probably want to add more context to the error messages, and/or use log messages instead of panics):

forwarder, err := portforward.New(dialer, ports, stopChan, readyChan, out, errOut)
if err != nil {
    panic(err)
}

go func() {
    for range readyChan { // Kubernetes will close this channel when it has something to tell us.
    }
    if len(errOut.String()) != 0 {
        panic(errOut.String())
    } else if len(out.String()) != 0 {
        fmt.Println(out.String())
    }
}()

if err = forwarder.ForwardPorts(); err != nil { // Locks until stopChan is closed.
    panic(err)
}

Just close the stopChan when you don't need this connection anymore.

I am getting an error while trying the above code as error upgrading connection: resource "test" not found

recommended read: #796 if you find any dataraces. I used the safe buffer implementation for the following gist:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

narasago picture narasago  路  3Comments

inetkiller picture inetkiller  路  6Comments

yashbhutwala picture yashbhutwala  路  4Comments

h0tbird picture h0tbird  路  3Comments

tamalsaha picture tamalsaha  路  6Comments