From 1e7b613fa0bdf61124b2e1f72c89011609944437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Sun, 15 Sep 2024 03:45:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0ntp=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 1 + go.sum | 2 + pkg/io/io_test.go | 4 +- pkg/ntp/ntp.go | 120 ++++++++++++++++++++++++++++++++++++++++++++ pkg/ntp/ntp_test.go | 50 ++++++++++++++++++ 5 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 pkg/ntp/ntp.go create mode 100644 pkg/ntp/ntp_test.go diff --git a/go.mod b/go.mod index e58ef9bb73..ffc6c39f29 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,7 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/andybalholm/brotli v1.0.4 // indirect + github.com/beevik/ntp v1.4.3 // indirect github.com/bodgit/plumbing v1.2.0 // indirect github.com/bodgit/sevenzip v1.3.0 // indirect github.com/bodgit/windows v1.0.0 // indirect diff --git a/go.sum b/go.sum index f4ef09bec8..be7a7cd24d 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,8 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho= +github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q= github.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM= github.com/bodgit/plumbing v1.2.0/go.mod h1:b9TeRi7Hvc6Y05rjm8VML3+47n4XTZPtQ/5ghqic2n8= github.com/bodgit/sevenzip v1.3.0 h1:1ljgELgtHqvgIp8W8kgeEGHIWP4ch3xGI8uOBZgLVKY= diff --git a/pkg/io/io_test.go b/pkg/io/io_test.go index 58a74790e4..ec885b81d3 100644 --- a/pkg/io/io_test.go +++ b/pkg/io/io_test.go @@ -102,7 +102,7 @@ func (s *IOTestSuite) TestMkdirCreatesDirectory() { func (s *IOTestSuite) TestChmodChangesPermissions() { if env.IsWindows() { - s.T().Skip("Skip on Windows") + s.T().Skip("Skipping on Windows") } path := "testdata/chmod_test.txt" s.NoError(Write(path, "test", 0644)) @@ -115,7 +115,7 @@ func (s *IOTestSuite) TestChmodChangesPermissions() { func (s *IOTestSuite) TestChownChangesOwner() { if env.IsWindows() { - s.T().Skip("Skip on Windows") + s.T().Skip("Skipping on Windows") } path := "testdata/chown_test.txt" s.NoError(Write(path, "test", 0644)) diff --git a/pkg/ntp/ntp.go b/pkg/ntp/ntp.go new file mode 100644 index 0000000000..8a316890b7 --- /dev/null +++ b/pkg/ntp/ntp.go @@ -0,0 +1,120 @@ +package ntp + +import ( + "errors" + "fmt" + "sync" + "time" + + "github.com/beevik/ntp" + + "github.com/TheTNB/panel/pkg/shell" +) + +var ErrNotReachable = errors.New("无法连接到 NTP 服务器") + +var ErrNoAvailableServer = errors.New("无可用的 NTP 服务器") + +var defaultAddresses = []string{ + //"ntp.ntsc.ac.cn", // 中科院国家授时中心的服务器很快,但是多刷几次就会被封 + "ntp.aliyun.com", // 阿里云 + "ntp1.aliyun.com", // 阿里云2 + "ntp.tencent.com", // 腾讯云 + "time.windows.com", // Windows + "time.apple.com", // Apple + "time.cloudflare.com", // Cloudflare +} + +func Now(address ...string) (time.Time, error) { + if len(address) > 0 { + if now, err := ntp.Time(address[0]); err != nil { + return time.Now(), fmt.Errorf("%w: %s", ErrNotReachable, err) + } else { + return now, nil + } + } + + best, err := bestServer(defaultAddresses...) + if err != nil { + return time.Now(), err + } + + now, err := ntp.Time(best) + if err != nil { + return time.Now(), fmt.Errorf("%w: %s", ErrNotReachable, err) + } + + return now, nil +} + +func UpdateSystemTime(time time.Time) error { + _, err := shell.Execf(`sudo date -s "%s"`, time.Format("2006-01-02 15:04:05")) + return err +} + +func UpdateSystemTimeZone(timezone string) error { + _, err := shell.Execf(`sudo timedatectl set-timezone %s`, timezone) + return err +} + +// pingServer 计算NTP服务器的延迟 +func pingServer(addr string) (time.Duration, error) { + options := ntp.QueryOptions{Timeout: 1 * time.Second} + response, err := ntp.QueryWithOptions(addr, options) + if err != nil { + return 0, err + } + + return response.RTT, nil +} + +// bestServer 返回延迟最低的NTP服务器 +func bestServer(addresses ...string) (string, error) { + if len(addresses) == 0 { + addresses = defaultAddresses + } + + type ntpResult struct { + address string + delay time.Duration + err error + } + + results := make(chan ntpResult, len(addresses)) + var wg sync.WaitGroup + + for _, addr := range addresses { + wg.Add(1) + go func(addr string) { + defer wg.Done() + + delay, err := pingServer(addr) + results <- ntpResult{address: addr, delay: delay, err: err} + }(addr) + } + + wg.Wait() + close(results) + + var bestAddr string + var bestDelay time.Duration + found := false + + for result := range results { + if result.err != nil { + continue + } + + if !found || result.delay < bestDelay { + bestAddr = result.address + bestDelay = result.delay + found = true + } + } + + if !found { + return "", ErrNoAvailableServer + } + + return bestAddr, nil +} diff --git a/pkg/ntp/ntp_test.go b/pkg/ntp/ntp_test.go new file mode 100644 index 0000000000..12cf5508d9 --- /dev/null +++ b/pkg/ntp/ntp_test.go @@ -0,0 +1,50 @@ +package ntp + +import ( + "testing" + "time" + + "github.com/go-rat/utils/env" + "github.com/stretchr/testify/suite" +) + +type NTPTestSuite struct { + suite.Suite +} + +func TestNTPTestSuite(t *testing.T) { + suite.Run(t, &NTPTestSuite{}) +} + +func (suite *NTPTestSuite) TestNowWithDefaultAddresses() { + now, err := Now() + suite.NoError(err) + suite.WithinDuration(time.Now(), now, time.Minute) +} + +func (suite *NTPTestSuite) TestNowWithCustomAddress() { + now, err := Now("time.windows.com") + suite.NoError(err) + suite.WithinDuration(time.Now(), now, time.Minute) +} + +func (suite *NTPTestSuite) TestNowWithInvalidAddress() { + _, err := Now("invalid.address") + suite.Error(err) +} + +func (suite *NTPTestSuite) TestUpdateSystemTime() { + if env.IsWindows() { + suite.T().Skip("Skipping on Windows") + } + err := UpdateSystemTime(time.Now()) + suite.NoError(err) +} + +func (suite *NTPTestSuite) TestUpdateSystemTimeZone() { + if env.IsWindows() { + suite.T().Skip("Skipping on Windows") + } + err := UpdateSystemTimeZone("UTC") + suite.NoError(err) +}