Rainbond/worker/server/server_test.go
2020-04-08 15:43:00 +08:00

444 lines
11 KiB
Go

package server
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"reflect"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/goodrain/rainbond/worker/appm/store"
v1 "github.com/goodrain/rainbond/worker/appm/types/v1"
"github.com/goodrain/rainbond/worker/server/pb"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func getReplicaSet() *appsv1.ReplicaSet {
layout := "2006-01-02T15:04:05Z"
creation, err := time.Parse(layout, "2019-08-15T12:15:56Z")
if err != nil {
fmt.Printf("%s\n", err.Error())
}
return &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
CreationTimestamp: metav1.NewTime(creation),
},
}
}
func getPods(filename string) ([]*corev1.Pod, error) {
pfile, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("error opening '%s': %v", filename, err.Error())
}
var pods []*corev1.Pod
if err := json.Unmarshal([]byte(pfile), &pods); err != nil {
return nil, fmt.Errorf("error unmarshaling pfile: %v", err.Error())
}
return pods, nil
}
func serviceAppPodListEqual(a, b *pb.ServiceAppPodList) bool {
if a == b {
return true
}
podequal := func(m, n []*pb.ServiceAppPod) bool {
if len(m) != len(n) {
return false
}
return true
}
if podequal(a.OldPods, b.OldPods) && podequal(a.NewPods, b.NewPods) {
return true
}
return false
}
func TestRuntimeServer_GetAppPods(t *testing.T) {
replicaset := getReplicaSet()
newpods, err := getPods("newpods.json")
if err != nil {
t.Errorf("error getting new pods: %s", err.Error())
}
oldpods, err := getPods("oldpods.json")
if err != nil {
t.Errorf("error getting old pods: %s", err.Error())
}
tests := []struct {
name, sid string
as *v1.AppService
expres *pb.ServiceAppPodList
experr bool
}{
{name: "empty result", sid: "dummy service id 1", as: nil, expres: nil, experr: false},
{name: "only new pods", sid: "dummy service id 2", as: func() *v1.AppService {
as := &v1.AppService{}
as.SetReplicaSets(replicaset)
for _, pod := range newpods {
as.SetPods(pod)
}
return as
}(), expres: &pb.ServiceAppPodList{
NewPods: []*pb.ServiceAppPod{{}, {}, {}},
}, experr: false},
{name: "old and new pods", sid: "dummy service id 4", as: func() *v1.AppService {
as := &v1.AppService{}
as.SetReplicaSets(replicaset)
pods := append(oldpods, newpods...)
for _, pod := range pods {
as.SetPods(pod)
}
return as
}(), expres: &pb.ServiceAppPodList{
OldPods: []*pb.ServiceAppPod{{}, {}, {}},
NewPods: []*pb.ServiceAppPod{{}, {}, {}},
}, experr: false},
}
for idx := range tests {
tc := tests[idx]
t.Run(tc.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
storer := store.NewMockStorer(ctrl)
rserver := &RuntimeServer{
store: storer,
}
storer.EXPECT().GetAppService(tc.sid).Return(tc.as)
sreq := &pb.ServiceRequest{ServiceId: tc.sid}
res, err := rserver.GetAppPods(context.Background(), sreq)
if tc.experr != (err != nil) {
t.Errorf("Unexpected error: %v", err)
}
if !serviceAppPodListEqual(res, tc.expres) {
t.Errorf("Expected %+v for pods\n, but returned %v", tc.expres, res)
}
})
}
}
func TestListEvents(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
storer := store.NewMockStorer(ctrl)
c, err := clientcmd.BuildConfigFromFlags("", "/Users/abe/go/src/github.com/goodrain/rainbond/test/admin.kubeconfig")
if err != nil {
t.Fatalf("read kube config file error: %v", err)
}
clientset, err := kubernetes.NewForConfig(c)
if err != nil {
t.Fatalf("create kube api client error: %v", err)
}
rserver := &RuntimeServer{
store: storer,
clientset: clientset,
}
as := &v1.AppService{}
storer.EXPECT().GetAppService("sid").Return(as)
name := "88d8c4c55657217522f3bb86cfbded7e-deployment-647b84b467-kd6zc"
namespace := "c1a29fe4d7b0413993dc859430cf743d"
podEvents := rserver.listPodEventsByName(name, namespace)
t.Logf("pod events: %v", podEvents)
}
func TestRuntimeServer_GetAppVolumeStatus(t *testing.T) {
tests := []struct {
name string
re *pb.ServiceRequest
as *v1.AppService
want *pb.ServiceVolumeStatusMessage
wantErr bool
}{
{
name: "running pod",
re: &pb.ServiceRequest{
ServiceId: "serviceID",
},
as: func() *v1.AppService {
as := &v1.AppService{}
as.SetPods(&corev1.Pod{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{
{
Name: "manual1",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{},
},
},
{
Name: "default-token-xwxgv",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{},
},
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,
},
})
return as
}(),
want: &pb.ServiceVolumeStatusMessage{
Status: map[string]pb.ServiceVolumeStatus{"1": pb.ServiceVolumeStatus_READY},
},
wantErr: false,
},
{
name: "PodStatus_PENDING",
re: &pb.ServiceRequest{
ServiceId: "serviceID",
},
as: func() *v1.AppService {
as := &v1.AppService{}
as.SetPods(&corev1.Pod{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{
{
Name: "manual1",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{},
},
},
{
Name: "default-token-xwxgv",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{},
},
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodPending,
ContainerStatuses: []corev1.ContainerStatus{
{
Ready: false,
},
},
},
})
return as
}(),
want: &pb.ServiceVolumeStatusMessage{
Status: map[string]pb.ServiceVolumeStatus{"1": pb.ServiceVolumeStatus_NOT_READY},
},
wantErr: false,
},
{
name: "PodStatus_INITIATING",
re: &pb.ServiceRequest{
ServiceId: "serviceID",
},
as: func() *v1.AppService {
as := &v1.AppService{}
as.SetPods(&corev1.Pod{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{
{
Name: "manual1",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{},
},
},
{
Name: "default-token-xwxgv",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{},
},
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodPending,
Conditions: []corev1.PodCondition{
{
Type: corev1.PodInitialized,
Status: corev1.ConditionTrue,
},
},
ContainerStatuses: []corev1.ContainerStatus{
{
Ready: false,
},
},
},
})
return as
}(),
want: &pb.ServiceVolumeStatusMessage{
Status: map[string]pb.ServiceVolumeStatus{"1": pb.ServiceVolumeStatus_READY},
},
wantErr: false,
},
{
name: "PodStatus_ABNORMAL",
re: &pb.ServiceRequest{
ServiceId: "serviceID",
},
as: func() *v1.AppService {
as := &v1.AppService{}
as.SetPods(&corev1.Pod{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{
{
Name: "manual1",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{},
},
},
{
Name: "default-token-xwxgv",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{},
},
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,
Conditions: []corev1.PodCondition{
{
Type: corev1.PodReady,
Status: corev1.ConditionTrue,
},
},
ContainerStatuses: []corev1.ContainerStatus{
{
Ready: false,
State: corev1.ContainerState{
Waiting: nil,
Running: nil,
Terminated: &corev1.ContainerStateTerminated{},
},
},
},
},
})
return as
}(),
want: &pb.ServiceVolumeStatusMessage{
Status: map[string]pb.ServiceVolumeStatus{"1": pb.ServiceVolumeStatus_READY},
},
wantErr: false,
},
{
name: "PodStatus_NOTREADY",
re: &pb.ServiceRequest{
ServiceId: "serviceID",
},
as: func() *v1.AppService {
as := &v1.AppService{}
as.SetPods(&corev1.Pod{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{
{
Name: "manual1",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{},
},
},
{
Name: "default-token-xwxgv",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{},
},
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,
Conditions: []corev1.PodCondition{
{
Type: corev1.PodReady,
Status: corev1.ConditionTrue,
},
},
ContainerStatuses: []corev1.ContainerStatus{},
},
})
return as
}(),
want: &pb.ServiceVolumeStatusMessage{
Status: map[string]pb.ServiceVolumeStatus{"1": pb.ServiceVolumeStatus_READY},
},
wantErr: false,
},
{
name: "PodStatus_SCHEDULING",
re: &pb.ServiceRequest{
ServiceId: "serviceID",
},
as: func() *v1.AppService {
as := &v1.AppService{}
as.SetPods(&corev1.Pod{
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{
{
Name: "manual1",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{},
},
},
{
Name: "default-token-xwxgv",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{},
},
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodPending,
Conditions: []corev1.PodCondition{
{
Type: corev1.PodScheduled,
Status: corev1.ConditionFalse,
},
},
},
})
return as
}(),
want: &pb.ServiceVolumeStatusMessage{
Status: map[string]pb.ServiceVolumeStatus{"1": pb.ServiceVolumeStatus_NOT_READY},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
storer := store.NewMockStorer(ctrl)
r := &RuntimeServer{
store: storer,
}
storer.EXPECT().GetAppService(tt.re.ServiceId).Return(tt.as)
got, err := r.GetAppVolumeStatus(context.Background(), tt.re)
if (err != nil) != tt.wantErr {
t.Errorf("GetAppVolumeStatus() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetAppVolumeStatus() got = %v, want %v", got, tt.want)
}
})
}
}