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  }