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  }