Path: blob/main/dev/preview/previewctl/pkg/k8s/context/k3s/k3s.go
2506 views
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package k3s56import (7"bytes"8"context"9"fmt"10"os"11"os/exec"12"path/filepath"13"strings"1415"github.com/cockroachdb/errors"16"github.com/sirupsen/logrus"17"golang.org/x/crypto/ssh"18"k8s.io/client-go/tools/clientcmd"19"k8s.io/client-go/tools/clientcmd/api"2021"github.com/gitpod-io/gitpod/previewctl/pkg/k8s"22kctx "github.com/gitpod-io/gitpod/previewctl/pkg/k8s/context"23pssh "github.com/gitpod-io/gitpod/previewctl/pkg/ssh"24)2526var _ kctx.Loader = (*ConfigLoader)(nil)2728const (29k3sConfigPath = "/etc/rancher/k3s/k3s.yaml"30catK3sConfigCmd = "sudo cat /etc/rancher/k3s/k3s.yaml"31)3233var (34ErrK3SConfigNotFound = errors.New("k3s config file not found")35)3637type ConfigLoader struct {38logger *logrus.Logger3940sshClientFactory pssh.ClientFactory41client pssh.Client4243configPath string44opts ConfigLoaderOpts45}4647type ConfigLoaderOpts struct {48Logger *logrus.Logger4950PreviewName string51PreviewNamespace string52SSHPrivateKeyPath string53SSHUser string54}5556func New(ctx context.Context, opts ConfigLoaderOpts) (*ConfigLoader, error) {57key, err := os.ReadFile(opts.SSHPrivateKeyPath)58if err != nil {59return nil, err60}6162signer, err := ssh.ParsePrivateKey(key)63if err != nil {64return nil, err65}6667config := &ConfigLoader{68logger: opts.Logger,69sshClientFactory: &pssh.FactoryImplementation{70SSHConfig: &ssh.ClientConfig{71User: opts.SSHUser,72Auth: []ssh.AuthMethod{73ssh.PublicKeys(signer),74},75HostKeyCallback: ssh.InsecureIgnoreHostKey(),76},77},78configPath: k3sConfigPath,79opts: opts,80}8182return config, nil83}8485func (k *ConfigLoader) installVMSSHKeys() error {86path := filepath.Join(os.Getenv("LEEWAY_WORKSPACE_ROOT"), "dev/preview/ssh-vm.sh")87cmd := exec.Command(path, "-c", "echo success", "-v", k.opts.PreviewName)88cmd.Env = os.Environ()89return cmd.Run()90}9192func (k *ConfigLoader) Load(ctx context.Context) (*api.Config, error) {93if k.client == nil {94err := k.installVMSSHKeys()95if err != nil {96k.logger.Error(err)97return nil, err98}99err = k.connectToHost(ctx, fmt.Sprintf("%s.preview.gitpod-dev.com", k.opts.PreviewName), "2222")100if err != nil {101k.logger.Error(err)102return nil, err103}104105defer func(k *ConfigLoader) {106err := k.Close()107if err != nil {108k.logger.WithFields(logrus.Fields{"err": err}).Error("failed to close client")109return110}111}(k)112}113114return k.getContext(ctx)115}116117func (k *ConfigLoader) getContext(ctx context.Context) (*api.Config, error) {118stdout := new(bytes.Buffer)119stderr := new(bytes.Buffer)120121err := k.client.Run(ctx, catK3sConfigCmd, stdout, stderr)122if err != nil {123if strings.Contains(stderr.String(), "No such file or directory") {124return nil, ErrK3SConfigNotFound125}126127return nil, errors.Wrap(err, stderr.String())128}129130c, err := clientcmd.NewClientConfigFromBytes(stdout.Bytes())131if err != nil {132return nil, err133}134135rc, err := c.RawConfig()136if err != nil {137return nil, err138}139140k3sConfig, err := k8s.RenameConfig(&rc, "default", k.opts.PreviewName)141if err != nil {142return nil, err143}144145k3sConfig.Clusters[k.opts.PreviewName].Server = fmt.Sprintf("https://%s.preview.gitpod-dev.com:6443", k.opts.PreviewName)146147return &rc, nil148}149150func (k *ConfigLoader) connectToHost(ctx context.Context, host, port string) error {151client, err := k.sshClientFactory.Dial(ctx, host, port)152if err != nil {153return err154}155k.client = client156157return nil158}159160func (k *ConfigLoader) Close() error {161if k.client == nil {162return errors.New("attempting to close a nil client")163}164165if err := k.client.Close(); err != nil {166return err167}168169k.client = nil170return nil171}172173174