github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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 statusInfo, err := u.AgentStatus() 142 if err != nil { 143 return false, false, err 144 } 145 return matchAgentStatus(patterns, statusInfo.Status) 146 } 147 148 func unitMatchWorkloadStatus(u *state.Unit, patterns []string) (bool, bool, error) { 149 workloadStatusInfo, err := u.Status() 150 if err != nil { 151 return false, false, err 152 } 153 agentStatusInfo, err := u.AgentStatus() 154 if err != nil { 155 return false, false, err 156 } 157 return matchWorkloadStatus(patterns, workloadStatusInfo.Status, agentStatusInfo.Status) 158 } 159 160 func unitMatchExposure(u *state.Unit, patterns []string) (bool, bool, error) { 161 s, err := u.Service() 162 if err != nil { 163 return false, false, err 164 } 165 return matchExposure(patterns, s) 166 } 167 168 func unitMatchSubnet(u *state.Unit, patterns []string) (bool, bool, error) { 169 pub, pubErr := u.PublicAddress() 170 if pubErr != nil && !network.IsNoAddress(pubErr) { 171 return true, false, errors.Trace(pubErr) 172 } 173 priv, privErr := u.PrivateAddress() 174 if privErr != nil && !network.IsNoAddress(privErr) { 175 return true, false, errors.Trace(privErr) 176 } 177 if pubErr != nil && privErr != nil { 178 return true, false, nil 179 } 180 return matchSubnet(patterns, pub.Value, priv.Value) 181 } 182 183 func unitMatchPort(u *state.Unit, patterns []string) (bool, bool, error) { 184 portRanges, err := u.OpenedPorts() 185 if err != nil { 186 return false, false, err 187 } 188 return matchPortRanges(patterns, portRanges...) 189 } 190 191 func buildServiceMatcherShims(s *state.Service, patterns ...string) (shims []closurePredicate, _ error) { 192 // Match on name. 193 shims = append(shims, func() (bool, bool, error) { 194 for _, p := range patterns { 195 if strings.ToLower(s.Name()) == strings.ToLower(p) { 196 return true, true, nil 197 } 198 } 199 return false, false, nil 200 }) 201 202 // Match on exposure. 203 shims = append(shims, func() (bool, bool, error) { return matchExposure(patterns, s) }) 204 205 // Match on network addresses. 206 networks, err := s.Networks() 207 if err != nil { 208 return nil, err 209 } 210 shims = append(shims, func() (bool, bool, error) { return matchSubnet(patterns, networks...) }) 211 212 // If the service has an unit instance that matches any of the 213 // given criteria, consider the service a match as well. 214 unitShims, err := buildShimsForUnit(s.AllUnits, patterns...) 215 if err != nil { 216 return nil, err 217 } 218 shims = append(shims, unitShims...) 219 220 // Units may be able to match the pattern. Ultimately defer to 221 // that logic, and guard against breaking the predicate-chain. 222 if len(unitShims) <= 0 { 223 shims = append(shims, func() (bool, bool, error) { return false, true, nil }) 224 } 225 226 return shims, nil 227 } 228 229 func buildShimsForUnit(unitsFn func() ([]*state.Unit, error), patterns ...string) (shims []closurePredicate, _ error) { 230 units, err := unitsFn() 231 if err != nil { 232 return nil, err 233 } 234 for _, u := range units { 235 shims = append(shims, buildUnitMatcherShims(u, patterns)...) 236 } 237 return shims, nil 238 } 239 240 func buildMachineMatcherShims(m *state.Machine, patterns []string) (shims []closurePredicate, _ error) { 241 // Look at machine status. 242 statusInfo, err := m.Status() 243 if err != nil { 244 return nil, err 245 } 246 shims = append(shims, func() (bool, bool, error) { return matchAgentStatus(patterns, statusInfo.Status) }) 247 248 // Look at machine addresses. WARNING: Avoid the temptation to 249 // bring the append into the loop. The value we would close over 250 // will continue to change after the closure is created, and we'd 251 // only examine the last element of the loop for all closures. 252 var addrs []string 253 for _, a := range m.Addresses() { 254 addrs = append(addrs, a.Value) 255 } 256 shims = append(shims, func() (bool, bool, error) { return matchSubnet(patterns, addrs...) }) 257 258 // If the machine hosts a unit that matches any of the given 259 // criteria, consider the machine a match as well. 260 unitShims, err := buildShimsForUnit(m.Units, patterns...) 261 if err != nil { 262 return nil, err 263 } 264 shims = append(shims, unitShims...) 265 266 // Units may be able to match the pattern. Ultimately defer to 267 // that logic, and guard against breaking the predicate-chain. 268 if len(unitShims) <= 0 { 269 shims = append(shims, func() (bool, bool, error) { return false, true, nil }) 270 } 271 272 return 273 } 274 275 func buildUnitMatcherShims(u *state.Unit, patterns []string) []closurePredicate { 276 closeOver := func(f func(*state.Unit, []string) (bool, bool, error)) closurePredicate { 277 return func() (bool, bool, error) { return f(u, patterns) } 278 } 279 return []closurePredicate{ 280 closeOver(unitMatchUnitName), 281 closeOver(unitMatchAgentStatus), 282 closeOver(unitMatchWorkloadStatus), 283 closeOver(unitMatchExposure), 284 closeOver(unitMatchSubnet), 285 closeOver(unitMatchPort), 286 } 287 } 288 289 func matchPortRanges(patterns []string, portRanges ...network.PortRange) (bool, bool, error) { 290 for _, p := range portRanges { 291 for _, patt := range patterns { 292 if strings.HasPrefix(p.String(), patt) { 293 return true, true, nil 294 } 295 } 296 } 297 return false, true, nil 298 } 299 300 func matchSubnet(patterns []string, addresses ...string) (bool, bool, error) { 301 oneValidPattern := false 302 for _, p := range patterns { 303 for _, a := range addresses { 304 ip, err := net.ResolveIPAddr("ip", a) 305 if err != nil { 306 errors.Trace(errors.Annotate(err, "could not parse machine's address")) 307 continue 308 } else if pip, err := net.ResolveIPAddr("ip", p); err == nil { 309 oneValidPattern = true 310 if ip.IP.Equal(pip.IP) { 311 return true, true, nil 312 } 313 } else if pip := net.ParseIP(p); pip != nil { 314 oneValidPattern = true 315 if ip.IP.Equal(pip) { 316 return true, true, nil 317 } 318 } else if _, ipNet, err := net.ParseCIDR(p); err == nil { 319 oneValidPattern = true 320 if ipNet.Contains(ip.IP) { 321 return true, true, nil 322 } 323 } 324 } 325 } 326 return false, oneValidPattern, nil 327 } 328 329 func matchExposure(patterns []string, s *state.Service) (bool, bool, error) { 330 if len(patterns) >= 1 && patterns[0] == "exposed" { 331 return s.IsExposed(), true, nil 332 } else if len(patterns) >= 2 && patterns[0] == "not" && patterns[1] == "exposed" { 333 return !s.IsExposed(), true, nil 334 } 335 return false, false, nil 336 } 337 338 func matchWorkloadStatus(patterns []string, workloadStatus state.Status, agentStatus state.Status) (bool, bool, error) { 339 oneValidStatus := false 340 for _, p := range patterns { 341 // If the pattern isn't a known status, ignore it. 342 ps := state.Status(p) 343 if !ps.KnownWorkloadStatus() { 344 continue 345 } 346 347 oneValidStatus = true 348 // To preserve current expected behaviour, we only report on workload status 349 // if the agent itself is not in error. 350 if agentStatus != state.StatusError && workloadStatus.WorkloadMatches(ps) { 351 return true, true, nil 352 } 353 } 354 return false, oneValidStatus, nil 355 } 356 357 func matchAgentStatus(patterns []string, status state.Status) (bool, bool, error) { 358 oneValidStatus := false 359 for _, p := range patterns { 360 // If the pattern isn't a known status, ignore it. 361 ps := state.Status(p) 362 if !ps.KnownAgentStatus() { 363 continue 364 } 365 366 oneValidStatus = true 367 if status.Matches(ps) { 368 return true, true, nil 369 } 370 } 371 return false, oneValidStatus, nil 372 } 373 374 type unitMatcher struct { 375 patterns []string 376 } 377 378 // matchesAny returns true if the unitMatcher will 379 // match any unit, regardless of its attributes. 380 func (m unitMatcher) matchesAny() bool { 381 return len(m.patterns) == 0 382 } 383 384 // matchUnit attempts to match a state.Unit to one of 385 // a set of patterns, taking into account subordinate 386 // relationships. 387 func (m unitMatcher) matchUnit(u *state.Unit) bool { 388 if m.matchesAny() { 389 return true 390 } 391 392 // Keep the unit if: 393 // (a) its name matches a pattern, or 394 // (b) it's a principal and one of its subordinates matches, or 395 // (c) it's a subordinate and its principal matches. 396 // 397 // Note: do *not* include a second subordinate if the principal is 398 // only matched on account of a first subordinate matching. 399 if m.matchString(u.Name()) { 400 return true 401 } 402 if u.IsPrincipal() { 403 for _, s := range u.SubordinateNames() { 404 if m.matchString(s) { 405 return true 406 } 407 } 408 return false 409 } 410 principal, valid := u.PrincipalName() 411 if !valid { 412 panic("PrincipalName failed for subordinate unit") 413 } 414 return m.matchString(principal) 415 } 416 417 // matchString matches a string to one of the patterns in 418 // the unit matcher, returning an error if a pattern with 419 // invalid syntax is encountered. 420 func (m unitMatcher) matchString(s string) bool { 421 for _, pattern := range m.patterns { 422 ok, err := path.Match(pattern, s) 423 if err != nil { 424 // We validate patterns, so should never get here. 425 panic(fmt.Errorf("pattern syntax error in %q", pattern)) 426 } else if ok { 427 return true 428 } 429 } 430 return false 431 } 432 433 // validPattern must match the parts of a unit or service name 434 // pattern either side of the '/' for it to be valid. 435 var validPattern = regexp.MustCompile("^[a-z0-9-*]+$") 436 437 // NewUnitMatcher returns a unitMatcher that matches units 438 // with one of the specified patterns, or all units if no 439 // patterns are specified. 440 // 441 // An error will be returned if any of the specified patterns 442 // is invalid. Patterns are valid if they contain only 443 // alpha-numeric characters, hyphens, or asterisks (and one 444 // optional '/' to separate service/unit). 445 func NewUnitMatcher(patterns []string) (unitMatcher, error) { 446 pattCopy := make([]string, len(patterns)) 447 for i, pattern := range patterns { 448 pattCopy[i] = patterns[i] 449 fields := strings.Split(pattern, "/") 450 if len(fields) > 2 { 451 return unitMatcher{}, fmt.Errorf("pattern %q contains too many '/' characters", pattern) 452 } 453 for _, f := range fields { 454 if !validPattern.MatchString(f) { 455 return unitMatcher{}, fmt.Errorf("pattern %q contains invalid characters", pattern) 456 } 457 } 458 if len(fields) == 1 { 459 pattCopy[i] += "/*" 460 } 461 } 462 return unitMatcher{pattCopy}, nil 463 }