feat: cmd status show remote-kubeconfig name (#695)

* feat: cmd status show remote-kubeconfig name
This commit is contained in:
naison
2025-08-10 14:23:01 +08:00
committed by GitHub
parent d2a6d78331
commit d40e69e781
15 changed files with 238 additions and 113 deletions

View File

@@ -114,7 +114,7 @@ func (f *warp) ToRawKubeConfigLoader() clientcmd.ClientConfig {
f.KubeConfig = ptr.To(strings.Replace(*f.KubeConfig, "~", home, 1))
}
if strings.TrimSpace(f.KubeconfigJson) != "" {
path, err := util.ConvertToTempKubeconfigFile([]byte(f.KubeconfigJson))
path, err := util.ConvertToTempKubeconfigFile([]byte(f.KubeconfigJson), "")
if err == nil {
f.KubeConfig = ptr.To(path)
}

View File

@@ -89,15 +89,14 @@ func CmdRun(f cmdutil.Factory) *cobra.Command {
if err != nil {
return err
}
if sshConf.IsEmpty() {
return nil
}
bytes, _, err := util.ConvertToKubeConfigBytes(f)
if err != nil {
return err
}
file, err := util.ConvertToTempKubeconfigFile(bytes)
if err != nil {
return err
}
return pkgssh.SshJumpAndSetEnv(cmd.Context(), sshConf, file, false)
return pkgssh.SshJumpAndSetEnv(cmd.Context(), sshConf, bytes, false)
},
RunE: func(cmd *cobra.Command, args []string) error {
options.Workload = args[0]

View File

@@ -252,20 +252,19 @@ func GetConnectionIDByConfig(cmd *cobra.Command, config Config) (string, error)
_ = flags.Set(flag.Name, value)
return nil
})
bytes, ns, err := util.ConvertToKubeConfigBytes(f)
if err != nil {
return "", err
}
file, err := util.ConvertToTempKubeconfigFile(bytes)
kubeConfigBytes, ns, err := util.ConvertToKubeConfigBytes(f)
if err != nil {
return "", err
}
var file string
defer os.Remove(file)
if !sshConf.IsEmpty() {
file, err = pkgssh.SshJump(cmd.Context(), sshConf, file, false)
if err != nil {
return "", err
}
file, err = pkgssh.SshJump(cmd.Context(), sshConf, kubeConfigBytes, false)
} else {
file, err = util.ConvertToTempKubeconfigFile(kubeConfigBytes, "")
}
if err != nil {
return "", err
}
var c = &handler.ConnectOptions{}
err = c.InitClient(util.InitFactoryByPath(file, ns))
@@ -297,7 +296,7 @@ func ParseArgs(cmd *cobra.Command, conf *Config) error {
return nil
}
file, err := util.ConvertToKubeconfigFile([]byte(str), filepath.Join(config.GetTempPath(), conf.Name))
file, err := util.ConvertToTempKubeconfigFile([]byte(str), filepath.Join(config.GetTempPath(), conf.Name))
if err != nil {
return err
}

View File

@@ -47,7 +47,7 @@ func (svr *Server) Connect(resp rpc.Daemon_ConnectServer) (err error) {
ImagePullSecretName: req.ImagePullSecretName,
Request: proto.Clone(req).(*rpc.ConnectRequest),
}
file, err := util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes))
file, err := util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes), "")
if err != nil {
return err
}
@@ -95,10 +95,6 @@ func (svr *Server) redirectConnectToSudoDaemon(req *rpc.ConnectRequest, resp rpc
return errors.Wrap(err, "sudo daemon not start")
}
var sshConf = ssh.ParseSshFromRPC(req.SshJump)
file, err := util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes))
if err != nil {
return err
}
sshCtx, sshCancel := context.WithCancel(context.Background())
sshCtx = plog.WithLogger(sshCtx, logger)
defer plog.WithoutLogger(sshCtx)
@@ -109,11 +105,28 @@ func (svr *Server) redirectConnectToSudoDaemon(req *rpc.ConnectRequest, resp rpc
OriginKubeconfigPath: req.OriginKubeconfigPath,
Request: proto.Clone(req).(*rpc.ConnectRequest),
}
var file string
connect.AddRolloutFunc(func() error {
sshCancel()
_ = os.Remove(file)
return nil
})
if !sshConf.IsEmpty() {
file, err = ssh.SshJump(sshCtx, sshConf, []byte(req.KubeconfigBytes), true)
if err != nil {
return err
}
if sshConf.RemoteKubeconfig != "" {
connect.OriginKubeconfigPath = file
}
} else {
file, err = util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes), "")
if err != nil {
return err
}
}
defer func() {
if err != nil {
connect.Cleanup(plog.WithLogger(context.Background(), logger))
@@ -134,13 +147,6 @@ func (svr *Server) redirectConnectToSudoDaemon(req *rpc.ConnectRequest, resp rpc
sshCancel()
}
}()
if !sshConf.IsEmpty() {
file, err = ssh.SshJump(sshCtx, sshConf, file, true)
if err != nil {
return err
}
}
err = connect.InitClient(util.InitFactoryByPath(file, req.Namespace))
if err != nil {
return err

View File

@@ -112,17 +112,17 @@ func (svr *Server) Disconnect(resp rpc.Daemon_DisconnectServer) (err error) {
}
func disconnectByKubeconfig(ctx context.Context, svr *Server, kubeconfigBytes string, ns string, jump *rpc.SshJump) error {
file, err := util.ConvertToTempKubeconfigFile([]byte(kubeconfigBytes))
if err != nil {
return err
}
defer os.Remove(file)
var file string
var err error
var sshConf = ssh.ParseSshFromRPC(jump)
if !sshConf.IsEmpty() {
file, err = ssh.SshJump(ctx, sshConf, file, false)
if err != nil {
return err
}
file, err = ssh.SshJump(ctx, sshConf, []byte(kubeconfigBytes), false)
} else {
file, err = util.ConvertToTempKubeconfigFile([]byte(kubeconfigBytes), "")
}
defer os.Remove(file)
if err != nil {
return err
}
connect := &handler.ConnectOptions{}
err = connect.InitClient(util.InitFactoryByPath(file, ns))

View File

@@ -36,16 +36,14 @@ func (svr *Server) Proxy(resp rpc.Daemon_ProxyServer) (err error) {
var sshConf = ssh.ParseSshFromRPC(req.SshJump)
var file string
file, err = util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes))
if err != nil {
return err
}
defer os.Remove(file)
if !sshConf.IsEmpty() {
file, err = ssh.SshJump(ctx, sshConf, file, false)
if err != nil {
return err
}
file, err = ssh.SshJump(ctx, sshConf, []byte(req.KubeconfigBytes), false)
} else {
file, err = util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes), "")
}
if err != nil {
return err
}
connect := &handler.ConnectOptions{
ExtraRouteInfo: *handler.ParseExtraRouteFromRPC(req.ExtraRoute),

View File

@@ -20,18 +20,17 @@ func (svr *Server) Reset(resp rpc.Daemon_ResetServer) error {
}
logger := plog.GetLoggerForClient(int32(log.InfoLevel), io.MultiWriter(newResetWarp(resp), svr.LogFile))
file, err := util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes))
if err != nil {
return err
}
var file string
defer os.Remove(file)
var sshConf = ssh.ParseSshFromRPC(req.SshJump)
var ctx = plog.WithLogger(resp.Context(), logger)
if !sshConf.IsEmpty() {
file, err = ssh.SshJump(ctx, sshConf, file, false)
if err != nil {
return err
}
file, err = ssh.SshJump(ctx, sshConf, []byte(req.KubeconfigBytes), false)
} else {
file, err = util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes), "")
}
if err != nil {
return err
}
connect := &handler.ConnectOptions{}
err = connect.InitClient(util.InitFactoryByPath(file, req.Namespace))

View File

@@ -89,26 +89,25 @@ func (svr *Server) Sync(resp rpc.Daemon_SyncServer) (err error) {
LocalDir: req.LocalDir,
RemoteDir: req.RemoteDir,
}
file, err := util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes))
if err != nil {
return err
}
defer func() {
if err != nil {
_ = options.Cleanup(sshCtx)
sshFunc()
}
}()
var file string
options.AddRollbackFunc(func() error {
sshFunc()
_ = os.Remove(file)
return nil
})
if !sshConf.IsEmpty() {
file, err = ssh.SshJump(sshCtx, sshConf, file, false)
if err != nil {
return err
}
file, err = ssh.SshJump(sshCtx, sshConf, []byte(req.KubeconfigBytes), false)
} else {
file, err = util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes), "")
}
if err != nil {
return err
}
f := util.InitFactoryByPath(file, req.Namespace)
err = options.InitClient(f)

View File

@@ -24,18 +24,17 @@ func (svr *Server) Uninstall(resp rpc.Daemon_UninstallServer) (err error) {
}
logger := plog.GetLoggerForClient(int32(log.InfoLevel), io.MultiWriter(newUninstallWarp(resp), svr.LogFile))
file, err := util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes))
if err != nil {
return err
}
var file string
defer os.Remove(file)
var sshConf = ssh.ParseSshFromRPC(req.SshJump)
var ctx = plog.WithLogger(resp.Context(), logger)
if !sshConf.IsEmpty() {
file, err = ssh.SshJump(ctx, sshConf, file, false)
if err != nil {
return err
}
file, err = ssh.SshJump(ctx, sshConf, []byte(req.KubeconfigBytes), false)
} else {
file, err = util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes), "")
}
if err != nil {
return err
}
factory := util.InitFactoryByPath(file, req.Namespace)
clientset, err := factory.KubernetesClientSet()

View File

@@ -51,6 +51,31 @@ func (conf SshConfig) Clone() SshConfig {
}
}
func (conf *SshConfig) GenKubeconfigIdentify() string {
var prefix string
if conf.ConfigAlias != "" {
prefix = conf.ConfigAlias
} else if conf.Addr != "" {
if host, port, err := net.SplitHostPort(conf.Addr); err == nil {
prefix = fmt.Sprintf("%s_%s", IPToFilename(host), port)
} else {
prefix = IPToFilename(conf.Addr)
}
} else if conf.Jump != "" {
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
var sshConf = &SshConfig{}
AddSshFlags(flags, sshConf)
_ = flags.Parse(strings.Split(conf.Jump, " "))
prefix = sshConf.GenKubeconfigIdentify()
}
if prefix == "" {
return filepath.Base(conf.RemoteKubeconfig)
}
return fmt.Sprintf("%s_%s", prefix, filepath.Base(conf.RemoteKubeconfig))
}
func ParseSshFromRPC(sshJump *rpc.SshJump) *SshConfig {
if sshJump == nil {
return &SshConfig{}

103
pkg/ssh/name.go Normal file
View File

@@ -0,0 +1,103 @@
package ssh
import (
"fmt"
"net"
"strings"
"unicode"
)
func IPToFilename(ipStr string) string {
ip := net.ParseIP(ipStr)
if ip == nil {
return "invalid-ip"
}
var filename string
if ip.To4() != nil {
filename = ip.String()
} else {
filename = convertIPv6(ip)
}
return sanitizeFilename(filename)
}
func convertIPv6(ip net.IP) string {
ip = ip.To16()
if ip == nil {
return "invalid-ipv6"
}
var zone string
if strings.Contains(ip.String(), "%") {
parts := strings.Split(ip.String(), "%")
if len(parts) > 1 {
zone = parts[1]
}
}
base := fmt.Sprintf("%02x%02x-%02x%02x-%02x%02x-%02x%02x",
ip[0], ip[1], ip[2], ip[3],
ip[4], ip[5], ip[6], ip[7],
)
if zone != "" {
zone = sanitizeZone(zone)
return base + "--" + zone
}
return base
}
func sanitizeZone(zone string) string {
var result strings.Builder
for _, r := range zone {
if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-' || r == '_' {
result.WriteRune(r)
} else {
result.WriteRune('-')
}
}
return result.String()
}
func sanitizeFilename(name string) string {
var result strings.Builder
lastWasDash := false
for _, r := range name {
switch {
case unicode.IsLetter(r) || unicode.IsDigit(r):
result.WriteRune(r)
lastWasDash = false
case r == '-' || r == '_' || r == '.':
if r == '.' {
if !lastWasDash {
result.WriteRune(r)
lastWasDash = true
}
} else {
result.WriteRune(r)
lastWasDash = true
}
default:
if !lastWasDash {
result.WriteRune('-')
lastWasDash = true
}
}
}
fname := result.String()
fname = strings.Trim(fname, "-_.")
if fname == "" {
return "ip-address"
}
return fname
}

21
pkg/ssh/name_test.go Normal file
View File

@@ -0,0 +1,21 @@
package ssh
import (
"testing"
)
func TestName(t *testing.T) {
testIPs := []string{
"192.168.1.1",
"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"::1",
"fe80::1%eth0",
"203.0.113.0",
"invalid-ip",
"::ffff:192.168.1.1",
}
for _, ip := range testIPs {
t.Logf("%-35s => %s", ip, IPToFilename(ip))
}
}

View File

@@ -139,8 +139,7 @@ func PortMapUntil(ctx context.Context, conf *SshConfig, remote, local netip.Addr
return nil
}
func SshJump(ctx context.Context, conf *SshConfig, kubeconfig string, print bool) (path string, err error) {
var kubeconfigBytes []byte
func SshJump(ctx context.Context, conf *SshConfig, kubeconfigBytes []byte, print bool) (path string, err error) {
if len(conf.RemoteKubeconfig) != 0 {
var stdout []byte
var stderr []byte
@@ -169,11 +168,7 @@ func SshJump(ctx context.Context, conf *SshConfig, kubeconfig string, print bool
return
}
kubeconfigBytes = bytes.TrimSpace(stdout)
} else {
kubeconfigBytes, err = os.ReadFile(kubeconfig)
if err != nil {
return
}
path = filepath.Join(config.GetTempPath(), conf.GenKubeconfigIdentify())
}
var clientConfig clientcmd.ClientConfig
clientConfig, err = clientcmd.NewClientConfigFromBytes(kubeconfigBytes)
@@ -289,7 +284,7 @@ func SshJump(ctx context.Context, conf *SshConfig, kubeconfig string, print bool
plog.G(ctx).Errorf("failed to marshal config: %v", err)
return
}
path, err = pkgutil.ConvertToTempKubeconfigFile(marshal)
path, err = pkgutil.ConvertToTempKubeconfigFile(marshal, path)
if err != nil {
plog.G(ctx).Errorf("failed to write kubeconfig: %v", err)
return
@@ -297,7 +292,6 @@ func SshJump(ctx context.Context, conf *SshConfig, kubeconfig string, print bool
go func() {
<-ctx.Done()
_ = os.Remove(path)
_ = os.Remove(kubeconfig)
}()
if print {
plog.G(ctx).Infof("Use temp kubeconfig: %s", path)
@@ -307,11 +301,8 @@ func SshJump(ctx context.Context, conf *SshConfig, kubeconfig string, print bool
return
}
func SshJumpAndSetEnv(ctx context.Context, sshConf *SshConfig, file string, print bool) error {
if sshConf.IsEmpty() {
return nil
}
path, err := SshJump(ctx, sshConf, file, print)
func SshJumpAndSetEnv(ctx context.Context, sshConf *SshConfig, kubeconfigBytes []byte, print bool) error {
path, err := SshJump(ctx, sshConf, kubeconfigBytes, print)
if err != nil {
return err
}

View File

@@ -85,7 +85,7 @@ func ConvertK8sApiServerToDomain(kubeConfigPath string) (newPath string, err err
if err != nil {
return
}
newPath, err = ConvertToTempKubeconfigFile(marshal)
newPath, err = ConvertToTempKubeconfigFile(marshal, "")
if err != nil {
return
}

View File

@@ -126,34 +126,20 @@ func GetAPIServerFromKubeConfigBytes(kubeconfigBytes []byte) *net.IPNet {
return &net.IPNet{IP: ip, Mask: mask}
}
func ConvertToTempKubeconfigFile(kubeconfigBytes []byte) (string, error) {
pattern := "*.kubeconfig"
cluster, ns, _ := GetCluster(kubeconfigBytes)
if cluster != "" && !containerPathSeparator(cluster) && !containerPathSeparator(ns) {
pattern = fmt.Sprintf("%s_%s_%s", cluster, ns, pattern)
pattern = strings.ReplaceAll(pattern, string(os.PathSeparator), "-")
func ConvertToTempKubeconfigFile(kubeconfigBytes []byte, path string) (string, error) {
var f *os.File
var err error
if path != "" {
f, err = os.Create(path)
} else {
pattern := "*.kubeconfig"
cluster, ns, _ := GetCluster(kubeconfigBytes)
if cluster != "" && !containerPathSeparator(cluster) && !containerPathSeparator(ns) {
pattern = fmt.Sprintf("%s_%s_%s", cluster, ns, pattern)
pattern = strings.ReplaceAll(pattern, string(os.PathSeparator), "-")
}
f, err = os.CreateTemp(config.GetTempPath(), pattern)
}
temp, err := os.CreateTemp(config.GetTempPath(), pattern)
if err != nil {
return "", err
}
_, err = temp.Write(kubeconfigBytes)
if err != nil {
return "", err
}
err = temp.Chmod(0644)
if err != nil {
return "", err
}
err = temp.Close()
if err != nil {
return "", err
}
return temp.Name(), nil
}
func ConvertToKubeconfigFile(kubeconfigBytes []byte, filename string) (string, error) {
f, err := os.Create(filename)
if err != nil {
return "", err
}
@@ -220,7 +206,7 @@ func InitFactory(kubeconfigBytes string, ns string) cmdutil.Factory {
}
return c
}
file, err := ConvertToTempKubeconfigFile([]byte(kubeconfigBytes))
file, err := ConvertToTempKubeconfigFile([]byte(kubeconfigBytes), "")
if err != nil {
return nil
}
@@ -270,7 +256,7 @@ func GetKubeconfigPath(factory cmdutil.Factory) (string, error) {
return "", err
}
file, err := ConvertToTempKubeconfigFile(kubeconfigJsonBytes)
file, err := ConvertToTempKubeconfigFile(kubeconfigJsonBytes, "")
if err != nil {
return "", err
}