// StartEmbeddedEtcd will start an embedded etcd server using embed.Config // passed to it. If unsuccessful, this function returns an error. func StartEmbeddedEtcd(cfg *embed.Config) error { etcdInstance.Lock() defer etcdInstance.Unlock() if etcdInstance.etcd != nil { return errors.New("An instance of etcd embedded server is already running") } // Start embedded etcd server etcd, err := embed.StartEtcd(cfg) if err != nil { return err } // The returned embed.Etcd.Server instance is not guaranteed to have // joined the cluster yet. Wait on the embed.Etcd.Server.ReadyNotify() // channel to know when it's ready for use. Stop waiting after an // arbitrary timeout (make it configurable?) of 42 seconds. select { case <-etcd.Server.ReadyNotify(): log.Info("Etcd embedded server is ready.") etcdInstance.etcd = etcd return nil case <-time.After(42 * time.Second): etcd.Server.Stop() // trigger a shutdown return errors.New("Etcd embedded server took too long to start") case err := <-etcd.Err(): return err } }
// startEtcd runs StartEtcd in addition to hooks needed for standalone etcd. func startEtcd(cfg *embed.Config) (<-chan struct{}, <-chan error, error) { e, err := embed.StartEtcd(cfg) if err != nil { return nil, nil, err } osutil.RegisterInterruptHandler(e.Server.Stop) return e.Server.StopNotify(), e.Err(), nil }
// startEtcd runs StartEtcd in addition to hooks needed for standalone etcd. func startEtcd(cfg *embed.Config) (<-chan struct{}, <-chan error, error) { defaultHost, dhErr := cfg.IsDefaultHost() if defaultHost != "" { if dhErr == nil { plog.Infof("advertising using detected default host %q", defaultHost) } else { plog.Noticef("failed to detect default host, advertise falling back to %q (%v)", defaultHost, dhErr) } } e, err := embed.StartEtcd(cfg) if err != nil { return nil, nil, err } osutil.RegisterInterruptHandler(e.Server.Stop) <-e.Server.ReadyNotify() // wait for e.Server to join the cluster return e.Server.StopNotify(), e.Err(), nil }
func TestEmbedEtcd(t *testing.T) { tests := []struct { cfg embed.Config werr string wpeers int wclients int }{ {werr: "multiple discovery"}, {werr: "advertise-client-urls is required"}, {werr: "should be at least"}, {werr: "is too long"}, {wpeers: 1, wclients: 1}, {wpeers: 2, wclients: 1}, {wpeers: 1, wclients: 2}, } urls := newEmbedURLs(10) // setup defaults for i := range tests { tests[i].cfg = *embed.NewConfig() } tests[0].cfg.Durl = "abc" setupEmbedCfg(&tests[1].cfg, []url.URL{urls[0]}, []url.URL{urls[1]}) tests[1].cfg.ACUrls = nil tests[2].cfg.TickMs = tests[2].cfg.ElectionMs - 1 tests[3].cfg.ElectionMs = 999999 setupEmbedCfg(&tests[4].cfg, []url.URL{urls[2]}, []url.URL{urls[3]}) setupEmbedCfg(&tests[5].cfg, []url.URL{urls[4]}, []url.URL{urls[5], urls[6]}) setupEmbedCfg(&tests[6].cfg, []url.URL{urls[7], urls[8]}, []url.URL{urls[9]}) dir := path.Join(os.TempDir(), fmt.Sprintf("embed-etcd")) os.RemoveAll(dir) defer os.RemoveAll(dir) for i, tt := range tests { tests[i].cfg.Dir = dir e, err := embed.StartEtcd(&tests[i].cfg) if tt.werr != "" { if err == nil || !strings.Contains(err.Error(), tt.werr) { t.Errorf("%d: expected error with %q, got %v", i, tt.werr, err) } if e != nil { e.Close() } continue } if err != nil { t.Errorf("%d: expected success, got error %v", i, err) continue } if len(e.Peers) != tt.wpeers { t.Errorf("%d: expected %d peers, got %d", i, tt.wpeers, len(e.Peers)) } if len(e.Clients) != tt.wclients { t.Errorf("%d: expected %d peers, got %d", i, tt.wclients, len(e.Clients)) } e.Close() } }
// RunEtcd starts an etcd server and runs it forever func RunEtcd(etcdServerConfig *configapi.EtcdConfig) { cfg := embed.NewConfig() cfg.Debug = true cfg.Name = defaultName cfg.Dir = etcdServerConfig.StorageDir clientTLS := configapi.UseTLS(etcdServerConfig.ServingInfo) if clientTLS { cfg.ClientTLSInfo.CAFile = etcdServerConfig.ServingInfo.ClientCA cfg.ClientTLSInfo.CertFile = etcdServerConfig.ServingInfo.ServerCert.CertFile cfg.ClientTLSInfo.KeyFile = etcdServerConfig.ServingInfo.ServerCert.KeyFile cfg.ClientTLSInfo.ClientCertAuth = len(cfg.ClientTLSInfo.CAFile) > 0 } u, err := types.NewURLs(addressToURLs(etcdServerConfig.ServingInfo.BindAddress, clientTLS)) if err != nil { glog.Fatalf("Unable to build etcd peer URLs: %v", err) } cfg.LCUrls = []url.URL(u) peerTLS := configapi.UseTLS(etcdServerConfig.PeerServingInfo) if peerTLS { cfg.PeerTLSInfo.CAFile = etcdServerConfig.PeerServingInfo.ClientCA cfg.PeerTLSInfo.CertFile = etcdServerConfig.PeerServingInfo.ServerCert.CertFile cfg.PeerTLSInfo.KeyFile = etcdServerConfig.PeerServingInfo.ServerCert.KeyFile cfg.PeerTLSInfo.ClientCertAuth = len(cfg.PeerTLSInfo.CAFile) > 0 } u, err = types.NewURLs(addressToURLs(etcdServerConfig.PeerServingInfo.BindAddress, peerTLS)) if err != nil { glog.Fatalf("Unable to build etcd peer URLs: %v", err) } cfg.LPUrls = []url.URL(u) u, err = types.NewURLs(addressToURLs(etcdServerConfig.Address, clientTLS)) if err != nil { glog.Fatalf("Unable to build etcd announce client URLs: %v", err) } cfg.ACUrls = []url.URL(u) u, err = types.NewURLs(addressToURLs(etcdServerConfig.PeerAddress, peerTLS)) if err != nil { glog.Fatalf("Unable to build etcd announce peer URLs: %v", err) } cfg.APUrls = []url.URL(u) cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name) osutil.HandleInterrupts() e, err := embed.StartEtcd(cfg) if err != nil { glog.Fatalf("Unable to start etcd: %v", err) } go func() { defer e.Close() select { case <-e.Server.ReadyNotify(): glog.Infof("Started etcd at %s", etcdServerConfig.Address) case <-time.After(60 * time.Second): glog.Warning("etcd took too long to start, stopped") e.Server.Stop() // trigger a shutdown } glog.Fatalf("etcd has returned an error: %v", <-e.Err()) }() }