func loadConfig(nm string, flagSet *flag.FlagSet, isOverride bool) error { f, e := os.Open(nm) if nil != e { return fmt.Errorf("load config '%s' failed, %v", nm, e) } var res map[string]interface{} e = json.NewDecoder(f).Decode(&res) if nil != e { return fmt.Errorf("load config '%s' failed, %v", nm, e) } actual := map[string]string{} fn := func(f *flag.Flag) { actual[f.Name] = f.Name } if nil == flagSet { flag.Visit(fn) } else { flagSet.Visit(fn) } e = assignFlagSet("", res, flagSet, actual, isOverride) if nil != e { return fmt.Errorf("load config '%s' failed, %v", nm, e) } return nil }
// SetFlagsFromYaml goes through all registered flags in the given flagset, // and if they are not already set it attempts to set their values from // the YAML config. It will use the key REPLACE(UPPERCASE(flagname), '-', '_') func SetFlagsFromYaml(fs *flag.FlagSet, rawYaml []byte) (err error) { conf := make(map[string]string) if err = yaml.Unmarshal(rawYaml, conf); err != nil { return } alreadySet := map[string]struct{}{} fs.Visit(func(f *flag.Flag) { alreadySet[f.Name] = struct{}{} }) errs := make([]error, 0) fs.VisitAll(func(f *flag.Flag) { if f.Name == "" { return } if _, ok := alreadySet[f.Name]; ok { return } tag := strings.Replace(strings.ToUpper(f.Name), "-", "_", -1) val, ok := conf[tag] if !ok { return } if serr := fs.Set(f.Name, val); serr != nil { errs = append(errs, fmt.Errorf("invalid value %q for %s: %v", val, tag, serr)) } }) if len(errs) != 0 { err = ErrorSlice(errs) } return }
func normalizeFlags(flags []Flag, set *flag.FlagSet) error { visited := make(map[string]bool) set.Visit(func(f *flag.Flag) { visited[f.Name] = true }) for _, f := range flags { parts := strings.Split(f.getName(), ",") if len(parts) == 1 { continue } var ff *flag.Flag for _, name := range parts { name = strings.Trim(name, " ") if visited[name] { if ff != nil { return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) } ff = set.Lookup(name) } } if ff == nil { continue } for _, name := range parts { name = strings.Trim(name, " ") if !visited[name] { copyFlag(name, ff, set) } } } return nil }
// SetFlagsFromEnv parses all registered flags in the given flagset, // and if they are not already set it attempts to set their values from // environment variables. Environment variables take the name of the flag but // are UPPERCASE, have the prefix "PREFIX_", and any dashes are replaced by // underscores - for example: some-flag => PREFIX_SOME_FLAG func SetFlagsFromEnv(fs *flag.FlagSet, prefix string) error { var err error alreadySet := make(map[string]bool) fs.Visit(func(f *flag.Flag) { alreadySet[f.Name] = true }) if prefix == "" { prefix = "_" } else { prefix = strings.ToUpper(strings.Replace(prefix, "-", "_", -1)) } fs.VisitAll(func(f *flag.Flag) { if !alreadySet[f.Name] { key := prefix + strings.ToUpper(strings.Replace(f.Name, "-", "_", -1)) val := os.Getenv(key) if val != "" { if serr := fs.Set(f.Name, val); serr != nil { err = fmt.Errorf("invalid value %q for %s: %v", val, key, serr) } } } }) return err }
// ParseSet parses the given flagset. The specified prefix will be applied to // the environment variable names. func ParseSet(prefix string, set *flag.FlagSet) error { var explicit []*flag.Flag var all []*flag.Flag set.Visit(func(f *flag.Flag) { explicit = append(explicit, f) }) var err error set.VisitAll(func(f *flag.Flag) { if err != nil { return } all = append(all, f) if !contains(explicit, f) { name := strings.Replace(f.Name, ".", "_", -1) name = strings.Replace(name, "-", "_", -1) if prefix != "" { name = prefix + name } name = strings.ToUpper(name) val := os.Getenv(name) if val != "" { if ferr := f.Value.Set(val); ferr != nil { err = fmt.Errorf("failed to set flag %q with value %q", f.Name, val) } } } }) return err }
func IsSet(fs *flag.FlagSet, name string) bool { set := false fs.Visit(func(f *flag.Flag) { if f.Name == name { set = true } }) return set }
// IsFlagSet returns true if the specified flag has been set on // the command line. func IsFlagSet(fs *flag.FlagSet, name string) bool { found := false fs.Visit(func(f *flag.Flag) { if f.Name == name { found = true } }) return found }
// FlagVisited determines whether the named flag has been visited in the FlagSet // given. This is helpful if you want to have a flag that triggers an action // when given, but is not a boolean flag. func FlagVisited(name string, flags flag.FlagSet) (didFind bool) { toFind := flags.Lookup(name) flags.Visit(func(f *flag.Flag) { if f == toFind { didFind = true } }) return }
func loadActualFlags(flagSet *flag.FlagSet) map[string]string { actual := map[string]string{} fn := func(f *flag.Flag) { actual[f.Name] = f.Name } if nil == flagSet { flag.Visit(fn) } else { flagSet.Visit(fn) } return actual }
// EnabledActionsFromFlags returns value as an array of string // that is assigned to a command line flag name `enabledActionsFlagName`. func EnabledActionsFromFlags(fs *flag.FlagSet, enabledActionsFlagName string) ([]string, error) { visited := make(map[string]struct{}) fs.Visit(func(f *flag.Flag) { visited[f.Name] = struct{}{} }) _, enabledActionsFlagIsSet := visited[enabledActionsFlagName] if enabledActionsFlagIsSet { enabledActions := *fs.Lookup(enabledActionsFlagName).Value.(*ActionNames) return enabledActions, nil } return []string{}, nil }
// flagsFromEnv parses all registered flags in the given flagset, // and if they are not already set it attempts to set their values from // environment variables. Environment variables take the name of the flag but // are UPPERCASE, have the given prefix, and any dashes are replaced by // underscores - for example: some-flag => PREFIX_SOME_FLAG func flagsFromEnv(prefix string, fs *flag.FlagSet) { alreadySet := make(map[string]bool) fs.Visit(func(f *flag.Flag) { alreadySet[f.Name] = true }) fs.VisitAll(func(f *flag.Flag) { if !alreadySet[f.Name] { key := strings.ToUpper(prefix + "_" + strings.Replace(f.Name, "-", "_", -1)) val := os.Getenv(key) if val != "" { fs.Set(f.Name, val) } } }) }
// SetFlagsFromEnv parses all registered flags in the given flagset, // and if they are not already set it attempts to set their values from // environment variables. Environment variables take the name of the flag but // are UPPERCASE, have the given prefix and any dashes are replaced by // underscores - for example: some-flag => ETCD_SOME_FLAG func SetFlagsFromEnv(prefix string, fs *flag.FlagSet) error { var err error alreadySet := make(map[string]bool) fs.Visit(func(f *flag.Flag) { alreadySet[flagToEnv(prefix, f.Name)] = true }) usedEnvKey := make(map[string]bool) fs.VisitAll(func(f *flag.Flag) { err = setFlagFromEnv(fs, prefix, f.Name, usedEnvKey, alreadySet, true) }) verifyEnv(prefix, usedEnvKey, alreadySet) return err }
func mustNotHave(fset *flag.FlagSet, keys ...string) error { for _, key := range keys { found := false fset.Visit( func(f *flag.Flag) { if f.Name == key { found = true } }) if found { return fmt.Errorf("Invalid flags. Flag '%s' cannot appear for this operation", key) } } return nil }
func mustHave(fset *flag.FlagSet, keys ...string) error { for _, key := range keys { found := false fset.Visit( func(f *flag.Flag) { if f.Name == key { found = true } }) if !found { flag := fset.Lookup(key) if flag == nil || flag.DefValue == "" { return fmt.Errorf("Invalid flags. Flag '%s' is required for this operation", key) } } } return nil }
// from github.com/coreos/etcd // // SetFlagsFromEnv parses all registered flags in the given flagset, // and if they are not already set it attempts to set their values from // environment variables. Environment variables take the name of the flag but // are UPPERCASE, have the prefix `s`, and any dashes are replaced by // underscores - for example: some-flag => ETCD_SOME_FLAG func AddEnvironmentToFlags(s string, fs *flag.FlagSet) error { var err error alreadySet := make(map[string]bool) fs.Visit(func(f *flag.Flag) { alreadySet[f.Name] = true }) fs.VisitAll(func(f *flag.Flag) { if !alreadySet[f.Name] { key := s + "_" + ToEnvironmentKey(f.Name) val := os.Getenv(key) if val != "" { if serr := fs.Set(f.Name, val); serr != nil { err = fmt.Errorf("invalid value %q for %s: %v", val, key, serr) } } } }) return err }
// MustFlag sets flags that are skipped by dst.Parse when p contains // the respective key for flag.Flag.Name. // // It's use is recommended with command line arguments as in: // flag.Parse() // p.MustFlag(flag.CommandLine) func (p *Properties) MustFlag(dst *flag.FlagSet) { m := make(map[string]*flag.Flag) dst.VisitAll(func(f *flag.Flag) { m[f.Name] = f }) dst.Visit(func(f *flag.Flag) { delete(m, f.Name) // overridden }) for name, f := range m { v, ok := p.Get(name) if !ok { continue } if err := f.Value.Set(v); err != nil { ErrorHandler(err) } } }
// SetFlagsFromEnv parses all registered flags in the given flagset, // and if they are not already set it attempts to set their values from // environment variables. Environment variables take the name of the flag but // are UPPERCASE, have the prefix "ETCD_", and any dashes are replaced by // underscores - for example: some-flag => ETCD_SOME_FLAG func SetFlagsFromEnv(fs *flag.FlagSet) error { var err error alreadySet := make(map[string]bool) fs.Visit(func(f *flag.Flag) { alreadySet[flagToEnv(f.Name)] = true }) usedEnvKey := make(map[string]bool) fs.VisitAll(func(f *flag.Flag) { key := flagToEnv(f.Name) if !alreadySet[key] { val := os.Getenv(key) if val != "" { usedEnvKey[key] = true if serr := fs.Set(f.Name, val); serr != nil { err = fmt.Errorf("invalid value %q for %s: %v", val, key, serr) } plog.Infof("recognized and used environment variable %s=%s", key, val) } } }) for _, env := range os.Environ() { kv := strings.SplitN(env, "=", 2) if len(kv) != 2 { plog.Warningf("found invalid env %s", env) } if usedEnvKey[kv[0]] { continue } if alreadySet[kv[0]] { plog.Infof("recognized environment variable %s, but unused: shadowed by corresponding flag ", kv[0]) continue } if strings.HasPrefix(env, "ETCD_") { plog.Warningf("unrecognized environment variable %s", env) } } return err }
// Populate sets the flags in set from the environment. The // environment value used will be the name of the flag in upper case, // with '-' changed to '_', and (if prefixis not the empty string) // prepended with prefix and an underscore. So, if prefix is // "DOORMAN", and the flag's name "foo-bar", the environment variable // DOORMAN_FOO_BAR will be used. func Populate(set *flag.FlagSet, prefix string) error { var ( setThroughFlags = make(map[string]bool) knownEnv = make(map[string]bool) err error ) set.Visit(func(f *flag.Flag) { setThroughFlags[f.Name] = true }) set.VisitAll(func(f *flag.Flag) { key := flagToEnv(prefix, f.Name) knownEnv[key] = true val := os.Getenv(key) if val == "" { return } if setThroughFlags[f.Name] { log.Warningf("Recognized environment variable %v, but shadowed by flag %v: won't be used.", key, f.Name) return } if e := set.Set(f.Name, val); e != nil { err = fmt.Errorf("Invalid value %q for %v.", val, key) return } }) for _, env := range os.Environ() { kv := strings.SplitN(env, "=", 2) if len(kv) < 1 { continue } if name := kv[0]; strings.HasPrefix(name, prefix) && !knownEnv[name] { log.Warningf("Unrecognized environment variable %s", name) } } return err }
// SetFlagsFromEnvFile iterates the given flagset and if any flags are not // already set it attempts to set their values from the given env file. Env // files may have KEY=VALUE lines where the environment variable names are // in UPPERCASE, prefixed by the given PREFIX, and dashes are replaced by // underscores. For example, if prefix=PREFIX, some-flag is named // PREFIX_SOME_FLAG. // Comment lines are skipped, but more complex env file parsing is not // performed. func SetFlagsFromEnvFile(fs *flag.FlagSet, prefix string, path string) (err error) { alreadySet := make(map[string]bool) fs.Visit(func(f *flag.Flag) { alreadySet[f.Name] = true }) envs, err := parseEnvFile(path) if err != nil { return err } fs.VisitAll(func(f *flag.Flag) { if !alreadySet[f.Name] { key := prefix + "_" + strings.ToUpper(strings.Replace(f.Name, "-", "_", -1)) val := envs[key] if val != "" { if serr := fs.Set(f.Name, val); serr != nil { err = fmt.Errorf("invalid value %q for %s: %v", val, key, serr) } } } }) return err }
// ParseFile parses the specified configuration file and populates the flags // in the flag.FlagSet based on the contents of the file. func ParseFile(flags *flag.FlagSet, filename string) { if filename == "" { return } var ( explicit []*flag.Flag all []*flag.Flag ) config := readConfig(filename) flags.Visit(func(f *flag.Flag) { explicit = append(explicit, f) }) flags.VisitAll(func(f *flag.Flag) { all = append(all, f) if !contains(explicit, f) { val := config[f.Name] if val != "" { err := f.Value.Set(val) if err != nil { log.Fatalf("Failed to set flag %s with value %s", f.Name, val) } } } }) Outer: for name, val := range config { for _, f := range all { if f.Name == name { continue Outer } } log.Fatalf("Unknown flag %s=%s in config file.", name, val) } }
// URLsFromFlags decides what URLs should be using two different flags // as datasources. The first flag's Value must be of type URLs, while // the second must be of type IPAddressPort. If both of these flags // are set, an error will be returned. If only the first flag is set, // the underlying url.URL objects will be returned unmodified. If the // second flag happens to be set, the underlying IPAddressPort will be // converted to a url.URL and returned. The Scheme of the returned // url.URL will be http unless the provided TLSInfo object is non-empty. // If neither of the flags have been explicitly set, the default value // of the first flag will be returned unmodified. func URLsFromFlags(fs *flag.FlagSet, urlsFlagName string, addrFlagName string, tlsInfo transport.TLSInfo) ([]url.URL, error) { visited := make(map[string]struct{}) fs.Visit(func(f *flag.Flag) { visited[f.Name] = struct{}{} }) _, urlsFlagIsSet := visited[urlsFlagName] _, addrFlagIsSet := visited[addrFlagName] if addrFlagIsSet { if urlsFlagIsSet { return nil, fmt.Errorf("Set only one of flags -%s and -%s", urlsFlagName, addrFlagName) } addr := *fs.Lookup(addrFlagName).Value.(*IPAddressPort) addrURL := url.URL{Scheme: "http", Host: addr.String()} if !tlsInfo.Empty() { addrURL.Scheme = "https" } return []url.URL{addrURL}, nil } return []url.URL(*fs.Lookup(urlsFlagName).Value.(*URLsValue)), nil }
func (flags Flags) FromFlagSet(fs *flag.FlagSet) Flags { fs.Visit(func(f *flag.Flag) { flags[f.Name] = f.Value.String() }) return flags }
// resolveConfig parses command line arguments and loads config file to // return config.Config information. func resolveConfig(fs *flag.FlagSet, argv []string) (*config.Config, error) { conf := &config.Config{} var ( conffile = fs.String("conf", config.DefaultConfig.Conffile, "Config file path (Configs in this file are over-written by command line options)") apibase = fs.String("apibase", config.DefaultConfig.Apibase, "API base") pidfile = fs.String("pidfile", config.DefaultConfig.Pidfile, "File containing PID") root = fs.String("root", config.DefaultConfig.Root, "Directory containing variable state information") apikey = fs.String("apikey", "", "(DEPRECATED) API key from mackerel.io web site") diagnostic = fs.Bool("diagnostic", false, "Enables diagnostic features") verbose bool roleFullnames roleFullnamesFlag ) fs.BoolVar(&verbose, "verbose", config.DefaultConfig.Verbose, "Toggle verbosity") fs.BoolVar(&verbose, "v", config.DefaultConfig.Verbose, "Toggle verbosity (shorthand)") // The value of "role" option is internally "roll fullname", // but we call it "role" here for ease. fs.Var(&roleFullnames, "role", "Set this host's roles (format: <service>:<role>)") fs.Parse(argv) conf, confErr := config.LoadConfig(*conffile) conf.Conffile = *conffile if confErr != nil { return nil, fmt.Errorf("Failed to load the config file: %s", confErr) } // overwrite config from file by config from args fs.Visit(func(f *flag.Flag) { switch f.Name { case "apibase": conf.Apibase = *apibase case "apikey": conf.Apikey = *apikey case "pidfile": conf.Pidfile = *pidfile case "root": conf.Root = *root case "diagnostic": conf.Diagnostic = *diagnostic case "verbose", "v": conf.Verbose = verbose case "role": conf.Roles = roleFullnames } }) r := []string{} for _, roleFullName := range conf.Roles { if !roleFullnamePattern.MatchString(roleFullName) { logger.Errorf("Bad format for role fullname (expecting <service>:<role>. Alphabet, numbers, hyphens and underscores are acceptable, but the first character must not be a hyphen or an underscore.): '%s'", roleFullName) } else { r = append(r, roleFullName) } } conf.Roles = r if conf.Verbose && conf.Silent { logger.Warningf("both of `verbose` and `silent` option are specified. In this case, `verbose` get preference over `silent`") } if conf.Apikey == "" { return nil, fmt.Errorf("Apikey must be specified in the config file (or by the DEPRECATED command-line flag)") } return conf, nil }