// 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 }
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 }
// 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 }