github.com/cactusblossom/fabric-ca@v0.0.0-20200611062428-0082fc643826/util/flag.go (about) 1 /* 2 Copyright IBM Corp. 2017 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package util 18 19 import ( 20 "fmt" 21 "reflect" 22 "strconv" 23 "strings" 24 "time" 25 26 "github.com/cloudflare/cfssl/log" 27 "github.com/mitchellh/mapstructure" 28 logging "github.com/op/go-logging" 29 "github.com/pkg/errors" 30 "github.com/spf13/cast" 31 "github.com/spf13/pflag" 32 "github.com/spf13/viper" 33 ) 34 35 const ( 36 // TagDefault is the tag name for a default value of a field as recognized 37 // by RegisterFlags. 38 TagDefault = "def" 39 // TagHelp is the tag name for a help message of a field as recognized 40 // by RegisterFlags. 41 TagHelp = "help" 42 // TagOpt is the tag name for a one character option of a field as recognized 43 // by RegisterFlags. For example, a value of "d" reserves "-d" for the 44 // command line argument. 45 TagOpt = "opt" 46 // TagSkip is the tag name which causes the field to be skipped by 47 // RegisterFlags. 48 TagSkip = "skip" 49 // TagHide is the tag name which causes the field to be hidden 50 TagHide = "hide" 51 ) 52 53 // RegisterFlags registers flags for all fields in an arbitrary 'config' object. 54 // This method recognizes the following field tags: 55 // "def" - the default value of the field; 56 // "opt" - the optional one character short name to use on the command line; 57 // "help" - the help message to display on the command line; 58 // "skip" - to skip the field. 59 func RegisterFlags(v *viper.Viper, flags *pflag.FlagSet, config interface{}, 60 tags map[string]string) error { 61 fr := &flagRegistrar{flags: flags, tags: tags, viper: v} 62 return ParseObj(config, fr.Register, tags) 63 } 64 65 type flagRegistrar struct { 66 flags *pflag.FlagSet 67 tags map[string]string 68 viper *viper.Viper 69 } 70 71 func (fr *flagRegistrar) Register(f *Field) (err error) { 72 // Don't register non-leaf fields 73 if !f.Leaf { 74 return nil 75 } 76 // Don't register fields with no address 77 if f.Addr == nil { 78 return errors.Errorf("Field is not addressable: %s", f.Path) 79 } 80 skip := fr.getTag(f, TagSkip) 81 if skip != "" { 82 return nil 83 } 84 85 help := fr.getTag(f, TagHelp) 86 opt := fr.getTag(f, TagOpt) 87 def := fr.getTag(f, TagDefault) 88 hide := fr.getHideBooleanTag(f) 89 switch f.Kind { 90 91 case reflect.String: 92 if help == "" && !hide { 93 return errors.Errorf("Field is missing a help tag: %s", f.Path) 94 } 95 fr.flags.StringVarP(f.Addr.(*string), f.Path, opt, def, help) 96 case reflect.Int: 97 if help == "" && !hide { 98 return errors.Errorf("Field is missing a help tag: %s", f.Path) 99 } 100 var intDef int 101 if def != "" { 102 intDef, err = strconv.Atoi(def) 103 if err != nil { 104 return errors.Errorf("Invalid integer value in 'def' tag of %s field", f.Path) 105 } 106 } 107 fr.flags.IntVarP(f.Addr.(*int), f.Path, opt, intDef, help) 108 case reflect.Int64: 109 if help == "" && !hide { 110 return errors.Errorf("Field is missing a help tag: %s", f.Path) 111 } 112 d, ok := f.Addr.(*time.Duration) 113 if !ok { 114 var intDef int64 115 if def != "" { 116 intDef, err = strconv.ParseInt(def, 10, 64) 117 if err != nil { 118 return errors.Errorf("Invalid int64 value in 'def' tag of %s field", f.Path) 119 } 120 } 121 fr.flags.Int64VarP(f.Addr.(*int64), f.Path, opt, intDef, help) 122 } else { 123 var intDef time.Duration 124 if def != "" { 125 intDef, err = time.ParseDuration(def) 126 if err != nil { 127 return errors.Errorf("Invalid duration value in 'def' tag of %s field", f.Path) 128 } 129 } 130 fr.flags.DurationVarP(d, f.Path, opt, intDef, help) 131 } 132 case reflect.Bool: 133 if help == "" && !hide { 134 return errors.Errorf("Field is missing a help tag: %s", f.Path) 135 } 136 var boolDef bool 137 if def != "" { 138 boolDef, err = strconv.ParseBool(def) 139 if err != nil { 140 return errors.Errorf("Invalid boolean value in 'def' tag of %s field", f.Path) 141 } 142 } 143 fr.flags.BoolVarP(f.Addr.(*bool), f.Path, opt, boolDef, help) 144 case reflect.Slice: 145 if f.Type.Elem().Kind() == reflect.String { 146 if help == "" && !hide { 147 return errors.Errorf("Field is missing a help tag: %s", f.Path) 148 } 149 fr.flags.StringSliceVarP(f.Addr.(*[]string), f.Path, opt, nil, help) 150 } else { 151 return nil 152 } 153 default: 154 log.Debugf("Not registering flag for '%s' because it is a currently unsupported type: %s\n", 155 f.Path, f.Kind) 156 return nil 157 } 158 if hide { 159 fr.flags.MarkHidden(f.Path) 160 } 161 bindFlag(fr.viper, fr.flags, f.Path) 162 return nil 163 } 164 165 func (fr *flagRegistrar) getTag(f *Field, tagName string) string { 166 var key, val string 167 key = fmt.Sprintf("%s.%s", tagName, f.Path) 168 if fr.tags != nil { 169 val = fr.tags[key] 170 } 171 if val == "" { 172 val = f.Tag.Get(tagName) 173 } 174 return val 175 } 176 177 func (fr *flagRegistrar) getHideBooleanTag(f *Field) bool { 178 boolVal, err := strconv.ParseBool(f.Hide) 179 if err != nil { 180 return false 181 } 182 return boolVal 183 } 184 185 // CmdRunBegin is called at the beginning of each cobra run function 186 func CmdRunBegin(v *viper.Viper) { 187 // If -d or --debug, set debug logging level 188 if v.GetBool("debug") { 189 log.Level = log.LevelDebug 190 191 logging.SetLevel(logging.INFO, "bccsp") 192 logging.SetLevel(logging.INFO, "bccsp_p11") 193 logging.SetLevel(logging.INFO, "bccsp_sw") 194 } 195 } 196 197 // FlagString sets up a flag for a string, binding it to its name 198 func FlagString(v *viper.Viper, flags *pflag.FlagSet, name, short string, def string, desc string) { 199 flags.StringP(name, short, def, desc) 200 bindFlag(v, flags, name) 201 } 202 203 // common binding function 204 func bindFlag(v *viper.Viper, flags *pflag.FlagSet, name string) { 205 flag := flags.Lookup(name) 206 if flag == nil { 207 panic(fmt.Errorf("failed to lookup '%s'", name)) 208 } 209 v.BindPFlag(name, flag) 210 } 211 212 // ViperUnmarshal is a work around for a bug in viper.Unmarshal 213 // This can be removed once https://github.com/spf13/viper/issues/327 is fixed 214 // and vendored. 215 func ViperUnmarshal(cfg interface{}, stringSliceFields []string, vp *viper.Viper) error { 216 decoderConfig := &mapstructure.DecoderConfig{ 217 Metadata: nil, 218 Result: cfg, 219 WeaklyTypedInput: true, 220 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 221 } 222 decoder, err := mapstructure.NewDecoder(decoderConfig) 223 if err != nil { 224 return errors.Wrap(err, "Failed to create decoder") 225 } 226 settings := vp.AllSettings() 227 for _, field := range stringSliceFields { 228 var ok bool 229 path := strings.Split(field, ".") 230 m := settings 231 name := path[0] 232 // If it is a top level option check to see if nil before continuing 233 if _, ok = m[name]; !ok { 234 continue 235 } 236 237 if len(path) > 1 { 238 for _, field2 := range path[1:] { 239 m = m[name].(map[string]interface{}) 240 name = field2 241 242 // Inspect nested options to see if nil before proceeding with loop 243 if _, ok = m[name]; !ok { 244 break 245 } 246 } 247 } 248 // Only do casting if path was valid 249 if ok { 250 m[name] = cast.ToStringSlice(m[name]) 251 } 252 } 253 254 return decoder.Decode(settings) 255 }