github.com/annwntech/go-micro/v2@v2.9.5/config/source/etcd/etcd.go (about)

     1  package etcd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"time"
     8  
     9  	cetcd "github.com/coreos/etcd/clientv3"
    10  	"github.com/coreos/etcd/mvcc/mvccpb"
    11  	"github.com/annwntech/go-micro/v2/config/source"
    12  )
    13  
    14  // Currently a single etcd reader
    15  type etcd struct {
    16  	prefix      string
    17  	stripPrefix string
    18  	opts        source.Options
    19  	client      *cetcd.Client
    20  	cerr        error
    21  }
    22  
    23  var (
    24  	DefaultPrefix = "/micro/config/"
    25  )
    26  
    27  func (c *etcd) Read() (*source.ChangeSet, error) {
    28  	if c.cerr != nil {
    29  		return nil, c.cerr
    30  	}
    31  
    32  	rsp, err := c.client.Get(context.Background(), c.prefix, cetcd.WithPrefix())
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	if rsp == nil || len(rsp.Kvs) == 0 {
    38  		return nil, fmt.Errorf("source not found: %s", c.prefix)
    39  	}
    40  
    41  	kvs := make([]*mvccpb.KeyValue, 0, len(rsp.Kvs))
    42  	for _, v := range rsp.Kvs {
    43  		kvs = append(kvs, (*mvccpb.KeyValue)(v))
    44  	}
    45  
    46  	data := makeMap(c.opts.Encoder, kvs, c.stripPrefix)
    47  
    48  	b, err := c.opts.Encoder.Encode(data)
    49  	if err != nil {
    50  		return nil, fmt.Errorf("error reading source: %v", err)
    51  	}
    52  
    53  	cs := &source.ChangeSet{
    54  		Timestamp: time.Now(),
    55  		Source:    c.String(),
    56  		Data:      b,
    57  		Format:    c.opts.Encoder.String(),
    58  	}
    59  	cs.Checksum = cs.Sum()
    60  
    61  	return cs, nil
    62  }
    63  
    64  func (c *etcd) String() string {
    65  	return "etcd"
    66  }
    67  
    68  func (c *etcd) Watch() (source.Watcher, error) {
    69  	if c.cerr != nil {
    70  		return nil, c.cerr
    71  	}
    72  	cs, err := c.Read()
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	return newWatcher(c.prefix, c.stripPrefix, c.client.Watcher, cs, c.opts)
    77  }
    78  
    79  func (c *etcd) Write(cs *source.ChangeSet) error {
    80  	return nil
    81  }
    82  
    83  func NewSource(opts ...source.Option) source.Source {
    84  	options := source.NewOptions(opts...)
    85  
    86  	var endpoints []string
    87  
    88  	// check if there are any addrs
    89  	addrs, ok := options.Context.Value(addressKey{}).([]string)
    90  	if ok {
    91  		for _, a := range addrs {
    92  			addr, port, err := net.SplitHostPort(a)
    93  			if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
    94  				port = "2379"
    95  				addr = a
    96  				endpoints = append(endpoints, fmt.Sprintf("%s:%s", addr, port))
    97  			} else if err == nil {
    98  				endpoints = append(endpoints, fmt.Sprintf("%s:%s", addr, port))
    99  			}
   100  		}
   101  	}
   102  
   103  	if len(endpoints) == 0 {
   104  		endpoints = []string{"localhost:2379"}
   105  	}
   106  
   107  	// check dial timeout option
   108  	dialTimeout, ok := options.Context.Value(dialTimeoutKey{}).(time.Duration)
   109  	if !ok {
   110  		dialTimeout = 3 * time.Second // default dial timeout
   111  	}
   112  
   113  	config := cetcd.Config{
   114  		Endpoints:   endpoints,
   115  		DialTimeout: dialTimeout,
   116  	}
   117  
   118  	u, ok := options.Context.Value(authKey{}).(*authCreds)
   119  	if ok {
   120  		config.Username = u.Username
   121  		config.Password = u.Password
   122  	}
   123  
   124  	// use default config
   125  	client, err := cetcd.New(config)
   126  
   127  	prefix := DefaultPrefix
   128  	sp := ""
   129  	f, ok := options.Context.Value(prefixKey{}).(string)
   130  	if ok {
   131  		prefix = f
   132  	}
   133  
   134  	if b, ok := options.Context.Value(stripPrefixKey{}).(bool); ok && b {
   135  		sp = prefix
   136  	}
   137  
   138  	return &etcd{
   139  		prefix:      prefix,
   140  		stripPrefix: sp,
   141  		opts:        options,
   142  		client:      client,
   143  		cerr:        err,
   144  	}
   145  }