Path: blob/main/dev/preview/previewctl/pkg/k8s/context/k3s/k3s.go
3622 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()8990output, err := cmd.CombinedOutput()91if err != nil {92k.logger.WithError(err).WithField("output", string(output)).Error("failed to install VM SSH keys")93return errors.Wrap(err, string(output))94}95return nil96}9798func (k *ConfigLoader) Load(ctx context.Context) (*api.Config, error) {99if k.client == nil {100err := k.installVMSSHKeys()101if err != nil {102k.logger.Error(err)103return nil, err104}105err = k.connectToHost(ctx, fmt.Sprintf("%s.preview.gitpod-dev.com", k.opts.PreviewName), "2222")106if err != nil {107k.logger.Error(err)108return nil, err109}110111defer func(k *ConfigLoader) {112err := k.Close()113if err != nil {114k.logger.WithFields(logrus.Fields{"err": err}).Error("failed to close client")115return116}117}(k)118}119120return k.getContext(ctx)121}122123func (k *ConfigLoader) getContext(ctx context.Context) (*api.Config, error) {124stdout := new(bytes.Buffer)125stderr := new(bytes.Buffer)126127err := k.client.Run(ctx, catK3sConfigCmd, stdout, stderr)128if err != nil {129if strings.Contains(stderr.String(), "No such file or directory") {130return nil, ErrK3SConfigNotFound131}132133return nil, errors.Wrap(err, stderr.String())134}135136c, err := clientcmd.NewClientConfigFromBytes(stdout.Bytes())137if err != nil {138return nil, err139}140141rc, err := c.RawConfig()142if err != nil {143return nil, err144}145146k3sConfig, err := k8s.RenameConfig(&rc, "default", k.opts.PreviewName)147if err != nil {148return nil, err149}150151k3sConfig.Clusters[k.opts.PreviewName].Server = fmt.Sprintf("https://%s.preview.gitpod-dev.com:6443", k.opts.PreviewName)152153return &rc, nil154}155156func (k *ConfigLoader) connectToHost(ctx context.Context, host, port string) error {157client, err := k.sshClientFactory.Dial(ctx, host, port)158if err != nil {159return err160}161k.client = client162163return nil164}165166func (k *ConfigLoader) Close() error {167if k.client == nil {168return errors.New("attempting to close a nil client")169}170171if err := k.client.Close(); err != nil {172return err173}174175k.client = nil176return nil177}178179180