github.com/erda-project/erda-infra@v1.0.9/providers/etcd/etcd.go (about) 1 // Copyright (c) 2021 Terminus, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package etcd 16 17 import ( 18 "crypto/tls" 19 "crypto/x509" 20 "io/ioutil" 21 "os" 22 "reflect" 23 "strings" 24 "time" 25 26 "github.com/coreos/etcd/clientv3" 27 "github.com/erda-project/erda-infra/base/logs" 28 "github.com/erda-project/erda-infra/base/servicehub" 29 "google.golang.org/grpc" 30 ) 31 32 // Interface . 33 type Interface interface { 34 Connect() (*clientv3.Client, error) 35 Client() *clientv3.Client 36 Timeout() time.Duration 37 } 38 39 type config struct { 40 Endpoints string `file:"endpoints" env:"ETCD_ENDPOINTS"` 41 Timeout time.Duration `file:"timeout" default:"5s"` 42 TLS struct { 43 CertFile string `file:"cert_file"` 44 CertKeyFile string `file:"cert_key_file"` 45 CaFile string `file:"ca_file"` 46 } `file:"tls"` 47 SyncConnect bool `file:"sync_connect" default:"true"` 48 Username string `file:"username"` 49 Password string `file:"password"` 50 } 51 52 var clientType = reflect.TypeOf((*clientv3.Client)(nil)) 53 54 type provider struct { 55 Cfg *config 56 Log logs.Logger 57 client *clientv3.Client 58 tlsConfig *tls.Config 59 } 60 61 func (p *provider) Init(ctx servicehub.Context) error { 62 err := p.initTLSConfig() 63 if err != nil { 64 return err 65 } 66 client, err := p.Connect() 67 if err != nil { 68 return err 69 } 70 p.client = client 71 return nil 72 } 73 74 func (p *provider) Connect() (*clientv3.Client, error) { 75 config := clientv3.Config{ 76 Endpoints: strings.Split(p.Cfg.Endpoints, ","), 77 DialTimeout: p.Cfg.Timeout, 78 TLS: p.tlsConfig, 79 Username: p.Cfg.Username, 80 Password: p.Cfg.Password, 81 } 82 if p.Cfg.SyncConnect { 83 config.DialOptions = append(config.DialOptions, grpc.WithBlock()) 84 } 85 return clientv3.New(config) 86 } 87 88 func (p *provider) Client() *clientv3.Client { return p.client } 89 90 func (p *provider) Timeout() time.Duration { return p.Cfg.Timeout } 91 92 func (p *provider) initTLSConfig() error { 93 if len(p.Cfg.TLS.CertFile) > 0 || len(p.Cfg.TLS.CertKeyFile) > 0 { 94 cfg, err := readTLSConfig(p.Cfg.TLS.CertFile, p.Cfg.TLS.CertKeyFile, p.Cfg.TLS.CaFile) 95 if err != nil { 96 if os.IsNotExist(err) { 97 p.Log.Warnf("fail to load tls files: %s", err) 98 return nil 99 } 100 return err 101 } 102 p.tlsConfig = cfg 103 } 104 return nil 105 } 106 107 func readTLSConfig(certFile, certKeyFile, caFile string) (*tls.Config, error) { 108 cert, err := tls.LoadX509KeyPair(certFile, certKeyFile) 109 if err != nil { 110 return nil, err 111 } 112 caData, err := ioutil.ReadFile(caFile) 113 if err != nil { 114 return nil, err 115 } 116 pool := x509.NewCertPool() 117 pool.AppendCertsFromPEM(caData) 118 return &tls.Config{ 119 Certificates: []tls.Certificate{cert}, 120 RootCAs: pool, 121 }, nil 122 } 123 124 // Provide . 125 func (p *provider) Provide(ctx servicehub.DependencyContext, args ...interface{}) interface{} { 126 if ctx.Type() == clientType || ctx.Service() == "etcd-client" { 127 return p.client 128 } 129 return p 130 } 131 132 func init() { 133 servicehub.Register("etcd", &servicehub.Spec{ 134 Services: []string{"etcd", "etcd-client"}, 135 Types: []reflect.Type{ 136 reflect.TypeOf((*Interface)(nil)).Elem(), 137 clientType, 138 }, 139 Description: "etcd", 140 ConfigFunc: func() interface{} { return &config{} }, 141 Creator: func() servicehub.Provider { 142 return &provider{} 143 }, 144 }) 145 }