vitess.io/vitess@v0.16.2/go/vt/topo/etcd2topo/server.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 /* 18 Package etcd2topo implements topo.Server with etcd as the backend. 19 20 We expect the following behavior from the etcd client library: 21 22 - Get and Delete return ErrorCodeKeyNotFound if the node doesn't exist. 23 - Create returns ErrorCodeNodeExist if the node already exists. 24 - Intermediate directories are always created automatically if necessary. 25 - Set returns ErrorCodeKeyNotFound if the node doesn't exist already. 26 - It returns ErrorCodeTestFailed if the provided version index doesn't match. 27 28 We follow these conventions within this package: 29 30 - Call convertError(err) on any errors returned from the etcd client library. 31 Functions defined in this package can be assumed to have already converted 32 errors as necessary. 33 */ 34 package etcd2topo 35 36 import ( 37 "crypto/tls" 38 "crypto/x509" 39 "strings" 40 "time" 41 42 "github.com/spf13/pflag" 43 "go.etcd.io/etcd/client/pkg/v3/tlsutil" 44 "google.golang.org/grpc" 45 46 clientv3 "go.etcd.io/etcd/client/v3" 47 48 "vitess.io/vitess/go/vt/servenv" 49 "vitess.io/vitess/go/vt/topo" 50 ) 51 52 var ( 53 clientCertPath string 54 clientKeyPath string 55 serverCaPath string 56 ) 57 58 // Factory is the consul topo.Factory implementation. 59 type Factory struct{} 60 61 // HasGlobalReadOnlyCell is part of the topo.Factory interface. 62 func (f Factory) HasGlobalReadOnlyCell(serverAddr, root string) bool { 63 return false 64 } 65 66 // Create is part of the topo.Factory interface. 67 func (f Factory) Create(cell, serverAddr, root string) (topo.Conn, error) { 68 return NewServer(serverAddr, root) 69 } 70 71 // Server is the implementation of topo.Server for etcd. 72 type Server struct { 73 // cli is the v3 client. 74 cli *clientv3.Client 75 76 // root is the root path for this client. 77 root string 78 79 running chan struct{} 80 } 81 82 func init() { 83 for _, cmd := range topo.FlagBinaries { 84 servenv.OnParseFor(cmd, registerEtcd2TopoFlags) 85 } 86 topo.RegisterFactory("etcd2", Factory{}) 87 } 88 89 func registerEtcd2TopoFlags(fs *pflag.FlagSet) { 90 fs.StringVar(&clientCertPath, "topo_etcd_tls_cert", clientCertPath, "path to the client cert to use to connect to the etcd topo server, requires topo_etcd_tls_key, enables TLS") 91 fs.StringVar(&clientKeyPath, "topo_etcd_tls_key", clientKeyPath, "path to the client key to use to connect to the etcd topo server, enables TLS") 92 fs.StringVar(&serverCaPath, "topo_etcd_tls_ca", serverCaPath, "path to the ca to use to validate the server cert when connecting to the etcd topo server") 93 } 94 95 // Close implements topo.Server.Close. 96 // It will nil out the global and cells fields, so any attempt to 97 // re-use this server will panic. 98 func (s *Server) Close() { 99 close(s.running) 100 s.cli.Close() 101 s.cli = nil 102 } 103 104 func newTLSConfig(certPath, keyPath, caPath string) (*tls.Config, error) { 105 var tlscfg *tls.Config 106 // If TLS is enabled, attach TLS config info. 107 if certPath != "" && keyPath != "" { 108 var ( 109 cert *tls.Certificate 110 cp *x509.CertPool 111 err error 112 ) 113 114 cert, err = tlsutil.NewCert(certPath, keyPath, nil) 115 if err != nil { 116 return nil, err 117 } 118 119 if caPath != "" { 120 cp, err = tlsutil.NewCertPool([]string{caPath}) 121 if err != nil { 122 return nil, err 123 } 124 } 125 126 tlscfg = &tls.Config{ 127 MinVersion: tls.VersionTLS12, 128 RootCAs: cp, 129 InsecureSkipVerify: false, 130 } 131 if cert != nil { 132 tlscfg.Certificates = []tls.Certificate{*cert} 133 } 134 } 135 return tlscfg, nil 136 } 137 138 // NewServerWithOpts creates a new server with the provided TLS options 139 func NewServerWithOpts(serverAddr, root, certPath, keyPath, caPath string) (*Server, error) { 140 // TODO: Rename this to NewServer and change NewServer to a name that signifies it uses the process-wide TLS settings. 141 config := clientv3.Config{ 142 Endpoints: strings.Split(serverAddr, ","), 143 DialTimeout: 5 * time.Second, 144 DialOptions: []grpc.DialOption{grpc.WithBlock()}, 145 } 146 147 tlscfg, err := newTLSConfig(certPath, keyPath, caPath) 148 if err != nil { 149 return nil, err 150 } 151 152 config.TLS = tlscfg 153 154 cli, err := clientv3.New(config) 155 if err != nil { 156 return nil, err 157 } 158 159 return &Server{ 160 cli: cli, 161 root: root, 162 running: make(chan struct{}), 163 }, nil 164 } 165 166 // NewServer returns a new etcdtopo.Server. 167 func NewServer(serverAddr, root string) (*Server, error) { 168 // TODO: Rename this to a name to signifies this function uses the process-wide TLS settings. 169 170 return NewServerWithOpts(serverAddr, root, clientCertPath, clientKeyPath, serverCaPath) 171 }