mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-18 03:56:36 +00:00
Feat: add a new resource level view: pod view (#4661)
Pod view is the next level view of managed resource view Signed-off-by: HanMengnan <1448189829@qq.com> Signed-off-by: HanMengnan <1448189829@qq.com>
This commit is contained in:
@@ -51,4 +51,12 @@ const (
|
||||
ObjectProgressingStatusColor = "[blue::]"
|
||||
// ObjectUnKnownStatusColor is object UnKnown status text color
|
||||
ObjectUnKnownStatusColor = "[gray::]"
|
||||
// PodPendingPhaseColor is pod pending phase text color
|
||||
PodPendingPhaseColor = "[yellow::]"
|
||||
// PodRunningPhaseColor is pod running phase text color
|
||||
PodRunningPhaseColor = "[green::]"
|
||||
// PodSucceededPhase is pod succeeded phase text color
|
||||
PodSucceededPhase = "[purple::]"
|
||||
// PodFailedPhase is pod failed phase text color
|
||||
PodFailedPhase = "[red::]"
|
||||
)
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/references/cli/top/utils"
|
||||
)
|
||||
|
||||
// NamespaceList is namespace list
|
||||
@@ -58,7 +60,7 @@ func ListClusterNamespaces(ctx context.Context, c client.Client) (*NamespaceList
|
||||
list.data = append(list.data, Namespace{
|
||||
Name: namespaceInfo.Name,
|
||||
Status: string(namespaceInfo.Status.Phase),
|
||||
Age: timeFormat(time.Since(namespaceInfo.CreationTimestamp.Time)),
|
||||
Age: utils.TimeFormat(time.Since(namespaceInfo.CreationTimestamp.Time)),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -25,28 +25,28 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestK8SObjectList_Header(t *testing.T) {
|
||||
func TestManagedResource_Header(t *testing.T) {
|
||||
list := ManagedResourceList{title: []string{"name", "namespace", "kind", "APIVersion", "cluster", "status"}}
|
||||
assert.Equal(t, len(list.Header()), 6)
|
||||
assert.Equal(t, list.Header(), []string{"name", "namespace", "kind", "APIVersion", "cluster", "status"})
|
||||
}
|
||||
|
||||
func TestK8SObjectList_Body(t *testing.T) {
|
||||
func TestManagedResource_Body(t *testing.T) {
|
||||
list := ManagedResourceList{data: []ManagedResource{{"", "", "", "", "", ""}}}
|
||||
assert.Equal(t, len(list.Body()), 1)
|
||||
assert.Equal(t, list.Body(), [][]string{{"", "", "", "", "", ""}})
|
||||
}
|
||||
|
||||
var _ = Describe("test k8s object", func() {
|
||||
var _ = Describe("test managed resource", func() {
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, &CtxKeyAppName, "first-vela-app")
|
||||
ctx = context.WithValue(ctx, &CtxKeyNamespace, "default")
|
||||
ctx = context.WithValue(ctx, &CtxKeyCluster, "local")
|
||||
|
||||
It("list k8s object", func() {
|
||||
It("list managed resource", func() {
|
||||
list, err := ListManagedResource(ctx, k8sClient)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(len(list.Header())).To(Equal(6))
|
||||
Expect(len(list.Body())).To(Equal(2))
|
||||
Expect(len(list.Body())).To(Equal(4))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -18,13 +18,12 @@ package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/references/cli/top/utils"
|
||||
)
|
||||
|
||||
// Namespace is namespace struct
|
||||
@@ -48,7 +47,7 @@ func ListNamespaces(ctx context.Context, c client.Reader) *NamespaceList {
|
||||
list.data = append(list.data, Namespace{
|
||||
Name: ns.Name,
|
||||
Status: string(ns.Status.Phase),
|
||||
Age: timeFormat(time.Since(ns.CreationTimestamp.Time)),
|
||||
Age: utils.TimeFormat(time.Since(ns.CreationTimestamp.Time)),
|
||||
})
|
||||
}
|
||||
return list
|
||||
@@ -67,20 +66,3 @@ func (l *NamespaceList) Body() [][]string {
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// timeFormat format time data of `time.Duration` type to string type
|
||||
func timeFormat(t time.Duration) string {
|
||||
str := t.String()
|
||||
// remove "."
|
||||
tmp := strings.Split(str, ".")
|
||||
tmp[0] += "s"
|
||||
|
||||
tmp = strings.Split(tmp[0], "h")
|
||||
// hour num
|
||||
hour, err := strconv.Atoi(tmp[0])
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%dd%dh%2s", hour/24, hour%24, tmp[1])
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package model
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -43,15 +42,6 @@ func TestNamespaceList_Body(t *testing.T) {
|
||||
assert.Equal(t, nsList.Body()[0], []string{AllNamespace, "*", "*"})
|
||||
}
|
||||
|
||||
func TestTimeFormat(t *testing.T) {
|
||||
t1, err1 := time.ParseDuration("1.5h")
|
||||
assert.NoError(t, err1)
|
||||
assert.Equal(t, timeFormat(t1), "0d1h30m0ss")
|
||||
t2, err2 := time.ParseDuration("25h")
|
||||
assert.NoError(t, err2)
|
||||
assert.Equal(t, timeFormat(t2), "1d1h0m0ss")
|
||||
}
|
||||
|
||||
var _ = Describe("test namespace", func() {
|
||||
ctx := context.Background()
|
||||
It("list namespace", func() {
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela 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 model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/velaql/providers/query"
|
||||
"github.com/oam-dev/kubevela/references/cli/top/utils"
|
||||
)
|
||||
|
||||
// Pod represent the k8s pod resource instance
|
||||
type Pod struct {
|
||||
Name string
|
||||
Namespace string
|
||||
Ready string
|
||||
Status string
|
||||
CPU string
|
||||
Mem string
|
||||
CPUR string
|
||||
CPUL string
|
||||
MemR string
|
||||
MemL string
|
||||
IP string
|
||||
NodeName string
|
||||
Age string
|
||||
}
|
||||
|
||||
// PodList is pod list
|
||||
type PodList struct {
|
||||
title []string
|
||||
data []Pod
|
||||
}
|
||||
|
||||
// ListPods return pod list of component
|
||||
func ListPods(ctx context.Context, cfg *rest.Config, c client.Client) (*PodList, error) {
|
||||
list := &PodList{title: []string{"Name", "Namespace", "Ready", "Status", "CPU", "MEM", "%CPU/R", "%CPU/L", "%MEM/R", "%MEM/L", "IP", "Node", "Age"}, data: []Pod{}}
|
||||
appName := ctx.Value(&CtxKeyAppName).(string)
|
||||
appNamespace := ctx.Value(&CtxKeyNamespace).(string)
|
||||
compCluster := ctx.Value(&CtxKeyCluster).(string)
|
||||
compNamespace := ctx.Value(&CtxKeyClusterNamespace).(string)
|
||||
compName := ctx.Value(&CtxKeyComponentName).(string)
|
||||
|
||||
opt := query.Option{
|
||||
Name: appName,
|
||||
Namespace: appNamespace,
|
||||
Filter: query.FilterOption{
|
||||
Cluster: compCluster,
|
||||
ClusterNamespace: compNamespace,
|
||||
Components: []string{compName},
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
WithTree: true,
|
||||
}
|
||||
resource, err := collectResource(ctx, c, opt)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
for _, object := range resource {
|
||||
pod := &v1.Pod{}
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(object.UnstructuredContent(), pod)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
list.data = append(list.data, LoadPodDetail(cfg, pod))
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// LoadPodDetail gather the pod detail info
|
||||
func LoadPodDetail(cfg *rest.Config, pod *v1.Pod) Pod {
|
||||
podInfo := Pod{
|
||||
Name: pod.Name,
|
||||
Namespace: pod.Namespace,
|
||||
Ready: readyContainerNum(pod),
|
||||
Status: string(pod.Status.Phase),
|
||||
Age: utils.TimeFormat(time.Since(pod.CreationTimestamp.Time)),
|
||||
IP: pod.Status.PodIP,
|
||||
NodeName: pod.Spec.NodeName,
|
||||
}
|
||||
metric, err := utils.PodMetric(cfg, pod.Name, pod.Namespace)
|
||||
if err != nil {
|
||||
podInfo.CPU, podInfo.Mem, podInfo.CPUL, podInfo.MemL, podInfo.CPUR, podInfo.MemR = utils.NA, utils.NA, utils.NA, utils.NA, utils.NA, utils.NA
|
||||
} else {
|
||||
c, r := utils.GatherPodMX(pod, metric)
|
||||
podInfo.CPU, podInfo.Mem = strconv.FormatInt(c.CPU, 10), strconv.FormatInt(c.Mem/1000000, 10)
|
||||
podInfo.CPUR = utils.ToPercentageStr(c.CPU, r.CPU)
|
||||
podInfo.MemR = utils.ToPercentageStr(c.Mem, r.Mem)
|
||||
podInfo.CPUL = utils.ToPercentageStr(c.CPU, r.Lcpu)
|
||||
podInfo.MemL = utils.ToPercentageStr(c.CPU, r.Lmem)
|
||||
}
|
||||
|
||||
return podInfo
|
||||
}
|
||||
|
||||
func readyContainerNum(pod *v1.Pod) string {
|
||||
total := len(pod.Status.ContainerStatuses)
|
||||
ready := 0
|
||||
for _, c := range pod.Status.ContainerStatuses {
|
||||
if c.Ready {
|
||||
ready++
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%d/%d", ready, total)
|
||||
}
|
||||
|
||||
// Header generate header of table in pod view
|
||||
func (l *PodList) Header() []string {
|
||||
return l.title
|
||||
}
|
||||
|
||||
// Body generate body of table in pod view
|
||||
func (l *PodList) Body() [][]string {
|
||||
data := make([][]string, 0)
|
||||
for _, pod := range l.data {
|
||||
data = append(data, []string{pod.Name, pod.Namespace, pod.Ready, pod.Status, pod.CPU, pod.Mem, pod.CPUR, pod.MemR, pod.CPUL, pod.MemL, pod.IP, pod.NodeName, pod.Age})
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela 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 model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestPod(t *testing.T) {
|
||||
pod := Pod{
|
||||
Name: "",
|
||||
Namespace: "",
|
||||
Ready: "",
|
||||
Status: "",
|
||||
CPU: "",
|
||||
Mem: "",
|
||||
CPUR: "",
|
||||
CPUL: "",
|
||||
MemR: "",
|
||||
MemL: "",
|
||||
IP: "",
|
||||
NodeName: "",
|
||||
Age: "",
|
||||
}
|
||||
podList := &PodList{title: []string{"Name", "Namespace", "Ready", "Status", "CPU", "MEM", "%CPU/R", "%CPU/L", "%MEM/R", "%MEM/L", "IP", "Node", "Age"}, data: []Pod{pod}}
|
||||
assert.Equal(t, len(podList.Header()), 13)
|
||||
assert.Equal(t, podList.Header()[0], "Name")
|
||||
assert.Equal(t, len(podList.Body()), 1)
|
||||
}
|
||||
|
||||
var _ = Describe("test pod", func() {
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, &CtxKeyAppName, "first-vela-app")
|
||||
ctx = context.WithValue(ctx, &CtxKeyNamespace, "default")
|
||||
ctx = context.WithValue(ctx, &CtxKeyCluster, "")
|
||||
ctx = context.WithValue(ctx, &CtxKeyClusterNamespace, "")
|
||||
ctx = context.WithValue(ctx, &CtxKeyComponentName, "deploy1")
|
||||
|
||||
It("list pods", func() {
|
||||
podList, err := ListPods(ctx, cfg, k8sClient)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(len(podList.Body())).To(Equal(1))
|
||||
})
|
||||
|
||||
It("load pod detail", func() {
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod",
|
||||
Namespace: "ns",
|
||||
CreationTimestamp: metav1.Time{Time: time.Now()},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: "node-1",
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: "running",
|
||||
PodIP: "10.1.1.1",
|
||||
ContainerStatuses: []v1.ContainerStatus{{Ready: true}},
|
||||
},
|
||||
}
|
||||
podInfo := LoadPodDetail(cfg, pod)
|
||||
Expect(podInfo.Ready).To(Equal("1/1"))
|
||||
Expect(podInfo.IP).To(Equal("10.1.1.1"))
|
||||
})
|
||||
})
|
||||
@@ -16,6 +16,18 @@ limitations under the License.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
apimachinerytypes "k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/velaql/providers/query"
|
||||
querytypes "github.com/oam-dev/kubevela/pkg/velaql/providers/query/types"
|
||||
)
|
||||
|
||||
// ResourceList an abstract kinds of resource list which can convert it to data of view in the form of table
|
||||
type ResourceList interface {
|
||||
// Header generate header of table in resource view
|
||||
@@ -23,3 +35,44 @@ type ResourceList interface {
|
||||
// Body generate body of table in resource view
|
||||
Body() [][]string
|
||||
}
|
||||
|
||||
func collectResource(ctx context.Context, c client.Client, opt query.Option) ([]unstructured.Unstructured, error) {
|
||||
app := new(v1beta1.Application)
|
||||
appKey := client.ObjectKey{Name: opt.Name, Namespace: opt.Namespace}
|
||||
if err := c.Get(context.Background(), appKey, app); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
collector := query.NewAppCollector(c, opt)
|
||||
appResList, err := collector.ListApplicationResources(context.Background(), app, opt.WithTree)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resources = make([]unstructured.Unstructured, 0)
|
||||
for _, res := range appResList {
|
||||
if res.ResourceTree != nil {
|
||||
resources = append(resources, sonLeafResource(*res, res.ResourceTree, opt.Filter.Kind, opt.Filter.APIVersion)...)
|
||||
}
|
||||
if (opt.Filter.Kind == "" && opt.Filter.APIVersion == "") || (res.Kind == opt.Filter.Kind && res.APIVersion == opt.Filter.APIVersion) {
|
||||
var object unstructured.Unstructured
|
||||
object.SetAPIVersion(opt.Filter.APIVersion)
|
||||
object.SetKind(opt.Filter.Kind)
|
||||
if err := c.Get(ctx, apimachinerytypes.NamespacedName{Namespace: res.Namespace, Name: res.Name}, &object); err == nil {
|
||||
resources = append(resources, object)
|
||||
}
|
||||
}
|
||||
}
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
func sonLeafResource(res querytypes.AppliedResource, node *querytypes.ResourceTreeNode, kind string, apiVersion string) []unstructured.Unstructured {
|
||||
objects := make([]unstructured.Unstructured, 0)
|
||||
if node.LeafNodes != nil {
|
||||
for i := 0; i < len(node.LeafNodes); i++ {
|
||||
objects = append(objects, sonLeafResource(res, node.LeafNodes[i], kind, apiVersion)...)
|
||||
}
|
||||
}
|
||||
if (kind == "" && apiVersion == "") || (node.Kind == kind && node.APIVersion == apiVersion) {
|
||||
objects = append(objects, node.Object)
|
||||
}
|
||||
return objects
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela 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 model
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/velaql/providers/query"
|
||||
querytypes "github.com/oam-dev/kubevela/pkg/velaql/providers/query/types"
|
||||
)
|
||||
|
||||
var _ = Describe("test resource", func() {
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, &CtxKeyAppName, "first-vela-app")
|
||||
ctx = context.WithValue(ctx, &CtxKeyNamespace, "default")
|
||||
ctx = context.WithValue(ctx, &CtxKeyCluster, "")
|
||||
ctx = context.WithValue(ctx, &CtxKeyClusterNamespace, "")
|
||||
ctx = context.WithValue(ctx, &CtxKeyComponentName, "webservice-test")
|
||||
|
||||
opt := query.Option{
|
||||
Name: "first-vela-app",
|
||||
Namespace: "default",
|
||||
Filter: query.FilterOption{
|
||||
Cluster: "",
|
||||
ClusterNamespace: "",
|
||||
Components: []string{"deploy1"},
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
WithTree: true,
|
||||
}
|
||||
|
||||
It("collect resource", func() {
|
||||
podList, err := collectResource(ctx, k8sClient, opt)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(len(podList)).To(Equal(1))
|
||||
})
|
||||
})
|
||||
|
||||
func TestSonLeafResource(t *testing.T) {
|
||||
res := querytypes.AppliedResource{}
|
||||
node := &querytypes.ResourceTreeNode{
|
||||
LeafNodes: []*querytypes.ResourceTreeNode{
|
||||
{
|
||||
Object: unstructured.Unstructured{},
|
||||
},
|
||||
},
|
||||
}
|
||||
objs := sonLeafResource(res, node, "", "")
|
||||
assert.Equal(t, len(objs), 2)
|
||||
}
|
||||
@@ -25,14 +25,11 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/utils/pointer"
|
||||
@@ -40,6 +37,8 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
|
||||
common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
helmapi "github.com/oam-dev/kubevela/pkg/appfile/helm/flux2apis"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
@@ -49,23 +48,8 @@ var cfg *rest.Config
|
||||
var k8sClient client.Client
|
||||
var testEnv *envtest.Environment
|
||||
|
||||
var createObject = func(name string, ns string, value string, kind string) *unstructured.Unstructured {
|
||||
o := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": name,
|
||||
"namespace": ns,
|
||||
},
|
||||
"data": map[string]interface{}{
|
||||
"key": value,
|
||||
},
|
||||
},
|
||||
}
|
||||
o.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind(kind))
|
||||
return o
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
// env init
|
||||
By("bootstrapping test environment")
|
||||
testEnv = &envtest.Environment{
|
||||
ControlPlaneStartTimeout: time.Minute * 3,
|
||||
@@ -75,19 +59,19 @@ var _ = BeforeSuite(func(done Done) {
|
||||
"../../../../charts/vela-core/crds",
|
||||
},
|
||||
}
|
||||
|
||||
// env start
|
||||
By("start kube test env")
|
||||
var err error
|
||||
cfg, err = testEnv.Start()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(cfg).ToNot(BeNil())
|
||||
|
||||
// client start
|
||||
By("new kube client")
|
||||
cfg.Timeout = time.Minute * 2
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: common.Scheme})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(k8sClient).ToNot(BeNil())
|
||||
|
||||
// create app
|
||||
name, namespace := "first-vela-app", "default"
|
||||
testApp := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -165,14 +149,97 @@ var _ = BeforeSuite(func(done Done) {
|
||||
}
|
||||
err = k8sClient.Status().Update(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
svc := createObject("service1", namespace, "x", "Service")
|
||||
// create service
|
||||
svc := &corev1.Service{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Service"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "service1",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{{Port: 2002}},
|
||||
},
|
||||
}
|
||||
svcRaw, err := json.Marshal(svc)
|
||||
Expect(err).Should(Succeed())
|
||||
dply := createObject("deploy1", namespace, "y", "Deployment")
|
||||
Expect(k8sClient.Create(context.TODO(), svc)).Should(BeNil())
|
||||
// create deploy
|
||||
dply := &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Deployment",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "deploy1",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"app": "test"},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: namespace, Labels: map[string]string{"app": "test"}},
|
||||
Spec: corev1.PodSpec{Containers: []corev1.Container{
|
||||
{
|
||||
Name: "vela-core-1",
|
||||
Image: "vela",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
dplyRaw, err := json.Marshal(dply)
|
||||
Expect(err).Should(Succeed())
|
||||
|
||||
Expect(k8sClient.Create(context.TODO(), dply)).Should(BeNil())
|
||||
//create replicaSet
|
||||
var rsNum int32 = 2
|
||||
rs := &appsv1.ReplicaSet{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "rs1",
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{"app": "test"},
|
||||
},
|
||||
Spec: appsv1.ReplicaSetSpec{
|
||||
Replicas: &rsNum,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"app": "test"},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: namespace, Labels: map[string]string{"app": "test"}},
|
||||
Spec: corev1.PodSpec{Containers: []corev1.Container{
|
||||
{
|
||||
Name: "vela-core-1",
|
||||
Image: "vela",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
rsRaw, err := json.Marshal(rs)
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(k8sClient.Create(context.TODO(), rs)).Should(BeNil())
|
||||
// create pod
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod",
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{"app": "test"},
|
||||
},
|
||||
Spec: corev1.PodSpec{Containers: []corev1.Container{
|
||||
{
|
||||
Name: "vela-core-1",
|
||||
Image: "vela",
|
||||
},
|
||||
}},
|
||||
}
|
||||
podRaw, err := json.Marshal(pod)
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(k8sClient.Create(context.TODO(), pod)).Should(BeNil())
|
||||
// create resourceTracker
|
||||
rt := &v1beta1.ResourceTracker{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-v1-%s", name, namespace),
|
||||
@@ -193,11 +260,11 @@ var _ = BeforeSuite(func(done Done) {
|
||||
APIVersion: "v1",
|
||||
Kind: "Service",
|
||||
Namespace: namespace,
|
||||
Name: "web",
|
||||
Name: "service1",
|
||||
},
|
||||
},
|
||||
OAMObjectReference: common2.OAMObjectReference{
|
||||
Component: "web",
|
||||
Component: "service1",
|
||||
},
|
||||
Data: &runtime.RawExtension{Raw: svcRaw},
|
||||
},
|
||||
@@ -208,14 +275,44 @@ var _ = BeforeSuite(func(done Done) {
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Namespace: namespace,
|
||||
Name: "web",
|
||||
Name: "deploy1",
|
||||
},
|
||||
},
|
||||
OAMObjectReference: common2.OAMObjectReference{
|
||||
Component: "web",
|
||||
Component: "deploy1",
|
||||
},
|
||||
Data: &runtime.RawExtension{Raw: dplyRaw},
|
||||
},
|
||||
{
|
||||
ClusterObjectReference: common2.ClusterObjectReference{
|
||||
Cluster: "",
|
||||
ObjectReference: corev1.ObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "ReplicaSet",
|
||||
Namespace: namespace,
|
||||
Name: "rs1",
|
||||
},
|
||||
},
|
||||
OAMObjectReference: common2.OAMObjectReference{
|
||||
Component: "rs1",
|
||||
},
|
||||
Data: &runtime.RawExtension{Raw: rsRaw},
|
||||
},
|
||||
{
|
||||
ClusterObjectReference: common2.ClusterObjectReference{
|
||||
Cluster: "",
|
||||
ObjectReference: corev1.ObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Pod",
|
||||
Namespace: namespace,
|
||||
Name: "pod1",
|
||||
},
|
||||
},
|
||||
OAMObjectReference: common2.OAMObjectReference{
|
||||
Component: "pod1",
|
||||
},
|
||||
Data: &runtime.RawExtension{Raw: podRaw},
|
||||
},
|
||||
},
|
||||
Type: v1beta1.ResourceTrackerTypeVersioned,
|
||||
},
|
||||
@@ -233,8 +330,6 @@ var _ = BeforeSuite(func(done Done) {
|
||||
quantityLimitsMemory, _ := resource.ParseQuantity("10Mi")
|
||||
quantityRequestsCPU, _ := resource.ParseQuantity("10m")
|
||||
quantityRequestsMemory, _ := resource.ParseQuantity("10Mi")
|
||||
//quantityUsageCPU, _ := resource.ParseQuantity("8m")
|
||||
//quantityUsageMemory, _ := resource.ParseQuantity("20Mi")
|
||||
|
||||
pod1 := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "vela-core", Namespace: "vela-system", Labels: map[string]string{"app.kubernetes.io/name": "vela-core"}},
|
||||
|
||||
@@ -58,6 +58,8 @@ var (
|
||||
CtxKeyCluster = "cluster"
|
||||
// CtxKeyClusterNamespace request context key of cluster namespace name
|
||||
CtxKeyClusterNamespace = "cluster"
|
||||
// CtxKeyComponentName request context key of component name
|
||||
CtxKeyComponentName = "componentName"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -17,12 +17,16 @@ limitations under the License.
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
apiv1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/metrics/pkg/apis/metrics/v1beta1"
|
||||
metrics "k8s.io/metrics/pkg/client/clientset/versioned"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -101,6 +105,20 @@ func podRequests(spec v1.PodSpec) (*resource.Quantity, *resource.Quantity) {
|
||||
return cpu, mem
|
||||
}
|
||||
|
||||
// PodMetric return the pod metric
|
||||
func PodMetric(cfg *rest.Config, name, namespace string) (*v1beta1.PodMetrics, error) {
|
||||
ctx := context.Background()
|
||||
c, err := metrics.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metric, err := c.MetricsV1beta1().PodMetricses(namespace).Get(ctx, name, apiv1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return metric, nil
|
||||
}
|
||||
|
||||
// ToPercentage computes percentage as string otherwise n/aa.
|
||||
func ToPercentage(v1, v2 int64) int {
|
||||
if v2 == 0 {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela 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 utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TimeFormat format time data of `time.Duration` type to string type
|
||||
func TimeFormat(t time.Duration) string {
|
||||
str := t.String()
|
||||
// remove "."
|
||||
tmp := strings.Split(str, ".")
|
||||
tmp[0] += "s"
|
||||
|
||||
tmp = strings.Split(tmp[0], "h")
|
||||
// hour num
|
||||
hour, err := strconv.Atoi(tmp[0])
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%dd%dh%2s", hour/24, hour%24, tmp[1])
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela 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 utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTimeFormat(t *testing.T) {
|
||||
t1, err1 := time.ParseDuration("1.5h")
|
||||
assert.NoError(t, err1)
|
||||
assert.Equal(t, TimeFormat(t1), "0d1h30m0ss")
|
||||
t2, err2 := time.ParseDuration("25h")
|
||||
assert.NoError(t, err2)
|
||||
assert.Equal(t, TimeFormat(t2), "1d1h0m0ss")
|
||||
}
|
||||
@@ -111,6 +111,7 @@ func (v *ManagedResourceView) ColorizeStatusText(rowNum int) {
|
||||
func (v *ManagedResourceView) bindKeys() {
|
||||
v.Actions().Delete([]tcell.Key{tcell.KeyEnter})
|
||||
v.Actions().Add(model.KeyActions{
|
||||
tcell.KeyEnter: model.KeyAction{Description: "Enter", Action: v.podView, Visible: true, Shared: true},
|
||||
component.KeyC: model.KeyAction{Description: "Select Cluster", Action: v.clusterView, Visible: true, Shared: true},
|
||||
component.KeyN: model.KeyAction{Description: "Select ClusterNS", Action: v.clusterNamespaceView, Visible: true, Shared: true},
|
||||
tcell.KeyESC: model.KeyAction{Description: "Back", Action: v.app.Back, Visible: true, Shared: true},
|
||||
@@ -131,3 +132,17 @@ func (v *ManagedResourceView) clusterNamespaceView(event *tcell.EventKey) *tcell
|
||||
v.app.command.run(v.ctx, "cns")
|
||||
return event
|
||||
}
|
||||
|
||||
func (v *ManagedResourceView) podView(event *tcell.EventKey) *tcell.EventKey {
|
||||
row, _ := v.GetSelection()
|
||||
if row == 0 {
|
||||
return event
|
||||
}
|
||||
name, namespace, cluster := v.GetCell(row, 0).Text, v.GetCell(row, 1).Text, v.GetCell(row, 4).Text
|
||||
v.ctx = context.WithValue(v.ctx, &model.CtxKeyCluster, cluster)
|
||||
v.ctx = context.WithValue(v.ctx, &model.CtxKeyClusterNamespace, namespace)
|
||||
v.ctx = context.WithValue(v.ctx, &model.CtxKeyComponentName, name)
|
||||
|
||||
v.app.command.run(v.ctx, "pod")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/references/cli/top/model"
|
||||
)
|
||||
|
||||
func TestK8SView(t *testing.T) {
|
||||
func TestManagedResourceView(t *testing.T) {
|
||||
testEnv := &envtest.Environment{
|
||||
ControlPlaneStartTimeout: time.Minute * 3,
|
||||
ControlPlaneStopTimeout: time.Minute,
|
||||
@@ -49,13 +49,13 @@ func TestK8SView(t *testing.T) {
|
||||
ctx = context.WithValue(ctx, &model.CtxKeyCluster, "")
|
||||
|
||||
view := NewManagedResourceView(ctx, app)
|
||||
k8sView, ok := (view).(*ManagedResourceView)
|
||||
resourceView, ok := (view).(*ManagedResourceView)
|
||||
assert.Equal(t, ok, true)
|
||||
|
||||
t.Run("init", func(t *testing.T) {
|
||||
k8sView.Init()
|
||||
assert.Equal(t, k8sView.Table.GetTitle(), "[ Managed Resource (all/all) ]")
|
||||
assert.Equal(t, k8sView.GetCell(0, 0).Text, "Name")
|
||||
resourceView.Init()
|
||||
assert.Equal(t, resourceView.Table.GetTitle(), "[ Managed Resource (all/all) ]")
|
||||
assert.Equal(t, resourceView.GetCell(0, 0).Text, "Name")
|
||||
})
|
||||
|
||||
t.Run("colorize text", func(t *testing.T) {
|
||||
@@ -66,26 +66,29 @@ func TestK8SView(t *testing.T) {
|
||||
{"app", "ns", "", "", "", "UnKnown"}}
|
||||
for i := 0; i < len(testData); i++ {
|
||||
for j := 0; j < len(testData[i]); j++ {
|
||||
k8sView.Table.SetCell(1+i, j, tview.NewTableCell(testData[i][j]))
|
||||
resourceView.Table.SetCell(1+i, j, tview.NewTableCell(testData[i][j]))
|
||||
}
|
||||
}
|
||||
k8sView.ColorizeStatusText(4)
|
||||
assert.Equal(t, k8sView.GetCell(1, 5).Text, "[green::]Healthy")
|
||||
assert.Equal(t, k8sView.GetCell(2, 5).Text, "[red::]UnHealthy")
|
||||
assert.Equal(t, k8sView.GetCell(3, 5).Text, "[blue::]Progressing")
|
||||
assert.Equal(t, k8sView.GetCell(4, 5).Text, "[gray::]UnKnown")
|
||||
resourceView.ColorizeStatusText(4)
|
||||
assert.Equal(t, resourceView.GetCell(1, 5).Text, "[green::]Healthy")
|
||||
assert.Equal(t, resourceView.GetCell(2, 5).Text, "[red::]UnHealthy")
|
||||
assert.Equal(t, resourceView.GetCell(3, 5).Text, "[blue::]Progressing")
|
||||
assert.Equal(t, resourceView.GetCell(4, 5).Text, "[gray::]UnKnown")
|
||||
})
|
||||
|
||||
t.Run("hint", func(t *testing.T) {
|
||||
assert.Equal(t, len(k8sView.Hint()), 4)
|
||||
assert.Equal(t, len(resourceView.Hint()), 5)
|
||||
})
|
||||
|
||||
t.Run("select cluster", func(t *testing.T) {
|
||||
assert.Empty(t, k8sView.clusterView(nil))
|
||||
assert.Empty(t, resourceView.clusterView(nil))
|
||||
})
|
||||
|
||||
t.Run("select cluster namespace", func(t *testing.T) {
|
||||
assert.Empty(t, k8sView.clusterNamespaceView(nil))
|
||||
assert.Empty(t, resourceView.clusterNamespaceView(nil))
|
||||
})
|
||||
|
||||
t.Run("pod view", func(t *testing.T) {
|
||||
assert.Empty(t, resourceView.podView(nil))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela 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 view
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/oam-dev/kubevela/references/cli/top/component"
|
||||
"github.com/oam-dev/kubevela/references/cli/top/config"
|
||||
"github.com/oam-dev/kubevela/references/cli/top/model"
|
||||
)
|
||||
|
||||
// PodView is the pod view, this view display info of pod belonging to component
|
||||
type PodView struct {
|
||||
*ResourceView
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewPodView return a new pod view
|
||||
func NewPodView(ctx context.Context, app *App) model.Component {
|
||||
v := &PodView{
|
||||
ResourceView: NewResourceView(app),
|
||||
ctx: ctx,
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Init cluster view init
|
||||
func (v *PodView) Init() {
|
||||
// set title of view
|
||||
title := fmt.Sprintf("[ %s ]", v.Name())
|
||||
v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor)
|
||||
|
||||
resourceList := v.ListPods()
|
||||
v.ResourceView.Init(resourceList)
|
||||
v.ColorizePhaseText(len(resourceList.Body()))
|
||||
|
||||
v.bindKeys()
|
||||
}
|
||||
|
||||
// ListPods list pods of component
|
||||
func (v *PodView) ListPods() model.ResourceList {
|
||||
list, err := model.ListPods(v.ctx, v.app.config.RestConfig, v.app.client)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// ColorizePhaseText colorize the phase column text
|
||||
func (v *PodView) ColorizePhaseText(rowNum int) {
|
||||
for i := 1; i < rowNum+1; i++ {
|
||||
phase := v.Table.GetCell(i, 3).Text
|
||||
switch v1.PodPhase(phase) {
|
||||
case v1.PodPending:
|
||||
phase = config.PodPendingPhaseColor + phase
|
||||
case v1.PodRunning:
|
||||
phase = config.PodRunningPhaseColor + phase
|
||||
case v1.PodSucceeded:
|
||||
phase = config.PodSucceededPhase + phase
|
||||
case v1.PodFailed:
|
||||
phase = config.PodFailedPhase + phase
|
||||
default:
|
||||
}
|
||||
v.Table.GetCell(i, 3).SetText(phase)
|
||||
}
|
||||
}
|
||||
|
||||
// Name return pod view name
|
||||
func (v *PodView) Name() string {
|
||||
return "Pod"
|
||||
}
|
||||
|
||||
// Hint return key action menu hints of the pod view
|
||||
func (v *PodView) Hint() []model.MenuHint {
|
||||
return v.Actions().Hint()
|
||||
}
|
||||
|
||||
func (v *PodView) bindKeys() {
|
||||
v.Actions().Delete([]tcell.Key{tcell.KeyEnter})
|
||||
v.Actions().Add(model.KeyActions{
|
||||
tcell.KeyESC: model.KeyAction{Description: "Back", Action: v.app.Back, Visible: true, Shared: true},
|
||||
component.KeyHelp: model.KeyAction{Description: "Help", Action: v.app.helpView, Visible: true, Shared: true},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela 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 view
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rivo/tview"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/references/cli/top/model"
|
||||
)
|
||||
|
||||
func TestPodView(t *testing.T) {
|
||||
testEnv := &envtest.Environment{
|
||||
ControlPlaneStartTimeout: time.Minute * 3,
|
||||
ControlPlaneStopTimeout: time.Minute,
|
||||
UseExistingCluster: pointer.BoolPtr(false),
|
||||
}
|
||||
cfg, err := testEnv.Start()
|
||||
assert.NoError(t, err)
|
||||
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
|
||||
assert.NoError(t, err)
|
||||
app := NewApp(testClient, cfg, "")
|
||||
assert.Equal(t, len(app.Components()), 4)
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, &model.CtxKeyAppName, "")
|
||||
ctx = context.WithValue(ctx, &model.CtxKeyNamespace, "")
|
||||
ctx = context.WithValue(ctx, &model.CtxKeyCluster, "")
|
||||
ctx = context.WithValue(ctx, &model.CtxKeyClusterNamespace, "")
|
||||
ctx = context.WithValue(ctx, &model.CtxKeyComponentName, "")
|
||||
|
||||
view, ok := NewPodView(ctx, app).(*PodView)
|
||||
assert.Equal(t, ok, true)
|
||||
|
||||
t.Run("init", func(t *testing.T) {
|
||||
view.Init()
|
||||
assert.Equal(t, view.Table.GetTitle(), "[ Pod ]")
|
||||
assert.Equal(t, view.GetCell(0, 0).Text, "Name")
|
||||
})
|
||||
|
||||
t.Run("colorize text", func(t *testing.T) {
|
||||
testData := [][]string{
|
||||
{"app", "ns", "1/1", "Running", "", "", "", "", "", "", "", "", ""},
|
||||
{"app", "ns", "1/1", "Pending", "", "", "", "", "", "", "", "", ""},
|
||||
{"app", "ns", "1/1", "Succeeded", "", "", "", "", "", "", "", "", ""},
|
||||
{"app", "ns", "1/1", "Failed", "", "", "", "", "", "", "", "", ""},
|
||||
}
|
||||
for i := 0; i < len(testData); i++ {
|
||||
for j := 0; j < len(testData[i]); j++ {
|
||||
view.Table.SetCell(1+i, j, tview.NewTableCell(testData[i][j]))
|
||||
}
|
||||
}
|
||||
view.ColorizePhaseText(5)
|
||||
assert.Equal(t, view.GetCell(1, 3).Text, "[green::]Running")
|
||||
assert.Equal(t, view.GetCell(2, 3).Text, "[yellow::]Pending")
|
||||
assert.Equal(t, view.GetCell(3, 3).Text, "[purple::]Succeeded")
|
||||
assert.Equal(t, view.GetCell(4, 3).Text, "[red::]Failed")
|
||||
})
|
||||
|
||||
t.Run("hint", func(t *testing.T) {
|
||||
assert.Equal(t, len(view.Hint()), 2)
|
||||
})
|
||||
}
|
||||
@@ -53,6 +53,9 @@ var ResourceMap = map[string]ResourceViewer{
|
||||
"cns": {
|
||||
viewFunc: NewClusterNamespaceView,
|
||||
},
|
||||
"pod": {
|
||||
viewFunc: NewPodView,
|
||||
},
|
||||
}
|
||||
|
||||
// NewResourceView return a new resource view
|
||||
|
||||
Reference in New Issue
Block a user