github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/client/filtering.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package client 5 6 import ( 7 "fmt" 8 "net" 9 "path" 10 "regexp" 11 "strings" 12 13 "github.com/juju/errors" 14 15 "github.com/juju/juju/network" 16 "github.com/juju/juju/state" 17 ) 18 19 var InvalidFormatErr = errors.Errorf("the given filter did not match any known patterns.") 20 21 // UnitChainPredicateFn builds a function which runs the given 22 // predicate over a unit and all of its subordinates. If one unit in 23 // the chain matches, the entire chain matches. 24 func UnitChainPredicateFn( 25 predicate Predicate, 26 getUnit func(string) *state.Unit, 27 ) func(*state.Unit) (bool, error) { 28 considered := make(map[string]bool) 29 var f func(unit *state.Unit) (bool, error) 30 f = func(unit *state.Unit) (bool, error) { 31 // Don't try and filter the same unit 2x. 32 if matches, ok := considered[unit.Name()]; ok { 33 logger.Debugf("%s has already been examined and found to be: %t", unit.Name(), matches) 34 return matches, nil 35 } 36 37 // Check the current unit. 38 matches, err := predicate(unit) 39 if err != nil { 40 return false, errors.Annotate(err, "could not filter units") 41 } 42 considered[unit.Name()] = matches 43 44 // Now check all of this unit's subordinates. 45 for _, subName := range unit.SubordinateNames() { 46 // A master match supercedes any subordinate match. 47 if matches { 48 logger.Infof("%s is a subordinate to a match.", subName) 49 considered[subName] = true 50 continue 51 } 52 53 subUnit := getUnit(subName) 54 if subUnit == nil { 55 // We have already deleted this unit 56 matches = false 57 continue 58 } 59 matches, err = f(subUnit) 60 if err != nil { 61 return false, err 62 } 63 considered[subName] = matches 64 } 65 66 return matches, nil 67 } 68 return f 69 } 70 71 // BuildPredicate returns a Predicate which will evaluate a machine, 72 // service, or unit against the given patterns. 73 func BuildPredicateFor(patterns []string) Predicate { 74 75 or := func(predicates ...closurePredicate) (bool, error) { 76 // Differentiate between a valid format that elimintated all 77 // elements, and an invalid query. 78 oneValidFmt := false 79 for _, p := range predicates { 80 if matches, ok, err := p(); err != nil { 81 return false, err 82 } else if ok { 83 oneValidFmt = true 84 if matches { 85 return true, nil 86 } 87 } 88 } 89 90 if !oneValidFmt && len(predicates) > 0 { 91 return false, InvalidFormatErr 92 } 93 94 return false, nil 95 } 96 97 return func(i interface{}) (bool, error) { 98 switch i.(type) { 99 default: 100 panic(errors.Errorf("Programming error. We should only ever pass in machines, services, or units. Received %T.", i)) 101 case *state.Machine: 102 shims, err := buildMachineMatcherShims(i.(*state.Machine), patterns) 103 if err != nil { 104 return false, err 105 } 106 return or(shims...) 107 case *state.Unit: 108 return or(buildUnitMatcherShims(i.(*state.Unit), patterns)...) 109 case *state.Service: 110 shims, err := buildServiceMatcherShims(i.(*state.Service), patterns...) 111 if err != nil { 112 return false, err 113 } 114 return or(shims...) 115 } 116 } 117 } 118 119 // Predicate is a function that when given a unit, machine, or 120 // service, will determine whether the unit meets some criteria. 121 type Predicate func(interface{}) (matches bool, _ error) 122 123 // closurePredicate is a function which has at some point been closed 124 // around an element so that it can examine whether this element 125 // matches some criteria. 126 type closurePredicate func() (matches bool, formatOK bool, _ error) 127 128 func unitMatchUnitName(u *state.Unit, patterns []string) (bool, bool, error) { 129 um, err := NewUnitMatcher(patterns) 130 if err != nil { 131 // Currently, the only error possible here is a matching 132 // error. We don't want this error to hold up further 133 // matching. 134 logger.Debugf("ignoring matching error: %v", err) 135 return false, false, nil 136 } 137 return um.matchUnit(u), true, nil 138 } 139 140 func unitMatchAgentStatus(u *state.Unit, patterns []string) (bool, bool, error) { 141 status, _, _, err := u.Status() 142 if err != nil { 143 return false, false, err 144 } 145 return matchAgentStatus(patterns, status) 146 } 147 148 func unitMatchExposure(u *state.Unit, patterns []string) (bool, bool, error) { 149 s, err := u.Service() 150 if err != nil { 151 return false, false, err 152 } 153 return matchExposure(patterns, s) 154 } 155 156 func unitMatchSubnet(u *state.Unit, patterns []string) (bool, bool, error) { 157 pub, pubOK := u.PublicAddress() 158 priv, privOK := u.PrivateAddress() 159 if !pubOK && !privOK { 160 return true, false, nil 161 } 162 return matchSubnet(patterns, pub, priv) 163 } 164 165 func unitMatchPort(u *state.Unit, patterns []string) (bool, bool, error) { 166 portRanges, err := u.OpenedPorts() 167 if err != nil { 168 return false, false, err 169 } 170 return matchPortRanges(patterns, portRanges...) 171 } 172 173 func buildServiceMatcherShims(s *state.Service, patterns ...string) (shims []closurePredicate, _ error) { 174 // Match on name. 175 shims = append(shims, func() (bool, bool, error) { 176 for _, p := range patterns { 177 if strings.ToLower(s.Name()) == strings.ToLower(p) { 178 return true, true, nil 179 } 180 } 181 return false, false, nil 182 }) 183 184 // Match on exposure. 185 shims = append(shims, func() (bool, bool, error) { return matchExposure(patterns, s) }) 186 187 // Match on network addresses. 188 networks, err := s.Networks() 189 if err != nil { 190 return nil, err 191 } 192 shims = append(shims, func() (bool, bool, error) { return matchSubnet(patterns, networks...) }) 193 194 // If the service has an unit instance that matches any of the 195 // given criteria, consider the service a match as well. 196 unitShims, err := buildShimsForUnit(s.AllUnits, patterns...) 197 if err != nil { 198 return nil, err 199 } 200 shims = append(shims, unitShims...) 201 202 // Units may be able to match the pattern. Ultimately defer to 203 // that logic, and guard against breaking the predicate-chain. 204 if len(unitShims) <= 0 { 205 shims = append(shims, func() (bool, bool, error) { return false, true, nil }) 206 } 207 208 return shims, nil 209 } 210 211 func buildShimsForUnit(unitsFn func() ([]*state.Unit, error), patterns ...string) (shims []closurePredicate, _ error) { 212 units, err := unitsFn() 213 if err != nil { 214 return nil, err 215 } 216 for _, u := range units { 217 shims = append(shims, buildUnitMatcherShims(u, patterns)...) 218 } 219 return shims, nil 220 } 221 222 func buildMachineMatcherShims(m *state.Machine, patterns []string) (shims []closurePredicate, _ error) { 223 // Look at machine status. 224 status, _, _, err := m.Status() 225 if err != nil { 226 return nil, err 227 } 228 shims = append(shims, func() (bool, bool, error) { return matchAgentStatus(patterns, status) }) 229 230 // Look at machine addresses. WARNING: Avoid the temptation to 231 // bring the append into the loop. The value we would close over 232 // will continue to change after the closure is created, and we'd 233 // only examine the last element of the loop for all closures. 234 var addrs []string 235 for _, a := range m.Addresses() { 236 addrs = append(addrs, a.Value) 237 } 238 shims = append(shims, func() (bool, bool, error) { return matchSubnet(patterns, addrs...) }) 239 240 // If the machine hosts a unit that matches any of the given 241 // criteria, consider the machine a match as well. 242 unitShims, err := buildShimsForUnit(m.Units, patterns...) 243 if err != nil { 244 return nil, err 245 } 246 shims = append(shims, unitShims...) 247 248 // Units may be able to match the pattern. Ultimately defer to 249 // that logic, and guard against breaking the predicate-chain. 250 if len(unitShims) <= 0 { 251 shims = append(shims, func() (bool, bool, error) { return false, true, nil }) 252 } 253 254 return 255 } 256 257 func buildUnitMatcherShims(u *state.Unit, patterns []string) []closurePredicate { 258 closeOver := func(f func(*state.Unit, []string) (bool, bool, error)) closurePredicate { 259 return func() (bool, bool, error) { return f(u, patterns) } 260 } 261 return []closurePredicate{ 262 closeOver(unitMatchUnitName), 263 closeOver(unitMatchAgentStatus), 264 closeOver(unitMatchExposure), 265 closeOver(unitMatchSubnet), 266 closeOver(unitMatchPort), 267 } 268 } 269 270 func matchPortRanges(patterns []string, portRanges ...network.PortRange) (bool, bool, error) { 271 for _, p := range portRanges { 272 for _, patt := range patterns { 273 if strings.HasPrefix(p.String(), patt) { 274 return true, true, nil 275 } 276 } 277 } 278 return false, true, nil 279 } 280 281 func matchSubnet(patterns []string, addresses ...string) (bool, bool, error) { 282 oneValidPattern := false 283 for _, p := range patterns { 284 for _, a := range addresses { 285 ip, err := net.ResolveIPAddr("ip", a) 286 if err != nil { 287 errors.Trace(errors.Annotate(err, "could not parse machine's address")) 288 continue 289 } else if pip, err := net.ResolveIPAddr("ip", p); err == nil { 290 oneValidPattern = true 291 if ip.IP.Equal(pip.IP) { 292 return true, true, nil 293 } 294 } else if pip := net.ParseIP(p); pip != nil { 295 oneValidPattern = true 296 if ip.IP.Equal(pip) { 297 return true, true, nil 298 } 299 } else if _, ipNet, err := net.ParseCIDR(p); err == nil { 300 oneValidPattern = true 301 if ipNet.Contains(ip.IP) { 302 return true, true, nil 303 } 304 } 305 } 306 } 307 return false, oneValidPattern, nil 308 } 309 310 func matchExposure(patterns []string, s *state.Service) (bool, bool, error) { 311 if len(patterns) >= 1 && patterns[0] == "exposed" { 312 return s.IsExposed(), true, nil 313 } else if len(patterns) >= 2 && patterns[0] == "not" && patterns[1] == "exposed" { 314 return !s.IsExposed(), true, nil 315 } 316 return false, false, nil 317 } 318 319 func matchAgentStatus(patterns []string, status state.Status) (bool, bool, error) { 320 oneValidStatus := false 321 for _, p := range patterns { 322 // If the pattern isn't a valid status, ignore it. 323 ps := state.Status(p) 324 if !ps.ValidAgentStatus() { 325 continue 326 } 327 328 oneValidStatus = true 329 if status.Matches(ps) { 330 return true, true, nil 331 } 332 } 333 return false, oneValidStatus, nil 334 } 335 336 type unitMatcher struct { 337 patterns []string 338 } 339 340 // matchesAny returns true if the unitMatcher will 341 // match any unit, regardless of its attributes. 342 func (m unitMatcher) matchesAny() bool { 343 return len(m.patterns) == 0 344 } 345 346 // matchUnit attempts to match a state.Unit to one of 347 // a set of patterns, taking into account subordinate 348 // relationships. 349 func (m unitMatcher) matchUnit(u *state.Unit) bool { 350 if m.matchesAny() { 351 return true 352 } 353 354 // Keep the unit if: 355 // (a) its name matches a pattern, or 356 // (b) it's a principal and one of its subordinates matches, or 357 // (c) it's a subordinate and its principal matches. 358 // 359 // Note: do *not* include a second subordinate if the principal is 360 // only matched on account of a first subordinate matching. 361 if m.matchString(u.Name()) { 362 return true 363 } 364 if u.IsPrincipal() { 365 for _, s := range u.SubordinateNames() { 366 if m.matchString(s) { 367 return true 368 } 369 } 370 return false 371 } 372 principal, valid := u.PrincipalName() 373 if !valid { 374 panic("PrincipalName failed for subordinate unit") 375 } 376 return m.matchString(principal) 377 } 378 379 // matchString matches a string to one of the patterns in 380 // the unit matcher, returning an error if a pattern with 381 // invalid syntax is encountered. 382 func (m unitMatcher) matchString(s string) bool { 383 for _, pattern := range m.patterns { 384 ok, err := path.Match(pattern, s) 385 if err != nil { 386 // We validate patterns, so should never get here. 387 panic(fmt.Errorf("pattern syntax error in %q", pattern)) 388 } else if ok { 389 return true 390 } 391 } 392 return false 393 } 394 395 // validPattern must match the parts of a unit or service name 396 // pattern either side of the '/' for it to be valid. 397 var validPattern = regexp.MustCompile("^[a-z0-9-*]+$") 398 399 // NewUnitMatcher returns a unitMatcher that matches units 400 // with one of the specified patterns, or all units if no 401 // patterns are specified. 402 // 403 // An error will be returned if any of the specified patterns 404 // is invalid. Patterns are valid if they contain only 405 // alpha-numeric characters, hyphens, or asterisks (and one 406 // optional '/' to separate service/unit). 407 func NewUnitMatcher(patterns []string) (unitMatcher, error) { 408 pattCopy := make([]string, len(patterns)) 409 for i, pattern := range patterns { 410 pattCopy[i] = patterns[i] 411 fields := strings.Split(pattern, "/") 412 if len(fields) > 2 { 413 return unitMatcher{}, fmt.Errorf("pattern %q contains too many '/' characters", pattern) 414 } 415 for _, f := range fields { 416 if !validPattern.MatchString(f) { 417 return unitMatcher{}, fmt.Errorf("pattern %q contains invalid characters", pattern) 418 } 419 } 420 if len(fields) == 1 { 421 pattCopy[i] += "/*" 422 } 423 } 424 return unitMatcher{pattCopy}, nil 425 }