go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/client/coordinator/query.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package coordinator 16 17 import ( 18 "context" 19 20 logdog "go.chromium.org/luci/logdog/api/endpoints/coordinator/logs/v1" 21 "go.chromium.org/luci/logdog/api/logpb" 22 "go.chromium.org/luci/logdog/common/types" 23 ) 24 25 // QueryTrinary is a 3-value query option type. 26 type QueryTrinary int 27 28 const ( 29 // Both means that the value should not have an effect. 30 Both QueryTrinary = iota 31 // Yes is a positive effect. 32 Yes 33 // No is a negative effect. 34 No 35 ) 36 37 func (t QueryTrinary) queryValue() logdog.QueryRequest_Trinary { 38 switch t { 39 case Yes: 40 return logdog.QueryRequest_YES 41 case No: 42 return logdog.QueryRequest_NO 43 default: 44 return logdog.QueryRequest_BOTH 45 } 46 } 47 48 // QueryStreamType is a 3-value query option type. 49 type QueryStreamType int 50 51 const ( 52 // Any means that the value should not have an effect. 53 Any QueryStreamType = iota 54 // Text selects only text streams. 55 Text 56 // Binary selects only binary streams. 57 Binary 58 // Datagram selects only datagram streams. 59 Datagram 60 ) 61 62 // queryValue returns the StreamType for a specified QueryStreamType parameter. 63 // If no StreamType is specified (Any), it will return -1 to indicate this. 64 func (t QueryStreamType) queryValue() logpb.StreamType { 65 switch t { 66 case Text: 67 return logpb.StreamType_TEXT 68 case Binary: 69 return logpb.StreamType_BINARY 70 case Datagram: 71 return logpb.StreamType_DATAGRAM 72 default: 73 return -1 74 } 75 } 76 77 // QueryOptions is the set of query options that can accompany a query. 78 type QueryOptions struct { 79 // Tags is the list of tags to require. The value may be empty if key presence 80 // is all that is being asserted. 81 Tags map[string]string 82 // ContentType, if not empty, restricts results to streams with the supplied 83 // content type. 84 ContentType string 85 86 // StreamType, if not STAny, is the stream type to query for. 87 StreamType QueryStreamType 88 89 // Purged, if not QBoth, selects logs streams that are/aren't purged. 90 Purged QueryTrinary 91 92 // State, if true, requests that the query results include the log streams' 93 // state. 94 State bool 95 } 96 97 // QueryCallback is a callback method type that is used in query requests. 98 // 99 // If it returns false, additional callbacks and queries will be aborted. 100 type QueryCallback func(r *LogStream) bool 101 102 // Query executes a query, invoking the supplied callback once for each query 103 // result. 104 // 105 // The path is the query parameter. 106 // 107 // The path expression may substitute a glob ("*") for a specific path 108 // component. That is, any stream that matches the remaining structure qualifies 109 // regardless of its value in that specific positional field. 110 // 111 // An unbounded wildcard may appear as a component at the end of both the 112 // prefix and name query components. "**" matches all remaining components. 113 // 114 // If the supplied path query does not contain a path separator ("+"), it will 115 // be treated as if the prefix is "**". 116 // 117 // Examples: 118 // - Empty ("") will return all streams. 119 // - **/+/** will return all streams. 120 // - foo/bar/** will return all streams with the "foo/bar" prefix. 121 // - foo/bar/**/+/baz will return all streams beginning with the "foo/bar" 122 // prefix and named "baz" (e.g., "foo/bar/qux/lol/+/baz") 123 // - foo/bar/+/** will return all streams with a "foo/bar" prefix. 124 // - foo/*/+/baz will return all streams with a two-component prefix whose 125 // first value is "foo" and whose name is "baz". 126 // - foo/bar will return all streams whose name is "foo/bar". 127 // - */* will return all streams with two-component names. 128 func (c *Client) Query(ctx context.Context, project string, path string, o QueryOptions, cb QueryCallback) error { 129 req := logdog.QueryRequest{ 130 Project: project, 131 Path: path, 132 ContentType: o.ContentType, 133 Purged: o.Purged.queryValue(), 134 State: o.State, 135 } 136 if st := o.StreamType.queryValue(); st >= 0 { 137 req.StreamType = &logdog.QueryRequest_StreamTypeFilter{Value: st} 138 } 139 140 // Clone tags. 141 if len(o.Tags) > 0 { 142 req.Tags = make(map[string]string, len(o.Tags)) 143 for k, v := range o.Tags { 144 req.Tags[k] = v 145 } 146 } 147 148 // Iteratively query until either our query is done (Next is empty) or we are 149 // asked to stop via callback. 150 for { 151 resp, err := c.C.Query(ctx, &req) 152 if err != nil { 153 return normalizeError(err) 154 } 155 156 for _, s := range resp.Streams { 157 st := loadLogStream(resp.Project, types.StreamPath(s.Path), s.State, s.Desc) 158 if !cb(st) { 159 return nil 160 } 161 } 162 163 // Advance our query cursor. 164 if resp.Next == "" { 165 return nil 166 } 167 req.Next = resp.Next 168 } 169 }