refactor: remove bootstrap sequence

Refactor things to remove the bootstrap sequence, this should help with
the task of sequencer concurrency changes and immediate API feedback.

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
Andrey Smirnov 2022-07-15 19:14:00 +04:00
parent 89c7da8991
commit f0b8eea5e5
No known key found for this signature in database
GPG Key ID: 7B26396447AB6DFD
7 changed files with 52 additions and 84 deletions

View File

@ -101,6 +101,8 @@ type Server struct {
machine.UnimplementedMachineServiceServer
Controller runtime.Controller
// breaking the import loop cycle between services/ package and v1alpha1_server.go
EtcdBootstrapper func(context.Context, runtime.Runtime, *machine.BootstrapRequest) error
server *grpc.Server
}
@ -425,17 +427,9 @@ func (s *Server) Bootstrap(ctx context.Context, in *machine.BootstrapRequest) (r
return nil, status.Error(codes.AlreadyExists, "etcd data directory is not empty")
}
go func() {
if err := s.Controller.Run(context.Background(), runtime.SequenceBootstrap, in); err != nil {
log.Println("bootstrap failed:", err)
if err != runtime.ErrLocked {
// NB: We stop the gRPC server since a failed sequence triggers a
// reboot.
s.server.GracefulStop()
}
}
}()
if err := s.EtcdBootstrapper(ctx, s.Controller.Runtime(), in); err != nil {
return nil, err
}
reply = &machine.BootstrapResponse{
Messages: []*machine.Bootstrap{

View File

@ -16,8 +16,6 @@ type Sequence int
const (
// SequenceBoot is the boot sequence.
SequenceBoot Sequence = iota
// SequenceBootstrap is the boot sequence.
SequenceBootstrap
// SequenceInitialize is the initialize sequence.
SequenceInitialize
// SequenceInstall is the install sequence.
@ -38,7 +36,6 @@ const (
const (
boot = "boot"
bootstrap = "bootstrap"
initialize = "initialize"
install = "install"
shutdown = "shutdown"
@ -51,7 +48,7 @@ const (
// String returns the string representation of a `Sequence`.
func (s Sequence) String() string {
return [...]string{boot, bootstrap, initialize, install, shutdown, upgrade, stageUpgrade, reset, reboot, noop}[s]
return [...]string{boot, initialize, install, shutdown, upgrade, stageUpgrade, reset, reboot, noop}[s]
}
// ParseSequence returns a `Sequence` that matches the specified string.
@ -61,8 +58,6 @@ func ParseSequence(s string) (seq Sequence, err error) {
switch s {
case boot:
seq = SequenceBoot
case bootstrap:
seq = SequenceBootstrap
case initialize:
seq = SequenceInitialize
case install:
@ -104,7 +99,6 @@ type PartitionTarget interface {
// management of the operating system.
type Sequencer interface {
Boot(Runtime) []Phase
Bootstrap(Runtime) []Phase
Initialize(Runtime) []Phase
Install(Runtime) []Phase
Reboot(Runtime) []Phase

View File

@ -95,7 +95,7 @@ func (c *Controller) Run(ctx context.Context, seq runtime.Sequence, data interfa
// Allow only one sequence to run at a time with the exception of bootstrap
// and reset sequences.
switch seq { //nolint:exhaustive
case runtime.SequenceBootstrap, runtime.SequenceReset:
case runtime.SequenceReset:
// Do not attempt to lock.
default:
if opts.Force {
@ -384,8 +384,6 @@ func (c *Controller) phases(seq runtime.Sequence, data interface{}) ([]runtime.P
switch seq {
case runtime.SequenceBoot:
phases = c.s.Boot(c.r)
case runtime.SequenceBootstrap:
phases = c.s.Bootstrap(c.r)
case runtime.SequenceInitialize:
phases = c.s.Initialize(c.r)
case runtime.SequenceInstall:

View File

@ -248,19 +248,6 @@ func (*Sequencer) Boot(r runtime.Runtime) []runtime.Phase {
return phases
}
// Bootstrap is the bootstrap sequence. This primary goal if this sequence is
// to bootstrap Etcd and Kubernetes.
func (*Sequencer) Bootstrap(r runtime.Runtime) []runtime.Phase {
phases := PhaseList{}
phases = phases.Append(
"etcd",
BootstrapEtcd,
)
return phases
}
// Reboot is the reboot sequence.
func (*Sequencer) Reboot(r runtime.Runtime) []runtime.Phase {
phases := PhaseList{}.Append(

View File

@ -51,7 +51,6 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform"
perrors "github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors"
"github.com/talos-systems/talos/internal/app/machined/pkg/system"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/events"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/services"
"github.com/talos-systems/talos/internal/app/maintenance"
"github.com/talos-systems/talos/internal/pkg/cri"
@ -1858,55 +1857,6 @@ func Install(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc,
}, "install"
}
// BootstrapEtcd represents the task for bootstrapping etcd.
func BootstrapEtcd(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {
req, ok := data.(*machineapi.BootstrapRequest)
if !ok {
return fmt.Errorf("failed to typecast boostrap request")
}
if err = system.Services(r).Stop(ctx, "etcd"); err != nil {
return fmt.Errorf("failed to stop etcd: %w", err)
}
// This is hack. We need to fake a finished state so that we can get the
// wait in the boot sequence to unblock.
for _, svc := range system.Services(r).List() {
if svc.AsProto().GetId() == "etcd" {
svc.UpdateState(events.StateFinished, "Bootstrap requested")
break
}
}
if entries, _ := os.ReadDir(constants.EtcdDataPath); len(entries) > 0 { //nolint:errcheck
return fmt.Errorf("etcd data directory is not empty")
}
svc := &services.Etcd{
Bootstrap: true,
RecoverFromSnapshot: req.RecoverEtcd,
RecoverSkipHashCheck: req.RecoverSkipHashCheck,
}
if err = system.Services(r).Unload(ctx, svc.ID(r)); err != nil {
return err
}
system.Services(r).Load(svc)
if err = system.Services(r).Start(svc.ID(r)); err != nil {
return fmt.Errorf("error starting etcd in bootstrap mode: %w", err)
}
ctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
defer cancel()
return system.WaitForService(system.StateEventUp, svc.ID(r)).Wait(ctx)
}, "bootstrapEtcd"
}
// ActivateLogicalVolumes represents the task for activating logical volumes.
func ActivateLogicalVolumes(seq runtime.Sequence, data interface{}) (runtime.TaskExecutionFunc, string) {
return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) {

View File

@ -32,6 +32,7 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/adv"
"github.com/talos-systems/talos/internal/app/machined/pkg/system"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/events"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/health"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/runner"
@ -42,6 +43,7 @@ import (
"github.com/talos-systems/talos/pkg/argsbuilder"
"github.com/talos-systems/talos/pkg/conditions"
"github.com/talos-systems/talos/pkg/logging"
machineapi "github.com/talos-systems/talos/pkg/machinery/api/machine"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/machinery/resources/k8s"
@ -755,3 +757,44 @@ func primaryAndListenAddresses(subnet string) (primary, listen string, err error
return primary, listen, nil
}
// BootstrapEtcd bootstraps the etcd cluster.
//
// Current instance of etcd (not joined yet) is stopped, and new instance is started in bootstrap mode.
func BootstrapEtcd(ctx context.Context, r runtime.Runtime, req *machineapi.BootstrapRequest) error {
if err := system.Services(r).Stop(ctx, "etcd"); err != nil {
return fmt.Errorf("failed to stop etcd: %w", err)
}
// This is hack. We need to fake a finished state so that we can get the
// wait in the boot sequence to unblock.
for _, svc := range system.Services(r).List() {
if svc.AsProto().GetId() == "etcd" {
svc.UpdateState(events.StateFinished, "Bootstrap requested")
break
}
}
if entries, _ := os.ReadDir(constants.EtcdDataPath); len(entries) > 0 { //nolint:errcheck
return fmt.Errorf("etcd data directory is not empty")
}
svc := &Etcd{
Bootstrap: true,
RecoverFromSnapshot: req.RecoverEtcd,
RecoverSkipHashCheck: req.RecoverSkipHashCheck,
}
if err := system.Services(r).Unload(ctx, svc.ID(r)); err != nil {
return err
}
system.Services(r).Load(svc)
if err := system.Services(r).Start(svc.ID(r)); err != nil {
return fmt.Errorf("error starting etcd in bootstrap mode: %w", err)
}
return nil
}

View File

@ -101,6 +101,8 @@ func (s *machinedService) Main(ctx context.Context, r runtime.Runtime, logWriter
server := factory.NewServer(
&v1alpha1server.Server{
Controller: s.c,
// breaking the import loop cycle between services/ package and v1alpha1_server.go
EtcdBootstrapper: BootstrapEtcd,
},
factory.WithLog("machined ", logWriter),