...

Source file src/edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/plugins/networking/ntp/ntp.go

Documentation: edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/plugins/networking/ntp

     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  // generateTargetNTPConfig will generate the target chronyd configuration file
    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  // restartChronyd will restart the chronyd service using the dbus api
    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