github.com/m3db/m3@v1.5.0/src/cmd/tools/dtest/config/config.go (about) 1 // Copyright (c) 2018 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 config 22 23 import ( 24 "fmt" 25 "io/ioutil" 26 "time" 27 28 etcdclient "github.com/m3db/m3/src/cluster/client/etcd" 29 "github.com/m3db/m3/src/cluster/placement" 30 m3emnode "github.com/m3db/m3/src/dbnode/x/m3em/node" 31 "github.com/m3db/m3/src/m3em/cluster" 32 "github.com/m3db/m3/src/m3em/generated/proto/m3em" 33 "github.com/m3db/m3/src/m3em/node" 34 "github.com/m3db/m3/src/m3em/x/grpc" 35 xconfig "github.com/m3db/m3/src/x/config" 36 37 "go.uber.org/zap" 38 "google.golang.org/grpc" 39 "google.golang.org/grpc/credentials" 40 ) 41 42 // Configuration is a collection of knobs to control test behavior 43 type Configuration struct { 44 DTest DTestConfig `yaml:"dtest"` 45 M3EM M3EMConfig `yaml:"m3em"` 46 KV etcdclient.Configuration `yaml:"kv"` 47 } 48 49 // DTestConfig is a collection of DTest configs 50 type DTestConfig struct { 51 DebugPort int `yaml:"debugPort" validate:"nonzero"` 52 BootstrapTimeout time.Duration `yaml:"bootstrapTimeout" validate:"nonzero"` 53 BootstrapReportInterval time.Duration `yaml:"bootstrapReportInterval" validate:"nonzero"` 54 NodePort int `yaml:"nodePort" validate:"nonzero"` 55 ServiceID string `yaml:"serviceID" validate:"nonzero"` 56 DataDir string `yaml:"dataDir" validate:"nonzero"` // path relative to m3em agent working directory 57 Seeds []SeedConfig `yaml:"seeds"` 58 Instances []PlacementInstance `yaml:"instances" validate:"min=1"` 59 } 60 61 // SeedConfig is a collection of Seed Data configurations 62 type SeedConfig struct { 63 Namespace string `yaml:"namespace" validate:"nonzero"` 64 LocalShardNum uint32 `yaml:"localShardNum" validate:"nonzero"` 65 Retention time.Duration `yaml:"retention" validate:"nonzero"` 66 BlockSize time.Duration `yaml:"blockSize" validate:"nonzero"` 67 Delay time.Duration `yaml:"delay"` 68 } 69 70 // PlacementInstance is a config for a placement instance. 71 type PlacementInstance struct { 72 ID string `yaml:"id" validate:"nonzero"` 73 Rack string `yaml:"rack" validate:"nonzero"` 74 Zone string `yaml:"zone" validate:"nonzero"` 75 Weight uint32 `yaml:"weight" validate:"nonzero"` 76 Hostname string `yaml:"hostname" validate:"nonzero"` 77 } 78 79 // M3EMConfig is a list of m3em environment settings 80 type M3EMConfig struct { 81 AgentPort int `yaml:"agentPort" validate:"nonzero"` 82 AgentTLS *TLSConfiguration `yaml:"agentTLS"` 83 HeartbeatPort int `yaml:"heartbeatPort" validate:"nonzero"` 84 Node node.Configuration `yaml:"node"` 85 Cluster cluster.Configuration `yaml:"cluster"` 86 } 87 88 // TLSConfiguration are the resources required for TLS Communication 89 type TLSConfiguration struct { 90 ServerName string `yaml:"serverName" validate:"nonzero"` 91 CACrtPath string `yaml:"caCrt" validate:"nonzero"` 92 ClientCrtPath string `yaml:"clientCrt" validate:"nonzero"` 93 ClientKeyPath string `yaml:"clientKey" validate:"nonzero"` 94 } 95 96 // Credentials returns the TransportCredentials corresponding to the provided struct 97 func (t TLSConfiguration) Credentials() (credentials.TransportCredentials, error) { 98 caCrt, err := ioutil.ReadFile(t.CACrtPath) 99 if err != nil { 100 return nil, err 101 } 102 103 clientCrt, err := ioutil.ReadFile(t.ClientCrtPath) 104 if err != nil { 105 return nil, err 106 } 107 108 clientKey, err := ioutil.ReadFile(t.ClientKeyPath) 109 if err != nil { 110 return nil, err 111 } 112 113 return xgrpc.NewClientCredentials(t.ServerName, caCrt, clientCrt, clientKey) 114 } 115 116 // New constructs a Configuration object from the path specified 117 func New(m3emConfigPath string) (*Configuration, error) { 118 var conf Configuration 119 if err := xconfig.LoadFile(&conf, m3emConfigPath, xconfig.Options{}); err != nil { 120 return nil, err 121 } 122 123 return &conf, nil 124 } 125 126 // Zone returns the zone configured for kv, and the instances if they are all the same; 127 // it returns an error if they're not. 128 func (c *Configuration) Zone() (string, error) { 129 kvZone := c.KV.Zone 130 for _, inst := range c.DTest.Instances { 131 if kvZone != inst.Zone { 132 return "", fmt.Errorf("instance has zone %s which differs from kv zone %s", kvZone, inst.Zone) 133 } 134 } 135 return kvZone, nil 136 } 137 138 // Nodes returns a slice of m3emnode.Nodes per the config provided 139 func (c *Configuration) Nodes(opts node.Options, numNodes int) ([]m3emnode.Node, error) { 140 // use all nodes if numNodes is zero 141 if numNodes <= 0 { 142 numNodes = len(c.DTest.Instances) 143 } 144 145 var ( 146 logger = opts.InstrumentOptions().Logger() 147 nodes = make([]m3emnode.Node, 0, len(c.DTest.Instances)) 148 nodeNum = 0 149 ) 150 151 for _, inst := range c.DTest.Instances { 152 if nodeNum >= numNodes { 153 break 154 } 155 156 pi := inst.newServicesPlacementInstance(c.DTest.NodePort) 157 clientFn, err := inst.operatorClientFn(c.M3EM.AgentPort, c.M3EM.AgentTLS) 158 if err != nil { 159 return nil, fmt.Errorf("unable to create operationClientFn for %+v, error: %v", inst, err) 160 } 161 162 newOpts := opts. 163 SetOperatorClientFn(clientFn). 164 SetInstrumentOptions(opts.InstrumentOptions().SetLogger( 165 logger.With(zap.String("host", inst.Hostname)))) 166 167 svcNode, err := node.New(pi, newOpts) 168 if err != nil { 169 return nil, fmt.Errorf("unable to create service node for %+v, error: %v", inst, err) 170 } 171 172 nodeOpts := m3emnode.NewOptions(newOpts.InstrumentOptions()).SetNodeOptions(newOpts) 173 n, err := m3emnode.New(svcNode, nodeOpts) 174 if err != nil { 175 return nil, fmt.Errorf("unable to create m3emnode for %+v, error: %v", inst, err) 176 } 177 nodes = append(nodes, n) 178 179 nodeNum++ 180 } 181 182 return nodes, nil 183 } 184 185 func (pi *PlacementInstance) operatorClientFn(agentPort int, tlsConfig *TLSConfiguration) (node.OperatorClientFn, error) { 186 agentEndpoint := fmt.Sprintf("%s:%d", pi.Hostname, agentPort) 187 188 dialOpt := grpc.WithInsecure() 189 if tlsConfig != nil { 190 tc, err := tlsConfig.Credentials() 191 if err != nil { 192 return nil, err 193 } 194 dialOpt = grpc.WithTransportCredentials(tc) 195 } 196 197 return func() (*grpc.ClientConn, m3em.OperatorClient, error) { 198 conn, err := grpc.Dial(agentEndpoint, dialOpt) 199 if err != nil { 200 return nil, nil, err 201 } 202 return conn, m3em.NewOperatorClient(conn), nil 203 }, nil 204 } 205 206 func (pi *PlacementInstance) newServicesPlacementInstance(nodePort int) placement.Instance { 207 endpoint := fmt.Sprintf("%s:%d", pi.Hostname, nodePort) 208 return placement.NewInstance(). 209 SetID(pi.ID). 210 SetIsolationGroup(pi.Rack). 211 SetZone(pi.Zone). 212 SetEndpoint(endpoint). 213 SetWeight(pi.Weight) 214 }