This is an automated email from the ASF dual-hosted git repository.
zhongxjian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-kubernetes.git
The following commit(s) were added to refs/heads/master by this push:
new 7016b8da [dubboctl] add profiledump logic (#562)
7016b8da is described below
commit 7016b8dac5d81cfb990aa95cf6ef7c1b6fbc734e
Author: Jian Zhong <[email protected]>
AuthorDate: Sun Jan 26 21:59:44 2025 +0800
[dubboctl] add profiledump logic (#562)
---
operator/cmd/cluster/profile.go | 199 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 198 insertions(+), 1 deletion(-)
diff --git a/operator/cmd/cluster/profile.go b/operator/cmd/cluster/profile.go
index 167a13b7..4e5f55ca 100644
--- a/operator/cmd/cluster/profile.go
+++ b/operator/cmd/cluster/profile.go
@@ -1,20 +1,55 @@
package cluster
import (
+ "encoding/json"
+ "fmt"
"github.com/apache/dubbo-kubernetes/dubboctl/pkg/cli"
"github.com/apache/dubbo-kubernetes/operator/pkg/helm"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/render"
+ "github.com/apache/dubbo-kubernetes/operator/pkg/util/clog"
+ "github.com/ghodss/yaml"
"github.com/spf13/cobra"
"sort"
+ "strings"
+)
+
+const (
+ jsonOutput = "json"
+ yamlOutput = "yaml"
+ flagsOutput = "flags"
+ pathSeparator = "."
+ escapedPathSeparator = "\\" + pathSeparator
+)
+
+const (
+ dubboOperatorTreeString = `
+apiVersion: install.dubbo.io/v1alpha1
+kind: DubboOperator
+`
)
type profileListArgs struct {
manifestsPath string
}
+type profileDumpArgs struct {
+ files []string
+ configPath string
+ outputFormat string
+ manifestsPath string
+}
+
func addProfileListFlags(cmd *cobra.Command, args *profileListArgs) {
cmd.PersistentFlags().StringVarP(&args.manifestsPath, "manifests", "d",
"", "Specify a path to a directory of charts and profiles")
}
+func addProfileDumpFlags(cmd *cobra.Command, args *profileDumpArgs) {
+ cmd.PersistentFlags().StringSliceVarP(&args.files, "files", "f", nil,
"")
+ cmd.PersistentFlags().StringVarP(&args.configPath, "config-path", "p",
"", "")
+ cmd.PersistentFlags().StringVarP(&args.outputFormat, "output", "o",
yamlOutput, "Output format: one of json|yaml|flags")
+ cmd.PersistentFlags().StringVarP(&args.manifestsPath, "manifests", "d",
"", "")
+}
+
func ProfileCmd(ctx cli.Context) *cobra.Command {
rootArgs := &RootArgs{}
plArgs := &profileListArgs{}
@@ -26,8 +61,11 @@ func ProfileCmd(ctx cli.Context) *cobra.Command {
Example: "dubboctl profile list\n" +
"dubboctl install --set profile=demo # Use a profile
from the list",
}
- AddFlags(pc, rootArgs)
+
pc.AddCommand(plc)
+ addProfileListFlags(plc, plArgs)
+
+ AddFlags(pc, rootArgs)
return pc
}
@@ -43,6 +81,24 @@ func profileListCmd(rootArgs *RootArgs, plArgs
*profileListArgs) *cobra.Command
}
}
+func profileDumpCmd(rootArgs *RootArgs, pdArgs *profileDumpArgs)
*cobra.Command {
+ return &cobra.Command{
+ Use: "dump [PROFILE]",
+ Short: "Dumps an Dubbo configuration profile",
+ Long: "The dump subcommand dumps the values in an Dubbo
configuration profile.",
+ Args: func(cmd *cobra.Command, args []string) error {
+ if len(args) > 1 {
+ return fmt.Errorf("too many positional
arguments")
+ }
+ return nil
+ },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ l := clog.NewConsoleLogger(cmd.OutOrStdout(),
cmd.ErrOrStderr(), InstallerScope)
+ return profileDump(args, rootArgs, pdArgs, l)
+ },
+ }
+}
+
func profileList(cmd *cobra.Command, args *RootArgs, plArgs *profileListArgs)
error {
profiles, err := helm.ListProfiles(plArgs.manifestsPath)
if err != nil {
@@ -58,6 +114,147 @@ func profileList(cmd *cobra.Command, args *RootArgs,
plArgs *profileListArgs) er
cmd.Printf(" %s\n", profile)
}
}
+ return nil
+}
+
+func profileDump(args []string, rootArgs *RootArgs, pdArgs *profileDumpArgs, l
clog.Logger) error {
+ if len(args) == 1 && pdArgs.files != nil {
+ return fmt.Errorf("cannot specify both profile name and
filename flag")
+ }
+
+ switch pdArgs.outputFormat {
+ case jsonOutput, yamlOutput, flagsOutput:
+ default:
+ return fmt.Errorf("unknown output format: %v",
pdArgs.outputFormat)
+ }
+
+ setFlags := applyFlagAliases(make([]string, 0), pdArgs.manifestsPath)
+ if len(args) == 1 {
+ setFlags = append(setFlags, "profile="+args[0])
+ }
+
+ y, _, err := render.GenerateManifest(pdArgs.files, setFlags, l, nil)
+ if err != nil {
+ return err
+ }
+
+ y, err = tpath.GetConfigSubtree(y, "spec")
+ if err != nil {
+ return err
+ }
+
+ if pdArgs.configPath == "" {
+ if y, err = prependHeader(y); err != nil {
+ return err
+ }
+ } else {
+ if y, err = tpath.GetConfigSubtree(y, pdArgs.configPath); err
!= nil {
+ return err
+ }
+ }
+
+ switch pdArgs.outputFormat {
+ case jsonOutput:
+ j, err := yamlToPrettyJSON(y)
+ if err != nil {
+ return err
+ }
+ l.Print(j + "\n")
+ case yamlOutput:
+ l.Print(y + "\n")
+ case flagsOutput:
+ f, err := yamlToFlags(y)
+ if err != nil {
+ return err
+ }
+ l.Print(strings.Join(f, "\n") + "\n")
+ }
return nil
}
+
+func yamlToFlags(yml string) ([]string, error) {
+ uglyJSON, err := yaml.YAMLToJSON([]byte(yml))
+ if err != nil {
+ return []string{}, err
+ }
+ var decoded map[string]interface{}
+ if err := json.Unmarshal(uglyJSON, &decoded); err != nil {
+ return []string{}, err
+ }
+ spec, ok := decoded["spec"]
+ if !ok {
+ spec = decoded
+ }
+ setflags, err := walk("", "", spec)
+ if err != nil {
+ return []string{}, err
+ }
+ sort.Strings(setflags)
+ return setflags, nil
+}
+
+func walk(path, separator string, obj interface{}) ([]string, error) {
+ switch v := obj.(type) {
+ case map[string]interface{}:
+ accum := make([]string, 0)
+ for key, vv := range v {
+ childwalk, err := walk(fmt.Sprintf("%s%s%s", path,
separator, pathComponent(key)), ".", vv)
+ if err != nil {
+ return accum, err
+ }
+ accum = append(accum, childwalk...)
+ }
+ return accum, nil
+ case []interface{}:
+ accum := make([]string, 0)
+ for idx, vv := range v {
+ indexwalk, err := walk(fmt.Sprintf("%s[%d]", path,
idx), ".", vv)
+ if err != nil {
+ return accum, err
+ }
+ accum = append(accum, indexwalk...)
+ }
+ return accum, nil
+ case string:
+ return []string{fmt.Sprintf("%s=%q", path, v)}, nil
+ default:
+ return []string{fmt.Sprintf("%s=%v", path, v)}, nil
+ }
+}
+
+func prependHeader(yml string) (string, error) {
+ out, err := tpath.AddSpecRoot(yml)
+ if err != nil {
+ return "", err
+ }
+ out2, err := util.OverlayYAML(dubboOperatorTreeString, out)
+ if err != nil {
+ return "", err
+ }
+ return out2, nil
+}
+
+// Convert the generated YAML to pretty JSON.
+func yamlToPrettyJSON(yml string) (string, error) {
+ uglyJSON, err := yaml.YAMLToJSON([]byte(yml))
+ if err != nil {
+ return "", err
+ }
+ var decoded map[string]interface{}
+ if err := json.Unmarshal(uglyJSON, &decoded); err != nil {
+ return "", err
+ }
+ prettyJSON, err := json.MarshalIndent(decoded, "", " ")
+ if err != nil {
+ return "", err
+ }
+ return string(prettyJSON), nil
+}
+
+func pathComponent(component string) string {
+ if !strings.Contains(component, pathSeparator) {
+ return component
+ }
+ return strings.ReplaceAll(component, pathSeparator,
escapedPathSeparator)
+}