mirror of
https://github.com/coredns/coredns.git
synced 2026-07-20 06:30:10 -04:00
ci: run plugin tests on Windows (#8314)
Plugin package tests previously ran only on Linux, so Windows- specific failures were never caught. Run them in CI and make the affected tests portable across platforms. Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
This commit is contained in:
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# DNSSEC key material must keep LF; CRLF breaks miekg/dns private-key parsing.
|
||||
*.private text eol=lf
|
||||
*.key text eol=lf
|
||||
6
.github/workflows/go.test.yml
vendored
6
.github/workflows/go.test.yml
vendored
@@ -59,7 +59,10 @@ jobs:
|
||||
|
||||
test-plugins:
|
||||
name: Test Plugins
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
@@ -76,6 +79,7 @@ jobs:
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: ( cd plugin; go test -race ./... )
|
||||
|
||||
test-e2e:
|
||||
|
||||
@@ -151,6 +151,11 @@ func TestWalkWarnsForDuplicateOrigin(t *testing.T) {
|
||||
|
||||
func TestWalkKeepsFirstMatchingFileForOrigin(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
// Match Walk: it stores paths after EvalSymlinks.
|
||||
dir, err := filepath.EvalSymlinks(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
zone := filepath.Join(dir, "example.org.zone")
|
||||
backup := filepath.Join(dir, "example.org.zone.bak-20260528")
|
||||
|
||||
|
||||
@@ -30,10 +30,10 @@ func TestSetupAutoPath(t *testing.T) {
|
||||
{`autopath example.org @kubernetes`, false, "example.org.", "kubernetes", nil, ""},
|
||||
{`autopath 10.0.0.0/8 @kubernetes`, false, "10.in-addr.arpa.", "kubernetes", nil, ""},
|
||||
{`autopath ` + resolv, false, "", "", []string{"bar.com.", "baz.com.", ""}, ""},
|
||||
// negative
|
||||
{`autopath kubernetes`, true, "", "", nil, "open kubernetes: no such file or directory"},
|
||||
// negative (OS error text differs; match the portable prefix)
|
||||
{`autopath kubernetes`, true, "", "", nil, "open kubernetes:"},
|
||||
{`autopath`, true, "", "", nil, "no resolv-conf"},
|
||||
{`autopath ""`, true, "", "", nil, "no such file"},
|
||||
{`autopath ""`, true, "", "", nil, "failed to parse"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
|
||||
@@ -83,26 +83,8 @@ func TestZoneReloadSOAChange(t *testing.T) {
|
||||
func TestZoneReloadByMtime(t *testing.T) {
|
||||
// Test 1: Basic mtime trigger - file modification should trigger reload
|
||||
t.Run("BasicMtimeTrigger", func(t *testing.T) {
|
||||
fileName, rm, err := test.TempFile(".", reloadZoneTest)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create zone: %s", err)
|
||||
}
|
||||
defer rm()
|
||||
|
||||
reader, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open zone: %s", err)
|
||||
}
|
||||
z, err := Parse(reader, "miek.nl", fileName, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse zone: %s", err)
|
||||
}
|
||||
reader.Close()
|
||||
|
||||
// Enable mtime-based reload
|
||||
z.ReloadInterval = 10 * time.Millisecond
|
||||
z.ReloadByMtime = true
|
||||
z.Reload(&transfer.Transfer{})
|
||||
z, fileName, cleanup := prepareMtimeZone(t, reloadZoneTest)
|
||||
defer cleanup()
|
||||
|
||||
// Wait for initial load to complete
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
@@ -136,26 +118,8 @@ func TestZoneReloadByMtime(t *testing.T) {
|
||||
|
||||
// Test 2: No reload when mtime unchanged
|
||||
t.Run("NoReloadWhenMtimeUnchanged", func(t *testing.T) {
|
||||
fileName, rm, err := test.TempFile(".", reloadZoneTest)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create zone: %s", err)
|
||||
}
|
||||
defer rm()
|
||||
|
||||
reader, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open zone: %s", err)
|
||||
}
|
||||
z, err := Parse(reader, "miek.nl", fileName, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse zone: %s", err)
|
||||
}
|
||||
reader.Close()
|
||||
|
||||
// Enable mtime-based reload
|
||||
z.ReloadInterval = 10 * time.Millisecond
|
||||
z.ReloadByMtime = true
|
||||
z.Reload(&transfer.Transfer{})
|
||||
z, _, cleanup := prepareMtimeZone(t, reloadZoneTest)
|
||||
defer cleanup()
|
||||
|
||||
// Wait for initial load
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
@@ -193,26 +157,8 @@ func TestZoneReloadByMtime(t *testing.T) {
|
||||
|
||||
// Test 3: Content verification after reload
|
||||
t.Run("ContentVerificationAfterReload", func(t *testing.T) {
|
||||
fileName, rm, err := test.TempFile(".", reloadZoneTest)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create zone: %s", err)
|
||||
}
|
||||
defer rm()
|
||||
|
||||
reader, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open zone: %s", err)
|
||||
}
|
||||
z, err := Parse(reader, "miek.nl", fileName, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse zone: %s", err)
|
||||
}
|
||||
reader.Close()
|
||||
|
||||
// Enable mtime-based reload
|
||||
z.ReloadInterval = 10 * time.Millisecond
|
||||
z.ReloadByMtime = true
|
||||
z.Reload(&transfer.Transfer{})
|
||||
z, fileName, cleanup := prepareMtimeZone(t, reloadZoneTest)
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.TODO()
|
||||
|
||||
@@ -266,26 +212,8 @@ func TestZoneReloadByMtime(t *testing.T) {
|
||||
|
||||
// Test 4: File deleted/missing during reload
|
||||
t.Run("FileMissingDuringReload", func(t *testing.T) {
|
||||
fileName, rm, err := test.TempFile(".", reloadZoneTest)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create zone: %s", err)
|
||||
}
|
||||
defer rm()
|
||||
|
||||
reader, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open zone: %s", err)
|
||||
}
|
||||
z, err := Parse(reader, "miek.nl", fileName, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse zone: %s", err)
|
||||
}
|
||||
reader.Close()
|
||||
|
||||
// Enable mtime-based reload
|
||||
z.ReloadInterval = 10 * time.Millisecond
|
||||
z.ReloadByMtime = true
|
||||
z.Reload(&transfer.Transfer{})
|
||||
z, fileName, cleanup := prepareMtimeZone(t, reloadZoneTest)
|
||||
defer cleanup()
|
||||
|
||||
// Wait for initial load
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
@@ -327,6 +255,45 @@ func TestZoneReloadByMtime(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// prepareMtimeZone creates a zone with mtime-based reload enabled.
|
||||
func prepareMtimeZone(t *testing.T, content string) (*Zone, string, func()) {
|
||||
t.Helper()
|
||||
fileName, rm, err := test.TempFile(".", content)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create zone: %s", err)
|
||||
}
|
||||
|
||||
reader, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
rm()
|
||||
t.Fatalf("Failed to open zone: %s", err)
|
||||
}
|
||||
z, err := Parse(reader, "miek.nl", fileName, 0)
|
||||
reader.Close()
|
||||
if err != nil {
|
||||
rm()
|
||||
t.Fatalf("Failed to parse zone: %s", err)
|
||||
}
|
||||
|
||||
fi, err := os.Stat(fileName)
|
||||
if err != nil {
|
||||
rm()
|
||||
t.Fatalf("Failed to stat zone: %s", err)
|
||||
}
|
||||
|
||||
z.ReloadInterval = 10 * time.Millisecond
|
||||
z.ReloadByMtime = true
|
||||
// Parse does not set file_mtime unless ReloadByMtime is already enabled.
|
||||
// Seed it so the reload loop only opens the file when mtime changes.
|
||||
z.file_mtime = fi.ModTime()
|
||||
z.Reload(&transfer.Transfer{})
|
||||
|
||||
return z, fileName, func() {
|
||||
z.OnShutdown()
|
||||
rm()
|
||||
}
|
||||
}
|
||||
|
||||
const reloadZoneTest = `miek.nl. 1627 IN SOA linode.atoom.net. miek.miek.nl. 1460175181 14400 3600 604800 14400
|
||||
miek.nl. 1627 IN NS ext.ns.whyscream.net.
|
||||
miek.nl. 1627 IN NS omval.tednet.nl.
|
||||
|
||||
@@ -72,11 +72,16 @@ func TestPrint(t *testing.T) {
|
||||
|
||||
f, err := os.CreateTemp(t.TempDir(), "print_test_tmp")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
//Redirect the printed results to a tmp file for later comparison
|
||||
// Redirect Print output to a temp file, then restore stdout and close
|
||||
// the handle so TempDir cleanup can remove it on Windows.
|
||||
stdout := os.Stdout
|
||||
os.Stdout = f
|
||||
t.Cleanup(func() {
|
||||
os.Stdout = stdout
|
||||
f.Close()
|
||||
})
|
||||
|
||||
tree.Print()
|
||||
/**
|
||||
@@ -85,12 +90,12 @@ func TestPrint(t *testing.T) {
|
||||
server3.example.com.
|
||||
*/
|
||||
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
buf := make([]byte, 256)
|
||||
f.Seek(0, 0)
|
||||
_, err = f.Read(buf)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
t.Error(err)
|
||||
if _, err := f.Read(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
height := strings.Count(string(buf), ". \n")
|
||||
//Compare the height of the print with the actual height of the tree
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -290,6 +291,12 @@ nameserver 10.10.255.253`), 0666); err != nil {
|
||||
}
|
||||
defer os.Remove(emptyResolv)
|
||||
|
||||
// Portable stand-in for /dev/null: a resolv.conf with no nameserver lines.
|
||||
nullResolv := filepath.Join(t.TempDir(), "null.conf")
|
||||
if err := os.WriteFile(nullResolv, nil, 0666); err != nil {
|
||||
t.Fatalf("Failed to write null.conf file: %s", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
shouldErr bool
|
||||
@@ -299,7 +306,7 @@ nameserver 10.10.255.253`), 0666); err != nil {
|
||||
// pass
|
||||
{`forward . ` + resolv, false, "", []string{"10.10.255.252:53", "10.10.255.253:53"}},
|
||||
// fail
|
||||
{`forward . /dev/null`, true, "no valid upstream addresses found", nil},
|
||||
{`forward . ` + nullResolv, true, "no valid upstream addresses found", nil},
|
||||
// IPV6 with local zone
|
||||
{`forward . ` + resolvIPV6, false, "", []string{"[0388:d254:7aec:6892:9f7f:e93b:5806:1b0f]:53"}},
|
||||
// pass when empty forward file is found
|
||||
|
||||
@@ -64,7 +64,7 @@ func TestGeoIPParse(t *testing.T) {
|
||||
{true, fmt.Sprintf("%s 1 2 3", pluginName), "Wrong argument count", 0},
|
||||
{true, fmt.Sprintf("%s { }", pluginName), "Error during parsing", 0},
|
||||
{true, fmt.Sprintf("%s /dbpath { city }", pluginName), "unknown property \"city\"", 0},
|
||||
{true, fmt.Sprintf("%s /invalidPath\n", pluginName), "failed to open database file: open /invalidPath: no such file or directory", 0},
|
||||
{true, fmt.Sprintf("%s /invalidPath\n", pluginName), "failed to open database file: open /invalidPath:", 0},
|
||||
{true, fmt.Sprintf("%s %s\n", pluginName, unknownDBPath), "reader does not support the \"UnknownDbType\" database type", 0},
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"net"
|
||||
"path"
|
||||
"runtime"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
@@ -99,6 +100,10 @@ func (m testServiceClient) Query(_ctx context.Context, _in *pb.DnsPacket, _opts
|
||||
}
|
||||
|
||||
func TestProxyUnix(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("unix domain sockets are not supported on windows")
|
||||
}
|
||||
|
||||
tdir := t.TempDir()
|
||||
|
||||
fd := path.Join(tdir, "test.grpc")
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"math"
|
||||
"net"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -221,6 +222,7 @@ func TestCoreDNSOverflow(t *testing.T) {
|
||||
response, _, _, err := p.Connect(context.Background(), request, options)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to connect to testdnsserver: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if response.Truncated != expectTruncated {
|
||||
@@ -228,15 +230,18 @@ func TestCoreDNSOverflow(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test PreferUDP, expect truncated response
|
||||
testConnection("PreferUDP", Options{PreferUDP: true}, true)
|
||||
// Oversized UDP replies are truncated on Unix; Windows surfaces WSAEMSGSIZE instead.
|
||||
if runtime.GOOS != "windows" {
|
||||
// Test PreferUDP, expect truncated response
|
||||
testConnection("PreferUDP", Options{PreferUDP: true}, true)
|
||||
|
||||
// Test No options specified, expect truncated response
|
||||
testConnection("NoOptionsSpecified", Options{}, true)
|
||||
}
|
||||
|
||||
// Test ForceTCP, expect no truncated response
|
||||
testConnection("ForceTCP", Options{ForceTCP: true}, false)
|
||||
|
||||
// Test No options specified, expect truncated response
|
||||
testConnection("NoOptionsSpecified", Options{}, true)
|
||||
|
||||
// Test both TCP and UDP provided, expect no truncated response
|
||||
testConnection("BothTCPAndUDP", Options{PreferUDP: true, ForceTCP: true}, false)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package tls
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -52,6 +53,12 @@ func TestTLS(t *testing.T) {
|
||||
t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
|
||||
}
|
||||
}
|
||||
|
||||
// setup registers OnShutdown to close KeyLogWriter, but tests never shut down.
|
||||
// Close explicitly so t.TempDir cleanup can remove the file on Windows.
|
||||
if err == nil {
|
||||
closeKeyLogWriter(t, dnsserver.GetConfig(c))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,5 +128,22 @@ func TestTLSKeyLog(t *testing.T) {
|
||||
if cfg.TLSConfig.KeyLogWriter == nil {
|
||||
t.Fatal("KeyLogWriter is not set")
|
||||
}
|
||||
// setup registers OnShutdown to close KeyLogWriter, but tests never shut down.
|
||||
// Close explicitly so t.TempDir cleanup can remove the file on Windows.
|
||||
closeKeyLogWriter(t, cfg)
|
||||
})
|
||||
}
|
||||
|
||||
func closeKeyLogWriter(t *testing.T, cfg *dnsserver.Config) {
|
||||
t.Helper()
|
||||
if cfg.TLSConfig == nil {
|
||||
return
|
||||
}
|
||||
closer, ok := cfg.TLSConfig.KeyLogWriter.(io.Closer)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
closer.Close()
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user