add args: startup_timeout for kubernetes plugin (#7068)

Signed-off-by: mangoyhuang <mangoyhuang@tencent.com>
Co-authored-by: mangoyhuang <mangoyhuang@tencent.com>
This commit is contained in:
Dave Brown
2025-06-12 02:22:07 +08:00
committed by GitHub
parent cbb318f4d0
commit ab74d3acf2
4 changed files with 69 additions and 4 deletions

View File

@@ -43,6 +43,7 @@ kubernetes [ZONES...] {
fallthrough [ZONES...] fallthrough [ZONES...]
ignore empty_service ignore empty_service
multicluster [ZONES...] multicluster [ZONES...]
startup_timeout DURATION
} }
``` ```
@@ -106,6 +107,8 @@ kubernetes [ZONES...] {
Services API (MCS-API). Specifying this option is generally paired with the Services API (MCS-API). Specifying this option is generally paired with the
installation of an MCS-API implementation and the ServiceImport and ServiceExport installation of an MCS-API implementation and the ServiceImport and ServiceExport
CRDs. The plugin MUST be authoritative for the zones listed here. CRDs. The plugin MUST be authoritative for the zones listed here.
* `startup_timeout` specifies the **DURATION** value that limits the time to wait for informer cache synced
when the kubernetes plugin starts. If not specified, the default timeout will be 5s.
Enabling zone transfer is done by using the *transfer* plugin. Enabling zone transfer is done by using the *transfer* plugin.
@@ -115,7 +118,7 @@ When CoreDNS starts with the *kubernetes* plugin enabled, it will delay serving
until it can connect to the Kubernetes API and synchronize all object watches. If this cannot happen within until it can connect to the Kubernetes API and synchronize all object watches. If this cannot happen within
5 seconds, then CoreDNS will start serving DNS while the *kubernetes* plugin continues to try to connect 5 seconds, then CoreDNS will start serving DNS while the *kubernetes* plugin continues to try to connect
and synchronize all object watches. CoreDNS will answer SERVFAIL to any request made for a Kubernetes record and synchronize all object watches. CoreDNS will answer SERVFAIL to any request made for a Kubernetes record
that has not yet been synchronized. that has not yet been synchronized. You can also determine how long to wait by specifying `startup_timeout`.
## Monitoring Kubernetes Endpoints ## Monitoring Kubernetes Endpoints

View File

@@ -49,6 +49,7 @@ type Kubernetes struct {
primaryZoneIndex int primaryZoneIndex int
localIPs []net.IP localIPs []net.IP
autoPathSearch []string // Local search path from /etc/resolv.conf. Needed for autopath. autoPathSearch []string // Local search path from /etc/resolv.conf. Needed for autopath.
startupTimeout time.Duration // startupTimeout set timeout of startup
} }
// Upstreamer is used to resolve CNAME or other external targets // Upstreamer is used to resolve CNAME or other external targets
@@ -276,8 +277,7 @@ func (k *Kubernetes) InitKubeCache(ctx context.Context) (onStart func() error, o
k.APIConn.Run() k.APIConn.Run()
}() }()
timeout := 5 * time.Second timeoutTicker := time.NewTicker(k.startupTimeout)
timeoutTicker := time.NewTicker(timeout)
defer timeoutTicker.Stop() defer timeoutTicker.Stop()
logDelay := 500 * time.Millisecond logDelay := 500 * time.Millisecond
logTicker := time.NewTicker(logDelay) logTicker := time.NewTicker(logDelay)

View File

@@ -7,6 +7,7 @@ import (
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/coredns/caddy" "github.com/coredns/caddy"
"github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/core/dnsserver"
@@ -112,6 +113,7 @@ func ParseStanza(c *caddy.Controller) (*Kubernetes, error) {
k8s.Upstream = upstream.New() k8s.Upstream = upstream.New()
k8s.startupTimeout = time.Second * 5
for c.NextBlock() { for c.NextBlock() {
switch c.Val() { switch c.Val() {
case "endpoint_pod_names": case "endpoint_pod_names":
@@ -231,6 +233,17 @@ func ParseStanza(c *caddy.Controller) (*Kubernetes, error) {
k8s.ClientConfig = config k8s.ClientConfig = config
case "multicluster": case "multicluster":
k8s.opts.multiclusterZones = plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), []string{}) k8s.opts.multiclusterZones = plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), []string{})
case "startup_timeout":
args := c.RemainingArgs()
if len(args) == 0 {
return nil, c.ArgErr()
} else {
var err error
k8s.startupTimeout, err = time.ParseDuration(args[0])
if err != nil {
return nil, fmt.Errorf("failed to parse startup_timeout: %v, %s", args[0], err)
}
}
default: default:
return nil, c.Errf("unknown property '%s'", c.Val()) return nil, c.Errf("unknown property '%s'", c.Val())
} }

View File

@@ -4,6 +4,7 @@ import (
"slices" "slices"
"strings" "strings"
"testing" "testing"
"time"
"github.com/coredns/caddy" "github.com/coredns/caddy"
"github.com/coredns/coredns/plugin/pkg/fall" "github.com/coredns/coredns/plugin/pkg/fall"
@@ -11,6 +12,8 @@ import (
meta "k8s.io/apimachinery/pkg/apis/meta/v1" meta "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
var defaultStartupTimeout = time.Second * 5
func TestKubernetesParse(t *testing.T) { func TestKubernetesParse(t *testing.T) {
tests := []struct { tests := []struct {
input string // Corefile data as string input string // Corefile data as string
@@ -22,6 +25,7 @@ func TestKubernetesParse(t *testing.T) {
expectedNamespaceLabelSelector string // expected namespace label selector value expectedNamespaceLabelSelector string // expected namespace label selector value
expectedPodMode string expectedPodMode string
expectedFallthrough fall.F expectedFallthrough fall.F
expectedStartupTimeout time.Duration
}{ }{
// positive // positive
{ {
@@ -34,6 +38,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local test.local`, `kubernetes coredns.local test.local`,
@@ -45,6 +50,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -57,6 +63,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -70,6 +77,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -83,6 +91,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -96,6 +105,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -109,6 +119,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -122,6 +133,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -135,6 +147,7 @@ func TestKubernetesParse(t *testing.T) {
"istio-injection=enabled", "istio-injection=enabled",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -149,6 +162,7 @@ func TestKubernetesParse(t *testing.T) {
"istio-injection=enabled", "istio-injection=enabled",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local test.local { `kubernetes coredns.local test.local {
@@ -165,6 +179,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Root, fall.Root,
defaultStartupTimeout,
}, },
// negative // negative
{ {
@@ -179,6 +194,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -192,6 +208,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -205,6 +222,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -218,6 +236,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
// pods disabled // pods disabled
{ {
@@ -232,6 +251,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
// pods insecure // pods insecure
{ {
@@ -246,6 +266,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeInsecure, podModeInsecure,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
// pods verified // pods verified
{ {
@@ -260,6 +281,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeVerified, podModeVerified,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
// pods invalid // pods invalid
{ {
@@ -274,6 +296,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeVerified, podModeVerified,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
// fallthrough with zones // fallthrough with zones
{ {
@@ -288,6 +311,7 @@ func TestKubernetesParse(t *testing.T) {
"", "",
podModeDisabled, podModeDisabled,
fall.F{Zones: []string{"ip6.arpa.", "inaddr.arpa.", "foo.com."}}, fall.F{Zones: []string{"ip6.arpa.", "inaddr.arpa.", "foo.com."}},
defaultStartupTimeout,
}, },
// More than one Kubernetes not allowed // More than one Kubernetes not allowed
{ {
@@ -301,6 +325,7 @@ kubernetes cluster.local`,
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -314,6 +339,7 @@ kubernetes cluster.local`,
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -327,6 +353,7 @@ kubernetes cluster.local`,
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -340,6 +367,7 @@ kubernetes cluster.local`,
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
}, },
{ {
`kubernetes coredns.local { `kubernetes coredns.local {
@@ -353,6 +381,22 @@ kubernetes cluster.local`,
"", "",
podModeDisabled, podModeDisabled,
fall.Zero, fall.Zero,
defaultStartupTimeout,
},
{
`kubernetes coredns.local {
kubeconfig file context
startup_timeout 1s
}`,
false,
"",
1,
0,
"",
"",
podModeDisabled,
fall.Zero,
time.Second * 1,
}, },
} }
@@ -414,6 +458,11 @@ kubernetes cluster.local`,
if !k8sController.Fall.Equal(test.expectedFallthrough) { if !k8sController.Fall.Equal(test.expectedFallthrough) {
t.Errorf("Test %d: Expected kubernetes controller to be initialized with fallthrough '%v'. Instead found fallthrough '%v' for input '%s'", i, test.expectedFallthrough, k8sController.Fall, test.input) t.Errorf("Test %d: Expected kubernetes controller to be initialized with fallthrough '%v'. Instead found fallthrough '%v' for input '%s'", i, test.expectedFallthrough, k8sController.Fall, test.input)
} }
// startupTimeout
if k8sController.startupTimeout.String() != test.expectedStartupTimeout.String() {
t.Errorf("Test %d: Expected kubernetes controller to be initialized with startupTimeout '%v'. Instead found startupTimeout '%v' for input '%s'", i, test.expectedStartupTimeout, k8sController.startupTimeout, test.input)
}
} }
} }