mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 02:03:20 -04:00 
			
		
		
		
	Plugin/RELOAD - Tune usage of var global, add limit to options (#1457)
* tune usage of var global, add limit to options * update readme for minimal values * useless change to quick-off codecov * fix msgs for min values and tune the flag for end of reload usage, with a 'maybe' option * adding UT for min values, adding MD5 of corefile on the log
This commit is contained in:
		
				
					committed by
					
						 John Belamaric
						John Belamaric
					
				
			
			
				
	
			
			
			
						parent
						
							3fb07161b7
						
					
				
				
					commit
					2440024772
				
			| @@ -35,6 +35,7 @@ reload [INTERVAL] [JITTER] | |||||||
| * The plugin will check for changes every **INTERVAL**, subject to +/- the **JITTER** duration | * The plugin will check for changes every **INTERVAL**, subject to +/- the **JITTER** duration | ||||||
| * **INTERVAL** and **JITTER** are Golang (durations)[https://golang.org/pkg/time/#ParseDuration] | * **INTERVAL** and **JITTER** are Golang (durations)[https://golang.org/pkg/time/#ParseDuration] | ||||||
| * Default **INTERVAL** is 30s, default **JITTER** is 15s | * Default **INTERVAL** is 30s, default **JITTER** is 15s | ||||||
|  | * Minimal value for **INTERVAL** is 2s, and for **JITTER** is 1s | ||||||
| * If **JITTER** is more than half of **INTERVAL**, it will be set to half of **INTERVAL** | * If **JITTER** is more than half of **INTERVAL**, it will be set to half of **INTERVAL** | ||||||
|  |  | ||||||
| ## Examples | ## Examples | ||||||
|   | |||||||
| @@ -9,12 +9,15 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| // reload periodically checks if the Corefile has changed, and reloads if so | // reload periodically checks if the Corefile has changed, and reloads if so | ||||||
|  | const ( | ||||||
|  | 	unused    = 0 | ||||||
|  | 	maybeUsed = 1 | ||||||
|  | 	used      = 2 | ||||||
|  | ) | ||||||
|  |  | ||||||
| type reload struct { | type reload struct { | ||||||
| 	instance *caddy.Instance |  | ||||||
| 	interval time.Duration | 	interval time.Duration | ||||||
| 	sum      [md5.Size]byte | 	usage    int | ||||||
| 	stopped  bool |  | ||||||
| 	quit     chan bool | 	quit     chan bool | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -26,13 +29,14 @@ func hook(event caddy.EventName, info interface{}) error { | |||||||
| 	// if reload is removed from the Corefile, then the hook | 	// if reload is removed from the Corefile, then the hook | ||||||
| 	// is still registered but setup is never called again | 	// is still registered but setup is never called again | ||||||
| 	// so we need a flag to tell us not to reload | 	// so we need a flag to tell us not to reload | ||||||
| 	if r.stopped { | 	if r.usage == unused { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// this should be an instance. ok to panic if not | 	// this should be an instance. ok to panic if not | ||||||
| 	r.instance = info.(*caddy.Instance) | 	instance := info.(*caddy.Instance) | ||||||
| 	r.sum = md5.Sum(r.instance.Caddyfile().Body()) | 	md5sum := md5.Sum(instance.Caddyfile().Body()) | ||||||
|  | 	log.Printf("[INFO] Running configuration MD5 = %x\n", md5sum) | ||||||
|  |  | ||||||
| 	go func() { | 	go func() { | ||||||
| 		tick := time.NewTicker(r.interval) | 		tick := time.NewTicker(r.interval) | ||||||
| @@ -40,19 +44,26 @@ func hook(event caddy.EventName, info interface{}) error { | |||||||
| 		for { | 		for { | ||||||
| 			select { | 			select { | ||||||
| 			case <-tick.C: | 			case <-tick.C: | ||||||
| 				corefile, err := caddy.LoadCaddyfile(r.instance.Caddyfile().ServerType()) | 				corefile, err := caddy.LoadCaddyfile(instance.Caddyfile().ServerType()) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					continue | 					continue | ||||||
| 				} | 				} | ||||||
| 				s := md5.Sum(corefile.Body()) | 				s := md5.Sum(corefile.Body()) | ||||||
| 				if s != r.sum { | 				if s != md5sum { | ||||||
| 					_, err := r.instance.Restart(corefile) | 					// Let not try to restart with the same file, even though it is wrong. | ||||||
|  | 					md5sum = s | ||||||
|  | 					// now lets consider that plugin will not be reload, unless appear in next config file | ||||||
|  | 					// change status iof usage will be reset in setup if the plugin appears in config file | ||||||
|  | 					r.usage = maybeUsed | ||||||
|  | 					_, err := instance.Restart(corefile) | ||||||
| 					if err != nil { | 					if err != nil { | ||||||
| 						log.Printf("[ERROR] Corefile changed but reload failed: %s\n", err) | 						log.Printf("[ERROR] Corefile changed but reload failed: %s\n", err) | ||||||
| 						continue | 						continue | ||||||
| 					} | 					} | ||||||
| 					// we are done, this hook gets called again with new instance | 					// we are done, if the plugin was not set used, then it is not. | ||||||
| 					r.stopped = true | 					if r.usage == maybeUsed { | ||||||
|  | 						r.usage = unused | ||||||
|  | 					} | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 			case <-r.quit: | 			case <-r.quit: | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package reload | package reload | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"math/rand" | 	"math/rand" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| @@ -17,7 +18,11 @@ func init() { | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
| var r *reload | // the info reload is global to all application, whatever number of reloads. | ||||||
|  | // it is used to transmit data between Setup and start of the hook called 'onInstanceStartup' | ||||||
|  | // channel for QUIT is never changed in purpose. | ||||||
|  | // WARNING: this data may be unsync after an invalid attempt of reload Corefile. | ||||||
|  | var r = reload{interval: defaultInterval, usage: unused, quit: make(chan bool)} | ||||||
| var once sync.Once | var once sync.Once | ||||||
|  |  | ||||||
| func setup(c *caddy.Controller) error { | func setup(c *caddy.Controller) error { | ||||||
| @@ -32,19 +37,25 @@ func setup(c *caddy.Controller) error { | |||||||
| 	if len(args) > 0 { | 	if len(args) > 0 { | ||||||
| 		d, err := time.ParseDuration(args[0]) | 		d, err := time.ParseDuration(args[0]) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return plugin.Error("reload", err) | ||||||
| 		} | 		} | ||||||
| 		i = d | 		i = d | ||||||
| 	} | 	} | ||||||
|  | 	if i < minInterval { | ||||||
|  | 		return plugin.Error("reload", fmt.Errorf("interval value must be greater or equal to %v", minInterval)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	j := defaultJitter | 	j := defaultJitter | ||||||
| 	if len(args) > 1 { | 	if len(args) > 1 { | ||||||
| 		d, err := time.ParseDuration(args[1]) | 		d, err := time.ParseDuration(args[1]) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return plugin.Error("reload", err) | ||||||
| 		} | 		} | ||||||
| 		j = d | 		j = d | ||||||
| 	} | 	} | ||||||
|  | 	if j < minJitter { | ||||||
|  | 		return plugin.Error("reload", fmt.Errorf("jitter value must be greater or equal to %v", minJitter)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if j > i/2 { | 	if j > i/2 { | ||||||
| 		j = i / 2 | 		j = i / 2 | ||||||
| @@ -53,20 +64,25 @@ func setup(c *caddy.Controller) error { | |||||||
| 	jitter := time.Duration(rand.Int63n(j.Nanoseconds()) - (j.Nanoseconds() / 2)) | 	jitter := time.Duration(rand.Int63n(j.Nanoseconds()) - (j.Nanoseconds() / 2)) | ||||||
| 	i = i + jitter | 	i = i + jitter | ||||||
|  |  | ||||||
| 	r = &reload{interval: i, quit: make(chan bool)} | 	// prepare info for next onInstanceStartup event | ||||||
|  | 	r.interval = i | ||||||
|  | 	r.usage = used | ||||||
|  |  | ||||||
| 	once.Do(func() { | 	once.Do(func() { | ||||||
| 		caddy.RegisterEventHook("reload", hook) | 		caddy.RegisterEventHook("reload", hook) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
|  | 	// re-register on finalShutDown as the instance most-likely will be changed | ||||||
| 	c.OnFinalShutdown(func() error { | 	c.OnFinalShutdown(func() error { | ||||||
| 		r.quit <- true | 		r.quit <- true | ||||||
| 		return nil | 		return nil | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
|  | 	minJitter       = 1 * time.Second | ||||||
|  | 	minInterval     = 2 * time.Second | ||||||
| 	defaultInterval = 30 * time.Second | 	defaultInterval = 30 * time.Second | ||||||
| 	defaultJitter   = 15 * time.Second | 	defaultJitter   = 15 * time.Second | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -36,4 +36,16 @@ func TestSetupReload(t *testing.T) { | |||||||
| 	if err := setup(c); err == nil { | 	if err := setup(c); err == nil { | ||||||
| 		t.Fatalf("Expected errors, but got: %v", err) | 		t.Fatalf("Expected errors, but got: %v", err) | ||||||
| 	} | 	} | ||||||
|  | 	c = caddy.NewTestController("dns", `reload 1s`) | ||||||
|  | 	if err := setup(c); err == nil { | ||||||
|  | 		t.Fatalf("Expected errors, but got: %v", err) | ||||||
|  | 	} | ||||||
|  | 	c = caddy.NewTestController("dns", `reload 0s`) | ||||||
|  | 	if err := setup(c); err == nil { | ||||||
|  | 		t.Fatalf("Expected errors, but got: %v", err) | ||||||
|  | 	} | ||||||
|  | 	c = caddy.NewTestController("dns", `reload 3s 0.5s`) | ||||||
|  | 	if err := setup(c); err == nil { | ||||||
|  | 		t.Fatalf("Expected errors, but got: %v", err) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user