mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 18:10:21 +00:00
Feat: add filtering features to the Application view and K8S object view of vela top (#4612)
* Feat: when `vela top` launch, can specify the namespace of the presentation application Signed-off-by: HanMengnan <1448189829@qq.com> * Feat: add filtering function to the k8s object view of the `vela top` command, which can be filtered by cluster and cluster namespace Signed-off-by: HanMengnan <1448189829@qq.com> Signed-off-by: HanMengnan <1448189829@qq.com>
This commit is contained in:
@@ -34,19 +34,35 @@ func NewTopCommand(c common.Args, order string, ioStreams cmdutil.IOStreams) *co
|
||||
Short: "Launch UI to display the platform overview.",
|
||||
Long: "Launch UI to display platform overview information and diagnose the status for any specific application.",
|
||||
Example: ` # Launch UI to display platform overview information and diagnose the status for any specific application
|
||||
vela top`,
|
||||
vela top
|
||||
|
||||
# Show applications which are in <vela-namespace> namespace
|
||||
vela top -n <vela-namespace>
|
||||
|
||||
# Show applications which are in all namespaces
|
||||
vela top -A
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return launchUI(c, cmd)
|
||||
namespace, err := GetFlagNamespaceOrEnv(cmd, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if AllNamespace {
|
||||
namespace = ""
|
||||
}
|
||||
return launchUI(c, namespace)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
types.TagCommandOrder: order,
|
||||
types.TagCommandType: types.TypeApp,
|
||||
},
|
||||
}
|
||||
addNamespaceAndEnvArg(cmd)
|
||||
cmd.Flags().BoolVarP(&AllNamespace, "all-namespaces", "A", false, "If true, check the specified action in all namespaces.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func launchUI(c common.Args, _ *cobra.Command) error {
|
||||
func launchUI(c common.Args, namespace string) error {
|
||||
k8sClient, err := c.GetClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get k8s client: %w", err)
|
||||
@@ -55,7 +71,7 @@ func launchUI(c common.Args, _ *cobra.Command) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app := view.NewApp(k8sClient, restConfig)
|
||||
app := view.NewApp(k8sClient, restConfig, namespace)
|
||||
app.Init()
|
||||
|
||||
return app.Run()
|
||||
|
||||
@@ -29,10 +29,71 @@ const (
|
||||
KeySpace = 32
|
||||
)
|
||||
|
||||
// Defines char keystrokes.
|
||||
const (
|
||||
KeyA tcell.Key = iota + 97
|
||||
KeyB
|
||||
KeyC
|
||||
KeyD
|
||||
KeyE
|
||||
KeyF
|
||||
KeyG
|
||||
KeyH
|
||||
KeyI
|
||||
KeyJ
|
||||
KeyK
|
||||
KeyL
|
||||
KeyM
|
||||
KeyN
|
||||
KeyO
|
||||
KeyP
|
||||
KeyQ
|
||||
KeyR
|
||||
KeyS
|
||||
KeyT
|
||||
KeyU
|
||||
KeyV
|
||||
KeyW
|
||||
KeyX
|
||||
KeyY
|
||||
KeyZ
|
||||
)
|
||||
|
||||
func init() {
|
||||
tcell.KeyNames[tcell.Key(KeyHelp)] = "?"
|
||||
tcell.KeyNames[tcell.Key(KeySlash)] = "/"
|
||||
tcell.KeyNames[tcell.Key(KeySpace)] = "space"
|
||||
|
||||
initStdKeys()
|
||||
}
|
||||
|
||||
func initStdKeys() {
|
||||
tcell.KeyNames[KeyA] = "a"
|
||||
tcell.KeyNames[KeyB] = "b"
|
||||
tcell.KeyNames[KeyC] = "c"
|
||||
tcell.KeyNames[KeyD] = "d"
|
||||
tcell.KeyNames[KeyE] = "e"
|
||||
tcell.KeyNames[KeyF] = "f"
|
||||
tcell.KeyNames[KeyG] = "g"
|
||||
tcell.KeyNames[KeyH] = "h"
|
||||
tcell.KeyNames[KeyI] = "i"
|
||||
tcell.KeyNames[KeyJ] = "j"
|
||||
tcell.KeyNames[KeyK] = "k"
|
||||
tcell.KeyNames[KeyL] = "l"
|
||||
tcell.KeyNames[KeyM] = "m"
|
||||
tcell.KeyNames[KeyN] = "n"
|
||||
tcell.KeyNames[KeyO] = "o"
|
||||
tcell.KeyNames[KeyP] = "p"
|
||||
tcell.KeyNames[KeyQ] = "q"
|
||||
tcell.KeyNames[KeyR] = "r"
|
||||
tcell.KeyNames[KeyS] = "s"
|
||||
tcell.KeyNames[KeyT] = "t"
|
||||
tcell.KeyNames[KeyU] = "u"
|
||||
tcell.KeyNames[KeyV] = "v"
|
||||
tcell.KeyNames[KeyW] = "w"
|
||||
tcell.KeyNames[KeyX] = "x"
|
||||
tcell.KeyNames[KeyY] = "y"
|
||||
tcell.KeyNames[KeyZ] = "z"
|
||||
}
|
||||
|
||||
// StandardizeKey standardized combined key event and return corresponding key
|
||||
|
||||
@@ -42,7 +42,12 @@ func NewMenu() *Menu {
|
||||
|
||||
// StackPop change itself when accept "pop" notify from app's main view
|
||||
func (m *Menu) StackPop(old, new model.Component) {
|
||||
m.UpdateMenu(new.Hint())
|
||||
if new == nil {
|
||||
m.UpdateMenu([]model.MenuHint{})
|
||||
} else {
|
||||
m.UpdateMenu(new.Hint())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// StackPush change itself when accept "push" notify from app's main view
|
||||
|
||||
@@ -60,6 +60,7 @@ func (l *ClusterList) Body() [][]string {
|
||||
func ListClusters(ctx context.Context, c client.Client) *ClusterList {
|
||||
list := &ClusterList{
|
||||
title: []string{"Name", "Alias", "Type", "EndPoint", "Labels"},
|
||||
data: []Cluster{{"all", "*", "*", "*", "*"}},
|
||||
}
|
||||
name := ctx.Value(&CtxKeyAppName).(string)
|
||||
ns := ctx.Value(&CtxKeyNamespace).(string)
|
||||
|
||||
68
references/cli/top/model/cluster_namespace.go
Normal file
68
references/cli/top/model/cluster_namespace.go
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// ListClusterNamespaces return namespace of application's resource
|
||||
func ListClusterNamespaces(ctx context.Context, c client.Client) ResourceList {
|
||||
list := &NamespaceList{
|
||||
title: []string{"Name", "Status", "Age"},
|
||||
data: []Namespace{{"all", "*", "*"}},
|
||||
}
|
||||
name := ctx.Value(&CtxKeyAppName).(string)
|
||||
ns := ctx.Value(&CtxKeyNamespace).(string)
|
||||
app, err := LoadApplication(c, name, ns)
|
||||
if err != nil {
|
||||
return list
|
||||
}
|
||||
clusterNSSet := make(map[string]interface{})
|
||||
for _, svc := range app.Status.AppliedResources {
|
||||
if svc.Namespace != "" {
|
||||
clusterNSSet[svc.Namespace] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for clusterNS := range clusterNSSet {
|
||||
namespaceInfo := LoadNamespaceDetail(ctx, c, clusterNS)
|
||||
if namespaceInfo != nil {
|
||||
list.data = append(list.data, Namespace{
|
||||
Name: namespaceInfo.Name,
|
||||
Status: string(namespaceInfo.Status.Phase),
|
||||
Age: timeFormat(time.Since(namespaceInfo.CreationTimestamp.Time)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
// LoadNamespaceDetail query detail info of a namespace by name
|
||||
func LoadNamespaceDetail(ctx context.Context, c client.Client, namespace string) *v1.Namespace {
|
||||
ns := new(v1.Namespace)
|
||||
if err := c.Get(ctx, types.NamespacedName{Name: namespace}, ns); err != nil {
|
||||
return nil
|
||||
}
|
||||
return ns
|
||||
}
|
||||
42
references/cli/top/model/cluster_namespace_test.go
Normal file
42
references/cli/top/model/cluster_namespace_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("test cluster namespace", func() {
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, &CtxKeyAppName, "first-vela-app")
|
||||
ctx = context.WithValue(ctx, &CtxKeyNamespace, "default")
|
||||
It("list cluster namespace", func() {
|
||||
cnsList := ListClusterNamespaces(ctx, k8sClient)
|
||||
Expect(len(cnsList.Header())).To(Equal(3))
|
||||
Expect(cnsList.Header()).To(Equal([]string{"Name", "Status", "Age"}))
|
||||
|
||||
Expect(len(cnsList.Body())).To(Equal(2))
|
||||
Expect(cnsList.Body()[1][0]).To(Equal("default"))
|
||||
})
|
||||
It("load cluster namespace detail info", func() {
|
||||
ns := LoadNamespaceDetail(ctx, k8sClient, "default")
|
||||
Expect(string(ns.Status.Phase)).To(Equal("Active"))
|
||||
})
|
||||
})
|
||||
@@ -45,7 +45,7 @@ var _ = Describe("test cluster", func() {
|
||||
clusterList := ListClusters(ctx, k8sClient)
|
||||
Expect(len(clusterList.Header())).To(Equal(5))
|
||||
Expect(clusterList.Header()).To(Equal([]string{"Name", "Alias", "Type", "EndPoint", "Labels"}))
|
||||
Expect(len(clusterList.Body())).To(Equal(1))
|
||||
Expect(clusterList.Body()[0]).To(Equal([]string{"local", "", "Internal", "-", ""}))
|
||||
Expect(len(clusterList.Body())).To(Equal(2))
|
||||
Expect(clusterList.Body()[1]).To(Equal([]string{"local", "", "Internal", "-", ""}))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/velaql/providers/query"
|
||||
)
|
||||
|
||||
@@ -55,6 +54,28 @@ func (l *K8SObjectList) Body() [][]string {
|
||||
return data
|
||||
}
|
||||
|
||||
// FilterCluster filter out objects that belong to the target cluster
|
||||
func (l *K8SObjectList) FilterCluster(cluster string) {
|
||||
data := make([]K8SObject, 0)
|
||||
for _, app := range l.data {
|
||||
if app.cluster == cluster {
|
||||
data = append(data, K8SObject{app.name, app.namespace, app.kind, app.apiVersion, app.cluster, app.status})
|
||||
}
|
||||
}
|
||||
l.data = data
|
||||
}
|
||||
|
||||
// FilterClusterNamespace filter out objects that belong to the target namespace
|
||||
func (l *K8SObjectList) FilterClusterNamespace(clusterNS string) {
|
||||
data := make([]K8SObject, 0)
|
||||
for _, app := range l.data {
|
||||
if app.namespace == clusterNS {
|
||||
data = append(data, K8SObject{app.name, app.namespace, app.kind, app.apiVersion, app.cluster, app.status})
|
||||
}
|
||||
}
|
||||
l.data = data
|
||||
}
|
||||
|
||||
// ListObjects return k8s object resource list
|
||||
func ListObjects(ctx context.Context, c client.Client) *K8SObjectList {
|
||||
list := &K8SObjectList{
|
||||
@@ -62,15 +83,13 @@ func ListObjects(ctx context.Context, c client.Client) *K8SObjectList {
|
||||
}
|
||||
name := ctx.Value(&CtxKeyAppName).(string)
|
||||
namespace := ctx.Value(&CtxKeyNamespace).(string)
|
||||
cluster := ctx.Value(&CtxKeyCluster).(string)
|
||||
|
||||
opt := query.Option{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Filter: query.FilterOption{},
|
||||
}
|
||||
if cluster != multicluster.ClusterLocalName {
|
||||
opt.Filter = query.FilterOption{Cluster: cluster}
|
||||
}
|
||||
|
||||
collector := query.NewAppCollector(c, opt)
|
||||
appResList, err := collector.CollectResourceFromApp()
|
||||
|
||||
@@ -82,6 +101,15 @@ func ListObjects(ctx context.Context, c client.Client) *K8SObjectList {
|
||||
list.data = append(list.data, LoadObjectDetail(resource))
|
||||
}
|
||||
|
||||
cluster, ok := ctx.Value(&CtxKeyCluster).(string)
|
||||
if ok && cluster != "" {
|
||||
list.FilterCluster(cluster)
|
||||
}
|
||||
clusterNamespace, ok := ctx.Value(&CtxKeyClusterNamespace).(string)
|
||||
if ok && clusterNamespace != "" {
|
||||
list.FilterClusterNamespace(clusterNamespace)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
92
references/cli/top/model/namespace.go
Normal file
92
references/cli/top/model/namespace.go
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
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"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// Namespace is namespace struct
|
||||
type Namespace struct {
|
||||
Name string
|
||||
Status string
|
||||
Age string
|
||||
}
|
||||
|
||||
// NamespaceList is namespace list
|
||||
type NamespaceList struct {
|
||||
title []string
|
||||
data []Namespace
|
||||
}
|
||||
|
||||
// AllNamespace is the key which represents all namespaces
|
||||
const AllNamespace = "all"
|
||||
|
||||
// ListNamespaces return all namespaces
|
||||
func ListNamespaces(ctx context.Context, c client.Reader) *NamespaceList {
|
||||
list := &NamespaceList{title: []string{"Name", "Status", "Age"}, data: []Namespace{{Name: AllNamespace, Status: "*", Age: "*"}}}
|
||||
var nsList v1.NamespaceList
|
||||
if err := c.List(ctx, &nsList); err != nil {
|
||||
return list
|
||||
}
|
||||
for _, ns := range nsList.Items {
|
||||
list.data = append(list.data, Namespace{
|
||||
Name: ns.Name,
|
||||
Status: string(ns.Status.Phase),
|
||||
Age: timeFormat(time.Since(ns.CreationTimestamp.Time)),
|
||||
})
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// Header generate header of table in namespace view
|
||||
func (l *NamespaceList) Header() []string {
|
||||
return l.title
|
||||
}
|
||||
|
||||
// Body generate body of table in namespace view
|
||||
func (l *NamespaceList) Body() [][]string {
|
||||
data := make([][]string, 0)
|
||||
for _, ns := range l.data {
|
||||
data = append(data, []string{ns.Name, ns.Status, ns.Age})
|
||||
}
|
||||
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])
|
||||
}
|
||||
66
references/cli/top/model/namespace_test.go
Normal file
66
references/cli/top/model/namespace_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
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"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNamespaceList_Header(t *testing.T) {
|
||||
nsList := NamespaceList{
|
||||
title: []string{"Name", "Status", "Age"},
|
||||
data: []Namespace{{Name: AllNamespace, Status: "*", Age: "*"}},
|
||||
}
|
||||
assert.Equal(t, nsList.Header(), []string{"Name", "Status", "Age"})
|
||||
}
|
||||
|
||||
func TestNamespaceList_Body(t *testing.T) {
|
||||
nsList := NamespaceList{
|
||||
title: []string{"Name", "Status", "Age"},
|
||||
data: []Namespace{{Name: AllNamespace, Status: "*", Age: "*"}},
|
||||
}
|
||||
assert.Equal(t, len(nsList.Body()), 1)
|
||||
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() {
|
||||
nsList := ListNamespaces(ctx, k8sClient)
|
||||
Expect(len(nsList.Header())).To(Equal(3))
|
||||
Expect(nsList.Header()).To(Equal([]string{"Name", "Status", "Age"}))
|
||||
fmt.Println(nsList.Body())
|
||||
Expect(len(nsList.Body())).To(Equal(5))
|
||||
Expect(nsList.Body()[0]).To(Equal([]string{"all", "*", "*"}))
|
||||
})
|
||||
})
|
||||
@@ -112,6 +112,13 @@ func (s *Stack) Empty() bool {
|
||||
return len(s.components) == 0
|
||||
}
|
||||
|
||||
// Clear out the stack
|
||||
func (s *Stack) Clear() {
|
||||
for !s.Empty() {
|
||||
s.PopComponent()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stack) notifyListener(action int, component Component) {
|
||||
for _, listener := range s.listeners {
|
||||
switch action {
|
||||
|
||||
@@ -52,8 +52,17 @@ type (
|
||||
var (
|
||||
// CtxKeyAppName request context key of application name
|
||||
CtxKeyAppName = "appName"
|
||||
// CtxKeyCluster request context key of cluster name
|
||||
CtxKeyCluster = "cluster"
|
||||
// CtxKeyNamespace request context key of namespace name
|
||||
CtxKeyNamespace = "appNs"
|
||||
// CtxKeyCluster request context key of cluster name
|
||||
CtxKeyCluster = "cluster"
|
||||
// CtxKeyClusterNamespace request context key of cluster namespace name
|
||||
CtxKeyClusterNamespace = "cluster"
|
||||
)
|
||||
|
||||
const (
|
||||
// AllClusterNamespace represent all cluster namespace
|
||||
AllClusterNamespace = "all"
|
||||
// AllCluster represent all cluster
|
||||
AllCluster = "all"
|
||||
)
|
||||
|
||||
@@ -36,19 +36,22 @@ type App struct {
|
||||
config config.Config
|
||||
command *Command
|
||||
content *PageStack
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewApp return a new app object
|
||||
func NewApp(c client.Client, restConfig *rest.Config) *App {
|
||||
func NewApp(c client.Client, restConfig *rest.Config, namespace string) *App {
|
||||
a := &App{
|
||||
App: component.NewApp(),
|
||||
client: c,
|
||||
config: config.Config{
|
||||
RestConfig: restConfig,
|
||||
},
|
||||
ctx: context.Background(),
|
||||
}
|
||||
a.command = NewCommand(a)
|
||||
a.content = NewPageStack(a)
|
||||
a.ctx = context.WithValue(a.ctx, &model.CtxKeyNamespace, namespace)
|
||||
return a
|
||||
}
|
||||
|
||||
@@ -125,9 +128,7 @@ func (a *App) inject(c model.Component) {
|
||||
|
||||
// defaultView is the first view of running application
|
||||
func (a *App) defaultView(event *tcell.EventKey) *tcell.EventKey {
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, &model.CtxKeyNamespace, "")
|
||||
a.command.run(ctx, "app")
|
||||
a.command.run(a.ctx, "app")
|
||||
return event
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestApp(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
|
||||
assert.NoError(t, err)
|
||||
app := NewApp(testClient, cfg)
|
||||
app := NewApp(testClient, cfg, "")
|
||||
assert.Equal(t, len(app.Components), 4)
|
||||
|
||||
t.Run("init", func(t *testing.T) {
|
||||
|
||||
@@ -46,7 +46,7 @@ func NewApplicationView(ctx context.Context, app *App) model.Component {
|
||||
// Init the application view
|
||||
func (v *ApplicationView) Init() {
|
||||
// set title of view
|
||||
title := fmt.Sprintf("[ %s ]", v.Name())
|
||||
title := fmt.Sprintf("[ %s ]", v.Title())
|
||||
v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor)
|
||||
// init view
|
||||
resourceList := v.ListApplications()
|
||||
@@ -80,6 +80,15 @@ func (v *ApplicationView) ColorizeStatusText(rowNum int) {
|
||||
}
|
||||
}
|
||||
|
||||
// Title return table title of application view
|
||||
func (v *ApplicationView) Title() string {
|
||||
namespace := v.ctx.Value(&model.CtxKeyNamespace).(string)
|
||||
if namespace == "" {
|
||||
namespace = "all"
|
||||
}
|
||||
return fmt.Sprintf("Application"+" (%s)", namespace)
|
||||
}
|
||||
|
||||
// Name return application view name
|
||||
func (v *ApplicationView) Name() string {
|
||||
return "Application"
|
||||
@@ -93,14 +102,14 @@ func (v *ApplicationView) Hint() []model.MenuHint {
|
||||
func (v *ApplicationView) bindKeys() {
|
||||
v.Actions().Delete([]tcell.Key{tcell.KeyEnter})
|
||||
v.Actions().Add(model.KeyActions{
|
||||
tcell.KeyEnter: model.KeyAction{Description: "Goto", Action: v.clusterView, Visible: true, Shared: true},
|
||||
tcell.KeyEnter: model.KeyAction{Description: "Goto", Action: v.k8sObjectView, Visible: true, Shared: true},
|
||||
component.KeyN: model.KeyAction{Description: "Select Namespace", Action: v.namespaceView, Visible: true, Shared: true},
|
||||
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},
|
||||
})
|
||||
}
|
||||
|
||||
// clusterView switch app main view to the cluster view
|
||||
func (v *ApplicationView) clusterView(event *tcell.EventKey) *tcell.EventKey {
|
||||
func (v *ApplicationView) k8sObjectView(event *tcell.EventKey) *tcell.EventKey {
|
||||
row, _ := v.GetSelection()
|
||||
if row == 0 {
|
||||
return event
|
||||
@@ -110,6 +119,12 @@ func (v *ApplicationView) clusterView(event *tcell.EventKey) *tcell.EventKey {
|
||||
v.ctx = context.WithValue(v.ctx, &model.CtxKeyAppName, name)
|
||||
v.ctx = context.WithValue(v.ctx, &model.CtxKeyNamespace, namespace)
|
||||
|
||||
v.app.command.run(v.ctx, "cluster")
|
||||
v.app.command.run(v.ctx, "k8s")
|
||||
return event
|
||||
}
|
||||
|
||||
func (v *ApplicationView) namespaceView(event *tcell.EventKey) *tcell.EventKey {
|
||||
v.app.content.Clear()
|
||||
v.app.command.run(v.ctx, "ns")
|
||||
return event
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestApplicationView(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
|
||||
assert.NoError(t, err)
|
||||
app := NewApp(testClient, cfg)
|
||||
app := NewApp(testClient, cfg, "")
|
||||
assert.Equal(t, len(app.Components), 4)
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, &model.CtxKeyNamespace, "")
|
||||
@@ -51,7 +51,7 @@ func TestApplicationView(t *testing.T) {
|
||||
|
||||
t.Run("init", func(t *testing.T) {
|
||||
appView.Init()
|
||||
assert.Equal(t, appView.Table.GetTitle(), "[ Application ]")
|
||||
assert.Equal(t, appView.Table.GetTitle(), "[ Application (all) ]")
|
||||
assert.Equal(t, appView.GetCell(0, 0).Text, "Name")
|
||||
})
|
||||
|
||||
@@ -70,12 +70,12 @@ func TestApplicationView(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("hint", func(t *testing.T) {
|
||||
assert.Equal(t, len(appView.Hint()), 3)
|
||||
assert.Equal(t, len(appView.Hint()), 4)
|
||||
})
|
||||
|
||||
t.Run("cluster view", func(t *testing.T) {
|
||||
t.Run("object view", func(t *testing.T) {
|
||||
appView.Table.Table.Table = appView.Table.Select(1, 1)
|
||||
assert.Empty(t, appView.clusterView(nil))
|
||||
assert.Empty(t, appView.k8sObjectView(nil))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
94
references/cli/top/view/cluster_namespace_view.go
Normal file
94
references/cli/top/view/cluster_namespace_view.go
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// ClusterNamespaceView is the cluster namespace, which display the namespace info of application's resource
|
||||
type ClusterNamespaceView struct {
|
||||
*ResourceView
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewClusterNamespaceView return a new cluster namespace view
|
||||
func NewClusterNamespaceView(ctx context.Context, app *App) model.Component {
|
||||
v := &ClusterNamespaceView{
|
||||
ResourceView: NewResourceView(app),
|
||||
ctx: ctx,
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Init the cluster namespace view
|
||||
func (v *ClusterNamespaceView) Init() {
|
||||
title := fmt.Sprintf("[ %s ]", v.Name())
|
||||
v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor)
|
||||
|
||||
resourceList := v.ListClusterNamespaces()
|
||||
v.ResourceView.Init(resourceList)
|
||||
|
||||
v.bindKeys()
|
||||
}
|
||||
|
||||
// ListClusterNamespaces return the namespace of application's resource
|
||||
func (v *ClusterNamespaceView) ListClusterNamespaces() model.ResourceList {
|
||||
return model.ListClusterNamespaces(v.ctx, v.app.client)
|
||||
}
|
||||
|
||||
// Hint return key action menu hints of the cluster namespace view
|
||||
func (v *ClusterNamespaceView) Hint() []model.MenuHint {
|
||||
return v.Actions().Hint()
|
||||
}
|
||||
|
||||
// Name return cluster namespace view name
|
||||
func (v *ClusterNamespaceView) Name() string {
|
||||
return "ClusterNamespace"
|
||||
}
|
||||
|
||||
func (v *ClusterNamespaceView) bindKeys() {
|
||||
v.Actions().Delete([]tcell.Key{tcell.KeyEnter})
|
||||
v.Actions().Add(model.KeyActions{
|
||||
tcell.KeyEnter: model.KeyAction{Description: "Select", Action: v.k8sObjectView, Visible: true, Shared: true},
|
||||
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},
|
||||
})
|
||||
}
|
||||
|
||||
// k8sObjectView switch cluster namespace view to k8s object view
|
||||
func (v *ClusterNamespaceView) k8sObjectView(event *tcell.EventKey) *tcell.EventKey {
|
||||
row, _ := v.GetSelection()
|
||||
if row == 0 {
|
||||
return event
|
||||
}
|
||||
v.app.content.PopComponent()
|
||||
clusterNamespace := v.GetCell(row, 0).Text
|
||||
if clusterNamespace == model.AllClusterNamespace {
|
||||
clusterNamespace = ""
|
||||
}
|
||||
v.ctx = context.WithValue(v.ctx, &model.CtxKeyClusterNamespace, clusterNamespace)
|
||||
v.app.command.run(v.ctx, "k8s")
|
||||
return event
|
||||
}
|
||||
66
references/cli/top/view/cluster_namespace_view_test.go
Normal file
66
references/cli/top/view/cluster_namespace_view_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
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/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 TestClusterNamespaceView(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, "")
|
||||
cnsView, ok := NewClusterNamespaceView(ctx, app).(*ClusterNamespaceView)
|
||||
assert.Equal(t, ok, true)
|
||||
|
||||
t.Run("init", func(t *testing.T) {
|
||||
cnsView.Init()
|
||||
assert.Equal(t, cnsView.GetTitle(), "[ ClusterNamespace ]")
|
||||
assert.Equal(t, cnsView.GetCell(0, 0).Text, "Name")
|
||||
assert.Equal(t, cnsView.GetCell(1, 0).Text, "all")
|
||||
})
|
||||
|
||||
t.Run("hint", func(t *testing.T) {
|
||||
assert.Equal(t, len(cnsView.Hint()), 3)
|
||||
})
|
||||
|
||||
t.Run("object view", func(t *testing.T) {
|
||||
cnsView.Table.Table.Table = cnsView.Table.Select(1, 1)
|
||||
assert.Empty(t, cnsView.k8sObjectView(nil))
|
||||
})
|
||||
}
|
||||
@@ -77,13 +77,17 @@ func (v *ClusterView) bindKeys() {
|
||||
})
|
||||
}
|
||||
|
||||
// k8sObjectView switch app main view to k8s object view
|
||||
// k8sObjectView switch cluster view to k8s object view
|
||||
func (v *ClusterView) k8sObjectView(event *tcell.EventKey) *tcell.EventKey {
|
||||
row, _ := v.GetSelection()
|
||||
if row == 0 {
|
||||
return event
|
||||
}
|
||||
v.app.content.PopComponent()
|
||||
clusterName := v.GetCell(row, 0).Text
|
||||
if clusterName == model.AllCluster {
|
||||
clusterName = ""
|
||||
}
|
||||
v.ctx = context.WithValue(v.ctx, &model.CtxKeyCluster, clusterName)
|
||||
v.app.command.run(v.ctx, "k8s")
|
||||
return event
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestClusterView(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
|
||||
assert.NoError(t, err)
|
||||
app := NewApp(testClient, cfg)
|
||||
app := NewApp(testClient, cfg, "")
|
||||
assert.Equal(t, len(app.Components), 4)
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, &model.CtxKeyAppName, "")
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestHelpView(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
|
||||
assert.NoError(t, err)
|
||||
app := NewApp(testClient, cfg)
|
||||
app := NewApp(testClient, cfg, "")
|
||||
assert.Equal(t, len(app.Components), 4)
|
||||
helpView := NewHelpView(app)
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ func NewK8SView(ctx context.Context, app *App) model.Component {
|
||||
// Init k8s view
|
||||
func (v *K8SView) Init() {
|
||||
// set title of view
|
||||
title := fmt.Sprintf("[ %s ]", v.Name())
|
||||
title := fmt.Sprintf("[ %s ]", v.Title())
|
||||
v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor)
|
||||
|
||||
resourceList := v.ListK8SObjects()
|
||||
@@ -61,6 +61,19 @@ func (v *K8SView) ListK8SObjects() model.ResourceList {
|
||||
return model.ListObjects(v.ctx, v.app.client)
|
||||
}
|
||||
|
||||
// Title return the table title of k8s object view
|
||||
func (v *K8SView) Title() string {
|
||||
namespace, ok := v.ctx.Value(&model.CtxKeyCluster).(string)
|
||||
if !ok || namespace == "" {
|
||||
namespace = "all"
|
||||
}
|
||||
clusterNS, ok := v.ctx.Value(&model.CtxKeyClusterNamespace).(string)
|
||||
if !ok || clusterNS == "" {
|
||||
clusterNS = "all"
|
||||
}
|
||||
return fmt.Sprintf("K8S-Object"+" (%s/%s)", namespace, clusterNS)
|
||||
}
|
||||
|
||||
// Name return k8s view name
|
||||
func (v *K8SView) Name() string {
|
||||
return "K8S-Object"
|
||||
@@ -93,7 +106,23 @@ func (v *K8SView) ColorizeStatusText(rowNum int) {
|
||||
func (v *K8SView) bindKeys() {
|
||||
v.Actions().Delete([]tcell.Key{tcell.KeyEnter})
|
||||
v.Actions().Add(model.KeyActions{
|
||||
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},
|
||||
component.KeyHelp: model.KeyAction{Description: "Help", Action: v.app.helpView, Visible: true, Shared: true},
|
||||
})
|
||||
}
|
||||
|
||||
// clusterView switch k8s object view to the cluster view
|
||||
func (v *K8SView) clusterView(event *tcell.EventKey) *tcell.EventKey {
|
||||
v.app.content.PopComponent()
|
||||
v.app.command.run(v.ctx, "cluster")
|
||||
return event
|
||||
}
|
||||
|
||||
// clusterView switch k8s object view to the cluster Namespace view
|
||||
func (v *K8SView) clusterNamespaceView(event *tcell.EventKey) *tcell.EventKey {
|
||||
v.app.content.PopComponent()
|
||||
v.app.command.run(v.ctx, "cns")
|
||||
return event
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func TestK8SView(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
|
||||
assert.NoError(t, err)
|
||||
app := NewApp(testClient, cfg)
|
||||
app := NewApp(testClient, cfg, "")
|
||||
assert.Equal(t, len(app.Components), 4)
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, &model.CtxKeyAppName, "")
|
||||
@@ -54,7 +54,7 @@ func TestK8SView(t *testing.T) {
|
||||
|
||||
t.Run("init", func(t *testing.T) {
|
||||
k8sView.Init()
|
||||
assert.Equal(t, k8sView.Table.GetTitle(), "[ K8S-Object ]")
|
||||
assert.Equal(t, k8sView.Table.GetTitle(), "[ K8S-Object (all/all) ]")
|
||||
assert.Equal(t, k8sView.GetCell(0, 0).Text, "Name")
|
||||
})
|
||||
|
||||
@@ -77,9 +77,15 @@ func TestK8SView(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("hint", func(t *testing.T) {
|
||||
t.Run("hint", func(t *testing.T) {
|
||||
assert.Equal(t, len(k8sView.Hint()), 2)
|
||||
})
|
||||
assert.Equal(t, len(k8sView.Hint()), 4)
|
||||
})
|
||||
|
||||
t.Run("select cluster", func(t *testing.T) {
|
||||
assert.Empty(t, k8sView.clusterView(nil))
|
||||
})
|
||||
|
||||
t.Run("select cluster namespace", func(t *testing.T) {
|
||||
assert.Empty(t, k8sView.clusterNamespaceView(nil))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
94
references/cli/top/view/namespace_view.go
Normal file
94
references/cli/top/view/namespace_view.go
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// NamespaceView is namespace view struct
|
||||
type NamespaceView struct {
|
||||
*ResourceView
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewNamespaceView return a new namespace view
|
||||
func NewNamespaceView(ctx context.Context, app *App) model.Component {
|
||||
v := &NamespaceView{
|
||||
ResourceView: NewResourceView(app),
|
||||
ctx: ctx,
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Init a namespace view
|
||||
func (v *NamespaceView) Init() {
|
||||
title := fmt.Sprintf("[ %s ]", v.Name())
|
||||
v.SetTitle(title).SetTitleColor(config.ResourceTableTitleColor)
|
||||
|
||||
resourceList := v.ListNamespaces()
|
||||
v.ResourceView.Init(resourceList)
|
||||
|
||||
v.bindKeys()
|
||||
}
|
||||
|
||||
// ListNamespaces return all namespaces
|
||||
func (v *NamespaceView) ListNamespaces() model.ResourceList {
|
||||
return model.ListNamespaces(v.ctx, v.app.client)
|
||||
}
|
||||
|
||||
// Hint return key action menu hints of the k8s view
|
||||
func (v *NamespaceView) Hint() []model.MenuHint {
|
||||
return v.Actions().Hint()
|
||||
}
|
||||
|
||||
// Name return k8s view name
|
||||
func (v *NamespaceView) Name() string {
|
||||
return "Namespace"
|
||||
}
|
||||
|
||||
func (v *NamespaceView) bindKeys() {
|
||||
v.Actions().Delete([]tcell.Key{tcell.KeyEnter})
|
||||
v.Actions().Add(model.KeyActions{
|
||||
tcell.KeyEnter: model.KeyAction{Description: "Select", Action: v.applicationView, Visible: true, Shared: true},
|
||||
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},
|
||||
})
|
||||
}
|
||||
|
||||
func (v *NamespaceView) applicationView(event *tcell.EventKey) *tcell.EventKey {
|
||||
row, _ := v.GetSelection()
|
||||
if row == 0 {
|
||||
return event
|
||||
}
|
||||
v.app.content.PopComponent()
|
||||
ns := v.Table.GetCell(row, 0).Text
|
||||
if ns == model.AllNamespace {
|
||||
ns = ""
|
||||
}
|
||||
v.ctx = context.WithValue(v.ctx, &model.CtxKeyNamespace, ns)
|
||||
v.app.command.run(v.ctx, "app")
|
||||
|
||||
return event
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func TestPageStack(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
|
||||
assert.NoError(t, err)
|
||||
app := NewApp(testClient, cfg)
|
||||
app := NewApp(testClient, cfg, "")
|
||||
assert.Equal(t, len(app.Components), 4)
|
||||
|
||||
stack := NewPageStack(app)
|
||||
|
||||
@@ -47,6 +47,12 @@ var ResourceMap = map[string]ResourceViewer{
|
||||
"k8s": {
|
||||
viewFunc: NewK8SView,
|
||||
},
|
||||
"ns": {
|
||||
viewFunc: NewNamespaceView,
|
||||
},
|
||||
"cns": {
|
||||
viewFunc: NewClusterNamespaceView,
|
||||
},
|
||||
}
|
||||
|
||||
// NewResourceView return a new resource view
|
||||
|
||||
Reference in New Issue
Block a user