// NewTelemetry configures a new prometheus Telemetry server
func NewTelemetry(raw interface{}) (*Telemetry, error) {
	t := &Telemetry{
		Port:        9090,
		ServiceName: "containerpilot",
		URL:         "/metrics",
		TTL:         15,
		Poll:        5,
		lock:        sync.RWMutex{},
	}

	if err := utils.DecodeRaw(raw, t); err != nil {
		return nil, fmt.Errorf("Telemetry configuration error: %v", err)
	}
	ipAddress, err := utils.IPFromInterfaces(t.Interfaces)
	if err != nil {
		return nil, err
	}
	ip := net.ParseIP(ipAddress)
	t.addr = net.TCPAddr{IP: ip, Port: t.Port}
	t.mux = http.NewServeMux()
	t.mux.Handle(t.URL, prometheus.Handler())
	// note that we don't return an error if there are no sensors
	// because the prometheus handler will still pick up metrics
	// internal to ContainerPilot (i.e. the golang runtime)
	if t.SensorConfigs != nil {
		sensors, err := NewSensors(t.SensorConfigs)
		if err != nil {
			return nil, err
		}
		t.Sensors = sensors
	}
	return t, nil
}
Beispiel #2
0
// NewBackends creates a new backend from a raw config structure
func NewBackends(raw []interface{}, disc discovery.ServiceBackend) ([]*Backend, error) {
	if raw == nil {
		return []*Backend{}, nil
	}
	var backends []*Backend
	if err := utils.DecodeRaw(raw, &backends); err != nil {
		return nil, fmt.Errorf("Backend configuration error: %v", err)
	}
	for _, b := range backends {
		if err := utils.ValidateServiceName(b.Name); err != nil {
			return nil, err
		}
		if b.OnChangeExec == nil {
			return nil, fmt.Errorf("`onChange` is required in backend %s",
				b.Name)
		}
		cmd, err := commands.NewCommand(b.OnChangeExec, b.Timeout)
		if err != nil {
			return nil, fmt.Errorf("Could not parse `onChange` in backend %s: %s",
				b.Name, err)
		}
		cmd.Name = fmt.Sprintf("%s.health", b.Name)
		b.onChangeCmd = cmd

		if b.Poll < 1 {
			return nil, fmt.Errorf("`poll` must be > 0 in backend %s",
				b.Name)
		}
		b.onChangeCmd = cmd
		b.discoveryService = disc
	}
	return backends, nil
}
Beispiel #3
0
// NewSensors creates new sensors from a raw config
func NewSensors(raw []interface{}) ([]*Sensor, error) {
	var sensors []*Sensor
	if err := utils.DecodeRaw(raw, &sensors); err != nil {
		return nil, fmt.Errorf("Sensor configuration error: %v", err)
	}
	for _, s := range sensors {
		check, err := commands.NewCommand(s.CheckExec, s.Timeout)
		if err != nil {
			return nil, fmt.Errorf("could not parse check in sensor %s: %s", s.Name, err)
		}
		check.Name = fmt.Sprintf("%s.sensor", s.Name)
		s.checkCmd = check

		// the prometheus client lib's API here is baffling... they don't expose
		// an interface or embed their Opts type in each of the Opts "subtypes",
		// so we can't share the initialization.
		switch {
		case s.Type == "counter":
			s.collector = prometheus.NewCounter(prometheus.CounterOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		case s.Type == "gauge":
			s.collector = prometheus.NewGauge(prometheus.GaugeOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		case s.Type == "histogram":
			s.collector = prometheus.NewHistogram(prometheus.HistogramOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		case s.Type == "summary":
			s.collector = prometheus.NewSummary(prometheus.SummaryOpts{
				Namespace: s.Namespace,
				Subsystem: s.Subsystem,
				Name:      s.Name,
				Help:      s.Help,
			})
		default:
			return nil, fmt.Errorf("invalid sensor type: %s", s.Type)
		}
		// we're going to unregister before every attempt to register
		// so that we can reload config
		prometheus.Unregister(s.collector)
		if err := prometheus.Register(s.collector); err != nil {
			return nil, err
		}
	}
	return sensors, nil
}
Beispiel #4
0
// We can't use mapstructure to decode our config map since we want the values
// to also be raw interface{} types. mapstructure can only decode
// into concrete structs and primitives
func decodeConfig(configMap map[string]interface{}, result *rawConfig) error {
	var logConfig LogConfig
	var stopTimeout int
	if err := utils.DecodeRaw(configMap["logging"], &logConfig); err != nil {
		return err
	}
	if err := utils.DecodeRaw(configMap["stopTimeout"], &stopTimeout); err != nil {
		return err
	}
	result.stopTimeout = stopTimeout
	result.logConfig = &logConfig
	result.onStart = configMap["onStart"]
	result.preStart = configMap["preStart"]
	result.preStop = configMap["preStop"]
	result.postStop = configMap["postStop"]
	result.servicesConfig = decodeArray(configMap["services"])
	result.backendsConfig = decodeArray(configMap["backends"])
	result.tasksConfig = decodeArray(configMap["tasks"])
	result.coprocessesConfig = decodeArray(configMap["coprocesses"])
	result.telemetryConfig = configMap["telemetry"]

	delete(configMap, "logging")
	delete(configMap, "onStart")
	delete(configMap, "preStart")
	delete(configMap, "preStop")
	delete(configMap, "postStop")
	delete(configMap, "stopTimeout")
	delete(configMap, "services")
	delete(configMap, "backends")
	delete(configMap, "tasks")
	delete(configMap, "coprocesses")
	delete(configMap, "telemetry")
	var unused []string
	for key := range configMap {
		unused = append(unused, key)
	}
	if len(unused) > 0 {
		return fmt.Errorf("Unknown config keys: %v", unused)
	}
	return nil
}
Beispiel #5
0
func configFromMap(raw map[string]interface{}) (*consul.Config, error) {
	config := &struct {
		Address string `mapstructure:"address"`
		Scheme  string `mapstructure:"scheme"`
		Token   string `mapstructure:"token"`
	}{}
	if err := utils.DecodeRaw(raw, config); err != nil {
		return nil, err
	}
	return &consul.Config{
		Address: config.Address,
		Scheme:  config.Scheme,
		Token:   config.Token,
	}, nil
}
Beispiel #6
0
// NewServices new services from a raw config
func NewServices(raw []interface{}, disc discovery.ServiceBackend) ([]*Service, error) {
	if raw == nil {
		return []*Service{}, nil
	}
	var services []*Service
	if err := utils.DecodeRaw(raw, &services); err != nil {
		return nil, fmt.Errorf("Service configuration error: %v", err)
	}
	for _, s := range services {
		if err := parseService(s, disc); err != nil {
			return nil, err
		}
	}
	return services, nil
}
// NewCoprocesses parses json config into an array of Coprocesses
func NewCoprocesses(raw []interface{}) ([]*Coprocess, error) {
	var coprocesses []*Coprocess
	if raw == nil {
		return coprocesses, nil
	}
	var configs []*Coprocess
	if err := utils.DecodeRaw(raw, &configs); err != nil {
		return nil, fmt.Errorf("Coprocess configuration error: %v", err)
	}
	for _, t := range configs {
		if err := parseCoprocess(t); err != nil {
			return nil, err
		}
		coprocesses = append(coprocesses, t)
	}
	return coprocesses, nil
}
Beispiel #8
0
// NewTasks parses json config into an array of Tasks
func NewTasks(raw []interface{}) ([]*Task, error) {
	var tasks []*Task
	if raw == nil {
		return tasks, nil
	}
	var configs []*Task
	if err := utils.DecodeRaw(raw, &configs); err != nil {
		return nil, fmt.Errorf("Task configuration error: %v", err)
	}
	for _, t := range configs {
		if err := parseTask(t); err != nil {
			return nil, err
		}
		tasks = append(tasks, t)
	}
	return tasks, nil
}
Beispiel #9
0
// NewEtcdConfig creates a new service discovery backend for etcd
func NewEtcdConfig(raw interface{}) (*Etcd, error) {
	etcd := &Etcd{
		Prefix: "/containerpilot",
	}
	var config etcdRawConfig
	etcdConfig := client.Config{}
	if err := utils.DecodeRaw(raw, &config); err != nil {
		return nil, err
	}
	etcdConfig.Endpoints = parseEndpoints(config.Endpoints)
	if config.Prefix != "" {
		etcd.Prefix = config.Prefix
	}

	etcdClient, err := client.New(etcdConfig)
	if err != nil {
		return nil, err
	}
	etcd.Client = etcdClient
	etcd.API = client.NewKeysAPI(etcdClient)
	return etcd, nil
}