// ProcessEvent reads, validates and emits a configuration. func (b *configuratorBehavior) ProcessEvent(event cells.Event) error { switch event.Topic() { case ReadConfigurationTopic: // Read configuration filename, ok := event.Payload().GetString(ConfigurationFilenamePayload) if !ok { logger.Errorf("cannot read configuration without filename payload") return nil } logger.Infof("reading configuration from %q", filename) cfg, err := etc.ReadFile(filename) if err != nil { return errors.Annotate(err, ErrCannotReadConfiguration, errorMessages) } // If wanted then validate it. if b.validate != nil { err = b.validate(cfg) if err != nil { return errors.Annotate(err, ErrCannotValidateConfiguration, errorMessages) } } // All done, emit it. pvs := cells.PayloadValues{ ConfigurationPayload: cfg, } b.cell.EmitNewContext(ConfigurationTopic, pvs, event.Context()) } return nil }
// receiveResponse retrieves a response from the server. func (r *resp) receiveResponse() *response { // Receive first line. line, err := r.reader.ReadBytes('\n') if err != nil { rerr := errors.Annotate(err, ErrConnectionBroken, errorMessages, "receive after "+r.cmd) return &response{receivingError, 0, nil, rerr} } content := line[1 : len(line)-2] // First byte defines kind. switch line[0] { case '+': // Status response. return &response{statusResponse, 0, line[:len(line)-2], nil} case '-': // Error response. return &response{errorResponse, 0, line[:len(line)-2], nil} case ':': // Integer response. return &response{integerResponse, 0, content, nil} case '$': // Bulk response or null bulk response. count, err := strconv.Atoi(string(content)) if err != nil { return &response{receivingError, 0, nil, errors.Annotate(err, ErrServerResponse, errorMessages)} } if count == -1 { // Null bulk response. return &response{nullBulkResponse, 0, nil, nil} } // Receive the bulk data. toRead := count + 2 buffer := make([]byte, toRead) n, err := io.ReadFull(r.reader, buffer) if err != nil { return &response{receivingError, 0, nil, err} } if n < toRead { return &response{receivingError, 0, nil, errors.New(ErrServerResponse, errorMessages)} } return &response{bulkResponse, 0, buffer[0:count], nil} case '*': // Array reply. Check for timeout. length, err := strconv.Atoi(string(content)) if err != nil { return &response{receivingError, 0, nil, errors.Annotate(err, ErrServerResponse, errorMessages)} } if length == -1 { // Timeout. return &response{timeoutError, 0, nil, nil} } return &response{arrayResponse, length, nil, nil} } return &response{receivingError, 0, nil, errors.New(ErrInvalidResponse, errorMessages, string(line))} }
// Read reads the SML source of the configuration from a // reader, parses it, and returns the configuration instance. func Read(source io.Reader) (Configuration, error) { builder := sml.NewKeyStringValueTreeBuilder() err := sml.ReadSML(source, builder) if err != nil { return nil, errors.Annotate(err, ErrIllegalSourceFormat, errorMessages) } tree, err := builder.Tree() if err != nil { return nil, errors.Annotate(err, ErrIllegalSourceFormat, errorMessages) } if err := tree.At("config").Error(); err != nil { return nil, errors.Annotate(err, ErrIllegalSourceFormat, errorMessages) } return &configuration{tree}, nil }
// ReadFile reads the SML source of a configuration file, // parses it, and returns the configuration instance. func ReadFile(filename string) (Configuration, error) { source, err := ioutil.ReadFile(filename) if err != nil { return nil, errors.Annotate(err, ErrCannotReadFile, errorMessages, filename) } return ReadString(string(source)) }
// Post is specified on the PostResourceHandler interface. func (h *FileUploadHandler) Post(ctx Context) (bool, error) { if err := ctx.Request().ParseMultipartForm(defaultMaxMemory); err != nil { return false, errors.Annotate(err, ErrUploadingFile, errorMessages) } for _, headers := range ctx.Request().MultipartForm.File { for _, header := range headers { logger.Infof("receiving file %q", header.Filename) // Open file and process it. if infile, err := header.Open(); err != nil { return false, errors.Annotate(err, ErrUploadingFile, errorMessages) } else if err := h.processor(ctx, header, infile); err != nil { return false, errors.Annotate(err, ErrUploadingFile, errorMessages) } } } return true, nil }
// DoAll implements the Set interface. func (s *set) DoAll(f func(v interface{}) error) error { for v := range s.values { if err := f(v); err != nil { return errors.Annotate(err, ErrDoAll, errorMessages) } } return nil }
// Validate checks if the GUID is valid. func (g *GUID) Validate() error { if g.IsPermaLink { if _, err := url.Parse(g.GUID); err != nil { return errors.Annotate(err, ErrParsing, errorMessages, "item GUID") } } return nil }
// selectDatabase selects the database. func (r *resp) selectDatabase() error { err := r.sendCommand("select", r.database.index) if err != nil { return errors.Annotate(err, ErrSelectDatabase, errorMessages) } result, err := r.receiveResultSet() if err != nil { return errors.Annotate(err, ErrSelectDatabase, errorMessages) } value, err := result.ValueAt(0) if err != nil { return errors.Annotate(err, ErrSelectDatabase, errorMessages) } if !value.IsOK() { return errors.New(ErrSelectDatabase, errorMessages) } return nil }
// Validate checks if the source is valid. func (s *Source) Validate() error { if s.Source == "" { return errors.New(ErrValidation, errorMessages, "item source must not be empty") } if _, err := url.Parse(s.URL); err != nil { return errors.Annotate(err, ErrParsing, errorMessages, "item source URL") } return nil }
// authenticate authenticates against the server if configured. func (r *resp) authenticate() error { if r.database.password != "" { err := r.sendCommand("auth", r.database.password) if err != nil { return errors.Annotate(err, ErrAuthenticate, errorMessages) } result, err := r.receiveResultSet() if err != nil { return errors.Annotate(err, ErrAuthenticate, errorMessages) } value, err := result.ValueAt(0) if err != nil { return errors.Annotate(err, ErrAuthenticate, errorMessages) } if !value.IsOK() { return errors.New(ErrAuthenticate, errorMessages) } } return nil }
// cleanupAllProps cleans all props. func (s *scene) cleanupAllProps() error { for _, box := range s.props { if box.cleanup != nil { err := box.cleanup(box.key, box.prop) if err != nil { return errors.Annotate(err, ErrCleanupFailed, errorMessages, box.key) } } } return nil }
// GetFloat64 implements the Value interface. func (v *value) GetFloat64() (float64, error) { raw, err := v.Get() if err != nil { return 0.0, err } fv, err := strconv.ParseFloat(raw, 64) if err != nil { return 0.0, errors.Annotate(err, ErrInvalidFormat, errorMessages, raw) } return fv, nil }
// GetInt implements the Value interface. func (v *value) GetInt() (int, error) { raw, err := v.Get() if err != nil { return 0, err } iv, err := strconv.ParseInt(raw, 10, 0) if err != nil { return 0, errors.Annotate(err, ErrInvalidFormat, errorMessages, raw) } return int(iv), nil }
// GetTime implements the Value interface. func (v *value) GetTime() (time.Time, error) { raw, err := v.Get() if err != nil { return time.Time{}, err } tv, err := time.Parse(time.RFC3339, raw) if err != nil { return tv, errors.Annotate(err, ErrInvalidFormat, errorMessages, raw) } return tv, nil }
// GetBool implements the Value interface. func (v *value) GetBool() (bool, error) { raw, err := v.Get() if err != nil { return false, err } bv, err := strconv.ParseBool(raw) if err != nil { return false, errors.Annotate(err, ErrInvalidFormat, errorMessages, raw) } return bv, nil }
// GetDuration implements the Value interface. func (v *value) GetDuration() (time.Duration, error) { raw, err := v.Get() if err != nil { return time.Duration(0), err } dv, err := time.ParseDuration(raw) if err != nil { return dv, errors.Annotate(err, ErrInvalidFormat, errorMessages, raw) } return dv, nil }
// newCell create a new cell around a behavior. func newCell(env *environment, id string, behavior Behavior) (*cell, error) { logger.Infof("cell '%s' starts", id) // Init cell runtime. c := &cell{ env: env, id: id, measuringID: identifier.Identifier("cells", env.id, "cell", id), behavior: behavior, emitters: newConnections(), subscribers: newConnections(), emitTimeoutTicker: time.NewTicker(5 * time.Second), } // Set configuration. if bebs, ok := behavior.(BehaviorEventBufferSize); ok { size := bebs.EventBufferSize() if size < minEventBufferSize { size = minEventBufferSize } c.eventc = make(chan Event, size) } else { c.eventc = make(chan Event, minEventBufferSize) } if brf, ok := behavior.(BehaviorRecoveringFrequency); ok { number, duration := brf.RecoveringFrequency() if duration.Seconds()/float64(number) < 0.1 { number = minRecoveringNumber duration = minRecoveringDuration } c.recoveringNumber = number c.recoveringDuration = duration } else { c.recoveringNumber = minRecoveringNumber c.recoveringDuration = minRecoveringDuration } if bet, ok := behavior.(BehaviorEmitTimeout); ok { timeout := bet.EmitTimeout() switch { case timeout < minEmitTimeout: timeout = minEmitTimeout case timeout > maxEmitTimeout: timeout = maxEmitTimeout } c.emitTimeout = int(timeout.Seconds() / 5) } else { c.emitTimeout = int(maxEmitTimeout.Seconds() / 5) } // Init behavior. if err := behavior.Init(c); err != nil { return nil, errors.Annotate(err, ErrCellInit, errorMessages, id) } // Start backend. c.loop = loop.GoRecoverable(c.backendLoop, c.checkRecovering, id) return c, nil }
// Validate checks if the enclosure is valid. func (e *Enclosure) Validate() error { if e.Length < 1 { return errors.New(ErrValidation, errorMessages, "item enclosure length %d is too small", e.Length) } if e.Type == "" { return errors.New(ErrValidation, errorMessages, "item enclosure type must not be empty") } if _, err := url.Parse(e.URL); err != nil { return errors.Annotate(err, ErrParsing, errorMessages, "item enclosure url") } return nil }
// 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 }
// checkRecovering checks if the cell may recover after a panic. It will // signal an error and let the cell stop working if there have been 12 recoverings // during the last minute or the behaviors Recover() signals, that it cannot // handle the error. func (c *cell) checkRecovering(rs loop.Recoverings) (loop.Recoverings, error) { logger.Errorf("recovering cell %q after error: %v", c.id, rs.Last().Reason) // Check frequency. if rs.Frequency(c.recoveringNumber, c.recoveringDuration) { return nil, errors.New(ErrRecoveredTooOften, errorMessages, rs.Last().Reason) } // Try to recover. if err := c.behavior.Recover(rs.Last().Reason); err != nil { return nil, errors.Annotate(err, ErrEventRecovering, errorMessages, rs.Last().Reason) } return rs.Trim(c.recoveringNumber), nil }
// newResp establishes a connection to a Redis database // based on the configuration of the passed database // configuration. func newResp(db *Database) (*resp, error) { // Dial the database and create the protocol instance. conn, err := net.DialTimeout(db.network, db.address, db.timeout) if err != nil { return nil, errors.Annotate(err, ErrConnectionEstablishing, errorMessages) } r := &resp{ database: db, conn: conn, reader: bufio.NewReader(conn), } return r, nil }
// Test the annotation of errors. func TestAnnotation(t *testing.T) { assert := audit.NewTestingAssertion(t, true) ec := 123 messages := errors.Messages{ec: "annotated"} aerr := testError("wrapped") err := errors.Annotate(aerr, ec, messages) assert.ErrorMatch(err, `\[ERRORS_TEST:123\] annotated: wrapped`) assert.Equal(errors.Annotated(err), aerr) assert.True(errors.IsInvalidTypeError(errors.Annotated(aerr))) assert.Length(errors.Stack(err), 2) }
// FindAll implements the Set interface. func (s *set) FindAll(f func(v interface{}) (bool, error)) ([]interface{}, error) { found := []interface{}{} for v := range s.values { ok, err := f(v) if err != nil { return nil, errors.Annotate(err, ErrFindAll, errorMessages) } if ok { found = append(found, v) } } return found, nil }
// FindAll implements the StringSet interface. func (s *stringSet) FindAll(f func(v string) (bool, error)) ([]string, error) { found := []string{} for v := range s.values { ok, err := f(v) if err != nil { return nil, errors.Annotate(err, ErrFindAll, errorMessages) } if ok { found = append(found, v) } } return found, nil }
// sendCommand sends a command and possible arguments to the server. func (r *resp) sendCommand(cmd string, args ...interface{}) error { r.cmd = cmd lengthPart := r.buildLengthPart(args) cmdPart := r.buildValuePart(cmd) argsPart := r.buildArgumentsPart(args) packet := join(lengthPart, cmdPart, argsPart) _, err := r.conn.Write(packet) if err != nil { return errors.Annotate(err, ErrConnectionBroken, errorMessages, "send "+r.cmd) } return nil }
// Apply implements the Configuration interface. func (c *configuration) Apply(kvs map[string]string) (Configuration, error) { cc := &configuration{ values: c.values.Copy(), } for key, value := range kvs { path := append([]string{"config"}, strings.Split(key, "/")...) _, err := cc.values.Create(path...).SetValue(value) if err != nil { return nil, errors.Annotate(err, ErrCannotApply, errorMessages) } } return cc, nil }
// Validate checks if the item is valid. func (i *Item) Validate() error { if i.Title == "" { if i.Description == "" { return errors.New(ErrValidation, errorMessages, "item title or description must not be empty") } } if i.Comments != "" { if _, err := url.Parse(i.Comments); err != nil { return errors.Annotate(err, ErrParsing, errorMessages, "item comments") } } if i.Enclosure != nil { if err := i.Enclosure.Validate(); err != nil { return err } } if i.GUID != nil { if err := i.GUID.Validate(); err != nil { return err } } if i.Link != "" { if _, err := url.Parse(i.Link); err != nil { return errors.Annotate(err, ErrParsing, errorMessages, "item link") } } if i.PubDate != "" { if _, err := ParseTime(i.PubDate); err != nil { return errors.Annotate(err, ErrParsing, errorMessages, "item publcation date") } } if i.Source != nil { if err := i.Source.Validate(); err != nil { return err } } return nil }
// Validate checks if the text input is valid. func (t *TextInput) Validate() error { if t.Description == "" { return errors.New(ErrValidation, errorMessages, "text input description must not be empty") } if _, err := url.Parse(t.Link); err != nil { return errors.Annotate(err, ErrParsing, errorMessages, "text input link") } if t.Name == "" { return errors.New(ErrValidation, errorMessages, "text input name must not be empty") } if t.Title == "" { return errors.New(ErrValidation, errorMessages, "text input title must not be empty") } return nil }
// TextNode implements the Builder interface. func (tb *KeyStringValueTreeBuilder) TextNode(text string) error { if tb.done { return errors.New(ErrBuilder, errorMessages, "building is already done") } value, err := tb.tree.At(tb.stack.All()...).Value() if err != nil { return errors.Annotate(err, ErrBuilder, errorMessages) } if value != "" { return errors.New(ErrBuilder, errorMessages, "node has multiple values") } text = strings.TrimSpace(text) if text != "" { _, err = tb.tree.At(tb.stack.All()...).SetValue(text) } return err }
// checkRecovering checks if the cell may recover after a panic. It will // signal an error and let the cell stop working if there have been 12 recoverings // during the last minute or the behaviors Recover() signals, that it cannot // handle the error. func (c *cell) checkRecovering(rs loop.Recoverings) (loop.Recoverings, error) { logger.Warningf("recovering cell %q after error: %v", c.id, rs.Last().Reason) // Check frequency. if rs.Frequency(c.recoveringNumber, c.recoveringDuration) { err := errors.New(ErrRecoveredTooOften, errorMessages, rs.Last().Reason) logger.Errorf("recovering frequency of cell %q too high", c.id) return nil, err } // Try to recover. if err := c.behavior.Recover(rs.Last().Reason); err != nil { err := errors.Annotate(err, ErrEventRecovering, errorMessages, rs.Last().Reason) logger.Errorf("recovering of cell %q failed: %v", c.id, err) return nil, err } logger.Infof("successfully recovered cell %q", c.id) return rs.Trim(c.recoveringNumber), nil }