From 4a659fd5c4ea50a805adefa22e1ef42a605c42b9 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 3 Aug 2021 08:45:25 +0200 Subject: [PATCH 01/11] Add db.BuildIgnoreStmt() --- pkg/icingadb/db.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pkg/icingadb/db.go b/pkg/icingadb/db.go index 247be2a6..985990ff 100644 --- a/pkg/icingadb/db.go +++ b/pkg/icingadb/db.go @@ -111,6 +111,20 @@ func (db *DB) BuildInsertStmt(into interface{}) (string, int) { ), len(columns) } +// BuildInsertIgnoreStmt returns an INSERT statement for the specified struct for +// which the database ignores rows that have already been inserted. +func (db *DB) BuildInsertIgnoreStmt(into interface{}) (string, int) { + columns := db.BuildColumns(into) + + return fmt.Sprintf( + // MySQL treats UPDATE id = id as a no-op. + `INSERT INTO %s (%s) VALUES (%s) ON DUPLICATE KEY UPDATE id = id`, + utils.TableName(into), + strings.Join(columns, ", "), + fmt.Sprintf(":%s", strings.Join(columns, ", :")), + ), len(columns) +} + // BuildSelectStmt returns a SELECT query that creates the FROM part from the given table struct // and the column list from the specified columns struct. func (db *DB) BuildSelectStmt(table interface{}, columns interface{}) string { From 54f9ef5a12853e6be9ede8b8c4bc7a15f302e81b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 30 Jul 2021 17:35:12 +0200 Subject: [PATCH 02/11] Insert environment With this change Icinga DB will insert the environment after each heartbeat takeover if it does not already exist in the database as the environment may have changed, although this is likely to happen very rarely, Instead of checking whether the environment already exists, uses an INSERT statement that does nothing if it does. --- pkg/icingadb/ha.go | 29 +++++++++++++++++++++++++++++ pkg/icingadb/v1/environment.go | 6 ++++++ 2 files changed, 35 insertions(+) create mode 100644 pkg/icingadb/v1/environment.go diff --git a/pkg/icingadb/ha.go b/pkg/icingadb/ha.go index d67e5bc4..6a8aacdb 100644 --- a/pkg/icingadb/ha.go +++ b/pkg/icingadb/ha.go @@ -264,7 +264,15 @@ func (h *HA) realize(ctx context.Context, s *icingaredisv1.IcingaStatus, t *type cancelCtx() return errors.Wrap(err, "can't commit transaction") } + if takeover { + // Insert the environment after each heartbeat takeover if it does not already exist in the database + // as the environment may have changed, although this is likely to happen very rarely. + if err := h.insertEnvironment(s); err != nil { + cancelCtx() + return errors.Wrap(err, "can't insert environment") + } + h.signalTakeover() } @@ -275,6 +283,27 @@ func (h *HA) realize(ctx context.Context, s *icingaredisv1.IcingaStatus, t *type return nil } +// insertEnvironment inserts the environment from the specified state into the database if it does not already exist. +func (h *HA) insertEnvironment(s *icingaredisv1.IcingaStatus) error { + e := v1.Environment{ + EntityWithoutChecksum: v1.EntityWithoutChecksum{ + IdMeta: v1.IdMeta{ + Id: s.EnvironmentID(), + }, + }, + Name: s.Environment, + } + + // Instead of checking whether the environment already exists, use an INSERT statement that does nothing if it does. + stmt, _ := h.db.BuildInsertIgnoreStmt(e) + + if _, err := h.db.NamedExecContext(h.ctx, stmt, e); err != nil { + return internal.CantPerformQuery(err, stmt) + } + + return nil +} + func (h *HA) removeInstance(ctx context.Context) { h.logger.Debugw("Removing our row from icingadb_instance", zap.String("instance_id", hex.EncodeToString(h.instanceId))) // Intentionally not using h.ctx here as it's already cancelled. diff --git a/pkg/icingadb/v1/environment.go b/pkg/icingadb/v1/environment.go new file mode 100644 index 00000000..91a0ed42 --- /dev/null +++ b/pkg/icingadb/v1/environment.go @@ -0,0 +1,6 @@ +package v1 + +type Environment struct { + EntityWithoutChecksum `json:",inline"` + Name string `json:"name"` +} From 26a184d9530954bd08c9a7c05b2e4ad788344990 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 4 Aug 2021 23:18:56 +0200 Subject: [PATCH 03/11] Make v1.Environment#Name types.String The default environment of Icinga is the empty string. In Golang, the zero value of string is also the empty string. But it makes sense to distinguish whether the name is not set or set to the empty string. That is possible with this change. --- pkg/icingadb/ha.go | 5 ++++- pkg/icingadb/v1/environment.go | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pkg/icingadb/ha.go b/pkg/icingadb/ha.go index 6a8aacdb..6d6a6979 100644 --- a/pkg/icingadb/ha.go +++ b/pkg/icingadb/ha.go @@ -291,7 +291,10 @@ func (h *HA) insertEnvironment(s *icingaredisv1.IcingaStatus) error { Id: s.EnvironmentID(), }, }, - Name: s.Environment, + Name: types.String{NullString: sql.NullString{ + String: s.Environment, + Valid: true, + }}, } // Instead of checking whether the environment already exists, use an INSERT statement that does nothing if it does. diff --git a/pkg/icingadb/v1/environment.go b/pkg/icingadb/v1/environment.go index 91a0ed42..22f1d1f0 100644 --- a/pkg/icingadb/v1/environment.go +++ b/pkg/icingadb/v1/environment.go @@ -1,6 +1,10 @@ package v1 +import ( + "github.com/icinga/icingadb/pkg/types" +) + type Environment struct { EntityWithoutChecksum `json:",inline"` - Name string `json:"name"` + Name types.String `json:"name"` } From e74b09daa181c167a51247210e74f76831c89dd2 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 5 Aug 2021 00:16:52 +0200 Subject: [PATCH 04/11] Use synctx instead of ctx Otherwise, code that is executed with ctx will not be cancelled if the synchronization is cancelled, e.g. in the case of heartbeat handovers. --- cmd/icingadb/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/icingadb/main.go b/cmd/icingadb/main.go index 239455ac..14de463e 100644 --- a/cmd/icingadb/main.go +++ b/cmd/icingadb/main.go @@ -142,7 +142,7 @@ func run() int { // Get the last IDs of the runtime update streams before starting anything else, // otherwise updates may be lost. - runtimeConfigUpdateStreams, runtimeStateUpdateStreams, err := rt.Streams(ctx) + runtimeConfigUpdateStreams, runtimeStateUpdateStreams, err := rt.Streams(synctx) if err != nil { logger.Fatalf("%+v", err) } From 084e0409bc635962fb42e24bbfc2f600d353c145 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 5 Aug 2021 00:22:52 +0200 Subject: [PATCH 05/11] Restart HA after environment change If the environment changes during runtime, we have to restart HA in order to stop a possibly running config sync and start a new one. --- cmd/icingadb/main.go | 4 +++ pkg/icingadb/ha.go | 61 ++++++++++++++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/cmd/icingadb/main.go b/cmd/icingadb/main.go index 14de463e..a1efd4a9 100644 --- a/cmd/icingadb/main.go +++ b/cmd/icingadb/main.go @@ -230,6 +230,10 @@ func run() int { case <-ha.Handover(): logger.Warn("Handing over") + cancelHactx() + case <-ha.Restart(): + logger.Info("Restarting HA") + cancelHactx() case <-hactx.Done(): // Nothing to do here, surrounding loop will terminate now. diff --git a/pkg/icingadb/ha.go b/pkg/icingadb/ha.go index 6d6a6979..6fbc84e2 100644 --- a/pkg/icingadb/ha.go +++ b/pkg/icingadb/ha.go @@ -26,11 +26,13 @@ type HA struct { cancelCtx context.CancelFunc instanceId types.Binary db *DB + environment *v1.Environment heartbeat *icingaredis.Heartbeat logger *zap.SugaredLogger responsible bool handover chan struct{} takeover chan struct{} + restart chan struct{} done chan struct{} mu *sync.Mutex err error @@ -52,6 +54,7 @@ func NewHA(ctx context.Context, db *DB, heartbeat *icingaredis.Heartbeat, logger logger: logger, handover: make(chan struct{}), takeover: make(chan struct{}), + restart: make(chan struct{}), done: make(chan struct{}), mu: &sync.Mutex{}, } @@ -96,6 +99,11 @@ func (h *HA) Takeover() chan struct{} { return h.takeover } +// Restart returns a channel with which restarts are signaled. +func (h *HA) Restart() chan struct{} { + return h.restart +} + func (h *HA) abort(err error) { h.errOnce.Do(func() { h.mu.Lock() @@ -141,6 +149,30 @@ func (h *HA) controller() { h.abort(err) } + if h.environment == nil || h.environment.Name.String != s.Environment { + var restart bool + + if h.environment != nil { + h.logger.Warnw("Got new environment", zap.String("current", h.environment.Name.String), zap.String("new", s.Environment)) + restart = true + } + + h.environment = &v1.Environment{ + EntityWithoutChecksum: v1.EntityWithoutChecksum{IdMeta: v1.IdMeta{ + Id: s.EnvironmentID(), + }}, + Name: types.String{NullString: sql.NullString{ + String: s.Environment, + Valid: true, + }}, + } + + if restart { + h.signalRestart() + continue + } + } + select { case <-logTicker.C: shouldLog = true @@ -268,7 +300,7 @@ func (h *HA) realize(ctx context.Context, s *icingaredisv1.IcingaStatus, t *type if takeover { // Insert the environment after each heartbeat takeover if it does not already exist in the database // as the environment may have changed, although this is likely to happen very rarely. - if err := h.insertEnvironment(s); err != nil { + if err := h.insertEnvironment(); err != nil { cancelCtx() return errors.Wrap(err, "can't insert environment") } @@ -284,23 +316,11 @@ func (h *HA) realize(ctx context.Context, s *icingaredisv1.IcingaStatus, t *type } // insertEnvironment inserts the environment from the specified state into the database if it does not already exist. -func (h *HA) insertEnvironment(s *icingaredisv1.IcingaStatus) error { - e := v1.Environment{ - EntityWithoutChecksum: v1.EntityWithoutChecksum{ - IdMeta: v1.IdMeta{ - Id: s.EnvironmentID(), - }, - }, - Name: types.String{NullString: sql.NullString{ - String: s.Environment, - Valid: true, - }}, - } - +func (h *HA) insertEnvironment() error { // Instead of checking whether the environment already exists, use an INSERT statement that does nothing if it does. - stmt, _ := h.db.BuildInsertIgnoreStmt(e) + stmt, _ := h.db.BuildInsertIgnoreStmt(h.environment) - if _, err := h.db.NamedExecContext(h.ctx, stmt, e); err != nil { + if _, err := h.db.NamedExecContext(h.ctx, stmt, h.environment); err != nil { return internal.CantPerformQuery(err, stmt) } @@ -364,3 +384,12 @@ func (h *HA) signalTakeover() { } } } + +func (h *HA) signalRestart() { + select { + case h.restart <- struct{}{}: + h.responsible = false + case <-h.ctx.Done(): + // Noop + } +} From a081927672861865efa70d27669beed5d86c17d4 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 5 Aug 2021 00:25:32 +0200 Subject: [PATCH 06/11] Only sync entities that belong to the current environment Previously, we selected each entity from the database. Now we only select entities that belong to the current environment. --- cmd/icingadb/main.go | 2 +- pkg/contracts/contracts.go | 7 +++++ pkg/icingadb/db.go | 27 ++++++++++++++++--- pkg/icingadb/ha.go | 48 ++++++++++++++++++++-------------- pkg/icingadb/scoped_entity.go | 32 +++++++++++++++++++++++ pkg/icingadb/sync.go | 16 +++++++++--- pkg/icingadb/v1/environment.go | 30 +++++++++++++++++++++ pkg/icingadb/v1/v1.go | 4 +++ 8 files changed, 138 insertions(+), 28 deletions(-) create mode 100644 pkg/icingadb/scoped_entity.go diff --git a/cmd/icingadb/main.go b/cmd/icingadb/main.go index a1efd4a9..77743401 100644 --- a/cmd/icingadb/main.go +++ b/cmd/icingadb/main.go @@ -133,7 +133,7 @@ func run() int { go func() { for hactx.Err() == nil { - synctx, cancelSynctx := context.WithCancel(hactx) + synctx, cancelSynctx := context.WithCancel(ha.Environment().NewContext(hactx)) g, synctx := errgroup.WithContext(synctx) // WaitGroups for initial synchronization. // Runtime updates must wait for initial synchronization to complete. diff --git a/pkg/contracts/contracts.go b/pkg/contracts/contracts.go index 0589c249..a8b42012 100644 --- a/pkg/contracts/contracts.go +++ b/pkg/contracts/contracts.go @@ -81,3 +81,10 @@ type Upserter interface { type TableNamer interface { TableName() string // TableName tells the table. } + +// Scoper implements the Scope method, +// which returns a struct specifying the WHERE conditions that +// entities must satisfy in order to be SELECTed. +type Scoper interface { + Scope() interface{} +} diff --git a/pkg/icingadb/db.go b/pkg/icingadb/db.go index 985990ff..bc7cbe75 100644 --- a/pkg/icingadb/db.go +++ b/pkg/icingadb/db.go @@ -128,11 +128,18 @@ func (db *DB) BuildInsertIgnoreStmt(into interface{}) (string, int) { // BuildSelectStmt returns a SELECT query that creates the FROM part from the given table struct // and the column list from the specified columns struct. func (db *DB) BuildSelectStmt(table interface{}, columns interface{}) string { - return fmt.Sprintf( + q := fmt.Sprintf( `SELECT %s FROM %s`, strings.Join(db.BuildColumns(columns), ", "), utils.TableName(table), ) + + if scoper, ok := table.(contracts.Scoper); ok { + where, _ := db.BuildWhere(scoper.Scope()) + q += ` WHERE ` + where + } + + return q } // BuildUpdateStmt returns an UPDATE statement for the given struct. @@ -177,6 +184,18 @@ func (db *DB) BuildUpsertStmt(subject interface{}) (stmt string, placeholders in ), len(insertColumns) } +// BuildWhere returns a WHERE clause with named placeholder conditions built from the specified struct +// combined with the AND operator. +func (db *DB) BuildWhere(subject interface{}) (string, int) { + columns := db.BuildColumns(subject) + where := make([]string, 0, len(columns)) + for _, col := range columns { + where = append(where, fmt.Sprintf("%s = :%s", col, col)) + } + + return strings.Join(where, ` AND `), len(columns) +} + // BulkExec bulk executes queries with a single slice placeholder in the form of `IN (?)`. // Takes in up to the number of arguments specified in count from the arg stream, // derives and expands a query and executes it with this set of arguments until the arg stream has been processed. @@ -398,10 +417,10 @@ func (db *DB) BatchSizeByPlaceholders(n int) int { return 1 } -// YieldAll executes the query with the supplied args, +// YieldAll executes the query with the supplied scope, // scans each resulting row into an entity returned by the factory function, // and streams them into a returned channel. -func (db *DB) YieldAll(ctx context.Context, factoryFunc contracts.EntityFactoryFunc, query string, args ...interface{}) (<-chan contracts.Entity, <-chan error) { +func (db *DB) YieldAll(ctx context.Context, factoryFunc contracts.EntityFactoryFunc, query string, scope interface{}) (<-chan contracts.Entity, <-chan error) { var cnt com.Counter entities := make(chan contracts.Entity, 1) g, ctx := errgroup.WithContext(ctx) @@ -415,7 +434,7 @@ func (db *DB) YieldAll(ctx context.Context, factoryFunc contracts.EntityFactoryF db.logger.Infof("Fetched %d elements of %s in %s", cnt.Val(), utils.Name(v), elapsed) }) - rows, err := db.QueryxContext(ctx, query, args...) + rows, err := db.NamedQueryContext(ctx, query, scope) if err != nil { return internal.CantPerformQuery(err, query) } diff --git a/pkg/icingadb/ha.go b/pkg/icingadb/ha.go index 6fbc84e2..65fb650c 100644 --- a/pkg/icingadb/ha.go +++ b/pkg/icingadb/ha.go @@ -22,21 +22,22 @@ var timeout = 60 * time.Second // HA provides high availability and indicates whether a Takeover or Handover must be made. type HA struct { - ctx context.Context - cancelCtx context.CancelFunc - instanceId types.Binary - db *DB - environment *v1.Environment - heartbeat *icingaredis.Heartbeat - logger *zap.SugaredLogger - responsible bool - handover chan struct{} - takeover chan struct{} - restart chan struct{} - done chan struct{} - mu *sync.Mutex - err error - errOnce sync.Once + ctx context.Context + cancelCtx context.CancelFunc + instanceId types.Binary + db *DB + environmentMu sync.Mutex + environment *v1.Environment + heartbeat *icingaredis.Heartbeat + logger *zap.SugaredLogger + responsible bool + handover chan struct{} + takeover chan struct{} + restart chan struct{} + done chan struct{} + errOnce sync.Once + errMu sync.Mutex + err error } // NewHA returns a new HA and starts the controller loop. @@ -56,7 +57,6 @@ func NewHA(ctx context.Context, db *DB, heartbeat *icingaredis.Heartbeat, logger takeover: make(chan struct{}), restart: make(chan struct{}), done: make(chan struct{}), - mu: &sync.Mutex{}, } go ha.controller() @@ -81,10 +81,18 @@ func (h *HA) Done() <-chan struct{} { return h.done } +// Environment returns the current environment. +func (h *HA) Environment() *v1.Environment { + h.environmentMu.Lock() + defer h.environmentMu.Unlock() + + return h.environment +} + // Err returns an error if Done has been closed and there is an error. Otherwise returns nil. func (h *HA) Err() error { - h.mu.Lock() - defer h.mu.Unlock() + h.errMu.Lock() + defer h.errMu.Unlock() return h.err } @@ -106,9 +114,9 @@ func (h *HA) Restart() chan struct{} { func (h *HA) abort(err error) { h.errOnce.Do(func() { - h.mu.Lock() + h.errMu.Lock() h.err = errors.Wrap(err, "HA aborted") - h.mu.Unlock() + h.errMu.Unlock() h.cancelCtx() }) diff --git a/pkg/icingadb/scoped_entity.go b/pkg/icingadb/scoped_entity.go new file mode 100644 index 00000000..7c1688c7 --- /dev/null +++ b/pkg/icingadb/scoped_entity.go @@ -0,0 +1,32 @@ +package icingadb + +import ( + "github.com/icinga/icingadb/pkg/contracts" + "github.com/icinga/icingadb/pkg/utils" +) + +// ScopedEntity combines an entity and a scope that specifies +// the WHERE conditions that entities of the +// enclosed entity type must satisfy in order to be SELECTed. +type ScopedEntity struct { + contracts.Entity + scope interface{} +} + +// Scope implements the contracts.Scoper interface. +func (e ScopedEntity) Scope() interface{} { + return e.scope +} + +// TableName implements the contracts.TableNamer interface. +func (e ScopedEntity) TableName() string { + return utils.TableName(e.Entity) +} + +// NewScopedEntity returns a new ScopedEntity. +func NewScopedEntity(entity contracts.Entity, scope interface{}) *ScopedEntity { + return &ScopedEntity{ + Entity: entity, + scope: scope, + } +} diff --git a/pkg/icingadb/sync.go b/pkg/icingadb/sync.go index 98688bf9..f1d57d07 100644 --- a/pkg/icingadb/sync.go +++ b/pkg/icingadb/sync.go @@ -78,8 +78,13 @@ func (s Sync) Sync(ctx context.Context, subject *common.SyncSubject) error { // Let errors from Redis cancel our group. com.ErrgroupReceive(g, redisErrs) + e, ok := v1.EnvironmentFromContext(ctx) + if !ok { + return errors.New("can't get environment from context") + } + actual, dbErrs := s.db.YieldAll( - ctx, subject.Factory(), s.db.BuildSelectStmt(subject.Entity(), subject.Entity().Fingerprint())) + ctx, subject.Factory(), s.db.BuildSelectStmt(NewScopedEntity(subject.Entity(), e.Meta()), subject.Entity().Fingerprint()), e.Meta()) // Let errors from DB cancel our group. com.ErrgroupReceive(g, dbErrs) @@ -164,6 +169,11 @@ func (s Sync) SyncCustomvars(ctx context.Context) error { s.logger.Info("Syncing customvar") s.logger.Info("Syncing customvar_flat") + e, ok := v1.EnvironmentFromContext(ctx) + if !ok { + return errors.New("can't get environment from context") + } + g, ctx := errgroup.WithContext(ctx) cv := common.NewSyncSubject(v1.NewCustomvar) @@ -175,7 +185,7 @@ func (s Sync) SyncCustomvars(ctx context.Context) error { com.ErrgroupReceive(g, errs) actualCvs, errs := s.db.YieldAll( - ctx, cv.Factory(), s.db.BuildSelectStmt(cv.Entity(), cv.Entity().Fingerprint())) + ctx, cv.Factory(), s.db.BuildSelectStmt(NewScopedEntity(cv.Entity(), e.Meta()), cv.Entity().Fingerprint()), e.Meta()) com.ErrgroupReceive(g, errs) g.Go(func() error { @@ -185,7 +195,7 @@ func (s Sync) SyncCustomvars(ctx context.Context) error { flatCv := common.NewSyncSubject(v1.NewCustomvarFlat) actualFlatCvs, errs := s.db.YieldAll( - ctx, flatCv.Factory(), s.db.BuildSelectStmt(flatCv.Entity(), flatCv.Entity().Fingerprint())) + ctx, flatCv.Factory(), s.db.BuildSelectStmt(NewScopedEntity(flatCv.Entity(), e.Meta()), flatCv.Entity().Fingerprint()), e.Meta()) com.ErrgroupReceive(g, errs) g.Go(func() error { diff --git a/pkg/icingadb/v1/environment.go b/pkg/icingadb/v1/environment.go index 22f1d1f0..fdddc5be 100644 --- a/pkg/icingadb/v1/environment.go +++ b/pkg/icingadb/v1/environment.go @@ -1,6 +1,7 @@ package v1 import ( + "context" "github.com/icinga/icingadb/pkg/types" ) @@ -8,3 +9,32 @@ type Environment struct { EntityWithoutChecksum `json:",inline"` Name types.String `json:"name"` } + +// NewContext returns a new Context that carries this Environment as value. +func (e *Environment) NewContext(parent context.Context) context.Context { + return context.WithValue(parent, environmentContextKey, e) +} + +// Meta returns the EnvironmentMeta for this Environment. +func (e *Environment) Meta() *EnvironmentMeta { + return &EnvironmentMeta{EnvironmentId: e.Id} +} + +// EnvironmentFromContext returns the Environment value stored in ctx, if any: +// +// e, ok := EnvironmentFromContext(ctx) +// if !ok { +// // Error handling. +// } +func EnvironmentFromContext(ctx context.Context) (*Environment, bool) { + if e, ok := ctx.Value(environmentContextKey).(*Environment); ok { + return e, true + } + + return nil, false +} + +// environmentContextKey is the key for Environment values in contexts. +// It's not exported, so callers use Environment.NewContext and EnvironmentFromContext +// instead of using that key directly. +var environmentContextKey contextKey diff --git a/pkg/icingadb/v1/v1.go b/pkg/icingadb/v1/v1.go index 7149cbf3..af19fdf8 100644 --- a/pkg/icingadb/v1/v1.go +++ b/pkg/icingadb/v1/v1.go @@ -52,3 +52,7 @@ var ConfigFactories = []contracts.EntityFactoryFunc{ NewUsergroupMember, NewZone, } + +// contextKey is an unexported type for context keys defined in this package. +// This prevents collisions with keys defined in other packages. +type contextKey int From 9b02b18f462b3bd05f6456750a8a644c2914a11e Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Fri, 8 Oct 2021 15:50:23 +0200 Subject: [PATCH 07/11] Use new environment ID https://github.com/Icinga/icinga2/pull/9036 introduced a new environment ID for Icinga DB that's written to the icinga:stats stream as field "icingadb_environment". This commit updates the code to make use of this ID instead of the one derived from the Icinga 2 Environment constant. --- pkg/icingadb/ha.go | 48 +++++++++++++++++++---------- pkg/icingaredis/heartbeat.go | 18 +++++++++-- pkg/icingaredis/v1/icinga_status.go | 11 ++----- 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/pkg/icingadb/ha.go b/pkg/icingadb/ha.go index 65fb650c..71c751f5 100644 --- a/pkg/icingadb/ha.go +++ b/pkg/icingadb/ha.go @@ -1,6 +1,7 @@ package icingadb import ( + "bytes" "context" "database/sql" "encoding/hex" @@ -157,23 +158,34 @@ func (h *HA) controller() { h.abort(err) } - if h.environment == nil || h.environment.Name.String != s.Environment { + envId, err := m.EnvironmentID() + if err != nil { + h.abort(err) + } + + if h.environment == nil || !bytes.Equal(h.environment.Id, envId) { var restart bool if h.environment != nil { - h.logger.Warnw("Got new environment", zap.String("current", h.environment.Name.String), zap.String("new", s.Environment)) + h.logger.Warnw("Got new environment", + zap.String("current", h.environment.Id.String()), + zap.String("new", envId.String())) restart = true } + h.environmentMu.Lock() h.environment = &v1.Environment{ EntityWithoutChecksum: v1.EntityWithoutChecksum{IdMeta: v1.IdMeta{ - Id: s.EnvironmentID(), - }}, - Name: types.String{NullString: sql.NullString{ - String: s.Environment, - Valid: true, + Id: envId, }}, + Name: types.String{ + NullString: sql.NullString{ + String: envId.String(), + Valid: true, + }, + }, } + h.environmentMu.Unlock() if restart { h.signalRestart() @@ -194,7 +206,7 @@ func (h *HA) controller() { } else { realizeCtx, cancelRealizeCtx = context.WithCancel(h.ctx) } - err = h.realize(realizeCtx, s, t, shouldLog) + err = h.realize(realizeCtx, s, t, envId, shouldLog) cancelRealizeCtx() if errors.Is(err, context.DeadlineExceeded) { h.signalHandover() @@ -205,7 +217,7 @@ func (h *HA) controller() { } if !oldInstancesRemoved { - go h.removeOldInstances(s) + go h.removeOldInstances(s, envId) oldInstancesRemoved = true } @@ -224,7 +236,7 @@ func (h *HA) controller() { } } -func (h *HA) realize(ctx context.Context, s *icingaredisv1.IcingaStatus, t *types.UnixMilli, shouldLog bool) error { +func (h *HA) realize(ctx context.Context, s *icingaredisv1.IcingaStatus, t *types.UnixMilli, envId types.Binary, shouldLog bool) error { boff := backoff.NewExponentialWithJitter(time.Millisecond*256, time.Second*3) for attempt := 0; true; attempt++ { sleep := boff(uint64(attempt)) @@ -239,7 +251,7 @@ func (h *HA) realize(ctx context.Context, s *icingaredisv1.IcingaStatus, t *type return errors.Wrap(err, "can't start transaction") } query := `SELECT id, heartbeat FROM icingadb_instance WHERE environment_id = ? AND responsible = ? AND id != ? AND heartbeat > ?` - rows, err := tx.QueryxContext(ctx, query, s.EnvironmentID(), "y", h.instanceId, utils.UnixMilli(time.Now().Add(-1*timeout))) + rows, err := tx.QueryxContext(ctx, query, envId, "y", h.instanceId, utils.UnixMilli(time.Now().Add(-1*timeout))) if err != nil { cancelCtx() return internal.CantPerformQuery(err, query) @@ -252,7 +264,11 @@ func (h *HA) realize(ctx context.Context, s *icingaredisv1.IcingaStatus, t *type h.logger.Errorw("Can't scan currently active instance", zap.Error(err)) } else { if shouldLog { - h.logger.Infow("Another instance is active", "instance_id", instance.Id, zap.String("environment", s.Environment), "heartbeat", instance.Heartbeat, zap.Duration("heartbeat_age", time.Since(instance.Heartbeat.Time()))) + h.logger.Infow("Another instance is active", + zap.String("instance_id", instance.Id.String()), + zap.String("environment", envId.String()), + "heartbeat", instance.Heartbeat, + zap.Duration("heartbeat_age", time.Since(instance.Heartbeat.Time()))) } takeover = false } @@ -265,7 +281,7 @@ func (h *HA) realize(ctx context.Context, s *icingaredisv1.IcingaStatus, t *type }, }, EnvironmentMeta: v1.EnvironmentMeta{ - EnvironmentId: s.EnvironmentID(), + EnvironmentId: envId, }, Heartbeat: *t, Responsible: types.Bool{Bool: takeover || h.responsible, Valid: true}, @@ -345,7 +361,7 @@ func (h *HA) removeInstance(ctx context.Context) { } } -func (h *HA) removeOldInstances(s *icingaredisv1.IcingaStatus) { +func (h *HA) removeOldInstances(s *icingaredisv1.IcingaStatus, envId types.Binary) { select { case <-h.ctx.Done(): return @@ -353,12 +369,12 @@ func (h *HA) removeOldInstances(s *icingaredisv1.IcingaStatus) { query := "DELETE FROM icingadb_instance " + "WHERE id != ? AND environment_id = ? AND endpoint_id = ? AND heartbeat < ?" heartbeat := types.UnixMilli(time.Now().Add(-timeout)) - result, err := h.db.ExecContext(h.ctx, query, h.instanceId, s.EnvironmentID(), + result, err := h.db.ExecContext(h.ctx, query, h.instanceId, envId, s.EndpointId, heartbeat) if err != nil { h.logger.Errorw("Can't remove rows of old instances", zap.Error(err), zap.String("query", query), - zap.String("id", h.instanceId.String()), zap.String("environment_id", s.EnvironmentID().String()), + zap.String("id", h.instanceId.String()), zap.String("environment_id", envId.String()), zap.String("endpoint_id", s.EndpointId.String()), zap.Time("heartbeat", heartbeat.Time())) return } diff --git a/pkg/icingaredis/heartbeat.go b/pkg/icingaredis/heartbeat.go index b33a8af0..fe7a966c 100644 --- a/pkg/icingaredis/heartbeat.go +++ b/pkg/icingaredis/heartbeat.go @@ -3,7 +3,9 @@ package icingaredis import ( "context" "github.com/go-redis/redis/v8" + "github.com/icinga/icingadb/internal" v1 "github.com/icinga/icingadb/pkg/icingaredis/v1" + "github.com/icinga/icingadb/pkg/types" "github.com/icinga/icingadb/pkg/utils" "github.com/pkg/errors" "go.uber.org/zap" @@ -124,11 +126,11 @@ func (h *Heartbeat) controller(ctx context.Context) { select { case m := <-messages: if !h.active { - s, err := m.Stats().IcingaStatus() + envId, err := m.EnvironmentID() if err != nil { - return errors.Wrapf(err, "can't parse Icinga 2 status from message %#v", m) + return err } - h.logger.Infow("Received first Icinga 2 heartbeat", zap.String("environment", s.Environment)) + h.logger.Infow("Received first Icinga 2 heartbeat", zap.String("environment", envId.String())) h.active = true } h.sendEvent(m) @@ -191,6 +193,16 @@ func (m *HeartbeatMessage) Stats() *v1.StatsMessage { return &m.stats } +// EnvironmentID returns the Icinga DB environment ID stored in the heartbeat message. +func (m *HeartbeatMessage) EnvironmentID() (types.Binary, error) { + var id types.Binary + err := internal.UnmarshalJSON([]byte(m.stats["icingadb_environment"].(string)), &id) + if err != nil { + return nil, err + } + return id, nil +} + // ExpiryTime returns the timestamp when the heartbeat expires. func (m *HeartbeatMessage) ExpiryTime() time.Time { return m.received.Add(timeout) diff --git a/pkg/icingaredis/v1/icinga_status.go b/pkg/icingaredis/v1/icinga_status.go index 9243926f..d94d3d65 100644 --- a/pkg/icingaredis/v1/icinga_status.go +++ b/pkg/icingaredis/v1/icinga_status.go @@ -1,13 +1,13 @@ package v1 import ( - "crypto/sha1" "github.com/icinga/icingadb/pkg/types" ) // IcingaStatus defines Icinga status information. type IcingaStatus struct { - Environment string `json:"environment"` + // Note: Icinga2Environment is not related to the environment_id used throughout Icinga DB. + Icinga2Environment string `json:"environment"` NodeName string `json:"node_name"` Version string `json:"version"` ProgramStart types.UnixMilli `json:"program_start"` @@ -19,10 +19,3 @@ type IcingaStatus struct { FlapDetectionEnabled types.Bool `json:"enable_flapping"` PerformanceDataEnabled types.Bool `json:"enable_perfdata"` } - -// EnvironmentID returns the environment ID. -func (s *IcingaStatus) EnvironmentID() types.Binary { - chksm := sha1.Sum([]byte(s.Environment)) - - return chksm[:] -} From 3342191b5e8f0d381fa1b8c4da5b6fc46396b22a Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Fri, 8 Oct 2021 17:42:35 +0200 Subject: [PATCH 08/11] Exit on environment ID changes There's a small risk that when the environment ID changes, Icinga DB could update write into the wrong environment in the database. Therefore, https://github.com/Icinga/icinga2/pull/9036 introduced a new default environment ID based on the CA public key so that there should be no cases where it's required to change the actual environment ID. So if this happens nonetheless, just bail out. --- cmd/icingadb/main.go | 4 ---- pkg/icingadb/ha.go | 26 +------------------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/cmd/icingadb/main.go b/cmd/icingadb/main.go index 77743401..4c404794 100644 --- a/cmd/icingadb/main.go +++ b/cmd/icingadb/main.go @@ -230,10 +230,6 @@ func run() int { case <-ha.Handover(): logger.Warn("Handing over") - cancelHactx() - case <-ha.Restart(): - logger.Info("Restarting HA") - cancelHactx() case <-hactx.Done(): // Nothing to do here, surrounding loop will terminate now. diff --git a/pkg/icingadb/ha.go b/pkg/icingadb/ha.go index 71c751f5..30d31222 100644 --- a/pkg/icingadb/ha.go +++ b/pkg/icingadb/ha.go @@ -34,7 +34,6 @@ type HA struct { responsible bool handover chan struct{} takeover chan struct{} - restart chan struct{} done chan struct{} errOnce sync.Once errMu sync.Mutex @@ -56,7 +55,6 @@ func NewHA(ctx context.Context, db *DB, heartbeat *icingaredis.Heartbeat, logger logger: logger, handover: make(chan struct{}), takeover: make(chan struct{}), - restart: make(chan struct{}), done: make(chan struct{}), } @@ -108,11 +106,6 @@ func (h *HA) Takeover() chan struct{} { return h.takeover } -// Restart returns a channel with which restarts are signaled. -func (h *HA) Restart() chan struct{} { - return h.restart -} - func (h *HA) abort(err error) { h.errOnce.Do(func() { h.errMu.Lock() @@ -164,13 +157,10 @@ func (h *HA) controller() { } if h.environment == nil || !bytes.Equal(h.environment.Id, envId) { - var restart bool - if h.environment != nil { - h.logger.Warnw("Got new environment", + h.logger.Fatalw("Environment changed unexpectedly", zap.String("current", h.environment.Id.String()), zap.String("new", envId.String())) - restart = true } h.environmentMu.Lock() @@ -186,11 +176,6 @@ func (h *HA) controller() { }, } h.environmentMu.Unlock() - - if restart { - h.signalRestart() - continue - } } select { @@ -408,12 +393,3 @@ func (h *HA) signalTakeover() { } } } - -func (h *HA) signalRestart() { - select { - case h.restart <- struct{}{}: - h.responsible = false - case <-h.ctx.Done(): - // Noop - } -} From aedfa8aeba271f34d80461090f2f9e0f0064bd30 Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Thu, 14 Oct 2021 11:31:49 +0200 Subject: [PATCH 09/11] No longer skip environment tests and adapt them to new behavior --- tests/environment_test.go | 79 +++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/tests/environment_test.go b/tests/environment_test.go index 416806bd..5aef492a 100644 --- a/tests/environment_test.go +++ b/tests/environment_test.go @@ -1,12 +1,14 @@ package icingadb_test import ( - "bytes" "encoding/hex" - "fmt" + "encoding/json" + "github.com/icinga/icinga-testing/services" + "github.com/icinga/icinga-testing/utils/eventually" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" + "sort" "testing" "time" ) @@ -15,26 +17,42 @@ func TestMultipleEnvironments(t *testing.T) { m := it.MysqlDatabaseT(t) m.ImportIcingaDbSchema() - envs := []string{"", "some-env", "other-env"} + numEnvs := 3 + icinga2Instances := make([]services.Icinga2, numEnvs) + // Start numEnvs icinga2 instances with an icingadb instance each, all writing to the same mysql database. var g errgroup.Group - for _, env := range envs { - env := env + for i := range icinga2Instances { + i := i g.Go(func() error { r := it.RedisServerT(t) - i := it.Icinga2NodeT(t, "master") - conf := bytes.NewBuffer(nil) - _, _ = fmt.Fprintf(conf, "const Environment = %q\n", env) - i.WriteConfig("etc/icinga2/conf.d/testdata.conf", conf.Bytes()) - i.EnableIcingaDb(r) - i.Reload() + icinga2Instances[i] = it.Icinga2NodeT(t, "master") + icinga2Instances[i].EnableIcingaDb(r) + icinga2Instances[i].Reload() it.IcingaDbInstanceT(t, r, m) return nil }) } _ = g.Wait() + // Query the IcingaDB environment_id from each icinga2 instance. + var expectedEnvs []string + for _, instance := range icinga2Instances { + res, err := instance.ApiClient().GetJson("/v1/objects/icingadbs") + require.NoError(t, err, "requesting IcingaDB objects from API should succeed") + var objects ObjectsIcingaDBsResponse + err = json.NewDecoder(res.Body).Decode(&objects) + require.NoError(t, err, "requesting IcingaDB objects from API should succeed") + require.NotEmpty(t, objects.Results, "API response should return an IcingaDB object") + expectedEnvs = append(expectedEnvs, objects.Results[0].Attrs.EnvironmentId) + } + + sort.Strings(expectedEnvs) + for i := 0; i < len(expectedEnvs)-1; i++ { + require.NotEqual(t, expectedEnvs[i], expectedEnvs[i+1], "all environment IDs should be distinct") + } + db, err := m.Open() require.NoError(t, err, "mysql open") t.Cleanup(func() { _ = db.Close() }) @@ -42,39 +60,28 @@ func TestMultipleEnvironments(t *testing.T) { t.Run("Table", func(t *testing.T) { t.Parallel() - // TODO(jb): needs fix for issue #292 - t.Skip("environment table is currently not populated") - - assert.Eventually(t, func() bool { - expectedRows := make(map[string]struct{}) - for _, env := range envs { - expectedRows[env] = struct{}{} - } - - rows, err := db.Query("SELECT id, name FROM environment") + eventually.Assert(t, func(t require.TestingT) { + rows, err := db.Query("SELECT LOWER(HEX(id)), name FROM environment ORDER BY id") require.NoError(t, err, "mysql query") defer rows.Close() + var gotEnvs []string for rows.Next() { var id, name string err := rows.Scan(&id, &name) require.NoError(t, err, "mysql scan") - - if _, ok := expectedRows[name]; ok { - delete(expectedRows, name) - } else { - return false - } + require.Equal(t, id, name, "name should be initialized to the environment id") + gotEnvs = append(gotEnvs, id) } - return len(expectedRows) == 0 - }, 20*time.Second, 1*time.Second, "there should be one row in the environments tables for each one") + require.Equal(t, expectedEnvs, gotEnvs, "each environment should be present in the environments table") + }, 20*time.Second, 250*time.Millisecond) }) t.Run("HA", func(t *testing.T) { t.Parallel() - assert.Eventually(t, func() bool { + eventually.Assert(t, func(t require.TestingT) { rows, err := db.Query("SELECT environment_id, COUNT(*) FROM icingadb_instance WHERE responsible = 'y' GROUP BY environment_id") require.NoError(t, err, "mysql query") defer rows.Close() @@ -90,7 +97,15 @@ func TestMultipleEnvironments(t *testing.T) { "environment %s must have at most one active instance", hex.EncodeToString(env)) numRows++ } - return numRows == len(envs) - }, 20*time.Second, 1*time.Second, "there should be one active instance per environment") + require.Equal(t, numEnvs, numRows, "each environment should have one active instance") + }, 20*time.Second, 250*time.Millisecond) }) } + +type ObjectsIcingaDBsResponse struct { + Results []struct { + Attrs struct { + EnvironmentId string `json:"environment_id"` + } `json:"attrs"` + } `json:"results"` +} From 6cf92666556d9888e31774ff6fab3de06a82510d Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Fri, 15 Oct 2021 17:32:52 +0200 Subject: [PATCH 10/11] Update schema comments to match new use of environments There was a change to how environments work in icinga2 that affects how many IDs are structured: https://github.com/Icinga/icinga2/pull/9036 --- schema/mysql/schema.sql | 152 ++++++++++++++-------------- schema/mysql/upgrades/1.0.0-rc2.sql | 128 +++++++++++++++++++++++ 2 files changed, 204 insertions(+), 76 deletions(-) diff --git a/schema/mysql/schema.sql b/schema/mysql/schema.sql index faae4e02..73aa73db 100644 --- a/schema/mysql/schema.sql +++ b/schema/mysql/schema.sql @@ -4,8 +4,8 @@ SET SESSION sql_mode = 'STRICT_ALL_TABLES,NO_ENGINE_SUBSTITUTION'; SET SESSION innodb_strict_mode = 1; CREATE TABLE host ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', name_checksum binary(20) NOT NULL COMMENT 'sha1(name)', properties_checksum binary(20) NOT NULL COMMENT 'sha1(all properties)', @@ -69,8 +69,8 @@ CREATE TABLE host ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE hostgroup ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', name_checksum binary(20) NOT NULL COMMENT 'sha1(name)', properties_checksum binary(20) NOT NULL COMMENT 'sha1(all properties)', @@ -84,8 +84,8 @@ CREATE TABLE hostgroup ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE hostgroup_member ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + host_id + hostgroup_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + host_id + hostgroup_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', host_id binary(20) NOT NULL COMMENT 'host.id', hostgroup_id binary(20) NOT NULL COMMENT 'hostgroup.id', @@ -96,8 +96,8 @@ CREATE TABLE hostgroup_member ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE host_customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + host_id + customvar_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + host_id + customvar_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', host_id binary(20) NOT NULL COMMENT 'host.id', customvar_id binary(20) NOT NULL COMMENT 'customvar.id', @@ -108,8 +108,8 @@ CREATE TABLE host_customvar ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE hostgroup_customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + hostgroup_id + customvar_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + hostgroup_id + customvar_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', hostgroup_id binary(20) NOT NULL COMMENT 'hostgroup.id', customvar_id binary(20) NOT NULL COMMENT 'customvar.id', @@ -122,7 +122,7 @@ CREATE TABLE hostgroup_customvar ( CREATE TABLE host_state ( id binary(20) NOT NULL COMMENT 'host.id', host_id binary(20) NOT NULL COMMENT 'host.id', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', properties_checksum binary(20) NOT NULL COMMENT 'sha1(all properties)', state_type enum('hard', 'soft') NOT NULL, @@ -166,8 +166,8 @@ CREATE TABLE host_state ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE service ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', name_checksum binary(20) NOT NULL COMMENT 'sha1(name)', properties_checksum binary(20) NOT NULL COMMENT 'sha1(all properties)', host_id binary(20) NOT NULL COMMENT 'sha1(host.id)', @@ -225,8 +225,8 @@ CREATE TABLE service ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE servicegroup ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', name_checksum binary(20) NOT NULL COMMENT 'sha1(name)', properties_checksum binary(20) NOT NULL COMMENT 'sha1(all properties)', @@ -240,8 +240,8 @@ CREATE TABLE servicegroup ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE servicegroup_member ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + servicegroup_id + service_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + servicegroup_id + service_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', service_id binary(20) NOT NULL COMMENT 'service.id', servicegroup_id binary(20) NOT NULL COMMENT 'servicegroup.id', @@ -252,8 +252,8 @@ CREATE TABLE servicegroup_member ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE service_customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + service_id + customvar_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + service_id + customvar_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', service_id binary(20) NOT NULL COMMENT 'service.id', customvar_id binary(20) NOT NULL COMMENT 'customvar.id', @@ -265,8 +265,8 @@ CREATE TABLE service_customvar ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE servicegroup_customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + servicegroup_id + customvar_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + servicegroup_id + customvar_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', servicegroup_id binary(20) NOT NULL COMMENT 'servicegroup.id', customvar_id binary(20) NOT NULL COMMENT 'customvar.id', @@ -279,7 +279,7 @@ CREATE TABLE servicegroup_customvar ( CREATE TABLE service_state ( id binary(20) NOT NULL COMMENT 'service.id', service_id binary(20) NOT NULL COMMENT 'service.id', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', properties_checksum binary(20) NOT NULL COMMENT 'sha1(all properties)', state_type enum('hard', 'soft') NOT NULL, @@ -324,8 +324,8 @@ CREATE TABLE service_state ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE endpoint ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', name_checksum binary(20) NOT NULL COMMENT 'sha1(name)', properties_checksum binary(20) NOT NULL, @@ -338,7 +338,7 @@ CREATE TABLE endpoint ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE environment ( - id binary(20) NOT NULL COMMENT 'sha1(name)', + id binary(20) NOT NULL COMMENT 'sha1(Icinga CA public key)', name varchar(255) NOT NULL, PRIMARY KEY (id) @@ -375,7 +375,7 @@ INSERT INTO icingadb_schema (version, timestamp) VALUES (2, CURRENT_TIMESTAMP() * 1000); CREATE TABLE checkcommand ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + type + name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + type + name)', environment_id binary(20) NOT NULL COMMENT 'env.id', zone_id binary(20) DEFAULT NULL COMMENT 'zone.id', @@ -391,7 +391,7 @@ CREATE TABLE checkcommand ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE checkcommand_argument ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + command_id + argument_key)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + argument_key)', environment_id binary(20) NOT NULL COMMENT 'env.id', command_id binary(20) NOT NULL COMMENT 'command.id', argument_key varchar(64) NOT NULL, @@ -411,7 +411,7 @@ CREATE TABLE checkcommand_argument ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE checkcommand_envvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + command_id + envvar_key)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + envvar_key)', environment_id binary(20) NOT NULL COMMENT 'env.id', command_id binary(20) NOT NULL COMMENT 'command.id', envvar_key varchar(64) NOT NULL, @@ -424,8 +424,8 @@ CREATE TABLE checkcommand_envvar ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE checkcommand_customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + command_id + customvar_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + customvar_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', command_id binary(20) NOT NULL COMMENT 'command.id', customvar_id binary(20) NOT NULL COMMENT 'customvar.id', @@ -438,7 +438,7 @@ CREATE TABLE checkcommand_customvar ( CREATE TABLE eventcommand ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + type + name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + type + name)', environment_id binary(20) NOT NULL COMMENT 'env.id', zone_id binary(20) DEFAULT NULL COMMENT 'zone.id', @@ -454,7 +454,7 @@ CREATE TABLE eventcommand ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE eventcommand_argument ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + command_id + argument_key)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + argument_key)', environment_id binary(20) NOT NULL COMMENT 'env.id', command_id binary(20) NOT NULL COMMENT 'command.id', argument_key varchar(64) NOT NULL, @@ -474,7 +474,7 @@ CREATE TABLE eventcommand_argument ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE eventcommand_envvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + command_id + envvar_key)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + envvar_key)', environment_id binary(20) NOT NULL COMMENT 'env.id', command_id binary(20) NOT NULL COMMENT 'command.id', envvar_key varchar(64) NOT NULL, @@ -487,8 +487,8 @@ CREATE TABLE eventcommand_envvar ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE eventcommand_customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + command_id + customvar_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + customvar_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', command_id binary(20) NOT NULL COMMENT 'command.id', customvar_id binary(20) NOT NULL COMMENT 'customvar.id', @@ -499,7 +499,7 @@ CREATE TABLE eventcommand_customvar ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE notificationcommand ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + type + name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + type + name)', environment_id binary(20) NOT NULL COMMENT 'env.id', zone_id binary(20) DEFAULT NULL COMMENT 'zone.id', @@ -515,7 +515,7 @@ CREATE TABLE notificationcommand ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE notificationcommand_argument ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + command_id + argument_key)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + argument_key)', environment_id binary(20) NOT NULL COMMENT 'env.id', command_id binary(20) NOT NULL COMMENT 'command.id', argument_key varchar(64) NOT NULL, @@ -535,7 +535,7 @@ CREATE TABLE notificationcommand_argument ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE notificationcommand_envvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + command_id + envvar_key)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + envvar_key)', environment_id binary(20) NOT NULL COMMENT 'env.id', command_id binary(20) NOT NULL COMMENT 'command.id', envvar_key varchar(64) NOT NULL, @@ -548,8 +548,8 @@ CREATE TABLE notificationcommand_envvar ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE notificationcommand_customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + command_id + customvar_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + customvar_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', command_id binary(20) NOT NULL COMMENT 'command.id', customvar_id binary(20) NOT NULL COMMENT 'customvar.id', @@ -560,7 +560,7 @@ CREATE TABLE notificationcommand_customvar ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE comment ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', environment_id binary(20) NOT NULL COMMENT 'environment.id', object_type enum('host', 'service') NOT NULL, @@ -588,7 +588,7 @@ CREATE TABLE comment ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE downtime ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', environment_id binary(20) NOT NULL COMMENT 'environment.id', triggered_by_id binary(20) DEFAULT NULL COMMENT 'The ID of the downtime that triggered this downtime. This is set when creating downtimes on a host or service higher up in the dependency chain using the "child_option" "DowntimeTriggeredChildren" and can also be set manually via the API.', @@ -623,8 +623,8 @@ CREATE TABLE downtime ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE notification ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', name_checksum binary(20) NOT NULL COMMENT 'sha1(name)', properties_checksum binary(20) NOT NULL, @@ -652,7 +652,7 @@ CREATE TABLE notification ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE notification_user ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + notification_id + user_id)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + notification_id + user_id)', environment_id binary(20) NOT NULL COMMENT 'environment.id', notification_id binary(20) NOT NULL COMMENT 'notification.id', user_id binary(20) NOT NULL COMMENT 'user.id', @@ -664,7 +664,7 @@ CREATE TABLE notification_user ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE notification_usergroup ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + notification_id + usergroup_id)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + notification_id + usergroup_id)', environment_id binary(20) NOT NULL COMMENT 'environment.id', notification_id binary(20) NOT NULL COMMENT 'notification.id', usergroup_id binary(20) NOT NULL COMMENT 'usergroup.id', @@ -676,7 +676,7 @@ CREATE TABLE notification_usergroup ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE notification_recipient ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + notification_id + (user_id | usergroup_id))', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + notification_id + (user_id | usergroup_id))', environment_id binary(20) NOT NULL COMMENT 'environment.id', notification_id binary(20) NOT NULL COMMENT 'notification.id', user_id binary(20) NULL COMMENT 'user.id', @@ -691,8 +691,8 @@ CREATE TABLE notification_recipient ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE notification_customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + notification_id + customvar_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + notification_id + customvar_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', notification_id binary(20) NOT NULL COMMENT 'notification.id', customvar_id binary(20) NOT NULL COMMENT 'customvar.id', @@ -704,7 +704,7 @@ CREATE TABLE notification_customvar ( CREATE TABLE icon_image ( id binary(20) NOT NULL COMMENT 'sha1(icon_image)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', icon_image text COLLATE utf8mb4_unicode_ci NOT NULL, PRIMARY KEY (environment_id, id), @@ -713,7 +713,7 @@ CREATE TABLE icon_image ( CREATE TABLE action_url ( id binary(20) NOT NULL COMMENT 'sha1(action_url)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', action_url text COLLATE utf8mb4_unicode_ci NOT NULL, PRIMARY KEY (environment_id, id), @@ -722,7 +722,7 @@ CREATE TABLE action_url ( CREATE TABLE notes_url ( id binary(20) NOT NULL COMMENT 'sha1(notes_url)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', notes_url text COLLATE utf8mb4_unicode_ci NOT NULL, PRIMARY KEY (environment_id, id), @@ -730,7 +730,7 @@ CREATE TABLE notes_url ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE timeperiod ( - id binary(20) NOT NULL COMMENT 'sha1(env.name + name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', environment_id binary(20) NOT NULL COMMENT 'env.id', name_checksum binary(20) NOT NULL COMMENT 'sha1(name)', @@ -747,7 +747,7 @@ CREATE TABLE timeperiod ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE timeperiod_range ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + range_id + timeperiod_id)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + range_id + timeperiod_id)', environment_id binary(20) NOT NULL COMMENT 'env.id', timeperiod_id binary(20) NOT NULL COMMENT 'timeperiod.id', range_key varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, @@ -758,7 +758,7 @@ CREATE TABLE timeperiod_range ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE timeperiod_override_include ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + include_id + timeperiod_id)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + include_id + timeperiod_id)', environment_id binary(20) NOT NULL COMMENT 'env.id', timeperiod_id binary(20) NOT NULL COMMENT 'timeperiod.id', override_id binary(20) NOT NULL COMMENT 'timeperiod.id', @@ -767,7 +767,7 @@ CREATE TABLE timeperiod_override_include ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE timeperiod_override_exclude ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + exclude_id + timeperiod_id)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + exclude_id + timeperiod_id)', environment_id binary(20) NOT NULL COMMENT 'env.id', timeperiod_id binary(20) NOT NULL COMMENT 'timeperiod.id', override_id binary(20) NOT NULL COMMENT 'timeperiod.id', @@ -776,8 +776,8 @@ CREATE TABLE timeperiod_override_exclude ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE timeperiod_customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + timeperiod_id + customvar_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + timeperiod_id + customvar_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', timeperiod_id binary(20) NOT NULL COMMENT 'timeperiod.id', customvar_id binary(20) NOT NULL COMMENT 'customvar.id', @@ -788,8 +788,8 @@ CREATE TABLE timeperiod_customvar ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name + value)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name + value)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', name_checksum binary(20) NOT NULL COMMENT 'sha1(name)', name varchar(255) NOT NULL, @@ -799,8 +799,8 @@ CREATE TABLE customvar ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE customvar_flat ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + flatname + flatvalue)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + flatname + flatvalue)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', customvar_id binary(20) NOT NULL COMMENT 'sha1(customvar.id)', flatname_checksum binary(20) NOT NULL COMMENT 'sha1(flatname after conversion)', @@ -813,8 +813,8 @@ CREATE TABLE customvar_flat ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE user ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', name_checksum binary(20) NOT NULL COMMENT 'sha1(name)', properties_checksum binary(20) NOT NULL COMMENT 'sha1(all properties)', @@ -842,8 +842,8 @@ CREATE TABLE user ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE usergroup ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', name_checksum binary(20) NOT NULL COMMENT 'sha1(name)', properties_checksum binary(20) NOT NULL COMMENT 'sha1(all properties)', @@ -861,8 +861,8 @@ CREATE TABLE usergroup ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE usergroup_member ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + usergroup_id + user_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + usergroup_id + user_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', user_id binary(20) NOT NULL COMMENT 'user.id', usergroup_id binary(20) NOT NULL COMMENT 'usergroup.id', @@ -873,8 +873,8 @@ CREATE TABLE usergroup_member ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE user_customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + user_id + customvar_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + user_id + customvar_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', user_id binary(20) NOT NULL COMMENT 'user.id', customvar_id binary(20) NOT NULL COMMENT 'customvar.id', @@ -885,8 +885,8 @@ CREATE TABLE user_customvar ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE usergroup_customvar ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + usergroup_id + customvar_id)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + usergroup_id + customvar_id)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', usergroup_id binary(20) NOT NULL COMMENT 'usergroup.id', customvar_id binary(20) NOT NULL COMMENT 'customvar.id', @@ -897,8 +897,8 @@ CREATE TABLE usergroup_customvar ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE zone ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + name)', - environment_id binary(20) NOT NULL COMMENT 'sha1(environment.name)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + environment_id binary(20) NOT NULL COMMENT 'environment.id', name_checksum binary(20) NOT NULL COMMENT 'sha1(name)', properties_checksum binary(20) NOT NULL COMMENT 'sha1(all properties)', @@ -1023,7 +1023,7 @@ CREATE TABLE comment_history ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE flapping_history ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + "Host"|"Service" + host|service.name + start_time)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + "Host"|"Service" + host|service.name + start_time)', environment_id binary(20) NOT NULL COMMENT 'environment.id', endpoint_id binary(20) DEFAULT NULL COMMENT 'endpoint.id', object_type enum('host', 'service') NOT NULL, @@ -1041,7 +1041,7 @@ CREATE TABLE flapping_history ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC; CREATE TABLE acknowledgement_history ( - id binary(20) NOT NULL COMMENT 'sha1(environment.name + "Host"|"Service" + host|service.name + set_time)', + id binary(20) NOT NULL COMMENT 'sha1(environment.id + "Host"|"Service" + host|service.name + set_time)', environment_id binary(20) NOT NULL COMMENT 'environment.id', endpoint_id binary(20) DEFAULT NULL COMMENT 'endpoint.id', object_type enum('host', 'service') NOT NULL, diff --git a/schema/mysql/upgrades/1.0.0-rc2.sql b/schema/mysql/upgrades/1.0.0-rc2.sql index 43a7e663..78891e72 100644 --- a/schema/mysql/upgrades/1.0.0-rc2.sql +++ b/schema/mysql/upgrades/1.0.0-rc2.sql @@ -194,3 +194,131 @@ ALTER TABLE history ALTER TABLE user_notification_history ADD CONSTRAINT fk_user_notification_history_notification_history FOREIGN KEY (notification_history_id) REFERENCES notification_history (id) ON DELETE CASCADE; + +ALTER TABLE host + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE hostgroup + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE hostgroup_member + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + host_id + hostgroup_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE host_customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + host_id + customvar_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE hostgroup_customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + hostgroup_id + customvar_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE host_state + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE service + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE servicegroup + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE servicegroup_member + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + servicegroup_id + service_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE service_customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + service_id + customvar_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE servicegroup_customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + servicegroup_id + customvar_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE service_state + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE endpoint + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE environment + MODIFY id binary(20) NOT NULL COMMENT 'sha1(Icinga CA public key)'; +ALTER TABLE checkcommand + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + type + name)'; +ALTER TABLE checkcommand_argument + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + argument_key)'; +ALTER TABLE checkcommand_envvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + envvar_key)'; +ALTER TABLE checkcommand_customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + customvar_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE eventcommand + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + type + name)'; +ALTER TABLE eventcommand_argument + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + argument_key)'; +ALTER TABLE eventcommand_envvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + envvar_key)'; +ALTER TABLE eventcommand_customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + customvar_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE notificationcommand + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + type + name)'; +ALTER TABLE notificationcommand_argument + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + argument_key)'; +ALTER TABLE notificationcommand_envvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + envvar_key)'; +ALTER TABLE notificationcommand_customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + command_id + customvar_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE comment + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)'; +ALTER TABLE downtime + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)'; +ALTER TABLE notification + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE notification_user + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + notification_id + user_id)'; +ALTER TABLE notification_usergroup + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + notification_id + usergroup_id)'; +ALTER TABLE notification_recipient + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + notification_id + (user_id | usergroup_id))'; +ALTER TABLE notification_customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + notification_id + customvar_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE icon_image + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE action_url + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE notes_url + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE timeperiod + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)'; +ALTER TABLE timeperiod_range + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + range_id + timeperiod_id)'; +ALTER TABLE timeperiod_override_include + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + include_id + timeperiod_id)'; +ALTER TABLE timeperiod_override_exclude + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + exclude_id + timeperiod_id)'; +ALTER TABLE timeperiod_customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + timeperiod_id + customvar_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name + value)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE customvar_flat + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + flatname + flatvalue)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE user + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE usergroup + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE usergroup_member + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + usergroup_id + user_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE user_customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + user_id + customvar_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE usergroup_customvar + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + usergroup_id + customvar_id)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE zone + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + name)', + MODIFY environment_id binary(20) NOT NULL COMMENT 'environment.id'; +ALTER TABLE flapping_history + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + "Host"|"Service" + host|service.name + start_time)'; +ALTER TABLE acknowledgement_history + MODIFY id binary(20) NOT NULL COMMENT 'sha1(environment.id + "Host"|"Service" + host|service.name + set_time)'; From 74fb2389230ec333735fe681c6c579a008cbd6fc Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Fri, 29 Oct 2021 10:35:56 +0200 Subject: [PATCH 11/11] Increase Redis schema version PR #391 and icinga2#9036 changed where the environment is read from in an incompatible way, therefore the schema version bump. --- cmd/icingadb/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/icingadb/main.go b/cmd/icingadb/main.go index 4c404794..aac043bf 100644 --- a/cmd/icingadb/main.go +++ b/cmd/icingadb/main.go @@ -26,7 +26,7 @@ import ( const ( ExitSuccess = 0 ExitFailure = 1 - expectedRedisSchemaVersion = "2" + expectedRedisSchemaVersion = "3" expectedDbSchemaVersion = 2 )