Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/dev/preview/previewctl/pkg/k8s/context/k3s/k3s.go
2506 views
1
// Copyright (c) 2022 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 k3s
6
7
import (
8
"bytes"
9
"context"
10
"fmt"
11
"os"
12
"os/exec"
13
"path/filepath"
14
"strings"
15
16
"github.com/cockroachdb/errors"
17
"github.com/sirupsen/logrus"
18
"golang.org/x/crypto/ssh"
19
"k8s.io/client-go/tools/clientcmd"
20
"k8s.io/client-go/tools/clientcmd/api"
21
22
"github.com/gitpod-io/gitpod/previewctl/pkg/k8s"
23
kctx "github.com/gitpod-io/gitpod/previewctl/pkg/k8s/context"
24
pssh "github.com/gitpod-io/gitpod/previewctl/pkg/ssh"
25
)
26
27
var _ kctx.Loader = (*ConfigLoader)(nil)
28
29
const (
30
k3sConfigPath = "/etc/rancher/k3s/k3s.yaml"
31
catK3sConfigCmd = "sudo cat /etc/rancher/k3s/k3s.yaml"
32
)
33
34
var (
35
ErrK3SConfigNotFound = errors.New("k3s config file not found")
36
)
37
38
type ConfigLoader struct {
39
logger *logrus.Logger
40
41
sshClientFactory pssh.ClientFactory
42
client pssh.Client
43
44
configPath string
45
opts ConfigLoaderOpts
46
}
47
48
type ConfigLoaderOpts struct {
49
Logger *logrus.Logger
50
51
PreviewName string
52
PreviewNamespace string
53
SSHPrivateKeyPath string
54
SSHUser string
55
}
56
57
func New(ctx context.Context, opts ConfigLoaderOpts) (*ConfigLoader, error) {
58
key, err := os.ReadFile(opts.SSHPrivateKeyPath)
59
if err != nil {
60
return nil, err
61
}
62
63
signer, err := ssh.ParsePrivateKey(key)
64
if err != nil {
65
return nil, err
66
}
67
68
config := &ConfigLoader{
69
logger: opts.Logger,
70
sshClientFactory: &pssh.FactoryImplementation{
71
SSHConfig: &ssh.ClientConfig{
72
User: opts.SSHUser,
73
Auth: []ssh.AuthMethod{
74
ssh.PublicKeys(signer),
75
},
76
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
77
},
78
},
79
configPath: k3sConfigPath,
80
opts: opts,
81
}
82
83
return config, nil
84
}
85
86
func (k *ConfigLoader) installVMSSHKeys() error {
87
path := filepath.Join(os.Getenv("LEEWAY_WORKSPACE_ROOT"), "dev/preview/ssh-vm.sh")
88
cmd := exec.Command(path, "-c", "echo success", "-v", k.opts.PreviewName)
89
cmd.Env = os.Environ()
90
return cmd.Run()
91
}
92
93
func (k *ConfigLoader) Load(ctx context.Context) (*api.Config, error) {
94
if k.client == nil {
95
err := k.installVMSSHKeys()
96
if err != nil {
97
k.logger.Error(err)
98
return nil, err
99
}
100
err = k.connectToHost(ctx, fmt.Sprintf("%s.preview.gitpod-dev.com", k.opts.PreviewName), "2222")
101
if err != nil {
102
k.logger.Error(err)
103
return nil, err
104
}
105
106
defer func(k *ConfigLoader) {
107
err := k.Close()
108
if err != nil {
109
k.logger.WithFields(logrus.Fields{"err": err}).Error("failed to close client")
110
return
111
}
112
}(k)
113
}
114
115
return k.getContext(ctx)
116
}
117
118
func (k *ConfigLoader) getContext(ctx context.Context) (*api.Config, error) {
119
stdout := new(bytes.Buffer)
120
stderr := new(bytes.Buffer)
121
122
err := k.client.Run(ctx, catK3sConfigCmd, stdout, stderr)
123
if err != nil {
124
if strings.Contains(stderr.String(), "No such file or directory") {
125
return nil, ErrK3SConfigNotFound
126
}
127
128
return nil, errors.Wrap(err, stderr.String())
129
}
130
131
c, err := clientcmd.NewClientConfigFromBytes(stdout.Bytes())
132
if err != nil {
133
return nil, err
134
}
135
136
rc, err := c.RawConfig()
137
if err != nil {
138
return nil, err
139
}
140
141
k3sConfig, err := k8s.RenameConfig(&rc, "default", k.opts.PreviewName)
142
if err != nil {
143
return nil, err
144
}
145
146
k3sConfig.Clusters[k.opts.PreviewName].Server = fmt.Sprintf("https://%s.preview.gitpod-dev.com:6443", k.opts.PreviewName)
147
148
return &rc, nil
149
}
150
151
func (k *ConfigLoader) connectToHost(ctx context.Context, host, port string) error {
152
client, err := k.sshClientFactory.Dial(ctx, host, port)
153
if err != nil {
154
return err
155
}
156
k.client = client
157
158
return nil
159
}
160
161
func (k *ConfigLoader) Close() error {
162
if k.client == nil {
163
return errors.New("attempting to close a nil client")
164
}
165
166
if err := k.client.Close(); err != nil {
167
return err
168
}
169
170
k.client = nil
171
return nil
172
}
173
174