Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/dev/gpctl/pkg/util/kubernetes.go
2500 views
1
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package util
6
7
import (
8
"bytes"
9
"context"
10
"crypto/tls"
11
"crypto/x509"
12
"fmt"
13
"net/http"
14
"net/url"
15
"strings"
16
"sync"
17
18
"golang.org/x/xerrors"
19
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20
"k8s.io/client-go/kubernetes"
21
"k8s.io/client-go/rest"
22
"k8s.io/client-go/tools/clientcmd"
23
"k8s.io/client-go/tools/portforward"
24
"k8s.io/client-go/transport/spdy"
25
26
wsk8s "github.com/gitpod-io/gitpod/common-go/kubernetes"
27
)
28
29
// GetKubeconfig loads kubernetes connection config from a kubeconfig file
30
func GetKubeconfig(kubeconfig string) (res *rest.Config, namespace string, err error) {
31
cfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
32
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig},
33
&clientcmd.ConfigOverrides{},
34
)
35
namespace, _, err = cfg.Namespace()
36
if err != nil {
37
return nil, "", err
38
}
39
40
res, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
41
if err != nil {
42
return nil, namespace, err
43
}
44
res.RateLimiter = &wsk8s.UnlimitedRateLimiter{}
45
46
return res, namespace, nil
47
}
48
49
// FindAnyPodForComponent returns the first pod we found for a particular component
50
func FindAnyPodForComponent(clientSet kubernetes.Interface, namespace, label string) (podName string, err error) {
51
ps, err := FindPodsForComponent(clientSet, namespace, label)
52
if err != nil {
53
return "", err
54
}
55
return ps[0], nil
56
}
57
58
// FindPodsForcomponent returns all pods we found for a particular component
59
func FindPodsForComponent(clientSet kubernetes.Interface, namespace, label string) ([]string, error) {
60
pods, err := clientSet.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{
61
LabelSelector: fmt.Sprintf("component=%s", label),
62
})
63
if err != nil {
64
return nil, err
65
}
66
if len(pods.Items) == 0 {
67
return nil, xerrors.Errorf("no pod in %s with label component=%s", namespace, label)
68
}
69
70
res := make([]string, len(pods.Items))
71
for i, p := range pods.Items {
72
res[i] = p.Name
73
}
74
return res, nil
75
}
76
77
// ForwardPort establishes a TCP port forwarding to a Kubernetes pod
78
func ForwardPort(ctx context.Context, config *rest.Config, namespace, pod, port string) (readychan chan struct{}, errchan chan error) {
79
errchan = make(chan error, 1)
80
readychan = make(chan struct{}, 1)
81
82
roundTripper, upgrader, err := spdy.RoundTripperFor(config)
83
if err != nil {
84
errchan <- err
85
return
86
}
87
88
path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", namespace, pod)
89
hostIP := strings.TrimPrefix(config.Host, "https://")
90
serverURL := url.URL{Scheme: "https", Path: path, Host: hostIP}
91
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, http.MethodPost, &serverURL)
92
93
stopChan := make(chan struct{}, 1)
94
fwdReadyChan := make(chan struct{}, 1)
95
out, errOut := new(bytes.Buffer), new(bytes.Buffer)
96
forwarder, err := portforward.New(dialer, []string{port}, stopChan, fwdReadyChan, out, errOut)
97
if err != nil {
98
panic(err)
99
}
100
101
var once sync.Once
102
go func() {
103
err := forwarder.ForwardPorts()
104
if err != nil {
105
errchan <- err
106
}
107
once.Do(func() { close(readychan) })
108
}()
109
110
go func() {
111
select {
112
case <-readychan:
113
// we're out of here
114
case <-ctx.Done():
115
close(stopChan)
116
}
117
}()
118
119
go func() {
120
for range fwdReadyChan {
121
}
122
123
if errOut.Len() != 0 {
124
errchan <- xerrors.Errorf(errOut.String())
125
return
126
}
127
128
once.Do(func() { close(readychan) })
129
}()
130
131
return
132
}
133
134
// CertPoolFromSecret creates a x509 cert pool from a Kubernetes secret
135
func CertPoolFromSecret(clientSet kubernetes.Interface, namespace, secretName string, files []string) (cert *x509.CertPool, err error) {
136
secret, err := clientSet.CoreV1().Secrets(namespace).Get(context.Background(), secretName, metav1.GetOptions{})
137
if err != nil {
138
return
139
}
140
cert = x509.NewCertPool()
141
for _, file := range files {
142
certFile := secret.Data[file]
143
144
if !cert.AppendCertsFromPEM(certFile) {
145
return nil, xerrors.Errorf("credentials: failed to append certificates")
146
}
147
}
148
return
149
}
150
151
// CertFromSecret creates a cert from a Kubernetes secret
152
func CertFromSecret(clientSet kubernetes.Interface, namespace, secretName, certFile, keyFile string) (cert tls.Certificate, err error) {
153
secret, err := clientSet.CoreV1().Secrets(namespace).Get(context.Background(), secretName, metav1.GetOptions{})
154
if err != nil {
155
return
156
}
157
certFileB := secret.Data[certFile]
158
keyFileB := secret.Data[keyFile]
159
return tls.X509KeyPair(certFileB, keyFileB)
160
}
161
162