refactor: remove target-kubeconfig of clone mode

This commit is contained in:
fengcaiwen
2025-06-02 17:42:26 +08:00
committed by naison
parent dd3ba1c059
commit 229eb747a4
12 changed files with 2357 additions and 1586 deletions

View File

@@ -36,9 +36,9 @@ func CmdClone(f cmdutil.Factory) *cobra.Command {
var imagePullSecretName string
cmd := &cobra.Command{
Use: "clone",
Short: i18n.T("Clone workloads to run in target-kubeconfig cluster with same volume、env、and network"),
Short: i18n.T("Clone workloads to run in same namespace with same volume、env、and network"),
Long: templates.LongDesc(i18n.T(`
Clone workloads to run into target-kubeconfig cluster with same volume、env、and network
Clone workloads to run in same namespace with same volume、env、and network
In this way, you can startup another deployment in same cluster or not, but with different image version,
it also supports service mesh proxy. only traffic with special header will hit to cloned_resource.
@@ -98,8 +98,6 @@ func CmdClone(f cmdutil.Factory) *cobra.Command {
}
return cmdutil.UsageErrorf(cmd, usageString)
}
// special empty string, eg: --target-registry ""
options.IsChangeTargetRegistry = cmd.Flags().Changed("target-registry")
if syncDir != "" {
local, remote, err := util.ParseDirMapping(syncDir)
@@ -122,26 +120,22 @@ func CmdClone(f cmdutil.Factory) *cobra.Command {
}
}
req := &rpc.CloneRequest{
KubeconfigBytes: string(bytes),
Namespace: ns,
Headers: options.Headers,
Workloads: args,
ExtraRoute: extraRoute.ToRPC(),
OriginKubeconfigPath: util.GetKubeConfigPath(f),
Engine: string(options.Engine),
SshJump: sshConf.ToRPC(),
TargetKubeconfig: options.TargetKubeconfig,
TargetNamespace: options.TargetNamespace,
TargetContainer: options.TargetContainer,
TargetImage: options.TargetImage,
TargetRegistry: options.TargetRegistry,
IsChangeTargetRegistry: options.IsChangeTargetRegistry,
TransferImage: transferImage,
Image: config.Image,
ImagePullSecretName: imagePullSecretName,
Level: int32(util.If(config.Debug, log.DebugLevel, log.InfoLevel)),
LocalDir: options.LocalDir,
RemoteDir: options.RemoteDir,
KubeconfigBytes: string(bytes),
Namespace: ns,
Headers: options.Headers,
Workloads: args,
ExtraRoute: extraRoute.ToRPC(),
OriginKubeconfigPath: util.GetKubeConfigPath(f),
Engine: string(options.Engine),
SshJump: sshConf.ToRPC(),
TargetContainer: options.TargetContainer,
TargetImage: options.TargetImage,
TransferImage: transferImage,
Image: config.Image,
ImagePullSecretName: imagePullSecretName,
Level: int32(util.If(config.Debug, log.DebugLevel, log.InfoLevel)),
LocalDir: options.LocalDir,
RemoteDir: options.RemoteDir,
}
cli, err := daemon.GetClient(false)
if err != nil {
@@ -168,9 +162,6 @@ func CmdClone(f cmdutil.Factory) *cobra.Command {
cmdutil.AddContainerVarFlags(cmd, &options.TargetContainer, options.TargetContainer)
cmd.Flags().StringVar(&options.TargetImage, "target-image", "", "Clone container use this image to startup container, if not special, use origin image")
cmd.Flags().StringVar(&options.TargetNamespace, "target-namespace", "", "Clone workloads in this namespace, if not special, use origin namespace")
cmd.Flags().StringVar(&options.TargetKubeconfig, "target-kubeconfig", "", "Clone workloads will create in this cluster, if not special, use origin cluster")
cmd.Flags().StringVar(&options.TargetRegistry, "target-registry", "", "Clone workloads will create this registry domain to replace origin registry, if not special, use origin registry")
cmd.Flags().StringVar(&syncDir, "sync", "", "Sync local dir to remote pod dir. format: LOCAL_DIR:REMOTE_DIR, eg: ~/code:/app/code")
handler.AddExtraRoute(cmd.Flags(), extraRoute)

View File

@@ -28,8 +28,9 @@ func CmdLeave(f cmdutil.Factory) *cobra.Command {
`)),
Example: templates.Examples(i18n.T(`
# leave proxy resource and restore it to origin
kubevpn leave deployment/authors
kubevpn leave deployment/authors-clone-645d7
`)),
Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.MinimumNArgs(1)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
return daemon.StartupDaemon(cmd.Context())
},

View File

@@ -2,7 +2,6 @@ package cmds
import (
"context"
"fmt"
"os"
log "github.com/sirupsen/logrus"
@@ -91,6 +90,7 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command {
# Auto proxy container port to same local port, and auto detect protocol
kubevpn proxy deployment/productpage
`)),
Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.MinimumNArgs(1)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
plog.InitLoggerForClient()
if err = daemon.StartupDaemon(cmd.Context()); err != nil {
@@ -102,16 +102,6 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command {
return err
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
fmt.Fprintf(os.Stdout, "You must specify the type of resource to proxy. %s\n\n", cmdutil.SuggestAPIResources("kubevpn"))
fullCmdName := cmd.Parent().CommandPath()
usageString := "Required resource not specified."
if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "explain") {
usageString = fmt.Sprintf("%s\nUse \"%s explain <resource>\" for a detailed description of that resource (e.g. %[2]s explain pods).", usageString, fullCmdName)
}
return cmdutil.UsageErrorf(cmd, usageString)
}
bytes, ns, err := util.ConvertToKubeConfigBytes(f)
if err != nil {
return err

View File

@@ -204,7 +204,7 @@ func genCloneMsg(w *tabwriter.Writer, list []*rpc.Status) {
_, _ = fmt.Fprintf(w, "\n")
w.SetRememberedWidths(nil)
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "ID", "Namespace", "Name", "Headers", "ToName", "ToKubeconfig", "ToNamespace", "SyncthingGUI")
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", "ID", "Namespace", "Name", "Headers", "ToName", "SyncthingGUI")
for _, c := range list {
for _, clone := range c.CloneList {
//_, _ = fmt.Fprintf(w, "%s\n", clone.Workload)
@@ -216,14 +216,12 @@ func genCloneMsg(w *tabwriter.Writer, list []*rpc.Status) {
if len(headers) == 0 {
headers = []string{"*"}
}
_, _ = fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
_, _ = fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%s\n",
c.ID,
clone.Namespace,
clone.Workload,
strings.Join(headers, ","),
rule.DstWorkload,
rule.DstKubeconfig,
rule.DstNamespace,
clone.SyncthingGUIAddr,
)
}

View File

@@ -51,15 +51,11 @@ func (svr *Server) Clone(req *rpc.CloneRequest, resp rpc.Daemon_CloneServer) (er
Engine: config.Engine(req.Engine),
OriginKubeconfigPath: req.OriginKubeconfigPath,
TargetKubeconfig: req.TargetKubeconfig,
TargetNamespace: req.TargetNamespace,
TargetContainer: req.TargetContainer,
TargetImage: req.TargetImage,
TargetRegistry: req.TargetRegistry,
IsChangeTargetRegistry: req.IsChangeTargetRegistry,
TargetWorkloadNames: map[string]string{},
LocalDir: req.LocalDir,
RemoteDir: req.RemoteDir,
TargetContainer: req.TargetContainer,
TargetImage: req.TargetImage,
TargetWorkloadNames: map[string]string{},
LocalDir: req.LocalDir,
RemoteDir: req.RemoteDir,
}
file, err := util.ConvertToTempKubeconfigFile([]byte(req.KubeconfigBytes))
if err != nil {

View File

@@ -147,11 +147,8 @@ func gen(ctx context.Context, connect *handler.ConnectOptions, clone *handler.Cl
SyncthingGUIAddr: clone.GetSyncthingGUIAddr(),
RuleList: []*rpc.CloneRule{
{
DstCluster: util.GetKubeconfigCluster(clone.GetFactory()),
Headers: clone.Headers,
DstWorkload: clone.TargetWorkloadNames[workload],
DstKubeconfig: clone.TargetKubeconfig,
DstNamespace: clone.TargetNamespace,
Headers: clone.Headers,
DstWorkload: clone.TargetWorkloadNames[workload],
},
},
})

File diff suppressed because it is too large Load Diff

View File

@@ -150,12 +150,8 @@ message CloneRequest {
SshJump SshJump = 8;
// target cluster info
string TargetKubeconfig = 9;
string TargetNamespace = 10;
string TargetContainer = 11;
string TargetImage = 12;
string TargetRegistry = 13;
bool IsChangeTargetRegistry = 14;
// transfer image
bool TransferImage = 15;

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc-gen-go v1.33.0
// protoc v5.29.3
// source: dhcpserver.proto
@@ -11,7 +11,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -22,18 +21,21 @@ const (
)
type RentIPRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
PodName string `protobuf:"bytes,1,opt,name=PodName,proto3" json:"PodName,omitempty"`
PodNamespace string `protobuf:"bytes,2,opt,name=PodNamespace,proto3" json:"PodNamespace,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
PodName string `protobuf:"bytes,1,opt,name=PodName,proto3" json:"PodName,omitempty"`
PodNamespace string `protobuf:"bytes,2,opt,name=PodNamespace,proto3" json:"PodNamespace,omitempty"`
}
func (x *RentIPRequest) Reset() {
*x = RentIPRequest{}
mi := &file_dhcpserver_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
if protoimpl.UnsafeEnabled {
mi := &file_dhcpserver_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RentIPRequest) String() string {
@@ -44,7 +46,7 @@ func (*RentIPRequest) ProtoMessage() {}
func (x *RentIPRequest) ProtoReflect() protoreflect.Message {
mi := &file_dhcpserver_proto_msgTypes[0]
if x != nil {
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -74,18 +76,21 @@ func (x *RentIPRequest) GetPodNamespace() string {
}
type RentIPResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
IPv4CIDR string `protobuf:"bytes,1,opt,name=IPv4CIDR,proto3" json:"IPv4CIDR,omitempty"`
IPv6CIDR string `protobuf:"bytes,2,opt,name=IPv6CIDR,proto3" json:"IPv6CIDR,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
IPv4CIDR string `protobuf:"bytes,1,opt,name=IPv4CIDR,proto3" json:"IPv4CIDR,omitempty"`
IPv6CIDR string `protobuf:"bytes,2,opt,name=IPv6CIDR,proto3" json:"IPv6CIDR,omitempty"`
}
func (x *RentIPResponse) Reset() {
*x = RentIPResponse{}
mi := &file_dhcpserver_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
if protoimpl.UnsafeEnabled {
mi := &file_dhcpserver_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RentIPResponse) String() string {
@@ -96,7 +101,7 @@ func (*RentIPResponse) ProtoMessage() {}
func (x *RentIPResponse) ProtoReflect() protoreflect.Message {
mi := &file_dhcpserver_proto_msgTypes[1]
if x != nil {
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -126,20 +131,23 @@ func (x *RentIPResponse) GetIPv6CIDR() string {
}
type ReleaseIPRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
PodName string `protobuf:"bytes,1,opt,name=PodName,proto3" json:"PodName,omitempty"`
PodNamespace string `protobuf:"bytes,2,opt,name=PodNamespace,proto3" json:"PodNamespace,omitempty"`
IPv4CIDR string `protobuf:"bytes,3,opt,name=IPv4CIDR,proto3" json:"IPv4CIDR,omitempty"`
IPv6CIDR string `protobuf:"bytes,4,opt,name=IPv6CIDR,proto3" json:"IPv6CIDR,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
PodName string `protobuf:"bytes,1,opt,name=PodName,proto3" json:"PodName,omitempty"`
PodNamespace string `protobuf:"bytes,2,opt,name=PodNamespace,proto3" json:"PodNamespace,omitempty"`
IPv4CIDR string `protobuf:"bytes,3,opt,name=IPv4CIDR,proto3" json:"IPv4CIDR,omitempty"`
IPv6CIDR string `protobuf:"bytes,4,opt,name=IPv6CIDR,proto3" json:"IPv6CIDR,omitempty"`
}
func (x *ReleaseIPRequest) Reset() {
*x = ReleaseIPRequest{}
mi := &file_dhcpserver_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
if protoimpl.UnsafeEnabled {
mi := &file_dhcpserver_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ReleaseIPRequest) String() string {
@@ -150,7 +158,7 @@ func (*ReleaseIPRequest) ProtoMessage() {}
func (x *ReleaseIPRequest) ProtoReflect() protoreflect.Message {
mi := &file_dhcpserver_proto_msgTypes[2]
if x != nil {
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -194,17 +202,20 @@ func (x *ReleaseIPRequest) GetIPv6CIDR() string {
}
type ReleaseIPResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}
func (x *ReleaseIPResponse) Reset() {
*x = ReleaseIPResponse{}
mi := &file_dhcpserver_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
if protoimpl.UnsafeEnabled {
mi := &file_dhcpserver_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ReleaseIPResponse) String() string {
@@ -215,7 +226,7 @@ func (*ReleaseIPResponse) ProtoMessage() {}
func (x *ReleaseIPResponse) ProtoReflect() protoreflect.Message {
mi := &file_dhcpserver_proto_msgTypes[3]
if x != nil {
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -239,40 +250,55 @@ func (x *ReleaseIPResponse) GetMessage() string {
var File_dhcpserver_proto protoreflect.FileDescriptor
const file_dhcpserver_proto_rawDesc = "" +
"\n" +
"\x10dhcpserver.proto\x12\x03rpc\"M\n" +
"\rRentIPRequest\x12\x18\n" +
"\aPodName\x18\x01 \x01(\tR\aPodName\x12\"\n" +
"\fPodNamespace\x18\x02 \x01(\tR\fPodNamespace\"H\n" +
"\x0eRentIPResponse\x12\x1a\n" +
"\bIPv4CIDR\x18\x01 \x01(\tR\bIPv4CIDR\x12\x1a\n" +
"\bIPv6CIDR\x18\x02 \x01(\tR\bIPv6CIDR\"\x88\x01\n" +
"\x10ReleaseIPRequest\x12\x18\n" +
"\aPodName\x18\x01 \x01(\tR\aPodName\x12\"\n" +
"\fPodNamespace\x18\x02 \x01(\tR\fPodNamespace\x12\x1a\n" +
"\bIPv4CIDR\x18\x03 \x01(\tR\bIPv4CIDR\x12\x1a\n" +
"\bIPv6CIDR\x18\x04 \x01(\tR\bIPv6CIDR\"-\n" +
"\x11ReleaseIPResponse\x12\x18\n" +
"\amessage\x18\x01 \x01(\tR\amessage2y\n" +
"\x04DHCP\x123\n" +
"\x06RentIP\x12\x12.rpc.RentIPRequest\x1a\x13.rpc.RentIPResponse\"\x00\x12<\n" +
"\tReleaseIP\x12\x15.rpc.ReleaseIPRequest\x1a\x16.rpc.ReleaseIPResponse\"\x00B\aZ\x05.;rpcb\x06proto3"
var file_dhcpserver_proto_rawDesc = []byte{
0x0a, 0x10, 0x64, 0x68, 0x63, 0x70, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x03, 0x72, 0x70, 0x63, 0x22, 0x4d, 0x0a, 0x0d, 0x52, 0x65, 0x6e, 0x74, 0x49,
0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x6f, 0x64, 0x4e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x50, 0x6f, 0x64, 0x4e, 0x61,
0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61,
0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d,
0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x48, 0x0a, 0x0e, 0x52, 0x65, 0x6e, 0x74, 0x49, 0x50,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x34,
0x43, 0x49, 0x44, 0x52, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x34,
0x43, 0x49, 0x44, 0x52, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x36, 0x43, 0x49, 0x44, 0x52,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x36, 0x43, 0x49, 0x44, 0x52,
0x22, 0x88, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x50, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12,
0x22, 0x0a, 0x0c, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x50, 0x6f, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70,
0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x34, 0x43, 0x49, 0x44, 0x52, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x34, 0x43, 0x49, 0x44, 0x52, 0x12,
0x1a, 0x0a, 0x08, 0x49, 0x50, 0x76, 0x36, 0x43, 0x49, 0x44, 0x52, 0x18, 0x04, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x49, 0x50, 0x76, 0x36, 0x43, 0x49, 0x44, 0x52, 0x22, 0x2d, 0x0a, 0x11, 0x52,
0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x79, 0x0a, 0x04, 0x44, 0x48,
0x43, 0x50, 0x12, 0x33, 0x0a, 0x06, 0x52, 0x65, 0x6e, 0x74, 0x49, 0x50, 0x12, 0x12, 0x2e, 0x72,
0x70, 0x63, 0x2e, 0x52, 0x65, 0x6e, 0x74, 0x49, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x13, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6e, 0x74, 0x49, 0x50, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3c, 0x0a, 0x09, 0x52, 0x65, 0x6c, 0x65, 0x61,
0x73, 0x65, 0x49, 0x50, 0x12, 0x15, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61,
0x73, 0x65, 0x49, 0x50, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x72, 0x70,
0x63, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x50, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x3b, 0x72, 0x70, 0x63, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_dhcpserver_proto_rawDescOnce sync.Once
file_dhcpserver_proto_rawDescData []byte
file_dhcpserver_proto_rawDescData = file_dhcpserver_proto_rawDesc
)
func file_dhcpserver_proto_rawDescGZIP() []byte {
file_dhcpserver_proto_rawDescOnce.Do(func() {
file_dhcpserver_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_dhcpserver_proto_rawDesc), len(file_dhcpserver_proto_rawDesc)))
file_dhcpserver_proto_rawDescData = protoimpl.X.CompressGZIP(file_dhcpserver_proto_rawDescData)
})
return file_dhcpserver_proto_rawDescData
}
var file_dhcpserver_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_dhcpserver_proto_goTypes = []any{
var file_dhcpserver_proto_goTypes = []interface{}{
(*RentIPRequest)(nil), // 0: rpc.RentIPRequest
(*RentIPResponse)(nil), // 1: rpc.RentIPResponse
(*ReleaseIPRequest)(nil), // 2: rpc.ReleaseIPRequest
@@ -295,11 +321,61 @@ func file_dhcpserver_proto_init() {
if File_dhcpserver_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_dhcpserver_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RentIPRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_dhcpserver_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RentIPResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_dhcpserver_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReleaseIPRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_dhcpserver_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReleaseIPResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_dhcpserver_proto_rawDesc), len(file_dhcpserver_proto_rawDesc)),
RawDescriptor: file_dhcpserver_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
@@ -310,6 +386,7 @@ func file_dhcpserver_proto_init() {
MessageInfos: file_dhcpserver_proto_msgTypes,
}.Build()
File_dhcpserver_proto = out.File
file_dhcpserver_proto_rawDesc = nil
file_dhcpserver_proto_goTypes = nil
file_dhcpserver_proto_depIdxs = nil
}

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc-gen-go-grpc v1.3.0
// - protoc v5.29.3
// source: dhcpserver.proto
@@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
DHCP_RentIP_FullMethodName = "/rpc.DHCP/RentIP"
@@ -40,9 +40,8 @@ func NewDHCPClient(cc grpc.ClientConnInterface) DHCPClient {
}
func (c *dHCPClient) RentIP(ctx context.Context, in *RentIPRequest, opts ...grpc.CallOption) (*RentIPResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RentIPResponse)
err := c.cc.Invoke(ctx, DHCP_RentIP_FullMethodName, in, out, cOpts...)
err := c.cc.Invoke(ctx, DHCP_RentIP_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
@@ -50,9 +49,8 @@ func (c *dHCPClient) RentIP(ctx context.Context, in *RentIPRequest, opts ...grpc
}
func (c *dHCPClient) ReleaseIP(ctx context.Context, in *ReleaseIPRequest, opts ...grpc.CallOption) (*ReleaseIPResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ReleaseIPResponse)
err := c.cc.Invoke(ctx, DHCP_ReleaseIP_FullMethodName, in, out, cOpts...)
err := c.cc.Invoke(ctx, DHCP_ReleaseIP_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
@@ -61,19 +59,16 @@ func (c *dHCPClient) ReleaseIP(ctx context.Context, in *ReleaseIPRequest, opts .
// DHCPServer is the server API for DHCP service.
// All implementations must embed UnimplementedDHCPServer
// for forward compatibility.
// for forward compatibility
type DHCPServer interface {
RentIP(context.Context, *RentIPRequest) (*RentIPResponse, error)
ReleaseIP(context.Context, *ReleaseIPRequest) (*ReleaseIPResponse, error)
mustEmbedUnimplementedDHCPServer()
}
// UnimplementedDHCPServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedDHCPServer struct{}
// UnimplementedDHCPServer must be embedded to have forward compatible implementations.
type UnimplementedDHCPServer struct {
}
func (UnimplementedDHCPServer) RentIP(context.Context, *RentIPRequest) (*RentIPResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RentIP not implemented")
@@ -82,7 +77,6 @@ func (UnimplementedDHCPServer) ReleaseIP(context.Context, *ReleaseIPRequest) (*R
return nil, status.Errorf(codes.Unimplemented, "method ReleaseIP not implemented")
}
func (UnimplementedDHCPServer) mustEmbedUnimplementedDHCPServer() {}
func (UnimplementedDHCPServer) testEmbeddedByValue() {}
// UnsafeDHCPServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to DHCPServer will
@@ -92,13 +86,6 @@ type UnsafeDHCPServer interface {
}
func RegisterDHCPServer(s grpc.ServiceRegistrar, srv DHCPServer) {
// If the following call pancis, it indicates UnimplementedDHCPServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&DHCP_ServiceDesc, srv)
}

View File

@@ -6,13 +6,10 @@ import (
"fmt"
"net"
"net/url"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
"github.com/distribution/reference"
"github.com/google/uuid"
libconfig "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/netutil"
@@ -23,11 +20,9 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/retry"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/cmd/util/podcmd"
@@ -37,7 +32,6 @@ import (
"k8s.io/utils/ptr"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/inject"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/syncthing"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
@@ -50,25 +44,14 @@ type CloneOptions struct {
ExtraRouteInfo ExtraRouteInfo
Engine config.Engine
TargetKubeconfig string
TargetNamespace string
TargetContainer string
TargetImage string
TargetRegistry string
IsChangeTargetRegistry bool
TargetWorkloadNames map[string]string
isSame bool
TargetContainer string
TargetImage string
TargetWorkloadNames map[string]string
OriginKubeconfigPath string
LocalDir string
RemoteDir string
targetClientset *kubernetes.Clientset
targetRestclient *rest.RESTClient
targetConfig *rest.Config
targetFactory cmdutil.Factory
clientset *kubernetes.Clientset
restclient *rest.RESTClient
config *rest.Config
@@ -93,31 +76,6 @@ func (d *CloneOptions) InitClient(f cmdutil.Factory) (err error) {
if d.Namespace, _, err = d.factory.ToRawKubeConfigLoader().Namespace(); err != nil {
return
}
// init target info
if len(d.TargetKubeconfig) == 0 {
d.TargetKubeconfig = d.OriginKubeconfigPath
d.targetFactory = d.factory
d.targetClientset = d.clientset
d.targetConfig = d.config
d.targetRestclient = d.restclient
if len(d.TargetNamespace) == 0 {
d.TargetNamespace = d.Namespace
d.isSame = true
}
return
}
configFlags := genericclioptions.NewConfigFlags(true)
configFlags.KubeConfig = pointer.String(d.TargetKubeconfig)
configFlags.Namespace = pointer.String(d.TargetNamespace)
matchVersionFlags := cmdutil.NewMatchVersionFlags(configFlags)
d.targetFactory = cmdutil.NewFactory(matchVersionFlags)
var found bool
d.TargetNamespace, found, err = d.targetFactory.ToRawKubeConfigLoader().Namespace()
if err != nil || !found {
d.TargetNamespace = d.Namespace
}
d.targetClientset, err = d.targetFactory.KubernetesClientSet()
return
}
@@ -147,7 +105,6 @@ func (d *CloneOptions) DoClone(ctx context.Context, kubeconfigJsonBytes []byte)
if err = unstructured.SetNestedField(u.UnstructuredContent(), int64(1), "spec", "replicas"); err != nil {
plog.G(ctx).Warnf("Failed to set repilcaset to 1: %v", err)
}
u.SetNamespace(d.TargetNamespace)
RemoveUselessInfo(u)
var newUUID uuid.UUID
newUUID, err = uuid.NewUUID()
@@ -156,17 +113,7 @@ func (d *CloneOptions) DoClone(ctx context.Context, kubeconfigJsonBytes []byte)
}
originName := u.GetName()
u.SetName(fmt.Sprintf("%s-clone-%s", u.GetName(), newUUID.String()[:5]))
d.TargetWorkloadNames[workload] = u.GetName()
// if is another cluster, needs to set volume and set env
if !d.isSame {
if err = d.setVolume(u); err != nil {
return err
}
if err = d.setEnv(u); err != nil {
return err
}
}
d.TargetWorkloadNames[workload] = fmt.Sprintf("%s/%s", object.Mapping.Resource.GroupResource().Resource, u.GetName())
labelsMap := map[string]string{
config.ManageBy: config.ConfigMapPodTrafficManager,
"owner-ref": u.GetName(),
@@ -185,7 +132,7 @@ func (d *CloneOptions) DoClone(ctx context.Context, kubeconfigJsonBytes []byte)
return err
}
var client dynamic.Interface
client, err = d.targetFactory.DynamicClient()
client, err = d.factory.DynamicClient()
if err != nil {
return err
}
@@ -282,104 +229,8 @@ func (d *CloneOptions) DoClone(ctx context.Context, kubeconfigJsonBytes []byte)
Value: "1",
},
}...)*/
container := &v1.Container{
Name: config.ContainerSidecarVPN,
Image: config.Image,
// https://stackoverflow.com/questions/32918849/what-process-signal-does-pod-receive-when-executing-kubectl-rolling-update
Command: append([]string{
"kubevpn",
"proxy",
workload,
"--kubeconfig", "/tmp/.kube/" + config.KUBECONFIG,
"--namespace", d.Namespace,
"--image", config.Image,
"--netstack", string(d.Engine),
"--foreground",
}, args...),
Env: []v1.EnvVar{},
Resources: v1.ResourceRequirements{
Requests: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: resource.MustParse("1000m"),
v1.ResourceMemory: resource.MustParse("1024Mi"),
},
Limits: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: resource.MustParse("2000m"),
v1.ResourceMemory: resource.MustParse("2048Mi"),
},
},
VolumeMounts: []v1.VolumeMount{
{
Name: config.KUBECONFIG,
ReadOnly: false,
MountPath: "/tmp/.kube",
},
},
Lifecycle: &v1.Lifecycle{
PostStart: &v1.LifecycleHandler{
Exec: &v1.ExecAction{
Command: []string{
"/bin/bash",
"-c",
`
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
echo 1 > /proc/sys/net/ipv4/conf/all/route_localnet
update-alternatives --set iptables /usr/sbin/iptables-legacy`,
},
},
},
},
ImagePullPolicy: v1.PullIfNotPresent,
SecurityContext: &v1.SecurityContext{
Capabilities: &v1.Capabilities{
Add: []v1.Capability{
"NET_ADMIN",
},
},
RunAsUser: pointer.Int64(0),
RunAsGroup: pointer.Int64(0),
Privileged: pointer.Bool(true),
},
}
containerSync := &v1.Container{
Name: config.ContainerSidecarSyncthing,
Image: config.Image,
// https://stackoverflow.com/questions/32918849/what-process-signal-does-pod-receive-when-executing-kubectl-rolling-update
Command: []string{
"kubevpn",
"syncthing",
"--dir",
d.RemoteDir,
},
Resources: v1.ResourceRequirements{
Requests: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: resource.MustParse("500m"),
v1.ResourceMemory: resource.MustParse("512Mi"),
},
Limits: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: resource.MustParse("1000m"),
v1.ResourceMemory: resource.MustParse("1024Mi"),
},
},
VolumeMounts: []v1.VolumeMount{
{
Name: syncDataDirName,
ReadOnly: false,
MountPath: d.RemoteDir,
},
},
// only for:
// panic: mkdir /.kubevpn: permission denied │
// │
// goroutine 1 [running]: │
// github.com/wencaiwulue/kubevpn/v2/pkg/config.init.1() │
// /go/src/github.com/wencaiwulue/kubevpn/pkg/config/const.go:34 +0x1ae
SecurityContext: &v1.SecurityContext{
RunAsUser: ptr.To[int64](0),
RunAsGroup: ptr.To[int64](0),
},
}
container := genVPNContainer(workload, d.Engine, d.Namespace, args)
containerSync := genSyncthingContainer(d.RemoteDir, syncDataDirName)
spec.Spec.Containers = append(containers, *container, *containerSync)
//set spec
marshal, err := json.Marshal(spec)
@@ -397,11 +248,8 @@ update-alternatives --set iptables /usr/sbin/iptables-legacy`,
if err = unstructured.SetNestedField(u.Object, m, path...); err != nil {
return err
}
if err = d.replaceRegistry(ctx, u); err != nil {
return err
}
_, createErr := client.Resource(object.Mapping.Resource).Namespace(d.TargetNamespace).Create(context.Background(), u, metav1.CreateOptions{})
_, createErr := client.Resource(object.Mapping.Resource).Namespace(d.Namespace).Create(context.Background(), u, metav1.CreateOptions{})
//_, createErr := runtimeresource.NewHelper(object.Client, object.Mapping).Create(d.TargetNamespace, true, u)
return createErr
})
@@ -411,7 +259,7 @@ update-alternatives --set iptables /usr/sbin/iptables-legacy`,
plog.G(ctx).Infof("Create clone resource %s/%s in target cluster", u.GetObjectKind().GroupVersionKind().GroupKind().String(), u.GetName())
plog.G(ctx).Infof("Wait for clone resource %s/%s to be ready", u.GetObjectKind().GroupVersionKind().GroupKind().String(), u.GetName())
plog.G(ctx).Infoln()
err = util.WaitPodToBeReady(ctx, d.targetClientset.CoreV1().Pods(d.TargetNamespace), metav1.LabelSelector{MatchLabels: labelsMap})
err = util.WaitPodToBeReady(ctx, d.clientset.CoreV1().Pods(d.Namespace), metav1.LabelSelector{MatchLabels: labelsMap})
if err != nil {
return err
}
@@ -427,8 +275,114 @@ update-alternatives --set iptables /usr/sbin/iptables-legacy`,
return nil
}
func genSyncthingContainer(remoteDir string, syncDataDirName string) *v1.Container {
containerSync := &v1.Container{
Name: config.ContainerSidecarSyncthing,
Image: config.Image,
// https://stackoverflow.com/questions/32918849/what-process-signal-does-pod-receive-when-executing-kubectl-rolling-update
Command: []string{
"kubevpn",
"syncthing",
"--dir",
remoteDir,
},
Resources: v1.ResourceRequirements{
Requests: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: resource.MustParse("500m"),
v1.ResourceMemory: resource.MustParse("512Mi"),
},
Limits: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: resource.MustParse("1000m"),
v1.ResourceMemory: resource.MustParse("1024Mi"),
},
},
VolumeMounts: []v1.VolumeMount{
{
Name: syncDataDirName,
ReadOnly: false,
MountPath: remoteDir,
},
},
// only for:
// panic: mkdir /.kubevpn: permission denied │
// │
// goroutine 1 [running]: │
// github.com/wencaiwulue/kubevpn/v2/pkg/config.init.1() │
// /go/src/github.com/wencaiwulue/kubevpn/pkg/config/const.go:34 +0x1ae
SecurityContext: &v1.SecurityContext{
RunAsUser: ptr.To[int64](0),
RunAsGroup: ptr.To[int64](0),
},
}
return containerSync
}
func genVPNContainer(workload string, engine config.Engine, namespace string, args []string) *v1.Container {
container := &v1.Container{
Name: config.ContainerSidecarVPN,
Image: config.Image,
// https://stackoverflow.com/questions/32918849/what-process-signal-does-pod-receive-when-executing-kubectl-rolling-update
Command: append([]string{
"kubevpn",
"proxy",
workload,
"--kubeconfig", "/tmp/.kube/" + config.KUBECONFIG,
"--namespace", namespace,
"--image", config.Image,
"--netstack", string(engine),
"--foreground",
}, args...),
Env: []v1.EnvVar{},
Resources: v1.ResourceRequirements{
Requests: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: resource.MustParse("1000m"),
v1.ResourceMemory: resource.MustParse("1024Mi"),
},
Limits: map[v1.ResourceName]resource.Quantity{
v1.ResourceCPU: resource.MustParse("2000m"),
v1.ResourceMemory: resource.MustParse("2048Mi"),
},
},
VolumeMounts: []v1.VolumeMount{
{
Name: config.KUBECONFIG,
ReadOnly: false,
MountPath: "/tmp/.kube",
},
},
Lifecycle: &v1.Lifecycle{
PostStart: &v1.LifecycleHandler{
Exec: &v1.ExecAction{
Command: []string{
"/bin/bash",
"-c",
`
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
echo 1 > /proc/sys/net/ipv4/conf/all/route_localnet
update-alternatives --set iptables /usr/sbin/iptables-legacy`,
},
},
},
},
ImagePullPolicy: v1.PullIfNotPresent,
SecurityContext: &v1.SecurityContext{
Capabilities: &v1.Capabilities{
Add: []v1.Capability{
"NET_ADMIN",
},
},
RunAsUser: pointer.Int64(0),
RunAsGroup: pointer.Int64(0),
Privileged: pointer.Bool(true),
},
}
return container
}
func (d *CloneOptions) SyncDir(ctx context.Context, labels string) error {
list, err := util.GetRunningPodList(ctx, d.targetClientset, d.TargetNamespace, labels)
list, err := util.GetRunningPodList(ctx, d.clientset, d.Namespace, labels)
if err != nil {
return err
}
@@ -449,8 +403,8 @@ func (d *CloneOptions) SyncDir(ctx context.Context, labels string) error {
defer time.Sleep(time.Second * 2)
sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(podutils.ActivePods(pods)) }
_, _, _ = polymorphichelpers.GetFirstPod(d.targetClientset.CoreV1(), d.TargetNamespace, labels, time.Second*30, sortBy)
list, err := util.GetRunningPodList(d.ctx, d.targetClientset, d.TargetNamespace, labels)
_, _, _ = polymorphichelpers.GetFirstPod(d.clientset.CoreV1(), d.Namespace, labels, time.Second*30, sortBy)
list, err := util.GetRunningPodList(d.ctx, d.clientset, d.Namespace, labels)
if err != nil {
plog.G(ctx).Error(err)
return
@@ -505,393 +459,31 @@ func RemoveUselessInfo(u *unstructured.Unstructured) {
u.SetAnnotations(a)
}
// setVolume
/*
1) calculate volume content, and download it into emptyDir
*/
func (d *CloneOptions) setVolume(u *unstructured.Unstructured) error {
const TokenVolumeMountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
type VolumeMountContainerPair struct {
container v1.Container
volumeMount v1.VolumeMount
}
temp, path, err := util.GetPodTemplateSpecPath(u)
if err != nil {
return err
}
lab := labels.SelectorFromSet(temp.Labels).String()
var list []v1.Pod
list, err = util.GetRunningPodList(context.Background(), d.clientset, d.Namespace, lab)
if err != nil {
return err
}
pod := list[0]
// remove serviceAccount info
temp.Spec.ServiceAccountName = ""
temp.Spec.AutomountServiceAccountToken = pointer.Bool(false)
var volumeMap = make(map[string]v1.Volume)
var volumeList []v1.Volume
// pod's volume maybe more than spec defined
for _, volume := range pod.Spec.Volumes {
volumeMap[volume.Name] = volume
// keep volume emptyDir
if volume.EmptyDir != nil {
volumeList = append(volumeList, volume)
} else {
volumeList = append(volumeList, v1.Volume{
Name: volume.Name,
VolumeSource: v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{},
},
})
}
}
var tokenVolume string
var volumeM = make(map[string][]VolumeMountContainerPair)
for _, container := range pod.Spec.Containers {
// group by volume name, what we want is figure out what's contains in every volume
// we need to restore a total volume base on mountPath and subPath
for _, volumeMount := range container.VolumeMounts {
if volumeMap[volumeMount.Name].EmptyDir != nil {
continue
}
if volumeMount.MountPath == TokenVolumeMountPath {
tokenVolume = volumeMount.Name
}
mounts := volumeM[volumeMount.Name]
if mounts == nil {
volumeM[volumeMount.Name] = []VolumeMountContainerPair{}
}
volumeM[volumeMount.Name] = append(volumeM[volumeMount.Name], VolumeMountContainerPair{
container: container,
volumeMount: volumeMount,
})
}
}
var initContainer []v1.Container
for _, volume := range pod.Spec.Volumes {
mountPoint := "/tmp/" + volume.Name
var args []string
for _, pair := range volumeM[volume.Name] {
remote := filepath.Join(pair.volumeMount.MountPath, pair.volumeMount.SubPath)
local := filepath.Join(mountPoint, pair.volumeMount.SubPath)
// kubectl cp <some-namespace>/<some-pod>:/tmp/foo /tmp/bar
args = append(args,
fmt.Sprintf("kubevpn cp %s/%s:%s %s -c %s", pod.Namespace, pod.Name, remote, local, pair.container.Name),
)
}
// means maybe volume only used in initContainers
if len(args) == 0 {
for i := 0; i < len(temp.Spec.InitContainers); i++ {
for _, mount := range temp.Spec.InitContainers[i].VolumeMounts {
if mount.Name == volume.Name {
// remove useless initContainer
temp.Spec.InitContainers = append(temp.Spec.InitContainers[:i], temp.Spec.InitContainers[i+1:]...)
i--
break
}
}
}
continue
}
newContainer := v1.Container{
Name: fmt.Sprintf("download-%s", volume.Name),
Image: config.Image,
Command: []string{"sh", "-c"},
Args: []string{strings.Join(args, "&&")},
WorkingDir: "/tmp",
Env: []v1.EnvVar{
{
Name: clientcmd.RecommendedConfigPathEnvVar,
Value: "/tmp/.kube/kubeconfig",
},
},
Resources: v1.ResourceRequirements{},
VolumeMounts: []v1.VolumeMount{
{
Name: volume.Name,
MountPath: mountPoint,
},
{
Name: config.KUBECONFIG,
ReadOnly: false,
MountPath: "/tmp/.kube",
},
},
ImagePullPolicy: v1.PullIfNotPresent,
}
initContainer = append(initContainer, newContainer)
}
// put download volume to front
temp.Spec.InitContainers = append(initContainer, temp.Spec.InitContainers...)
// replace old one
temp.Spec.Volumes = volumeList
// remove containers vpn and envoy-proxy
inject.RemoveContainers(temp)
// add each container service account token
if tokenVolume != "" {
for i := 0; i < len(temp.Spec.Containers); i++ {
var found bool
for _, mount := range temp.Spec.Containers[i].VolumeMounts {
if mount.MountPath == TokenVolumeMountPath {
found = true
break
}
}
if !found {
temp.Spec.Containers[i].VolumeMounts = append(temp.Spec.Containers[i].VolumeMounts, v1.VolumeMount{
Name: tokenVolume,
MountPath: TokenVolumeMountPath,
})
}
}
}
var marshal []byte
if marshal, err = json.Marshal(temp.Spec); err != nil {
return err
}
var content map[string]interface{}
if err = json.Unmarshal(marshal, &content); err != nil {
return err
}
if err = unstructured.SetNestedField(u.Object, content, append(path, "spec")...); err != nil {
return err
}
return nil
}
func (d *CloneOptions) setEnv(u *unstructured.Unstructured) error {
temp, path, err := util.GetPodTemplateSpecPath(u)
if err != nil {
return err
}
/*sortBy := func(pods []*v1.Pod) sort.Interface {
for i := 0; i < len(pods); i++ {
if pods[i].DeletionTimestamp != nil {
pods = append(pods[:i], pods[i+1:]...)
i--
}
}
return sort.Reverse(podutils.ActivePods(pods))
}
lab := labels.SelectorFromSet(temp.Labels).String()
pod, _, err := polymorphichelpers.GetFirstPod(d.clientset.CoreV1(), d.Namespace, lab, time.Second*60, sortBy)
if err != nil {
return err
}
var envMap map[string][]string
envMap, err = util.GetEnv(context.Background(), d.factory, d.Namespace, pod.Name)
if err != nil {
return err
}*/
var secretMap = make(map[string]*v1.Secret)
var configmapMap = make(map[string]*v1.ConfigMap)
var howToGetCm = func(name string) {
if configmapMap[name] == nil {
cm, err := d.clientset.CoreV1().ConfigMaps(d.Namespace).Get(context.Background(), name, metav1.GetOptions{})
if err == nil {
configmapMap[name] = cm
}
}
}
var howToGetSecret = func(name string) {
if configmapMap[name] == nil {
secret, err := d.clientset.CoreV1().Secrets(d.Namespace).Get(context.Background(), name, metav1.GetOptions{})
if err == nil {
secretMap[name] = secret
}
}
}
// get all ref configmaps and secrets
for _, container := range temp.Spec.Containers {
for _, envVar := range container.Env {
if envVar.ValueFrom != nil {
if ref := envVar.ValueFrom.ConfigMapKeyRef; ref != nil {
howToGetCm(ref.Name)
}
if ref := envVar.ValueFrom.SecretKeyRef; ref != nil {
howToGetSecret(ref.Name)
}
}
}
for _, source := range container.EnvFrom {
if ref := source.ConfigMapRef; ref != nil {
if configmapMap[ref.Name] == nil {
howToGetCm(ref.Name)
}
}
if ref := source.SecretRef; ref != nil {
howToGetSecret(ref.Name)
}
}
}
// parse real value from secrets and configmaps
for i := 0; i < len(temp.Spec.Containers); i++ {
container := temp.Spec.Containers[i]
var envVars []v1.EnvVar
for _, envFromSource := range container.EnvFrom {
if ref := envFromSource.ConfigMapRef; ref != nil && configmapMap[ref.Name] != nil {
cm := configmapMap[ref.Name]
for k, v := range cm.Data {
if strings.HasPrefix(k, envFromSource.Prefix) {
envVars = append(envVars, v1.EnvVar{
Name: k,
Value: v,
})
}
}
}
if ref := envFromSource.SecretRef; ref != nil && secretMap[ref.Name] != nil {
secret := secretMap[ref.Name]
for k, v := range secret.Data {
if strings.HasPrefix(k, envFromSource.Prefix) {
envVars = append(envVars, v1.EnvVar{
Name: k,
Value: string(v),
})
}
}
}
}
temp.Spec.Containers[i].EnvFrom = nil
temp.Spec.Containers[i].Env = append(temp.Spec.Containers[i].Env, envVars...)
for j, envVar := range container.Env {
if envVar.ValueFrom != nil {
if ref := envVar.ValueFrom.ConfigMapKeyRef; ref != nil {
if configMap := configmapMap[ref.Name]; configMap != nil {
temp.Spec.Containers[i].Env[j].Value = configMap.Data[ref.Key]
temp.Spec.Containers[i].Env[j].ValueFrom = nil
}
}
if ref := envVar.ValueFrom.SecretKeyRef; ref != nil {
if secret := secretMap[ref.Name]; secret != nil {
temp.Spec.Containers[i].Env[j].Value = string(secret.Data[ref.Key])
temp.Spec.Containers[i].Env[j].ValueFrom = nil
}
}
}
}
}
var marshal []byte
if marshal, err = json.Marshal(temp.Spec); err != nil {
return err
}
var content map[string]interface{}
if err = json.Unmarshal(marshal, &content); err != nil {
return err
}
if err = unstructured.SetNestedField(u.Object, content, append(path, "spec")...); err != nil {
return err
}
return nil
}
// replace origin registry with special registry for pulling image
func (d *CloneOptions) replaceRegistry(ctx context.Context, u *unstructured.Unstructured) error {
// not pass this options, do nothing
if !d.IsChangeTargetRegistry {
return nil
}
temp, path, err := util.GetPodTemplateSpecPath(u)
if err != nil {
return err
}
for i, container := range temp.Spec.InitContainers {
oldImage := container.Image
named, err := reference.ParseNormalizedNamed(oldImage)
if err != nil {
return err
}
domain := reference.Domain(named)
newImage := strings.TrimPrefix(strings.ReplaceAll(oldImage, domain, d.TargetRegistry), "/")
temp.Spec.InitContainers[i].Image = newImage
plog.G(ctx).Debugf("Update init container: %s image: %s --> %s", container.Name, oldImage, newImage)
}
for i, container := range temp.Spec.Containers {
oldImage := container.Image
named, err := reference.ParseNormalizedNamed(oldImage)
if err != nil {
return err
}
domain := reference.Domain(named)
newImage := strings.TrimPrefix(strings.ReplaceAll(oldImage, domain, d.TargetRegistry), "/")
temp.Spec.Containers[i].Image = newImage
plog.G(ctx).Debugf("Update container: %s image: %s --> %s", container.Name, oldImage, newImage)
}
var marshal []byte
if marshal, err = json.Marshal(temp.Spec); err != nil {
return err
}
var content map[string]interface{}
if err = json.Unmarshal(marshal, &content); err != nil {
return err
}
if err = unstructured.SetNestedField(u.Object, content, append(path, "spec")...); err != nil {
return err
}
return nil
}
func (d *CloneOptions) Cleanup(ctx context.Context, workloads ...string) error {
if len(workloads) == 0 {
workloads = d.Workloads
for _, v := range d.TargetWorkloadNames {
workloads = append(workloads, v)
}
}
for _, workload := range workloads {
plog.G(ctx).Infof("Cleaning up clone workload: %s", workload)
object, err := util.GetUnstructuredObject(d.factory, d.Namespace, workload)
if err != nil {
plog.G(ctx).Errorf("Failed to get unstructured object error: %s", err.Error())
return err
}
labelsMap := map[string]string{
config.ManageBy: config.ConfigMapPodTrafficManager,
"origin-workload": object.Name,
}
selector := labels.SelectorFromSet(labelsMap)
controller, err := util.GetTopOwnerReferenceBySelector(d.targetFactory, d.TargetNamespace, selector.String())
if err != nil {
plog.G(ctx).Errorf("Failed to get controller error: %s", err.Error())
plog.G(ctx).Errorf("Failed to get unstructured object: %s", err)
return err
}
var client dynamic.Interface
client, err = d.targetFactory.DynamicClient()
client, err = d.factory.DynamicClient()
if err != nil {
plog.G(ctx).Errorf("Failed to get dynamic client error: %s", err.Error())
plog.G(ctx).Errorf("Failed to get dynamic client: %v", err)
return err
}
for _, cloneName := range controller.UnsortedList() {
split := strings.Split(cloneName, "/")
if len(split) == 2 {
cloneName = split[1]
}
err = client.Resource(object.Mapping.Resource).Namespace(d.TargetNamespace).Delete(context.Background(), cloneName, metav1.DeleteOptions{})
if err != nil && !apierrors.IsNotFound(err) {
plog.G(ctx).Errorf("Failed to delete clone object: %v", err)
return err
}
plog.G(ctx).Infof("Deleted clone object: %s", cloneName)
err = client.Resource(object.Mapping.Resource).Namespace(d.Namespace).Delete(context.Background(), object.Name, metav1.DeleteOptions{})
if err != nil && !apierrors.IsNotFound(err) {
plog.G(ctx).Errorf("Failed to delete clone object: %v", err)
return err
}
plog.G(ctx).Debugf("Cleanup clone workload: %s successfully", workload)
plog.G(ctx).Infof("Deleted clone object: %s/%s", object.Mapping.Resource.GroupResource().Resource, object.Name)
}
for _, f := range d.rollbackFuncList {
if f != nil {