package haproxy import ( "bytes" "context" "crypto/sha256" "fmt" "io" "io/ioutil" "os" "os/exec" "sync" "text/template" ) type ServiceManager interface { Reload(context.Context) error } type HAProxyManager struct { sync.Mutex configFile string serviceManager ServiceManager template *template.Template hash []byte } func NewHAProxyManager(configFile string) (*HAProxyManager, error) { t, err := template.New("haproxy").Parse(haproxyConfigTemplate) if err != nil { return nil, err } return &HAProxyManager{ configFile: configFile, serviceManager: NewSystemdManager("haproxy"), template: t, }, nil } 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 { ha.Lock() defer ha.Unlock() 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 } file.Close() ha.hash = hash return ha.serviceManager.Reload(ctx) }