Exemple #1
0
// New returns a new Service. Config can not be nil.
func New(config *Config) (*Service, error) {

	var err error
	var keyBytes *[SecretKeyLength]byte
	var metricsClient metrics.Client

	// read in key from keypath or if not given, try getting them from key bytes.
	if config.KeyPath != "" {
		keyBytes, err = readKeyFromDisk(config.KeyPath)
		if err != nil {
			return nil, err
		}
	} else {
		if config.KeyBytes == nil {
			return nil, fmt.Errorf("No key bytes provided.")
		}
		keyBytes = config.KeyBytes
	}

	// setup metrics service
	if config.EmitStats {
		// get hostname of box
		hostname, err := os.Hostname()
		if err != nil {
			return nil, fmt.Errorf("failed to obtain hostname: %v", err)
		}

		// build lemma prefix
		prefix := "lemma." + strings.Replace(hostname, ".", "_", -1)
		if config.StatsdPrefix != "" {
			prefix += "." + config.StatsdPrefix
		}

		// build metrics client
		hostport := fmt.Sprintf("%v:%v", config.StatsdHost, config.StatsdPort)
		metricsClient, err = metrics.NewWithOptions(hostport, prefix, metrics.Options{UseBuffering: true})
		if err != nil {
			return nil, err
		}
	} else {
		// if you don't want to emit stats, use the nop client
		metricsClient = metrics.NewNop()
	}

	return &Service{
		secretKey:     keyBytes,
		metricsClient: metricsClient,
	}, nil
}
Exemple #2
0
// Returns a new Service. Provides control over time and random providers.
func NewWithProviders(config *Config, timeProvider timetools.TimeProvider,
	randomProvider random.RandomProvider) (*Service, error) {

	// config is required!
	if config == nil {
		return nil, fmt.Errorf("config is required.")
	}

	// set defaults if not set
	if config.NonceCacheCapacity < 1 {
		config.NonceCacheCapacity = CacheCapacity
	}
	if config.NonceCacheTimeout < 1 {
		config.NonceCacheTimeout = CacheTimeout
	}
	if config.NonceHeaderName == "" {
		config.NonceHeaderName = XMailgunNonce
	}
	if config.TimestampHeaderName == "" {
		config.TimestampHeaderName = XMailgunTimestamp
	}
	if config.SignatureHeaderName == "" {
		config.SignatureHeaderName = XMailgunSignature
	}
	if config.SignatureVersionHeaderName == "" {
		config.SignatureVersionHeaderName = XMailgunSignatureVersion
	}

	// setup metrics service
	metricsClient := metrics.NewNop()
	if config.EmitStats {
		// get hostname of box
		hostname, err := os.Hostname()
		if err != nil {
			return nil, fmt.Errorf("failed to obtain hostname: %v", err)
		}

		// build lemma prefix
		prefix := "lemma." + strings.Replace(hostname, ".", "_", -1)
		if config.StatsdPrefix != "" {
			prefix += "." + config.StatsdPrefix
		}

		// build metrics client
		hostport := fmt.Sprintf("%v:%v", config.StatsdHost, config.StatsdPort)
		metricsClient, err = metrics.NewWithOptions(hostport, prefix, metrics.Options{UseBuffering: true})
		if err != nil {
			return nil, err
		}
	}

	// read key from disk, if no key is read that's okay it might be passed in
	keyBytes, err := readKeyFromDisk(config.Keypath)

	// setup nonce cache
	ncache, err := NewNonceCache(config.NonceCacheCapacity, config.NonceCacheTimeout, timeProvider)
	if err != nil {
		return nil, err
	}

	// return service
	return &Service{
		config:         config,
		nonceCache:     ncache,
		secretKey:      keyBytes,
		timeProvider:   timeProvider,
		randomProvider: randomProvider,
		metricsClient:  metricsClient,
	}, nil
}
Exemple #3
0
func (s *Service) Start() error {
	// if .LogFormatter is set, it'll be used in log.SetFormatter() and .Log will be ignored.
	if s.options.LogFormatter != nil {
		log.SetFormatter(s.options.LogFormatter)
	} else {
		switch s.options.Log {
		case "console":
			{
				log.SetFormatter(&log.TextFormatter{})
			}
		case "json":
			{
				log.SetFormatter(&log.JSONFormatter{})
			}
		case "syslog":
			{
				hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
				if err == nil {
					log.SetFormatter(&log.TextFormatter{DisableColors: true})
					log.AddHook(hook)
				} else {
					setFallbackLogFormatter(s.options)
				}
			}
		case "logstash":
			{
				log.SetFormatter(&logrus_logstash.LogstashFormatter{Type: "logs"})
			}
		default:
			setFallbackLogFormatter(s.options)
		}
	}
	log.SetOutput(os.Stdout)
	log.SetLevel(s.options.LogSeverity.S)

	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.MetricsClient != nil {
		s.metricsClient = s.options.MetricsClient
	} else 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
		}
	}
}