haproxy-lb/pkg/haproxy/haproxy.go
2021-04-09 22:38:03 +02:00

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)
}