github.com/pdmccormick/importable-docker-buildx@v0.0.0-20240426161518-e47091289030/driver/kubernetes/execconn/execconn.go (about)

     1  package execconn
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"net"
     7  	"os"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/sirupsen/logrus"
    12  	corev1 "k8s.io/api/core/v1"
    13  	"k8s.io/client-go/kubernetes/scheme"
    14  	"k8s.io/client-go/rest"
    15  	"k8s.io/client-go/tools/remotecommand"
    16  )
    17  
    18  func ExecConn(ctx context.Context, restClient rest.Interface, restConfig *rest.Config, namespace, pod, container string, cmd []string) (net.Conn, error) {
    19  	req := restClient.
    20  		Post().
    21  		Namespace(namespace).
    22  		Resource("pods").
    23  		Name(pod).
    24  		SubResource("exec").
    25  		VersionedParams(&corev1.PodExecOptions{
    26  			Container: container,
    27  			Command:   cmd,
    28  			Stdin:     true,
    29  			Stdout:    true,
    30  			Stderr:    true,
    31  			TTY:       false,
    32  		}, scheme.ParameterCodec)
    33  	exec, err := remotecommand.NewSPDYExecutor(restConfig, "POST", req.URL())
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	stdinR, stdinW := io.Pipe()
    38  	stdoutR, stdoutW := io.Pipe()
    39  	kc := &kubeConn{
    40  		stdin:      stdinW,
    41  		stdout:     stdoutR,
    42  		localAddr:  dummyAddr{network: "dummy", s: "dummy-0"},
    43  		remoteAddr: dummyAddr{network: "dummy", s: "dummy-1"},
    44  	}
    45  	go func() {
    46  		serr := exec.StreamWithContext(ctx, remotecommand.StreamOptions{
    47  			Stdin:  stdinR,
    48  			Stdout: stdoutW,
    49  			Stderr: os.Stderr,
    50  			Tty:    false,
    51  		})
    52  		if serr != nil && serr != context.Canceled {
    53  			logrus.Error(serr)
    54  		}
    55  	}()
    56  	return kc, nil
    57  }
    58  
    59  type kubeConn struct {
    60  	stdin         io.WriteCloser
    61  	stdout        io.ReadCloser
    62  	stdioClosedMu sync.Mutex // for stdinClosed and stdoutClosed
    63  	stdinClosed   bool
    64  	stdoutClosed  bool
    65  	localAddr     net.Addr
    66  	remoteAddr    net.Addr
    67  }
    68  
    69  func (c *kubeConn) Write(p []byte) (int, error) {
    70  	return c.stdin.Write(p)
    71  }
    72  
    73  func (c *kubeConn) Read(p []byte) (int, error) {
    74  	return c.stdout.Read(p)
    75  }
    76  
    77  func (c *kubeConn) CloseWrite() error {
    78  	err := c.stdin.Close()
    79  	c.stdioClosedMu.Lock()
    80  	c.stdinClosed = true
    81  	c.stdioClosedMu.Unlock()
    82  	return err
    83  }
    84  func (c *kubeConn) CloseRead() error {
    85  	err := c.stdout.Close()
    86  	c.stdioClosedMu.Lock()
    87  	c.stdoutClosed = true
    88  	c.stdioClosedMu.Unlock()
    89  	return err
    90  }
    91  
    92  func (c *kubeConn) Close() error {
    93  	var err error
    94  	c.stdioClosedMu.Lock()
    95  	stdinClosed := c.stdinClosed
    96  	c.stdioClosedMu.Unlock()
    97  	if !stdinClosed {
    98  		err = c.CloseWrite()
    99  	}
   100  	c.stdioClosedMu.Lock()
   101  	stdoutClosed := c.stdoutClosed
   102  	c.stdioClosedMu.Unlock()
   103  	if !stdoutClosed {
   104  		err = c.CloseRead()
   105  	}
   106  	return err
   107  }
   108  
   109  func (c *kubeConn) LocalAddr() net.Addr {
   110  	return c.localAddr
   111  }
   112  func (c *kubeConn) RemoteAddr() net.Addr {
   113  	return c.remoteAddr
   114  }
   115  func (c *kubeConn) SetDeadline(t time.Time) error {
   116  	return nil
   117  }
   118  func (c *kubeConn) SetReadDeadline(t time.Time) error {
   119  	return nil
   120  }
   121  func (c *kubeConn) SetWriteDeadline(t time.Time) error {
   122  	return nil
   123  }
   124  
   125  type dummyAddr struct {
   126  	network string
   127  	s       string
   128  }
   129  
   130  func (d dummyAddr) Network() string {
   131  	return d.network
   132  }
   133  
   134  func (d dummyAddr) String() string {
   135  	return d.s
   136  }