Skip to content

is there a way to add flags dynamically? #1758

@jinlohwu

Description

@jinlohwu

we build a binary with Cobra, the primary function is to collect input and send all arguments to a restful server, this server will call other functions to create a K8S cluster.
when we do it, there are a lot of arguments, and we need to enhance and change them more regularly, so, if there is a way to add flags dynamically, it will be beneficial for us.

I have written some code to implement this feature, but I am not familiar with Go lang and Corba, I'm not sure we are on the right way, if there is a more good way, please help me figure it out.

API parts, which return dynamic data.

@ns.route('/custom-api')
class CustomAPI(Resource):
    def get(self):
        flags_list = {
            "cert": {
                "type": "string",
                "des": "add self-signed certs for private registries. Format: registry-name1:/path/to/cert-file1,registry-name2:/path/to/cert-file2,...",
                "default": ""
            },
            "csf-grocery": {
                "type": "bool",
                "des": "set up CSF Grocery",
                "default": True
            }
        }
        return json_success(flags_list)

parse API result to flag

package utils

import (
	"fmt"
	"os"

	resty "github.com/go-resty/resty/v2"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/tidwall/gjson"
)

// return data map 
var customizedMap = make(map[string]interface{})

func ParseCutomizedFlags(url string, cmd *cobra.Command, args []string) map[string]interface{} {
        if len(args) == 0 {
		cmd.Help()
		fmt.Println()
		os.Exit(0)
	}


	fullURL := viper.GetString("datacenter-server-api") + url
	var customizedData gjson.Result
	client := resty.New()
	resp, err := client.R().ForceContentType("application/json").EnableTrace().Get(fullURL)
	if err == nil {
		if gjson.Get(resp.String(), "status").String() == "OK" {
			customizedData = gjson.Get(resp.String(), "data")
		}
	} else {
		fmt.Println(err.Error())
	}


	if len(customizedData.Map()) > 0 {
		for key_name, valueMap := range customizedData.Map() {
			value_type := valueMap.Get("type").String()
			if value_type == "string" {
				cmd.Flags().String(string(key_name), valueMap.Map()["default"].String(), valueMap.Map()["des"].String())
			} else if value_type == "bool" {
				cmd.Flags().Bool(string(key_name), valueMap.Map()["default"].Bool(), valueMap.Map()["des"].String())
			}
		}
	}

        // set flag back
	cmd.DisableFlagParsing = false 
       // parse flag manually
	err = cmd.ParseFlags(args)
       // if get error, print error and help info
	if err != nil {
		fmt.Println(err)
		cmd.Help()
		fmt.Println()
		os.Exit(0)
	}

	if cmd.Flag("help").Value.String() == "true" {
		cmd.Help()
		fmt.Println()
		os.Exit(0)
	}

        // put all data into map which have no changed.
	if len(customizedData.Array()) > 0 {
		for key_name := range customizedData.Map() {
			if cmd.Flag(key_name).Value.String() != cmd.Flag(key_name).DefValue {
				customizedMap[key_name] = cmd.Flag(key_name).Value
			}
		}
	}

	return customizedMap
}

commands parts

import (
	"fmt"

	"github.com/go-resty/resty/v2"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"nuke.csf.nokia.net/nuke-cli/utils"
)

var DemoClusterCreate = &cobra.Command{
	Use:                "demo [string to print]",
	Short:              "demo anything to the screen",
	Long:               `demo is for printing anything back to the screen. For many years people have printed back to the screen.`,
	RunE:               createDemoCluster,
	DisableFlagParsing: true,
}

func createDemoCluster(cmd *cobra.Command, args []string) error {
        // call function to get all flags data
	var customizedMap = utils.ParseCutomizedFlags("/api/v1/demo/custom-api", cmd, args)

	for va := range customizedMap {
		fmt.Println(va)
		fmt.Println(customizedMap[va])
	}


	return nil
}

CLI result

PS D:\CLI>  go run nuke.go create cluster demo --cert xxxx --csf-grocery=false
cert
xxxx
csf-grocery
false
PS D:\CLI>  go run nuke.go create cluster demo --cert xxxx --csf-grocery=false --zz=0
unknown flag: --zz

Usage:
  nuke create cluster demo [string to print] [flags]

Flags:
      --cert string   add self-signed certs for private registries. Format: registry-name1:/path/to/cert-file1,registry-name2:/path/to/cert-file2,...
      --csf-grocery   set up CSF Grocery (default true)
  -h, --help          help for demo

Global Flags:
      --datacenter-server-api string   data center server api (default "http://127.0.0.1:5678")
      --log string                     nuke log level [debug, info] (default "info")
  -o, --output string                  result output format. [json, yaml, file] (default "json")
      --output-folder string           when output type if file, all result files will be save to this folder. (default "./output/")
      --wrap-processbar                if need to wrap process bar when running a long task (default true)

PS D:\CLI>    go run nuke.go create cluster demo --help
demo is for printing anything back to the screen. For many years people have printed back to the screen.

Usage:
  nuke create cluster demo [string to print] [flags]

Flags:
      --cert string   add self-signed certs for private registries. Format: registry-name1:/path/to/cert-file1,registry-name2:/path/to/cert-file2,...
      --csf-grocery   set up CSF Grocery (default true)
  -h, --help          help for demo

Global Flags:
      --datacenter-server-api string   data center server api (default "http://127.0.0.1:5678")
      --log string                     nuke log level [debug, info] (default "info")
  -o, --output string                  result output format. [json, yaml, file] (default "json")
      --output-folder string           when output type if file, all result files will be save to this folder. (default "./output/")

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions