github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/integration/resources/types.go (about) 1 // Copyright (c) 2021 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 // Package resources contains integration test resources for spinning up M3 22 // components. 23 package resources 24 25 import ( 26 "context" 27 "fmt" 28 "strconv" 29 "strings" 30 "sync" 31 "time" 32 33 "github.com/m3db/m3/src/aggregator/aggregator" 34 "github.com/m3db/m3/src/dbnode/generated/thrift/rpc" 35 "github.com/m3db/m3/src/query/api/v1/options" 36 "github.com/m3db/m3/src/query/generated/proto/admin" 37 "github.com/m3db/m3/src/query/generated/proto/prompb" 38 "github.com/m3db/m3/src/x/errors" 39 40 "github.com/prometheus/common/model" 41 ) 42 43 // ResponseVerifier is a function that checks if the query response is valid. 44 type ResponseVerifier func(int, map[string][]string, string, error) error 45 46 // GoalStateVerifier verifies that the given results are valid. 47 type GoalStateVerifier func(string, error) error 48 49 // Headers represents http headers. 50 type Headers map[string][]string 51 52 // Coordinator is a wrapper for a coordinator. It provides a wrapper on HTTP 53 // endpoints that expose cluster management APIs as well as read and write 54 // endpoints for series data. 55 // TODO: consider having this work on underlying structures. 56 type Coordinator interface { 57 Admin 58 59 // Start starts the coordinator instance. 60 Start() 61 // HostDetails returns this coordinator instance's host details. 62 HostDetails() (*InstanceInfo, error) 63 // ApplyKVUpdate applies a KV update. 64 ApplyKVUpdate(update string) error 65 // WriteCarbon writes a carbon metric datapoint at a given time. 66 WriteCarbon(port int, metric string, v float64, t time.Time) error 67 // WriteProm writes a prometheus metric. Takes tags/labels as a map for convenience. 68 WriteProm(name string, tags map[string]string, samples []prompb.Sample, headers Headers) error 69 // WritePromWithRequest executes a prometheus write request. Allows you to 70 // provide the request directly which is useful for batch metric requests. 71 WritePromWithRequest(writeRequest prompb.WriteRequest, headers Headers) error 72 // RunQuery runs the given query with a given verification function. 73 RunQuery(verifier ResponseVerifier, query string, headers Headers) error 74 // InstantQuery runs an instant query with provided headers 75 InstantQuery(req QueryRequest, headers Headers) (model.Vector, error) 76 // InstantQueryWithEngine runs an instant query with provided headers and the specified 77 // query engine. 78 InstantQueryWithEngine(req QueryRequest, engine options.QueryEngine, headers Headers) (model.Vector, error) 79 // RangeQuery runs a range query with provided headers 80 RangeQuery(req RangeQueryRequest, headers Headers) (model.Matrix, error) 81 // GraphiteQuery retrieves graphite raw data. 82 GraphiteQuery(GraphiteQueryRequest) ([]Datapoint, error) 83 // RangeQueryWithEngine runs a range query with provided headers and the specified 84 // query engine. 85 RangeQueryWithEngine(req RangeQueryRequest, engine options.QueryEngine, headers Headers) (model.Matrix, error) 86 // LabelNames return matching label names based on the request. 87 LabelNames(req LabelNamesRequest, headers Headers) (model.LabelNames, error) 88 // LabelValues returns matching label values based on the request. 89 LabelValues(req LabelValuesRequest, headers Headers) (model.LabelValues, error) 90 // Series returns matching series based on the request. 91 Series(req SeriesRequest, headers Headers) ([]model.Metric, error) 92 } 93 94 // Admin is a wrapper for admin functions. 95 type Admin interface { 96 // GetNamespace gets namespaces. 97 GetNamespace() (admin.NamespaceGetResponse, error) 98 // WaitForNamespace blocks until the given namespace is enabled. 99 // NB: if the name string is empty, this will instead 100 // check for a successful response. 101 WaitForNamespace(name string) error 102 // AddNamespace adds a namespace. 103 AddNamespace(admin.NamespaceAddRequest) (admin.NamespaceGetResponse, error) 104 // UpdateNamespace updates the namespace. 105 UpdateNamespace(admin.NamespaceUpdateRequest) (admin.NamespaceGetResponse, error) 106 // DeleteNamespace removes the namespace. 107 DeleteNamespace(namespaceID string) error 108 // CreateDatabase creates a database. 109 CreateDatabase(admin.DatabaseCreateRequest) (admin.DatabaseCreateResponse, error) 110 // GetPlacement gets placements. 111 GetPlacement(PlacementRequestOptions) (admin.PlacementGetResponse, error) 112 // InitPlacement initializes placements. 113 InitPlacement(PlacementRequestOptions, admin.PlacementInitRequest) (admin.PlacementGetResponse, error) 114 // DeleteAllPlacements deletes all placements for the service specified 115 // in the PlacementRequestOptions. 116 DeleteAllPlacements(PlacementRequestOptions) error 117 // WaitForInstances blocks until the given instance is available. 118 WaitForInstances(ids []string) error 119 // WaitForShardsReady waits until all shards gets ready. 120 WaitForShardsReady() error 121 // InitM3msgTopic initializes an m3msg topic. 122 InitM3msgTopic(M3msgTopicOptions, admin.TopicInitRequest) (admin.TopicGetResponse, error) 123 // GetM3msgTopic gets an m3msg topic. 124 GetM3msgTopic(M3msgTopicOptions) (admin.TopicGetResponse, error) 125 // AddM3msgTopicConsumer adds a consumer service to an m3msg topic. 126 AddM3msgTopicConsumer(M3msgTopicOptions, admin.TopicAddRequest) (admin.TopicGetResponse, error) 127 // WaitForClusterReady waits until the cluster is ready to receive reads and writes. 128 WaitForClusterReady() error 129 // Close closes the wrapper and releases any held resources, including 130 // deleting docker containers. 131 Close() error 132 } 133 134 // Node is a wrapper for a db node. It provides a wrapper on HTTP 135 // endpoints that expose cluster management APIs as well as read and write 136 // endpoints for series data. 137 // TODO: consider having this work on underlying structures. 138 type Node interface { 139 // Start starts the dbnode instance. 140 Start() 141 // HostDetails returns this node's host details on the given port. 142 HostDetails(port int) (*admin.Host, error) 143 // Health gives this node's health. 144 Health() (*rpc.NodeHealthResult_, error) 145 // WaitForBootstrap blocks until the node has bootstrapped. 146 WaitForBootstrap() error 147 // WritePoint writes a datapoint to the node directly. 148 WritePoint(req *rpc.WriteRequest) error 149 // WriteTaggedPoint writes a datapoint with tags to the node directly. 150 WriteTaggedPoint(req *rpc.WriteTaggedRequest) error 151 // WriteTaggedBatchRaw writes a batch of writes to the node directly. 152 WriteTaggedBatchRaw(req *rpc.WriteTaggedBatchRawRequest) error 153 // AggregateTiles starts tiles aggregation, waits until it will complete 154 // and returns the amount of aggregated tiles. 155 AggregateTiles(req *rpc.AggregateTilesRequest) (int64, error) 156 // Fetch fetches datapoints. 157 Fetch(req *rpc.FetchRequest) (*rpc.FetchResult_, error) 158 // FetchTagged fetches datapoints by tag. 159 FetchTagged(req *rpc.FetchTaggedRequest) (*rpc.FetchTaggedResult_, error) 160 // Exec executes the given commands on the node container, returning 161 // stdout and stderr from the container. 162 Exec(commands ...string) (string, error) 163 // GoalStateExec executes the given commands on the node container, retrying 164 // until applying the verifier returns no error or the default timeout. 165 GoalStateExec(verifier GoalStateVerifier, commands ...string) error 166 // Restart restarts this container. 167 Restart() error 168 // Close closes the wrapper and releases any held resources, including 169 // deleting docker containers. 170 Close() error 171 } 172 173 // Aggregator is an aggregator instance. 174 type Aggregator interface { 175 // Start starts the aggregator instance. 176 Start() 177 // HostDetails returns this aggregator instance's host details. 178 HostDetails() (*InstanceInfo, error) 179 // IsHealthy determines whether an instance is healthy. 180 IsHealthy() error 181 // Status returns the instance status. 182 Status() (aggregator.RuntimeStatus, error) 183 // Resign asks an aggregator instance to give up its current leader role if applicable. 184 Resign() error 185 // Close closes the wrapper and releases any held resources, including 186 // deleting docker containers. 187 Close() error 188 } 189 190 // M3Resources represents a set of test M3 components. 191 type M3Resources interface { 192 // Start starts all the M3 components. 193 Start() 194 // Cleanup cleans up after each started component. 195 Cleanup() error 196 // Nodes returns all node resources. 197 Nodes() Nodes 198 // Coordinator returns the coordinator resource. 199 Coordinator() Coordinator 200 // Aggregators returns all aggregator resources. 201 Aggregators() Aggregators 202 } 203 204 // ExternalResources represents an external (i.e. non-M3) 205 // resource that we'd like to be able to spin up for an 206 // integration test. 207 type ExternalResources interface { 208 // Setup sets up the external resource so that it's ready 209 // for use. 210 Setup(ctx context.Context) error 211 212 // Close stops and cleans up all the resources associated with 213 // the external resource. 214 Close(ctx context.Context) error 215 } 216 217 // InstanceInfo represents the host information for an instance. 218 type InstanceInfo struct { 219 // ID is the name of the host. It can be hostname or UUID or any other string. 220 ID string 221 // Env specifies the zone the host resides in. 222 Env string 223 // Zone specifies the zone the host resides in. 224 Zone string 225 // Address can be IP address or hostname, this is used to connect to the host. 226 Address string 227 // M3msgAddress is the address of the m3msg server if there is one. 228 M3msgAddress string 229 // Port is the port number. 230 Port uint32 231 // Port is the port of the m3msg server if there is one. 232 M3msgPort uint32 233 } 234 235 // Nodes is a slice of nodes. 236 type Nodes []Node 237 238 // WaitForHealthy waits for each Node in Nodes to be healthy 239 // and bootstrapped before returning. 240 func (n Nodes) WaitForHealthy() error { 241 var ( 242 multiErr errors.MultiError 243 mu sync.Mutex 244 wg sync.WaitGroup 245 ) 246 247 for _, node := range n { 248 wg.Add(1) 249 node := node 250 go func() { 251 defer wg.Done() 252 err := node.WaitForBootstrap() 253 if err != nil { 254 mu.Lock() 255 multiErr = multiErr.Add(err) 256 mu.Unlock() 257 } 258 }() 259 } 260 261 wg.Wait() 262 return multiErr.FinalError() 263 } 264 265 // Aggregators is a slice of aggregators. 266 type Aggregators []Aggregator 267 268 // WaitForHealthy waits for each Aggregator in Aggregators to be healthy 269 func (a Aggregators) WaitForHealthy() error { 270 var ( 271 multiErr errors.MultiError 272 mu sync.Mutex 273 wg sync.WaitGroup 274 ) 275 276 for _, agg := range a { 277 wg.Add(1) 278 agg := agg 279 go func() { 280 defer wg.Done() 281 err := Retry(agg.IsHealthy) 282 if err != nil { 283 mu.Lock() 284 multiErr = multiErr.Add(err) 285 mu.Unlock() 286 } 287 }() 288 } 289 290 wg.Wait() 291 return multiErr.FinalError() 292 } 293 294 // QueryRequest represents an instant query request 295 type QueryRequest struct { 296 // Query is the Prometheus expression query string. 297 Query string 298 // Time is the evaluation timestamp. It is optional. 299 Time *time.Time 300 } 301 302 // RangeQueryRequest represents a range query request 303 type RangeQueryRequest struct { 304 // Query is the Prometheus expression query string. 305 Query string 306 // Start is the start timestamp of the query range. The default value is time.Now(). 307 Start time.Time 308 // End is the end timestamp of the query range. The default value is time.Now(). 309 End time.Time 310 // Step is the query resolution step width. It is default to 15 seconds. 311 Step time.Duration 312 } 313 314 // MetadataRequest contains the parameters for making API requests related to metadata. 315 type MetadataRequest struct { 316 // Start is the start timestamp of labels to include. 317 Start time.Time 318 // End is the end timestamp of labels to include. 319 End time.Time 320 // Match is the series selector that selects series to read label names from. 321 Match string 322 } 323 324 // LabelNamesRequest contains the parameters for making label names API calls. 325 type LabelNamesRequest struct { 326 MetadataRequest 327 } 328 329 // LabelValuesRequest contains the parameters for making label values API calls. 330 type LabelValuesRequest struct { 331 MetadataRequest 332 333 // LabelName is the name of the label to retrieve values for. 334 LabelName string 335 } 336 337 // SeriesRequest contains the parameters for making series API calls. 338 type SeriesRequest struct { 339 MetadataRequest 340 } 341 342 func (m *MetadataRequest) String() string { 343 var ( 344 start string 345 end string 346 parts []string 347 ) 348 if !m.Start.IsZero() { 349 start = strconv.Itoa(int(m.Start.Unix())) 350 parts = append(parts, fmt.Sprintf("start=%v", start)) 351 } 352 if !m.End.IsZero() { 353 end = strconv.Itoa(int(m.End.Unix())) 354 parts = append(parts, fmt.Sprintf("end=%v", end)) 355 } 356 if m.Match != "" { 357 parts = append(parts, fmt.Sprintf("match[]=%v", m.Match)) 358 } 359 360 return strings.Join(parts, "&") 361 } 362 363 // GraphiteQueryRequest represents a graphite render query request. 364 type GraphiteQueryRequest struct { 365 // Target speicifies a path identifying one or several metrics. 366 Target string 367 // From is the beginning of the time period to query. 368 From time.Time 369 // Until is the end of the time period to query. 370 Until time.Time 371 } 372 373 // Datapoint is a data point returned by the graphite render query. 374 type Datapoint struct { 375 // Value is the value of the datapoint. 376 Value *float64 377 // Timestamp is the timestamp (in seconds) of the datapoint. 378 Timestamp int64 379 }