mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-18 03:56:36 +00:00
Fix: enhance vela status tree print (#3639)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
This commit is contained in:
@@ -65,6 +65,7 @@ require (
|
||||
go.uber.org/zap v1.18.1
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/tools v0.1.6 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
@@ -259,7 +260,6 @@ require (
|
||||
golang.org/x/mod v0.4.2 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -45,11 +46,15 @@ import (
|
||||
)
|
||||
|
||||
// ResourceDetailRetriever retriever to get details for resource
|
||||
type ResourceDetailRetriever func(*resourceRow) error
|
||||
type ResourceDetailRetriever func(*resourceRow, string) error
|
||||
|
||||
// ResourceTreePrintOptions print options for resource tree
|
||||
type ResourceTreePrintOptions struct {
|
||||
DetailRetriever ResourceDetailRetriever
|
||||
// MaxWidth if set, the detail part will auto wrap
|
||||
MaxWidth *int
|
||||
// Format for details
|
||||
Format string
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -171,9 +176,64 @@ func (options *ResourceTreePrintOptions) fillResourceRows(rows []*resourceRow, c
|
||||
colsWidth[1] += 4
|
||||
}
|
||||
|
||||
func (options *ResourceTreePrintOptions) writeResourceTree(writer io.Writer, rows []*resourceRow, colsWidth []int) {
|
||||
applyTimeWidth := 20
|
||||
const (
|
||||
applyTimeWidth = 20
|
||||
detailMinWidth = 20
|
||||
)
|
||||
|
||||
func (options *ResourceTreePrintOptions) _getWidthForDetails(colsWidth []int) int {
|
||||
detailWidth := 0
|
||||
if options.MaxWidth == nil {
|
||||
return math.MaxInt
|
||||
}
|
||||
detailWidth = *options.MaxWidth - applyTimeWidth
|
||||
for _, width := range colsWidth {
|
||||
detailWidth -= width
|
||||
}
|
||||
// if the space for details exceeds the max allowed width, give up wrapping lines
|
||||
if detailWidth < detailMinWidth {
|
||||
detailWidth = math.MaxInt
|
||||
}
|
||||
return detailWidth
|
||||
}
|
||||
|
||||
func (options *ResourceTreePrintOptions) _wrapDetails(detail string, width int) (lines []string) {
|
||||
for _, row := range strings.Split(detail, "\n") {
|
||||
var sb strings.Builder
|
||||
row = strings.ReplaceAll(row, "\t", " ")
|
||||
for _, token := range strings.Split(row, " ") {
|
||||
if sb.Len()+len(token)+2 <= width {
|
||||
if sb.Len() > 0 {
|
||||
sb.WriteString(" ")
|
||||
}
|
||||
sb.WriteString(token)
|
||||
} else {
|
||||
if sb.Len() > 0 {
|
||||
lines = append(lines, sb.String())
|
||||
sb.Reset()
|
||||
}
|
||||
offset := 0
|
||||
for {
|
||||
if offset+width > len(token) {
|
||||
break
|
||||
}
|
||||
lines = append(lines, token[offset:offset+width])
|
||||
offset += width
|
||||
}
|
||||
sb.WriteString(token[offset:])
|
||||
}
|
||||
}
|
||||
if sb.Len() > 0 {
|
||||
lines = append(lines, sb.String())
|
||||
}
|
||||
}
|
||||
if len(lines) == 0 {
|
||||
lines = []string{""}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
func (options *ResourceTreePrintOptions) writeResourceTree(writer io.Writer, rows []*resourceRow, colsWidth []int) {
|
||||
writePaddedString := func(sb *strings.Builder, head string, tail string, width int) {
|
||||
sb.WriteString(head)
|
||||
for c := strutil.StringWidth(head) + strutil.StringWidth(tail); c < width; c++ {
|
||||
@@ -195,14 +255,15 @@ func (options *ResourceTreePrintOptions) writeResourceTree(writer io.Writer, row
|
||||
|
||||
connectorColorizer := color.WhiteString
|
||||
outdatedColorizer := color.WhiteString
|
||||
detailWidth := options._getWidthForDetails(colsWidth)
|
||||
|
||||
for _, row := range rows {
|
||||
if options.DetailRetriever != nil && row.status != resourceRowStatusNotDeployed {
|
||||
if err := options.DetailRetriever(row); err != nil {
|
||||
if err := options.DetailRetriever(row, options.Format); err != nil {
|
||||
row.details = "Error: " + err.Error()
|
||||
}
|
||||
}
|
||||
for lineIdx, line := range strings.Split(row.details, "\n") {
|
||||
for lineIdx, line := range options._wrapDetails(row.details, detailWidth) {
|
||||
var sb strings.Builder
|
||||
rscName, rscStatus, applyTime := row.resourceName, row.status, row.applyTime
|
||||
if row.status != resourceRowStatusUpdated {
|
||||
@@ -275,7 +336,7 @@ func (rt tableRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// RetrieveKubeCtlGetMessageGenerator get details like kubectl get
|
||||
func RetrieveKubeCtlGetMessageGenerator(cfg *rest.Config, format string) (ResourceDetailRetriever, error) {
|
||||
func RetrieveKubeCtlGetMessageGenerator(cfg *rest.Config) (ResourceDetailRetriever, error) {
|
||||
cfg.Wrap(func(rt http.RoundTripper) http.RoundTripper {
|
||||
return tableRoundTripper{rt: rt}
|
||||
})
|
||||
@@ -283,7 +344,7 @@ func RetrieveKubeCtlGetMessageGenerator(cfg *rest.Config, format string) (Resour
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(row *resourceRow) error {
|
||||
return func(row *resourceRow, format string) error {
|
||||
mr := row.mr
|
||||
un := &unstructured.Unstructured{}
|
||||
un.SetAPIVersion(mr.APIVersion)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2021 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 resourcetracker
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
func TestResourceTreePrintOption_getWidthForDetails(t *testing.T) {
|
||||
r := require.New(t)
|
||||
options := &ResourceTreePrintOptions{}
|
||||
r.Equal(math.MaxInt, options._getWidthForDetails(nil))
|
||||
options.MaxWidth = pointer.Int(50 + applyTimeWidth)
|
||||
r.Equal(30, options._getWidthForDetails([]int{10, 10}))
|
||||
r.Equal(math.MaxInt, options._getWidthForDetails([]int{20, 20}))
|
||||
}
|
||||
|
||||
func TestResourceTreePrintOptions_wrapDetails(t *testing.T) {
|
||||
r := require.New(t)
|
||||
options := &ResourceTreePrintOptions{}
|
||||
detail := "test-key: test-val\ttest-data: test-val\ntest-next-line: text-next-value test-long-key: test long long long long value test-append: test-append-val"
|
||||
r.Equal(
|
||||
[]string{
|
||||
"test-key: test-val test-data: test-val",
|
||||
"test-next-line: text-next-value",
|
||||
"test-long-key: test long long long long ",
|
||||
"value test-append: test-append-val",
|
||||
},
|
||||
options._wrapDetails(detail, 40))
|
||||
}
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
commontypes "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
@@ -396,11 +398,15 @@ func printApplicationTree(c common.Args, cmd *cobra.Command, appName string, app
|
||||
if err == nil {
|
||||
placements, _ = policy.GetPlacementsFromTopologyPolicies(context.Background(), cli, app, af.Policies, true)
|
||||
}
|
||||
options := resourcetracker.ResourceTreePrintOptions{}
|
||||
printDetails, _ := cmd.Flags().GetBool("detail")
|
||||
format, _ := cmd.Flags().GetString("detail-format")
|
||||
var maxWidth *int
|
||||
if w, _, err := term.GetSize(0); err == nil && w > 0 {
|
||||
maxWidth = pointer.Int(w)
|
||||
}
|
||||
options := resourcetracker.ResourceTreePrintOptions{MaxWidth: maxWidth, Format: format}
|
||||
printDetails, _ := cmd.Flags().GetBool("detail")
|
||||
if printDetails {
|
||||
msgRetriever, err := resourcetracker.RetrieveKubeCtlGetMessageGenerator(config, format)
|
||||
msgRetriever, err := resourcetracker.RetrieveKubeCtlGetMessageGenerator(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user