github.com/letsencrypt/boulder@v0.20251208.0/cmd/registry.go (about) 1 package cmd 2 3 import ( 4 "fmt" 5 "reflect" 6 "sort" 7 "sync" 8 9 "github.com/letsencrypt/validator/v10" 10 ) 11 12 type ConfigValidator struct { 13 Config any 14 Validators map[string]validator.Func 15 } 16 17 var registry struct { 18 sync.Mutex 19 commands map[string]func() 20 configs map[string]*ConfigValidator 21 } 22 23 // RegisterCommand registers a subcommand and its corresponding config 24 // validator. The provided func() is called when the subcommand is invoked on 25 // the command line. The ConfigValidator is optional and used to validate the 26 // config file for the subcommand. 27 func RegisterCommand(name string, f func(), cv *ConfigValidator) { 28 registry.Lock() 29 defer registry.Unlock() 30 31 if registry.commands == nil { 32 registry.commands = make(map[string]func()) 33 } 34 35 if registry.commands[name] != nil { 36 panic(fmt.Sprintf("command %q was registered twice", name)) 37 } 38 registry.commands[name] = f 39 40 if cv == nil { 41 return 42 } 43 44 if registry.configs == nil { 45 registry.configs = make(map[string]*ConfigValidator) 46 } 47 48 if registry.configs[name] != nil { 49 panic(fmt.Sprintf("config validator for command %q was registered twice", name)) 50 } 51 registry.configs[name] = cv 52 } 53 54 func LookupCommand(name string) func() { 55 registry.Lock() 56 defer registry.Unlock() 57 return registry.commands[name] 58 } 59 60 func AvailableCommands() []string { 61 registry.Lock() 62 defer registry.Unlock() 63 var avail []string 64 for name := range registry.commands { 65 avail = append(avail, name) 66 } 67 sort.Strings(avail) 68 return avail 69 } 70 71 // LookupConfigValidator constructs an instance of the *ConfigValidator for the 72 // given Boulder component name. If no *ConfigValidator was registered, nil is 73 // returned. 74 func LookupConfigValidator(name string) *ConfigValidator { 75 registry.Lock() 76 defer registry.Unlock() 77 if registry.configs[name] == nil { 78 return nil 79 } 80 81 // Create a new copy of the config struct so that we can validate it 82 // multiple times without mutating the registry's copy. 83 copy := reflect.New(reflect.ValueOf( 84 registry.configs[name].Config).Elem().Type(), 85 ).Interface() 86 87 return &ConfigValidator{ 88 Config: copy, 89 Validators: registry.configs[name].Validators, 90 } 91 } 92 93 // AvailableConfigValidators returns a list of Boulder component names for which 94 // a *ConfigValidator has been registered. 95 func AvailableConfigValidators() []string { 96 registry.Lock() 97 defer registry.Unlock() 98 var avail []string 99 for name := range registry.configs { 100 avail = append(avail, name) 101 } 102 sort.Strings(avail) 103 return avail 104 }