mirror of
https://github.com/wahyd4/kt-connect.git
synced 2026-08-08 21:08:04 +10:00
test: add test case for run command
This commit is contained in:
@@ -78,17 +78,17 @@ func (mr *MockActionInterfaceMockRecorder) Check(cli interface{}) *gomock.Call {
|
||||
}
|
||||
|
||||
// Run mocks base method
|
||||
func (m *MockActionInterface) Run(service string, options *options.DaemonOptions) error {
|
||||
func (m *MockActionInterface) Run(service string, cli kt.CliInterface, options *options.DaemonOptions) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Run", service, options)
|
||||
ret := m.ctrl.Call(m, "Run", service, cli, options)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Run indicates an expected call of Run
|
||||
func (mr *MockActionInterfaceMockRecorder) Run(service, options interface{}) *gomock.Call {
|
||||
func (mr *MockActionInterfaceMockRecorder) Run(service, cli, options interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockActionInterface)(nil).Run), service, options)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockActionInterface)(nil).Run), service, cli, options)
|
||||
}
|
||||
|
||||
// Exchange mocks base method
|
||||
|
||||
@@ -37,11 +37,12 @@ func (m *MockCliInterface) EXPECT() *MockCliInterfaceMockRecorder {
|
||||
}
|
||||
|
||||
// Kubernetes mocks base method
|
||||
func (m *MockCliInterface) Kubernetes() cluster.KubernetesInterface {
|
||||
func (m *MockCliInterface) Kubernetes() (cluster.KubernetesInterface, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Kubernetes")
|
||||
ret0, _ := ret[0].(cluster.KubernetesInterface)
|
||||
return ret0
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Kubernetes indicates an expected call of Kubernetes
|
||||
|
||||
@@ -33,14 +33,63 @@ func (k *Kubernetes) Scale(deployment *appV1.Deployment, replicas *int32) (err e
|
||||
}
|
||||
|
||||
// Deployment get deployment
|
||||
func (k *Kubernetes) Deployment(name, namespace string) (deployment *appV1.Deployment, err error) {
|
||||
deployment, err = k.Clientset.AppsV1().Deployments(namespace).Get(name, metaV1.GetOptions{})
|
||||
return
|
||||
func (k *Kubernetes) Deployment(name, namespace string) (*appV1.Deployment, error) {
|
||||
return k.Clientset.AppsV1().Deployments(namespace).Get(name, metaV1.GetOptions{})
|
||||
}
|
||||
|
||||
// CreateShadow create shadow
|
||||
func (k *Kubernetes) CreateShadow(name, namespace, image string, labels map[string]string, debug bool) (podIP, podName, sshcm string, credential *util.SSHCredential, err error) {
|
||||
return CreateShadow(k.Clientset, name, labels, namespace, image, debug)
|
||||
component, version := labels["kt-component"], labels["version"]
|
||||
sshcm = fmt.Sprintf("kt-%s-public-key-%s", component, version)
|
||||
|
||||
generator, err := util.Generate(util.PrivateKeyPath(component, version))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
clientSet := k.Clientset
|
||||
|
||||
labels["kt"] = sshcm
|
||||
cli := clientSet.CoreV1().ConfigMaps(namespace)
|
||||
configMap, err := cli.Create(&v1.ConfigMap{
|
||||
ObjectMeta: metaV1.ObjectMeta{
|
||||
Name: sshcm,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
Data: map[string]string{
|
||||
vars.SSHAuthKey: string(generator.PublicKey),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Info().Msgf("successful create ssh config map %v", configMap.ObjectMeta.Name)
|
||||
|
||||
localIPAddress := util.GetOutboundIP()
|
||||
log.Info().Msgf("Client address %s", localIPAddress)
|
||||
labels["remoteAddress"] = localIPAddress
|
||||
|
||||
labels["kt"] = name
|
||||
client := clientSet.AppsV1().Deployments(namespace)
|
||||
deployment := generatorDeployment(namespace, name, labels, image, sshcm, debug)
|
||||
log.Info().Msg("shadow template is prepare ready.")
|
||||
result, err := client.Create(deployment)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Info().Msgf("deploy shadow deployment %s in namespace %s\n", result.GetObjectMeta().GetName(), namespace)
|
||||
|
||||
pod, err := waitPodReadyUsingInformer(namespace, name, clientSet)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
podIP = pod.Status.PodIP
|
||||
podName = pod.GetObjectMeta().GetName()
|
||||
credential = util.NewDefaultSSHCredential()
|
||||
credential.PrivateKeyPath = generator.PrivateKeyPath
|
||||
return
|
||||
}
|
||||
|
||||
// CreateService create kubernetes service
|
||||
@@ -136,66 +185,7 @@ func RemoveService(
|
||||
return client.Delete(name, &metaV1.DeleteOptions{})
|
||||
}
|
||||
|
||||
// CreateShadow create shadow
|
||||
func CreateShadow(
|
||||
clientset *kubernetes.Clientset,
|
||||
name string,
|
||||
labels map[string]string,
|
||||
namespace,
|
||||
image string,
|
||||
debug bool,
|
||||
) (podIP, podName, sshcm string, credential *util.SSHCredential, err error) {
|
||||
|
||||
component, version := labels["kt-component"], labels["version"]
|
||||
sshcm = fmt.Sprintf("kt-%s-public-key-%s", component, version)
|
||||
|
||||
generator, err := util.Generate(util.PrivateKeyPath(component, version))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
labels["kt"] = sshcm
|
||||
cli := clientset.CoreV1().ConfigMaps(namespace)
|
||||
_, err = cli.Create(&v1.ConfigMap{
|
||||
ObjectMeta: metaV1.ObjectMeta{
|
||||
Name: sshcm,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
Data: map[string]string{
|
||||
vars.SSHAuthKey: string(generator.PublicKey),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
localIPAddress := util.GetOutboundIP()
|
||||
log.Info().Msgf("Client address %s", localIPAddress)
|
||||
labels["remoteAddress"] = localIPAddress
|
||||
|
||||
labels["kt"] = name
|
||||
client := clientset.AppsV1().Deployments(namespace)
|
||||
deployment := generatorDeployment(namespace, name, labels, image, sshcm, debug)
|
||||
result, err := client.Create(deployment)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Info().Msgf("deploy shadow deployment %s in namespace %s\n", result.GetObjectMeta().GetName(), namespace)
|
||||
|
||||
// pod, err := waitPodReady(namespace, name, clientset)
|
||||
pod, err := waitPodReadyUsingInformer(namespace, name, clientset)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
podIP = pod.Status.PodIP
|
||||
podName = pod.GetObjectMeta().GetName()
|
||||
credential = util.NewDefaultSSHCredential()
|
||||
credential.PrivateKeyPath = generator.PrivateKeyPath
|
||||
return
|
||||
}
|
||||
|
||||
func waitPodReadyUsingInformer(namespace, name string, clientset *kubernetes.Clientset) (pod v1.Pod, err error) {
|
||||
func waitPodReadyUsingInformer(namespace, name string, clientset kubernetes.Interface) (pod v1.Pod, err error) {
|
||||
stopSignal := make(chan struct{})
|
||||
defer close(stopSignal)
|
||||
podListener, err := clusterWatcher.PodListener(clientset, stopSignal)
|
||||
|
||||
@@ -34,7 +34,7 @@ type KubernetesInterface interface {
|
||||
type Kubernetes struct {
|
||||
KubeConfig string
|
||||
// TODO: should remove
|
||||
Clientset *kubernetes.Clientset
|
||||
Clientset kubernetes.Interface
|
||||
// TODO: should remove
|
||||
ServiceListener v1.ServiceLister
|
||||
}
|
||||
|
||||
+20
-19
@@ -7,33 +7,31 @@ import (
|
||||
|
||||
"github.com/alibaba/kt-connect/pkg/kt"
|
||||
|
||||
"github.com/alibaba/kt-connect/pkg/kt/cluster"
|
||||
"github.com/alibaba/kt-connect/pkg/kt/connect"
|
||||
"github.com/alibaba/kt-connect/pkg/kt/options"
|
||||
"github.com/alibaba/kt-connect/pkg/kt/util"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/urfave/cli"
|
||||
urfave "github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// newRunCommand return new run command
|
||||
func newRunCommand(kt kt.CliInterface, options *options.DaemonOptions, action ActionInterface) cli.Command {
|
||||
return cli.Command{
|
||||
func newRunCommand(cli kt.CliInterface, options *options.DaemonOptions, action ActionInterface) urfave.Command {
|
||||
return urfave.Command{
|
||||
Name: "run",
|
||||
Usage: "create a shadow deployment to redirect request to user local",
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{
|
||||
Flags: []urfave.Flag{
|
||||
urfave.IntFlag{
|
||||
Name: "port",
|
||||
Usage: "The port that exposes",
|
||||
Destination: &options.RunOptions.Port,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
urfave.BoolFlag{
|
||||
Name: "expose",
|
||||
Usage: " If true, a public, external service is created",
|
||||
Destination: &options.RunOptions.Expose,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
Action: func(c *urfave.Context) error {
|
||||
if options.Debug {
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
@@ -41,15 +39,22 @@ func newRunCommand(kt kt.CliInterface, options *options.DaemonOptions, action Ac
|
||||
if port == 0 {
|
||||
return errors.New("--port is required")
|
||||
}
|
||||
return action.Run(c.Args().First(), options)
|
||||
return action.Run(c.Args().First(), cli, options)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Run create a new service in cluster
|
||||
func (action *Action) Run(service string, options *options.DaemonOptions) error {
|
||||
func (action *Action) Run(service string, cli kt.CliInterface, options *options.DaemonOptions) error {
|
||||
ch := SetUpCloseHandler(options)
|
||||
kubernetes, err := cluster.Create(options.KubeConfig)
|
||||
run(service, cli, options)
|
||||
<-ch
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run create a new service in cluster
|
||||
func run(service string, cli kt.CliInterface, options *options.DaemonOptions) error {
|
||||
kubernetes, err := cli.Kubernetes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -74,7 +79,8 @@ func (action *Action) Run(service string, options *options.DaemonOptions) error
|
||||
|
||||
if options.RunOptions.Expose {
|
||||
log.Info().Msgf("expose deployment %s to %s:%v", service, service, options.RunOptions.Port)
|
||||
if _, err = kubernetes.CreateService(service, options.Namespace, options.RunOptions.Port, labels); err != nil {
|
||||
_, err = kubernetes.CreateService(service, options.Namespace, options.RunOptions.Port, labels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.RuntimeOptions.Service = service
|
||||
@@ -83,16 +89,11 @@ func (action *Action) Run(service string, options *options.DaemonOptions) error
|
||||
options.RuntimeOptions.Shadow = service
|
||||
options.RuntimeOptions.SSHCM = sshcm
|
||||
|
||||
shadow := connect.Create(options)
|
||||
err = shadow.Inbound(strconv.Itoa(options.RunOptions.Port), podName, podIP, credential)
|
||||
err = cli.Shadow().Inbound(strconv.Itoa(options.RunOptions.Port), podName, podIP, credential)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Msgf("forward remote %s:%v -> 127.0.0.1:%v", podIP, options.RunOptions.Port, options.RunOptions.Port)
|
||||
|
||||
s := <-ch
|
||||
log.Info().Msgf("Terminal Signal is %s", s)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+120
-5
@@ -6,21 +6,24 @@ import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/alibaba/kt-connect/pkg/fake/kt"
|
||||
|
||||
fakeKt "github.com/alibaba/kt-connect/pkg/fake/kt"
|
||||
"github.com/alibaba/kt-connect/pkg/fake/kt/action"
|
||||
"github.com/alibaba/kt-connect/pkg/fake/kt/cluster"
|
||||
"github.com/alibaba/kt-connect/pkg/fake/kt/connect"
|
||||
"github.com/alibaba/kt-connect/pkg/kt/options"
|
||||
"github.com/alibaba/kt-connect/pkg/kt/util"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/urfave/cli"
|
||||
coreV1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func Test_runCommand(t *testing.T) {
|
||||
|
||||
ctl := gomock.NewController(t)
|
||||
mockAction := action.NewMockActionInterface(ctl)
|
||||
fakeKtCli := kt.NewMockCliInterface(ctl)
|
||||
fakeKtCli := fakeKt.NewMockCliInterface(ctl)
|
||||
|
||||
mockAction.EXPECT().Run(gomock.Eq("service"), gomock.Any()).Return(nil).AnyTimes()
|
||||
mockAction := action.NewMockActionInterface(ctl)
|
||||
mockAction.EXPECT().Run(gomock.Eq("service"), fakeKtCli, gomock.Any()).Return(nil).AnyTimes()
|
||||
|
||||
cases := []struct {
|
||||
testArgs []string
|
||||
@@ -56,3 +59,115 @@ func Test_runCommand(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_run(t *testing.T) {
|
||||
|
||||
ctl := gomock.NewController(t)
|
||||
fakeKtCli := fakeKt.NewMockCliInterface(ctl)
|
||||
kubernetes := cluster.NewMockKubernetesInterface(ctl)
|
||||
shadow := connect.NewMockShadowInterface(ctl)
|
||||
|
||||
fakeKtCli.EXPECT().Kubernetes().AnyTimes().Return(kubernetes, nil)
|
||||
fakeKtCli.EXPECT().Shadow().AnyTimes().Return(shadow)
|
||||
|
||||
type args struct {
|
||||
service string
|
||||
options *options.DaemonOptions
|
||||
shadowResponse createShadowResponse
|
||||
serviceResponse createServiceResponse
|
||||
inboundResponse inboundResponse
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "shouldExposeLocalServiceToCluster",
|
||||
args: args{
|
||||
service: "test",
|
||||
options: options.NewRunDaemonOptions(
|
||||
"aa=bb",
|
||||
&options.RunOptions{
|
||||
Expose: true,
|
||||
Port: 8081,
|
||||
}),
|
||||
shadowResponse: createShadowResponse{
|
||||
podIP: "172.168.0.1",
|
||||
podName: "shadow",
|
||||
sshcm: "shadow-ssh-cm",
|
||||
credential: &util.SSHCredential{
|
||||
RemoteHost: "127.0.0.1",
|
||||
Port: "2222",
|
||||
PrivateKeyPath: "/tmp/pk",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
serviceResponse: createServiceResponse{
|
||||
service: &coreV1.Service{},
|
||||
err: nil,
|
||||
},
|
||||
inboundResponse: inboundResponse{
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "shouldExposeLocalServiceFailWhenShadowCreateFail",
|
||||
args: args{
|
||||
service: "test2",
|
||||
options: options.NewRunDaemonOptions(
|
||||
"aaa=bbb",
|
||||
&options.RunOptions{
|
||||
Expose: true,
|
||||
Port: 8081,
|
||||
}),
|
||||
shadowResponse: createShadowResponse{
|
||||
podIP: "172.168.0.1",
|
||||
podName: "shadow",
|
||||
sshcm: "shadow-ssh-cm",
|
||||
credential: &util.SSHCredential{
|
||||
RemoteHost: "127.0.0.1",
|
||||
Port: "2222",
|
||||
PrivateKeyPath: "/tmp/pk",
|
||||
},
|
||||
err: errors.New("fail create shadow"),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
kubernetes.EXPECT().
|
||||
CreateShadow(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).
|
||||
Return(tt.args.shadowResponse.podIP, tt.args.shadowResponse.podName, tt.args.shadowResponse.sshcm, tt.args.shadowResponse.credential, tt.args.shadowResponse.err)
|
||||
kubernetes.EXPECT().CreateService(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(tt.args.serviceResponse.service, tt.args.serviceResponse.err)
|
||||
shadow.EXPECT().
|
||||
Inbound(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).
|
||||
Return(tt.args.inboundResponse.err)
|
||||
|
||||
if err := run(tt.args.service, fakeKtCli, tt.args.options); (err != nil) != tt.wantErr {
|
||||
t.Errorf("run() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type inboundResponse struct {
|
||||
err error
|
||||
}
|
||||
|
||||
type createServiceResponse struct {
|
||||
service *coreV1.Service
|
||||
err error
|
||||
}
|
||||
|
||||
type createShadowResponse struct {
|
||||
podIP string
|
||||
podName string
|
||||
sshcm string
|
||||
credential *util.SSHCredential
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ type ActionInterface interface {
|
||||
OpenDashboard(options *options.DaemonOptions) error
|
||||
Connect(options *options.DaemonOptions) error
|
||||
Check(cli kt.CliInterface) error
|
||||
Run(service string, options *options.DaemonOptions) error
|
||||
Run(service string, cli kt.CliInterface, options *options.DaemonOptions) error
|
||||
Exchange(service string, options *options.DaemonOptions) error
|
||||
Mesh(service string, options *options.DaemonOptions) error
|
||||
ApplyDashboard(options *options.DaemonOptions) error
|
||||
|
||||
@@ -17,10 +17,9 @@ import (
|
||||
|
||||
// Inbound mapping local port from cluster
|
||||
func (s *Shadow) Inbound(exposePort, podName, remoteIP string, credential *util.SSHCredential) (err error) {
|
||||
kubernetesCli := &kubectl.Cli{
|
||||
KubeConfig: s.Options.KubeConfig,
|
||||
}
|
||||
kubernetesCli := &kubectl.Cli{KubeConfig: s.Options.KubeConfig}
|
||||
sshCli := &ssh.Cli{}
|
||||
log.Info().Msg("creating shadow inbound(remote->local)")
|
||||
return inbound(exposePort, podName, remoteIP, credential, s.Options, kubernetesCli, sshCli)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import (
|
||||
"github.com/alibaba/kt-connect/pkg/kt/util"
|
||||
)
|
||||
|
||||
type runOptions struct {
|
||||
// RunOptions ...
|
||||
type RunOptions struct {
|
||||
Expose bool
|
||||
Port int
|
||||
}
|
||||
@@ -57,7 +58,7 @@ type DaemonOptions struct {
|
||||
Image string
|
||||
Labels string
|
||||
RuntimeOptions *runtimeOptions
|
||||
RunOptions *runOptions
|
||||
RunOptions *RunOptions
|
||||
ConnectOptions *connectOptions
|
||||
ExchangeOptions *exchangeOptions
|
||||
MeshOptions *meshOptions
|
||||
@@ -80,6 +81,14 @@ func NewDaemonOptions() *DaemonOptions {
|
||||
ExchangeOptions: &exchangeOptions{},
|
||||
MeshOptions: &meshOptions{},
|
||||
DashboardOptions: &dashboardOptions{},
|
||||
RunOptions: &runOptions{},
|
||||
RunOptions: &RunOptions{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewRunDaemonOptions ...
|
||||
func NewRunDaemonOptions(labels string, options *RunOptions) *DaemonOptions {
|
||||
daemonOptions := NewDaemonOptions()
|
||||
daemonOptions.Labels = labels
|
||||
daemonOptions.RunOptions = options
|
||||
return daemonOptions
|
||||
}
|
||||
|
||||
+8
-3
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// CliInterface ...
|
||||
type CliInterface interface {
|
||||
Kubernetes() cluster.KubernetesInterface
|
||||
Kubernetes() (cluster.KubernetesInterface, error)
|
||||
Shadow() connect.ShadowInterface
|
||||
Exec() exec.CliInterface
|
||||
}
|
||||
@@ -20,10 +20,15 @@ type Cli struct {
|
||||
}
|
||||
|
||||
// Kubernetes ...
|
||||
func (c *Cli) Kubernetes() cluster.KubernetesInterface {
|
||||
func (c *Cli) Kubernetes() (cluster.KubernetesInterface, error) {
|
||||
clientset, err := cluster.GetKubernetesClient(c.Options.KubeConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cluster.Kubernetes{
|
||||
KubeConfig: c.Options.KubeConfig,
|
||||
}
|
||||
Clientset: clientset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Shadow ...
|
||||
|
||||
Reference in New Issue
Block a user