go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/config/set.go (about) 1 // Copyright 2018 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package config 16 17 import ( 18 "fmt" 19 "regexp" 20 "strings" 21 ) 22 23 // ServiceNamePattern is the regexp pattern string that matches valid service 24 // name. 25 const ServiceNamePattern = `[a-z0-9\-_]+` 26 27 // serviceNameRegexp matches valid service name 28 var serviceNameRegexp = regexp.MustCompile(fmt.Sprintf(`^%s$`, ServiceNamePattern)) 29 30 // Set is a name of a configuration set: a bunch of config files versioned and 31 // stored as a single unit in a same repository. 32 // 33 // A config set name consists of a domain and a target. 34 // 35 // - Service config sets are config sets in the "services" domain, with the 36 // service name as the target. 37 // - Project config sets are config sets in the "projects" domain. The target 38 // is the project name. 39 type Set string 40 41 // Domain describes the domain of the config set. 42 type Domain string 43 44 const ( 45 // ProjectDomain is the domain of project config set(e.g. projects/chromium). 46 ProjectDomain Domain = "projects" 47 // ServiceDomain is the domain of service config set (e.g. 48 // services/luci-config). 49 ServiceDomain Domain = "services" 50 ) 51 52 // ServiceSet returns the name of a config set for the specified service. 53 // 54 // Returns error if the service name doesn't match `ServiceNamePattern`. 55 func ServiceSet(service string) (Set, error) { 56 if !serviceNameRegexp.MatchString(service) { 57 return "", fmt.Errorf("invalid service name %q, expected to match %q", service, ServiceNamePattern) 58 } 59 return Set(fmt.Sprintf("%s/%s", ServiceDomain, service)), nil 60 } 61 62 // MustServiceSet is like `ServiceSet` but panic on invalid service name. 63 func MustServiceSet(service string) Set { 64 cs, err := ServiceSet(service) 65 if err != nil { 66 panic(err) 67 } 68 return cs 69 } 70 71 // ProjectSet returns the config set for the specified project. 72 // 73 // Returns error if the project name is invalid. See `ValidateProjectName`. 74 func ProjectSet(project string) (Set, error) { 75 if err := ValidateProjectName(project); err != nil { 76 return "", fmt.Errorf("invalid project name: %w", err) 77 } 78 return Set(fmt.Sprintf("%s/%s", ProjectDomain, project)), nil 79 } 80 81 // MustProjectSet is like `ProjectSet` but panic on invalid project name. 82 func MustProjectSet(project string) Set { 83 cs, err := ProjectSet(project) 84 if err != nil { 85 panic(err) 86 } 87 return cs 88 } 89 90 // Split splits a Set into its domain, target components. 91 func (cs Set) Split() (domain Domain, target string) { 92 p := strings.SplitN(string(cs), "/", 2) 93 if len(p) == 1 { 94 return Domain(p[0]), "" 95 } 96 return Domain(p[0]), p[1] 97 } 98 99 // Service returns a service name for a service config set or empty string for 100 // all other sets. 101 func (cs Set) Service() string { 102 domain, target := cs.Split() 103 if domain == ServiceDomain { 104 return target 105 } 106 return "" 107 } 108 109 // Project returns a project name for a project config set or empty string for 110 // all other sets. 111 func (cs Set) Project() string { 112 domain, target := cs.Split() 113 if domain == ProjectDomain { 114 return target 115 } 116 return "" 117 } 118 119 // Domain returns the domain of the config set. 120 func (cs Set) Domain() Domain { 121 domain, _ := cs.Split() 122 return domain 123 } 124 125 // Validate checks that the config set is well-formed. 126 func (cs Set) Validate() error { 127 switch domain, target := cs.Split(); domain { 128 case "": 129 return fmt.Errorf("can not extract domain from config set %q. expected syntax \"domain/target\"", cs) 130 case ProjectDomain: 131 _, err := ProjectSet(target) 132 return err 133 case ServiceDomain: 134 _, err := ServiceSet(target) 135 return err 136 default: 137 return fmt.Errorf("unknown domain %q for config set %q; currently supported domains [%s, %s]", domain, cs, ProjectDomain, ServiceDomain) 138 } 139 }