github.com/resonatecoop/id@v1.1.0-43/config/etcd.go (about) 1 package config 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "os" 7 "strings" 8 9 //"github.com/etcd-io/etcdetcd/etcdserver/api/v3rpc/rpctypes" 10 //"github.com/etcd-io/etcdetcd/pkg/transport" 11 12 "github.com/resonatecoop/id/log" 13 "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" 14 clientv3 "go.etcd.io/etcd/client/v3" 15 transport "go.etcd.io/etcd/pkg/transport" 16 "golang.org/x/net/context" 17 ) 18 19 var ( 20 etcdEndpoints = "http://localhost:2379" 21 etcdCertFile, etcdKeyFile, etcdCaFile string 22 etcdConfigPath = "/config/go_oauth2_server.json" 23 ) 24 25 type etcdBackend struct{} 26 27 func (b *etcdBackend) InitConfigBackend() { 28 // Overwrite default values with environment variables if they are set 29 if os.Getenv("ETCD_ENDPOINTS") != "" { 30 etcdEndpoints = os.Getenv("ETCD_ENDPOINTS") 31 } 32 if os.Getenv("ETCD_CERT_FILE") != "" { 33 etcdCertFile = os.Getenv("ETCD_CERT_FILE") 34 } 35 if os.Getenv("ETCD_KEY_FILE") != "" { 36 etcdKeyFile = os.Getenv("ETCD_KEY_FILE") 37 } 38 if os.Getenv("ETCD_CA_FILE") != "" { 39 etcdCaFile = os.Getenv("ETCD_CA_FILE") 40 } 41 if os.Getenv("ETCD_CONFIG_PATH") != "" { 42 etcdConfigPath = os.Getenv("ETCD_CONFIG_PATH") 43 } 44 } 45 46 // LoadConfig gets the JSON from ETCD and unmarshals it to the config object 47 func (b *etcdBackend) LoadConfig() (*Config, error) { 48 49 cli, err := newEtcdClient(etcdEndpoints, etcdCertFile, etcdKeyFile, etcdCaFile) 50 if err != nil { 51 return nil, err 52 } 53 defer cli.Close() 54 55 // Read from remote config the first time 56 ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) 57 resp, err := cli.Get(ctx, etcdConfigPath) 58 cancel() 59 if err != nil { 60 switch err { 61 case context.Canceled: 62 return nil, fmt.Errorf("ctx is canceled by another routine: %v", err) 63 case context.DeadlineExceeded: 64 return nil, fmt.Errorf("ctx is attached with a deadline is exceeded: %v", err) 65 case rpctypes.ErrEmptyKey: 66 return nil, fmt.Errorf("client-side error: %v", err) 67 default: 68 return nil, fmt.Errorf("bad cluster endpoints, which are not etcd servers: %v", err) 69 } 70 } 71 72 if len(resp.Kvs) == 0 { 73 return nil, fmt.Errorf("key not found: %s", etcdConfigPath) 74 } 75 76 // Unmarshal the config JSON into the cnf object 77 newCnf := new(Config) 78 79 if err := json.Unmarshal([]byte(resp.Kvs[0].Value), newCnf); err != nil { 80 return nil, err 81 } 82 83 return newCnf, nil 84 } 85 86 // RefreshConfig sets config through the pointer so config actually gets refreshed 87 func (b *etcdBackend) RefreshConfig(newCnf *Config) { 88 *Cnf = *newCnf 89 } 90 91 func newEtcdClient(theEndpoints, certFile, keyFile, caFile string) (*clientv3.Client, error) { 92 // Log the etcd endpoint for debugging purposes 93 log.INFO.Printf("ETCD Endpoints: %s", theEndpoints) 94 95 // ETCD config 96 etcdConfig := clientv3.Config{ 97 Endpoints: strings.Split(theEndpoints, ","), 98 DialTimeout: dialTimeout, 99 } 100 101 // Optionally, configure TLS transport 102 if certFile != "" && keyFile != "" && caFile != "" { 103 // Load client cert 104 tlsInfo := transport.TLSInfo{ 105 CertFile: certFile, 106 KeyFile: keyFile, 107 TrustedCAFile: caFile, 108 } 109 110 // Setup HTTPS client 111 tlsConfig, err := tlsInfo.ClientConfig() 112 if err != nil { 113 return nil, err 114 } 115 // Add TLS config 116 etcdConfig.TLS = tlsConfig 117 } 118 119 // ETCD client 120 return clientv3.New(etcdConfig) 121 }