From f4123ad06e1ab2062d6040087df0a0db83464f14 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Thu, 5 Oct 2023 12:43:45 +0200 Subject: [PATCH] Allow flexible id tags --- internal/listener/listener.go | 7 +++++++ internal/object/db_types.go | 34 +++++++++++++++++++++++++--------- internal/object/object.go | 27 ++++++++++++++------------- schema/pgsql/schema.sql | 11 ++++++++--- schema/pgsql/upgrades/020.sql | 14 ++++++++++++++ 5 files changed, 68 insertions(+), 25 deletions(-) create mode 100644 schema/pgsql/upgrades/020.sql diff --git a/internal/listener/listener.go b/internal/listener/listener.go index 481a24af..5de659d3 100644 --- a/internal/listener/listener.go +++ b/internal/listener/listener.go @@ -92,6 +92,13 @@ func (l *Listener) ProcessEvent(w http.ResponseWriter, req *http.Request) { _, _ = fmt.Fprintf(w, "cannot parse JSON body: %v\n", err) return } + + if len(ev.Tags) == 0 { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintln(w, "ignoring invalid event: tags cannot be empty") + return + } + ev.Time = time.Now() if ev.Severity == event.SeverityNone && ev.Type == "" { diff --git a/internal/object/db_types.go b/internal/object/db_types.go index e8e6eec0..36c669c5 100644 --- a/internal/object/db_types.go +++ b/internal/object/db_types.go @@ -7,24 +7,33 @@ import ( "github.com/icinga/icingadb/pkg/types" ) -// ExtraTagRow represents a single database object extra tag like `hostgroup/foo: null`. -type ExtraTagRow struct { +// TagRow is a base type for IdTagRow and ExtraTagRow +type TagRow struct { ObjectId types.Binary `db:"object_id"` Tag string `db:"tag"` Value string `db:"value"` } +// ExtraTagRow represents a single database object extra tag like `hostgroup/foo: null`. +type ExtraTagRow TagRow + // TableName implements the contracts.TableNamer interface. func (e *ExtraTagRow) TableName() string { return "object_extra_tag" } +// IdTagRow represents a single database object id tag. +type IdTagRow TagRow + +// TableName implements the contracts.TableNamer interface. +func (e *IdTagRow) TableName() string { + return "object_id_tag" +} + type ObjectRow struct { ID types.Binary `db:"id"` SourceID int64 `db:"source_id"` Name string `db:"name"` - Host string `db:"host"` - Service types.String `db:"service"` URL types.String `db:"url"` } @@ -56,9 +65,17 @@ func LoadFromDB(ctx context.Context, db *icingadb.DB, id types.Binary) (*Object, return nil, fmt.Errorf("failed to fetch object: %w", err) } - tags := map[string]string{"host": objectRow.Host} - if objectRow.Service.Valid { - tags["service"] = objectRow.Service.String + var idTagRows []*IdTagRow + err = db.SelectContext( + ctx, &idTagRows, + db.Rebind(db.BuildSelectStmt(&IdTagRow{}, &IdTagRow{})+` WHERE "object_id" = ?`), id, + ) + if err != nil { + return nil, fmt.Errorf("failed to fetch object id tags: %w", err) + } + idTags := map[string]string{} + for _, idTag := range idTagRows { + idTags[idTag.Tag] = idTag.Value } var extraTagRows []*ExtraTagRow @@ -69,7 +86,6 @@ func LoadFromDB(ctx context.Context, db *icingadb.DB, id types.Binary) (*Object, if err != nil { return nil, fmt.Errorf("failed to fetch object extra tags: %w", err) } - extraTags := map[string]string{} for _, extraTag := range extraTagRows { extraTags[extraTag.Tag] = extraTag.Value @@ -80,7 +96,7 @@ func LoadFromDB(ctx context.Context, db *icingadb.DB, id types.Binary) (*Object, ID: id, Name: objectRow.Name, URL: objectRow.URL.String, - Tags: tags, + Tags: idTags, ExtraTags: extraTags, } cache[id.String()] = obj diff --git a/internal/object/object.go b/internal/object/object.go index ebccbecc..4b1ec42d 100644 --- a/internal/object/object.go +++ b/internal/object/object.go @@ -72,31 +72,32 @@ func FromEvent(ctx context.Context, db *icingadb.DB, ev *event.Event) (*Object, ID: object.ID, SourceID: ev.SourceId, Name: ev.Name, - Host: ev.Tags["host"], URL: utils.ToDBString(ev.URL), } - if service, ok := ev.Tags["service"]; ok { - dbObj.Service = utils.ToDBString(service) - } - stmt, _ := object.db.BuildUpsertStmt(&ObjectRow{}) _, err = tx.NamedExecContext(ctx, stmt, dbObj) if err != nil { return nil, fmt.Errorf("failed to insert object: %w", err) } + stmt, _ = object.db.BuildUpsertStmt(&IdTagRow{}) + _, err = tx.NamedExecContext(ctx, stmt, mapToTagRows(object.ID, ev.Tags)) + if err != nil { + return nil, fmt.Errorf("failed to upsert object id tags: %w", err) + } + extraTag := &ExtraTagRow{ObjectId: object.ID} _, err = tx.NamedExecContext(ctx, `DELETE FROM "object_extra_tag" WHERE "object_id" = :object_id`, extraTag) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to delete object extra tags: %w", err) } if len(ev.ExtraTags) > 0 { stmt, _ := object.db.BuildInsertStmt(extraTag) - _, err = tx.NamedExecContext(ctx, stmt, mapToExtraTagRows(object.ID, ev.ExtraTags)) + _, err = tx.NamedExecContext(ctx, stmt, mapToTagRows(object.ID, ev.ExtraTags)) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to insert object extra tags: %w", err) } } @@ -258,16 +259,16 @@ func ID(source int64, tags map[string]string) types.Binary { return h.Sum(nil) } -// mapToExtraTags transforms the object extra tags map to a slice of ExtraTagRow struct. -func mapToExtraTagRows(objectId types.Binary, extraTags map[string]string) []*ExtraTagRow { - var extraTagRows []*ExtraTagRow +// mapToTagRows transforms the object (extra) tags map to a slice of TagRow struct. +func mapToTagRows(objectId types.Binary, extraTags map[string]string) []*TagRow { + var tagRows []*TagRow for key, val := range extraTags { - extraTagRows = append(extraTagRows, &ExtraTagRow{ + tagRows = append(tagRows, &TagRow{ ObjectId: objectId, Tag: key, Value: val, }) } - return extraTagRows + return tagRows } diff --git a/schema/pgsql/schema.sql b/schema/pgsql/schema.sql index 90d2f197..901e1aad 100644 --- a/schema/pgsql/schema.sql +++ b/schema/pgsql/schema.sql @@ -136,9 +136,6 @@ CREATE TABLE object ( id bytea NOT NULL, -- SHA256 of identifying tags and the source.id source_id bigint NOT NULL REFERENCES source(id), name text NOT NULL, - -- this will probably become more flexible in the future - host text NOT NULL, - service text, url text, @@ -147,6 +144,14 @@ CREATE TABLE object ( CONSTRAINT pk_object PRIMARY KEY (id) ); +CREATE TABLE object_id_tag ( + object_id bytea NOT NULL REFERENCES object(id), + tag text NOT NULL, + value text NOT NULL, + + CONSTRAINT pk_object_id_tag PRIMARY KEY (object_id, tag) +); + CREATE TABLE object_extra_tag ( object_id bytea NOT NULL REFERENCES object(id), tag text NOT NULL, diff --git a/schema/pgsql/upgrades/020.sql b/schema/pgsql/upgrades/020.sql new file mode 100644 index 00000000..40b5cb40 --- /dev/null +++ b/schema/pgsql/upgrades/020.sql @@ -0,0 +1,14 @@ +CREATE TABLE object_id_tag ( + object_id bytea NOT NULL REFERENCES object(id), + tag text NOT NULL, + value text NOT NULL, + + CONSTRAINT pk_object_id_tag PRIMARY KEY (object_id, tag) +); + +INSERT INTO object_id_tag (object_id, tag, value) SELECT id, 'host', host FROM object; +INSERT INTO object_id_tag (object_id, tag, value) SELECT id, 'service', service FROM object WHERE service IS NOT NULL; + +ALTER TABLE object + DROP COLUMN host, + DROP COLUMN service;