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