Example #1
0
// ResolveKeyspaceWildcard will resolve keyspace wildcards.
// - If the param is not a wildcard, it will just be returned (if the keyspace
//   doesn't exist, it is still returned).
// - If the param is a wildcard, it will get all keyspaces and returns
//   the ones which match the wildcard (which may be an empty list).
func ResolveKeyspaceWildcard(server WildcardBackend, param string) ([]string, error) {
	if !fileutil.HasWildcard(param) {
		return []string{param}, nil
	}

	result := make([]string, 0)

	keyspaces, err := server.GetKeyspaces()
	if err != nil {
		return nil, fmt.Errorf("failed to read keyspaces from topo: %v", err)
	}
	for _, k := range keyspaces {
		matched, err := path.Match(param, k)
		if err != nil {
			return nil, fmt.Errorf("invalid pattern %v: %v", param, err)
		}
		if matched {
			result = append(result, k)
		}
	}
	return result, nil
}
Example #2
0
func resolveRecursive(zconn Conn, parts []string, toplevel bool) ([]string, error) {
	for i, part := range parts {
		if fileutil.HasWildcard(part) {
			var children []string
			var err error
			if i == 2 {
				children, err = ZkKnownCells(false)
				if err != nil {
					return children, err
				}
			} else {
				zkParentPath := strings.Join(parts[:i], "/")
				children, _, err = zconn.Children(zkParentPath)
				if err != nil {
					// we asked for something like
					// /zk/cell/aaa/* and
					// /zk/cell/aaa doesn't exist
					// -> return empty list, no error
					// (note we check both a regular zk
					// error and the error the test
					// produces)
					if zookeeper.IsError(err, zookeeper.ZNONODE) {
						return nil, nil
					}
					// otherwise we return the error
					return nil, err
				}
			}
			sort.Strings(children)

			results := make([][]string, len(children))
			wg := &sync.WaitGroup{}
			mu := &sync.Mutex{}
			var firstError error

			for j, child := range children {
				matched, err := path.Match(part, child)
				if err != nil {
					return nil, err
				}
				if matched {
					// we have a match!
					wg.Add(1)
					newParts := make([]string, len(parts))
					copy(newParts, parts)
					newParts[i] = child
					go func(j int) {
						defer wg.Done()
						subResult, err := resolveRecursive(zconn, newParts, false)
						if err != nil {
							mu.Lock()
							if firstError != nil {
								log.Infof("Multiple error: %v", err)
							} else {
								firstError = err
							}
							mu.Unlock()
						} else {
							results[j] = subResult
						}
					}(j)
				}
			}

			wg.Wait()
			if firstError != nil {
				return nil, firstError
			}

			result := make([]string, 0, 32)
			for j := 0; j < len(children); j++ {
				subResult := results[j]
				if subResult != nil {
					result = append(result, subResult...)
				}
			}

			// we found a part that is a wildcard, we
			// added the children already, we're done
			return result, nil
		}
	}

	// no part contains a wildcard, add the path if it exists, and done
	path := strings.Join(parts, "/")
	if toplevel {
		// for whatever the user typed at the toplevel, we don't
		// check it exists or not, we just return it
		return []string{path}, nil
	}

	// this is an expanded path, we need to check if it exists
	stat, err := zconn.Exists(path)
	if err != nil {
		return nil, err
	}
	if stat != nil {
		return []string{path}, nil
	}
	return nil, nil
}
Example #3
0
// ResolveShardWildcard will resolve shard wildcards. Both keyspace and shard
// names can use wildcard. Errors talking to the topology server are returned.
// ErrNoNode is ignored if it's the result of resolving a wildcard. Examples:
// - */* returns all keyspace/shard pairs, or empty list if none.
// - user/* returns all shards in user keyspace (or error if user keyspace
//   doesn't exist)
// - us*/* returns all shards in all keyspaces that start with 'us'. If no such
//   keyspace exists, list is empty (it is not an error).
func ResolveShardWildcard(server WildcardBackend, param string) ([]KeyspaceShard, error) {
	parts := strings.Split(param, "/")
	if len(parts) != 2 {
		return nil, fmt.Errorf("invalid shard path: %v", param)
	}
	result := make([]KeyspaceShard, 0, 1)

	// get all the matched keyspaces first, remember if it was a wildcard
	keyspaceHasWildcards := fileutil.HasWildcard(parts[0])
	matchedKeyspaces, err := ResolveKeyspaceWildcard(server, parts[0])
	if err != nil {
		return nil, err
	}

	// for each matched keyspace, get the shards
	for _, matchedKeyspace := range matchedKeyspaces {
		shard := parts[1]
		if fileutil.HasWildcard(shard) {
			// get all the shards for the keyspace
			shardNames, err := server.GetShardNames(matchedKeyspace)
			switch err {
			case nil:
				// got all the shards, we can keep going
			case ErrNoNode:
				// keyspace doesn't exist
				if keyspaceHasWildcards {
					// that's the */* case when a keyspace has no shards
					continue
				}
				return nil, fmt.Errorf("keyspace %v doesn't exist", matchedKeyspace)
			default:
				return nil, fmt.Errorf("cannot read keyspace shards for %v: %v", matchedKeyspace, err)
			}
			for _, s := range shardNames {
				matched, err := path.Match(shard, s)
				if err != nil {
					return nil, fmt.Errorf("Invalid pattern %v: %v", shard, err)
				}
				if matched {
					result = append(result, KeyspaceShard{matchedKeyspace, s})
				}
			}
		} else {
			// if the shard name contains a '-', we assume it's the
			// name for a ranged based shard, so we lower case it.
			if strings.Contains(shard, "-") {
				shard = strings.ToLower(shard)
			}
			if keyspaceHasWildcards {
				// keyspace was a wildcard, shard is not, just try it
				_, err := server.GetShard(matchedKeyspace, shard)
				switch err {
				case nil:
					// shard exists, add it
					result = append(result, KeyspaceShard{matchedKeyspace, shard})
				case ErrNoNode:
					// no shard, ignore
				default:
					// other error
					return nil, fmt.Errorf("Cannot read shard %v/%v: %v", matchedKeyspace, shard, err)
				}
			} else {
				// keyspace and shards are not wildcards, just add the value
				result = append(result, KeyspaceShard{matchedKeyspace, shard})
			}
		}
	}
	return result, nil
}