diff --git a/cmd/icingadb/main.go b/cmd/icingadb/main.go index 239455ac..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 ) @@ -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. @@ -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) } 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 8d9740c9..606e7ebf 100644 --- a/pkg/icingadb/db.go +++ b/pkg/icingadb/db.go @@ -118,14 +118,35 @@ 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 { - 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. @@ -170,6 +191,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. @@ -391,10 +424,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) @@ -408,7 +441,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 d67e5bc4..30d31222 100644 --- a/pkg/icingadb/ha.go +++ b/pkg/icingadb/ha.go @@ -1,6 +1,7 @@ package icingadb import ( + "bytes" "context" "database/sql" "encoding/hex" @@ -22,19 +23,21 @@ 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 - heartbeat *icingaredis.Heartbeat - logger *zap.SugaredLogger - responsible bool - handover chan struct{} - takeover 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{} + done chan struct{} + errOnce sync.Once + errMu sync.Mutex + err error } // NewHA returns a new HA and starts the controller loop. @@ -53,7 +56,6 @@ func NewHA(ctx context.Context, db *DB, heartbeat *icingaredis.Heartbeat, logger handover: make(chan struct{}), takeover: make(chan struct{}), done: make(chan struct{}), - mu: &sync.Mutex{}, } go ha.controller() @@ -78,10 +80,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 } @@ -98,9 +108,9 @@ func (h *HA) Takeover() 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() }) @@ -141,6 +151,33 @@ func (h *HA) controller() { h.abort(err) } + envId, err := m.EnvironmentID() + if err != nil { + h.abort(err) + } + + if h.environment == nil || !bytes.Equal(h.environment.Id, envId) { + if h.environment != nil { + h.logger.Fatalw("Environment changed unexpectedly", + zap.String("current", h.environment.Id.String()), + zap.String("new", envId.String())) + } + + h.environmentMu.Lock() + h.environment = &v1.Environment{ + EntityWithoutChecksum: v1.EntityWithoutChecksum{IdMeta: v1.IdMeta{ + Id: envId, + }}, + Name: types.String{ + NullString: sql.NullString{ + String: envId.String(), + Valid: true, + }, + }, + } + h.environmentMu.Unlock() + } + select { case <-logTicker.C: shouldLog = true @@ -154,7 +191,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() @@ -165,7 +202,7 @@ func (h *HA) controller() { } if !oldInstancesRemoved { - go h.removeOldInstances(s) + go h.removeOldInstances(s, envId) oldInstancesRemoved = true } @@ -184,7 +221,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)) @@ -199,7 +236,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) @@ -212,7 +249,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 } @@ -225,7 +266,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}, @@ -264,7 +305,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(); err != nil { + cancelCtx() + return errors.Wrap(err, "can't insert environment") + } + h.signalTakeover() } @@ -275,6 +324,18 @@ 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() error { + // Instead of checking whether the environment already exists, use an INSERT statement that does nothing if it does. + stmt, _ := h.db.BuildInsertIgnoreStmt(h.environment) + + if _, err := h.db.NamedExecContext(h.ctx, stmt, h.environment); 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. @@ -285,7 +346,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 @@ -293,12 +354,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/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 new file mode 100644 index 00000000..fdddc5be --- /dev/null +++ b/pkg/icingadb/v1/environment.go @@ -0,0 +1,40 @@ +package v1 + +import ( + "context" + "github.com/icinga/icingadb/pkg/types" +) + +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 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[:] -} 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)'; 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"` +}