Merge pull request #391 from Icinga/bugfix/multi-environment

Better handling of multiple environments
This commit is contained in:
Julian Brost 2021-11-05 16:55:21 +01:00 committed by GitHub
commit 54dbe0cfbe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 492 additions and 157 deletions

View file

@ -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)
}

View file

@ -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{}
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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,
}
}

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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[:]
}

View file

@ -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,

View file

@ -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)';

View file

@ -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"`
}