fix: should roll back configuration if failure of reload

This commit is contained in:
mzz2017 2023-03-02 22:25:51 +08:00
parent 0433f8d5da
commit c15c0ee410
3 changed files with 54 additions and 12 deletions

View File

@ -2,6 +2,7 @@ package cmd
import ( import (
"fmt" "fmt"
"github.com/mohae/deepcopy"
"github.com/okzk/sdnotify" "github.com/okzk/sdnotify"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -120,33 +121,46 @@ loop:
// Load new config. // Load new config.
log.Warnln("[Reload] Load new config") log.Warnln("[Reload] Load new config")
conf, includes, err := readConfig(cfgFile) newConf, includes, err := readConfig(cfgFile)
if err != nil { if err != nil {
log.WithFields(logrus.Fields{ log.WithFields(logrus.Fields{
"err": err, "err": err,
}).Errorln("Failed to reload") }).Errorln("[Reload] Failed to reload")
sdnotify.Ready() sdnotify.Ready()
continue continue
} }
log.Infof("Include config files: [%v]", strings.Join(includes, ", ")) log.Infof("Include config files: [%v]", strings.Join(includes, ", "))
// New logger. // New logger.
log = logger.NewLogger(conf.Global.LogLevel, disableTimestamp) log = logger.NewLogger(newConf.Global.LogLevel, disableTimestamp)
logrus.SetLevel(log.Level) logrus.SetLevel(log.Level)
// New control plane. // New control plane.
obj := c.EjectBpf() obj := c.EjectBpf()
log.Warnln("[Reload] Load new control plane") log.Warnln("[Reload] Load new control plane")
newC, err := newControlPlane(log, obj, conf) newC, err := newControlPlane(log, obj, newConf)
if err != nil { if err != nil {
log.WithFields(logrus.Fields{ log.WithFields(logrus.Fields{
"err": err, "err": err,
}).Errorln("Failed to reload") }).Errorln("[Reload] Failed to reload; try to roll back configuration")
sdnotify.Ready() // Load last config back.
continue newC, err = newControlPlane(log, obj, conf)
if err != nil {
sdnotify.Stopping()
obj.Close()
c.Close()
log.WithFields(logrus.Fields{
"err": err,
}).Fatalln("[Reload] Failed to roll back configuration")
} }
log.Warnln("[Reload] Last reload failed; rolled back configuration")
} else {
log.Warnln("[Reload] Stopped old control plane") log.Warnln("[Reload] Stopped old control plane")
}
// Inject bpf objects into the new control plane life-cycle.
newC.InjectBpf(obj)
c.Close() c.Close()
c = newC c = newC
conf = newConf
reloading = true reloading = true
default: default:
break loop break loop
@ -160,6 +174,9 @@ loop:
} }
func newControlPlane(log *logrus.Logger, bpf interface{}, conf *config.Config) (c *control.ControlPlane, err error) { func newControlPlane(log *logrus.Logger, bpf interface{}, conf *config.Config) (c *control.ControlPlane, err error) {
// Deep copy to prevent modification.
conf = deepcopy.Copy(conf).(*config.Config)
/// Get tag -> nodeList mapping. /// Get tag -> nodeList mapping.
tagToNodeList := map[string][]string{} tagToNodeList := map[string][]string{}
if len(conf.Node) > 0 { if len(conf.Node) > 0 {

View File

@ -155,6 +155,8 @@ func NewControlPlane(
) )
defer func() { defer func() {
if err != nil { if err != nil {
// Flip back.
core.Flip()
_ = core.Close() _ = core.Close()
} }
}() }()
@ -358,6 +360,9 @@ func NewControlPlane(
func (c *ControlPlane) EjectBpf() *bpfObjects { func (c *ControlPlane) EjectBpf() *bpfObjects {
return c.core.EjectBpf() return c.core.EjectBpf()
} }
func (c *ControlPlane) InjectBpf(bpf *bpfObjects) {
c.core.InjectBpf(bpf)
}
func (c *ControlPlane) dnsUpstreamReadyCallback(raw *url.URL, dnsUpstream *dns.Upstream) (err error) { func (c *ControlPlane) dnsUpstreamReadyCallback(raw *url.URL, dnsUpstream *dns.Upstream) (err error) {
// Waiting for ready. // Waiting for ready.

View File

@ -37,6 +37,7 @@ type controlPlaneCore struct {
flip int flip int
isReload bool isReload bool
bpfEjected bool
} }
func newControlPlaneCore(log *logrus.Logger, func newControlPlaneCore(log *logrus.Logger,
@ -48,9 +49,13 @@ func newControlPlaneCore(log *logrus.Logger,
if isReload { if isReload {
coreFlip = coreFlip&1 ^ 1 coreFlip = coreFlip&1 ^ 1
} }
var deferFuncs []func() error
if !isReload {
deferFuncs = append(deferFuncs, bpf.Close)
}
return &controlPlaneCore{ return &controlPlaneCore{
log: log, log: log,
deferFuncs: []func() error{bpf.Close}, deferFuncs: deferFuncs,
bpf: bpf, bpf: bpf,
outboundId2Name: outboundId2Name, outboundId2Name: outboundId2Name,
kernelVersion: kernelVersion, kernelVersion: kernelVersion,
@ -59,6 +64,9 @@ func newControlPlaneCore(log *logrus.Logger,
} }
} }
func (c *controlPlaneCore) Flip() {
coreFlip = coreFlip&1 ^ 1
}
func (c *controlPlaneCore) Close() (err error) { func (c *controlPlaneCore) Close() (err error) {
// Invoke defer funcs in reverse order. // Invoke defer funcs in reverse order.
for i := len(c.deferFuncs) - 1; i >= 0; i-- { for i := len(c.deferFuncs) - 1; i >= 0; i-- {
@ -513,6 +521,18 @@ func (c *controlPlaneCore) BatchUpdateDomainRouting(cache *DnsCache) error {
// EjectBpf will resect bpf from destroying life-cycle of control plane core. // EjectBpf will resect bpf from destroying life-cycle of control plane core.
func (c *controlPlaneCore) EjectBpf() *bpfObjects { func (c *controlPlaneCore) EjectBpf() *bpfObjects {
if !c.bpfEjected && !c.isReload {
c.deferFuncs = c.deferFuncs[1:] c.deferFuncs = c.deferFuncs[1:]
}
c.bpfEjected = true
return c.bpf return c.bpf
} }
// InjectBpf will inject bpf back.
func (c *controlPlaneCore) InjectBpf(bpf *bpfObjects) {
if c.bpfEjected {
c.bpfEjected = false
c.deferFuncs = append([]func() error{bpf.Close}, c.deferFuncs...)
}
return
}