github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/cluster/integration/etcd/etcd.go (about) 1 // Copyright (c) 2017 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 etcd 22 23 import ( 24 "context" 25 "encoding/json" 26 "fmt" 27 "io/ioutil" 28 "net/http" 29 "strings" 30 31 "github.com/m3db/m3/src/cluster/client" 32 etcdclient "github.com/m3db/m3/src/cluster/client/etcd" 33 "github.com/m3db/m3/src/cluster/services" 34 "github.com/m3db/m3/src/integration/resources/docker/dockerexternal" 35 "github.com/m3db/m3/src/x/instrument" 36 37 "github.com/ory/dockertest/v3" 38 ) 39 40 type embeddedKV struct { 41 etcd *dockerexternal.EtcdNode 42 opts Options 43 dir string 44 } 45 46 // New creates a new EmbeddedKV 47 func New(opts Options) (EmbeddedKV, error) { 48 dir, err := ioutil.TempDir("", opts.Dir()) 49 if err != nil { 50 return nil, err 51 } 52 53 pool, err := dockertest.NewPool("") 54 if err != nil { 55 return nil, fmt.Errorf("constructing dockertest.Pool for EmbeddedKV: %w", err) 56 } 57 e, err := dockerexternal.NewEtcd(pool, instrument.NewOptions()) 58 if err != nil { 59 return nil, fmt.Errorf("unable to start etcd, err: %v", err) 60 } 61 return &embeddedKV{ 62 etcd: e, 63 opts: opts, 64 dir: dir, 65 }, nil 66 } 67 68 func (e *embeddedKV) Close() error { 69 return e.etcd.Close(context.TODO()) 70 } 71 72 func (e *embeddedKV) Start() error { 73 timeout := e.opts.InitTimeout() 74 ctx, cancel := context.WithTimeout(context.Background(), timeout) 75 defer cancel() 76 77 return e.etcd.Setup(ctx) 78 } 79 80 type versionResponse struct { 81 Version string `json:"etcdcluster"` 82 } 83 84 func version3Available(endpoint string) bool { 85 resp, err := http.Get(endpoint) 86 if err != nil { 87 return false 88 } 89 if resp.StatusCode != 200 { 90 return false 91 } 92 defer resp.Body.Close() 93 94 decoder := json.NewDecoder(resp.Body) 95 var data versionResponse 96 err = decoder.Decode(&data) 97 if err != nil { 98 return false 99 } 100 101 return strings.Index(data.Version, "3.") == 0 102 } 103 104 func (e *embeddedKV) Endpoints() []string { 105 return []string{e.etcd.Address()} 106 } 107 108 func (e *embeddedKV) ConfigServiceClient(fns ...ClientOptFn) (client.Client, error) { 109 eopts := etcdclient.NewOptions(). 110 SetInstrumentOptions(e.opts.InstrumentOptions()). 111 SetServicesOptions(services.NewOptions().SetInitTimeout(e.opts.InitTimeout())). 112 SetClusters([]etcdclient.Cluster{ 113 etcdclient.NewCluster().SetZone(e.opts.Zone()). 114 SetEndpoints(e.Endpoints()). 115 SetAutoSyncInterval(-1), 116 }). 117 SetService(e.opts.ServiceID()). 118 SetEnv(e.opts.Environment()). 119 SetZone(e.opts.Zone()) 120 for _, fn := range fns { 121 eopts = fn(eopts) 122 } 123 return etcdclient.NewConfigServiceClient(eopts) 124 }