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  }