github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/printer/describe.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package printer 21 22 import ( 23 "encoding/json" 24 "fmt" 25 "io" 26 "reflect" 27 28 "gopkg.in/yaml.v2" 29 corev1 "k8s.io/api/core/v1" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 32 33 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 34 "github.com/1aal/kubeblocks/pkg/cli/types" 35 "github.com/1aal/kubeblocks/pkg/cli/util" 36 cfgcore "github.com/1aal/kubeblocks/pkg/configuration/core" 37 ) 38 39 const NoneString = "<none>" 40 41 func PrintAllWarningEvents(events *corev1.EventList, out io.Writer) { 42 objs := util.SortEventsByLastTimestamp(events, corev1.EventTypeWarning) 43 title := fmt.Sprintf("\n%s Events: ", corev1.EventTypeWarning) 44 if objs == nil || len(*objs) == 0 { 45 fmt.Fprintln(out, title+NoneString) 46 return 47 } 48 tbl := NewTablePrinter(out) 49 fmt.Fprintln(out, title) 50 tbl.SetHeader("TIME", "TYPE", "REASON", "OBJECT", "MESSAGE") 51 for _, o := range *objs { 52 e := o.(*corev1.Event) 53 tbl.AddRow(util.GetEventTimeStr(e), e.Type, e.Reason, util.GetEventObject(e), e.Message) 54 } 55 tbl.Print() 56 57 } 58 59 // PrintConditions prints the conditions of resource. 60 func PrintConditions(conditions []metav1.Condition, out io.Writer) { 61 // if the conditions are empty, return. 62 if len(conditions) == 0 { 63 return 64 } 65 tbl := NewTablePrinter(out) 66 PrintTitle("Conditions") 67 tbl.SetHeader("LAST-TRANSITION-TIME", "TYPE", "REASON", "STATUS", "MESSAGE") 68 for _, con := range conditions { 69 tbl.AddRow(util.TimeFormat(&con.LastTransitionTime), con.Type, con.Reason, con.Status, con.Message) 70 } 71 tbl.Print() 72 } 73 74 // PrintComponentConfigMeta prints the conditions of resource. 75 func PrintComponentConfigMeta(tplInfos []types.ConfigTemplateInfo, clusterName, componentName string, out io.Writer) { 76 if len(tplInfos) == 0 { 77 return 78 } 79 tbl := NewTablePrinter(out) 80 PrintTitle("ConfigSpecs Meta") 81 enableReconfiguring := func(tpl appsv1alpha1.ComponentConfigSpec, configFileKey string) string { 82 if len(tpl.ConfigConstraintRef) > 0 && cfgcore.IsSupportConfigFileReconfigure(tpl, configFileKey) { 83 return "true" 84 } 85 return "false" 86 } 87 tbl.SetHeader("CONFIG-SPEC-NAME", "FILE", "ENABLED", "TEMPLATE", "CONSTRAINT", "RENDERED", "COMPONENT", "CLUSTER") 88 for _, info := range tplInfos { 89 for configFileKey := range info.CMObj.Data { 90 tbl.AddRow( 91 BoldYellow(info.Name), 92 configFileKey, 93 BoldYellow(enableReconfiguring(info.TPL, configFileKey)), 94 info.TPL.TemplateRef, 95 info.TPL.ConfigConstraintRef, 96 info.CMObj.Name, 97 componentName, 98 clusterName) 99 } 100 } 101 tbl.Print() 102 } 103 104 // PrintHelmValues prints the helm values file of the release in specified format, supports JSON、YAML and Table 105 func PrintHelmValues(configs map[string]interface{}, format Format, out io.Writer) { 106 inTable := func() { 107 p := NewTablePrinter(out) 108 p.SetHeader("KEY", "VALUE") 109 p.SortBy(1) 110 for key, value := range configs { 111 addRows(key, value, p, true) // to table 112 } 113 p.Print() 114 } 115 if format.IsHumanReadable() { 116 inTable() 117 return 118 } 119 120 var data []byte 121 if format == YAML { 122 data, _ = yaml.Marshal(configs) 123 } else { 124 data, _ = json.MarshalIndent(configs, "", " ") 125 data = append(data, '\n') 126 } 127 fmt.Fprint(out, string(data)) 128 } 129 130 // addRows parses the interface value and add it to the Table 131 func addRows(key string, value interface{}, p *TablePrinter, ori bool) { 132 if value == nil { 133 p.AddRow(key, value) 134 return 135 } 136 if reflect.TypeOf(value).Kind() == reflect.Map && ori { 137 if len(value.(map[string]interface{})) == 0 { 138 data, _ := json.Marshal(value) 139 p.AddRow(key, string(data)) 140 } 141 for k, v := range value.(map[string]interface{}) { 142 addRows(key+"."+k, v, p, false) 143 } 144 } else { 145 data, _ := json.Marshal(value) 146 p.AddRow(key, string(data)) 147 } 148 } 149 150 func PrettyPrintObj(obj *unstructured.Unstructured) error { 151 objYAML, err := yaml.Marshal(obj.Object) 152 if err != nil { 153 return err 154 } 155 156 // Parse YAML back into a structured map for pretty printing 157 var parsedObj map[string]interface{} 158 if err := yaml.Unmarshal(objYAML, &parsedObj); err != nil { 159 return err 160 } 161 162 // Marshal again with indentation for pretty printing 163 prettyYAML, err := yaml.Marshal(parsedObj) 164 if err != nil { 165 return err 166 } 167 168 fmt.Println(string(prettyYAML)) 169 return nil 170 }