mirror of
https://github.com/openziti/ziti.git
synced 2026-09-10 08:45:41 +00:00
d8cedeb8ea
- adds a clusterId field to SyncSnapshotCommand and writes it into the database after the migration snapshot restore, so a controller bootstrapped by migrating a database ends up with a durable cluster id instead of an empty one - the snapshot restore replaces the whole FSM database with the migration source, which carries no cluster id, so without this the id written during bootstrap was silently wiped and the node came up with an empty, non-durable cluster id, defeating the mesh cluster-id validation - persists the raft index after the cluster id in RestoreSnapshot so the index remains the completion gate: a failure before it halts (SyncSnapshotCommand is a critical command) and replays/retries on restart rather than skipping the command with a blank cluster id - fails RaftRestoreFromBoltDb when the cluster id is blank after bootstrap - regenerates cmd.pb.go for the new field
175 lines
5.2 KiB
Go
175 lines
5.2 KiB
Go
package command
|
|
|
|
import (
|
|
"github.com/openziti/ziti/v2/common/pb/cmd_pb"
|
|
"github.com/openziti/ziti/v2/controller/change"
|
|
"github.com/openziti/ziti/v2/controller/fields"
|
|
"github.com/openziti/ziti/v2/controller/models"
|
|
"github.com/openziti/ziti/v2/controller/storage/boltz"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// EntityMarshaller instances can marshal and unmarshal entities of the type that they manage
|
|
// as well as knowing their entity type
|
|
type EntityMarshaller[T any] interface {
|
|
// GetEntityTypeId returns the entity type id. This is distinct from the Store entity id
|
|
// which may be shared by types, such as service and router. The entity type is unique
|
|
// for each type
|
|
GetEntityTypeId() string
|
|
|
|
// Marshall marshals the entity to a bytes encoded format
|
|
Marshall(entity T) ([]byte, error)
|
|
|
|
// Unmarshall unmarshalls the bytes back into an entity
|
|
Unmarshall(bytes []byte) (T, error)
|
|
}
|
|
|
|
// EntityCreator instances can apply a create entity command to create entities of a given type
|
|
type EntityCreator[T models.Entity] interface {
|
|
EntityMarshaller[T]
|
|
|
|
// ApplyCreate creates the entity described by the given command
|
|
ApplyCreate(cmd *CreateEntityCommand[T], ctx boltz.MutateContext) error
|
|
}
|
|
|
|
// EntityUpdater instances can apply an update entity command to update entities of a given type
|
|
type EntityUpdater[T models.Entity] interface {
|
|
EntityMarshaller[T]
|
|
|
|
// ApplyUpdate updates the entity described by the given command
|
|
ApplyUpdate(cmd *UpdateEntityCommand[T], ctx boltz.MutateContext) error
|
|
}
|
|
|
|
// EntityDeleter instances can apply a delete entity command to delete entities of a given type
|
|
type EntityDeleter interface {
|
|
GetEntityTypeId() string
|
|
|
|
// ApplyDelete deletes the entity described by the given command
|
|
ApplyDelete(cmd *DeleteEntityCommand, ctx boltz.MutateContext) error
|
|
}
|
|
|
|
// EntityManager instances can handle create, update and delete entities of a specific type
|
|
type EntityManager[T models.Entity] interface {
|
|
EntityCreator[T]
|
|
EntityUpdater[T]
|
|
EntityDeleter
|
|
}
|
|
|
|
type CreateEntityCommand[T models.Entity] struct {
|
|
Context *change.Context
|
|
Creator EntityCreator[T]
|
|
Entity T
|
|
PostCreateHook func(ctx boltz.MutateContext, entity T) error
|
|
Flags uint32
|
|
}
|
|
|
|
func (self *CreateEntityCommand[T]) Apply(ctx boltz.MutateContext) error {
|
|
return self.Creator.ApplyCreate(self, ctx)
|
|
}
|
|
|
|
func (self *CreateEntityCommand[T]) Encode() ([]byte, error) {
|
|
entityType := self.Creator.GetEntityTypeId()
|
|
encodedEntity, err := self.Creator.Marshall(self.Entity)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error mashalling entity of type %T (%v)", self.Entity, entityType)
|
|
}
|
|
return cmd_pb.EncodeProtobuf(&cmd_pb.CreateEntityCommand{
|
|
Ctx: self.Context.ToProtoBuf(),
|
|
EntityType: entityType,
|
|
EntityData: encodedEntity,
|
|
Flags: self.Flags,
|
|
})
|
|
}
|
|
|
|
func (self *CreateEntityCommand[T]) GetChangeContext() *change.Context {
|
|
return self.Context
|
|
}
|
|
|
|
type UpdateEntityCommand[T models.Entity] struct {
|
|
Context *change.Context
|
|
Updater EntityUpdater[T]
|
|
Entity T
|
|
UpdatedFields fields.UpdatedFields
|
|
Flags uint32
|
|
}
|
|
|
|
func (self *UpdateEntityCommand[T]) Apply(ctx boltz.MutateContext) error {
|
|
return self.Updater.ApplyUpdate(self, ctx)
|
|
}
|
|
|
|
func (self *UpdateEntityCommand[T]) Encode() ([]byte, error) {
|
|
entityType := self.Updater.GetEntityTypeId()
|
|
encodedEntity, err := self.Updater.Marshall(self.Entity)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error mashalling entity of type %T (%v)", self.Entity, entityType)
|
|
}
|
|
|
|
updatedFields, err := fields.UpdatedFieldsToSlice(self.UpdatedFields)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return cmd_pb.EncodeProtobuf(&cmd_pb.UpdateEntityCommand{
|
|
Ctx: self.Context.ToProtoBuf(),
|
|
EntityType: entityType,
|
|
EntityData: encodedEntity,
|
|
UpdatedFields: updatedFields,
|
|
Flags: self.Flags,
|
|
})
|
|
}
|
|
|
|
type DeleteEntityCommand struct {
|
|
Context *change.Context
|
|
Deleter EntityDeleter
|
|
Id string
|
|
}
|
|
|
|
func (self *UpdateEntityCommand[T]) GetChangeContext() *change.Context {
|
|
return self.Context
|
|
}
|
|
|
|
func (self *DeleteEntityCommand) Apply(ctx boltz.MutateContext) error {
|
|
return self.Deleter.ApplyDelete(self, ctx)
|
|
}
|
|
|
|
func (self *DeleteEntityCommand) Encode() ([]byte, error) {
|
|
return cmd_pb.EncodeProtobuf(&cmd_pb.DeleteEntityCommand{
|
|
Ctx: self.Context.ToProtoBuf(),
|
|
EntityId: self.Id,
|
|
EntityType: self.Deleter.GetEntityTypeId(),
|
|
})
|
|
}
|
|
|
|
func (self *DeleteEntityCommand) GetChangeContext() *change.Context {
|
|
return self.Context
|
|
}
|
|
|
|
var _ CriticalCommand = (*SyncSnapshotCommand)(nil)
|
|
|
|
type SyncSnapshotCommand struct {
|
|
TimelineId string
|
|
Snapshot []byte
|
|
ClusterId string
|
|
SnapshotSink func(cmd *SyncSnapshotCommand, index uint64) error
|
|
}
|
|
|
|
func (self *SyncSnapshotCommand) Apply(ctx boltz.MutateContext) error {
|
|
changeCtx := change.FromContext(ctx.Context())
|
|
return self.SnapshotSink(self, changeCtx.RaftIndex)
|
|
}
|
|
|
|
// IsCriticalCommand marks SyncSnapshotCommand as base state: a failed apply halts rather than advances.
|
|
func (self *SyncSnapshotCommand) IsCriticalCommand() {}
|
|
|
|
func (self *SyncSnapshotCommand) Encode() ([]byte, error) {
|
|
return cmd_pb.EncodeProtobuf(&cmd_pb.SyncSnapshotCommand{
|
|
SnapshotId: self.TimelineId,
|
|
Snapshot: self.Snapshot,
|
|
ClusterId: self.ClusterId,
|
|
})
|
|
}
|
|
|
|
func (self *SyncSnapshotCommand) GetChangeContext() *change.Context {
|
|
return nil
|
|
}
|