mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
142 lines
3.8 KiB
Go
142 lines
3.8 KiB
Go
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
|
|
// Licensed under the GNU Affero General Public License (AGPL).
|
|
// See License-AGPL.txt in the project root for license information.
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/spf13/pflag"
|
|
)
|
|
|
|
var (
|
|
project = pflag.StringP("project", "p", "", "name of the Google project - defaults to what's configured in gcloud")
|
|
kubeconfig = pflag.StringP("kubeconfig", "k", os.Getenv("KUBECONFIG"), "kubeconfig filepath")
|
|
)
|
|
|
|
func main() {
|
|
pflag.Parse()
|
|
|
|
node := pflag.Arg(0)
|
|
if node == "" {
|
|
logrus.Fatalf("usage: %s [--project|-p GoogleProject] [--kubeconfig|-k ~/.kube/config] <nodeName>", os.Args[0])
|
|
}
|
|
|
|
_, err := exec.LookPath("gcloud")
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("gcloud is not available")
|
|
}
|
|
|
|
prj := *project
|
|
if prj == "" {
|
|
out, err := exec.Command("gcloud", "config", "get-value", "project").CombinedOutput()
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("cannot get configured project. Use --project to explicitly set one.")
|
|
}
|
|
prj = strings.TrimSpace(string(out))
|
|
}
|
|
kubecfgfn := *kubeconfig
|
|
if kubecfgfn == "" {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
kubecfgfn = filepath.Join(home, ".kube", "config")
|
|
}
|
|
|
|
serverIP, err := getServerIP(node, prj)
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("cannot get node IP")
|
|
}
|
|
|
|
kubecfg, err := getK3sKubeconfig(node, prj)
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("cannot get kubeconfig")
|
|
}
|
|
|
|
kubecfg = bytes.ReplaceAll(kubecfg, []byte("127.0.0.1"), []byte(serverIP))
|
|
kubecfg = bytes.ReplaceAll(kubecfg, []byte("default"), []byte(node))
|
|
|
|
tmpfile, err := os.CreateTemp("", "kubecfg-*.yaml")
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
_, err = tmpfile.Write(kubecfg)
|
|
if err != nil {
|
|
logrus.WithError(err).Fatal("cannot write temporary kubeconfig")
|
|
}
|
|
tmpfile.Close()
|
|
defer os.Remove(tmpfile.Name())
|
|
|
|
cmd := exec.Command("kubectl", "config", "view", "--flatten", "--merge")
|
|
cmd.Env = append(os.Environ(), fmt.Sprintf("KUBECONFIG=%s:%s", kubecfgfn, tmpfile.Name()))
|
|
res, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
logrus.WithError(err).Error("cannot merge kubeconfig")
|
|
return
|
|
}
|
|
|
|
err = ioutil.WriteFile(kubecfgfn, res, 0644)
|
|
if err != nil {
|
|
logrus.WithError(err).WithField("path", kubecfgfn).Error("cannot write kubeconfig. Dumping combined result:")
|
|
fmt.Println(string(res))
|
|
return
|
|
}
|
|
}
|
|
|
|
func getServerIP(nodeName, project string) (sererIP string, err error) {
|
|
var nfo struct {
|
|
NetworkInterfaces []struct {
|
|
AccessConfigs []struct {
|
|
NatIP string `json:"natIP"`
|
|
} `json:"accessConfigs"`
|
|
} `json:"networkInterfaces"`
|
|
}
|
|
|
|
out, err := exec.Command("gcloud", "compute", "instances", "describe", "--format=json", "--project", project, nodeName).CombinedOutput()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to describe node: %s: %w", string(out), err)
|
|
}
|
|
|
|
err = json.Unmarshal(out, &nfo)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to unmarshal node description: %s: %w", string(out), err)
|
|
}
|
|
|
|
for _, iff := range nfo.NetworkInterfaces {
|
|
for _, n := range iff.AccessConfigs {
|
|
if n.NatIP == "" {
|
|
continue
|
|
}
|
|
|
|
return n.NatIP, nil
|
|
}
|
|
}
|
|
|
|
return "", fmt.Errorf("did not find public IP for node")
|
|
}
|
|
|
|
func getK3sKubeconfig(nodeName, project string) ([]byte, error) {
|
|
res, err := exec.Command("gcloud", "compute", "ssh", "--project", project, "--command", "sudo cat /etc/rancher/k3s/k3s.yaml", nodeName).CombinedOutput()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot ssh into node: %s: %w", string(res), err)
|
|
}
|
|
|
|
// we may have had to login first, i.e. the output might not be the pure kubeconfig file
|
|
idx := bytes.Index(res, []byte("apiVersion:"))
|
|
if idx == -1 {
|
|
return nil, fmt.Errorf("did not find kubeconfig")
|
|
}
|
|
|
|
return res[idx:], nil
|
|
}
|