提交 b2ef2fbc 编写于 作者: H harisanker1780 提交者: Marcin Maciaszczyk

Show PersistentVolumeClaim List in Pod Details (#2515)

* Show PersistentVolumeClaim List in Pod Details

* Fixed build error

* Fixed build error

* Fixed file format issues
上级 11fc56c4
......@@ -253,6 +253,10 @@ func CreateHTTPAPIHandler(iManager integration.IntegrationManager, cManager clie
apiV1Ws.GET("/pod/{namespace}/{pod}/shell/{container}").
To(apiHandler.handleExecShell).
Writes(TerminalResponse{}))
apiV1Ws.Route(
apiV1Ws.GET("/pod/{namespace}/{pod}/persistentvolumeclaim").
To(apiHandler.handleGetPodPersistentVolumeClaims).
Writes(persistentvolumeclaim.PersistentVolumeClaimList{}))
apiV1Ws.Route(
apiV1Ws.GET("/deployment").
......@@ -2150,6 +2154,26 @@ func (apiHandler *APIHandler) handleGetStorageClassPersistentVolumes(request *re
response.WriteHeaderAndEntity(http.StatusOK, result)
}
func (apiHandler *APIHandler) handleGetPodPersistentVolumeClaims(request *restful.Request,
response *restful.Response) {
k8sClient, err := apiHandler.cManager.Client(request)
if err != nil {
handleInternalError(response, err)
return
}
name := request.PathParameter("pod")
namespace := request.PathParameter("namespace")
dataSelect := parseDataSelectPathParameter(request)
result, err := persistentvolumeclaim.GetPodPersistentVolumeClaims(k8sClient,
namespace, name, dataSelect)
if err != nil {
handleInternalError(response, err)
return
}
response.WriteHeaderAndEntity(http.StatusOK, result)
}
func (apiHandler *APIHandler) handleLogSource(request *restful.Request, response *restful.Response) {
k8sClient, err := apiHandler.cManager.Client(request)
if err != nil {
......
......@@ -15,14 +15,77 @@
package persistentvolumeclaim
import (
"log"
"strings"
"github.com/kubernetes/dashboard/src/app/backend/errors"
"github.com/kubernetes/dashboard/src/app/backend/resource/common"
"github.com/kubernetes/dashboard/src/app/backend/resource/dataselect"
api "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
client "k8s.io/client-go/kubernetes"
)
// The code below allows to perform complex data section on []api.PersistentVolumeClaim
type PersistentVolumeClaimCell api.PersistentVolumeClaim
// GetPodPersistentVolumeClaims gets persistentvolumeclaims that are associated with this pod.
func GetPodPersistentVolumeClaims(client client.Interface, namespace string, podName string,
dsQuery *dataselect.DataSelectQuery) (*PersistentVolumeClaimList, error) {
pod, err := client.CoreV1().Pods(namespace).Get(podName, metaV1.GetOptions{})
if err != nil {
return nil, err
}
claimNames := make([]string, 0)
if pod.Spec.Volumes != nil && len(pod.Spec.Volumes) > 0 {
for _, v := range pod.Spec.Volumes {
persistentVolumeClaim := v.PersistentVolumeClaim
if persistentVolumeClaim != nil {
claimNames = append(claimNames, persistentVolumeClaim.ClaimName)
}
}
}
if len(claimNames) > 0 {
channels := &common.ResourceChannels{
PersistentVolumeClaimList: common.GetPersistentVolumeClaimListChannel(
client, common.NewSameNamespaceQuery(namespace), 1),
}
persistentVolumeClaimList := <-channels.PersistentVolumeClaimList.List
err = <-channels.PersistentVolumeClaimList.Error
nonCriticalErrors, criticalError := errors.HandleError(err)
if criticalError != nil {
return nil, criticalError
}
podPersistentVolumeClaims := make([]api.PersistentVolumeClaim, 0)
for _, pvc := range persistentVolumeClaimList.Items {
for _, claimName := range claimNames {
if strings.Compare(claimName, pvc.Name) == 0 {
podPersistentVolumeClaims = append(podPersistentVolumeClaims, pvc)
break
}
}
}
log.Printf("Found %d persistentvolumeclaims related to %s pod",
len(podPersistentVolumeClaims), podName)
return toPersistentVolumeClaimList(podPersistentVolumeClaims,
nonCriticalErrors, dsQuery), nil
}
log.Printf("No persistentvolumeclaims found related to %s pod", podName)
// No ClaimNames found in Pod details, return empty response.
return &PersistentVolumeClaimList{}, nil
}
func (self PersistentVolumeClaimCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue {
switch name {
case dataselect.NameProperty:
......
// 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 persistentvolumeclaim
import (
"reflect"
"testing"
"github.com/kubernetes/dashboard/src/app/backend/api"
"github.com/kubernetes/dashboard/src/app/backend/resource/dataselect"
v1 "k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
)
func TestGetPodPersistentVolumeClaims(t *testing.T) {
cases := []struct {
pod *v1.Pod
name string
namespace string
persistentVolumeClaimList *v1.PersistentVolumeClaimList
expected *PersistentVolumeClaimList
}{
{
pod: &v1.Pod{
ObjectMeta: metaV1.ObjectMeta{
Name: "test-pod", Namespace: "test-namespace", Labels: map[string]string{"app": "test"},
},
Spec: v1.PodSpec{
Volumes: []v1.Volume{{
Name: "vol-1",
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: "pvc-1",
},
},
}},
},
},
name: "test-pod",
namespace: "test-namespace",
persistentVolumeClaimList: &v1.PersistentVolumeClaimList{Items: []v1.PersistentVolumeClaim{
{
ObjectMeta: metaV1.ObjectMeta{
Name: "pvc-1", Namespace: "test-namespace", Labels: map[string]string{"app": "test"},
},
},
}},
expected: &PersistentVolumeClaimList{
ListMeta: api.ListMeta{TotalItems: 1},
Items: []PersistentVolumeClaim{{
TypeMeta: api.TypeMeta{Kind: api.ResourceKindPersistentVolumeClaim},
ObjectMeta: api.ObjectMeta{Name: "pvc-1", Namespace: "test-namespace",
Labels: map[string]string{"app": "test"}},
}},
Errors: []error{},
},
},
}
for _, c := range cases {
fakeClient := fake.NewSimpleClientset(c.persistentVolumeClaimList, c.pod)
actual, _ := GetPodPersistentVolumeClaims(fakeClient, c.namespace, c.name, dataselect.NoDataSelect)
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("GetPodPersistentVolumeClaims(client, %#v, %#v) == \ngot: %#v, \nexpected %#v",
c.name, c.namespace, actual, c.expected)
}
}
}
......@@ -27,6 +27,7 @@ import (
"github.com/kubernetes/dashboard/src/app/backend/resource/common"
"github.com/kubernetes/dashboard/src/app/backend/resource/controller"
"github.com/kubernetes/dashboard/src/app/backend/resource/dataselect"
"github.com/kubernetes/dashboard/src/app/backend/resource/persistentvolumeclaim"
"k8s.io/api/core/v1"
res "k8s.io/apimachinery/pkg/api/resource"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
......@@ -36,19 +37,20 @@ import (
// PodDetail is a presentation layer view of Kubernetes Pod resource.
type PodDetail struct {
ObjectMeta api.ObjectMeta `json:"objectMeta"`
TypeMeta api.TypeMeta `json:"typeMeta"`
PodPhase v1.PodPhase `json:"podPhase"`
PodIP string `json:"podIP"`
NodeName string `json:"nodeName"`
RestartCount int32 `json:"restartCount"`
QOSClass string `json:"qosClass"`
Controller controller.ResourceOwner `json:"controller"`
Containers []Container `json:"containers"`
InitContainers []Container `json:"initContainers"`
Metrics []metricapi.Metric `json:"metrics"`
Conditions []common.Condition `json:"conditions"`
EventList common.EventList `json:"eventList"`
ObjectMeta api.ObjectMeta `json:"objectMeta"`
TypeMeta api.TypeMeta `json:"typeMeta"`
PodPhase v1.PodPhase `json:"podPhase"`
PodIP string `json:"podIP"`
NodeName string `json:"nodeName"`
RestartCount int32 `json:"restartCount"`
QOSClass string `json:"qosClass"`
Controller controller.ResourceOwner `json:"controller"`
Containers []Container `json:"containers"`
InitContainers []Container `json:"initContainers"`
Metrics []metricapi.Metric `json:"metrics"`
Conditions []common.Condition `json:"conditions"`
EventList common.EventList `json:"eventList"`
PersistentvolumeclaimList persistentvolumeclaim.PersistentVolumeClaimList `json:"persistentVolumeClaimList"`
// List of non-critical errors, that occurred during resource retrieval.
Errors []error `json:"errors"`
......@@ -131,7 +133,15 @@ func GetPodDetail(client kubernetes.Interface, metricClient metricapi.MetricClie
return nil, criticalError
}
podDetail := toPodDetail(pod, metrics, configMapList, secretList, controller, eventList, nonCriticalErrors)
persistentVolumeClaimList, err := persistentvolumeclaim.GetPodPersistentVolumeClaims(client,
namespace, name, dataselect.DefaultDataSelect)
nonCriticalErrors, criticalError = errorHandler.AppendError(err, nonCriticalErrors)
if criticalError != nil {
return nil, criticalError
}
podDetail := toPodDetail(pod, metrics, configMapList, secretList, controller,
eventList, persistentVolumeClaimList, nonCriticalErrors)
return &podDetail, nil
}
......@@ -196,22 +206,24 @@ func extractContainerInfo(containerList []v1.Container, pod *v1.Pod, configMaps
}
func toPodDetail(pod *v1.Pod, metrics []metricapi.Metric, configMaps *v1.ConfigMapList, secrets *v1.SecretList,
controller controller.ResourceOwner, events *common.EventList, nonCriticalErrors []error) PodDetail {
controller controller.ResourceOwner, events *common.EventList,
persistentVolumeClaimList *persistentvolumeclaim.PersistentVolumeClaimList, nonCriticalErrors []error) PodDetail {
return PodDetail{
ObjectMeta: api.NewObjectMeta(pod.ObjectMeta),
TypeMeta: api.NewTypeMeta(api.ResourceKindPod),
PodPhase: pod.Status.Phase,
PodIP: pod.Status.PodIP,
RestartCount: getRestartCount(*pod),
QOSClass: string(pod.Status.QOSClass),
NodeName: pod.Spec.NodeName,
Controller: controller,
Containers: extractContainerInfo(pod.Spec.Containers, pod, configMaps, secrets),
InitContainers: extractContainerInfo(pod.Spec.InitContainers, pod, configMaps, secrets),
Metrics: metrics,
Conditions: getPodConditions(*pod),
EventList: *events,
Errors: nonCriticalErrors,
ObjectMeta: api.NewObjectMeta(pod.ObjectMeta),
TypeMeta: api.NewTypeMeta(api.ResourceKindPod),
PodPhase: pod.Status.Phase,
PodIP: pod.Status.PodIP,
RestartCount: getRestartCount(*pod),
QOSClass: string(pod.Status.QOSClass),
NodeName: pod.Spec.NodeName,
Controller: controller,
Containers: extractContainerInfo(pod.Spec.Containers, pod, configMaps, secrets),
InitContainers: extractContainerInfo(pod.Spec.InitContainers, pod, configMaps, secrets),
Metrics: metrics,
Conditions: getPodConditions(*pod),
EventList: *events,
PersistentvolumeclaimList: *persistentVolumeClaimList,
Errors: nonCriticalErrors,
}
}
......
......@@ -24,6 +24,7 @@ import (
"github.com/kubernetes/dashboard/src/app/backend/resource/common"
"github.com/kubernetes/dashboard/src/app/backend/resource/controller"
"github.com/kubernetes/dashboard/src/app/backend/resource/dataselect"
"github.com/kubernetes/dashboard/src/app/backend/resource/persistentvolumeclaim"
"k8s.io/api/core/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
......@@ -47,12 +48,13 @@ func TestGetPodDetail(t *testing.T) {
Namespace: "test-namespace",
Labels: map[string]string{"app": "test"},
},
Controller: controller.ResourceOwner{},
Containers: []Container{},
InitContainers: []Container{},
EventList: common.EventList{Events: []common.Event{}},
Metrics: []metricapi.Metric{},
Errors: []error{},
Controller: controller.ResourceOwner{},
Containers: []Container{},
InitContainers: []Container{},
EventList: common.EventList{Events: []common.Event{}},
Metrics: []metricapi.Metric{},
PersistentvolumeclaimList: persistentvolumeclaim.PersistentVolumeClaimList{},
Errors: []error{},
},
},
}
......
......@@ -19,13 +19,17 @@ export class PodDetailController {
/**
* @param {!backendApi.PodDetail} podDetail
* @param {!angular.Resource} kdPodEventsResource
* @param {!angular.Resource} kdPodPersistentVolumeClaimsResource
* @ngInject
*/
constructor(podDetail, kdPodEventsResource) {
constructor(podDetail, kdPodEventsResource, kdPodPersistentVolumeClaimsResource) {
/** @export {!backendApi.PodDetail} */
this.podDetail = podDetail;
/** @export {!angular.Resource} */
this.eventListResource = kdPodEventsResource;
/**@export {!angular.Resource} */
this.persistentVolumeClaimsResource = kdPodPersistentVolumeClaimsResource;
}
}
......@@ -51,3 +51,11 @@ limitations under the License.
<kd-event-card-list event-list="::ctrl.podDetail.eventList"
event-list-resource="::ctrl.eventListResource"></kd-event-card-list>
<kd-content-card>
<kd-content>
<kd-persistent-volume-claim-card-list persistent-volume-claim-list="::ctrl.podDetail.persistentVolumeClaimList"
persistent-volume-claim-list-resource="::ctrl.persistentVolumeClaimsResource">
</kd-persistent-volume-claim-card-list>
</kd-content>
</kd-content-card>
......@@ -80,3 +80,12 @@ export function getPodDetailResource($resource, $stateParams) {
export function getPodDetail(podDetailResource) {
return podDetailResource.get().$promise;
}
/**
* @param {!angular.$resource} $resource
* @return {!angular.Resource}
* @ngInject
*/
export function podPersistentVolumeClaimsResource($resource) {
return $resource(`api/v1/pod/:namespace/:name/persistentvolumeclaim`);
}
......@@ -18,11 +18,13 @@ import filtersModule from '../common/filters/module';
import namespaceModule from '../common/namespace/module';
import configMapModule from '../configmap/module';
import eventsModule from '../events/module';
import persistentvolumeclaimModule from '../persistentvolumeclaim/module';
import {containerInfoComponent} from './detail/containerinfo_component';
import {creatorInfoComponent} from './detail/creatorinfo_component';
import {podInfoComponent} from './detail/info_component';
import {podEventsResource} from './detail/stateconfig';
import {podPersistentVolumeClaimsResource} from './detail/stateconfig';
import {podCardComponent} from './list/card_component';
import {podCardListComponent} from './list/cardlist_component';
import {podListResource} from './list/stateconfig';
......@@ -44,6 +46,7 @@ export default angular
eventsModule.name,
filtersModule.name,
namespaceModule.name,
persistentvolumeclaimModule.name,
])
.config(stateConfig)
.component('kdContainerInfo', containerInfoComponent)
......@@ -52,4 +55,5 @@ export default angular
.component('kdPodCardList', podCardListComponent)
.component('kdPodInfo', podInfoComponent)
.factory('kdPodEventsResource', podEventsResource)
.factory('kdPodListResource', podListResource);
.factory('kdPodListResource', podListResource)
.factory('kdPodPersistentVolumeClaimsResource', podPersistentVolumeClaimsResource);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册