devlopment branch (#1736)
This commit is contained in:
@@ -19,47 +19,56 @@ package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/klog"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/api/resource/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
)
|
||||
|
||||
func GetComponentStatus(name string) (interface{}, error) {
|
||||
type ComponentsGetter interface {
|
||||
GetComponentStatus(name string) (v1alpha2.ComponentStatus, error)
|
||||
GetSystemHealthStatus() (v1alpha2.HealthStatus, error)
|
||||
GetAllComponentsStatus() ([]v1alpha2.ComponentStatus, error)
|
||||
}
|
||||
|
||||
type componentsGetter struct {
|
||||
informers informers.SharedInformerFactory
|
||||
}
|
||||
|
||||
func NewComponentsGetter(informers informers.SharedInformerFactory) ComponentsGetter {
|
||||
return &componentsGetter{informers: informers}
|
||||
}
|
||||
|
||||
func (c *componentsGetter) GetComponentStatus(name string) (v1alpha2.ComponentStatus, error) {
|
||||
|
||||
var service *corev1.Service
|
||||
var err error
|
||||
|
||||
serviceLister := informers.SharedInformerFactory().Core().V1().Services().Lister()
|
||||
|
||||
for _, ns := range constants.SystemNamespaces {
|
||||
service, err = serviceLister.Services(ns).Get(name)
|
||||
service, err = c.informers.Core().V1().Services().Lister().Services(ns).Get(name)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return v1alpha2.ComponentStatus{}, err
|
||||
}
|
||||
|
||||
if len(service.Spec.Selector) == 0 {
|
||||
return nil, fmt.Errorf("component %s has no selector", name)
|
||||
return v1alpha2.ComponentStatus{}, fmt.Errorf("component %s has no selector", name)
|
||||
}
|
||||
|
||||
podLister := informers.SharedInformerFactory().Core().V1().Pods().Lister()
|
||||
|
||||
pods, err := podLister.Pods(service.Namespace).List(labels.SelectorFromValidatedSet(service.Spec.Selector))
|
||||
pods, err := c.informers.Core().V1().Pods().Lister().Pods(service.Namespace).List(labels.SelectorFromValidatedSet(service.Spec.Selector))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return v1alpha2.ComponentStatus{}, err
|
||||
}
|
||||
|
||||
component := models.ComponentStatus{
|
||||
component := v1alpha2.ComponentStatus{
|
||||
Name: service.Name,
|
||||
Namespace: service.Namespace,
|
||||
SelfLink: service.SelfLink,
|
||||
@@ -86,21 +95,20 @@ func isAllContainersReady(pod *corev1.Pod) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func GetSystemHealthStatus() (*models.HealthStatus, error) {
|
||||
func (c *componentsGetter) GetSystemHealthStatus() (v1alpha2.HealthStatus, error) {
|
||||
|
||||
status := &models.HealthStatus{}
|
||||
status := v1alpha2.HealthStatus{}
|
||||
|
||||
// get kubesphere-system components
|
||||
components, err := GetAllComponentsStatus()
|
||||
components, err := c.GetAllComponentsStatus()
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
}
|
||||
|
||||
status.KubeSphereComponents = components
|
||||
|
||||
nodeLister := informers.SharedInformerFactory().Core().V1().Nodes().Lister()
|
||||
// get node status
|
||||
nodes, err := nodeLister.List(labels.Everything())
|
||||
nodes, err := c.informers.Core().V1().Nodes().Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return status, nil
|
||||
@@ -116,7 +124,7 @@ func GetSystemHealthStatus() (*models.HealthStatus, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
nodeStatus := models.NodeStatus{TotalNodes: totalNodes, HealthyNodes: healthyNodes}
|
||||
nodeStatus := v1alpha2.NodeStatus{TotalNodes: totalNodes, HealthyNodes: healthyNodes}
|
||||
|
||||
status.NodeStatus = nodeStatus
|
||||
|
||||
@@ -124,16 +132,14 @@ func GetSystemHealthStatus() (*models.HealthStatus, error) {
|
||||
|
||||
}
|
||||
|
||||
func GetAllComponentsStatus() ([]models.ComponentStatus, error) {
|
||||
serviceLister := informers.SharedInformerFactory().Core().V1().Services().Lister()
|
||||
podLister := informers.SharedInformerFactory().Core().V1().Pods().Lister()
|
||||
func (c *componentsGetter) GetAllComponentsStatus() ([]v1alpha2.ComponentStatus, error) {
|
||||
|
||||
components := make([]models.ComponentStatus, 0)
|
||||
components := make([]v1alpha2.ComponentStatus, 0)
|
||||
|
||||
var err error
|
||||
for _, ns := range constants.SystemNamespaces {
|
||||
|
||||
services, err := serviceLister.Services(ns).List(labels.Everything())
|
||||
services, err := c.informers.Core().V1().Services().Lister().Services(ns).List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
@@ -147,7 +153,7 @@ func GetAllComponentsStatus() ([]models.ComponentStatus, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
component := models.ComponentStatus{
|
||||
component := v1alpha2.ComponentStatus{
|
||||
Name: service.Name,
|
||||
Namespace: service.Namespace,
|
||||
SelfLink: service.SelfLink,
|
||||
@@ -157,7 +163,7 @@ func GetAllComponentsStatus() ([]models.ComponentStatus, error) {
|
||||
TotalBackends: 0,
|
||||
}
|
||||
|
||||
pods, err := podLister.Pods(ns).List(labels.SelectorFromValidatedSet(service.Spec.Selector))
|
||||
pods, err := c.informers.Core().V1().Pods().Lister().Pods(ns).List(labels.SelectorFromValidatedSet(service.Spec.Selector))
|
||||
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
|
||||
356
pkg/models/components/components_test.go
Normal file
356
pkg/models/components/components_test.go
Normal file
@@ -0,0 +1,356 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"kubesphere.io/kubesphere/pkg/api/resource/v1alpha2"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func service(name, namespace string, selector map[string]string) runtime.Object {
|
||||
return &v1.Service{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Service",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
Selector: selector,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func pods(name, namespace string, labels map[string]string, healthPods, totalPods int) []runtime.Object {
|
||||
var ps []runtime.Object
|
||||
|
||||
for index := 0; index < totalPods; index++ {
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-%d", name, index),
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodRunning,
|
||||
ContainerStatuses: []v1.ContainerStatus{
|
||||
{
|
||||
Name: fmt.Sprintf("%s-%d", name, index),
|
||||
Ready: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if index >= healthPods {
|
||||
pod.Status.Phase = v1.PodPending
|
||||
pod.Status.ContainerStatuses[0].Ready = false
|
||||
}
|
||||
|
||||
ps = append(ps, pod)
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func nodes(name string, healthNodes, totalNodes int) []runtime.Object {
|
||||
var ns []runtime.Object
|
||||
|
||||
for index := 0; index < totalNodes; index++ {
|
||||
node := &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-%d", name, index),
|
||||
},
|
||||
Status: v1.NodeStatus{
|
||||
Phase: v1.NodeRunning,
|
||||
Conditions: []v1.NodeCondition{
|
||||
{
|
||||
Type: v1.NodeReady,
|
||||
Status: v1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if index >= healthNodes {
|
||||
node.Status.Phase = v1.NodePending
|
||||
node.Status.Conditions[0].Status = v1.ConditionFalse
|
||||
}
|
||||
|
||||
ns = append(ns, node)
|
||||
}
|
||||
|
||||
return ns
|
||||
}
|
||||
|
||||
func TestGetSystemHealthStatus(t *testing.T) {
|
||||
var tests = []struct {
|
||||
description string
|
||||
labels map[string]string
|
||||
namespace string
|
||||
name string
|
||||
healthPods int
|
||||
totalPods int
|
||||
healthNodes int
|
||||
totalNodes int
|
||||
expected v1alpha2.HealthStatus
|
||||
}{
|
||||
{
|
||||
"no backends",
|
||||
map[string]string{"app": "foo"},
|
||||
"kube-system",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
v1alpha2.HealthStatus{
|
||||
KubeSphereComponents: []v1alpha2.ComponentStatus{
|
||||
{
|
||||
Namespace: "kube-system",
|
||||
Label: map[string]string{"app": "foo"},
|
||||
TotalBackends: 0,
|
||||
HealthyBackends: 0,
|
||||
},
|
||||
},
|
||||
NodeStatus: v1alpha2.NodeStatus{},
|
||||
},
|
||||
},
|
||||
{
|
||||
"all healthy",
|
||||
map[string]string{"app": "foo"},
|
||||
"kubesphere-system",
|
||||
"ks-apiserver",
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
v1alpha2.HealthStatus{
|
||||
KubeSphereComponents: []v1alpha2.ComponentStatus{
|
||||
{
|
||||
Name: "ks-apiserver",
|
||||
Namespace: "kubesphere-system",
|
||||
Label: map[string]string{"app": "foo"},
|
||||
TotalBackends: 2,
|
||||
HealthyBackends: 2,
|
||||
},
|
||||
},
|
||||
NodeStatus: v1alpha2.NodeStatus{
|
||||
TotalNodes: 2,
|
||||
HealthyNodes: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"all unhealthy",
|
||||
map[string]string{"app": "foo"},
|
||||
"kubesphere-system",
|
||||
"ks-apiserver",
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
2,
|
||||
v1alpha2.HealthStatus{
|
||||
KubeSphereComponents: []v1alpha2.ComponentStatus{
|
||||
{
|
||||
Name: "ks-apiserver",
|
||||
Namespace: "kubesphere-system",
|
||||
Label: map[string]string{"app": "foo"},
|
||||
TotalBackends: 2,
|
||||
HealthyBackends: 0,
|
||||
},
|
||||
},
|
||||
NodeStatus: v1alpha2.NodeStatus{
|
||||
TotalNodes: 2,
|
||||
HealthyNodes: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"half healthy",
|
||||
map[string]string{"app": "foo"},
|
||||
"kubesphere-system",
|
||||
"ks-apiserver",
|
||||
2,
|
||||
4,
|
||||
2,
|
||||
4,
|
||||
v1alpha2.HealthStatus{
|
||||
KubeSphereComponents: []v1alpha2.ComponentStatus{
|
||||
{
|
||||
Name: "ks-apiserver",
|
||||
Namespace: "kubesphere-system",
|
||||
Label: map[string]string{"app": "foo"},
|
||||
TotalBackends: 4,
|
||||
HealthyBackends: 2,
|
||||
},
|
||||
},
|
||||
NodeStatus: v1alpha2.NodeStatus{
|
||||
TotalNodes: 4,
|
||||
HealthyNodes: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
ps := pods(test.name, test.namespace, test.labels, test.healthPods, test.totalPods)
|
||||
svc := service(test.name, test.namespace, test.labels)
|
||||
ns := nodes(test.name, test.healthNodes, test.totalNodes)
|
||||
|
||||
var objs []runtime.Object
|
||||
objs = append(objs, ps...)
|
||||
objs = append(objs, svc)
|
||||
objs = append(objs, ns...)
|
||||
|
||||
client := fake.NewSimpleClientset(objs...)
|
||||
|
||||
informer := informers.NewSharedInformerFactory(client, time.Minute*10)
|
||||
|
||||
informer.Core().V1().Services().Informer().GetIndexer().Add(svc)
|
||||
|
||||
for _, obj := range ps {
|
||||
informer.Core().V1().Pods().Informer().GetIndexer().Add(obj)
|
||||
}
|
||||
|
||||
for _, obj := range ns {
|
||||
informer.Core().V1().Nodes().Informer().GetIndexer().Add(obj)
|
||||
}
|
||||
|
||||
c := NewComponentsGetter(informer)
|
||||
healthStatus, err := c.GetSystemHealthStatus()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(healthStatus, test.expected); diff != "" {
|
||||
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetComponentStatus(t *testing.T) {
|
||||
var tests = []struct {
|
||||
description string
|
||||
name string
|
||||
namespace string
|
||||
labels map[string]string
|
||||
healthPods int
|
||||
totalPods int
|
||||
expected v1alpha2.ComponentStatus
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
"no component",
|
||||
"random",
|
||||
"foo",
|
||||
map[string]string{"app": "foo"},
|
||||
2,
|
||||
4,
|
||||
v1alpha2.ComponentStatus{
|
||||
Name: "",
|
||||
Namespace: "",
|
||||
SelfLink: "",
|
||||
Label: nil,
|
||||
StartedAt: time.Time{},
|
||||
TotalBackends: 0,
|
||||
HealthyBackends: 0,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"all healthy",
|
||||
"ks-apiserver",
|
||||
"kubesphere-system",
|
||||
map[string]string{"app": "foo"},
|
||||
2,
|
||||
4,
|
||||
v1alpha2.ComponentStatus{
|
||||
Name: "ks-apiserver",
|
||||
Namespace: "kubesphere-system",
|
||||
Label: map[string]string{"app": "foo"},
|
||||
TotalBackends: 4,
|
||||
HealthyBackends: 2,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"all unhealthy",
|
||||
"ks-apiserver",
|
||||
"kubesphere-system",
|
||||
map[string]string{"app": "foo"},
|
||||
0,
|
||||
4,
|
||||
v1alpha2.ComponentStatus{
|
||||
Name: "ks-apiserver",
|
||||
Namespace: "kubesphere-system",
|
||||
Label: map[string]string{"app": "foo"},
|
||||
TotalBackends: 4,
|
||||
HealthyBackends: 0,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"half healthy",
|
||||
"ks-apiserver",
|
||||
"kubesphere-system",
|
||||
map[string]string{"app": "foo"},
|
||||
2,
|
||||
4,
|
||||
v1alpha2.ComponentStatus{
|
||||
Name: "ks-apiserver",
|
||||
Namespace: "kubesphere-system",
|
||||
Label: map[string]string{"app": "foo"},
|
||||
TotalBackends: 4,
|
||||
HealthyBackends: 2,
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
ps := pods(test.name, test.namespace, test.labels, test.healthPods, test.totalPods)
|
||||
svc := service(test.name, test.namespace, test.labels)
|
||||
|
||||
var objs []runtime.Object
|
||||
objs = append(objs, ps...)
|
||||
objs = append(objs, svc)
|
||||
|
||||
client := fake.NewSimpleClientset(objs...)
|
||||
|
||||
informer := informers.NewSharedInformerFactory(client, time.Minute*10)
|
||||
|
||||
informer.Core().V1().Services().Informer().GetIndexer().Add(svc)
|
||||
|
||||
for _, obj := range ps {
|
||||
informer.Core().V1().Pods().Informer().GetIndexer().Add(obj)
|
||||
}
|
||||
|
||||
c := NewComponentsGetter(informer)
|
||||
healthStatus, err := c.GetComponentStatus(test.name)
|
||||
if err == nil && test.expectedError {
|
||||
t.Fatalf("expected error while got nothing")
|
||||
} else if err != nil && !test.expectedError {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(healthStatus, test.expected); diff != "" {
|
||||
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user