github.com/m3db/m3@v1.5.0/src/integration/resources/docker/dbnode.go (about)

     1  // Copyright (c) 2020 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 docker
    22  
    23  import (
    24  	"fmt"
    25  	"strings"
    26  
    27  	"github.com/m3db/m3/src/dbnode/generated/thrift/rpc"
    28  	"github.com/m3db/m3/src/dbnode/integration"
    29  	"github.com/m3db/m3/src/integration/resources"
    30  	"github.com/m3db/m3/src/query/generated/proto/admin"
    31  
    32  	"github.com/ory/dockertest/v3"
    33  	"github.com/ory/dockertest/v3/docker"
    34  	"go.uber.org/zap"
    35  )
    36  
    37  const (
    38  	defaultDBNodeSource        = "dbnode"
    39  	defaultDBNodeContainerName = "dbnode01"
    40  )
    41  
    42  var (
    43  	defaultDBNodePortList = []int{2379, 2380, 9000, 9001, 9002, 9003, 9004}
    44  
    45  	defaultDBNodeOptions = ResourceOptions{
    46  		Source:        defaultDBNodeSource,
    47  		ContainerName: defaultDBNodeContainerName,
    48  		PortList:      defaultDBNodePortList,
    49  	}
    50  )
    51  
    52  type dbNode struct {
    53  	tchanClient *integration.TestTChannelClient
    54  	resource    *Resource
    55  }
    56  
    57  func newDockerHTTPNode(
    58  	pool *dockertest.Pool,
    59  	opts ResourceOptions,
    60  ) (resources.Node, error) {
    61  	opts = opts.withDefaults(defaultDBNodeOptions)
    62  	resource, err := NewDockerResource(pool, opts)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	completed := false
    68  	defer func() {
    69  		if !completed {
    70  			_ = resource.Close()
    71  		}
    72  	}()
    73  
    74  	addr := resource.resource.GetHostPort("9000/tcp")
    75  	tchanClient, err := integration.NewTChannelClient("client", addr)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	resource.logger.Info("set up tchanClient", zap.String("node_addr", addr))
    81  	completed = true
    82  	return &dbNode{
    83  		tchanClient: tchanClient,
    84  		resource:    resource,
    85  	}, nil
    86  }
    87  
    88  func (c *dbNode) Start() {
    89  	// noop as docker container should already be started
    90  }
    91  
    92  func (c *dbNode) HostDetails(p int) (*admin.Host, error) {
    93  	var network docker.ContainerNetwork
    94  	for _, n := range c.resource.resource.Container.NetworkSettings.Networks { // nolint: gocritic
    95  		network = n
    96  	}
    97  
    98  	host := strings.TrimLeft(c.resource.resource.Container.Name, "/")
    99  	return &admin.Host{
   100  		Id:             host,
   101  		IsolationGroup: "rack-a-" + c.resource.resource.Container.Name,
   102  		Zone:           "embedded",
   103  		Weight:         1024,
   104  		Address:        network.IPAddress,
   105  		Port:           uint32(p),
   106  	}, nil
   107  }
   108  
   109  func (c *dbNode) Health() (*rpc.NodeHealthResult_, error) {
   110  	if c.resource.closed {
   111  		return nil, errClosed
   112  	}
   113  
   114  	logger := c.resource.logger.With(resources.ZapMethod("health"))
   115  	res, err := c.tchanClient.TChannelClientHealth(timeout)
   116  	if err != nil {
   117  		logger.Error("failed get", zap.Error(err), zap.Any("res", res))
   118  	}
   119  
   120  	return res, err
   121  }
   122  
   123  func (c *dbNode) WaitForBootstrap() error {
   124  	if c.resource.closed {
   125  		return errClosed
   126  	}
   127  
   128  	logger := c.resource.logger.With(resources.ZapMethod("waitForBootstrap"))
   129  	return c.resource.pool.Retry(func() error {
   130  		health, err := c.Health()
   131  		if err != nil {
   132  			return err
   133  		}
   134  
   135  		if !health.GetBootstrapped() {
   136  			err = fmt.Errorf("not bootstrapped")
   137  			logger.Error("could not get health", zap.Error(err))
   138  			return err
   139  		}
   140  
   141  		return nil
   142  	})
   143  }
   144  
   145  func (c *dbNode) WritePoint(req *rpc.WriteRequest) error {
   146  	if c.resource.closed {
   147  		return errClosed
   148  	}
   149  
   150  	logger := c.resource.logger.With(resources.ZapMethod("write"))
   151  	err := c.tchanClient.TChannelClientWrite(timeout, req)
   152  	if err != nil {
   153  		logger.Error("could not write", zap.Error(err))
   154  		return err
   155  	}
   156  
   157  	logger.Info("wrote")
   158  	return nil
   159  }
   160  
   161  func (c *dbNode) WriteTaggedPoint(req *rpc.WriteTaggedRequest) error {
   162  	if c.resource.closed {
   163  		return errClosed
   164  	}
   165  
   166  	logger := c.resource.logger.With(resources.ZapMethod("write-tagged"))
   167  	err := c.tchanClient.TChannelClientWriteTagged(timeout, req)
   168  	if err != nil {
   169  		logger.Error("could not write-tagged", zap.Error(err))
   170  		return err
   171  	}
   172  
   173  	logger.Info("wrote")
   174  	return nil
   175  }
   176  
   177  // WriteTaggedBatchRaw writes a batch of writes to the node directly.
   178  func (c *dbNode) WriteTaggedBatchRaw(req *rpc.WriteTaggedBatchRawRequest) error {
   179  	if c.resource.closed {
   180  		return errClosed
   181  	}
   182  
   183  	logger := c.resource.logger.With(resources.ZapMethod("write-tagged-batch-raw"))
   184  	err := c.tchanClient.TChannelClientWriteTaggedBatchRaw(timeout, req)
   185  	if err != nil {
   186  		logger.Error("writeTaggedBatchRaw call failed", zap.Error(err))
   187  		return err
   188  	}
   189  
   190  	logger.Info("wrote")
   191  	return nil
   192  }
   193  
   194  func (c *dbNode) AggregateTiles(req *rpc.AggregateTilesRequest) (int64, error) {
   195  	if c.resource.closed {
   196  		return 0, errClosed
   197  	}
   198  
   199  	logger := c.resource.logger.With(resources.ZapMethod("aggregate-tiles"))
   200  	rsp, err := c.tchanClient.TChannelClientAggregateTiles(timeout, req)
   201  	if err != nil {
   202  		logger.Error("could not aggregate tiles", zap.Error(err))
   203  		return 0, err
   204  	}
   205  
   206  	logger.Info("wrote")
   207  	return rsp.ProcessedTileCount, nil
   208  }
   209  
   210  func (c *dbNode) Fetch(req *rpc.FetchRequest) (*rpc.FetchResult_, error) {
   211  	if c.resource.closed {
   212  		return nil, errClosed
   213  	}
   214  
   215  	logger := c.resource.logger.With(resources.ZapMethod("fetch"))
   216  	dps, err := c.tchanClient.TChannelClientFetch(timeout, req)
   217  	if err != nil {
   218  		logger.Error("could not fetch", zap.Error(err))
   219  		return nil, err
   220  	}
   221  
   222  	logger.Info("fetched", zap.Int("num_points", len(dps.GetDatapoints())))
   223  	return dps, nil
   224  }
   225  
   226  func (c *dbNode) FetchTagged(req *rpc.FetchTaggedRequest) (*rpc.FetchTaggedResult_, error) {
   227  	if c.resource.closed {
   228  		return nil, errClosed
   229  	}
   230  
   231  	logger := c.resource.logger.With(resources.ZapMethod("fetchtagged"))
   232  	result, err := c.tchanClient.TChannelClientFetchTagged(timeout, req)
   233  	if err != nil {
   234  		logger.Error("could not fetch", zap.Error(err))
   235  		return nil, err
   236  	}
   237  
   238  	logger.Info("fetched", zap.Int("series_count", len(result.GetElements())))
   239  	return result, nil
   240  }
   241  
   242  func (c *dbNode) Restart() error {
   243  	if c.resource.closed {
   244  		return errClosed
   245  	}
   246  
   247  	cName := c.resource.resource.Container.Name
   248  	logger := c.resource.logger.With(resources.ZapMethod("restart"))
   249  	logger.Info("restarting container", zap.String("container", cName))
   250  	err := c.resource.pool.Client.RestartContainer(cName, 60)
   251  	if err != nil {
   252  		logger.Error("could not restart", zap.Error(err))
   253  		return err
   254  	}
   255  
   256  	return nil
   257  }
   258  
   259  func (c *dbNode) Exec(commands ...string) (string, error) {
   260  	if c.resource.closed {
   261  		return "", errClosed
   262  	}
   263  
   264  	return c.resource.Exec(commands...)
   265  }
   266  
   267  func (c *dbNode) GoalStateExec(
   268  	verifier resources.GoalStateVerifier,
   269  	commands ...string,
   270  ) error {
   271  	if c.resource.closed {
   272  		return errClosed
   273  	}
   274  
   275  	return c.resource.GoalStateExec(verifier, commands...)
   276  }
   277  
   278  func (c *dbNode) Close() error {
   279  	if c.resource.closed {
   280  		return errClosed
   281  	}
   282  
   283  	return c.resource.Close()
   284  }