-
Notifications
You must be signed in to change notification settings - Fork 1
/
portforward.go
143 lines (127 loc) · 3.67 KB
/
portforward.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package kubectl
import (
"flag"
"fmt"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/portforward"
"k8s.io/client-go/transport/spdy"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"sync"
)
type PortForwardAPodRequest struct {
// RestConfig is the kubernetes config
RestConfig *rest.Config
// Pod is the selected pod for this port forwarding
Pod v1.Pod
// LocalPort is the local port that will be selected to expose the PodPort
LocalPort int
// PodPort is the target port for the pod
PodPort int
// Steams configures where to write or read input from
Streams genericclioptions.IOStreams
// StopCh is the channel used to manage the port forward lifecycle
StopCh <-chan struct{}
// ReadyCh communicates when the tunnel is ready to receive traffic
ReadyCh chan struct{}
}
func portForwardAPod(req PortForwardAPodRequest) error {
path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward",
req.Pod.Namespace, req.Pod.Name)
hostIP := strings.TrimLeft(req.RestConfig.Host, "htps:/")
transport, upgrader, err := spdy.RoundTripperFor(req.RestConfig)
if err != nil {
return err
}
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, http.MethodPost, &url.URL{Scheme: "https", Path: path, Host: hostIP})
fw, err := portforward.New(dialer, []string{fmt.Sprintf("%d:%d", req.LocalPort, req.PodPort)}, req.StopCh, req.ReadyCh, req.Streams.Out, req.Streams.ErrOut)
if err != nil {
return err
}
return fw.ForwardPorts()
}
func RunPortForward(pwg *sync.WaitGroup, podname, namespace string, localport, podport int, silent bool) error {
var wg sync.WaitGroup
//defer pwg.Done()
wg.Add(1)
var kubeconfig *string
if home := 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()
// use the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
pwg.Done()
return err
}
// stopCh control the port forwarding lifecycle. When it gets closed the
// port forward will terminate
stopCh := make(chan struct{}, 1)
// readyCh communicate when the port forward is ready to get traffic
readyCh := make(chan struct{})
// stream is used to tell the port forwarder where to place its output or
// where to expect input if needed. For the port forwarding we just need
// the output eventually
stream := genericclioptions.IOStreams{
In: os.Stdin,
Out: os.Stdout,
ErrOut: os.Stderr,
}
if silent {
stream.Out = nil
}
defer func() {
close(stopCh)
}()
// no need here, terminal console will terminate the process
// managing termination signal from the terminal. As you can see the stopCh
// gets closed to gracefully handle its termination.
//sigs := make(chan os.Signal, 1)
//signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
//go func() {
// s := <-sigs
// close(stopCh)
//}()
go func() {
err := portForwardAPod(PortForwardAPodRequest{
RestConfig: config,
Pod: v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podname,
Namespace: namespace,
},
},
LocalPort: localport,
PodPort: podport,
Streams: stream,
StopCh: stopCh,
ReadyCh: readyCh,
})
if err != nil {
panic(err)
}
}()
select {
case <-readyCh:
break
}
pwg.Done()
wg.Wait()
return nil
}
func homeDir() string {
if h := os.Getenv("HOME"); h != "" {
return h
}
return os.Getenv("USERPROFILE") // windows
}