diff --git a/pkg/models/resources/v1alpha3/node/nodes.go b/pkg/models/resources/v1alpha3/node/nodes.go index dfc9ea516b87e4f29b8ca09e9f8dfb96a7bd2eb3..5bf36b7b8ac44edec12cd2ae0b4d5cccfe841fe4 100644 --- a/pkg/models/resources/v1alpha3/node/nodes.go +++ b/pkg/models/resources/v1alpha3/node/nodes.go @@ -31,14 +31,19 @@ import ( // Those annotations were added to node only for display purposes const ( - nodeCPURequests = "node.kubesphere.io/cpu-requests" - nodeMemoryRequests = "node.kubesphere.io/memory-requests" - nodeCPULimits = "node.kubesphere.io/cpu-limits" - nodeMemoryLimits = "node.kubesphere.io/memory-limits" - nodeCPURequestsFraction = "node.kubesphere.io/cpu-requests-fraction" - nodeCPULimitsFraction = "node.kubesphere.io/cpu-limits-fraction" - nodeMemoryRequestsFraction = "node.kubesphere.io/memory-requests-fraction" - nodeMemoryLimitsFraction = "node.kubesphere.io/memory-limits-fraction" + nodeCPURequests = "node.kubesphere.io/cpu-requests" + nodeMemoryRequests = "node.kubesphere.io/memory-requests" + nodeCPULimits = "node.kubesphere.io/cpu-limits" + nodeMemoryLimits = "node.kubesphere.io/memory-limits" + nodeCPURequestsFraction = "node.kubesphere.io/cpu-requests-fraction" + nodeCPULimitsFraction = "node.kubesphere.io/cpu-limits-fraction" + nodeMemoryRequestsFraction = "node.kubesphere.io/memory-requests-fraction" + nodeMemoryLimitsFraction = "node.kubesphere.io/memory-limits-fraction" + nodeConfigOK v1.NodeConditionType = "ConfigOK" + nodeKubeletReady v1.NodeConditionType = "KubeletReady" + statusRunning = "running" + statusWarning = "warning" + statusUnschedulable = "unschedulable" ) type nodesGetter struct { @@ -107,6 +112,10 @@ func (c nodesGetter) filter(object runtime.Object, filter query.Filter) bool { if !ok { return false } + switch filter.Field { + case query.FieldStatus: + return getNodeStatus(node) == string(filter.Value) + } return v1alpha3.DefaultObjectMetaFilter(node.ObjectMeta, filter) } @@ -177,3 +186,34 @@ func (c nodesGetter) getPodsTotalRequestAndLimits(pods []*v1.Pod) (reqs map[v1.R } return } + +func getNodeStatus(node *v1.Node) string { + if node.Spec.Unschedulable { + return statusUnschedulable + } + for _, condition := range node.Status.Conditions { + if isUnhealthyStatus(condition) { + return statusWarning + } + } + + return statusRunning +} + +var expectedConditions = map[v1.NodeConditionType]v1.ConditionStatus{ + v1.NodeMemoryPressure: v1.ConditionFalse, + v1.NodeDiskPressure: v1.ConditionFalse, + v1.NodePIDPressure: v1.ConditionFalse, + v1.NodeNetworkUnavailable: v1.ConditionFalse, + nodeConfigOK: v1.ConditionTrue, + nodeKubeletReady: v1.ConditionTrue, + v1.NodeReady: v1.ConditionTrue, +} + +func isUnhealthyStatus(condition v1.NodeCondition) bool { + expectedStatus := expectedConditions[condition.Type] + if expectedStatus != "" && condition.Status != expectedStatus { + return true + } + return false +} diff --git a/pkg/models/resources/v1alpha3/node/nodes_test.go b/pkg/models/resources/v1alpha3/node/nodes_test.go index 3576fb21fd5f6bd8fb2b66cec3ffbd6ef781c8f7..e85ee6c56d25f508a3e3237fdbb538832ffaf6c9 100644 --- a/pkg/models/resources/v1alpha3/node/nodes_test.go +++ b/pkg/models/resources/v1alpha3/node/nodes_test.go @@ -23,6 +23,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "strconv" "testing" ) @@ -144,3 +148,112 @@ func TestNodesGetterGet(t *testing.T) { } } + +func TestListNodes(t *testing.T) { + tests := []struct { + query *query.Query + expected *api.ListResult + }{ + { + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value(node2.Name)}, + }, + &api.ListResult{ + Items: []interface{}{ + node2, + }, + TotalItems: 1, + }, + }, + { + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldStatus: query.Value(statusUnschedulable)}, + }, + &api.ListResult{ + Items: []interface{}{ + node1, + }, + TotalItems: 1, + }, + }, + { + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldStatus: query.Value(statusRunning)}, + }, + &api.ListResult{ + Items: []interface{}{ + node2, + }, + TotalItems: 1, + }, + }, + } + + getter := prepare() + + for index, test := range tests { + t.Run(strconv.Itoa(index), func(t *testing.T) { + + got, err := getter.List("", test.query) + + if err != nil { + t.Error(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } + +} + +var ( + node1 = &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + Spec: corev1.NodeSpec{ + Unschedulable: true, + }, + } + node2 = &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node2", + }, + Spec: corev1.NodeSpec{ + Unschedulable: false, + }, + } + nodes = []*corev1.Node{node1, node2} +) + +func prepare() v1alpha3.Interface { + + fake := fake.NewSimpleClientset(node1, node2) + + informer := informers.NewSharedInformerFactory(fake, 0) + for _, node := range nodes { + informer.Core().V1().Nodes().Informer().GetIndexer().Add(node) + } + + return New(informer) +}