112 lines
2.2 KiB
Go
112 lines
2.2 KiB
Go
package haproxy
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"sync"
|
|
"text/template"
|
|
)
|
|
|
|
type ServiceManager interface {
|
|
Reload(context.Context) error
|
|
Status(context.Context) error
|
|
Start(context.Context) error
|
|
Stop(context.Context) error
|
|
}
|
|
|
|
type HAProxyManager struct {
|
|
sync.Mutex
|
|
|
|
configFile string
|
|
svc ServiceManager
|
|
template *template.Template
|
|
hash []byte
|
|
}
|
|
|
|
func NewHAProxyManager(configFile, service string) (ha *HAProxyManager, err error) {
|
|
t, err := template.New("haproxy").Parse(haproxyConfigTemplate)
|
|
if err != nil {
|
|
return
|
|
}
|
|
ha = &HAProxyManager{
|
|
configFile: configFile,
|
|
svc: NewSystemdManager(service),
|
|
template: t,
|
|
}
|
|
return
|
|
}
|
|
|
|
func (ha *HAProxyManager) checkConfig(ctx context.Context, lbs Config) (hash []byte, err error) {
|
|
tmp, err := ioutil.TempFile(os.TempDir(), "haproxy_")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer tmp.Close()
|
|
defer os.Remove(tmp.Name())
|
|
h := sha256.New()
|
|
w := io.MultiWriter(tmp, h)
|
|
err = ha.template.Execute(w, lbs)
|
|
if err != nil {
|
|
return
|
|
}
|
|
hash = h.Sum(nil)
|
|
out, err := exec.CommandContext(ctx, "haproxy", "-c", "-f", tmp.Name()).CombinedOutput()
|
|
if err != nil {
|
|
err = fmt.Errorf("%s:%w", string(out), err)
|
|
}
|
|
return hash, err
|
|
}
|
|
|
|
func (ha *HAProxyManager) updateConfig(ctx context.Context, lbs Config) error {
|
|
hash, err := ha.checkConfig(ctx, lbs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if bytes.Equal(ha.hash, hash) {
|
|
return nil
|
|
}
|
|
file, err := os.OpenFile(ha.configFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = ha.template.Execute(file, lbs)
|
|
if err != nil {
|
|
file.Close()
|
|
return err
|
|
}
|
|
err = file.Close()
|
|
ha.hash = hash
|
|
return err
|
|
}
|
|
|
|
func (ha *HAProxyManager) UpdateConfig(ctx context.Context, lbs Config) error {
|
|
ha.Lock()
|
|
defer ha.Unlock()
|
|
err := ha.updateConfig(ctx, lbs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return ha.svc.Reload(ctx)
|
|
}
|
|
|
|
func (ha *HAProxyManager) Start(ctx context.Context) error {
|
|
err := ha.svc.Status(ctx)
|
|
if err != nil {
|
|
err = ha.updateConfig(ctx, Config{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = ha.svc.Start(ctx)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (ha *HAProxyManager) Stop(ctx context.Context) error {
|
|
return ha.svc.Stop(ctx)
|
|
}
|