Fix: enhance vela status tree print (#3639)

Signed-off-by: Somefive <yd219913@alibaba-inc.com>
This commit is contained in:
Somefive
2022-04-13 17:46:55 +08:00
committed by GitHub
parent 7a0d2b552b
commit c33eaa0609
4 changed files with 126 additions and 11 deletions
+1 -1
View File
@@ -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
+68 -7
View File
@@ -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)
+48
View File
@@ -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))
}
+9 -3
View File
@@ -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
}