github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/cmd/testhelpers/kube_test_helpers.go (about)

     1  package testhelpers
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"net"
     7  	"net/http"
     8  	"net/url"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/jenkins-x/jx-logging/pkg/log"
    13  	"github.com/olli-ai/jx/v2/pkg/cmd/clients"
    14  	"github.com/pkg/errors"
    15  	core_v1 "k8s.io/api/core/v1"
    16  	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  	"k8s.io/client-go/kubernetes"
    18  	"k8s.io/client-go/tools/portforward"
    19  	"k8s.io/client-go/transport/spdy"
    20  )
    21  
    22  // GetFreePort asks the kernel for a free open port that is ready to use.
    23  func GetFreePort() (int, error) {
    24  	addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
    25  	if err != nil {
    26  		return 0, err
    27  	}
    28  
    29  	l, err := net.ListenTCP("tcp", addr)
    30  	if err != nil {
    31  		return 0, err
    32  	}
    33  	defer func() {
    34  		_ = l.Close()
    35  	}()
    36  	return l.Addr().(*net.TCPAddr).Port, nil
    37  }
    38  
    39  // WaitForPod waits for the specified duration for the given Pod to get into the 'Running' status.
    40  func WaitForPod(pod *core_v1.Pod, namespace string, labels map[string]string, timeout time.Duration, kubeClient kubernetes.Interface) error {
    41  	status := pod.Status
    42  	watch, err := kubeClient.CoreV1().Pods(namespace).Watch(meta_v1.ListOptions{
    43  		Watch:           true,
    44  		ResourceVersion: pod.ResourceVersion,
    45  		LabelSelector:   LabelSelector(labels),
    46  	})
    47  	if err != nil {
    48  		return errors.Wrapf(err, "unable to create watch for pod '%s'", pod.Name)
    49  	}
    50  
    51  	func() {
    52  		for {
    53  			select {
    54  			case events, ok := <-watch.ResultChan():
    55  				if !ok {
    56  					return
    57  				}
    58  				pod := events.Object.(*core_v1.Pod)
    59  				log.Logger().Debugf("Pod status: %v", pod.Status.Phase)
    60  				status = pod.Status
    61  				if pod.Status.Phase != core_v1.PodPending {
    62  					watch.Stop()
    63  				}
    64  			case <-time.After(timeout):
    65  				log.Logger().Debugf("timeout to wait for pod active")
    66  				watch.Stop()
    67  			}
    68  		}
    69  	}()
    70  	if status.Phase != core_v1.PodRunning {
    71  		return errors.Errorf("Pod '%s' should be running", pod.Name)
    72  	}
    73  	return nil
    74  }
    75  
    76  // PortForward port forwards the container port of the specified pod in the given namespace to the specified local forwarding port.
    77  // The functions returns a stop channel to stop port forwarding.
    78  func PortForward(namespace string, podName string, containerPort string, forwardPort string, factory clients.Factory) (chan struct{}, error) {
    79  	config, err := factory.CreateKubeConfig()
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	roundTripper, upgrader, err := spdy.RoundTripperFor(config)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", namespace, podName)
    90  	hostIP := strings.TrimLeft(config.Host, "https:/")
    91  	serverURL := url.URL{Scheme: "https", Path: path, Host: hostIP}
    92  
    93  	dialer := spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, http.MethodPost, &serverURL)
    94  
    95  	stopChan, readyChan := make(chan struct{}, 1), make(chan struct{}, 1)
    96  	out, errOut := new(bytes.Buffer), new(bytes.Buffer)
    97  
    98  	forwarder, err := portforward.New(dialer, []string{fmt.Sprintf("%s:%s", forwardPort, containerPort)}, stopChan, readyChan, out, errOut)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	go func() {
   104  		for range readyChan { // Kubernetes will close this channel when it has something to tell us.
   105  		}
   106  		if len(errOut.String()) != 0 {
   107  			log.Logger().Error(errOut.String())
   108  		} else if len(out.String()) != 0 {
   109  			log.Logger().Info(out.String())
   110  		}
   111  	}()
   112  
   113  	go func() {
   114  		err := forwarder.ForwardPorts()
   115  		if err != nil {
   116  			log.Logger().Errorf("error during port forwarding: %s", errOut.String())
   117  		}
   118  	}()
   119  
   120  	return stopChan, err
   121  }
   122  
   123  // LabelSelector builds a Kubernetes label selector from the specified map.
   124  func LabelSelector(labels map[string]string) string {
   125  	selector := ""
   126  	for k, v := range labels {
   127  		selector = selector + fmt.Sprintf("%s=%s,", k, v)
   128  	}
   129  	selector = strings.TrimRight(selector, ",")
   130  	return selector
   131  }