func (s *srv) newTLSConfig() (*tls.Config, error) { config, err := s.listener.TLSConfig() if err != nil { return nil, err } if config.NextProtos == nil { config.NextProtos = []string{"http/1.1"} } pairs := map[string]tls.Certificate{} for _, host := range s.mux.hosts { c := host.Settings.KeyPair if c == nil { continue } keyPair, err := tls.X509KeyPair(c.Cert, c.Key) if err != nil { return nil, err } if host.Settings.OCSP.Enabled { log.Infof("%v OCSP is enabled for %v, resolvers: %v", s, host, host.Settings.OCSP.Responders) r, err := s.mux.stapler.StapleHost(&host) if err != nil { log.Warningf("%v failed to staple %v, error %v", s, host, err) } else if r.Response.Status == ocsp.Good || r.Response.Status == ocsp.Revoked { keyPair.OCSPStaple = r.Staple } else { log.Warningf("%s got undefined status from OCSP responder: %v", s, r.Response.Status) } } pairs[host.Name] = keyPair } config.Certificates = make([]tls.Certificate, 0, len(pairs)) if s.defaultHost != "" { keyPair, exists := pairs[s.defaultHost] if !exists { return nil, fmt.Errorf("default host '%s' certificate is not passed", s.defaultHost) } config.Certificates = append(config.Certificates, keyPair) } for h, keyPair := range pairs { if h != s.defaultHost { config.Certificates = append(config.Certificates, keyPair) } } config.BuildNameToCertificate() return config, nil }
// Subscribe watches etcd changes and generates structured events telling vulcand to add or delete frontends, hosts etc. // It is a blocking function. func (n *ng) Subscribe(changes chan interface{}, cancelC chan bool) error { // This index helps us to get changes in sequence, as they were performed by clients. waitIndex := uint64(0) for { response, err := n.client.Watch(n.etcdKey, waitIndex, true, nil, cancelC) if err != nil { switch err { case etcd.ErrWatchStoppedByUser: log.Infof("Stop watching: graceful shutdown") return nil default: log.Errorf("unexpected error: %s, stop watching", err) return err } } waitIndex = response.Node.ModifiedIndex + 1 log.Infof("%s", responseToString(response)) change, err := n.parseChange(response) if err != nil { log.Warningf("Ignore '%s', error: %s", responseToString(response), err) continue } if change != nil { log.Infof("%v", change) select { case changes <- change: case <-cancelC: return nil } } } }
func (s *Service) Start() error { log.InitWithConfig(log.Config{ Name: s.options.Log, Severity: s.options.LogSeverity.S.String(), }) log.Infof("Service starts with options: %#v", s.options) if s.options.PidPath != "" { ioutil.WriteFile(s.options.PidPath, []byte(fmt.Sprint(os.Getpid())), 0644) } if s.options.StatsdAddr != "" { var err error s.metricsClient, err = metrics.NewWithOptions(s.options.StatsdAddr, s.options.StatsdPrefix, metrics.Options{UseBuffering: true}) if err != nil { return err } } apiFile, muxFiles, err := s.getFiles() if err != nil { return err } if err := s.newEngine(); err != nil { return err } s.stapler = stapler.New() s.supervisor = supervisor.New( s.newProxy, s.ng, s.errorC, supervisor.Options{Files: muxFiles}) // Tells configurator to perform initial proxy configuration and start watching changes if err := s.supervisor.Start(); err != nil { return err } if err := s.initApi(); err != nil { return err } go func() { s.errorC <- s.startApi(apiFile) }() if s.metricsClient != nil { go s.reportSystemMetrics() } signal.Notify(s.sigC, os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGUSR2, syscall.SIGCHLD) // Block until a signal is received or we got an error for { select { case signal := <-s.sigC: switch signal { case syscall.SIGTERM, syscall.SIGINT: log.Infof("Got signal '%s', shutting down gracefully", signal) s.supervisor.Stop(true) log.Infof("All servers stopped") return nil case syscall.SIGKILL: log.Infof("Got signal '%s', exiting now without waiting", signal) s.supervisor.Stop(false) return nil case syscall.SIGUSR2: log.Infof("Got signal '%s', forking a new self", signal) if err := s.startChild(); err != nil { log.Infof("Failed to start self: %s", err) } else { log.Infof("Successfully started self") } case syscall.SIGCHLD: log.Warningf("Child exited, got '%s', collecting status", signal) var wait syscall.WaitStatus syscall.Wait4(-1, &wait, syscall.WNOHANG, nil) log.Warningf("Collected exit status from child") default: log.Infof("Ignoring '%s'", signal) } case err := <-s.errorC: log.Infof("Got request to shutdown with error: %s", err) return err } } }
// initProxy reads the configuration from the engine and configures the server func initProxy(ng engine.Engine, p proxy.Proxy) error { hosts, err := ng.GetHosts() if err != nil { return err } for _, h := range hosts { if err := p.UpsertHost(h); err != nil { return err } } bs, err := ng.GetBackends() if err != nil { return err } for _, b := range bs { if err := p.UpsertBackend(b); err != nil { return err } bk := engine.BackendKey{Id: b.Id} servers, err := ng.GetServers(bk) if err != nil { return err } for _, s := range servers { if err := p.UpsertServer(bk, s); err != nil { return err } } } ls, err := ng.GetListeners() if err != nil { return err } for _, l := range ls { if err := p.UpsertListener(l); err != nil { return err } } fs, err := ng.GetFrontends() if err != nil { return err } if len(fs) == 0 { log.Warningf("No frontends found") } for _, f := range fs { if err := p.UpsertFrontend(f); err != nil { return err } fk := engine.FrontendKey{Id: f.Id} ms, err := ng.GetMiddlewares(fk) if err != nil { return err } for _, m := range ms { if err := p.UpsertMiddleware(fk, m); err != nil { return err } } } return nil }