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 }