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  }