Skip to content

Commit

Permalink
fix(api): role/clusterole/podlist endpoints (#8881)
Browse files Browse the repository at this point in the history
* fix: role/clusterrole listing, improve logging when metrics are disabled and add container statuses to pod list

* fix unit tests and license check

* update cd-helm workflow

* remove chart lint from master workflow

* remove debug log line

* regenerate schema
  • Loading branch information
floreks committed Apr 5, 2024
1 parent d72a202 commit cb65bda
Show file tree
Hide file tree
Showing 14 changed files with 352 additions and 87 deletions.
1 change: 0 additions & 1 deletion .github/workflows/cd-helm.yml
Expand Up @@ -34,7 +34,6 @@ jobs:
with:
version: v3.12.1
- uses: helm/chart-testing-action@v2.6.1
- run: ct lint --config=.ct.yml --target-branch ${{ github.event.repository.default_branch }}
- uses: helm/kind-action@v1.9.0
with:
node_image: kindest/node:v1.29.0
Expand Down
3 changes: 2 additions & 1 deletion modules/api/Makefile
Expand Up @@ -61,7 +61,8 @@ fix-go: $(PRE)
.PHONY: run
run: build
@$(API_DIST_BINARY) \
--kubeconfig=$(KUBECONFIG)
--kubeconfig=$(KUBECONFIG) \
--metrics-provider=none

.PHONY: schema
schema: --ensure-kind-cluster build --swagger
Expand Down
12 changes: 12 additions & 0 deletions modules/api/pkg/handler/apihandler.go
Expand Up @@ -994,6 +994,12 @@ func CreateHTTPAPIHandler(iManager integration.Manager) (*restful.Container, err
Returns(http.StatusOK, "OK", clusterrolebinding.ClusterRoleBindingDetail{}))

// Role
apiV1Ws.Route(
apiV1Ws.GET("/role").To(apiHandler.handleGetRoleList).
// docs
Doc("returns a list of Roles from all namespace").
Writes(role.RoleList{}).
Returns(http.StatusOK, "OK", role.RoleList{}))
apiV1Ws.Route(
apiV1Ws.GET("/role/{namespace}").To(apiHandler.handleGetRoleList).
// docs
Expand All @@ -1011,6 +1017,12 @@ func CreateHTTPAPIHandler(iManager integration.Manager) (*restful.Container, err
Returns(http.StatusOK, "OK", role.RoleDetail{}))

// RoleBinding
apiV1Ws.Route(
apiV1Ws.GET("/rolebinding").To(apiHandler.handleGetRoleBindingList).
// docs
Doc("returns a list of RoleBindings from all namespace").
Writes(rolebinding.RoleBindingList{}).
Returns(http.StatusOK, "OK", rolebinding.RoleBindingList{}))
apiV1Ws.Route(
apiV1Ws.GET("/rolebinding/{namespace}").To(apiHandler.handleGetRoleBindingList).
// docs
Expand Down
21 changes: 15 additions & 6 deletions modules/api/pkg/resource/dataselect/dataselect.go
Expand Up @@ -15,9 +15,10 @@
package dataselect

import (
"log"
"sort"

"k8s.io/klog/v2"

metricapi "k8s.io/dashboard/api/pkg/integration/metric/api"
"k8s.io/dashboard/errors"
)
Expand Down Expand Up @@ -129,7 +130,7 @@ func (self *DataSelector) getMetrics(metricClient metricapi.MetricClient) (
metricPromises := make([]metricapi.MetricPromises, 0)

if metricClient == nil {
return metricPromises, errors.NewInternal("No metric client provided. Skipping metrics.")
return metricPromises, nil
}

metricNames := self.DataSelectQuery.MetricQuery.MetricNames
Expand All @@ -142,7 +143,7 @@ func (self *DataSelector) getMetrics(metricClient metricapi.MetricClient) (
// make sure data cells support metrics
metricDataCell, ok := dataCell.(MetricDataCell)
if !ok {
log.Printf("Data cell does not implement MetricDataCell. Skipping. %v", dataCell)
klog.V(0).InfoS("Data cell does not implement MetricDataCell. Skipping.", "dataCell", dataCell)
continue
}

Expand All @@ -162,7 +163,11 @@ func (self *DataSelector) getMetrics(metricClient metricapi.MetricClient) (
func (self *DataSelector) GetMetrics(metricClient metricapi.MetricClient) *DataSelector {
metricPromisesList, err := self.getMetrics(metricClient)
if err != nil {
log.Print(err)
klog.ErrorS(err, "error during getting metrics")
return self
}

if len(metricPromisesList) == 0 {
return self
}

Expand All @@ -180,13 +185,17 @@ func (self *DataSelector) GetMetrics(metricClient metricapi.MetricClient) *DataS
func (self *DataSelector) GetCumulativeMetrics(metricClient metricapi.MetricClient) *DataSelector {
metricPromisesList, err := self.getMetrics(metricClient)
if err != nil {
log.Print(err)
klog.ErrorS(err, "error during getting metrics")
return self
}

if len(metricPromisesList) == 0 {
return self
}

metricNames := self.DataSelectQuery.MetricQuery.MetricNames
if metricNames == nil {
log.Print("No metrics specified. Skipping metrics.")
klog.V(1).Info("Metrics names not provided. Skipping.")
return self
}

Expand Down
3 changes: 3 additions & 0 deletions modules/api/pkg/resource/node/detail.go
Expand Up @@ -84,6 +84,7 @@ type NodeDetail struct {
Node `json:",inline"`

// NodePhase is the current lifecycle phase of the node.
// Deprecated
Phase v1.NodePhase `json:"phase"`

// PodCIDR represents the pod IP range assigned to the node.
Expand Down Expand Up @@ -339,8 +340,10 @@ func toNodeDetail(node v1.Node, pods *pod.PodList, eventList *common.EventList,
Node: Node{
ObjectMeta: types.NewObjectMeta(node.ObjectMeta),
TypeMeta: types.NewTypeMeta(types.ResourceKindNode),
Ready: getNodeConditionStatus(node, v1.NodeReady),
AllocatedResources: allocatedResources,
},
// TODO: Remove deprecated field
Phase: node.Status.Phase,
ProviderID: node.Spec.ProviderID,
PodCIDR: node.Spec.PodCIDR,
Expand Down
1 change: 1 addition & 0 deletions modules/api/pkg/resource/node/detail_test.go
Expand Up @@ -64,6 +64,7 @@ func TestGetNodeDetail(t *testing.T) {
PodCapacity: 0,
PodFraction: 0,
},
Ready: v1.ConditionUnknown,
},
PodCIDR: "127.0.0.1",
ProviderID: "ID-1",
Expand Down
44 changes: 29 additions & 15 deletions modules/api/pkg/resource/pod/common_test.go
Expand Up @@ -40,9 +40,10 @@ func TestToPodPodStatusFailed(t *testing.T) {
}

expected := Pod{
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Status: string(v1.PodFailed),
Warnings: []common.Event{},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Status: string(v1.PodFailed),
Warnings: []common.Event{},
ContainerStatuses: make([]ContainerStatus, 0),
}

actual := toPod(pod, &MetricsByPod{}, []common.Event{})
Expand All @@ -67,9 +68,10 @@ func TestToPodPodStatusSucceeded(t *testing.T) {
}

expected := Pod{
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Status: string(v1.PodSucceeded),
Warnings: []common.Event{},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Status: string(v1.PodSucceeded),
Warnings: []common.Event{},
ContainerStatuses: make([]ContainerStatus, 0),
}

actual := toPod(pod, &MetricsByPod{}, []common.Event{})
Expand Down Expand Up @@ -98,9 +100,10 @@ func TestToPodPodStatusRunning(t *testing.T) {
}

expected := Pod{
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Status: string(v1.PodRunning),
Warnings: []common.Event{},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Status: string(v1.PodRunning),
Warnings: []common.Event{},
ContainerStatuses: make([]ContainerStatus, 0),
}

actual := toPod(pod, &MetricsByPod{}, []common.Event{})
Expand All @@ -125,9 +128,10 @@ func TestToPodPodStatusPending(t *testing.T) {
}

expected := Pod{
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Status: string(v1.PodPending),
Warnings: []common.Event{},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Status: string(v1.PodPending),
Warnings: []common.Event{},
ContainerStatuses: make([]ContainerStatus, 0),
}

actual := toPod(pod, &MetricsByPod{}, []common.Event{})
Expand Down Expand Up @@ -165,6 +169,14 @@ func TestToPodContainerStates(t *testing.T) {
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Status: "Terminated",
Warnings: []common.Event{},
ContainerStatuses: []ContainerStatus{
{
State: Terminated,
},
{
State: Waiting,
},
},
}

actual := toPod(pod, &MetricsByPod{}, []common.Event{})
Expand All @@ -184,8 +196,9 @@ func TestToPod(t *testing.T) {
{
pod: &v1.Pod{}, metrics: &MetricsByPod{},
expected: Pod{
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Warnings: []common.Event{},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Warnings: []common.Event{},
ContainerStatuses: make([]ContainerStatus, 0),
},
}, {
pod: &v1.Pod{
Expand All @@ -199,7 +212,8 @@ func TestToPod(t *testing.T) {
Name: "test-pod",
Namespace: "test-namespace",
},
Warnings: []common.Event{},
Warnings: []common.Event{},
ContainerStatuses: make([]ContainerStatus, 0),
},
},
}
Expand Down
63 changes: 63 additions & 0 deletions modules/api/pkg/resource/pod/container.go
@@ -0,0 +1,63 @@
// Copyright 2017 The Kubernetes Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pod

import (
v1 "k8s.io/api/core/v1"
)

type ContainerStatus struct {
Name string `json:"name"`
State ContainerState `json:"state"`
Ready bool `json:"ready"`
}

type ContainerState string

const (
Waiting ContainerState = "Waiting"
Running ContainerState = "Running"
Terminated ContainerState = "Terminated"
Unknown ContainerState = "Unknown"
)

func ToContainerStatuses(containerStatuses []v1.ContainerStatus) []ContainerStatus {
result := make([]ContainerStatus, len(containerStatuses))
for i, c := range containerStatuses {
result[i] = ContainerStatus{
Name: c.Name,
State: toContainerState(c.State),
Ready: c.Ready,
}
}

return result
}

func toContainerState(state v1.ContainerState) ContainerState {
if state.Waiting != nil {
return Waiting
}

if state.Terminated != nil {
return Terminated
}

if state.Running != nil {
return Running
}

return Unknown
}
17 changes: 10 additions & 7 deletions modules/api/pkg/resource/pod/list.go
Expand Up @@ -73,6 +73,8 @@ type Pod struct {

// ContainerImages holds a list of the Pod images.
ContainerImages []string `json:"containerImages"`

ContainerStatuses []ContainerStatus `json:"containerStatuses"`
}

var EmptyPodList = &PodList{
Expand Down Expand Up @@ -155,13 +157,14 @@ func ToPodList(pods []v1.Pod, events []v1.Event, nonCriticalErrors []error, dsQu

func toPod(pod *v1.Pod, metrics *MetricsByPod, warnings []common.Event) Pod {
podDetail := Pod{
ObjectMeta: types.NewObjectMeta(pod.ObjectMeta),
TypeMeta: types.NewTypeMeta(types.ResourceKindPod),
Warnings: warnings,
Status: getPodStatus(*pod),
RestartCount: getRestartCount(*pod),
NodeName: pod.Spec.NodeName,
ContainerImages: common.GetContainerImages(&pod.Spec),
ObjectMeta: types.NewObjectMeta(pod.ObjectMeta),
TypeMeta: types.NewTypeMeta(types.ResourceKindPod),
Warnings: warnings,
Status: getPodStatus(*pod),
RestartCount: getRestartCount(*pod),
NodeName: pod.Spec.NodeName,
ContainerImages: common.GetContainerImages(&pod.Spec),
ContainerStatuses: ToContainerStatuses(append(pod.Status.InitContainerStatuses, pod.Status.ContainerStatuses...)),
}

if m, exists := metrics.MetricsMap[pod.UID]; exists {
Expand Down
5 changes: 3 additions & 2 deletions modules/api/pkg/resource/pod/list_test.go
Expand Up @@ -102,8 +102,9 @@ func TestGetPodListFromChannels(t *testing.T) {
Labels: map[string]string{"key": "value"},
CreationTimestamp: metav1.Unix(111, 222),
},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Warnings: []common.Event{},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Warnings: []common.Event{},
ContainerStatuses: make([]pod.ContainerStatus, 0),
}},
Errors: []error{},
},
Expand Down
5 changes: 3 additions & 2 deletions modules/api/pkg/resource/service/pods_test.go
Expand Up @@ -58,8 +58,9 @@ func TestGetServicePods(t *testing.T) {
Name: "pod-1",
UID: "test-uid",
Namespace: "ns-1"},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Warnings: []common.Event{},
TypeMeta: types.TypeMeta{Kind: types.ResourceKindPod},
Warnings: []common.Event{},
ContainerStatuses: make([]pod.ContainerStatus, 0),
},
},
Errors: []error{},
Expand Down

0 comments on commit cb65bda

Please sign in to comment.