mirror of
https://github.com/jsiebens/ionscale.git
synced 2026-03-31 15:07:49 +01:00
125 lines
2.7 KiB
Go
125 lines
2.7 KiB
Go
package dns
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/hashicorp/go-plugin"
|
|
"github.com/jsiebens/ionscale/internal/util"
|
|
dnsplugin "github.com/jsiebens/libdns-plugin"
|
|
"github.com/libdns/libdns"
|
|
"go.uber.org/zap"
|
|
"os/exec"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// pluginManager handles plugin lifecycle and resilience
|
|
type pluginManager struct {
|
|
pluginPath string
|
|
client *plugin.Client
|
|
instance dnsplugin.Provider
|
|
lock sync.RWMutex
|
|
logger *zap.Logger
|
|
|
|
zone string
|
|
config json.RawMessage
|
|
}
|
|
|
|
// NewPluginManager creates a new plugin manager
|
|
func newPluginManager(pluginPath string, zone string, config json.RawMessage) (*pluginManager, error) {
|
|
logger := zap.L().Named("dns").With(zap.String("plugin_path", pluginPath))
|
|
|
|
p := &pluginManager{
|
|
pluginPath: pluginPath,
|
|
logger: logger,
|
|
zone: zone,
|
|
config: config,
|
|
}
|
|
|
|
if err := p.ensureRunning(true); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return p, nil
|
|
}
|
|
|
|
// ensureRunning makes sure the plugin is running
|
|
func (pm *pluginManager) ensureRunning(start bool) error {
|
|
pm.lock.RLock()
|
|
running := pm.client != nil && !pm.client.Exited()
|
|
instance := pm.instance
|
|
pm.lock.RUnlock()
|
|
|
|
if running && instance != nil {
|
|
return nil
|
|
}
|
|
|
|
// Need to restart
|
|
pm.lock.Lock()
|
|
defer pm.lock.Unlock()
|
|
|
|
if !start {
|
|
pm.logger.Info("Restarting DNS plugin")
|
|
}
|
|
|
|
if pm.client != nil {
|
|
pm.client.Kill()
|
|
}
|
|
|
|
// Create a new client
|
|
cmd := exec.Command(pm.pluginPath)
|
|
pm.client = plugin.NewClient(&plugin.ClientConfig{
|
|
HandshakeConfig: dnsplugin.Handshake,
|
|
Plugins: dnsplugin.PluginMap,
|
|
Cmd: cmd,
|
|
AllowedProtocols: []plugin.Protocol{
|
|
plugin.ProtocolNetRPC,
|
|
plugin.ProtocolGRPC,
|
|
},
|
|
Managed: true,
|
|
Logger: util.NewZapAdapter(pm.logger, "dns"),
|
|
})
|
|
|
|
// Connect via RPC
|
|
rpcClient, err := pm.client.Client()
|
|
if err != nil {
|
|
return fmt.Errorf("error creating plugin client: %w", err)
|
|
}
|
|
|
|
// Request the plugin
|
|
raw, err := rpcClient.Dispense(dnsplugin.ProviderPluginName)
|
|
if err != nil {
|
|
return fmt.Errorf("error dispensing plugin: %w", err)
|
|
}
|
|
|
|
// Convert to the interface
|
|
pm.instance = raw.(dnsplugin.Provider)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
defer cancel()
|
|
|
|
if err := pm.instance.Configure(ctx, pm.config); err != nil {
|
|
return fmt.Errorf("error configuring plugin: %w", err)
|
|
}
|
|
|
|
pm.logger.Info("DNS plugin started")
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pm *pluginManager) SetRecord(ctx context.Context, recordType, recordName, value string) error {
|
|
if err := pm.ensureRunning(false); err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err := pm.instance.SetRecords(ctx, pm.zone, []libdns.Record{{
|
|
Type: recordType,
|
|
Name: libdns.RelativeName(recordName, pm.zone),
|
|
Value: value,
|
|
TTL: 1 * time.Minute,
|
|
}})
|
|
|
|
return err
|
|
}
|