kubernetes: add multicluster support (#7266)

* kubernetes: add multicluster support

Add multicluster support via Multi-Cluster Services API (MCS-API) via a
new option `multiclusterZones` in the kubernetes plugin.

When some multicluster zones are passed to the kubernetes plugin, it
will start watching the ServiceImport objects and its associated
EndpointSlices.

Signed-off-by: Arthur Outhenin-Chalandre <arthur@cri.epita.fr>

* kubernetes: implement xfr support for multicluster zones

Signed-off-by: Arthur Outhenin-Chalandre <arthur@cri.epita.fr>

---------

Signed-off-by: Arthur Outhenin-Chalandre <arthur@cri.epita.fr>
This commit is contained in:
Arthur Outhenin-Chalandre
2025-05-19 07:58:16 +02:00
committed by GitHub
parent 76b199f829
commit 5c71bd0b87
23 changed files with 1634 additions and 298 deletions

View File

@@ -1,6 +1,7 @@
package kubernetes
import (
"slices"
"strings"
"testing"
@@ -610,3 +611,74 @@ func TestKubernetesParseIgnoreEmptyService(t *testing.T) {
}
}
}
func TestKubernetesParseMulticluster(t *testing.T) {
tests := []struct {
input string // Corefile data as string
shouldErr bool // true if test case is expected to produce an error.
expectedErrContent string // substring from the expected error. Empty for positive cases.
expectedMulticlusterZones []string
}{
// valid
{
`kubernetes coredns.local clusterset.local {
multicluster clusterset.local
}`,
false,
"",
[]string{"clusterset.local."},
},
// invalid
{
`kubernetes coredns.local {
multicluster clusterset.local
}`,
true,
"Error during parsing: is not authoritative for the multicluster zone clusterset.local.",
[]string{"clusterset.local."},
},
{
`kubernetes coredns.local clusterset.local {
multicluster clusterset.local test.local
}`,
true,
"Error during parsing: is not authoritative for the multicluster zone test.local.",
[]string{"clusterset.local.", "test.local."},
},
// not set
{
`kubernetes coredns.local {
multicluster
}`,
false,
"",
[]string{},
},
}
for i, test := range tests {
c := caddy.NewTestController("dns", test.input)
k8sController, err := kubernetesParse(c)
if test.shouldErr && err == nil {
t.Errorf("Test %d: Expected error, but did not find error for input '%s'. Error was: '%v'", i, test.input, err)
}
if err != nil {
if !test.shouldErr {
t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
continue
}
if !strings.Contains(err.Error(), test.expectedErrContent) {
t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
}
continue
}
foundMulticlusterZones := k8sController.opts.multiclusterZones
if !slices.Equal(foundMulticlusterZones, test.expectedMulticlusterZones) {
t.Errorf("Test %d: Expected kubernetes controller to be initialized with multicluster '%v'. Instead found multicluster '%v' for input '%s'", i, test.expectedMulticlusterZones, foundMulticlusterZones, test.input)
}
}
}