...
1 package ntp
2
3 import (
4 "bytes"
5 "context"
6 "errors"
7 "fmt"
8 "io/fs"
9 "os"
10 "strings"
11
12 "github.com/coreos/go-systemd/v22/dbus"
13
14 "edge-infra.dev/pkg/k8s/runtime/controller/reconcile"
15 v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
16 "edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/config"
17 )
18
19 const (
20 chronydService = "chronyd"
21 chronydFileMode = fs.FileMode(0644)
22 )
23
24 var (
25 restartChronydFn = restartChronyd
26 chronydPath = "/host-etc/chrony/sources.d/ien-ntp-server.sources"
27 )
28
29 type Plugin struct{}
30
31 func (ntpConfigurationPlugin Plugin) Reconcile(ctx context.Context, ienode *v1ien.IENode, _ config.Config) (reconcile.Result, error) {
32 currentCfg, err := os.ReadFile(chronydPath)
33 if err != nil && !errors.Is(err, os.ErrNotExist) {
34 return reconcile.ResultRequeue, fmt.Errorf("error reading chronyd file: %v", err)
35 }
36
37 targetCfg := generateTargetNTPConfig(ienode.Spec.NTPServers)
38 if bytes.Equal(currentCfg, targetCfg) {
39 return reconcile.ResultSuccess, nil
40 }
41
42 if err := os.WriteFile(chronydPath, targetCfg, chronydFileMode); err != nil {
43 return reconcile.ResultRequeue, fmt.Errorf("error writing chronyd file: %v", err)
44 }
45
46 if err := restartChronydFn(ctx); err != nil {
47 if err := os.WriteFile(chronydPath, currentCfg, chronydFileMode); err != nil {
48 return reconcile.ResultRequeue, fmt.Errorf("error writing previous chronyd file backup: %v", err)
49 }
50 return reconcile.ResultRequeue, fmt.Errorf("error restarting chronyd.service: %v", err)
51 }
52 return reconcile.ResultSuccess, nil
53 }
54
55
56 func generateTargetNTPConfig(ntpServers []string) []byte {
57 chronydSourcesLines := []string{}
58 for _, ntpServer := range ntpServers {
59 chronydDirective := fmt.Sprintf("server %s", ntpServer)
60 chronydSourcesLines = append(chronydSourcesLines, chronydDirective)
61 }
62 chronydSourcesLines = append(chronydSourcesLines, "")
63 chronydSources := strings.Join(chronydSourcesLines, "\n")
64 return []byte(chronydSources)
65 }
66
67
68 func restartChronyd(ctx context.Context) error {
69 conn, err := dbus.NewSystemdConnectionContext(ctx)
70 if err != nil {
71 return err
72 }
73 resp := make(chan string)
74 if _, err := conn.RestartUnitContext(ctx, "chronyd.service", "replace", resp); err != nil {
75 return err
76 }
77 jobResp := <-resp
78 if jobResp == "done" {
79 return nil
80 }
81 return fmt.Errorf("failed to restart chronyd: %s", jobResp)
82 }
83
View as plain text