Skip to content

Commit 98fdb45

Browse files
committed
adding secret watcher to restart ptp4l process
1 parent 6f45185 commit 98fdb45

File tree

1 file changed

+166
-0
lines changed

1 file changed

+166
-0
lines changed

pkg/daemon/daemon.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package daemon
33
import (
44
"bufio"
55
"cmp"
6+
"crypto/sha256"
67
"fmt"
8+
"io"
79
"net"
810
"os"
911
"os/exec"
@@ -73,6 +75,13 @@ var ptpProcesses = []string{
7375
chronydProcessName, // there can be only one chronyd process in the system
7476
}
7577

78+
// saFileInfo tracks authentication file information for a profile
79+
type saFileInfo struct {
80+
profileName string
81+
filePath string
82+
fileHash string
83+
}
84+
7685
var ptpTmpFiles = []string{
7786
ts2phcProcessName,
7887
syncEProcessName,
@@ -306,6 +315,10 @@ type Daemon struct {
306315

307316
// Allow vendors to include plugins
308317
pluginManager plugin.PluginManager
318+
319+
// Track sa_file (authentication) files for monitoring changes
320+
saFileTracker map[string]*saFileInfo
321+
saFileMutex sync.Mutex
309322
}
310323

311324
// New LinuxPTP is called by daemon to generate new linuxptp instance
@@ -348,19 +361,43 @@ func New(
348361
processManager: pm,
349362
readyTracker: tracker,
350363
stopCh: stopCh,
364+
saFileTracker: make(map[string]*saFileInfo),
351365
}
352366
}
353367

354368
// Run in a for loop to listen for any LinuxPTPConfUpdate changes
369+
// This function handles two types of configuration changes:
370+
// 1. PtpConfig changes (via ConfigMap) - triggers UpdateCh
371+
// 2. Authentication file changes (via Secret) - triggers sa_file check
372+
// Both trigger applyNodePTPProfiles() which restarts PTP processes WITHOUT restarting the pod
355373
func (dn *Daemon) Run() {
356374
go dn.processManager.ptpEventHandler.ProcessEvents()
375+
376+
// Start sa_file monitoring ticker (check every 5 seconds for faster detection)
377+
// This detects when Kubernetes updates the mounted secret file
378+
// Total delay = Kubernetes propagation (1-10s) + our check interval (0-5s)
379+
saFileCheckTicker := time.NewTicker(5 * time.Second)
380+
defer saFileCheckTicker.Stop()
381+
357382
for {
358383
select {
359384
case <-dn.ptpUpdate.UpdateCh:
385+
// PtpConfig change detected via ConfigMap
386+
glog.Info("PtpConfig change detected, restarting PTP processes")
360387
err := dn.applyNodePTPProfiles()
361388
if err != nil {
362389
glog.Errorf("linuxPTP apply node profile failed: %v", err)
363390
}
391+
case <-saFileCheckTicker.C:
392+
// Check for sa_file (authentication) changes
393+
// When Secret is updated, Kubernetes automatically updates the mounted file
394+
if dn.checkSaFileChanges() {
395+
glog.Info("sa_file authentication file changed, restarting PTP processes")
396+
err := dn.applyNodePTPProfiles()
397+
if err != nil {
398+
glog.Errorf("linuxPTP apply node profile failed after sa_file change: %v", err)
399+
}
400+
}
364401
case <-dn.stopCh:
365402
dn.stopAllProcesses()
366403
glog.Infof("linuxPTP stop signal received, existing..")
@@ -369,6 +406,131 @@ func (dn *Daemon) Run() {
369406
}
370407
}
371408

409+
// computeFileHash computes SHA256 hash of a file for change detection
410+
// This is completely generic and works with any file content (binary, text, any format)
411+
// Used to detect when Kubernetes updates mounted secret files
412+
func computeFileHash(filePath string) (string, error) {
413+
file, err := os.Open(filePath)
414+
if err != nil {
415+
return "", err
416+
}
417+
defer file.Close()
418+
419+
hash := sha256.New()
420+
if _, err := io.Copy(hash, file); err != nil {
421+
return "", err
422+
}
423+
424+
return fmt.Sprintf("%x", hash.Sum(nil)), nil
425+
}
426+
427+
// extractSaFileFromConf extracts sa_file path from ptp4lConf if present
428+
func extractSaFileFromConf(ptp4lConf *string) string {
429+
if ptp4lConf == nil {
430+
return ""
431+
}
432+
433+
// Parse the config to extract sa_file from [global] section
434+
for _, line := range strings.Split(*ptp4lConf, "\n") {
435+
line = strings.TrimSpace(line)
436+
// Skip comments
437+
if strings.HasPrefix(line, "#") {
438+
continue
439+
}
440+
// Look for sa_file option
441+
if strings.HasPrefix(line, "sa_file") {
442+
parts := strings.Fields(line)
443+
if len(parts) >= 2 {
444+
return parts[1]
445+
}
446+
}
447+
}
448+
return ""
449+
}
450+
451+
// checkSaFileChanges checks if any tracked sa_file has changed
452+
// When a Secret is updated in Kubernetes, the mounted file is automatically updated
453+
// This function detects those changes via hash comparison and returns true if any file changed
454+
// This triggers a PTP process restart (not pod restart) - same as ConfigMap changes
455+
func (dn *Daemon) checkSaFileChanges() bool {
456+
dn.saFileMutex.Lock()
457+
defer dn.saFileMutex.Unlock()
458+
459+
changed := false
460+
for _, info := range dn.saFileTracker {
461+
if info.filePath == "" {
462+
continue
463+
}
464+
465+
// Check if file exists
466+
if _, err := os.Stat(info.filePath); os.IsNotExist(err) {
467+
glog.Warningf("sa_file %s for profile %s does not exist", info.filePath, info.profileName)
468+
continue
469+
}
470+
471+
// Compute current hash
472+
currentHash, err := computeFileHash(info.filePath)
473+
if err != nil {
474+
glog.Errorf("Failed to compute hash for sa_file %s: %v", info.filePath, err)
475+
continue
476+
}
477+
478+
// Compare with stored hash
479+
if currentHash != info.fileHash {
480+
glog.Infof("sa_file %s for profile %s has changed (old hash: %.16s... -> %.16s...)",
481+
info.filePath, info.profileName, info.fileHash, currentHash)
482+
info.fileHash = currentHash
483+
changed = true
484+
}
485+
}
486+
487+
return changed
488+
}
489+
490+
// updateSaFileTracking updates the tracking information for sa_files from current profiles
491+
// Called after profiles are applied to initialize or update sa_file monitoring
492+
// Extracts sa_file paths from ptp4lConf and sets up hash-based change detection
493+
func (dn *Daemon) updateSaFileTracking() {
494+
dn.saFileMutex.Lock()
495+
defer dn.saFileMutex.Unlock()
496+
497+
// Clear old tracking
498+
dn.saFileTracker = make(map[string]*saFileInfo)
499+
500+
// Add tracking for each profile that has sa_file configured
501+
for _, profile := range dn.ptpUpdate.NodeProfiles {
502+
if profile.Name == nil {
503+
continue
504+
}
505+
506+
saFilePath := extractSaFileFromConf(profile.Ptp4lConf)
507+
if saFilePath == "" {
508+
continue
509+
}
510+
511+
glog.Infof("Tracking sa_file %s for profile %s", saFilePath, *profile.Name)
512+
513+
// Compute initial hash
514+
hash := ""
515+
if _, err := os.Stat(saFilePath); err == nil {
516+
if h, err := computeFileHash(saFilePath); err == nil {
517+
hash = h
518+
glog.Infof("Initialized tracking for sa_file %s (hash: %.16s...)", saFilePath, h)
519+
} else {
520+
glog.Warningf("Failed to compute initial hash for sa_file %s: %v", saFilePath, err)
521+
}
522+
} else {
523+
glog.Warningf("sa_file %s does not exist yet for profile %s", saFilePath, *profile.Name)
524+
}
525+
526+
dn.saFileTracker[*profile.Name] = &saFileInfo{
527+
profileName: *profile.Name,
528+
filePath: saFilePath,
529+
fileHash: hash,
530+
}
531+
}
532+
}
533+
372534
func printWhenNotNil(p interface{}, description string) {
373535
switch v := p.(type) {
374536
case *string:
@@ -502,6 +664,10 @@ func (dn *Daemon) applyNodePTPProfiles() error {
502664
dn.pluginManager.PopulateHwConfig(dn.hwconfigs)
503665
*dn.refreshNodePtpDevice = true
504666
dn.readyTracker.setConfig(true)
667+
668+
// Update sa_file tracking after profiles are applied
669+
dn.updateSaFileTracking()
670+
505671
return nil
506672
}
507673

0 commit comments

Comments
 (0)