github.com/argoproj/argo-cd@v1.8.7/util/kube/portforwarder.go (about)

     1  package kube
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"log"
     8  	"net"
     9  	"net/http"
    10  	"os"
    11  
    12  	"github.com/pkg/errors"
    13  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/client-go/kubernetes"
    15  	"k8s.io/client-go/tools/clientcmd"
    16  	"k8s.io/client-go/tools/portforward"
    17  	"k8s.io/client-go/transport/spdy"
    18  
    19  	"github.com/argoproj/argo-cd/util/io"
    20  )
    21  
    22  func PortForward(podSelector string, targetPort int, namespace string) (int, error) {
    23  	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
    24  	loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
    25  	overrides := clientcmd.ConfigOverrides{}
    26  	clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin)
    27  	config, err := clientConfig.ClientConfig()
    28  	if err != nil {
    29  		return -1, err
    30  	}
    31  
    32  	if namespace == "" {
    33  		namespace, _, err = clientConfig.Namespace()
    34  		if err != nil {
    35  			return -1, err
    36  		}
    37  	}
    38  
    39  	clientSet, err := kubernetes.NewForConfig(config)
    40  	if err != nil {
    41  		return -1, err
    42  	}
    43  
    44  	pods, err := clientSet.CoreV1().Pods(namespace).List(context.Background(), v1.ListOptions{
    45  		LabelSelector: podSelector,
    46  	})
    47  	if err != nil {
    48  		return -1, err
    49  	}
    50  
    51  	if len(pods.Items) == 0 {
    52  		return -1, fmt.Errorf("cannot find %s pod", podSelector)
    53  	}
    54  
    55  	url := clientSet.CoreV1().RESTClient().Post().
    56  		Resource("pods").
    57  		Namespace(pods.Items[0].Namespace).
    58  		Name(pods.Items[0].Name).
    59  		SubResource("portforward").URL()
    60  
    61  	transport, upgrader, err := spdy.RoundTripperFor(config)
    62  	if err != nil {
    63  		return -1, errors.Wrap(err, "Could not create round tripper")
    64  	}
    65  	dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", url)
    66  
    67  	readyChan := make(chan struct{}, 1)
    68  	out := new(bytes.Buffer)
    69  	errOut := new(bytes.Buffer)
    70  
    71  	ln, err := net.Listen("tcp", "[::]:0")
    72  	if err != nil {
    73  		return -1, err
    74  	}
    75  	port := ln.Addr().(*net.TCPAddr).Port
    76  	io.Close(ln)
    77  
    78  	forwarder, err := portforward.New(dialer, []string{fmt.Sprintf("%d:%d", port, targetPort)}, context.Background().Done(), readyChan, out, errOut)
    79  	if err != nil {
    80  		return -1, err
    81  	}
    82  
    83  	go func() {
    84  		err = forwarder.ForwardPorts()
    85  		if err != nil {
    86  			log.Fatal(err)
    87  		}
    88  	}()
    89  	for range readyChan {
    90  	}
    91  	if len(errOut.String()) != 0 {
    92  		return -1, fmt.Errorf(errOut.String())
    93  	}
    94  	return port, nil
    95  }