Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

is there a way to add flags dynamically? #1758

Closed
skeetwu opened this issue Jul 19, 2022 · 4 comments
Closed

is there a way to add flags dynamically? #1758

skeetwu opened this issue Jul 19, 2022 · 4 comments

Comments

@skeetwu
Copy link
Contributor

skeetwu commented Jul 19, 2022

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/")
@marckhouzam
Copy link
Collaborator

Hi @skeetwu. You can handle flags dynamically by setting to true the field DisableFlagParsing. With that, your program will receive all flags as arguments and you can do what you want with them.

I hope this helps.

@skeetwu
Copy link
Contributor Author

skeetwu commented Jul 20, 2022

Hi @marckhouzam , thx for your response.
you can find I have done it in code. I have set DisableFlagParsing as true for command DemoClusterCreate, but it still needs lots of code to implement this feature, I create this ticket just want to know if there is a more convenient way.

@marckhouzam
Copy link
Collaborator

marckhouzam commented Jul 20, 2022

Looks like you disable flag parsing, then once the command gets called, you read the list of flags from an API, you create those flags and then ask Cobra to parse them.

This seems good to me.

@skeetwu
Copy link
Contributor Author

skeetwu commented Jul 20, 2022

ok, great thx for your comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants