vitess.io/vitess@v0.16.2/go/vt/vtadmin/cluster/flags.go (about) 1 /* 2 Copyright 2020 The Vitess Authors. 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 cluster 18 19 import ( 20 "fmt" 21 "regexp" 22 "strings" 23 24 "vitess.io/vitess/go/vt/log" 25 "vitess.io/vitess/go/vt/vtadmin/cache" 26 ) 27 28 // FlagsByImpl groups a set of flags by discovery implementation. Its mapping is 29 // impl_name=>flag=>value. 30 type FlagsByImpl map[string]map[string]string 31 32 // Merge applies the flags in the parameter to the receiver, conflicts are 33 // resolved in favor of the parameter and not the receiver. 34 func (base *FlagsByImpl) Merge(override map[string]map[string]string) { 35 if (*base) == nil { 36 *base = map[string]map[string]string{} 37 } 38 39 for impl, flags := range override { 40 _, ok := (*base)[impl] 41 if !ok { 42 (*base)[impl] = map[string]string{} 43 } 44 45 for k, v := range flags { 46 (*base)[impl][k] = v 47 } 48 } 49 } 50 51 // ClustersFlag implements flag.Value allowing multiple occurrences of a flag to 52 // be accumulated into a map. 53 type ClustersFlag map[string]Config 54 55 // String is part of the flag.Value interface. 56 func (cf *ClustersFlag) String() string { 57 buf := strings.Builder{} 58 59 buf.WriteString("[") 60 61 i := 0 62 63 for _, cfg := range *cf { 64 buf.WriteString(cfg.String()) 65 66 if i < len(*cf)-1 { 67 buf.WriteString(" ") 68 } 69 70 i++ 71 } 72 73 buf.WriteString("]") 74 75 return buf.String() 76 } 77 78 // Type is part of the pflag.Value interface. 79 func (cf *ClustersFlag) Type() string { 80 return "cluster.ClustersFlag" 81 } 82 83 // Set is part of the flag.Value interface. It merges the parsed config into the 84 // map, allowing ClustersFlag to power a repeated flag. See (*Config).Set for 85 // details on flag parsing. 86 func (cf *ClustersFlag) Set(value string) error { 87 if (*cf) == nil { 88 (*cf) = map[string]Config{} 89 } 90 91 cfg := Config{ 92 DiscoveryFlagsByImpl: map[string]map[string]string{}, 93 } 94 95 if err := parseFlag(&cfg, value); err != nil { 96 return err 97 } 98 99 // Merge a potentially existing config for the same cluster ID. 100 c, ok := (*cf)[cfg.ID] 101 if !ok { 102 // If we don't have an existing config, create an empty one to "merge" 103 // into. 104 c = Config{} 105 } 106 107 (*cf)[cfg.ID] = cfg.Merge(c) 108 109 return nil 110 } 111 112 // nolint:gochecknoglobals 113 var discoveryFlagRegexp = regexp.MustCompile(`^discovery-(?P<impl>\w+)-(?P<flag>.+)$`) 114 115 func parseFlag(cfg *Config, value string) error { 116 args := strings.Split(value, ",") 117 for _, arg := range args { 118 var ( 119 name string 120 val string 121 ) 122 123 if strings.Contains(arg, "=") { 124 parts := strings.Split(arg, "=") 125 name = parts[0] 126 val = strings.Join(parts[1:], "=") 127 } else { 128 name = arg 129 val = "true" 130 } 131 132 if err := parseOne(cfg, name, val); err != nil { 133 return err 134 } 135 } 136 137 return nil 138 } 139 140 func parseOne(cfg *Config, name string, val string) error { 141 switch name { 142 case "id": 143 cfg.ID = val 144 case "name": 145 cfg.Name = val 146 case "discovery": 147 cfg.DiscoveryImpl = val 148 case "tablet-fqdn-tmpl": 149 cfg.TabletFQDNTmplStr = val 150 default: 151 switch { 152 case strings.HasPrefix(name, "vtsql-"): 153 if cfg.VtSQLFlags == nil { 154 cfg.VtSQLFlags = map[string]string{} 155 } 156 157 cfg.VtSQLFlags[strings.TrimPrefix(name, "vtsql-")] = val 158 case strings.HasPrefix(name, "vtctld-"): 159 if cfg.VtctldFlags == nil { 160 cfg.VtctldFlags = map[string]string{} 161 } 162 163 cfg.VtctldFlags[strings.TrimPrefix(name, "vtctld-")] = val 164 case strings.HasPrefix(name, "backup-read-pool-"): 165 if cfg.BackupReadPoolConfig == nil { 166 cfg.BackupReadPoolConfig = &RPCPoolConfig{ 167 Size: -1, 168 WaitTimeout: -1, 169 } 170 } 171 172 if err := cfg.BackupReadPoolConfig.parseFlag(strings.TrimPrefix(name, "backup-read-pool-"), val); err != nil { 173 return fmt.Errorf("error parsing %s: %w", name, err) 174 } 175 case strings.HasPrefix(name, "schema-read-pool-"): 176 if cfg.SchemaReadPoolConfig == nil { 177 cfg.SchemaReadPoolConfig = &RPCPoolConfig{ 178 Size: -1, 179 WaitTimeout: -1, 180 } 181 } 182 183 if err := cfg.SchemaReadPoolConfig.parseFlag(strings.TrimPrefix(name, "schema-read-pool-"), val); err != nil { 184 return fmt.Errorf("error parsing %s: %w", name, err) 185 } 186 case strings.HasPrefix(name, "topo-read-pool-"): 187 if cfg.TopoReadPoolConfig == nil { 188 cfg.TopoReadPoolConfig = &RPCPoolConfig{ 189 Size: -1, 190 WaitTimeout: -1, 191 } 192 } 193 194 if err := cfg.TopoReadPoolConfig.parseFlag(strings.TrimPrefix(name, "topo-read-pool-"), val); err != nil { 195 return fmt.Errorf("error parsing %s: %w", name, err) 196 } 197 case strings.HasPrefix(name, "topo-rw-pool-"): 198 if cfg.TopoRWPoolConfig == nil { 199 cfg.TopoRWPoolConfig = &RPCPoolConfig{ 200 Size: -1, 201 WaitTimeout: -1, 202 } 203 } 204 205 if err := cfg.TopoRWPoolConfig.parseFlag(strings.TrimPrefix(name, "topo-rw-pool-"), val); err != nil { 206 return fmt.Errorf("error parsing %s: %w", name, err) 207 } 208 case strings.HasPrefix(name, "workflow-read-pool-"): 209 if cfg.WorkflowReadPoolConfig == nil { 210 cfg.WorkflowReadPoolConfig = &RPCPoolConfig{ 211 Size: -1, 212 WaitTimeout: -1, 213 } 214 } 215 216 if err := cfg.WorkflowReadPoolConfig.parseFlag(strings.TrimPrefix(name, "workflow-read-pool-"), val); err != nil { 217 return fmt.Errorf("error parsing %s: %w", name, err) 218 } 219 case strings.HasPrefix(name, "emergency-failover-pool-"): 220 if cfg.EmergencyFailoverPoolConfig == nil { 221 cfg.EmergencyFailoverPoolConfig = &RPCPoolConfig{ 222 Size: -1, 223 WaitTimeout: -1, 224 } 225 } 226 227 if err := cfg.EmergencyFailoverPoolConfig.parseFlag(strings.TrimPrefix(name, "emergency-failover-pool-"), val); err != nil { 228 return fmt.Errorf("error parsing %s: %w", name, err) 229 } 230 case strings.HasPrefix(name, "failover-pool-"): 231 if cfg.FailoverPoolConfig == nil { 232 cfg.FailoverPoolConfig = &RPCPoolConfig{ 233 Size: -1, 234 WaitTimeout: -1, 235 } 236 } 237 238 if err := cfg.FailoverPoolConfig.parseFlag(strings.TrimPrefix(name, "failover-pool-"), val); err != nil { 239 return fmt.Errorf("error parsing %s: %w", name, err) 240 } 241 case strings.HasPrefix(name, "schema-cache-"): 242 if cfg.SchemaCacheConfig == nil { 243 cfg.SchemaCacheConfig = &cache.Config{ 244 DefaultExpiration: -1, 245 CleanupInterval: -1, 246 BackfillRequestTTL: -1, 247 BackfillRequestDuplicateInterval: -1, 248 BackfillQueueSize: -1, 249 BackfillEnqueueWaitTime: -1, 250 } 251 } 252 253 if err := parseCacheConfigFlag(cfg.SchemaCacheConfig, strings.TrimPrefix(name, "schema-cache-"), val); err != nil { 254 return fmt.Errorf("error parsing %s: %w", name, err) 255 } 256 default: 257 match := discoveryFlagRegexp.FindStringSubmatch(name) 258 if match == nil { 259 // not a discovery flag 260 log.Warningf("Attempted to parse %q as a discovery flag, ignoring ...", name) 261 return nil 262 } 263 264 var impl, flag string 265 266 for i, g := range discoveryFlagRegexp.SubexpNames() { 267 switch g { 268 case "impl": 269 impl = match[i] 270 case "flag": 271 flag = match[i] 272 } 273 } 274 275 if cfg.DiscoveryFlagsByImpl == nil { 276 cfg.DiscoveryFlagsByImpl = map[string]map[string]string{} 277 } 278 279 if cfg.DiscoveryFlagsByImpl[impl] == nil { 280 cfg.DiscoveryFlagsByImpl[impl] = map[string]string{} 281 } 282 283 cfg.DiscoveryFlagsByImpl[impl][flag] = val 284 } 285 } 286 287 return nil 288 }