Beispiel #1
0
// backendLoop runs the backend loop of the scene.
func (s *scene) backendLoop(l loop.Loop) (err error) {
	// Defer cleanup.
	defer func() {
		cerr := s.cleanupAllProps()
		if err == nil {
			err = cerr
		}
	}()
	// Init timers.
	var watchdog <-chan time.Time
	var clapperboard <-chan time.Time
	if s.absolute > 0 {
		clapperboard = time.After(s.absolute)
	}
	// Run loop.
	for {
		if s.inactivity > 0 {
			watchdog = time.After(s.inactivity)
		}
		select {
		case <-l.ShallStop():
			return nil
		case timeout := <-watchdog:
			return errors.New(ErrTimeout, errorMessages, "inactivity", timeout)
		case timeout := <-clapperboard:
			return errors.New(ErrTimeout, errorMessages, "absolute", timeout)
		case command := <-s.commandChan:
			s.processCommand(command)
		}
	}
}
Beispiel #2
0
// WaitFlagLimited is specified on the Scene interface.
func (s *scene) WaitFlagLimited(topic string, timeout time.Duration) error {
	// Add signal channel.
	command := &envelope{
		kind: wait,
		signaling: &signaling{
			topic:      topic,
			signalChan: make(chan struct{}, 1),
		},
		respChan: make(chan *envelope, 1),
	}
	_, err := s.command(command)
	if err != nil {
		return err
	}
	// Wait for signal.
	var timeoutChan <-chan time.Time
	if timeout > 0 {
		timeoutChan = time.After(timeout)
	}
	select {
	case <-s.backend.IsStopping():
		err = s.Wait()
		if err == nil {
			err = errors.New(ErrSceneEnded, errorMessages)
		}
		return err
	case <-command.signaling.signalChan:
		return nil
	case <-timeoutChan:
		return errors.New(ErrWaitedTooLong, errorMessages, topic)
	}
}
Beispiel #3
0
// checkRecovering checks if the backend can be recovered.
func (m *systemMonitor) checkRecovering(rs loop.Recoverings) (loop.Recoverings, error) {
	if rs.Frequency(12, time.Minute) {
		logger.Errorf("monitor cannot be recovered: %v", rs.Last().Reason)
		return nil, errors.New(ErrMonitorCannotBeRecovered, errorMessages, rs.Last().Reason)
	}
	logger.Warningf("monitor recovered: %v", rs.Last().Reason)
	return rs.Trim(12), nil
}
Beispiel #4
0
// checkRecovering checks if the backend can be recovered.
func (c *Crontab) checkRecovering(rs loop.Recoverings) (loop.Recoverings, error) {
	if rs.Frequency(12, time.Minute) {
		logger.Errorf("crontab cannot be recovered: %v", rs.Last().Reason)
		return nil, errors.New(ErrCrontabCannotBeRecovered, errorMessages, rs.Last().Reason)
	}
	logger.Warningf("crontab recovered: %v", rs.Last().Reason)
	return rs.Trim(12), nil
}
Beispiel #5
0
// command sends a command to the system monitor and waits for a response.
func (m *systemMonitor) command(opCode int, args interface{}) (interface{}, error) {
	cmd := &command{opCode, args, make(chan interface{})}
	m.commandChan <- cmd
	resp, ok := <-cmd.respChan
	if !ok {
		return nil, errors.New(ErrMonitorPanicked, errorMessages)
	}
	if err, ok := resp.(error); ok {
		return nil, err
	}
	return resp, nil
}
Beispiel #6
0
// NewUUIDByHex creates a UUID based on the passed hex string which has to
// have the length of 32 bytes.
func NewUUIDByHex(source string) (UUID, error) {
	uuid := UUID{}
	if len([]byte(source)) != 32 {
		return uuid, errors.New(ErrInvalidHexLength, errorMessages)
	}
	raw, err := hex.DecodeString(source)
	if err != nil {
		return uuid, errors.Annotate(err, ErrInvalidHexValue, errorMessages)
	}
	copy(uuid[:], raw)
	return uuid, nil
}
Beispiel #7
0
// Test the validation.
func TestValidation(t *testing.T) {
	assert := asserts.NewTestingAssertion(t, true)

	ec := 1
	messages := errors.Messages{ec: "valid"}
	err := errors.New(ec, messages)
	packageName, fileName, line, lerr := errors.Location(err)

	assert.True(errors.Valid(err))
	assert.Nil(lerr)
	assert.Equal(packageName, "github.com/pellaeon/goas/v3/errors_test")
	assert.Equal(fileName, "errors_test.go")
	assert.Equal(line, 31)
}
Beispiel #8
0
// command sends a command envelope to the backend and
// waits for the response.
func (s *scene) command(command *envelope) (*envelope, error) {
	select {
	case s.commandChan <- command:
	case <-s.backend.IsStopping():
		err := s.Wait()
		if err == nil {
			err = errors.New(ErrSceneEnded, errorMessages)
		}
		return nil, err
	}
	select {
	case <-s.backend.IsStopping():
		err := s.Wait()
		if err == nil {
			err = errors.New(ErrSceneEnded, errorMessages)
		}
		return nil, err
	case resp := <-command.respChan:
		if resp.err != nil {
			return nil, resp.err
		}
		return resp, nil
	}
}
Beispiel #9
0
// Test creation and checking.
func TestIsError(t *testing.T) {
	assert := asserts.NewTestingAssertion(t, true)

	ec := 42
	messages := errors.Messages{ec: "test error %d"}
	err := errors.New(ec, messages, 1)

	assert.ErrorMatch(err, `\[ERRORS_TEST:042\] test error 1`)
	assert.True(errors.IsError(err, ec))
	assert.False(errors.IsError(err, 0))

	err = testError("test error 2")

	assert.ErrorMatch(err, "test error 2")
	assert.False(errors.IsError(err, ec))
	assert.False(errors.IsError(err, 0))
}
Beispiel #10
0
// processCommand handles the received commands of the monitor.
func (m *systemMonitor) processCommand(cmd *command) {
	defer cmd.close()
	switch cmd.opCode {
	case cmdReset:
		// Reset monitoring.
		m.init()
		cmd.ok()
	case cmdMeasuringPointRead:
		// Read just one measuring point.
		id := cmd.args.(string)
		if mp, ok := m.etmData[id]; ok {
			// Measuring point found.
			clone := *mp
			cmd.respond(&clone)
		} else {
			// Measuring point does not exist.
			cmd.respond(errors.New(ErrMeasuringPointNotExists, errorMessages, id))
		}
	case cmdMeasuringPointsReadAll:
		// Read all measuring points.
		resp := MeasuringPoints{}
		for _, mp := range m.etmData {
			clone := *mp
			resp = append(resp, &clone)
		}
		sort.Sort(resp)
		cmd.respond(resp)
	case cmdStaySetVariableRead:
		// Read just one stay-set variable.
		id := cmd.args.(string)
		if ssv, ok := m.ssvData[id]; ok {
			// Variable found.
			clone := *ssv
			cmd.respond(&clone)
		} else {
			// Variable does not exist.
			cmd.respond(errors.New(ErrStaySetVariableNotExists, errorMessages, id))
		}
	case cmdStaySetVariablesReadAll:
		// Read all stay-set variables.
		resp := StaySetVariables{}
		for _, mp := range m.ssvData {
			clone := *mp
			resp = append(resp, &clone)
		}
		sort.Sort(resp)
		cmd.respond(resp)
	case cmdDynamicStatusRetrieverRead:
		// Read just one dynamic status value.
		id := cmd.args.(string)
		if dsr, ok := m.dsrData[id]; ok {
			// Dynamic status found.
			v, err := dsr()
			if err != nil {
				cmd.respond(err)
			} else {
				cmd.respond(v)
			}
		} else {
			// Dynamic status does not exist.
			cmd.respond(errors.New(ErrDynamicStatusNotExists, errorMessages, id))
		}
	case cmdDynamicStatusRetrieversReadAll:
		// Read all dynamic status values.
		resp := DynamicStatusValues{}
		for id, dsr := range m.dsrData {
			v, err := dsr()
			if err != nil {
				cmd.respond(err)
			}
			dsv := &DynamicStatusValue{id, v}
			resp = append(resp, dsv)
		}
		sort.Sort(resp)
		cmd.respond(resp)
	}
}
Beispiel #11
0
// processCommand processes the sent commands.
func (s *scene) processCommand(command *envelope) {
	switch command.kind {
	case storeProp:
		// Add a new prop.
		_, ok := s.props[command.box.key]
		if ok {
			command.err = errors.New(ErrPropAlreadyExist, errorMessages, command.box.key)
		} else {
			s.props[command.box.key] = command.box
		}
	case fetchProp:
		// Retrieve a prop.
		box, ok := s.props[command.box.key]
		if !ok {
			command.err = errors.New(ErrPropNotFound, errorMessages, command.box.key)
		} else {
			command.box = box
		}
	case disposeProp:
		// Remove a prop.
		box, ok := s.props[command.box.key]
		if !ok {
			command.err = errors.New(ErrPropNotFound, errorMessages, command.box.key)
		} else {
			delete(s.props, command.box.key)
			command.box = box
			if box.cleanup != nil {
				cerr := box.cleanup(box.key, box.prop)
				if cerr != nil {
					command.err = errors.Annotate(cerr, ErrCleanupFailed, errorMessages, box.key)
				}
			}
		}
	case flag:
		// Signal a topic.
		s.flags[command.signaling.topic] = true
		// Notify subscribers.
		subscribers, ok := s.signalings[command.signaling.topic]
		if ok {
			delete(s.signalings, command.signaling.topic)
			for _, subscriber := range subscribers {
				subscriber <- struct{}{}
			}
		}
	case unflag:
		// Drop a topic.
		delete(s.flags, command.signaling.topic)
	case wait:
		// Add a waiter for a topic.
		active := s.flags[command.signaling.topic]
		if active {
			command.signaling.signalChan <- struct{}{}
		} else {
			waiters := s.signalings[command.signaling.topic]
			s.signalings[command.signaling.topic] = append(waiters, command.signaling.signalChan)
		}
	default:
		panic("illegal command")
	}
	// Return the changed command as response.
	command.respChan <- command
}