mirror of https://github.com/k3s-io/k3s
Cancellable leader election with context
parent
1d99fff1ac
commit
dc32a341c0
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
@ -135,7 +136,7 @@ func Run(c *cloudcontrollerconfig.CompletedConfig) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run := func(stop <-chan struct{}) {
|
run := func(ctx context.Context) {
|
||||||
rootClientBuilder := controller.SimpleControllerClientBuilder{
|
rootClientBuilder := controller.SimpleControllerClientBuilder{
|
||||||
ClientConfig: c.Kubeconfig,
|
ClientConfig: c.Kubeconfig,
|
||||||
}
|
}
|
||||||
|
@ -151,13 +152,16 @@ func Run(c *cloudcontrollerconfig.CompletedConfig) error {
|
||||||
clientBuilder = rootClientBuilder
|
clientBuilder = rootClientBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := startControllers(c, rootClientBuilder, clientBuilder, stop, cloud); err != nil {
|
if err := startControllers(c, rootClientBuilder, clientBuilder, ctx.Done(), cloud); err != nil {
|
||||||
glog.Fatalf("error running controllers: %v", err)
|
glog.Fatalf("error running controllers: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runCtx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if !c.ComponentConfig.GenericComponent.LeaderElection.LeaderElect {
|
if !c.ComponentConfig.GenericComponent.LeaderElection.LeaderElect {
|
||||||
run(wait.NeverStop)
|
run(runCtx)
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +187,7 @@ func Run(c *cloudcontrollerconfig.CompletedConfig) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try and become the leader and start cloud controller manager loops
|
// Try and become the leader and start cloud controller manager loops
|
||||||
leaderelection.RunOrDie(wait.NeverStop, leaderelection.LeaderElectionConfig{
|
leaderelection.RunOrDie(runCtx, leaderelection.LeaderElectionConfig{
|
||||||
Lock: rl,
|
Lock: rl,
|
||||||
LeaseDuration: c.ComponentConfig.GenericComponent.LeaderElection.LeaseDuration.Duration,
|
LeaseDuration: c.ComponentConfig.GenericComponent.LeaderElection.LeaseDuration.Duration,
|
||||||
RenewDeadline: c.ComponentConfig.GenericComponent.LeaderElection.RenewDeadline.Duration,
|
RenewDeadline: c.ComponentConfig.GenericComponent.LeaderElection.RenewDeadline.Duration,
|
||||||
|
|
|
@ -21,6 +21,7 @@ limitations under the License.
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
@ -144,7 +145,7 @@ func Run(c *config.CompletedConfig) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run := func(stop <-chan struct{}) {
|
run := func(runCtx context.Context) {
|
||||||
rootClientBuilder := controller.SimpleControllerClientBuilder{
|
rootClientBuilder := controller.SimpleControllerClientBuilder{
|
||||||
ClientConfig: c.Kubeconfig,
|
ClientConfig: c.Kubeconfig,
|
||||||
}
|
}
|
||||||
|
@ -164,7 +165,7 @@ func Run(c *config.CompletedConfig) error {
|
||||||
} else {
|
} else {
|
||||||
clientBuilder = rootClientBuilder
|
clientBuilder = rootClientBuilder
|
||||||
}
|
}
|
||||||
ctx, err := CreateControllerContext(c, rootClientBuilder, clientBuilder, stop)
|
ctx, err := CreateControllerContext(c, rootClientBuilder, clientBuilder, runCtx.Done())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Fatalf("error building controller context: %v", err)
|
glog.Fatalf("error building controller context: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -180,8 +181,11 @@ func Run(c *config.CompletedConfig) error {
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runCtx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
if !c.ComponentConfig.GenericComponent.LeaderElection.LeaderElect {
|
if !c.ComponentConfig.GenericComponent.LeaderElection.LeaderElect {
|
||||||
run(wait.NeverStop)
|
run(runCtx)
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +208,7 @@ func Run(c *config.CompletedConfig) error {
|
||||||
glog.Fatalf("error creating lock: %v", err)
|
glog.Fatalf("error creating lock: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
leaderelection.RunOrDie(wait.NeverStop, leaderelection.LeaderElectionConfig{
|
leaderelection.RunOrDie(runCtx, leaderelection.LeaderElectionConfig{
|
||||||
Lock: rl,
|
Lock: rl,
|
||||||
LeaseDuration: c.ComponentConfig.GenericComponent.LeaderElection.LeaseDuration.Duration,
|
LeaseDuration: c.ComponentConfig.GenericComponent.LeaderElection.LeaseDuration.Duration,
|
||||||
RenewDeadline: c.ComponentConfig.GenericComponent.LeaderElection.RenewDeadline.Duration,
|
RenewDeadline: c.ComponentConfig.GenericComponent.LeaderElection.RenewDeadline.Duration,
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -189,7 +190,9 @@ func Run(c schedulerserverconfig.CompletedConfig, stopCh <-chan struct{}) error
|
||||||
// If leader election is enabled, run via LeaderElector until done and exit.
|
// If leader election is enabled, run via LeaderElector until done and exit.
|
||||||
if c.LeaderElection != nil {
|
if c.LeaderElection != nil {
|
||||||
c.LeaderElection.Callbacks = leaderelection.LeaderCallbacks{
|
c.LeaderElection.Callbacks = leaderelection.LeaderCallbacks{
|
||||||
OnStartedLeading: run,
|
OnStartedLeading: func(ctx context.Context) {
|
||||||
|
run(ctx.Done())
|
||||||
|
},
|
||||||
OnStoppedLeading: func() {
|
OnStoppedLeading: func() {
|
||||||
utilruntime.HandleError(fmt.Errorf("lost master"))
|
utilruntime.HandleError(fmt.Errorf("lost master"))
|
||||||
},
|
},
|
||||||
|
@ -199,7 +202,7 @@ func Run(c schedulerserverconfig.CompletedConfig, stopCh <-chan struct{}) error
|
||||||
return fmt.Errorf("couldn't create leader elector: %v", err)
|
return fmt.Errorf("couldn't create leader elector: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
leaderElector.Run(stopCh)
|
leaderElector.Run(context.TODO())
|
||||||
|
|
||||||
return fmt.Errorf("lost lease")
|
return fmt.Errorf("lost lease")
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,9 @@ limitations under the License.
|
||||||
package leaderelection
|
package leaderelection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -120,7 +120,7 @@ type LeaderElectionConfig struct {
|
||||||
// * OnChallenge()
|
// * OnChallenge()
|
||||||
type LeaderCallbacks struct {
|
type LeaderCallbacks struct {
|
||||||
// OnStartedLeading is called when a LeaderElector client starts leading
|
// OnStartedLeading is called when a LeaderElector client starts leading
|
||||||
OnStartedLeading func(stop <-chan struct{})
|
OnStartedLeading func(context.Context)
|
||||||
// OnStoppedLeading is called when a LeaderElector client stops leading
|
// OnStoppedLeading is called when a LeaderElector client stops leading
|
||||||
OnStoppedLeading func()
|
OnStoppedLeading func()
|
||||||
// OnNewLeader is called when the client observes a leader that is
|
// OnNewLeader is called when the client observes a leader that is
|
||||||
|
@ -146,28 +146,28 @@ type LeaderElector struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts the leader election loop
|
// Run starts the leader election loop
|
||||||
func (le *LeaderElector) Run(stop <-chan struct{}) {
|
func (le *LeaderElector) Run(ctx context.Context) {
|
||||||
defer func() {
|
defer func() {
|
||||||
runtime.HandleCrash()
|
runtime.HandleCrash()
|
||||||
le.config.Callbacks.OnStoppedLeading()
|
le.config.Callbacks.OnStoppedLeading()
|
||||||
}()
|
}()
|
||||||
if !le.acquire(stop) {
|
if !le.acquire(ctx) {
|
||||||
return // stop signalled done
|
return // ctx signalled done
|
||||||
}
|
}
|
||||||
internalStop := make(chan struct{})
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer close(internalStop)
|
defer cancel()
|
||||||
go le.config.Callbacks.OnStartedLeading(internalStop)
|
go le.config.Callbacks.OnStartedLeading(ctx)
|
||||||
le.renew(stop)
|
le.renew(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunOrDie starts a client with the provided config or panics if the config
|
// RunOrDie starts a client with the provided config or panics if the config
|
||||||
// fails to validate.
|
// fails to validate.
|
||||||
func RunOrDie(stop <-chan struct{}, lec LeaderElectionConfig) {
|
func RunOrDie(ctx context.Context, lec LeaderElectionConfig) {
|
||||||
le, err := NewLeaderElector(lec)
|
le, err := NewLeaderElector(lec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
le.Run(stop)
|
le.Run(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLeader returns the identity of the last observed leader or returns the empty string if
|
// GetLeader returns the identity of the last observed leader or returns the empty string if
|
||||||
|
@ -182,17 +182,10 @@ func (le *LeaderElector) IsLeader() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// acquire loops calling tryAcquireOrRenew and returns true immediately when tryAcquireOrRenew succeeds.
|
// acquire loops calling tryAcquireOrRenew and returns true immediately when tryAcquireOrRenew succeeds.
|
||||||
// Returns false if stop signals done.
|
// Returns false if ctx signals done.
|
||||||
func (le *LeaderElector) acquire(stop <-chan struct{}) bool {
|
func (le *LeaderElector) acquire(ctx context.Context) bool {
|
||||||
tmpStop := make(chan struct{})
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
once := sync.Once{}
|
defer cancel()
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-stop:
|
|
||||||
once.Do(func() { close(tmpStop) })
|
|
||||||
case <-tmpStop:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
succeeded := false
|
succeeded := false
|
||||||
desc := le.config.Lock.Describe()
|
desc := le.config.Lock.Describe()
|
||||||
glog.Infof("attempting to acquire leader lease %v...", desc)
|
glog.Infof("attempting to acquire leader lease %v...", desc)
|
||||||
|
@ -205,41 +198,22 @@ func (le *LeaderElector) acquire(stop <-chan struct{}) bool {
|
||||||
}
|
}
|
||||||
le.config.Lock.RecordEvent("became leader")
|
le.config.Lock.RecordEvent("became leader")
|
||||||
glog.Infof("successfully acquired lease %v", desc)
|
glog.Infof("successfully acquired lease %v", desc)
|
||||||
once.Do(func() { close(tmpStop) })
|
cancel()
|
||||||
}, le.config.RetryPeriod, JitterFactor, true, tmpStop)
|
}, le.config.RetryPeriod, JitterFactor, true, ctx.Done())
|
||||||
return succeeded
|
return succeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
// renew loops calling tryAcquireOrRenew and returns immediately when tryAcquireOrRenew fails or ctx signals done.
|
// renew loops calling tryAcquireOrRenew and returns immediately when tryAcquireOrRenew fails or ctx signals done.
|
||||||
func (le *LeaderElector) renew(stop <-chan struct{}) {
|
func (le *LeaderElector) renew(ctx context.Context) {
|
||||||
tmpStop := make(chan struct{})
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
once := sync.Once{}
|
defer cancel()
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-stop:
|
|
||||||
once.Do(func() { close(tmpStop) })
|
|
||||||
case <-tmpStop:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
wait.Until(func() {
|
wait.Until(func() {
|
||||||
// PollUntil() sleeps for "interval" duration before calling the function so we need to increase the timeout by le.config.RetryPeriod
|
// PollUntil() sleeps for "interval" duration before calling the function so we need to increase the timeout by le.config.RetryPeriod
|
||||||
t := time.NewTimer(le.config.RetryPeriod + le.config.RenewDeadline)
|
timeoutCtx, timeoutCancel := context.WithTimeout(ctx, le.config.RetryPeriod+le.config.RenewDeadline)
|
||||||
defer t.Stop()
|
defer timeoutCancel()
|
||||||
internalStop := make(chan struct{})
|
|
||||||
internalOnce := sync.Once{}
|
|
||||||
defer internalOnce.Do(func() { close(internalStop) })
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-tmpStop:
|
|
||||||
internalOnce.Do(func() { close(internalStop) })
|
|
||||||
case <-t.C:
|
|
||||||
internalOnce.Do(func() { close(internalStop) })
|
|
||||||
case <-internalStop:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
err := wait.PollUntil(le.config.RetryPeriod, func() (bool, error) {
|
err := wait.PollUntil(le.config.RetryPeriod, func() (bool, error) {
|
||||||
return le.tryAcquireOrRenew(), nil
|
return le.tryAcquireOrRenew(), nil
|
||||||
}, internalStop)
|
}, timeoutCtx.Done())
|
||||||
le.maybeReportTransition()
|
le.maybeReportTransition()
|
||||||
desc := le.config.Lock.Describe()
|
desc := le.config.Lock.Describe()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -248,8 +222,8 @@ func (le *LeaderElector) renew(stop <-chan struct{}) {
|
||||||
}
|
}
|
||||||
le.config.Lock.RecordEvent("stopped leading")
|
le.config.Lock.RecordEvent("stopped leading")
|
||||||
glog.Infof("failed to renew lease %v: %v", desc, err)
|
glog.Infof("failed to renew lease %v: %v", desc, err)
|
||||||
once.Do(func() { close(tmpStop) })
|
cancel()
|
||||||
}, 0, tmpStop)
|
}, 0, ctx.Done())
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryAcquireOrRenew tries to acquire a leader lease if it is not already acquired,
|
// tryAcquireOrRenew tries to acquire a leader lease if it is not already acquired,
|
||||||
|
|
Loading…
Reference in New Issue