コード例 #1
0
ファイル: sensors_test.go プロジェクト: joyent/containerpilot
func TestSensorObserve(t *testing.T) {

	cmd1, _ := commands.NewCommand("./testdata/test.sh doStuff --debug", "1s")
	sensor := &Sensor{checkCmd: cmd1}
	if val, err := sensor.observe(); err != nil {
		t.Fatalf("Unexpected error from sensor check: %s", err)
	} else if val != "Running doStuff with args: --debug\n" {
		t.Fatalf("Unexpected output from sensor check: %s", val)
	}

	// Ensure we can run it more than once
	if _, err := sensor.observe(); err != nil {
		t.Fatalf("Unexpected error from sensor check (x2): %s", err)
	}

	// Ensure bad commands return error
	cmd2, _ := commands.NewCommand("./testdata/doesNotExist.sh", "0")
	sensor = &Sensor{checkCmd: cmd2}
	if val, err := sensor.observe(); err == nil {
		t.Fatalf("Expected error from sensor check but got %s", val)
	} else if err.Error() != "fork/exec ./testdata/doesNotExist.sh: no such file or directory" {
		t.Fatalf("Unexpected error from invalid sensor check: %s", err)
	}

}
コード例 #2
0
ファイル: backends.go プロジェクト: joyent/containerpilot
// 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
}
コード例 #3
0
ファイル: tasks.go プロジェクト: joyent/containerpilot
func parseTask(task *Task) error {

	if task.Command == nil {
		return fmt.Errorf("Task did not provide a command")
	}

	freq, err := utils.ParseDuration(task.Frequency)
	if err != nil {
		return fmt.Errorf("Unable to parse frequency %s: %v", task.Frequency, err)
	}
	if freq < time.Millisecond {
		return fmt.Errorf("Frequency %v cannot be less that %v", freq, taskMinDuration)
	}
	task.freqDuration = freq

	if task.Timeout == "" {
		task.Timeout = task.Frequency
	}
	cmd, err := commands.NewCommand(task.Command, task.Timeout)
	if cmd.TimeoutDuration < taskMinDuration {
		return fmt.Errorf("Timeout %v cannot be less that %v", cmd.TimeoutDuration, taskMinDuration)
	}
	cmd.Name = fmt.Sprintf("task[%s]", task.Name)
	task.cmd = cmd

	return nil
}
コード例 #4
0
func TestHealthCheckBad(t *testing.T) {
	cmd1, _ := commands.NewCommand("./testdata/test.sh failStuff", "")
	service := &Service{
		healthCheckCmd: cmd1,
	}
	if err := service.CheckHealth(); err == nil {
		t.Errorf("Expected error from CheckHealth but got nil")
	}
}
コード例 #5
0
ファイル: sensors.go プロジェクト: joyent/containerpilot
// 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
}
コード例 #6
0
ファイル: signals_test.go プロジェクト: joyent/containerpilot
func getSignalTestConfig() *App {
	service, _ := services.NewService(
		"test-service", 1, 1, 1, nil, nil, nil, &NoopServiceBackend{})
	app := EmptyApp()
	cmd, _ := commands.NewCommand([]string{
		"./testdata/test.sh",
		"interruptSleep"}, "0")
	app.Command = cmd
	app.StopTimeout = 5
	app.Services = []*services.Service{service}
	return app
}
コード例 #7
0
func TestHealthCheck(t *testing.T) {
	cmd1, _ := commands.NewCommand("./testdata/test.sh doStuff --debug", "1s")
	service := &Service{
		healthCheckCmd: cmd1,
	}
	if err := service.CheckHealth(); err != nil {
		t.Errorf("Unexpected error CheckHealth: %s", err)
	}
	// Ensure we can run it more than once
	if err := service.CheckHealth(); err != nil {
		t.Errorf("Unexpected error CheckHealth (x2): %s", err)
	}
}
コード例 #8
0
func TestOnChangeCmd(t *testing.T) {
	cmd1, _ := commands.NewCommand("./testdata/test.sh doStuff --debug", "1s")
	backend := &Backend{
		onChangeCmd: cmd1,
	}
	if err := backend.OnChange(); err != nil {
		t.Errorf("Unexpected error OnChange: %s", err)
	}
	// Ensure we can run it more than once
	if err := backend.OnChange(); err != nil {
		t.Errorf("Unexpected error OnChange (x2): %s", err)
	}
}
コード例 #9
0
ファイル: app.go プロジェクト: joyent/containerpilot
// Run starts the application and blocks until finished
func (a *App) Run() {
	// Set up handlers for polling and to accept signal interrupts
	if 1 == os.Getpid() {
		reapChildren()
	}
	args := getArgs(flag.Args())
	cmd, err := commands.NewCommand(args, "0")
	if err != nil {
		log.Errorf("Unable to parse command arguments: %v", err)
	}
	cmd.Name = "APP"
	a.Command = cmd

	a.handleSignals()

	if a.PreStartCmd != nil {
		// Run the preStart handler, if any, and exit if it returns an error
		fields := log.Fields{"process": "PreStart"}
		if code, err := commands.RunAndWait(a.PreStartCmd, fields); err != nil {
			os.Exit(code)
		}
	}
	a.handleCoprocesses()
	a.handlePolling()

	if a.Command != nil {
		// Run our main application and capture its stdout/stderr.
		// This will block until the main application exits and then os.Exit
		// with the exit code of that application.
		code, err := commands.RunAndWait(a.Command, nil)
		if err != nil {
			log.Println(err)
		}
		// Run the PostStop handler, if any, and exit if it returns an error
		if a.PostStopCmd != nil {
			fields := log.Fields{"process": "PostStop"}
			if postStopCode, err := commands.RunAndWait(a.PostStopCmd, fields); err != nil {
				os.Exit(postStopCode)
			}
		}
		os.Exit(code)
	}

	// block forever, as we're polling in the two polling functions and
	// did not os.Exit by waiting on an external application.
	select {}
}
コード例 #10
0
ファイル: coprocess.go プロジェクト: joyent/containerpilot
func parseCoprocess(coprocess *Coprocess) error {
	if coprocess.Command == nil {
		return fmt.Errorf("Coprocess did not provide a command")
	}
	cmd, err := commands.NewCommand(coprocess.Command, "0")
	if err != nil {
		return fmt.Errorf("Could not parse `coprocess` command %s: %s",
			coprocess.Name, err)
	}
	if coprocess.Name == "" {
		args := append([]string{cmd.Exec}, cmd.Args...)
		coprocess.Name = strings.Join(args, " ")
	}
	cmd.Name = fmt.Sprintf("coprocess[%s]", coprocess.Name)
	coprocess.cmd = cmd
	return parseCoprocessRestarts(coprocess)
}
コード例 #11
0
ファイル: sensors_test.go プロジェクト: joyent/containerpilot
func TestSensorPollAction(t *testing.T) {
	testServer := httptest.NewServer(prometheus.UninstrumentedHandler())
	defer testServer.Close()
	cmd, _ := commands.NewCommand("./testdata/test.sh measureStuff", "0")
	sensor := &Sensor{
		Type:     "counter",
		checkCmd: cmd,
		collector: prometheus.NewCounter(prometheus.CounterOpts{
			Namespace: "telemetry",
			Subsystem: "sensors",
			Name:      "TestSensorPollAction",
			Help:      "help",
		})}
	prometheus.MustRegister(sensor.collector)
	sensor.PollAction()
	resp := getFromTestServer(t, testServer)
	if strings.Count(resp, "telemetry_sensors_TestSensorPollAction 42") != 1 {
		t.Fatalf("Failed to get match for sensor in response: %s", resp)
	}
}
コード例 #12
0
ファイル: services.go プロジェクト: joyent/containerpilot
func parseService(s *Service, disc discovery.ServiceBackend) error {
	if err := utils.ValidateServiceName(s.Name); err != nil {
		return err
	}
	hostname, _ := os.Hostname()
	s.ID = fmt.Sprintf("%s-%s", s.Name, hostname)
	s.discoveryService = disc
	if s.Poll < 1 {
		return fmt.Errorf("`poll` must be > 0 in service %s", s.Name)
	}
	if s.TTL < 1 {
		return fmt.Errorf("`ttl` must be > 0 in service %s", s.Name)
	}
	if s.Port < 1 {
		return fmt.Errorf("`port` must be > 0 in service %s", s.Name)
	}

	// if the HealthCheckExec is nil then we'll have no health check
	// command; this is useful for the telemetry service
	if s.HealthCheckExec != nil {
		cmd, err := commands.NewCommand(s.HealthCheckExec, s.Timeout)
		if err != nil {
			return fmt.Errorf("Could not parse `health` in service %s: %s", s.Name, err)
		}
		cmd.Name = fmt.Sprintf("%s.health", s.Name)
		s.healthCheckCmd = cmd
	}

	interfaces, ifaceErr := utils.ToStringArray(s.Interfaces)
	if ifaceErr != nil {
		return ifaceErr
	}

	ipAddress, err := utils.GetIP(interfaces)
	if err != nil {
		return err
	}
	s.IPAddress = ipAddress

	var consulExtras *discovery.ConsulExtras
	if s.ConsulConfig != nil {

		if s.ConsulConfig.DeregisterCriticalServiceAfter != "" {
			if _, err := time.ParseDuration(s.ConsulConfig.DeregisterCriticalServiceAfter); err != nil {
				return fmt.Errorf("Could not parse consul `deregisterCriticalServiceAfter` in service %s: %s", s.Name, err)
			}
		}

		consulExtras = &discovery.ConsulExtras{
			DeregisterCriticalServiceAfter: s.ConsulConfig.DeregisterCriticalServiceAfter,
			EnableTagOverride:              s.ConsulConfig.EnableTagOverride,
		}
	}

	s.definition = &discovery.ServiceDefinition{
		ID:           s.ID,
		Name:         s.Name,
		Port:         s.Port,
		TTL:          s.TTL,
		Tags:         s.Tags,
		IPAddress:    s.IPAddress,
		ConsulExtras: consulExtras,
	}
	return nil
}
コード例 #13
0
ファイル: sensors_test.go プロジェクト: joyent/containerpilot
func TestSensorBadRecord(t *testing.T) {
	cmd, _ := commands.NewCommand("./testdata/test.sh doStuff --debug", "0")
	sensor := &Sensor{checkCmd: cmd}
	sensor.PollAction() // logs but no crash
}
コード例 #14
0
ファイル: sensors_test.go プロジェクト: joyent/containerpilot
func TestSensorBadPollAction(t *testing.T) {
	cmd, _ := commands.NewCommand("./testdata/doesNotExist.sh", "0")
	sensor := &Sensor{checkCmd: cmd}
	sensor.PollAction() // logs but no crash
}